Path: blob/main/Scripts/Special/Sonic.txt
1319 views
//-------------------Sonic CD Sonic Script--------------------//1//--------Scripted by Christian Whitehead 'The Taxman'--------//2//-------Unpacked By Rubberduckycooly's Script Unpacker-------//34// Aliases56// These are three sets of aliases, this object is rather inconsistent in its variable use, where sometimes7// normal Object.Values are used, but then other times, Player. values are used instead8// For the sake of simplicity, these have all been merged into a SSSonic. namespace via aliases910// Value aliases11#alias Object.Value0 : SSSonic.ZPos12#alias Object.Value1 : SSSonic.FrameLoop13#alias Object.Value2 : SSSonic.FrameEnd14#alias Object.Value3 : SSSonic.FrameTimer15#alias Object.Value4 : SSSonic.Unused // Completely unused16#alias Object.Value5 : SSSonic.ScreenDepth // Same use as other Special Stage objects, see Special Setup17#alias Object.Value6 : SSSonic.Timer18#alias Object.Value7 : SSSonic.Tilt1920// Player. aliases21#alias Player.ControlMode : SSSonic.ControlMode22#alias Player.Speed : SSSonic.Speed23#alias Player.Angle : SSSonic.Angle24#alias Player.XPos : SSSonic.XPos25#alias Player.YPos : SSSonic.YPos26#alias Player.ScreenXPos : SSSonic.ScreenXPos27#alias Player.ScreenYPos : SSSonic.ScreenYPos28#alias Player.YVelocity : SSSonic.YVelocity2930// Object. aliases31#alias Object.Type : SSSonic.Type32#alias Object.Priority : SSSonic.Priority33#alias Object.State : SSSonic.State34#alias Object.Frame : SSSonic.Frame35#alias Object.Direction : SSSonic.Direction36#alias Object.AnimationSpeed : SSSonic.AnimationSpeed3738// State aliases39#alias 0 : SSSONIC_INTROPOSE40#alias 1 : SSSONIC_INTROTURNAROUND41#alias 2 : SSSONIC_WALKING42#alias 3 : SSSONIC_JUMPING43#alias 4 : SSSONIC_SPEEDBOOSTER44#alias 5 : SSSONIC_FAN45#alias 6 : SSSONIC_TRIPPED46#alias 7 : SSSONIC_SPEEDSHOESRUN47#alias 8 : SSSONIC_FINISHSTAND48#alias 9 : SSSONIC_CAMERAPAN49#alias 10 : SSSONIC_STONEGRABBED50#alias 11 : SSSONIC_SPRING5152// Animation aliases53// Most are divided into three parts where there's the animation's start, its loop point, and its end54// This is to match with how this Object animates itself, check out Sonic_ProcessAnimation for more info5556#alias 1 : ANI_STANDING_START57// This standing animation doesn't, well, animate, so no extended aliases needed here5859#alias 2 : ANI_FACINGAHEAD_START60#alias 4 : ANI_FACINGAHEAD_LOOP61#alias 4 : ANI_FACINGAHEAD_END6263#alias 5 : ANI_INTROPOSE_START64#alias 5 : ANI_INTROPOSE_LOOP65#alias 8 : ANI_INTROPOSE_END6667#alias 9 : ANI_WALKING_START68#alias 9 : ANI_WALKING_LOOP69#alias 14 : ANI_WALKING_END7071#alias 39 : ANI_JUMPING_START72#alias 39 : ANI_JUMPING_LOOP73#alias 42 : ANI_JUMPING_END7475#alias 43 : ANI_BRAKING_START76#alias 43 : ANI_BRAKING_LOOP77#alias 47 : ANI_BRAKING_END7879#alias 48 : ANI_FAN_START80#alias 48 : ANI_FAN_LOOP81#alias 53 : ANI_FAN_END8283#alias 54 : ANI_TRIP_START84#alias 54 : ANI_TRIP_LOOP85#alias 76 : ANI_TRIP_END8687#alias 77 : ANI_RUN_START88#alias 77 : ANI_RUN_LOOP89#alias 80 : ANI_RUN_END9091// HUD Aliases92#alias Object.Value0 : HUD.UFOsCount93#alias Object.Value3 : HUD.Rings94#alias Object.Value4 : HUD.LastUFOType95#alias Object.Value5 : HUD.SpeedShoes9697// Ring Aliases98#alias Object.Value0 : Ring.ZPos99#alias Object.Value1 : Ring.XVelocity100#alias Object.Value2 : Ring.YVelocity101#alias Object.Value3 : Ring.ZVelocity102103// Touch Control Aliases104// This value is used to store if the jump button was touched last frame, to help differenciate new taps from old holds105#alias Object.Value7 : TouchControls.TouchJump106107// Generic Object Alias108// (Here it's only used for the Water Splash object, but it does apply to every SS game object as well)109#alias Object.Value5 : Object.ScreenDepth110111// Player aliases112#alias 0 : PLAYER_SONIC_A113#alias 1 : PLAYER_TAILS_A114115// Global SFX116#alias 0 : SFX_G_JUMP117#alias 4 : SFX_G_LOSERINGS118#alias 11 : SFX_G_SPRING119#alias 24 : SFX_G_FLYING120#alias 25 : SFX_G_TIRED121#alias 27 : SFX_G_SELECT122123// Stage SFX124#alias 2 : SFX_S_BUMPER2125#alias 3 : SFX_S_LARGEBOOSTER126#alias 4 : SFX_S_SMALLBOOSTER127#alias 6 : SFX_S_FAN128129// ControlMode Aliases130#alias -1 : CONTROLMODE_NONE131#alias 0 : CONTROLMODE_NORMAL132133// Engine Callback Aliases134#alias 13 : CALLBACK_PAUSE_REQUESTED135136// Engine Messages137#alias 2 : MESSAGE_LOSTFOCUS138139// Priority140#alias 1 : PRIORITY_ACTIVE141#alias 2 : PRIORITY_ALWAYS142143// Tile Layer types144#alias 3 : LAYER_3DFLOOR145146// Tile Info147#alias 1 : TILEINFO_DIRECTION148#alias 6 : TILEINFO_ANGLEA149150// Stage Finish Aliases151#alias Object.PropertyValue : Object.ResultsType152153#alias 1 : STAGEFINISH_T_STONEOBTAINED154155156function Sonic_ProcessPlayer157158if Options.AttractMode == false159#platform: Mobile160// Mobile has a lot more checks here than standard, especially because of the touchscreen, so it's just its own separate part here161162if Options.TouchControls == true163if SSSonic.ControlMode == CONTROLMODE_NORMAL164CheckTouchRect(0, 96, Screen.CenterX, Screen.YSize)165if CheckResult > -1166167// Move the current touch screen array pos to the one found by CheckTouchRect168ArrayPos0 = CheckResult169170// Move its XPos within range171TempValue0 = TouchScreen[ArrayPos0].XPos172TempValue0 -= Options.DPadX173174// And move its YPos within range too175TempValue1 = TouchScreen[ArrayPos0].YPos176TempValue1 -= 192177178// Find the arctan from the value pair and shift it a bit to match with one of four directions179ATan2(TempValue2, TempValue0, TempValue1)180TempValue2 += 32181TempValue2 &= 255182TempValue2 >>= 6183184// Take the result and match it with the corresponding direction of the DPad185switch TempValue2186case 0187KeyDown[1].Right = true188break189190case 1191KeyDown[1].Down = true192break193194case 2195KeyDown[1].Left = true196break197198case 3199KeyDown[1].Up = true200break201202end switch203end if204205// Check if the jump button was pressed206CheckTouchRect(Screen.CenterX, 166, Screen.XSize, 240)207208if CheckResult > -1209KeyDown[1].ButtonA = true210end if211212// If the jump button wasn't held last frame, then that means the current touch is a new press213if TouchControls[25].TouchJump == false214KeyPress[1].ButtonA |= KeyDown[1].ButtonA215end if216TouchControls[25].TouchJump = KeyDown[1].ButtonA217218// If the Pause Menu doesn't currently exist...219if Object[9].Type == TypeName[Blank Object]220221// Check if the touch screen's pause button was pressed222CheckTouchRect(240, 0, Screen.XSize, 40)223if CheckResult > -1224225// Pause the entire stage (including its objects)226Stage.State = STAGE_PAUSED227228// Pause (but don't fully stop) the music229PauseMusic()230231// Play the menu SFX232PlaySfx(SFX_G_SELECT, false)233234// And stop the currently playing game SFX, if any235StopSfx(SFX_G_FLYING)236StopSfx(SFX_G_TIRED)237238// Spawn the Pause Menu in reserved object slot 9239Object[9].Type = TypeName[Pause Menu]240241// Give it a high draw order to make it draw ontop of everything242// The HUD object shares this same priority, but since the Pause Menu is further down the object list it'll still get drawn on top243Object[9].DrawOrder = 6244245// Give the Pause Menu object the special PRIORITY_ALWAYS priority in order to keep it running while the stage is paused246Object[9].Priority = PRIORITY_ALWAYS247248// Disable Frame Skip here, not too much is happening while paused anyway249if Engine.FrameSkipTimer > -1250Engine.FrameSkipTimer = -1251end if252253// And set the floor to be of the actual "3d floor" type254// The floor was indeed 3d already, but this disables High Quality mode to help save on resources while paused255TileLayer[0].Type = LAYER_3DFLOOR256257end if258259// Check if game focus was lost260// (As in, did the player exit the app?)261if Engine.Message == MESSAGE_LOSTFOCUS262263// Pause the entire stage (including its objects)264Stage.State = STAGE_PAUSED265266// Pause (but don't fully stop) the music267PauseMusic()268269// Play the menu SFX270PlaySfx(SFX_G_SELECT, false)271272// And stop game SFX from currently playing, if there are any273StopSfx(SFX_G_FLYING)274StopSfx(SFX_G_TIRED)275276// Spawn the Pause Menu in reserved object slot 9277Object[9].Type = TypeName[Pause Menu]278279// Give it a high draw order to make it draw ontop of everything280// The HUD object shares this same priority, but since the Pause Menu is further down the object list it'll still get drawn on top281Object[9].DrawOrder = 6282283// Give the Pause Menu object the special PRIORITY_ALWAYS priority in order to keep it running while the stage is paused284Object[9].Priority = PRIORITY_ALWAYS285286// Disable Frame Skip, it's not like much is happening while paused anyway287if Engine.FrameSkipTimer > -1288Engine.FrameSkipTimer = -1289end if290291// This sets the floor to the "3d floor" type, in order to disable High Quality rendering mode292TileLayer[0].Type = LAYER_3DFLOOR293294end if295end if296end if297else298// Using physical controls299300if SSSonic.ControlMode == CONTROLMODE_NORMAL301302// Check that no Pause Menu object exists yet303if Object[9].Type == TypeName[Blank Object]304305// First check for the physical start button being pressed306if KeyPress[1].Start == true307308// Clear the Start button state309// (This is here just to make sure "double-pauses" don't occur, where the game gets paused twice or more from a single pause button press)310KeyPress[1].Start = false311312// Pause the entire stage (including its objects)313Stage.State = STAGE_PAUSED314315// Don't stop the music, just pause it instead so that it can be resumed later316PauseMusic()317318// Play the menu SFX as it pops out319PlaySfx(SFX_G_SELECT, false)320321// And stop the other game SFX currently playing, if any322StopSfx(SFX_G_FLYING)323StopSfx(SFX_G_TIRED)324325// Spawn the Pause Menu in reserved object slot 9326Object[9].Type = TypeName[Pause Menu]327328// Give it a high draw order to make it's above everything else329// -> This draw order is shared with the HUD object too, but since the Pause Menu object slot (9) is330// further down the list than the HUD object slot (4), it'll still be drawn on top331Object[9].DrawOrder = 6332333// And give the Pause Menu a special priority to make sure it'll keep on running while the stage is paused334Object[9].Priority = PRIORITY_ALWAYS335336// Disable frame skip, pausing doesn't have much activity anyway337if Engine.FrameSkipTimer > -1338Engine.FrameSkipTimer = -1339end if340341// Keep the floor as 3d, but as the lower quality version to stay efficient while paused342TileLayer[0].Type = LAYER_3DFLOOR343344end if345346// Then check if game focus was lost347// Meaning, did the player exit the app?348if Engine.Message == MESSAGE_LOSTFOCUS349350// Pause the entire stage, along with all objects351Stage.State = STAGE_PAUSED352353// Pause the music as well, not stopping it so that it can be resumed rather than restarted upon resuming the game354PauseMusic()355356// Play the select SFX as the menu slides in357PlaySfx(SFX_G_SELECT, false)358359// And stop game SFX that could be currently playing360StopSfx(SFX_G_FLYING)361StopSfx(SFX_G_TIRED)362363// Spawn the Pause Menu in reserved object slot 9364Object[9].Type = TypeName[Pause Menu]365366// Give it a high draw priority to make sure it draws above everything else367Object[9].DrawOrder = 6368369// And give is a special update priority too, to make sure it'll persist during the stage's paused state370Object[9].Priority = PRIORITY_ALWAYS371372// No frame skip here, no siree!373if Engine.FrameSkipTimer > -1374Engine.FrameSkipTimer = -1375end if376377// The floor should already be 3d here, but make it a 3d floor again in order to exit High Quality mode while paused378TileLayer[0].Type = LAYER_3DFLOOR379380end if381382end if383end if384end if385#endplatform386387#platform: Standard388// Standard doesn't need nearly as much code as mobile to handle its inputs,389// thanks to the lack of a touchscreen and other mobile things390391if SSSonic.ControlMode == CONTROLMODE_NORMAL392393// Make sure no Pause Menu object exists already394if Object[9].Type == TypeName[Blank Object]395if KeyPress[1].Start == true396397// Clear the Start state in order to avoid "double-pauses" oddities398KeyPress[1].Start = false399400// If the player was brought here from the Dev Menu, use the game-object version of the pause menu401if Options.DevMenuFlag == true402403// Pause the stage, and all objects in it404Stage.State = STAGE_PAUSED405406// Pause the music too, not fully stopping it so that it can be resumed later407PauseMusic()408409// Play the menu SFX as it pops out from the side410PlaySfx(SFX_G_SELECT, false)411412// And stop the game SFX, as well413StopSfx(SFX_G_FLYING)414StopSfx(SFX_G_TIRED)415416// Spawn the Pause Menu object in reserved object slot 9417Object[9].Type = TypeName[Pause Menu]418419// Give it a high draw order to insure it stays ontop of other objects420Object[9].DrawOrder = 6421422// Give it a special priority so that it runs even during pauses, or else it'll just be frozen423Object[9].Priority = PRIORITY_ALWAYS424425// The floor should already be 3d, this is actually a form of exiting High Quality mode426TileLayer[0].Type = LAYER_3DFLOOR427428else429430// In normal gameplay, call the engine's built-in pause menu431// (Although, do note, that with the fan-made engine decompilation of the game,432// this callback will just call the in-game Pause Menu anyway...)433EngineCallback(CALLBACK_PAUSE_REQUESTED)434435end if436end if437end if438end if439#endplatform440441// Assign all Player.* input values to their corresponding Input.* values442ProcessPlayerControl()443444end if // Options.AttractMode == false445446end function447448449function Sonic_HandlePause450451// This function is only called while tripped, it's nearly identical to the normal pause function with the exception of control values not being set452// Don't really know why this exists, but there's probably some reason453454if Options.AttractMode == false455#platform: Mobile456if SSSonic.ControlMode == CONTROLMODE_NORMAL457458// Make sure the Pause Menu doesn't already exist459if Object[9].Type == TypeName[Blank Object]460461// See if the player's touched the pause button462CheckTouchRect(240, 0, Screen.XSize, 40)463if CheckResult > -1464465// Pause the entire stage, including its objects466Stage.State = STAGE_PAUSED467468// And pause the music too469// Not completely stopping the music though, since it'll possibly resumed later470PauseMusic()471472// SFX473PlaySfx(SFX_G_SELECT, false)474StopSfx(SFX_G_FLYING)475StopSfx(SFX_G_TIRED)476477// Create the pause menu and initialise its values478// - Object Type of [Pause Menu] (of course)479// - High Draw Order, above other things like even the HUD480// - Priority of PRIORITY_ALWAYS, since the Object should continue running even while the stage is paused481Object[9].Type = TypeName[Pause Menu]482Object[9].DrawOrder = 6483Object[9].Priority = PRIORITY_ALWAYS484485// Disable frameskip when paused486if Engine.FrameSkipTimer > -1487Engine.FrameSkipTimer = -1488end if489490// Make the floor lower quality instead while paused, to be more efficient491TileLayer[0].Type = LAYER_3DFLOOR492493end if494495// Check if focus was lost (as in, the player exiting the game)496if Engine.Message == MESSAGE_LOSTFOCUS497498// Pause the stage & music499Stage.State = STAGE_PAUSED500PauseMusic()501502// Play the menu SFX as it appears503PlaySfx(SFX_G_SELECT, false)504505// And stop some other game SFX, too506StopSfx(SFX_G_FLYING)507StopSfx(SFX_G_TIRED)508509// Place the Pause Menu in reserved object slot 9510Object[9].Type = TypeName[Pause Menu]511Object[9].DrawOrder = 6512Object[9].Priority = PRIORITY_ALWAYS513514// No frame skip needed when paused, not much really happens then anyway515if Engine.FrameSkipTimer > -1516Engine.FrameSkipTimer = -1517end if518519// The floor should already be 3d, this is ensuring that it's drawn in standard quality rather than High Quality mode520TileLayer[0].Type = LAYER_3DFLOOR521522end if523end if524end if525#endplatform526527#platform: Standard528if SSSonic.ControlMode == CONTROLMODE_NORMAL529530// Make sure the game isn't already paused via checking if the Pause Menu object exists or not531if Object[9].Type == TypeName[Blank Object]532if KeyPress[1].Start == true533534// Disable start for this frame, this'll help with "double-pausing" cases535KeyPress[1].Start = false536537// Give different pause menus depending on if the player was brought here from the Dev Menu538// (...but why?)539if Options.DevMenuFlag == true540541// Pause the stage, this'll pause all the stage's objects too542Stage.State = STAGE_PAUSED543544// and pause the stage's music, as well545PauseMusic()546547// SFX dealings548PlaySfx(SFX_G_SELECT, false)549StopSfx(SFX_G_FLYING)550StopSfx(SFX_G_TIRED)551552// Create the Pause Menu and set it up as needed553Object[9].Type = TypeName[Pause Menu]554Object[9].DrawOrder = 6555Object[9].Priority = PRIORITY_ALWAYS556557// Turn the floor into low-quality 3d558TileLayer[0].Type = LAYER_3DFLOOR559560else561562// Call the engine's built-in pause menu563EngineCallback(CALLBACK_PAUSE_REQUESTED)564565// It may be worth noting, though, that when using the fan decompilation of the game, the Pause Menu callback566// just becomes a roundabout way of calling the in-game Pause Menu Object anyway...567568end if569end if570end if571end if572#endplatform573end if // Options.AttractMode == false574575end function576577578function Sonic_HandleMovement579580// Update Sonic's tilt based on what directions the player is currently holding581// Left takes priority over right, similarly to the maingame582if Player.Left == true583SSSonic.Tilt--584585// Min tilt of -8586if SSSonic.Tilt < -8587SSSonic.Tilt = -8588end if589else590if Player.Right == true591SSSonic.Tilt++592593// Max tilt of 8594if SSSonic.Tilt > 8595SSSonic.Tilt = 8596end if597else598// Neither left nor right are held, restore Sonic's tilt to neutral position599600if SSSonic.Tilt > 0601SSSonic.Tilt--602end if603604if SSSonic.Tilt < 0605SSSonic.Tilt++606end if607end if608end if609610if Player.Left == true611SSSonic.Angle += 2612end if613614if Player.Right == true615SSSonic.Angle -= 2616end if617618if SSSonic.Angle < 0619// It's worth noting, Angle uses a 512-based value, which is why this is "allowed"620SSSonic.Angle += 512621end if622623SSSonic.Angle &= 511624625// Update movement based on the player's angle626627Sin(TempValue0, SSSonic.Angle)628TempValue0 *= SSSonic.Speed629TempValue0 >>= 9630SSSonic.XPos += TempValue0631632Cos(TempValue0, SSSonic.Angle)633TempValue0 *= SSSonic.Speed634TempValue0 >>= 9635SSSonic.ZPos += TempValue0636637end function638639640function Sonic_ProcessAnimation641642// This function is used for animating the object, here's a short overview of the values it uses643// (Frame values are in accordance with SpriteFrames set in ObjectStartup)644// -> SSSonic.AnimationSpeed is the speed at which Sonic should animate, it's added to his timer every frame645// -> SSSonic.FrameTimer is the timer used for animating Sonic, think of it akin to Object.AnimationTimer646// - The next frame is triggered whenever the value is 240 or above, the speed is controlled by SSSonic.AnimationSpeed647// - This doesn't get reset whenever the frame transitions, so keep that in mind too648// -> SSSonic.FrameEnd is the final frame of the animation649// -> SSSonic.FrameLoop is the loop point for the animation to go back to after reaching its end650// -> SSSonic.Frame is the frame to be displayed when drawing the object651652// For each animation's corresponding values, check out the ANI_* aliases up above653654SSSonic.FrameTimer += SSSonic.AnimationSpeed655656if SSSonic.FrameTimer > 239657SSSonic.FrameTimer -= 240658659SSSonic.Frame++660if SSSonic.Frame > SSSonic.FrameEnd661SSSonic.Frame = SSSonic.FrameLoop662end if663end if664665end function666667668function Sonic_HandleBumperInteraction669670// Preconditions:671// - TempValue0 is player's truncated XPos, and672// - TempValue1 is truncated ZPos673674// Reset TempValue2, it's gonna get used as a bitfield for where bumper collision has been sensed,675// see below for which bits correspond to what "sensors"676TempValue2 = 0677678// Check upper-left tile, uses the first bit679TempValue0 -= 8680TempValue1 -= 8681Get16x16TileInfo(CheckResult, TempValue0, TempValue1, TILEINFO_ANGLEA)682if CheckResult == 3683SetBit(TempValue2, 0, true)684end if685686// Check upper-right tile, uses the second bit687TempValue0 += 16688Get16x16TileInfo(CheckResult, TempValue0, TempValue1, TILEINFO_ANGLEA)689if CheckResult == 3690SetBit(TempValue2, 1, true)691end if692693// Check bottom left tile, uses the third bit694TempValue0 -= 16695TempValue1 += 16696Get16x16TileInfo(CheckResult, TempValue0, TempValue1, TILEINFO_ANGLEA)697if CheckResult == 3698SetBit(TempValue2, 2, true)699end if700701// Check bottom right tile, uses the fourth bit702TempValue0 += 16703Get16x16TileInfo(CheckResult, TempValue0, TempValue1, TILEINFO_ANGLEA)704if CheckResult == 3705SetBit(TempValue2, 3, true)706end if707708// Was any collision sensed?709if TempValue2 > 0710TempValue3 = SSSonic.Speed711TempValue3 += 0x10000712713if SSSonic.Timer != 32714PlayStageSfx(SFX_S_BUMPER2, false)715end if716717SSSonic.Timer = 32718719// Jump to the corresponding collision match, each match is labelled in accordance with what sensors are triggered720721switch TempValue2722case 1 // Upper left tile only723SSSonic.ScreenXPos = TempValue3724SSSonic.ScreenYPos = TempValue3725break726727case 2 // Upper right tile only728SSSonic.ScreenXPos = TempValue3729SSSonic.ScreenYPos = TempValue3730FlipSign(SSSonic.ScreenXPos)731break732733case 3 // Upper left tile and upper right734SSSonic.ScreenXPos = 0735SSSonic.ScreenYPos = TempValue3736break737738case 4 // Bottom left tile only739SSSonic.ScreenXPos = TempValue3740SSSonic.ScreenYPos = TempValue3741FlipSign(SSSonic.ScreenYPos)742break743744case 5 // Upper left tile and bottom left tile745SSSonic.ScreenXPos = TempValue3746SSSonic.ScreenYPos = 0747break748749case 6 // Upper right tile and bottom left tile750case 7 // Upper left tile, upper right tile, and bottom left tile751SSSonic.ScreenXPos = TempValue3752SSSonic.ScreenYPos = TempValue3753break754755case 8 // Bottom right tile only756case 14 // Upper right tile, bottom left tile, and bottom right tile757case 15 // Upper left tile, upper right tile, bottom left tile, and bottom right tile758SSSonic.ScreenXPos = TempValue3759SSSonic.ScreenYPos = TempValue3760FlipSign(SSSonic.ScreenXPos)761FlipSign(SSSonic.ScreenYPos)762break763764case 9 // Upper left tile and bottom right tile765case 11 // Upper left tile, upper right tile, and bottom right tile766SSSonic.ScreenXPos = TempValue3767SSSonic.ScreenYPos = TempValue3768FlipSign(SSSonic.ScreenXPos)769break770771case 10 // Upper right tile and bottom right tile772SSSonic.ScreenXPos = TempValue3773SSSonic.ScreenYPos = 0774FlipSign(SSSonic.ScreenXPos)775break776777case 12 // Bottom left tile and bottom right tile778SSSonic.ScreenXPos = 0779SSSonic.ScreenYPos = TempValue3780FlipSign(SSSonic.ScreenYPos)781break782783case 13 // Upper left tile, bottom left tile, and bottom right tile784SSSonic.ScreenXPos = TempValue3785SSSonic.ScreenYPos = TempValue3786FlipSign(SSSonic.ScreenYPos)787break788789end switch790end if791792end function793794795function Sonic_HandleTileInteractions796797// Get the player's truncated XPos and ZPos for tile collision purposes798TempValue0 = SSSonic.XPos799TempValue0 >>= 16800TempValue1 = SSSonic.ZPos801TempValue1 >>= 16802803// Pass the results over to the bumper function for bumping interations804CallFunction(Sonic_HandleBumperInteraction)805806// Get the player's truncated XPos and ZPos for tile collision purposes (again)807TempValue0 = SSSonic.XPos808TempValue0 >>= 16809TempValue1 = SSSonic.ZPos810TempValue1 >>= 16811812// Find what tile type the player is currently standing on813Get16x16TileInfo(CheckResult, TempValue0, TempValue1, TILEINFO_ANGLEA)814switch CheckResult815case 1816// Offroad tile, slow down Sonic and kick up some dust817Object[3].Type = TypeName[Dust Puff]818819// Note that we're giving it a Draw Order of 4, not only does this make it draw above Sonic but820// more importantly this makes it so that it won't be treated as a 3d object821Object[3].DrawOrder = 4822823if HUD[4].SpeedShoes == 0824if SSSonic.Speed > 0x28000825// While on dust without speed shoes, the maximum speed is 2.5 px per frame826SSSonic.Speed = 0x28000827end if828else829if SSSonic.Speed > 0x50000830// While on dust *with* speed shoes, the maximum speed is now bumped up to 5 px per frame831SSSonic.Speed = 0x50000832end if833end if834break835836case 2837// Water tile, slow down the player and make a splash838Object[3].Type = TypeName[WaterSplash]839Object[3].DrawOrder = 4840841// Give the water splash a Z Pos of just a tad bit lower than Sonic, in order to make it draw behind him842Object[3].ScreenDepth = 0x57FE843844if HUD[4].SpeedShoes > 0845if SSSonic.Speed > 0x50000846// If Sonic has speed shoes, then enforce a max speed of 5px per frame847SSSonic.Speed = 0x50000848end if849end if850break851852// 3, the value used by the bumper tiles, is skipped here853// It's handled in Sonic_HandleBumperInteraction instead, called above854855case 4856// Ouch, a Cruncher!857// Make Sonic fall858859SSSonic.State = SSSONIC_TRIPPED860SSSonic.Timer = 136861862SSSonic.Frame = ANI_TRIP_START863SSSonic.FrameLoop = ANI_TRIP_LOOP864SSSonic.FrameEnd = ANI_TRIP_END865866SSSonic.AnimationSpeed = 40867SSSonic.FrameTimer = 0868869// Go to a speed of 1px per frame870SSSonic.Speed = 0x10000871872if HUD[4].Rings > 0873PlaySfx(SFX_G_LOSERINGS, false)874875// Cut the player's rings in half876TempValue0 = HUD[4].Rings877HUD[4].Rings >>= 1878879// Find how many rings were lost880TempValue0 -= HUD[4].Rings881882// Max of 8 rings can be dropped883if TempValue0 > 8884TempValue0 = 8885end if886887// If Sonic has 0 rings now, then reset the UFO streak888if HUD[4].Rings == 0889HUD[4].LastUFOType = -1890endif891892// Create all the dropped rings893while TempValue0 > 0894CreateTempObject(TypeName[Ring], 0, SSSonic.XPos, 0)895Object[TempObjectPos].Priority = PRIORITY_ACTIVE896897// Move the Ring to Sonic898// (XPos is matched already via object spawning function)899Ring[TempObjectPos].ZPos = SSSonic.ZPos900901// Randomise the X Velocity902Rand(TempValue1, 64)903TempValue1 -= 32904TempValue1 <<= 10905Ring[TempObjectPos].XVelocity = TempValue1906907// Make the Rings fall in the general direction of Sonic, though908Sin(TempValue1, SSSonic.Angle)909TempValue1 *= 96910Ring[TempObjectPos].XVelocity += TempValue1911912// Randomise the Y Velocity as well913Rand(TempValue1, 64)914TempValue1 += 32915TempValue1 <<= 12916Ring[TempObjectPos].YVelocity = TempValue1917918// No further stuff needed for Y Velocity since there's really only one way for them to go - up!919920// Randomise the Ring's Z Velocity too921Rand(TempValue1, 64)922TempValue1 -= 32923TempValue1 <<= 10924Ring[TempObjectPos].ZVelocity = TempValue1925926// And make it somewhat match Sonic's direction927Cos(TempValue1, SSSonic.Angle)928TempValue1 *= 96929Ring[TempObjectPos].ZVelocity += TempValue1930931TempValue0--932loop933end if934break935936case 5937// Spring, send the player up, up, and away into the skies938SSSonic.State = SSSONIC_SPRING939SSSonic.Timer = 0940941SSSonic.Frame = ANI_JUMPING_START942SSSonic.FrameLoop = ANI_JUMPING_LOOP943SSSonic.FrameEnd = ANI_JUMPING_END944945SSSonic.AnimationSpeed = 80946SSSonic.FrameTimer = 0947948// Ascending at a rate of 8.75 pixels per frame949SSSonic.YVelocity = 0x8C000950951PlaySfx(SFX_G_SPRING, false)952break953954case 6955// Fan, starting gliding956SSSonic.State = SSSONIC_FAN957SSSonic.Timer = 0958959SSSonic.Frame = ANI_FAN_START960SSSonic.FrameLoop = ANI_FAN_LOOP961SSSonic.FrameEnd = ANI_FAN_END962963SSSonic.AnimationSpeed = 24964SSSonic.FrameTimer = 0965966// Fans don't give that much upwards boost, only giving a starting velocity of 2.5 pixels per frame967SSSonic.YVelocity = 0x28000968969PlayStageSfx(SFX_S_FAN, false)970break971972case 7973// Large arrow booster pad, facing left974SSSonic.Timer = 40975SSSonic.ScreenXPos = -0xC0000976SSSonic.ScreenYPos = 0977978// Get the tile's flip directions979Get16x16TileInfo(CheckResult, TempValue0, TempValue1, TILEINFO_DIRECTION)980981// Flip Sonic's direction if needed982if CheckResult == 1983FlipSign(SSSonic.ScreenXPos)984else985if CheckResult == 3986FlipSign(SSSonic.ScreenXPos)987end if988end if989990if SSSonic.State != SSSONIC_SPEEDBOOSTER991PlayStageSfx(SFX_S_LARGEBOOSTER, false)992end if993994SSSonic.State = SSSONIC_SPEEDBOOSTER995996SSSonic.Frame = ANI_BRAKING_START997SSSonic.FrameLoop = ANI_BRAKING_LOOP998SSSonic.FrameEnd = ANI_BRAKING_END9991000SSSonic.AnimationSpeed = 241001SSSonic.FrameTimer = 01002break10031004case 81005// Large arrow booster pad, facing right1006SSSonic.Timer = 401007SSSonic.ScreenXPos = 0xC00001008SSSonic.ScreenYPos = 010091010// Get the tile's flip directions1011Get16x16TileInfo(CheckResult, TempValue0, TempValue1, TILEINFO_DIRECTION)10121013// Flip Sonic's direction if needed1014if CheckResult == 11015FlipSign(SSSonic.ScreenXPos)1016else1017if CheckResult == 31018FlipSign(SSSonic.ScreenXPos)1019end if1020end if10211022if SSSonic.State != SSSONIC_SPEEDBOOSTER1023PlayStageSfx(SFX_S_LARGEBOOSTER, false)1024end if10251026SSSonic.State = SSSONIC_SPEEDBOOSTER10271028SSSonic.Frame = ANI_BRAKING_START1029SSSonic.FrameLoop = ANI_BRAKING_LOOP1030SSSonic.FrameEnd = ANI_BRAKING_END10311032SSSonic.AnimationSpeed = 241033SSSonic.FrameTimer = 01034break10351036case 91037// Large arrow booster pad, facing up1038SSSonic.Timer = 401039SSSonic.ScreenXPos = 01040SSSonic.ScreenYPos = -0xC000010411042// Get the tile's flip directions1043Get16x16TileInfo(CheckResult, TempValue0, TempValue1, TILEINFO_DIRECTION)10441045// Flip Sonic's direction if needed1046if CheckResult == 21047FlipSign(SSSonic.ScreenYPos)1048else1049if CheckResult == 31050FlipSign(SSSonic.ScreenYPos)1051end if1052end if10531054if SSSonic.State != SSSONIC_SPEEDBOOSTER1055PlayStageSfx(SFX_S_LARGEBOOSTER, false)1056end if10571058SSSonic.State = SSSONIC_SPEEDBOOSTER10591060SSSonic.Frame = ANI_BRAKING_START1061SSSonic.FrameLoop = ANI_BRAKING_LOOP1062SSSonic.FrameEnd = ANI_BRAKING_END10631064SSSonic.AnimationSpeed = 241065SSSonic.FrameTimer = 01066break10671068case 101069// Large arrow booster pad, facing down1070SSSonic.Timer = 401071SSSonic.ScreenXPos = 01072SSSonic.ScreenYPos = 0xC000010731074// Get the tile's flip directions1075Get16x16TileInfo(CheckResult, TempValue0, TempValue1, TILEINFO_DIRECTION)10761077// Flip Sonic's direction if needed1078if CheckResult == 21079FlipSign(SSSonic.ScreenYPos)1080else1081if CheckResult == 31082FlipSign(SSSonic.ScreenYPos)1083end if1084end if10851086if SSSonic.State != SSSONIC_SPEEDBOOSTER1087PlayStageSfx(SFX_S_LARGEBOOSTER, false)1088end if10891090SSSonic.State = SSSONIC_SPEEDBOOSTER10911092SSSonic.Frame = ANI_BRAKING_START1093SSSonic.FrameLoop = ANI_BRAKING_LOOP1094SSSonic.FrameEnd = ANI_BRAKING_END10951096SSSonic.AnimationSpeed = 241097SSSonic.FrameTimer = 01098break10991100case 111101// Small arrow booster pad, facing left1102if SSSonic.Timer != 161103PlayStageSfx(SFX_S_SMALLBOOSTER, false)1104end if11051106SSSonic.Timer = 161107SSSonic.ScreenXPos = -0x800001108SSSonic.ScreenYPos = 011091110// Get the tile's flip directions1111Get16x16TileInfo(CheckResult, TempValue0, TempValue1, TILEINFO_DIRECTION)11121113// Flip Sonic's direction if needed1114if CheckResult == 11115FlipSign(SSSonic.ScreenXPos)1116else1117if CheckResult == 31118FlipSign(SSSonic.ScreenXPos)1119end if1120end if1121break11221123case 121124// Small arrow booster pad, facing right1125if SSSonic.Timer != 161126PlayStageSfx(SFX_S_SMALLBOOSTER, false)1127end if11281129SSSonic.Timer = 161130SSSonic.ScreenXPos = 0x800001131SSSonic.ScreenYPos = 011321133// Get the tile's flip directions1134Get16x16TileInfo(CheckResult, TempValue0, TempValue1, TILEINFO_DIRECTION)11351136// Flip Sonic's direction if needed1137if CheckResult == 11138FlipSign(SSSonic.ScreenXPos)1139else1140if CheckResult == 31141FlipSign(SSSonic.ScreenXPos)1142end if1143end if1144break11451146case 131147// Small arrow booster pad, facing up1148if SSSonic.Timer != 161149PlayStageSfx(SFX_S_SMALLBOOSTER, false)1150end if11511152SSSonic.Timer = 161153SSSonic.ScreenXPos = 01154SSSonic.ScreenYPos = -0x8000011551156// Get the tile's flip directions1157Get16x16TileInfo(CheckResult, TempValue0, TempValue1, TILEINFO_DIRECTION)11581159// Flip Sonic's direction if needed1160if CheckResult == 21161FlipSign(SSSonic.ScreenYPos)1162else1163if CheckResult == 31164FlipSign(SSSonic.ScreenYPos)1165end if1166end if1167break11681169case 141170// Small arrow booster pad, facing down1171if SSSonic.Timer != 161172PlayStageSfx(SFX_S_SMALLBOOSTER, false)1173end if11741175SSSonic.Timer = 161176SSSonic.ScreenXPos = 01177SSSonic.ScreenYPos = 0x8000011781179// Get the tile's flip directions1180Get16x16TileInfo(CheckResult, TempValue0, TempValue1, TILEINFO_DIRECTION)11811182// Flip Sonic's direction if needed1183if CheckResult == 21184FlipSign(SSSonic.ScreenYPos)1185else1186if CheckResult == 31187FlipSign(SSSonic.ScreenYPos)1188end if1189end if1190break11911192end switch11931194end function119511961197sub ObjectMain1198// Update Speed Shoes, the value's stored within the HUD object for whatever reason1199if HUD[4].SpeedShoes > 01200HUD[4].SpeedShoes--1201end if12021203switch SSSonic.State1204case SSSONIC_INTROPOSE1205CallFunction(Sonic_ProcessAnimation)1206SSSonic.Timer++1207if SSSonic.Timer == 1201208// Stop posing and start turning around1209SSSonic.Timer = 012101211SSSonic.State = SSSONIC_INTROTURNAROUND12121213SSSonic.Frame = ANI_FACINGAHEAD_START1214SSSonic.FrameLoop = ANI_FACINGAHEAD_LOOP1215SSSonic.FrameEnd = ANI_FACINGAHEAD_END12161217SSSonic.FrameTimer = 01218SSSonic.AnimationSpeed = 201219end if1220break12211222case SSSONIC_INTROTURNAROUND1223CallFunction(Sonic_ProcessAnimation)1224SSSonic.Timer++1225if SSSonic.Timer == 1401226// Fully turned around now and ready to go!1227SSSonic.Timer = 012281229// Start advancing ahead1230SSSonic.State = SSSONIC_WALKING12311232SSSonic.Frame = ANI_WALKING_START1233SSSonic.FrameLoop = ANI_WALKING_LOOP1234SSSonic.FrameEnd = ANI_WALKING_END12351236SSSonic.Speed = 01237end if1238break12391240case SSSONIC_WALKING1241CallFunction(Sonic_ProcessPlayer)1242if SSSonic.Speed < 0x500001243SSSonic.Speed += 0x08001244else1245SSSonic.Speed = 0x500001246end if12471248if KeyDown[1].Down == true1249SSSonic.Speed -= 0x0C0012501251if SSSonic.Speed < 0x1E0001252SSSonic.Speed = 0x1E0001253end if1254end if12551256// Sonic's animation speed is dependant on his actual speed1257SSSonic.AnimationSpeed = SSSonic.Speed1258SSSonic.AnimationSpeed *= 151259SSSonic.AnimationSpeed /= 0x140001260SSSonic.AnimationSpeed += 2012611262CallFunction(Sonic_HandleMovement)1263CallFunction(Sonic_ProcessAnimation)12641265// Update Sonic's gimmick interaction timer1266if SSSonic.Timer > 01267SSSonic.Timer--1268SSSonic.XPos += SSSonic.ScreenXPos1269SSSonic.ZPos += SSSonic.ScreenYPos1270end if12711272CallFunction(Sonic_HandleTileInteractions)12731274// Check for jumping1275if Player.JumpPress == true1276SSSonic.State = SSSONIC_JUMPING1277SSSonic.Timer = 01278SSSonic.Frame = ANI_JUMPING_START1279SSSonic.FrameLoop = ANI_JUMPING_LOOP1280SSSonic.FrameEnd = ANI_JUMPING_END1281SSSonic.FrameTimer = 01282SSSonic.AnimationSpeed = 801283SSSonic.YVelocity = 0x460001284PlaySfx(SFX_G_JUMP, false)1285end if1286break12871288case SSSONIC_JUMPING1289CallFunction(Sonic_ProcessPlayer)1290if SSSonic.Speed < 0x500001291SSSonic.Speed += 0x08001292end if12931294if KeyDown[1].Down == true1295SSSonic.Speed -= 0x0C0012961297if SSSonic.Speed < 0x1E0001298SSSonic.Speed = 0x1E0001299end if1300end if13011302if Player.JumpHold == false1303if SSSonic.YVelocity > 0x2A0001304SSSonic.YVelocity = 0x2A0001305end if1306end if13071308CallFunction(Sonic_HandleMovement)1309CallFunction(Sonic_ProcessAnimation)13101311// Gravity of 0.125 per frame1312SSSonic.YVelocity -= 0x200013131314SSSonic.YPos += SSSonic.YVelocity13151316// Touched the ground?1317if SSSonic.YPos < 01318SSSonic.YPos = 013191320if HUD[4].UFOsCount > 01321if HUD[4].SpeedShoes == 01322SSSonic.State = SSSONIC_WALKING13231324SSSonic.Frame = ANI_WALKING_START1325SSSonic.FrameLoop = ANI_WALKING_LOOP1326SSSonic.FrameEnd = ANI_WALKING_END13271328SSSonic.FrameTimer = 01329else1330SSSonic.State = SSSONIC_SPEEDSHOESRUN13311332SSSonic.Frame = ANI_RUN_START1333SSSonic.FrameLoop = ANI_RUN_LOOP1334SSSonic.FrameEnd = ANI_RUN_END13351336SSSonic.AnimationSpeed = 801337SSSonic.FrameTimer = 01338end if1339else1340SSSonic.ControlMode = CONTROLMODE_NONE1341SSSonic.State = SSSONIC_FINISHSTAND1342Stage.TimeEnabled = false1343end if1344end if1345break13461347case SSSONIC_SPEEDBOOSTER1348CallFunction(Sonic_ProcessPlayer)13491350if SSSonic.Speed < 0x500001351SSSonic.Speed += 0x08001352end if13531354if KeyDown[1].Down == true1355SSSonic.Speed -= 0x0C0013561357if SSSonic.Speed < 0x1E0001358SSSonic.Speed = 0x1E0001359end if1360end if13611362CallFunction(Sonic_HandleMovement)1363CallFunction(Sonic_ProcessAnimation)13641365// Update the booster timer1366if SSSonic.Timer > 01367SSSonic.Timer--1368SSSonic.XPos += SSSonic.ScreenXPos1369SSSonic.ZPos += SSSonic.ScreenYPos1370else1371// Booster's over, restore the player to normal13721373if HUD[4].SpeedShoes == 01374SSSonic.State = SSSONIC_WALKING13751376SSSonic.Frame = ANI_WALKING_START1377SSSonic.FrameLoop = ANI_WALKING_LOOP1378SSSonic.FrameEnd = ANI_WALKING_END13791380SSSonic.FrameTimer = 01381else1382SSSonic.State = SSSONIC_SPEEDSHOESRUN13831384SSSonic.Frame = ANI_RUN_START1385SSSonic.FrameLoop = ANI_RUN_LOOP1386SSSonic.FrameEnd = ANI_RUN_END13871388SSSonic.AnimationSpeed = 801389SSSonic.FrameTimer = 01390end if13911392end if13931394CallFunction(Sonic_HandleTileInteractions)13951396// See if the player wants to jump out of the boost1397if Player.JumpPress == true1398SSSonic.State = SSSONIC_JUMPING1399SSSonic.Timer = 014001401SSSonic.Frame = ANI_JUMPING_START1402SSSonic.FrameLoop = ANI_JUMPING_LOOP1403SSSonic.FrameEnd = ANI_JUMPING_END14041405SSSonic.AnimationSpeed = 801406SSSonic.YVelocity = 0x4600014071408PlaySfx(SFX_G_JUMP, false)1409end if1410break14111412case SSSONIC_FAN1413CallFunction(Sonic_ProcessPlayer)14141415if SSSonic.Speed < 0x500001416SSSonic.Speed += 0x08001417end if14181419CallFunction(Sonic_HandleMovement)1420CallFunction(Sonic_ProcessAnimation)14211422// Gravity of 0.03125 pixels per frame, the fan provides some air resistance to lessen the normal gravity from 0.125 pixels per frame1423SSSonic.YVelocity -= 0x080014241425SSSonic.YPos += SSSonic.YVelocity14261427// Touched the ground?1428if SSSonic.YPos < 01429SSSonic.YPos = 014301431if HUD[4].UFOsCount > 01432if HUD[4].SpeedShoes == 01433SSSonic.State = SSSONIC_WALKING14341435SSSonic.Frame = ANI_WALKING_START1436SSSonic.FrameLoop = ANI_WALKING_LOOP1437SSSonic.FrameEnd = ANI_WALKING_END14381439SSSonic.FrameTimer = 01440else1441SSSonic.State = SSSONIC_SPEEDSHOESRUN14421443SSSonic.Frame = ANI_RUN_START1444SSSonic.FrameLoop = ANI_RUN_LOOP1445SSSonic.FrameEnd = ANI_RUN_END14461447SSSonic.AnimationSpeed = 801448SSSonic.FrameTimer = 01449end if1450else1451SSSonic.State = SSSONIC_FINISHSTAND1452SSSonic.ControlMode = CONTROLMODE_NONE1453Stage.TimeEnabled = false1454end if1455end if1456break14571458case SSSONIC_TRIPPED1459CallFunction(Sonic_HandlePause)14601461// Lock movement1462Player.Left = false1463Player.Right = false14641465CallFunction(Sonic_HandleMovement)1466CallFunction(Sonic_ProcessAnimation)14671468// Get the player's truncated XPos and ZPos14691470TempValue0 = SSSonic.XPos1471TempValue0 >>= 1614721473TempValue1 = SSSonic.ZPos1474TempValue1 >>= 1614751476Get16x16TileInfo(CheckResult, TempValue0, TempValue1, TILEINFO_ANGLEA)14771478// If the player's hit a bumper tile, then get up and start walking again1479if CheckResult == 31480SSSonic.State = SSSONIC_WALKING14811482SSSonic.Frame = ANI_WALKING_START1483SSSonic.FrameLoop = ANI_WALKING_LOOP1484SSSonic.FrameEnd = ANI_WALKING_END14851486SSSonic.FrameTimer = 01487end if14881489if SSSonic.Timer > 01490SSSonic.Timer--1491else1492if HUD[4].SpeedShoes == 01493SSSonic.State = SSSONIC_WALKING14941495SSSonic.Frame = ANI_WALKING_START1496SSSonic.FrameLoop = ANI_WALKING_LOOP1497SSSonic.FrameEnd = ANI_WALKING_END14981499SSSonic.FrameTimer = 01500else1501SSSonic.State = SSSONIC_SPEEDSHOESRUN15021503SSSonic.Frame = ANI_RUN_START1504SSSonic.FrameLoop = ANI_RUN_LOOP1505SSSonic.FrameEnd = ANI_RUN_END15061507SSSonic.AnimationSpeed = 801508SSSonic.FrameTimer = 01509end if1510end if1511break15121513case SSSONIC_SPEEDSHOESRUN1514CallFunction(Sonic_ProcessPlayer)15151516if SSSonic.Speed < 0x700001517SSSonic.Speed += 0x10001518end if15191520if KeyDown[1].Down == true1521SSSonic.Speed -= 0x0C0015221523if SSSonic.Speed < 0x1E0001524SSSonic.Speed = 0x1E0001525end if1526end if15271528CallFunction(Sonic_HandleMovement)1529CallFunction(Sonic_ProcessAnimation)15301531if SSSonic.Timer > 01532SSSonic.Timer--1533SSSonic.XPos += SSSonic.ScreenXPos1534SSSonic.ZPos += SSSonic.ScreenYPos1535end if15361537CallFunction(Sonic_HandleTileInteractions)15381539if Player.JumpPress == true1540SSSonic.State = SSSONIC_JUMPING1541SSSonic.Timer = 015421543SSSonic.Frame = ANI_JUMPING_START1544SSSonic.FrameLoop = ANI_JUMPING_LOOP1545SSSonic.FrameEnd = ANI_JUMPING_END15461547SSSonic.FrameTimer = 01548SSSonic.AnimationSpeed = 801549SSSonic.YVelocity = 0x4600015501551PlaySfx(SFX_G_JUMP, false)1552end if15531554if HUD[4].SpeedShoes == 01555SSSonic.State = SSSONIC_WALKING15561557SSSonic.Frame = ANI_WALKING_START1558SSSonic.FrameLoop = ANI_WALKING_LOOP1559SSSonic.FrameEnd = ANI_WALKING_END15601561SSSonic.FrameTimer = 01562end if1563break15641565case SSSONIC_FINISHSTAND1566SSSonic.Frame = ANI_STANDING_START1567SSSonic.Timer = 01568SSSonic.Speed = 01569break15701571case SSSONIC_CAMERAPAN1572// This state is given to Sonic from the Time Stone object, not from himself15731574if SSSonic.Timer < 1281575SSSonic.Timer++1576SSSonic.Angle -= 21577if SSSonic.Angle < 01578SSSonic.Angle += 5121579end if1580else1581if Object[3].Type == TypeName[Blank Object]1582// Spawn the Time Stone and place it 24 pixels above the screen1583ResetObjectEntity(3, TypeName[Time Stone], 0, 0, -0x180000)1584Object[3].iXPos = Screen.CenterX1585Object[3].Priority = PRIORITY_ACTIVE1586end if1587end if15881589// Make Sonic's rotation based on how far into the pan we are1590// -> 81 is the starting Sprite Frame ID of the rotation frames1591SSSonic.Frame = SSSonic.Timer1592SSSonic.Frame >>= 41593SSSonic.Frame += 811594break15951596case SSSONIC_STONEGRABBED1597if SSSonic.Timer == 3081598// Spawn the Stage Results, as the "TIME STONES" variant15991600Object[30].Type = TypeName[Stage Finish]1601Object[30].ResultsType = STAGEFINISH_T_STONEOBTAINED1602#platform: Use_Origins1603Object[30].DrawOrder = 6 // Make sure this is on the right draw layer, as it needs to render correctly in Mirror Mode.1604#endplatform1605else1606SSSonic.Timer++1607end if1608break16091610case SSSONIC_SPRING1611CallFunction(Sonic_ProcessPlayer)16121613if SSSonic.Speed < 0x500001614SSSonic.Speed += 0x08001615end if16161617if KeyDown[1].Down == true1618SSSonic.Speed -= 0x0C0016191620if SSSonic.Speed < 0x1E0001621SSSonic.Speed = 0x1E0001622end if1623end if16241625CallFunction(Sonic_HandleMovement)1626CallFunction(Sonic_ProcessAnimation)16271628// Update gravity with an eight of a pixel per frame as a gravity value1629SSSonic.YVelocity -= 0x20001630SSSonic.YPos += SSSonic.YVelocity16311632if SSSonic.YPos < 01633SSSonic.YPos = 01634if HUD[4].UFOsCount > 01635if HUD[4].SpeedShoes == 01636SSSonic.State = SSSONIC_WALKING16371638SSSonic.Frame = ANI_WALKING_START1639SSSonic.FrameLoop = ANI_WALKING_LOOP1640SSSonic.FrameEnd = ANI_WALKING_END16411642SSSonic.FrameTimer = 01643else1644SSSonic.State = SSSONIC_SPEEDSHOESRUN16451646SSSonic.Frame = ANI_RUN_START1647SSSonic.FrameLoop = ANI_RUN_LOOP1648SSSonic.FrameEnd = ANI_RUN_END16491650SSSonic.AnimationSpeed = 801651SSSonic.FrameTimer = 01652end if1653else1654SSSonic.ControlMode = CONTROLMODE_NONE1655SSSonic.State = SSSONIC_FINISHSTAND1656end if1657end if1658break16591660end switch16611662// Enforce stage bounds1663// These stage size values are set by the stage's BGEffects object on Startup16641665if SSSonic.XPos > Stage.XBoundary21666SSSonic.XPos = Stage.XBoundary21667end if16681669if SSSonic.XPos < Stage.XBoundary11670SSSonic.XPos = Stage.XBoundary11671end if16721673// The Stage Bounds are setup for a 2d plane, which is why ZPos is being paired with the "Y" Bounds here1674if SSSonic.ZPos > Stage.YBoundary21675SSSonic.ZPos = Stage.YBoundary21676end if16771678if SSSonic.ZPos < Stage.YBoundary11679SSSonic.ZPos = Stage.YBoundary11680end if16811682// So we're kind of not actually moving around a 3d stage - instead, we're moving the entire world around Sonic1683// So do the calculations for that16841685TileLayer[0].Angle = SSSonic.Angle16861687// X/Z movement1688Sin(TileLayer[0].XPos, TileLayer[0].Angle)1689Cos(TileLayer[0].ZPos, TileLayer[0].Angle)1690TileLayer[0].XPos *= -0x2C001691TileLayer[0].ZPos *= -0x2C001692TileLayer[0].XPos += SSSonic.XPos1693TileLayer[0].ZPos += SSSonic.ZPos16941695// Y movement is much easier, no complex calcuations needed here1696// Just take Sonic's Y Position, smooth it out a bit, and offset it by 88 pixels1697TileLayer[0].YPos = SSSonic.YPos1698TileLayer[0].YPos /= 31699TileLayer[0].YPos += 0x58000017001701end sub170217031704sub ObjectDraw1705// First draw Sonic's shadow17061707// Find his the ground position to draw the shadow on1708TempValue0 = TileLayer[0].YPos1709TempValue0 >>= 81710TempValue0 *= 961711TempValue0 /= 0x58001712TempValue0 += 12817131714// And now draw the shadow at that given spot1715DrawSpriteScreenXY(0, Screen.CenterX, TempValue0)17161717// And now draw Sonic himself17181719// Get the Y position to draw him at1720TempValue0 = TileLayer[0].YPos1721TempValue0 -= SSSonic.YPos1722TempValue0 >>= 81723TempValue0 *= 961724TempValue0 /= 0x58001725TempValue0 += 12817261727// If in the walking animation, jump to its corresponding special drawing code,1728// otherwise just do the standard drawing sprite routine1729switch SSSonic.State1730default1731// Just draw Sonic's sprite, nothing special needed here1732DrawSpriteScreenXY(SSSonic.Frame, Screen.CenterX, TempValue0)1733break17341735case SSSONIC_WALKING1736// Bump Sonic's sprite based on how "tilted" he is17371738TempValue1 = SSSonic.Frame17391740TempValue2 = SSSonic.Tilt1741TempValue2 >>= 21742TempValue2 += 217431744// And now jump to the result1745// Alternate SpriteFrames are used, as well as sprite flipping too1746switch TempValue21747case 01748TempValue1 += 61749SSSonic.Direction = FACING_LEFT1750break17511752case 11753TempValue1 += 121754SSSonic.Direction = FACING_LEFT1755break17561757case 21758SSSonic.Direction = FACING_RIGHT1759break17601761case 31762TempValue1 += 181763SSSonic.Direction = FACING_RIGHT1764break17651766case 41767TempValue1 += 241768SSSonic.Direction = FACING_RIGHT1769break17701771end switch17721773// And now draw the result sprite from that1774DrawSpriteScreenFX(TempValue1, FX_FLIP, Screen.CenterX, TempValue0)1775break17761777end switch17781779end sub178017811782sub ObjectStartup17831784// Load the correct sprite sheet based on the current player17851786if Stage.PlayerListPos == PLAYER_SONIC_A // PLAYER_SONIC1787LoadSpriteSheet("Special/Sonic.gif")1788end if1789if Stage.PlayerListPos == PLAYER_TAILS_A // PLAYER_TAILS in origins1790LoadSpriteSheet("Special/Tails.gif")1791end if1792#platform: Use_Origins1793if Stage.PlayerListPos == PLAYER_KNUCKLES1794LoadSpriteSheet("Special/Knuckles.gif")1795end if1796if Stage.PlayerListPos == PLAYER_AMY1797LoadSpriteSheet("Special/Amy.gif")1798end if1799#endplatform18001801// Place a Sonic object into reserved object slot 2 and initialise its values1802SSSonic[2].Type = TypeName[Sonic]18031804// Make Sonic always active1805SSSonic[2].Priority = PRIORITY_ACTIVE18061807// Give him a standard draw order depth, since he's just about in the middle of the screen1808SSSonic[2].ScreenDepth = 0x580018091810// Start with him doing his intro pose, pretty fancy1811SSSonic[2].Frame = ANI_INTROPOSE_START1812SSSonic[2].FrameLoop = ANI_INTROPOSE_LOOP1813SSSonic[2].FrameEnd = ANI_INTROPOSE_END1814SSSonic[2].AnimationSpeed = 3018151816// Cycle through the scene to find all Sonic objects1817ArrayPos0 = 321818while ArrayPos0 < 10561819if Object[ArrayPos0].Type == TypeName[Sonic]18201821// Transfer this placed Sonic object's values to the main Sonic object18221823SSSonic.XPos = Object[ArrayPos0].XPos18241825// Due to the difference between editor and game, turn YPos into ZPos here1826SSSonic[2].ZPos = Object[ArrayPos0].YPos18271828// And then reset YPos, so that he's starting on the floor1829SSSonic.YPos = 018301831// PropertyValue is Sonic's starting direction1832// Sonic's PropertyValue is normally always 0xC0, which is facing left1833SSSonic.Angle = Object[ArrayPos0].PropertyValue1834SSSonic.Angle <<= 118351836// Remove this placed Sonic object from the scene set of objects,1837// as it's been moved to a reserved object slot now1838ResetObjectEntity(ArrayPos0, TypeName[Blank Object], 0, 0, 0)18391840end if18411842ArrayPos0++1843loop18441845// Player Frames1846// Refer to the ANI_* constants too, those are a good outline of what these SpriteFrames are18471848// 0 - Shadow Frame1849SpriteFrame(-20, -4, 40, 8, 210, 377)18501851// 1 - Standing Frame1852SpriteFrame(-20, -48, 40, 48, 1, 197)18531854// 2-4 - Turning Ahead Frames1855SpriteFrame(-20, -48, 40, 48, 83, 197)1856SpriteFrame(-20, -48, 40, 48, 42, 197)1857SpriteFrame(-20, -48, 40, 48, 1, 197)18581859// 5-8 - Intro Animation Frames1860SpriteFrame(-20, -48, 40, 48, 1, 246)1861SpriteFrame(-20, -48, 40, 48, 42, 246)1862SpriteFrame(-20, -48, 40, 48, 83, 246)1863SpriteFrame(-20, -48, 40, 48, 42, 246)18641865// 9-14 - Walking Ahead Frames1866SpriteFrame(-20, -48, 40, 48, 1, 1)1867SpriteFrame(-20, -48, 40, 48, 42, 1)1868SpriteFrame(-20, -48, 40, 48, 83, 1)1869SpriteFrame(-20, -48, 40, 48, 1, 50)1870SpriteFrame(-20, -48, 40, 48, 42, 50)1871SpriteFrame(-20, -48, 40, 48, 83, 50)18721873// 15-20 - Heavily Leaning Right Frames1874SpriteFrame(-20, -48, 40, 48, 1, 99)1875SpriteFrame(-20, -48, 40, 48, 42, 99)1876SpriteFrame(-20, -48, 40, 48, 83, 99)1877SpriteFrame(-20, -48, 40, 48, 1, 148)1878SpriteFrame(-20, -48, 40, 48, 42, 148)1879SpriteFrame(-20, -48, 40, 48, 83, 148)18801881// 21-26 - Lightly Leaning Right Frames1882SpriteFrame(-20, -48, 40, 48, 124, 1)1883SpriteFrame(-20, -48, 40, 48, 165, 1)1884SpriteFrame(-20, -48, 40, 48, 206, 1)1885SpriteFrame(-20, -48, 40, 48, 124, 50)1886SpriteFrame(-20, -48, 40, 48, 165, 50)1887SpriteFrame(-20, -48, 40, 48, 206, 50)18881889// 27-29 - Leaning Right Frames again..?1890// Seems to be unused, perhaps these only exist to pad out the frame number?1891SpriteFrame(-20, -48, 40, 48, 124, 50)1892SpriteFrame(-20, -48, 40, 48, 165, 50)1893SpriteFrame(-20, -48, 40, 48, 206, 50)18941895// 30-35 - Lightly Leaning Right Frames1896SpriteFrame(-20, -48, 40, 48, 124, 1)1897SpriteFrame(-20, -48, 40, 48, 165, 1)1898SpriteFrame(-20, -48, 40, 48, 206, 1)1899SpriteFrame(-20, -48, 40, 48, 1, 148)1900SpriteFrame(-20, -48, 40, 48, 42, 148)1901SpriteFrame(-20, -48, 40, 48, 83, 148)19021903// 36-38 - Heavily Leaning Right Frames1904SpriteFrame(-20, -48, 40, 48, 1, 99)1905SpriteFrame(-20, -48, 40, 48, 42, 99)1906SpriteFrame(-20, -48, 40, 48, 83, 99)19071908// 39-42 - Jumping Animation Frames1909SpriteFrame(-20, -40, 40, 40, 165, 99)1910SpriteFrame(-20, -40, 40, 40, 206, 99)1911SpriteFrame(-20, -40, 40, 40, 124, 140)1912SpriteFrame(-20, -40, 40, 40, 124, 99)19131914// 43-47 - Braking Animation1915SpriteFrame(-20, -48, 40, 48, 51, 344)1916SpriteFrame(-21, -48, 42, 48, 182, 295)1917SpriteFrame(-25, -48, 49, 48, 1, 344)1918if Stage.PlayerListPos == PLAYER_SONIC_A // PLAYER_SONIC in origins1919SpriteFrame(-21, -48, 42, 48, 135, 410)1920SpriteFrame(-20, -48, 40, 48, 178, 410)1921end if1922if Stage.PlayerListPos == PLAYER_TAILS_A // PLAYER_TAILS in origins1923SpriteFrame(-21, -48, 42, 48, 135, 426)1924SpriteFrame(-20, -48, 40, 48, 178, 426)1925end if19261927#platform: Use_Origins1928if Stage.PlayerListPos == PLAYER_KNUCKLES1929SpriteFrame(-21, -48, 42, 48, 135, 410)1930SpriteFrame(-20, -48, 40, 48, 178, 410)1931end if1932if Stage.PlayerListPos == PLAYER_AMY1933SpriteFrame(-21, -48, 42, 48, 135, 410)1934SpriteFrame(-20, -48, 40, 48, 178, 410)1935end if1936#endplatform1937// 48-53 - Fan Animation1938SpriteFrame(-16, -32, 54, 32, 17, 442) // Not accurate to the pink sprite box, it's actually cropped a bit from that1939SpriteFrame(-26, -32, 52, 32, 78, 442) // Same story here, too1940SpriteFrame(-37, -32, 54, 32, 178, 475) // And here...1941SpriteFrame(-32, -32, 48, 32, 1, 475)1942SpriteFrame(-30, -32, 60, 32, 115, 475)1943SpriteFrame(-16, -32, 48, 32, 208, 344)19441945// 54-76 - Falling Down Animation1946SpriteFrame(-28, -48, 56, 48, 92, 344)1947SpriteFrame(-30, -32, 60, 32, 54, 475)19481949// Most Frames in the animation varies between Sonic and Tails1950if Stage.PlayerListPos != PLAYER_TAILS_A1951SpriteFrame(-29, -32, 58, 32, 149, 344)1952SpriteFrame(-30, -32, 60, 32, 149, 377)1953SpriteFrame(-30, -32, 60, 32, 149, 377)1954SpriteFrame(-30, -32, 60, 32, 149, 377)1955SpriteFrame(-30, -32, 60, 32, 149, 377)1956SpriteFrame(-30, -32, 60, 32, 149, 377)1957SpriteFrame(-30, -32, 60, 32, 149, 377)1958SpriteFrame(-30, -32, 60, 32, 149, 377)1959SpriteFrame(-30, -32, 60, 32, 149, 377)1960SpriteFrame(-30, -32, 60, 32, 149, 377)1961SpriteFrame(-30, -32, 60, 32, 149, 377)1962SpriteFrame(-30, -32, 60, 32, 149, 377)1963SpriteFrame(-30, -32, 60, 32, 149, 377)1964SpriteFrame(-30, -32, 60, 32, 149, 377)1965SpriteFrame(-30, -32, 60, 32, 149, 377)1966SpriteFrame(-30, -32, 60, 32, 149, 377)1967SpriteFrame(-30, -32, 60, 32, 149, 377)1968SpriteFrame(-30, -32, 60, 32, 149, 377)1969else1970SpriteFrame(-29, -32, 58, 37, 149, 344)1971SpriteFrame(-30, -32, 60, 32, 149, 382)1972SpriteFrame(-30, -32, 60, 32, 149, 382)1973SpriteFrame(-30, -32, 60, 32, 149, 382)1974SpriteFrame(-30, -32, 60, 32, 149, 382)1975SpriteFrame(-30, -32, 60, 32, 149, 382)1976SpriteFrame(-30, -32, 60, 32, 149, 382)1977SpriteFrame(-30, -32, 60, 32, 149, 382)1978SpriteFrame(-30, -32, 60, 32, 149, 382)1979SpriteFrame(-30, -32, 60, 32, 149, 382)1980SpriteFrame(-30, -32, 60, 32, 149, 382)1981SpriteFrame(-30, -32, 60, 32, 149, 382)1982SpriteFrame(-30, -32, 60, 32, 149, 382)1983SpriteFrame(-30, -32, 60, 32, 149, 382)1984SpriteFrame(-30, -32, 60, 32, 149, 382)1985SpriteFrame(-30, -32, 60, 32, 149, 382)1986SpriteFrame(-30, -32, 60, 32, 149, 382)1987SpriteFrame(-30, -32, 60, 32, 149, 382)1988end if1989SpriteFrame(-20, -48, 40, 48, 1, 393)1990SpriteFrame(-20, -48, 40, 48, 42, 393)1991SpriteFrame(-20, -48, 40, 48, 83, 393)19921993// 77-80 - Running Frames1994SpriteFrame(-20, -48, 40, 48, 165, 140)1995SpriteFrame(-20, -48, 40, 48, 206, 140)1996SpriteFrame(-20, -48, 40, 48, 165, 189)1997SpriteFrame(-20, -48, 40, 48, 206, 189)19981999// 81-90 - Time Stone Grabbing Frames2000// These aren't used in the traditional animation system, and are instead manually set in SSSONIC_CAMERAPAN2001SpriteFrame(-20, -48, 40, 48, 1, 197)2002SpriteFrame(-20, -48, 40, 48, 124, 246)2003SpriteFrame(-20, -48, 40, 48, 165, 246)2004SpriteFrame(-20, -48, 40, 48, 206, 246)2005SpriteFrame(-20, -48, 40, 48, 1, 295)2006SpriteFrame(-20, -48, 40, 48, 42, 295)2007SpriteFrame(-16, -48, 32, 48, 83, 295)2008SpriteFrame(-16, -48, 32, 48, 116, 295)2009SpriteFrame(-16, -48, 32, 48, 116, 295)2010SpriteFrame(-16, -48, 32, 48, 149, 295)20112012end sub201320142015// ========================2016// Editor Subs2017// ========================20182019sub RSDKEdit2020if Editor.ReturnVariable == true2021switch Editor.VariableID2022case EDIT_VAR_PROPVAL // Property Value2023CheckResult = Object.PropertyValue2024break2025case 0 // StartDir2026CheckResult = Object.PropertyValue2027break2028end switch2029else2030switch Editor.VariableID2031case EDIT_VAR_PROPVAL // Property Value2032Object.PropertyValue = Editor.VariableValue2033break2034case 0 // StartDir2035Object.PropertyValue = Editor.VariableValue2036break2037end switch2038end if2039end sub204020412042sub RSDKDraw2043DrawSprite(0)2044end sub204520462047sub RSDKLoad2048LoadSpriteSheet("Special/Sonic.gif")2049SpriteFrame(-20, -48, 40, 48, 1, 246)20502051AddEditorVariable("StartDir")2052SetActiveVariable("StartDir")2053AddEnumVariable("Up", 0)2054AddEnumVariable("Right", 64)2055AddEnumVariable("Down", 128)2056AddEnumVariable("Left", 192)2057end sub205820592060