diff --git a/src/supaplex.c b/src/supaplex.c
index cd554c1a..62993303 100644
--- a/src/supaplex.c
+++ b/src/supaplex.c
@@ -1,14346 +1,14346 @@
-/*
- * This file is part of the OpenSupaplex distribution (https://github.com/sergiou87/open-supaplex).
- * Copyright (c) 2020 Sergio Padrino
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, version 3.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include "animations.h"
-#include "audio.h"
-#include "buttonBorders.h"
-#include "commandLineParser.h"
-#include "conditionals.h"
-#include "config.h"
-#include "controller.h"
-#include "demo.h"
-#include "file.h"
-#include "globals.h"
-#include "graphics.h"
-#include "input.h"
-#include "keyboard.h"
-#include "logging.h"
-#include "menu.h"
-#include "savegame.h"
-#include "touchscreen.h"
-#include "utils.h"
-#include "video.h"
-#include "virtualKeyboard.h"
-#include "system.h"
-
-#ifdef __PSP__
-#include
-
-// PSP_MODULE_INFO("OpenSupaplex", 0, 7, 1); -> SDL_main sets this for us, for now at least
-PSP_MAIN_THREAD_ATTR(THREAD_ATTR_USER | THREAD_ATTR_VFPU);
-PSP_HEAP_SIZE_KB(-1024);
-#endif
-
-// title1DataBuffer -> A000:4DAC - A000:CAAC
-// title2DataBuffer -> 0x4DD4 - 0xCAD4
-
-// maps are 58 x 22 tiles
-
-typedef enum
-{
- UserInputNone = 0,
- UserInputUp = 1,
- UserInputLeft = 2,
- UserInputDown = 3,
- UserInputRight = 4,
- UserInputSpaceUp = 5,
- UserInputSpaceLeft = 6,
- UserInputSpaceDown = 7,
- UserInputSpaceRight = 8,
- UserInputSpaceOnly = 9,
-} UserInput;
-
-static const uint8_t kUserInputSpaceAndDirectionOffset = (UserInputSpaceUp - 1);
-
-uint8_t byte_50919 = 0;
-uint8_t byte_5091A = 0;
-UserInput gCurrentUserInput = 0; // byte_50941 -> 0x0631
-uint8_t byte_50946 = 0;
-uint16_t word_50947 = 0;
-uint16_t word_50949 = 0;
-uint8_t byte_50953 = 0;
-uint8_t byte_50954 = 0;
-uint8_t gShouldAutoselectNextLevelToPlay = 0; // byte_51ABE
-uint8_t gHasChangedLevelSetFromAdvancedMenu = 0;
-uint8_t byte_58D47 = 0; //
-uint8_t byte_59821 = 0; //
-uint8_t byte_59822 = 0; //
-uint8_t byte_59823 = 0; //
-uint8_t gDemoRecordingRandomGeneratorSeedHigh = 0; // byte_59B5C
-uint8_t gDemoRecordingRandomGeneratorSeedLow = 0; // byte_59B5F
-uint8_t gToggleFancyEasyTilesThrottleCounter = 0; // byte_59B7A -> data_subrest
-uint8_t gIsShowingFancyTiles = 1; // byte_59B7B -> data_subrstflg
-uint8_t gToggleGravityAutorepeatFlag = 0; // byte_59B7C
-uint8_t gToggleZonksFrozenAutorepeatFlag = 0; // byte_59B7D
-uint8_t gToggleEnemiesFrozenAutorepeatFlag = 0; // byte_59B7E
-uint8_t gDebugSkipPreviousLevelAutorepeatFlag_1 = 0; // byte_59B7F
-uint8_t gDebugSkipPreviousLevelAutorepeatFlag_2 = 0; // byte_59B80
-uint8_t gDebugSkipNextLevelAutorepeatFlag_1 = 0; // byte_59B81
-uint8_t gDebugSkipNextLevelAutorepeatFlag_2 = 0; // byte_59B82
-uint8_t byte_59B83 = 0;
-uint8_t byte_59B86 = 0;
-uint16_t gDemoRecordingRandomGeneratorSeed = 0; // word_5A199
-// uint8_t byte_5A140 = 0; // speedFixMagicNumber inside of level
-uint8_t byte_5A19B = 0;
-uint8_t gIsLevelStartedAsDemo = 0; // byte_5A19C
-uint8_t gDemoRecordingJustStarted = 0; // byte_5A2F8
-uint8_t gHasUserCheated = 0; // byte_5A2F9
-uint8_t byte_5A323 = 0;
-uint16_t word_5A33C = 0;
-uint8_t gHasUserInterruptedDemo = 0; // byte_5A33E
-uint8_t gIsGameBusy = 0; // byte_5A33F -> this was used mainly to avoid some graphic glitches when some text from the main menu was written on the game field
-// uint8_t gIsMouseAvailable = 0; // byte_58487
-uint8_t gLevelListButtonPressed = 0; // byte_50918
-uint8_t gLevelListDownButtonPressed = 0; // byte_50916
-uint8_t gLevelListUpButtonPressed = 0; // byte_50917
-uint8_t gNewPlayerEntryIndex = 0; // byte_59820
-uint8_t gPlayerListButtonPressed = 0; // byte_50912
-uint8_t gPlayerListDownButtonPressed = 0; // byte_50910
-uint8_t gPlayerListUpButtonPressed = 0; // byte_50911
-uint8_t gRankingListButtonPressed = 0; // byte_50915
-uint8_t gRankingListDownButtonPressed = 0; // byte_50913
-uint8_t gRankingListUpButtonPressed = 0; // byte_50914
-uint16_t gCurrentSelectedLevelIndex = 0; // word_51ABC
-uint16_t gNewPlayerNameLength = 0; // word_58475
-
-uint16_t word_5157A = 0x4A62; // -> 0x126A -> (64, 132)
-uint16_t word_5157C = 0x0502; // -> 0x126C -> (97, 132)
-uint16_t kMurphyStillSpriteCoordinates = 0x4A80; // word_5157E -> 0x126E -> (304, 132)
-uint16_t word_51580 = 0x1AB2; // -> 0x1270 -> (0, 32)
-uint16_t word_515A2 = 0x32A2; // -> 0x1292 -> (224, 82) ??
-
-uint16_t word_515C4 = 0x1358; // (240, 178) -> 0x12B4
-
-// { 128, 64 }, // -> 0x2a02 -> 12f6 -> orange disk falling
-
-uint16_t word_5177E = 0x0CAE; // -> 0x129 -> (256, 164)
-uint16_t word_51790 = 0x4A7E; // -> 0x129 -> (288, 132)
-uint16_t word_5182E = 0x2A64; // -> (272, 388)
-uint16_t word_51840 = 0x2A06; // -> 0x129 -> (160, 64)
-uint16_t word_51842 = 0x132C; // -> 0x129 -> (208, 16) confirmed
-uint16_t word_51844 = 0x2A08; // -> 0x129 -> (176, 64)
-uint16_t word_51846 = 0x132A; // -> 0x129 -> (192, 16)
-uint16_t kTerminalOnSpriteCoordinates = 0x2A62; // word_51848 -> 0x1268 -> (256, 388) I don't get the math for this one, but the coordinates are right
-uint16_t word_5184A = 0x2A66; // -> 0x1268 -> (
-uint16_t word_5184C = 0x2A67; // -> 0x1268 -> (
-uint16_t word_5184E = 0x2E36; // -> 0x1268 -> (
-uint16_t word_51850 = 0x2E37; // -> 0x1268 -> (
-uint16_t word_51852 = 0x2A68; // -> 0x1268 -> (
-uint16_t word_51854 = 0x2A69; // -> 0x1268 -> (
-uint16_t word_51856 = 0x2E38; // -> 0x1268 -> (
-uint16_t word_51858 = 0x2E39; // -> 0x1268 -> (
-uint16_t gIsMoveScrollModeEnabled = 0; // word_51A01
-uint16_t gDebugExtraRenderDelay = 1; // this was used to add an extra delay in debug mode using keys 1-9
-uint16_t word_58463 = 0;
-uint8_t gIsInMainMenu = 0;
-uint16_t gAutomaticDemoPlaybackCountdown = 0; // word_58465
-uint16_t word_58467 = 0;
-uint16_t gLevelListThrottleCurrentCounter = 0; // word_58469
-uint16_t gLevelListThrottleNextCounter = 0;
-uint16_t gPlayerListThrottleCurrentCounter = 0; // word_5846D
-uint16_t gPlayerListThrottleNextCounter = 0; // word_5846F
-uint16_t gRankingListThrottleCurrentCounter = 0; // word_58471
-uint16_t gRankingListThrottleNextCounter = 0; // word_58473
-uint16_t gSelectedOriginalDemoIndex = 0; // word_599D6 -> used loading old demo files demo
-uint16_t gSelectedOriginalDemoLevelNumber = 0; // word_599D8 -> used loading old demo files demo -> the high byte is set to -1 in readLevels for some unknown reason
-// These two store the scroll offset to get back to Murphy when we're in "free mode"
-uint16_t gMurphyScrollOffsetX = 0; // word_59B88
-uint16_t gMurphyScrollOffsetY = 0; // word_59B8A
-uint16_t gLevelSetRotationThrottleCurrentCounter = 0; // word_59B8C
-uint16_t gLevelSetRotationThrottleNextCounter = 0; // word_59B8E
-uint16_t gLastDrawnMinutesAndSeconds; // word_510B7
-uint8_t gLastDrawnHours; // byte_510B9
-FILE *gCurrentRecordingDemoFile; // word_510E4
-uint8_t gDemoRecordingLowestSpeed; // speed?2
-int16_t gAdditionalScrollOffsetX; // word_51963
-int16_t gAdditionalScrollOffsetY; // word_51965
-uint8_t isJoystickEnabled = 0; // byte_50940
-uint8_t isMusicEnabled = 0; // byte_59886
-uint8_t isFXEnabled = 0; // byte_59885
-
-uint8_t gIsFlashingBackgroundModeEnabled = 0; // flashingbackgroundon
-const float kSpeedTimeFactors[kNumberOfGameSpeeds] = {3.5, 3.0, 2.5, 2.0, 1.5, 1.0, 0.75, 2.0 / 3.0, 5.0 / 8.0, 3.0 / 5.0, 1.0 / 70.0};
-
-// Used to measure game speed (reference is 35 iterations per second)
-float gGameIterationRate = 0.f;
-uint32_t gGameIterationRateReferenceTime = 0;
-
-// Used to limit game speed
-uint32_t gGameIterationStartTime = 0;
-uint32_t gNumberOfGameIterations = 0;
-
-static const uint16_t kFallAnimationGravityOffsets[18] = {
- 0x0000, // -> 0x6C95
- 0x007A, // -> 0x6C97
- 0x00F4, // -> 0x6C99
- 0x016E, // -> 0x6C9B
- 0x01E8, // -> 0x6C9D
- 0x0262, // -> 0x6C9F
- 0x02DC, // -> 0x6CA1
- 0x0356, // -> 0x6CA3
- 0x03D0, // -> 0x6CA5
- 0x044A, // -> 0x6CA7
- 0x04C4, // -> 0x6CA9
- 0x053E, // -> 0x6CAB
- 0x05B8, // -> 0x6CAD
- 0x0632, // -> 0x6CAF
- 0x06AC, // -> 0x6CB1
- 0x0726, // -> 0x6CB3
- 0x07A0, // -> 0x6CB5
- 0x081A, // -> 0x6CB7
-};
-
-static const int kConfigDataLength = 4;
-
-uint16_t gRandomGeneratorSeed = 0;
-
-enum MouseButton
-{
- MouseButtonLeft = 1 << 0,
- MouseButtonRight = 1 << 1,
-};
-
-uint8_t gShouldCloseAdvancedMenu = 0;
-uint8_t gAdvancedMenuRecordDemoIndex = 0;
-uint8_t gAdvancedMenuPlayDemoIndex = 0;
-
-typedef struct
-{
- uint16_t startX, startY;
- uint16_t endX, endY;
- void (*handler)(void);
-} ButtonDescriptor;
-
-void handleNewPlayerOptionClick(void);
-void handlePlayerListScrollUp(void);
-void handlePlayerListScrollDown(void);
-void handlePlayerListClick(void);
-void handleLevelListScrollUp(void);
-void handleLevelListScrollDown(void);
-void handleRankingListScrollUp(void);
-void handleRankingListScrollDown(void);
-void handleLevelCreditsClick(void);
-void handleGfxTutorOptionClick(void);
-void handleDemoOptionClick(void);
-void handleSkipLevelOptionClick(void);
-void handleFloppyDiskButtonClick(void);
-void handleDeletePlayerOptionClick(void);
-void handleStatisticsOptionClick(void);
-void handleControlsOptionClick(void);
-void handleOkButtonClick(void);
-
-#define kNumberOfMainMenuButtons 17
-static const ButtonDescriptor kMainMenuButtonDescriptors[kNumberOfMainMenuButtons] = { // located in DS:0000
- {
- 5, 6,
- 157, 14,
- handleNewPlayerOptionClick, // New player
- },
- {
- 5, 15,
- 157, 23,
- handleDeletePlayerOptionClick, // Delete player
- },
- {
- 5, 24,
- 157, 32,
- handleSkipLevelOptionClick, // Skip level
- },
- {
- 5, 33,
- 157, 41,
- handleStatisticsOptionClick, // Statistics
- },
- {
- 5, 42,
- 157, 50,
- handleGfxTutorOptionClick, // GFX-tutor
- },
- {
- 5, 51,
- 157, 59,
- handleDemoOptionClick, // Demo
- },
- {
- 5, 60,
- 157, 69,
- handleControlsOptionClick, // Controls
- },
- {
- 140, 90,
- 155, 108,
- handleRankingListScrollUp, // Rankings arrow up
- },
- {
- 140, 121,
- 155, 138,
- handleRankingListScrollDown, // Rankings arrow down
- },
- {
- 96, 140,
- 115, 163,
- handleOkButtonClick, // Ok button
- },
- {
- 83, 168,
- 126, 192,
- handleFloppyDiskButtonClick, // Insert data disk according to https://supaplex.fandom.com/wiki/Main_menu
- },
- {
- 11, 142,
- 67, 153,
- handlePlayerListScrollUp, // Players arrow up
- },
- {
- 11, 181,
- 67, 192,
- handlePlayerListScrollDown, // Players arrow down
- },
- {
- 11, 154,
- 67, 180,
- handlePlayerListClick, // Players list area
- },
- {
- 142, 142,
- 306, 153,
- handleLevelListScrollUp, // Levels arrow up
- },
- {
- 142, 181,
- 306, 192,
- handleLevelListScrollDown, // Levels arrow down
- },
- {
- 297, 37,
- 312, 52,
- handleLevelCreditsClick, // Credits
- }};
-
-void handleOptionsExitAreaClick(void);
-void handleOptionsMusicClick(void);
-void handleOptionsAdlibClick(void);
-void handleOptionsSoundBlasterClick(void);
-void handleOptionsRolandClick(void);
-void handleOptionsCombinedClick(void);
-void handleOptionsStandardClick(void);
-void handleOptionsSamplesClick(void);
-void handleOptionsInternalClick(void);
-void handleOptionsFXClick(void);
-void handleOptionsKeyboardClick(void);
-void handleOptionsJoystickClick(void);
-
-#define kNumberOfOptionsMenuButtons 13
-static const ButtonDescriptor kOptionsMenuButtonDescriptors[kNumberOfOptionsMenuButtons] = {
- // located in DS:00AC
- {
- 12, 13,
- 107, 36,
- handleOptionsAdlibClick, // Adlib
- },
- {
- 12, 49,
- 107, 72,
- handleOptionsSoundBlasterClick, // Sound Blaster
- },
- {
- 12, 85,
- 107, 108,
- handleOptionsRolandClick, // Roland
- },
- {
- 12, 121,
- 107, 144,
- handleOptionsCombinedClick, // Combined
- },
- {
- 132, 13,
- 211, 31,
- handleOptionsInternalClick, // Internal
- },
- {
- 126, 43,
- 169, 54,
- handleOptionsStandardClick, // Standard
- },
- {
- 174, 43,
- 217, 54,
- handleOptionsSamplesClick, // Samples
- },
- {
- 132, 86,
- 175, 120,
- handleOptionsMusicClick, // Music
- },
- {
- 134, 132,
- 168, 152,
- handleOptionsFXClick, // FX
- },
- {
- 201, 80,
- 221, 154,
- handleOptionsKeyboardClick, // Keyboard
- },
- {
- 233, 80,
- 252, 154,
- handleOptionsJoystickClick, // Joystick
- },
- {
- 0, 181,
- 319, 199,
- handleOptionsExitAreaClick, // Exit (bottom)
- },
- {
- 284, 0,
- 319, 180,
- handleOptionsExitAreaClick, // Exit (right)
- },
-};
-
-typedef void (*MovingFunction)(int16_t);
-typedef void (*FrameBasedMovingFunction)(int16_t, uint8_t);
-
-void updateZonkTiles(int16_t position);
-void updateInfotronTiles(int16_t position);
-void updateOrangeDiskTiles(int16_t position);
-void updateSnikSnakTiles(int16_t position);
-void updateTerminalTiles(int16_t position);
-void updateElectronTiles(int16_t position);
-void updateBugTiles(int16_t position);
-void updateExplosionTiles(int16_t position);
-
-static const MovingFunction movingFunctions[32] = {
- NULL,
- updateZonkTiles,
- NULL,
- NULL,
- updateInfotronTiles,
- NULL,
- NULL,
- NULL,
- updateOrangeDiskTiles,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- updateSnikSnakTiles,
- NULL,
- updateTerminalTiles,
- NULL,
- NULL,
- NULL,
- NULL,
- updateElectronTiles,
- updateBugTiles,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- updateExplosionTiles,
-};
-
-void updateElectronTurnLeft(int16_t position, uint8_t frame);
-void updateElectronTurnRight(int16_t position, uint8_t frame);
-void updateElectronMovementUp(int16_t position, uint8_t frame);
-void updateElectronMovementDown(int16_t position, uint8_t frame);
-void updateElectronMovementRight(int16_t position, uint8_t frame);
-void updateElectronMovementLeft(int16_t position, uint8_t frame);
-
-static const FrameBasedMovingFunction kElectronMovingFunctions[] = {
- updateElectronTurnLeft,
- updateElectronTurnLeft,
- updateElectronTurnLeft,
- updateElectronTurnLeft,
- updateElectronTurnLeft,
- updateElectronTurnLeft,
- updateElectronTurnLeft,
- updateElectronTurnLeft,
- updateElectronTurnRight,
- updateElectronTurnRight,
- updateElectronTurnRight,
- updateElectronTurnRight,
- updateElectronTurnRight,
- updateElectronTurnRight,
- updateElectronTurnRight,
- updateElectronTurnRight,
- updateElectronMovementUp,
- updateElectronMovementUp,
- updateElectronMovementUp,
- updateElectronMovementUp,
- updateElectronMovementUp,
- updateElectronMovementUp,
- updateElectronMovementUp,
- updateElectronMovementUp,
- updateElectronMovementDown,
- updateElectronMovementDown,
- updateElectronMovementDown,
- updateElectronMovementDown,
- updateElectronMovementDown,
- updateElectronMovementDown,
- updateElectronMovementDown,
- updateElectronMovementDown,
- updateElectronMovementRight,
- updateElectronMovementRight,
- updateElectronMovementRight,
- updateElectronMovementRight,
- updateElectronMovementRight,
- updateElectronMovementRight,
- updateElectronMovementRight,
- updateElectronMovementRight,
- updateElectronMovementLeft,
- updateElectronMovementLeft,
- updateElectronMovementLeft,
- updateElectronMovementLeft,
- updateElectronMovementLeft,
- updateElectronMovementLeft,
- updateElectronMovementLeft,
- updateElectronMovementLeft,
-};
-
-void updateSnikSnakTurnLeft(int16_t position, uint8_t frame);
-void updateSnikSnakTurnRight(int16_t position, uint8_t frame);
-void updateSnikSnakMovementUp(int16_t position, uint8_t frame);
-void updateSnikSnakMovementLeft(int16_t position, uint8_t frame);
-void updateSnikSnakMovementDown(int16_t position, uint8_t frame);
-void updateSnikSnakMovementRight(int16_t position, uint8_t frame);
-
-static const FrameBasedMovingFunction kSnikSnakMovingFunctions[48] = {
- updateSnikSnakTurnLeft,
- updateSnikSnakTurnLeft,
- updateSnikSnakTurnLeft,
- updateSnikSnakTurnLeft,
- updateSnikSnakTurnLeft,
- updateSnikSnakTurnLeft,
- updateSnikSnakTurnLeft,
- updateSnikSnakTurnLeft,
- updateSnikSnakTurnRight,
- updateSnikSnakTurnRight,
- updateSnikSnakTurnRight,
- updateSnikSnakTurnRight,
- updateSnikSnakTurnRight,
- updateSnikSnakTurnRight,
- updateSnikSnakTurnRight,
- updateSnikSnakTurnRight,
- updateSnikSnakMovementUp,
- updateSnikSnakMovementUp,
- updateSnikSnakMovementUp,
- updateSnikSnakMovementUp,
- updateSnikSnakMovementUp,
- updateSnikSnakMovementUp,
- updateSnikSnakMovementUp,
- updateSnikSnakMovementUp,
- updateSnikSnakMovementLeft,
- updateSnikSnakMovementLeft,
- updateSnikSnakMovementLeft,
- updateSnikSnakMovementLeft,
- updateSnikSnakMovementLeft,
- updateSnikSnakMovementLeft,
- updateSnikSnakMovementLeft,
- updateSnikSnakMovementLeft,
- updateSnikSnakMovementDown,
- updateSnikSnakMovementDown,
- updateSnikSnakMovementDown,
- updateSnikSnakMovementDown,
- updateSnikSnakMovementDown,
- updateSnikSnakMovementDown,
- updateSnikSnakMovementDown,
- updateSnikSnakMovementDown,
- updateSnikSnakMovementRight,
- updateSnikSnakMovementRight,
- updateSnikSnakMovementRight,
- updateSnikSnakMovementRight,
- updateSnikSnakMovementRight,
- updateSnikSnakMovementRight,
- updateSnikSnakMovementRight,
- updateSnikSnakMovementRight,
-};
-
-void throttledRotateLevelSet(uint8_t descending);
-void rotateLevelSet(uint8_t descending);
-void updateMenuAfterLevelSetChanged(void);
-void initializeGameStateData(void);
-void startDirectlyFromLevel(uint8_t levelNumber);
-void stopDemoAndPlay(void);
-void emulateClock(void);
-void loadScreen2(void);
-void readEverything(void);
-void drawSpeedFixTitleAndVersion(void);
-void openCreditsBlock(void);
-void drawSpeedFixCredits(void);
-void readConfig(void);
-void activateAdlibSound(void);
-void activateSoundBlasterSound(void);
-void activateRolandSound(void);
-void activateCombinedSound(void);
-void activateInternalStandardSound(void);
-void activateInternalSamplesSound(void);
-void prepareDemoRecordingFilename(void);
-void runMainMenu(void);
-void convertNumberTo3DigitPaddedString(uint8_t number, char numberString[3], char useSpacesForPadding);
-void stopMusicAndSounds(void);
-void playMusicIfNeeded(void);
-void stopMusic(void);
-void playExplosionSound(void);
-void playInfotronSound(void);
-void playPushSound(void);
-void playFallSound(void);
-void playBugSound(void);
-void playBaseSound(void);
-void playExitSound(void);
-void sound11(void);
-void savePlayerListData(void);
-void saveHallOfFameData(void);
-void getMouseStatus(uint16_t *mouseX, uint16_t *mouseY, uint16_t *mouseButtonStatus);
-void drawMainMenuButtonBorders(void);
-void drawMainMenuButtonBorder(ButtonBorderDescriptor border, uint8_t color);
-void generateRandomSeedFromClock(void);
-void initializeFadePalette(void);
-void initializeMouse(void);
-void prepareLevelDataForCurrentPlayer(void);
-void drawPlayerList(void);
-void drawLevelList(void);
-void drawHallOfFame(void);
-void drawRankings(void);
-void drawTextWithChars6FontWithOpaqueBackgroundIfPossible(size_t destX, size_t destY, uint8_t color, const char *text);
-void drawTextWithChars6FontWithTransparentBackgroundIfPossible(size_t destX, size_t destY, uint8_t color, const char *text);
-void waitForKeyMouseOrJoystick(void);
-void drawMenuTitleAndDemoLevelResult(void);
-void scrollRightToNewScreen(void);
-void scrollLeftToMainMenu(void);
-void convertNumberTo3DigitStringWithPadding0(uint8_t number, char numberString[3]);
-void changePlayerCurrentLevelState(void);
-void updateHallOfFameEntries(void);
-void drawSoundTypeOptionsSelection(uint8_t *destBuffer);
-void dimOptionsButtonText(size_t startX, size_t startY, size_t width, size_t height, uint8_t *destBuffer);
-void drawOptionsMenuLine(ButtonBorderDescriptor border, uint8_t color, uint8_t *destBuffer);
-void highlightOptionsButtonText(size_t startX, size_t startY, size_t width, size_t height, uint8_t *destBuffer);
-void drawAudioOptionsSelection(uint8_t *destBuffer);
-void drawInputOptionsSelection(uint8_t *destBuffer);
-void updateOptionsMenuState(uint8_t *destBuffer);
-void convertLevelNumberTo3DigitStringWithPadding0(uint8_t number);
-void readLevels(void);
-void initializeGameInfo(void);
-void drawGamePanel(void);
-void drawNumberOfRemainingInfotrons(void);
-void drawGameTime(void);
-uint16_t convertToEasyTiles(void);
-void resetNumberOfInfotrons(uint16_t numberOfInfotronsInGameField);
-void findMurphy(void);
-void drawGamePanelText(void);
-void scrollToMurphy(void);
-void runLevel(void);
-void slideDownGameDash(void);
-void updateScrollOffset(void);
-uint16_t generateRandomNumber(void);
-void handleGameUserInput(void);
-void loc_49C41(void);
-void loc_49C2C(char text[3]);
-void showSavegameOperationError(void);
-void saveGameSnapshot(void);
-void loadGameSnapshot(void);
-void checkDebugKeys(void);
-void loc_4988E(void);
-void restoreOriginalFancyTiles(void);
-void updateMovingObjects(void);
-int16_t updateMurphy(int16_t position);
-int16_t updateMurphyAnimation(int16_t position);
-int16_t updateMurphyAnimationInfo(int16_t position, MurphyAnimationDescriptor unknownMurphyData);
-int16_t handleMurphyDirectionRight(int16_t position);
-int16_t handleMurphyDirectionDown(int16_t position);
-int16_t handleMurphyDirectionLeft(int16_t position);
-int16_t handleMurphyDirectionUp(int16_t position);
-void detonateYellowDisks(void);
-void updateUserInputInScrollMovementMode(void);
-void updateUserInput(void);
-void saveInputForDemo(void);
-void simulateDemoInput(void);
-void fetchAndInitializeLevel(void);
-void removeTiles(LevelTileType tileType);
-void restartLevel(void);
-void restartLevelWithoutAddingCurrentGameTimeToPlayer(void);
-void recordDemo(uint16_t demoIndex);
-void stopRecordingDemo(void);
-void debugSkipLevel(void);
-void forceRestoreOriginalFancyTiles(void);
-void drawNumberOfRemainingRedDisks(void);
-void clearAdditionalInfoInGamePanelIfNeeded(void);
-void updatePlantedRedDisk(void);
-void updateExplosionTimers(void);
-void addCurrentGameTimeToPlayer(void);
-void drawFailedLevelResultScreen(void);
-void handleZonkStateAfterFallingOneTile(int16_t position);
-void detonateBigExplosion(int16_t position);
-void detonateZonk(int16_t position, uint8_t state, uint8_t tile);
-void sub_4AA34(int16_t position, uint8_t state, uint8_t tile);
-void sub_4AAB4(int16_t position);
-uint8_t checkMurphyMovementToPosition(int16_t position, UserInput userInput);
-void handleZonkPushedByMurphy(int16_t position);
-void decreaseRemainingRedDisksIfNeeded(int16_t position);
-void updateSpecialPort(int16_t position);
-void handleInfotronStateAfterFallingOneTile(int16_t position);
-void int9handler(uint8_t shouldYieldCpu);
-void updateDemoRecordingLowestSpeed(void);
-void playDemo(uint16_t demoIndex);
-
-static const char *kAdvancedConfigGeneralSection = "general";
-static const char *kAdvancedConfigDebugSection = "debug";
-
-static const char *kAdvancedConfigPlayerKey = "player";
-static const char *kAdvancedConfigLevelSetKey = "levelSet";
-static const char *kAdvancedConfigGameSpeedKey = "gameSpeed";
-static const char *kAdvancedConfigMusicVolumeKey = "musicVolume";
-static const char *kAdvancedConfigFXVolumeKey = "fxVolume";
-static const char *kAdvancedConfigScalingModeKey = "scalingMode";
-static const char *kAdvancedConfigFullscreenKey = "fullscreen";
-static const char *kAdvancedConfigDisplayFPSKey = "displayFPS";
-static const char *kAdvancedConfigLimitFPSKey = "limitFPS";
-
-void readAdvancedConfig()
-{
- Config *config = initializeConfigForReading("ADVANCED.CFG");
-
- if (config == NULL)
- {
- spLogInfo("Couldn't read advanced config");
- return;
- }
-
- char currentSuffix[3] = "AT";
- strcpy(currentSuffix, &gLevelsDatFilename[8]);
-
- int player = readConfigInt(config, kAdvancedConfigGeneralSection, kAdvancedConfigPlayerKey, 0);
- player = CLAMP(player, 0, kNumberOfPlayers - 1);
- gCurrentPlayerIndex = player;
-
- // Only apply level set from config if it wasn't overriden before (by command line)
- if (strcmp(currentSuffix, "AT") == 0)
- {
- int levelSet = readConfigInt(config, kAdvancedConfigGeneralSection, kAdvancedConfigLevelSetKey, 0);
-
- if (levelSet != 0)
- {
- char newSuffix[3] = "00";
- snprintf(newSuffix, 3, "%02d", levelSet);
-
- strcpy(&gLevelsDatFilename[8], newSuffix);
- strcpy(&gLevelLstFilename[7], newSuffix);
- strcpy(&gDemo0BinFilename[7], newSuffix);
- strcpy(&gPlayerLstFilename[8], newSuffix);
- strcpy(&gHallfameLstFilename[10], newSuffix);
-
- if (gShouldAlwaysWriteSavegameSav == 0) // cmp byte ptr gShouldAlwaysWriteSavegameSav, 0
- {
- strcpy(&gSavegameSavFilename[10], newSuffix);
- }
- }
- }
-
- gGameSpeed = readConfigInt(config, kAdvancedConfigGeneralSection, kAdvancedConfigGameSpeedKey, kDefaultGameSpeed);
-
- int volume = readConfigInt(config, kAdvancedConfigGeneralSection, kAdvancedConfigMusicVolumeKey, getMusicVolume());
- setMusicVolume(volume);
-
- volume = readConfigInt(config, kAdvancedConfigGeneralSection, kAdvancedConfigFXVolumeKey, getSoundEffectsVolume());
- setSoundEffectsVolume(volume);
-
- int scalingMode = readConfigInt(config, kAdvancedConfigGeneralSection, kAdvancedConfigScalingModeKey, getScalingMode());
- setScalingMode(scalingMode);
-
- gShouldShowFPS = readConfigInt(config, kAdvancedConfigDebugSection, kAdvancedConfigDisplayFPSKey, gShouldShowFPS);
- gShouldLimitFPS = readConfigInt(config, kAdvancedConfigDebugSection, kAdvancedConfigLimitFPSKey, gShouldLimitFPS);
-
- int fullscreen = readConfigInt(config, kAdvancedConfigGeneralSection, kAdvancedConfigFullscreenKey, getFullscreenMode());
- setFullscreenMode(fullscreen);
-
- destroyConfig(config);
-}
-
-void writeAdvancedConfig()
-{
- Config *config = initializeConfigForWriting("ADVANCED.CFG");
-
- if (config == NULL)
- {
- spLogInfo("Couldn't write advanced config");
- return;
- }
-
- writeConfigSection(config, kAdvancedConfigGeneralSection);
-
- char currentSuffix[3] = "AT";
- strcpy(currentSuffix, &gLevelsDatFilename[8]);
-
- int levelSet = 0;
-
- if (strcmp(currentSuffix, "AT") != 0)
- {
- levelSet = atoi(currentSuffix);
- }
-
- writeConfigInt(config, kAdvancedConfigPlayerKey, gCurrentPlayerIndex);
- writeConfigInt(config, kAdvancedConfigLevelSetKey, levelSet);
- writeConfigInt(config, kAdvancedConfigGameSpeedKey, gGameSpeed);
- writeConfigInt(config, kAdvancedConfigMusicVolumeKey, getMusicVolume());
- writeConfigInt(config, kAdvancedConfigFXVolumeKey, getSoundEffectsVolume());
- writeConfigInt(config, kAdvancedConfigScalingModeKey, getScalingMode());
- writeConfigInt(config, kAdvancedConfigFullscreenKey, getFullscreenMode());
-
- writeConfigSection(config, kAdvancedConfigDebugSection);
- writeConfigInt(config, kAdvancedConfigDisplayFPSKey, gShouldShowFPS);
- writeConfigInt(config, kAdvancedConfigLimitFPSKey, gShouldLimitFPS);
-
- destroyConfig(config);
-}
-
-/// @return 1 if the action was to go back / close the menu
-uint8_t handleAdvancedOptionsMenuInput(AdvancedOptionsMenu *menu)
-{
- if (isUpButtonPressed())
- {
- playBaseSound();
- moveUpAdvancedOptionsSelectedEntry(menu);
- }
-
- if (isDownButtonPressed())
- {
- playBaseSound();
- moveDownAdvancedOptionsSelectedEntry(menu);
- }
-
- if (isLeftButtonPressed())
- {
- playBaseSound();
- decreaseAdvancedOptionsSelectedEntry(menu);
- }
-
- if (isRightButtonPressed())
- {
- playBaseSound();
- increaseAdvancedOptionsSelectedEntry(menu);
- }
-
- if (isMenuConfirmButtonPressed())
- {
- playInfotronSound();
- selectAdvancedOptionsSelectedEntry(menu);
- }
-
- if (isMenuCancelButtonPressed())
- {
- playPushSound();
- return 1;
- }
-
- if (isPauseButtonPressed())
- {
- playPushSound();
- gShouldCloseAdvancedMenu = 1;
- return 1;
- }
-
- if (gShouldCloseAdvancedMenu)
- {
- return 1;
- }
-
- return 0;
-}
-
-void advancedOptionsMenuWaitForKeyPress()
-{
- do
- {
- int9handler(1);
- } while (isUpButtonPressed() == 0 && isDownButtonPressed() == 0 && isLeftButtonPressed() == 0 && isRightButtonPressed() == 0 && isMenuBackButtonPressed() == 0 && isMenuConfirmButtonPressed() == 0 && isMenuCancelButtonPressed() == 0 && isPauseButtonPressed() == 0);
-}
-
-void advancedOptionsMenuWaitForKeyRelease()
-{
- do
- {
- int9handler(1);
- } while (isUpButtonPressed() || isDownButtonPressed() || isLeftButtonPressed() || isRightButtonPressed() || isMenuBackButtonPressed() || isMenuConfirmButtonPressed() || isMenuCancelButtonPressed() || isPauseButtonPressed());
-}
-
-void runAdvancedOptionsMenu(AdvancedOptionsMenu *menu)
-{
- playFallSound();
-
- advancedOptionsMenuWaitForKeyRelease();
-
- do
- {
- if (handleAdvancedOptionsMenuInput(menu))
- {
- break;
- }
-
- renderAdvancedOptionsMenu(menu);
- advancedOptionsMenuWaitForKeyRelease();
- advancedOptionsMenuWaitForKeyPress();
- } while (1);
-
- advancedOptionsMenuWaitForKeyRelease();
-}
-
-void buildGameSpeedOptionTitle(char output[kMaxAdvancedOptionsMenuEntryTitleLength])
-{
- snprintf(output, kMaxAdvancedOptionsMenuEntryTitleLength, "GAME SPEED: %d", gGameSpeed);
-}
-
-void buildMusicVolumeOptionTitle(char output[kMaxAdvancedOptionsMenuEntryTitleLength])
-{
- snprintf(output, kMaxAdvancedOptionsMenuEntryTitleLength, "MUSIC VOLUME: %d", getMusicVolume());
-}
-
-void buildFXVolumeOptionTitle(char output[kMaxAdvancedOptionsMenuEntryTitleLength])
-{
- snprintf(output, kMaxAdvancedOptionsMenuEntryTitleLength, "FX VOLUME: %d", getSoundEffectsVolume());
-}
-
-void buildScalingModeOptionTitle(char output[kMaxAdvancedOptionsMenuEntryTitleLength])
-{
- static const char *kAspectFitScalingModeString = "NORMAL";
- static const char *kAspectFillScalingModeString = "ZOOM";
- static const char *kIntegerFactorScalingModeString = "INTEGER FACTOR";
- static const char *kFullscreenScalingModeString = "FULLSCREEN";
- static const char *kAspectCorrectScalingModeString = "4:3";
-
- const char *mode = kAspectFitScalingModeString;
-
- switch (getScalingMode())
- {
- case ScalingModeAspectFill:
- mode = kAspectFillScalingModeString;
- break;
- case ScalingModeIntegerFactor:
- mode = kIntegerFactorScalingModeString;
- break;
- case ScalingModeFullscreen:
- mode = kFullscreenScalingModeString;
- break;
- case ScalingModeAspectCorrect:
- mode = kAspectCorrectScalingModeString;
- break;
- default:
- mode = kAspectFitScalingModeString;
- break;
- }
-
- snprintf(output, kMaxAdvancedOptionsMenuEntryTitleLength, "SCALING MODE: %s", mode);
-}
-
-void buildBooleanOptionTitle(char output[kMaxAdvancedOptionsMenuEntryTitleLength], char title[kMaxAdvancedOptionsMenuEntryTitleLength], uint8_t value)
-{
- snprintf(output, kMaxAdvancedOptionsMenuEntryTitleLength, "%s: %s", title, (value == 0 ? "OFF" : "ON"));
-}
-
-void buildDisplayFPSOptionTitle(char output[kMaxAdvancedOptionsMenuEntryTitleLength])
-{
- buildBooleanOptionTitle(output, "DISPLAY FPS", gShouldShowFPS);
-}
-
-void buildLimitFPSOptionTitle(char output[kMaxAdvancedOptionsMenuEntryTitleLength])
-{
- buildBooleanOptionTitle(output, "LIMIT FPS", gShouldLimitFPS);
-}
-
-void buildPlayDemoOptionTitle(char output[kMaxAdvancedOptionsMenuEntryTitleLength])
-{
- snprintf(output, kMaxAdvancedOptionsMenuEntryTitleLength, "PLAY DEMO: %d", gAdvancedMenuPlayDemoIndex);
-}
-
-void buildRecordDemoOptionTitle(char output[kMaxAdvancedOptionsMenuEntryTitleLength])
-{
- snprintf(output, kMaxAdvancedOptionsMenuEntryTitleLength, "RECORD DEMO: %d", gAdvancedMenuRecordDemoIndex);
-}
-
-void increaseGameSpeed()
-{
- if (gGameSpeed < kNumberOfGameSpeeds - 1)
- {
- gGameSpeed++;
- }
-
- updateDemoRecordingLowestSpeed();
-}
-
-void decreaseGameSpeed()
-{
- if (gGameSpeed > 0)
- {
- gGameSpeed--;
- }
-
- updateDemoRecordingLowestSpeed();
-}
-
-void updateDemoRecordingLowestSpeed()
-{
- if (gGameSpeed < gDemoRecordingLowestSpeed)
- {
- gDemoRecordingLowestSpeed = gGameSpeed;
- }
-}
-
-void increaseMusicVolume()
-{
- uint8_t volume = getMusicVolume();
-
- if (volume < kMaxVolume)
- {
- setMusicVolume(volume + 1);
- }
-}
-
-void decreaseMusicVolume()
-{
- uint8_t volume = getMusicVolume();
-
- if (volume > 0)
- {
- setMusicVolume(volume - 1);
- }
-}
-
-void increaseFXVolume()
-{
- uint8_t volume = getSoundEffectsVolume();
-
- if (volume < kMaxVolume)
- {
- setSoundEffectsVolume(volume + 1);
- }
-}
-
-void decreaseAdvancedMenuRecordDemoIndex()
-{
- if (gAdvancedMenuRecordDemoIndex > 0)
- {
- gAdvancedMenuRecordDemoIndex--;
- }
-}
-
-void increaseAdvancedMenuRecordDemoIndex()
-{
- if (gAdvancedMenuRecordDemoIndex < kNumberOfDemos - 1)
- {
- gAdvancedMenuRecordDemoIndex++;
- }
-}
-
-void decreaseAdvancedMenuPlayDemoIndex()
-{
- if (gAdvancedMenuPlayDemoIndex > 0)
- {
- gAdvancedMenuPlayDemoIndex--;
- }
-}
-
-void increaseAdvancedMenuPlayDemoIndex()
-{
- if (gAdvancedMenuPlayDemoIndex < kNumberOfDemos - 1)
- {
- gAdvancedMenuPlayDemoIndex++;
- }
-}
-
-void decreaseAdvancedMenuScalingMode()
-{
- ScalingMode mode = getScalingMode();
- if (mode > 0)
- {
- setScalingMode(mode - 1);
- }
- else
- {
- setScalingMode(ScalingModeCount - 1);
- }
-}
-
-void increaseAdvancedMenuScalingMode()
-{
- ScalingMode mode = getScalingMode();
- if (mode < ScalingModeCount - 1)
- {
- setScalingMode(mode + 1);
- }
- else
- {
- setScalingMode(0);
- }
-}
-
-void decreaseFXVolume()
-{
- uint8_t volume = getSoundEffectsVolume();
-
- if (volume > 0)
- {
- setSoundEffectsVolume(volume - 1);
- }
-}
-
-void toggleDisplayFPSOption()
-{
- TOGGLE_BOOL(gShouldShowFPS);
-}
-
-void toggleLimitFPSOption()
-{
- TOGGLE_BOOL(gShouldLimitFPS);
-}
-
-void handleResumeOptionSelection()
-{
- gShouldCloseAdvancedMenu = 1;
-}
-
-void handleRestartLevelOptionSelection()
-{
- restartLevel();
- gShouldCloseAdvancedMenu = 1;
-}
-
-void handleRemoveZonksOptionSelection()
-{
- removeTiles(LevelTileTypeZonk);
- gShouldCloseAdvancedMenu = 1;
-}
-
-void handleRemoveHardwareOptionSelection()
-{
- removeTiles(LevelTileTypeHardware);
- gShouldCloseAdvancedMenu = 1;
-}
-
-void handleRemoveBaseOptionSelection()
-{
- removeTiles(LevelTileTypeBase);
- gShouldCloseAdvancedMenu = 1;
-}
-
-void handleRemoveChipsOptionSelection()
-{
- removeTiles(LevelTileTypeChip);
- gShouldCloseAdvancedMenu = 1;
-}
-
-void handleRemoveSnikSnakOptionSelection()
-{
- removeTiles(LevelTileTypeSnikSnak);
- gShouldCloseAdvancedMenu = 1;
-}
-
-void handleMoveScrollOptionSelection()
-{
- gIsMoveScrollModeEnabled = 1;
- gShouldCloseAdvancedMenu = 1;
-}
-
-void handleRecordDemoOptionSelection()
-{
- recordDemo(gAdvancedMenuRecordDemoIndex);
- gShouldCloseAdvancedMenu = 1;
-}
-
-void handlePlayDemoOptionSelection()
-{
- playDemo(gAdvancedMenuPlayDemoIndex);
- gShouldCloseAdvancedMenu = 1;
-}
-
-void handleStopDemoAndPlayOptionSelection()
-{
- stopDemoAndPlay();
- gShouldCloseAdvancedMenu = 1;
-}
-
-void handleStopRecordingDemoOptionSelection()
-{
- stopRecordingDemo();
- gShouldCloseAdvancedMenu = 1;
-}
-
-void handleExitLevelOptionSelection()
-{
- gShouldKillMurphy = 1;
- gShouldCloseAdvancedMenu = 1;
-}
-
-void handleExitGameOptionSelection()
-{
- gShouldExitGame = 1;
- gShouldCloseAdvancedMenu = 1;
-}
-
-void runAdvancedOptionsSubMenu(AdvancedOptionsMenu menu)
-{
- runAdvancedOptionsMenu(&menu);
-}
-
-void handleDebugOptionSelection()
-{
- AdvancedOptionsMenu menu;
- initializeAdvancedOptionsMenu(&menu);
-
- strncpy(menu.title, "DEBUG (DANGER)", kMaxAdvancedOptionsMenuEntryTitleLength);
-
- addAdvancedOptionsEntry(&menu, (AdvancedOptionsMenuEntry){
- "",
- buildDisplayFPSOptionTitle,
- toggleDisplayFPSOption,
- toggleDisplayFPSOption,
- toggleDisplayFPSOption,
- });
- addAdvancedOptionsEntry(&menu, (AdvancedOptionsMenuEntry){
- "",
- buildLimitFPSOptionTitle,
- toggleLimitFPSOption,
- toggleLimitFPSOption,
- toggleLimitFPSOption,
- });
- if (gIsInMainMenu == 0)
- {
- addAdvancedOptionsEntry(&menu, (AdvancedOptionsMenuEntry){
- "LOAD GAME STATE",
- NULL,
- loadGameSnapshot,
- NULL,
- NULL,
- });
- addAdvancedOptionsEntry(&menu, (AdvancedOptionsMenuEntry){
- "SAVE GAME STATE",
- NULL,
- saveGameSnapshot,
- NULL,
- NULL,
- });
- addAdvancedOptionsEntry(&menu, (AdvancedOptionsMenuEntry){
- "MOVE FREELY",
- NULL,
- handleMoveScrollOptionSelection,
- NULL,
- NULL,
- });
- addAdvancedOptionsEntry(&menu, (AdvancedOptionsMenuEntry){
- "REMOVE ZONKS",
- NULL,
- handleRemoveZonksOptionSelection,
- NULL,
- NULL,
- });
- addAdvancedOptionsEntry(&menu, (AdvancedOptionsMenuEntry){
- "REMOVE HARDWARE",
- NULL,
- handleRemoveHardwareOptionSelection,
- NULL,
- NULL,
- });
- addAdvancedOptionsEntry(&menu, (AdvancedOptionsMenuEntry){
- "REMOVE SNIK SNAKS",
- NULL,
- handleRemoveSnikSnakOptionSelection,
- NULL,
- NULL,
- });
- addAdvancedOptionsEntry(&menu, (AdvancedOptionsMenuEntry){
- "REMOVE CHIPS",
- NULL,
- handleRemoveChipsOptionSelection,
- NULL,
- NULL,
- });
- addAdvancedOptionsEntry(&menu, (AdvancedOptionsMenuEntry){
- "REMOVE HARDWARE",
- NULL,
- handleRemoveHardwareOptionSelection,
- NULL,
- NULL,
- });
- addAdvancedOptionsEntry(&menu, (AdvancedOptionsMenuEntry){
- "REMOVE BASE",
- NULL,
- handleRemoveBaseOptionSelection,
- NULL,
- NULL,
- });
- }
-
- runAdvancedOptionsSubMenu(menu);
-}
-
-void buildLevelSetOptionTitle(char output[kMaxAdvancedOptionsMenuEntryTitleLength])
-{
- const char *kOriginalLevelSetName = "ORIGINAL";
- char currentSuffix[3] = "AT";
- strcpy(currentSuffix, &gLevelsDatFilename[8]);
-
- const char *levelSetName = kOriginalLevelSetName;
- if (strcmp(currentSuffix, "AT") != 0)
- {
- levelSetName = currentSuffix;
- }
-
- snprintf(output, kMaxAdvancedOptionsMenuEntryTitleLength, "LEVEL SET: %s", levelSetName);
-}
-
-void decreaseLevelSet()
-{
- rotateLevelSet(1);
- gHasChangedLevelSetFromAdvancedMenu = 1;
-}
-
-void increaseLevelSet()
-{
- rotateLevelSet(0);
- gHasChangedLevelSetFromAdvancedMenu = 1;
-}
-
-void runAdvancedOptionsRootMenu()
-{
- AdvancedOptionsMenu menu;
- initializeAdvancedOptionsMenu(&menu);
-
- strncpy(menu.title, "OPENSUPAPLEX " VERSION_STRING, kMaxAdvancedOptionsMenuEntryTitleLength);
-
- if (gIsPlayingDemo)
- {
- addAdvancedOptionsEntry(&menu, (AdvancedOptionsMenuEntry){
- "RESUME DEMO",
- NULL,
- handleResumeOptionSelection,
- NULL,
- NULL,
- });
- }
- else
- {
- addAdvancedOptionsEntry(&menu, (AdvancedOptionsMenuEntry){
- "RESUME GAME",
- NULL,
- handleResumeOptionSelection,
- NULL,
- NULL,
- });
- }
-
- if (gIsInMainMenu)
- {
- addAdvancedOptionsEntry(&menu, (AdvancedOptionsMenuEntry){
- "",
- buildLevelSetOptionTitle,
- NULL,
- decreaseLevelSet,
- increaseLevelSet,
- });
- }
-
- if (gIsPlayingDemo && gIsInMainMenu == 0)
- {
- addAdvancedOptionsEntry(&menu, (AdvancedOptionsMenuEntry){
- "STOP DEMO AND PLAY",
- NULL,
- handleStopDemoAndPlayOptionSelection,
- NULL,
- NULL,
- });
- }
-
- if (gIsInMainMenu == 0)
- {
- if (gIsPlayingDemo)
- {
- addAdvancedOptionsEntry(&menu, (AdvancedOptionsMenuEntry){
- "RESTART DEMO",
- NULL,
- handleRestartLevelOptionSelection,
- NULL,
- NULL,
- });
- }
- else
- {
- addAdvancedOptionsEntry(&menu, (AdvancedOptionsMenuEntry){
- "RESTART LEVEL",
- NULL,
- handleRestartLevelOptionSelection,
- NULL,
- NULL,
- });
- }
- addAdvancedOptionsEntry(&menu, (AdvancedOptionsMenuEntry){
- "",
- buildGameSpeedOptionTitle,
- NULL,
- decreaseGameSpeed,
- increaseGameSpeed,
- });
- }
- addAdvancedOptionsEntry(&menu, (AdvancedOptionsMenuEntry){
- "",
- buildMusicVolumeOptionTitle,
- NULL,
- decreaseMusicVolume,
- increaseMusicVolume,
- });
- addAdvancedOptionsEntry(&menu, (AdvancedOptionsMenuEntry){
- "",
- buildFXVolumeOptionTitle,
- NULL,
- decreaseFXVolume,
- increaseFXVolume,
- });
- addAdvancedOptionsEntry(&menu, (AdvancedOptionsMenuEntry){
- "",
- buildScalingModeOptionTitle,
- NULL,
- decreaseAdvancedMenuScalingMode,
- increaseAdvancedMenuScalingMode,
- });
- if (gIsInMainMenu)
- {
- addAdvancedOptionsEntry(&menu, (AdvancedOptionsMenuEntry){
- "",
- buildPlayDemoOptionTitle,
- handlePlayDemoOptionSelection,
- decreaseAdvancedMenuPlayDemoIndex,
- increaseAdvancedMenuPlayDemoIndex,
- });
- }
- if (gIsInMainMenu == 0 && gIsPlayingDemo == 0)
- {
- if (gIsRecordingDemo)
- {
- addAdvancedOptionsEntry(&menu, (AdvancedOptionsMenuEntry){
- "STOP RECORDING DEMO",
- NULL,
- handleStopRecordingDemoOptionSelection,
- NULL,
- NULL,
- });
- }
- else
- {
- addAdvancedOptionsEntry(&menu, (AdvancedOptionsMenuEntry){
- "",
- buildRecordDemoOptionTitle,
- handleRecordDemoOptionSelection,
- decreaseAdvancedMenuRecordDemoIndex,
- increaseAdvancedMenuRecordDemoIndex,
- });
- }
- }
- addAdvancedOptionsEntry(&menu, (AdvancedOptionsMenuEntry){
- "DEBUG (DANGER)",
- NULL,
- handleDebugOptionSelection,
- NULL,
- NULL,
- });
- if (gIsInMainMenu == 0)
- {
- addAdvancedOptionsEntry(&menu, (AdvancedOptionsMenuEntry){
- "EXIT LEVEL",
- NULL,
- handleExitLevelOptionSelection,
- NULL,
- NULL,
- });
- }
- else
- {
- addAdvancedOptionsEntry(&menu, (AdvancedOptionsMenuEntry){
- "EXIT GAME",
- NULL,
- handleExitGameOptionSelection,
- NULL,
- NULL,
- });
- }
-
- gShouldCloseAdvancedMenu = 0;
-
- saveScreenForAdvancedMenu();
-
- setPalette(gGameDimmedPalette);
-
- runAdvancedOptionsMenu(&menu);
-
- writeAdvancedConfig();
-
- restoreScreenFromAdvancedMenu();
- videoLoop();
-
- setPalette(gGamePalette);
-
- if (gHasChangedLevelSetFromAdvancedMenu)
- {
- updateMenuAfterLevelSetChanged();
- gHasChangedLevelSetFromAdvancedMenu = 0;
- }
-}
-
-int main(int argc, char *argv[])
-{
- parseCommandLineOptions(argc, argv);
-
- if (gFastMode == FastModeTypeUltra)
- {
- setLogLevel(LogLevelDemo);
- }
-
- initializeLogging();
- initializeSystem();
- initializeVideo(gFastMode);
- initializeControllers();
-
- if (gFastMode != FastModeTypeUltra)
- {
- initializeAudio();
- readAdvancedConfig();
- }
-
- // Override the initial game speed with the one from the command line if needed
- if (gForcedInitialGameSpeed != kInvalidForcedInitialGameSpeed)
- {
- gGameSpeed = gForcedInitialGameSpeed;
- }
-
- handleSystemEvents();
-
- initializeGameStateData();
-
- // doesNotHaveCommandLine: //; CODE XREF: start+13j start+23Fj ...
- generateRandomSeedFromClock();
- // checkVideo();
-
- // leaveVideoStatus: //; CODE XREF: start+28Aj
- initializeFadePalette(); // 01ED:026F
- initializeMouse();
-
- if (gFastMode == FastModeTypeNone)
- {
- setPalette(gBlackPalette);
- videoLoop();
- readAndRenderTitleDat();
- ColorPalette titleDatPalette; // si = 0x5F15;
- convertPaletteDataToPalette(gTitlePaletteData, titleDatPalette);
- fadeToPalette(titleDatPalette);
- }
-
- // isFastMode: //; CODE XREF: start+2ADj
- loadMurphySprites(); // 01ED:029D
- // Conditions to whether show
- if (gShouldStartFromSavedSnapshot || gIsForcedLevel || gIsSPDemoAvailableToRun || gFastMode)
- {
- readEverything();
- }
- else
- {
- // openingSequence:
- loadScreen2(); // 01ED:02B9
- readEverything(); // 01ED:02BC
- drawSpeedFixTitleAndVersion(); // 01ED:02BF
- openCreditsBlock(); // credits inside the block // 01ED:02C2
- drawSpeedFixCredits(); // credits below the block (herman perk and elmer productions) // 01ED:02C5
- }
-
- // afterOpeningSequence: //; CODE XREF: start+2DEj
- readConfig();
- if (byte_50946 == 0)
- {
- isJoystickEnabled = 0;
- }
- else
- {
- isJoystickEnabled = 1;
- }
-
- // loc_46F25: //; CODE XREF: start+2FEj
- if (gFastMode == FastModeTypeNone)
- {
- // isNotFastMode: //; CODE XREF: start+30Aj
- fadeToPalette(gBlackPalette);
- word_58467 = 1;
- }
-
- // Can't think of a better way to handle this right now, but at this point we had a "jmp loc_46FBE" which basically
- // skipped a few lines of the loop in the first iteration. Will revisit later when I have more knowledge of the code.
- //
- uint8_t shouldSkipFirstPart = 1;
-
- do
- {
- // loc_46F3E: //; CODE XREF: start+428j start+444j
- if (shouldSkipFirstPart == 0)
- {
- readLevels(); // 01ED:02F7
- fadeToPalette(gBlackPalette);
- gIsGameBusy = 0;
- drawPlayerList();
- initializeGameInfo();
- drawFixedLevel();
- drawGamePanel(); // 01ED:0311
- uint16_t numberOfInfotrons = convertToEasyTiles();
- resetNumberOfInfotrons(numberOfInfotrons);
- findMurphy();
- gCurrentPanelHeight = kPanelBitmapHeight;
- drawCurrentLevelViewport(gCurrentPanelHeight); // Added by me
- if (gFastMode != FastModeTypeUltra)
- {
- fadeToPalette(gGamePalette); // At this point the screen fades in and shows the game
- }
-
- if (isMusicEnabled == 0)
- {
- stopMusic();
- }
-
- // loc_46F77: //; CODE XREF: start+352j
- gIsGameBusy = 1;
- runLevel();
- gIsSPDemoAvailableToRun = 0;
- if (gShouldExitGame != 0)
- {
- break; // goto loc_47067;
- }
-
- // loc_46F8E: //; CODE XREF: start+369j
- if (gFastMode != FastModeTypeNone)
- {
- break;
- }
-
- // isNotFastMode2: //; CODE XREF: start+373j
- slideDownGameDash(); // 01ED:0351
- if (byte_59B71 != 0)
- {
- loadMurphySprites();
- }
-
- // loc_46FA5: //; CODE XREF: start+380j
- gIsGameBusy = 0;
- if (gShouldExitGame != 0)
- {
- break; // goto loc_47067;
- }
-
- // loc_46FB4: //; CODE XREF: start+38Fj
- if (isMusicEnabled == 0)
- {
- playMusicIfNeeded();
- }
- }
-
- shouldSkipFirstPart = 0;
-
- // loc_46FBE: //; CODE XREF: start+30Cj start+31Bj ...
- prepareDemoRecordingFilename(); // 01ED:037A
-
- uint8_t levelNumberForcedToLoad = 0;
-
- if (gIsSPDemoAvailableToRun == 2)
- {
- gIsSPDemoAvailableToRun = 1;
- if (fileIsDemo == 1)
- {
- playDemo(0);
- }
- else
- {
- // loc_46FDF: //; CODE XREF: start+3B5j
- gIsPlayingDemo = 0;
- }
-
- // loc_46FE4: //; CODE XREF: start+3BDj
- gShouldUpdateTotalLevelTime = 0;
- gHasUserCheated = 1;
- memcpy(&gSPDemoFileName[3], "---", 3);
- // loc_4701A: //; CODE XREF: start+3DDj start+433j
- startDirectlyFromLevel(1);
- continue;
- }
- else
- {
- // loc_46FFF: //; CODE XREF: start+3A9j
- levelNumberForcedToLoad = gIsForcedLevel;
- gIsForcedLevel = 0;
- gIsPlayingDemo = 0;
-
- if (levelNumberForcedToLoad > 0)
- {
- convertLevelNumberTo3DigitStringWithPadding0(levelNumberForcedToLoad);
- }
- }
-
- if (levelNumberForcedToLoad > 0)
- {
- // loc_4701A: //; CODE XREF: start+3DDj start+433j
- startDirectlyFromLevel(levelNumberForcedToLoad);
- continue;
- }
-
- // loc_4704B: //; CODE XREF: start+3EEj start+3F2j
- if (gShouldStartFromSavedSnapshot != 0)
- {
- // loc_4701A: //; CODE XREF: start+3DDj start+433j
- startDirectlyFromLevel(1);
- continue;
- }
- gHasUserCheated = 0;
- runMainMenu();
- } while (gShouldExitGame == 0);
-
- int runResult = 0;
-
- if (gFastMode != FastModeTypeNone)
- {
- char *message = "";
- if (gIsLevelStartedAsDemo == 0)
- {
- // loc_47094: //; CODE XREF: start+45Fj
- if (byte_5A19B == 0)
- {
- // loc_470A1: //; CODE XREF: start+479j
- message = "\"@\"-ERROR: Level(?) failed: ";
- runResult = 1;
- }
- else
- {
- message = "\"@\"-ERROR: Level(?) successful: ";
- }
- }
- else if (byte_5A19B == 0)
- {
- // loc_4708E: //; CODE XREF: start+466j
- message = "Demo failed: ";
- runResult = 1;
- }
- else
- {
- message = "Demo successful: ";
- }
-
- // printMessageAfterward: //; CODE XREF: start+46Cj start+472j ...
- spLogDemo("%s%s", message, demoFileName);
- }
- else
- {
- // loc_47067: //; CODE XREF: start+36Bj start+391j ...
- fadeToPalette(gBlackPalette); // 0x60D5
-
- writeAdvancedConfig();
- }
-
- // Tidy up
- destroyAudio();
- destroyLogging();
- destroyVideo();
- destroySystem();
-
- return runResult;
-}
-
-void initializeGameStateData()
-{
- // Initialize game state with the same values as in the original game
- static const uint16_t kLevelStatePrecedingPadding[kSizeOfLevelStatePrecedingPadding] = {
- 0x8995, 0x8995, 0x8995, 0x8a3b, 0x8a3b, 0x8a3b, 0x8a3b, 0x8a3b,
- 0x8a3b, 0x8a3b, 0x8a3b, 0x8ae8, 0x8ae8, 0x8ae8, 0x8ae8, 0x8ae8,
- 0x8ae8, 0x8ae8, 0x8ae8, 0x8bb1, 0x8bb1, 0x8bb1, 0x8bb1, 0x8bb1,
- 0x8bb1, 0x8bb1, 0x8bb1, 0x8c85, 0x8c85, 0x8c85, 0x8c85, 0x8c85,
- 0x8c85, 0x8c85, 0x8c85, 0x8d5b, 0x8d5b, 0x8d5b, 0x8d5b, 0x8d5b,
- 0x8d5b, 0x8d5b, 0x8d5b, 0x8e06, 0x8e06, 0x8e06, 0x8e06, 0x8e06,
- 0x8e06, 0x8e06, 0x8e06, 0x8eac, 0x8eac, 0x8eac, 0x8eac, 0x8eac,
- 0x8eac, 0x8eac, 0x8eac, 0x8f59, 0x8f59, 0x8f59, 0x8f59, 0x8f59,
- 0x8f59, 0x8f59, 0x8f59, 0x0000, 0x1370, 0x0000, 0x0000, 0x17e8,
- 0x0000, 0x0000, 0x0000, 0x3869, 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x0000, 0x86d0, 0x0000, 0x34b2, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x8b8f, 0x341d, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x0000, 0x3923, 0x0909, 0x0c00, 0x0800, 0x5800, 0x0000,
- 0x0000, 0x2500, 0x0677, 0x007f, 0x0000, 0x0001, 0x0000, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x0000, 0xec00, 0x2606, 0x0005, 0x0000,
- 0x0000, 0x0100, 0x0000, 0x0000, 0x3231, 0x3433, 0x3635, 0x3837,
- 0x3039, 0x002d, 0x0008, 0x5751, 0x5245, 0x5954, 0x4955, 0x504f,
- 0x0000, 0x000a, 0x5341, 0x4644, 0x4847, 0x4b4a, 0x004c, 0x0000,
- 0x0000, 0x585a, 0x5643, 0x4e42, 0x004d, 0x0000, 0x0000, 0x2000,
- 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x002e, 0x001e, 0x0031, 0x0014, 0x0039,
- 0x001f, 0x0014, 0x0018, 0xffff, 0x0001, 0x4c01, 0x5645, 0x4c45,
- 0x2e53, 0x4144, 0x0054, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
- 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000};
-
- for (int idx = 0; idx < kSizeOfLevelStatePrecedingPadding; ++idx)
- {
- uint16_t value = kLevelStatePrecedingPadding[idx];
- value = convert16LE(value);
- StatefulLevelTile *tile = &gCurrentLevelStateWithPadding[idx];
- tile->tile = (value & 0xFF);
- tile->state = (value >> 8);
- }
-
- gFrameCounter = 0xF000;
-}
-
-void startDirectlyFromLevel(uint8_t levelNumber)
-{
- gIsGameBusy = 1;
- gShouldAutoselectNextLevelToPlay = 1;
- prepareLevelDataForCurrentPlayer();
- drawPlayerList();
- word_58467 = 0;
- playMusicIfNeeded();
- gCurrentSelectedLevelIndex = levelNumber;
- restoreLastMouseAreaBitmap();
- drawLevelList();
- gShouldLeaveMainMenu = 0;
- byte_5A19B = 0;
-}
-
-void slideDownGameDash() // proc near ; CODE XREF: start:isNotFastMode2p
-{
- // 01ED:04ED
- if (gShouldShowGamePanel == 0)
- {
- return;
- }
-
- for (int panelHeight = kPanelBitmapHeight; panelHeight > 0; --panelHeight)
- {
- // Move the bottom panel line by line to the bottom of the screen
- for (int y = kScreenHeight - 2; y >= kScreenHeight - panelHeight; --y)
- {
- uint16_t srcAddress = y * kScreenWidth;
- uint16_t dstAddress = (y + 1) * kScreenWidth;
- memcpy(&gScreenPixels[dstAddress], &gScreenPixels[srcAddress], kScreenWidth);
- }
-
- drawCurrentLevelViewport(panelHeight);
- videoLoop();
- }
-}
-
-/// This alternative int9 handler seems to control the keys +, -, *, / in the numpad
-/// to alter the game speed, and also the key X for something else.
-/// @param shouldYieldCpu If 1, will sleep the thread for a bit to prevent 100% CPU usage.
-void int9handler(uint8_t shouldYieldCpu) // proc far ; DATA XREF: setint9+1Fo
-{
- updateKeyboardState();
-
- // 01ED:0659
-
- if (gIsLeftAltPressed && gIsEnterPressed)
- {
- toggleFullscreen();
- }
-
- // storeKey: ; CODE XREF: int9handler+2Bj
- if (gIsInMainMenu == 0)
- {
- if (gIsNumpadMultiplyPressed) // Key * in the numpad, restore speed
- {
- gGameSpeed = kDefaultGameSpeed;
- updateDemoRecordingLowestSpeed();
- }
- // checkSlash: ; CODE XREF: int9handler+3Ej
- // ; int9handler+45j
- else if (gIsNumpadDividePressed) // Keypad / -> fastest playback seed
- {
- gGameSpeed = kNumberOfGameSpeeds - 1;
- updateDemoRecordingLowestSpeed();
- }
- // checkPlus: ; CODE XREF: int9handler+54j
- else if (gIsGameSpeedChangeButtonPressed == 0)
- {
- if (isIncreaseGameSpeedButtonPressed())
- {
- increaseGameSpeed();
- }
- // checkMinus: ; CODE XREF: int9handler+65j
- // ; int9handler+71j
- else if (isDecreaseGameSpeedButtonPressed())
- {
- decreaseGameSpeed();
- }
- }
-
- gIsGameSpeedChangeButtonPressed = (isIncreaseGameSpeedButtonPressed() || isDecreaseGameSpeedButtonPressed());
- }
-
- // checkX: ; CODE XREF: int9handler+39j
- // ; int9handler+60j ...
- if (gIsXKeyPressed && gIsLeftAltPressed != 0)
- {
- gShouldExitLevel = 1;
- gShouldExitGame = 1;
- }
-
- if (shouldYieldCpu)
- {
- waitTime(10); // Avoid burning the CPU
- }
-}
-
-void readConfig() // proc near ; CODE XREF: start:loc_46F0Fp
-{
- if (gFastMode == FastModeTypeUltra)
- {
- return;
- }
-
- FILE *file = openWritableFile("SUPAPLEX.CFG", "rb");
- if (file == NULL)
- {
- if (errno == ENOENT || errno == ENOSYS || errno == EIO) // ax == 2? ax has error code, 2 is file not found (http://stanislavs.org/helppc/dos_error_codes.html)
- {
- // loc_47551: //; CODE XREF: readConfig+Fj
- // ; readConfig+17j
- activateCombinedSound();
- isMusicEnabled = 1;
- isFXEnabled = 1;
- isJoystickEnabled = 0;
- return;
- }
- else
- {
- exitWithError("Error opening SUPAPLEX.CFG\n");
- }
- }
-
- // loc_474BE: // ; CODE XREF: readConfig+8j
-
- uint8_t configData[kConfigDataLength];
-
- size_t bytes = fileReadBytes(configData, sizeof(configData), file);
-
- if (fclose(file) != 0)
- {
- exitWithError("Error closing SUPAPLEX.CFG\n");
- }
-
- // loc_474DF: // ; CODE XREF: readConfig+39j
- if (bytes < sizeof(configData))
- {
- exitWithError("Error reading SUPAPLEX.CFG\n");
- }
-
- // loc_474E5: // ; CODE XREF: readConfig+3Fj
- uint8_t soundSetting = configData[0];
-
- if (soundSetting == 's')
- {
- activateInternalSamplesSound();
- }
- else if (soundSetting == 'a')
- {
- activateAdlibSound();
- }
- else if (soundSetting == 'b')
- {
- activateSoundBlasterSound();
- }
- else if (soundSetting == 'r')
- {
- activateRolandSound();
- }
- else if (soundSetting == 'c')
- {
- activateCombinedSound();
- }
- else
- {
- activateInternalStandardSound();
- }
-
- // loc_4751D: // ; CODE XREF: readConfig+4Fj
- // ; readConfig+59j ...
-
- isJoystickEnabled = 0;
- if (configData[1] == 'j')
- {
- isJoystickEnabled = 1;
- // calibrateJoystick(); not needed anymore
- }
-
- // loc_47530: //; CODE XREF: readConfig+85j
- isMusicEnabled = (configData[2] == 'm');
-
- // loc_47540: //; CODE XREF: readConfig+98j
- isFXEnabled = (configData[3] == 'x');
-}
-
-void saveConfiguration() // sub_4755A proc near ; CODE XREF: code:loc_4CAECp
-{
- FILE *file = openWritableFile("SUPAPLEX.CFG", "wb");
- if (file == NULL)
- {
- spLogInfo("Error opening SUPAPLEX.CFG\n");
- return;
- }
-
- // loc_4756A: ; CODE XREF: saveConfiguration+Bj
- uint8_t configData[kConfigDataLength];
-
- if (sndType == SoundTypeInternalSamples)
- {
- configData[0] = 's';
- }
- else if (sndType == SoundTypeInternalStandard)
- {
- configData[0] = 'i';
- }
- else if (sndType == SoundTypeAdlib)
- {
- configData[0] = 'a';
- }
- else if (sndType == SoundTypeRoland)
- {
- configData[0] = 'r';
- }
- else if (musType == SoundTypeRoland)
- {
- configData[0] = 'c';
- }
- else
- {
- configData[0] = 'b';
- }
-
- // loc_475AF: ; CODE XREF: saveConfiguration+20j
- // ; saveConfiguration+2Cj ...
- if (isJoystickEnabled == 0)
- {
- configData[1] = 'k';
- }
- else
- {
- configData[1] = 'j';
- }
-
- // loc_475BF: ; CODE XREF: saveConfiguration+60j
- if (isMusicEnabled != 0)
- {
- configData[2] = 'm';
- }
- else
- {
- configData[2] = 'n';
- }
-
- // loc_475CF: ; CODE XREF: saveConfiguration+70j
- if (isFXEnabled != 0)
- {
- configData[3] = 'x';
- }
- else
- {
- configData[3] = 'y';
- }
-
- // loc_475DF: ; CODE XREF: saveConfiguration+80j
- size_t bytes = fileWriteBytes(configData, sizeof(configData), file);
- if (bytes < sizeof(configData))
- {
- exitWithError("Error writing SUPAPLEX.CFG\n");
- }
-
- if (fclose(file) != 0)
- {
- exitWithError("Error closing SUPAPLEX.CFG\n");
- }
-}
-
-/// @return Number of demo files read
-uint8_t readDemoFiles() // proc near ; CODE XREF: readEverything+12p
- // ; handleDemoOptionClickp ...
-{
- // 01ED:09A6
-
- gDemoCurrentInputIndex = 0;
- word_5A33C = 0;
-
- memset(&gDemos.demoFirstIndices, 0xFF, sizeof(gDemos.demoFirstIndices)); // fills 11 words (22 bytes) with 0xFFFF
-
- for (int i = 0; i < kNumberOfDemos; ++i)
- {
- // loc_47629: //; CODE XREF: readDemoFiles+175j
- gSelectedOriginalDemoLevelNumber = 0;
- char *filename = gDemo0BinFilename;
-
- if (gIsSPDemoAvailableToRun == 1)
- {
- filename = demoFileName;
- }
- else
- {
- // loc_4763C: // ; CODE XREF: readDemoFiles+2Cj
- gDemo0BinFilename[4] = '0' + i; // Replaces the number in "DEMO0.BIN" with the right value
- }
-
- // loc_47647: // ; CODE XREF: readDemoFiles+31j
- FILE *file = openWritableFileWithReadonlyFallback(filename, "rb");
- if (file == NULL)
- {
- return i;
- }
-
- // loc_47651: //; CODE XREF: readDemoFiles+43j
- if (gIsSPDemoAvailableToRun == 1)
- {
- if (gSelectedOriginalDemoFromCommandLineLevelNumber == 0)
- {
- fseek(file, kLevelDataLength, SEEK_SET);
- }
- }
- else
- {
- // loc_47674: // ; CODE XREF: readDemoFiles+52j
- int result = fseek(file, 0, SEEK_END);
- long fileSize = ftell(file);
-
- // this is probably to support old level formats
- if (result == 0 && fileSize < kLevelDataLength)
- {
- gSelectedOriginalDemoLevelNumber = getLevelNumberFromOriginalDemoFile(file, fileSize);
- }
-
- // loc_47690: // ; CODE XREF: readDemoFiles+76j readDemoFiles+7Aj ...
- fseek(file, 0, SEEK_SET);
-
- if (gSelectedOriginalDemoLevelNumber == 0)
- {
- Level *level = &gDemos.level[i];
- size_t bytes = fileReadBytes(level, kLevelDataLength, file);
-
- if (bytes < kLevelDataLength)
- {
- return i;
- }
-
- // loc_476D3: // ; CODE XREF: readDemoFiles+C5j
- gDemoRandomSeeds[i] = level->randomSeed;
- }
- }
-
- // loc_476DB: // ; CODE XREF: readDemoFiles+59j readDemoFiles+69j ...
- uint16_t maxNumberOfBytesToRead = kMaxDemoInputSteps + 1; // 48649
- maxNumberOfBytesToRead -= gDemoCurrentInputIndex;
-
- if (maxNumberOfBytesToRead > kMaxDemoInputSteps + 1) // weird way of checking if gDemoCurrentInputIndex < 0 ????
- {
- maxNumberOfBytesToRead = 0;
- }
-
- uint16_t numberOfDemoBytesRead = 0;
-
- // loc_476EA: // ; CODE XREF: readDemoFiles+DDj
- if (maxNumberOfBytesToRead == 0)
- {
- numberOfDemoBytesRead = 0;
- }
- else
- {
- // loc_476F3: // ; CODE XREF: readDemoFiles+E4j
- numberOfDemoBytesRead = fileReadBytes(&gDemos.demoData[gDemoCurrentInputIndex], maxNumberOfBytesToRead, file);
-
- if (numberOfDemoBytesRead == 0)
- {
- if (fclose(file) != 0)
- {
- exitWithError("Error closing DEMO file");
- }
- return i;
- }
-
- // loc_47719: // ; CODE XREF: readDemoFiles+FCj
- }
-
- // loc_4771A: // ; CODE XREF: readDemoFiles+E8j
- if (fclose(file) != 0)
- {
- exitWithError("Error closing DEMO file");
- }
-
- // loc_47729: ; CODE XREF: readDemoFiles+11Bj
- gDemos.demoData[gDemoCurrentInputIndex] = gDemos.demoData[gDemoCurrentInputIndex] & 0x7F; // this removes the MSB from the levelNumber that was added in the speed fix mods
- int isZero = (gSelectedOriginalDemoLevelNumber == 0);
- gSelectedOriginalDemoLevelNumber = 0;
- if (isZero)
- {
- gDemos.demoData[gDemoCurrentInputIndex] = gDemos.demoData[gDemoCurrentInputIndex] | 0x80; // This sets the MSB?? maybe the "interpreter" later needs it
- }
-
- // loc_47743: // ; CODE XREF: readDemoFiles+134j
- uint16_t demoLastByteIndex = gDemoCurrentInputIndex + numberOfDemoBytesRead - 1;
- // cx = bx; // bx here has the value of gDemoCurrentInputIndex
- // bx += numberOfDemoBytesRead; // ax here has the number of bytes read regarding the level itself (levelNumber + inputSteps)
- // push(ds);
- // push(es);
- // pop(ds);
- // assume ds:nothing
- // bx--;
- if (demoLastByteIndex == 0xFFFF // this would mean bx was 0. is this possible?
- || numberOfDemoBytesRead <= 1 // this means the demo is empty (only has levelNumber or nothing)
- || gDemos.demoData[demoLastByteIndex] != 0xFF)
- {
- // loc_4775A: // ; CODE XREF: readDemoFiles+145j
- // ; readDemoFiles+14Aj
- if (demoLastByteIndex < sizeof(BaseDemo))
- {
- numberOfDemoBytesRead++;
- gDemos.demoData[demoLastByteIndex + 1] = 0xFF;
- }
- }
-
- // loc_47765: // ; CODE XREF: readDemoFiles+14Fj
- // ; readDemoFiles+155j
- gDemos.demoFirstIndices[i] = gDemoCurrentInputIndex;
- gDemoCurrentInputIndex += numberOfDemoBytesRead;
- }
-
- return kNumberOfDemos;
-}
-
-void openCreditsBlock() // proc near ; CODE XREF: start+2E9p
-{
- static const int kEdgeWidth = 13;
- static const int kEdgeHeight = 148;
- static const int kEdgeStep = 4;
- static const int kEdgeTopY = 26;
- static const int kNumberOfFrames = 60;
-
- const uint32_t kAnimationDuration = kNumberOfFrames * 1000 / 70; // ~429 ms
-
- uint32_t animationTime = 0;
-
- static const int kInitialLeftEdgeX = 147;
- const int kInitialRightEdgeX = kInitialLeftEdgeX + kEdgeWidth + 1;
-
- const int kEdgeAnimationDistance = (kEdgeStep * kNumberOfFrames) / 2 + 1;
-
- int leftEdgeX = kInitialLeftEdgeX;
- int rightEdgeX = kInitialRightEdgeX;
-
- startTrackingRenderDeltaTime();
-
- while (animationTime < kAnimationDuration)
- {
- // loc_47800: // ; CODE XREF: openCreditsBlock+AFj
- animationTime += updateRenderDeltaTime();
- animationTime = MIN(animationTime, kAnimationDuration);
-
- float animationFactor = (float)animationTime / kAnimationDuration;
-
- int previousLeftEdgeX = leftEdgeX;
- int previousRightEdgeX = rightEdgeX;
-
- int distance = ceilf(kEdgeAnimationDistance * animationFactor);
-
- leftEdgeX = kInitialLeftEdgeX - distance;
- rightEdgeX = kInitialRightEdgeX + distance;
-
- int leftEdgeStep = previousLeftEdgeX - leftEdgeX;
- int rightEdgeStep = rightEdgeX - previousRightEdgeX;
-
- // This loop moves both edges of the panel, and fills the inside of the panel with the contents of TITLE2.DAT
- for (int y = kEdgeTopY; y < kEdgeTopY + kEdgeHeight; ++y)
- {
- // Left edge
- for (int x = leftEdgeX; x < previousLeftEdgeX + kEdgeWidth - leftEdgeStep; ++x)
- {
- long addr = y * kScreenWidth + x;
- gScreenPixels[addr] = gScreenPixels[addr + leftEdgeStep]; // Move panel edge
- }
-
- // Content of visible panel unveiled by left edge
- for (int x = leftEdgeX + kEdgeWidth; x < previousLeftEdgeX + kEdgeWidth + 1; ++x)
- {
- long addr = y * kScreenWidth + x;
- gScreenPixels[addr] = gTitle2DecodedBitmapData[addr];
- }
-
- // Right edge
- for (int x = rightEdgeX + kEdgeWidth; x > previousRightEdgeX; --x)
- {
- long addr = y * kScreenWidth + x;
- gScreenPixels[addr] = gScreenPixels[addr - rightEdgeStep]; // Move panel edge
- }
-
- // Content of visible panel unveiled by right edge
- for (int x = previousRightEdgeX; x < rightEdgeX; ++x)
- {
- long addr = y * kScreenWidth + x;
- gScreenPixels[addr] = gTitle2DecodedBitmapData[addr];
- }
- }
-
- videoLoop();
- }
-
- // loc_47884: // ; CODE XREF: openCreditsBlock+C7j
- // Display now the contents of TITLE2.DAT starting at the y=panel_edge_top_y (to prevent removing the top title)
- // This basically makes the edges of the panel docked at the sides of the screen look better (as intended in TITLE2.DAT
- // compared to how they look when the "crafted" animation concludes).
- //
- size_t copyOffset = kEdgeTopY * kScreenWidth;
- memcpy(gScreenPixels + copyOffset, gTitle2DecodedBitmapData + copyOffset, sizeof(gTitle2DecodedBitmapData) - copyOffset);
-
- ColorPalette title2Palette;
- convertPaletteDataToPalette(gTitle2PaletteData, title2Palette);
- fadeToPalette(title2Palette); // fades current frame buffer into the title 2.dat (screen with the credits)
-}
-
-void loadScreen2() // proc near ; CODE XREF: start:loc_46F00p
-{
- readAndRenderTitle1Dat();
-
- // loc_4792E: //; CODE XREF: loadScreen2+76j
- ColorPalette title1DatPalette;
- convertPaletteDataToPalette(gTitle1PaletteData, title1DatPalette);
- setPalette(title1DatPalette);
- videoLoop();
-
- readTitle2Dat();
-}
-
-void readLevelsLst() // proc near ; CODE XREF: readLevelsLst+CCj
- // ; readEverything+Fp ...
-{
- // 01ED:1038
-
- char paddingEntryText[kListLevelNameLength] = " ";
- for (int i = 0; i < kNumberOfLevelsWithPadding; ++i)
- {
- memcpy(&gPaddedLevelListData[i * kListLevelNameLength], paddingEntryText, sizeof(paddingEntryText));
- }
- memcpy(&gPaddedLevelListData[kLastLevelIndex * kListLevelNameLength],
- "- REPLAY SKIPPED LEVELS!! -",
- kListLevelNameLength);
- memcpy(&gPaddedLevelListData[(kLastLevelIndex + 1) * kListLevelNameLength],
- "---- UNBELIEVEABLE!!!! ----",
- kListLevelNameLength);
-
- FILE *file = openWritableFile(gLevelLstFilename, "rb");
- if (file == NULL)
- {
- // errorOpeningLevelLst: // ; CODE XREF: readLevelsLst+8j
- FILE *file = openReadonlyFile(gLevelsDatFilename, "rb");
- if (file == NULL)
- {
- // errorOpeningLevelsDat: // ; CODE XREF: readLevelsLst+17j
- exitWithError("Error opening LEVELS.DAT\n");
- }
- // successOpeningLevelsDat: // ; CODE XREF: readLevelsLst+15j
-
- for (int i = 0; i < kNumberOfLevels; ++i)
- {
- // loc_47CC4: // ; CODE XREF: readLevelsLst:loc_47CE4j
- char number[5];
- sprintf(number, "%03d ", i + 1);
-
- memcpy(gLevelListData + i * kListLevelNameLength, number, sizeof(number) - 1);
- gLevelListData[i * kListLevelNameLength + kListLevelNameLength - 1] = '\n';
- // loc_47CE4: // ; CODE XREF: readLevelsLst+3Aj
- }
-
- for (int i = 0; i < kNumberOfLevels; ++i)
- {
- // loc_47CF1: // ; CODE XREF: readLevelsLst+83j
-
- int seekOffset = 0x5A6 + i * kLevelDataLength;
-
- fseek(file, seekOffset, SEEK_SET); // position 1446
- size_t bytes = fileReadBytes(gLevelListData + i * kListLevelNameLength + 4, kLevelNameLength - 1, file);
-
- if (bytes < kLevelNameLength - 1)
- {
- fclose(file);
- exitWithError("Error reading LEVELS.DAT\n");
- }
- }
- if (fclose(file) != 0)
- {
- exitWithError("Error closing LEVELS.DAT\n");
- }
-
- if (gShouldRecreateLevelLstIfNeeded == 0)
- {
- return;
- }
-
- // loc_47D35: // ; CODE XREF: readLevelsLst+95j
- file = openWritableFile(gLevelLstFilename, "wb");
- if (file == NULL)
- {
- exitWithError("Error opening %s\n", gLevelLstFilename);
- }
-
- // writeLevelLstData: // ; CODE XREF: readLevelsLst+A5j
- size_t bytes = fileWriteBytes(gLevelListData, kLevelListDataLength, file);
- if (bytes < kLevelListDataLength)
- {
- exitWithError("Error writing %s\n", gLevelLstFilename);
- }
-
- // loc_47D5B: // ; CODE XREF: readLevelsLst+BBj
- if (fclose(file) != 0)
- {
- exitWithError("Error closing %s\n", gLevelLstFilename);
- }
- return;
- }
-
- // successOpeningLevelLst: // ; CODE XREF: readLevelsLst+Aj
- size_t bytes = fileReadBytes(gLevelListData, kLevelListDataLength, file);
- if (bytes < kLevelListDataLength)
- {
- fclose(file);
- exitWithError("Error reading LEVEL.LST\n");
- }
-
- // loc_47D8D: // ; CODE XREF: readLevelsLst+8Aj
- // ; readLevelsLst:loc_47D5Bj ...
- if (fclose(file) != 0)
- {
- exitWithError("Error closing LEVEL.LST\n");
- }
-}
-
-void readPlayersLst() // proc near ; CODE XREF: readEverything+1Bp
- // ; handleFloppyDiskButtonClick+149p
-{
- if (gIsForcedCheatMode != 0)
- {
- return;
- }
-
- for (int i = 0; i < kNumberOfPlayers; ++i)
- {
- strcpy(gPlayerListData[i].name, "--------");
- }
-
- FILE *file = openWritableFile(gPlayerLstFilename, "rb");
- if (file == NULL)
- {
- return;
- }
-
- size_t bytes = fileReadBytes(gPlayerListData, sizeof(gPlayerListData), file);
- if (bytes == 0)
- {
- return;
- }
-
- fclose(file);
-}
-
-void readHallfameLst() // proc near ; CODE XREF: readEverything+18p
- // ; handleFloppyDiskButtonClick+146p
-{
- if (gIsForcedCheatMode != 0)
- {
- return;
- }
-
- FILE *file = openWritableFile(gHallfameLstFilename, "rb");
- if (file == NULL)
- {
- return;
- }
-
- size_t bytes = fileReadBytes(gHallOfFameData, sizeof(gHallOfFameData), file);
- if (bytes == 0)
- {
- return;
- }
-
- fclose(file);
-}
-
-void readEverything() // proc near ; CODE XREF: start+2DBp start+2E3p ...
-{
- // 01ED:1213
- readPalettes();
- readBitmapFonts();
- readPanelDat();
- readMenuDat();
- readControlsDat();
- readLevelsLst();
- readDemoFiles();
- readBackDat();
- readHallfameLst();
- readPlayersLst();
- readGfxDat();
-}
-
-void waitForKeyMouseOrJoystick() // sub_47E98 proc near ; CODE XREF: recoverFilesFromFloppyDisk+4Ap
-// ; handleStatisticsOptionClick+216p ...
-{
- byte_59B86 = 0;
-
- do
- {
- // keyIsPressed: ; CODE XREF: waitForKeyMouseOrJoystick+16j
- if (gIsEscapeKeyPressed != 0)
- {
- byte_59B86 = 0xFF;
- }
-
- int9handler(1);
- // loc_47EA9: ; CODE XREF: waitForKeyMouseOrJoystick+Aj
- } while (isAnyKeyPressed());
-
- uint16_t mouseButtonsStatus = 0;
-
- do
- {
- // mouseIsClicked: ; CODE XREF: waitForKeyMouseOrJoystick+1Ej
- getMouseStatus(NULL, NULL, &mouseButtonsStatus);
- } while (mouseButtonsStatus != 0);
-
- for (int i = 0; i < 4200; ++i)
- {
- // loc_47EC6: ; CODE XREF: waitForKeyMouseOrJoystick+57j
- videoLoop();
-
- getMouseStatus(NULL, NULL, &mouseButtonsStatus);
- int9handler(0);
- updateUserInput();
-
- if (mouseButtonsStatus != 0)
- {
- break;
- }
-
- if (isAnyKeyPressed())
- {
- break;
- }
-
- if (gCurrentUserInput > kUserInputSpaceAndDirectionOffset)
- {
- break;
- }
- }
-
- if (mouseButtonsStatus != 0)
- {
- // loc_47F02: ; CODE XREF: waitForKeyMouseOrJoystick+44j
- // ; waitForKeyMouseOrJoystick+70j
- do
- {
- getMouseStatus(NULL, NULL, &mouseButtonsStatus);
- } while (mouseButtonsStatus != 0);
- }
- else if (isAnyKeyPressed())
- {
- do
- {
- // loc_47F0C: ; CODE XREF: waitForKeyMouseOrJoystick+4Bj
- // ; waitForKeyMouseOrJoystick+85j
- if (gIsEscapeKeyPressed != 0)
- {
- byte_59B86 = 0xFF;
- }
-
- int9handler(1);
- } while (isAnyKeyPressed());
- }
- else if (gCurrentUserInput > kUserInputSpaceAndDirectionOffset)
- {
- do
- {
- // loc_47F21: ; CODE XREF: waitForKeyMouseOrJoystick+55j
- // ; waitForKeyMouseOrJoystick+9Dj
- if (gIsEscapeKeyPressed != 0)
- {
- byte_59B86 = 0xFF;
- }
-
- int9handler(1);
- updateUserInput();
- } while (gCurrentUserInput > kUserInputSpaceAndDirectionOffset);
- }
-}
-
-void updateZonkTiles(int16_t position) // proc near ; DATA XREF: data:160Co
-{
- // 01ED:132D
-
- StatefulLevelTile *currentTile = &gCurrentLevelState[position];
- StatefulLevelTile *belowTile = &gCurrentLevelState[position + kLevelWidth];
- StatefulLevelTile *belowLeftTile = &gCurrentLevelState[position + kLevelWidth - 1];
- StatefulLevelTile *leftTile = &gCurrentLevelState[position - 1];
- StatefulLevelTile *belowRightTile = &gCurrentLevelState[position + kLevelWidth + 1];
- StatefulLevelTile *rightTile = &gCurrentLevelState[position + 1];
- StatefulLevelTile *aboveRightTile = &gCurrentLevelState[position - kLevelWidth + 1];
-
- if (currentTile->tile != LevelTileTypeZonk) // cmp byte ptr leveldata[si], 1
- {
- return;
- }
-
- // loc_47F98: ; CODE XREF: movefun+5j
- uint8_t shouldSkipFirstPartOfLoop = 0;
-
- if (currentTile->tile != LevelTileTypeZonk || currentTile->state != 0)
- {
- shouldSkipFirstPartOfLoop = 1; // used to emulate "jmp loc_48035"
- }
- else
- {
- // loc_47FA4: ; CODE XREF: movefun+Fj
- if (gAreZonksFrozen == 2)
- {
- return;
- }
-
- // loc_47FAC: ; CODE XREF: movefun+19j
- // Check if the zonk can just fall vertically
- if (belowTile->tile == LevelTileTypeSpace && belowTile->state == 0)
- {
- // loc_47FF4: ; CODE XREF: movefun+23j
- currentTile->state = 0x40;
- shouldSkipFirstPartOfLoop = 1; // used to emulate "jmp loc_48035"
- }
- else
- {
- // Check if below the zonk is another object that could be used to slide down left or right
- if (belowTile->state != 0 || (belowTile->tile != LevelTileTypeZonk && belowTile->tile != LevelTileTypeInfotron && belowTile->tile != LevelTileTypeChip))
- {
- return;
- }
-
- // loc_47FC5: ; CODE XREF: movefun+28j
- // ; movefun+2Dj ...
- // Check if it can fall to the left side...
- if ((belowLeftTile->tile == LevelTileTypeSpace && belowLeftTile->state == 0) || (belowLeftTile->tile == 0x88 && belowLeftTile->state == 0x88) || (belowLeftTile->tile == 0xAA && belowLeftTile->state == 0xAA))
- {
- // loc_47FFB: ; CODE XREF: movefun+3Aj
- // ; movefun+42j ...
- // ...but only if the left tile is empty
- if (leftTile->state == 0 && leftTile->tile == LevelTileTypeSpace)
- {
- // loc_48004: ; CODE XREF: movefun+70j
- currentTile->state = 0x50;
- leftTile->state = 0x88;
- leftTile->tile = 0x88;
- shouldSkipFirstPartOfLoop = 1; // used to emulate "jmp loc_48035"
- }
- }
- }
- }
-
- uint8_t shouldSkipTo_loc_481C6 = 0;
-
- do
- {
- // loc_47FDC: ; CODE XREF: movefun+72j
- // ; movefun+1F1j
- if (shouldSkipFirstPartOfLoop == 0) // used to emulate "jmp loc_48035"
- {
- // Checks if it can fall to the right side
- if ((belowRightTile->state != 0 || belowRightTile->tile != LevelTileTypeSpace) && (belowRightTile->state != 0x88 || belowRightTile->tile != 0x88) && (belowRightTile->state != 0xAA || belowRightTile->tile != 0xAA))
- {
- return;
- }
- else
- {
- // loc_48011: ; CODE XREF: movefun+51j
- // ; movefun+59j ...
- // Only if the right tile is empty or... other circumstances?
- if ((rightTile->state != 0 || rightTile->tile != LevelTileTypeSpace) && ((rightTile->state != 0x99 || rightTile->tile != 0x99) || (aboveRightTile->state != 0 || aboveRightTile->tile != LevelTileTypeZonk)))
- {
- return;
- }
-
- // loc_48028: ; CODE XREF: movefun+86j
- // ; movefun+95j
- currentTile->state = 0x60;
- rightTile->state = 0x88;
- rightTile->tile = 0x88;
- }
- }
-
- shouldSkipFirstPartOfLoop = 0; // don't skip the first part in the next iteration
-
- // loc_48035: ; CODE XREF: movefun+11j
- // ; movefun+69j ...
- uint8_t state = currentTile->state;
- uint8_t stateType = state & 0xF0;
-
- if (stateType != 0x10) // 16
- {
- // loc_48045: ; CODE XREF: movefun+B1j
- if (stateType == 0x20) // 32
- {
- // loc_48212: ; CODE XREF: movefun+B9j
- // 01ED:15AF
- uint8_t stateFrame = state & 0x7; // module 8?
-
- uint8_t tileX = (position % kLevelWidth);
- uint8_t tileY = (position / kLevelWidth);
-
- uint16_t dstX = tileX * kTileSize;
- uint16_t dstY = tileY * kTileSize;
-
- // mov si, 1294h
- // mov si, [bx+si]
- Point frameCoordinates = kZonkSlideLeftAnimationFrameCoordinates[stateFrame];
-
- drawMovingSpriteFrameInLevel(frameCoordinates.x,
- frameCoordinates.y,
- kTileSize * 2,
- kTileSize,
- dstX, dstY);
-
- // 01ED:15D6
- state = currentTile->state; // mov bl, [si+1835h]
- state++;
-
- if (state == 0x24) // 36
- {
- rightTile->state = 0xAA;
- rightTile->tile = 0xAA;
- }
- // loc_4824A: ; CODE XREF: movefun+2B2j
- if (state == 0x26) // 38
- {
- currentTile->state = state;
- handleZonkStateAfterFallingOneTile(position + 1);
- return;
- }
- // loc_4825D: ; CODE XREF: movefun+2BDj
- else if (state < 0x28) // 40
- {
- currentTile->state = state;
- return;
- }
- // loc_48267: ; CODE XREF: movefun+2D0j
- else
- {
- currentTile->state = 0xFF;
- currentTile->tile = 0xFF;
- belowTile->state = 0x10;
- belowTile->tile = LevelTileTypeZonk;
- return;
- }
- }
- // loc_4804C: ; CODE XREF: movefun+B7j
- else if (stateType == 0x30) // 48
- {
- // loc_48277: ; CODE XREF: movefun+C0j
- uint8_t stateFrame = state & 0x7; // module 8?
-
- uint8_t tileX = ((position - 1) % kLevelWidth);
- uint8_t tileY = ((position - 1) / kLevelWidth);
-
- uint16_t dstX = tileX * kTileSize;
- uint16_t dstY = tileY * kTileSize;
-
- // mov si, 12A4h
- // mov si, [bx+si]
- Point frameCoordinates = kZonkSlideRightAnimationFrameCoordinates[stateFrame];
-
- drawMovingSpriteFrameInLevel(frameCoordinates.x,
- frameCoordinates.y,
- kTileSize * 2,
- kTileSize,
- dstX, dstY);
-
- state = currentTile->state;
- state++;
- if (state == 0x34) // 52
- {
- leftTile->state = 0xAA;
- leftTile->tile = 0xAA;
- }
- // loc_482AF: ; CODE XREF: movefun+317j
- if (state == 0x36) // 54
- {
- currentTile->state = state;
- handleZonkStateAfterFallingOneTile(position - 1); // left tile
- return;
- }
- // loc_482C2: ; CODE XREF: movefun+322j
- else if (state < 0x38) // 54
- {
- currentTile->state = state;
- return;
- }
- // loc_482CC: ; CODE XREF: movefun+335j
- else
- {
- currentTile->state = 0xFF;
- currentTile->tile = 0xFF;
- belowTile->state = 0x10;
- belowTile->tile = LevelTileTypeZonk;
- return;
- }
- }
- // loc_48053: ; CODE XREF: movefun+BEj
- else if (gAreZonksFrozen == 2)
- {
- return;
- }
- // loc_4805B: ; CODE XREF: movefun+C8j
- else if (stateType == 0x40) // 64
- {
- // loc_482DC: ; CODE XREF: movefun+CFj
- state++;
- if (state < 0x42) // 66
- {
- currentTile->state = state;
- return;
- }
- // loc_482E8: ; CODE XREF: movefun+351j
- else if (belowTile->tile != LevelTileTypeSpace || belowTile->state != 0) // cmp word ptr [si+18ACh], 0
- {
- state--;
- currentTile->state = state;
- return;
- }
- // loc_482F6: ; CODE XREF: movefun+35Dj
- else
- {
- currentTile->state = 0xFF;
- currentTile->tile = 0xFF;
- belowTile->state = 0x10;
- belowTile->tile = LevelTileTypeZonk;
-
- return;
- }
- }
- // loc_48062: ; CODE XREF: movefun+CDj
- else if (stateType == 0x50) // Zonk sliding left
- {
- // loc_4830A: ; CODE XREF: movefun+D6j
- uint8_t stateFrame = state & 0x7; // module 8?
-
- uint8_t tileX = ((position - 1) % kLevelWidth);
- uint8_t tileY = ((position - 1) / kLevelWidth);
-
- uint16_t dstX = tileX * kTileSize;
- uint16_t dstY = tileY * kTileSize;
-
- // mov si, 1294h
- // mov si, [bx+si]
- Point frameCoordinates = kZonkSlideLeftAnimationFrameCoordinates[stateFrame];
-
- drawMovingSpriteFrameInLevel(frameCoordinates.x,
- frameCoordinates.y,
- kTileSize * 2,
- kTileSize,
- dstX, dstY);
-
- state = currentTile->state;
- state++;
-
- if (state < 0x52) // 82
- {
- currentTile->state = state;
- return;
- }
- // loc_48341: ; CODE XREF: movefun+3AAj
- else if (belowLeftTile->state != 0 || belowLeftTile->tile != LevelTileTypeSpace) // cmp word ptr [si+18AAh], 0
- {
- // loc_48371: ; CODE XREF: movefun+3B6j
- // ; movefun+3C5j
- state--;
- currentTile->state = state;
- return;
- }
- else if ((leftTile->state != 0 || leftTile->tile != LevelTileTypeSpace) // cmp word ptr [si+1832h], 0
- && (leftTile->state != 0x88 || leftTile->tile != 0x88)) // cmp word ptr [si+1832h], 8888h
- {
- // loc_48371: ; CODE XREF: movefun+3B6j
- // ; movefun+3C5j
- state--;
- currentTile->state = state;
- return;
- }
- else
- {
- // loc_48357: ; CODE XREF: movefun+3BDj
- currentTile->state = 0xFF;
- currentTile->tile = 0xFF;
- leftTile->state = 0x22;
- leftTile->tile = LevelTileTypeZonk;
- belowLeftTile->state = 0xFF;
- belowLeftTile->tile = 0xFF;
- return;
- }
- }
- // loc_48069: ; CODE XREF: movefun+D4j
- else if (stateType == 0x60) // 96
- {
- // loc_48378: ; CODE XREF: movefun+DDj
- uint8_t stateFrame = state & 0x7; // module 8?
-
- uint8_t tileX = (position % kLevelWidth);
- uint8_t tileY = (position / kLevelWidth);
-
- uint16_t dstX = tileX * kTileSize;
- uint16_t dstY = tileY * kTileSize;
-
- // mov si, 12A4h
- Point frameCoordinates = kZonkSlideRightAnimationFrameCoordinates[stateFrame];
-
- drawMovingSpriteFrameInLevel(frameCoordinates.x,
- frameCoordinates.y,
- kTileSize * 2,
- kTileSize,
- dstX, dstY);
-
- state = currentTile->state;
- state++;
-
- if (state < 0x62) // 98
- {
- currentTile->state = state;
- return;
- }
- // loc_483AF: ; CODE XREF: movefun+418j
- else if (belowRightTile->state != 0 || belowRightTile->tile != LevelTileTypeSpace) // cmp word ptr [si+18AEh], 0
- {
- // loc_483DF: ; CODE XREF: movefun+424j
- // ; movefun+433j
- state--;
- currentTile->state = state;
- return;
- }
- else if ((rightTile->state != 0 || rightTile->tile != LevelTileTypeSpace) // cmp word ptr [si+1836h], 0
- && (rightTile->state != 0x88 || rightTile->tile != 0x88)) // cmp word ptr [si+1836h], 8888h
- {
- // loc_483DF: ; CODE XREF: movefun+424j
- // ; movefun+433j
- state--;
- currentTile->state = state;
- return;
- }
- else
- {
- // loc_483C5: ; CODE XREF: movefun+42Bj
- currentTile->state = 0xFF;
- currentTile->tile = 0xFF;
- rightTile->state = 0x32;
- rightTile->tile = LevelTileTypeZonk;
- belowRightTile->state = 0xFF;
- belowRightTile->tile = 0xFF;
- return;
- }
- }
- // loc_48070: ; CODE XREF: movefun+DBj
- else if (stateType == 0x70) // 112
- {
- // loc_483E6: ; CODE XREF: movefun+E4j
- if ((belowTile->state != 0 || belowTile->tile != LevelTileTypeSpace) // cmp word ptr [si+18ACh], 0
- && (belowTile->state != 0x99 && belowTile->tile != 0x99)) // cmp word ptr [si+18ACh], 9999h
- {
- return;
- }
-
- // loc_483F6: ; CODE XREF: movefun+45Bj
- // ; movefun+463j
- currentTile->state = 0xFF;
- currentTile->tile = 0xFF;
-
- // Move down and update tiles
- position += kLevelWidth;
-
- currentTile = &gCurrentLevelState[position];
- belowTile = &gCurrentLevelState[position + kLevelWidth];
- belowLeftTile = &gCurrentLevelState[position + kLevelWidth - 1];
- leftTile = &gCurrentLevelState[position - 1];
- belowRightTile = &gCurrentLevelState[position + kLevelWidth + 1];
- rightTile = &gCurrentLevelState[position + 1];
- aboveRightTile = &gCurrentLevelState[position - kLevelWidth + 1];
-
- currentTile->state = 0x10;
- currentTile->tile = LevelTileTypeZonk;
- }
- // locret_48077: ; CODE XREF: movefun+E2j
- else
- {
- return;
- }
- }
-
- // loc_48078: ; CODE XREF: movefun+B3j
- // ; movefun+475j
- // This animates the Zonk falling
- // 01ED:1415
- uint8_t somePositionThing = state;
- somePositionThing *= 2;
- somePositionThing &= 0x1F;
-
- uint16_t offset = kFallAnimationGravityOffsets[somePositionThing];
-
- uint16_t finalPosition = position - kLevelWidth;
- uint8_t tileX = (finalPosition % kLevelWidth);
- uint8_t tileY = (finalPosition / kLevelWidth);
-
- uint16_t dstX = tileX * kTileSize + (offset % 122);
- uint16_t dstY = tileY * kTileSize + (offset / 122);
-
- drawMovingSpriteFrameInLevel(224, 82,
- kTileSize,
- kTileSize + 2,
- dstX, dstY);
-
- uint8_t newState = currentTile->state;
- newState++;
- if (newState == 0x16) // 22
- {
- currentTile->state = newState;
- handleZonkStateAfterFallingOneTile(position - kLevelWidth); // Tile above
- return;
- }
- // loc_480BB: ; CODE XREF: movefun+11Bj
- else if (newState < 0x18) // 24
- {
- currentTile->state = newState;
- return;
- }
-
- // loc_480C5: ; CODE XREF: movefun+12Ej
- // This part handles what to do when the zonk finished falling 1 tile
- // 01ED:1462
- currentTile->state = 0;
- if (gAreZonksFrozen == 2)
- {
- return;
- }
-
- // loc_480D2: ; CODE XREF: movefun+13Fj
- if ((belowTile->tile == LevelTileTypeSpace && belowTile->state == 0) // cmp word ptr [si+18ACh], 0
- || (belowTile->tile == 0x99 && belowTile->state == 0x99)) // cmp word ptr [si+18ACh], 9999h
- {
- // loc_4816D: ; CODE XREF: movefun+149j
- // ; movefun+154j
- currentTile->state = 0x70;
- currentTile->tile = LevelTileTypeZonk;
- belowTile->state = 0x99;
- belowTile->tile = 0x99;
- return;
- }
-
- // loc_480E7: ; CODE XREF: movefun+152j
- if (belowTile->tile == LevelTileTypeMurphy) // cmp byte ptr [si+18ACh], 3
- {
- break;
- }
-
- // loc_480F1: ; CODE XREF: movefun+15Cj
- if (belowTile->tile == LevelTileTypeSnikSnak) // cmp byte ptr [si+18ACh], 11h
- {
- // loc_481FE: ; CODE XREF: movefun+168j
- // ; movefun+188j ...
- detonateBigExplosion(position + kLevelWidth); // Tile below
- return;
- }
-
- // loc_480FB: ; CODE XREF: movefun+166j
- if (belowTile->tile == 0xBB && belowTile->state == 0x2) // cmp word ptr [si+18ACh], 2BBh
- {
- shouldSkipTo_loc_481C6 = 1;
- break;
- }
-
- // loc_48106: ; CODE XREF: movefun+171j
- if (belowTile->tile == 0xBB && belowTile->state == 0x4) // cmp word ptr [si+18ACh], 4BBh
- {
- // loc_481E2: ; CODE XREF: movefun+17Ej
- if (belowRightTile->tile == LevelTileTypeElectron) // cmp byte ptr [si+18AEh], 18h
- {
- belowTile->tile = LevelTileTypeElectron;
- belowTile->state = 0;
- }
- // loc_481EF: ; CODE XREF: movefun+257j
- if (belowRightTile->tile != LevelTileTypeExplosion) // cmp byte ptr [si+18AEh], 1Fh
- {
- belowRightTile->tile = LevelTileTypeSpace;
- belowRightTile->state = 0;
- }
- // loc_481FE: ; CODE XREF: movefun+168j
- // ; movefun+188j ...
- detonateBigExplosion(position + kLevelWidth); // Tile below
- return;
- }
-
- // loc_48111: ; CODE XREF: movefun+17Cj
- if (belowTile->tile == LevelTileTypeElectron) // cmp byte ptr [si+18ACh], 18h
- {
- // loc_481FE: ; CODE XREF: movefun+168j
- // ; movefun+188j ...
- detonateBigExplosion(position + kLevelWidth); // Tile below
- return;
- }
-
- // loc_4811B: ; CODE XREF: movefun+186j
- if (belowTile->tile == LevelTileTypeOrangeDisk && belowTile->state == 0) // cmp word ptr [si+18ACh], 8
- {
- // loc_48205: ; CODE XREF: movefun+192j
- gExplosionTimers[position + kLevelWidth] = 6;
- return;
- }
-
- // loc_48125: ; CODE XREF: movefun+190j
- playFallSound();
- if ((belowTile->tile != LevelTileTypeZonk || belowTile->state != 0) // cmp word ptr [si+18ACh], 1
- && (belowTile->tile != LevelTileTypeInfotron || belowTile->state != 0) // cmp word ptr [si+18ACh], 4
- && (belowTile->tile != LevelTileTypeChip || belowTile->state != 0)) // cmp word ptr [si+18ACh], 5
- {
- return;
- }
-
- // loc_4813E: ; CODE XREF: movefun+19Dj
- // ; movefun+1A4j ...
- if ((belowLeftTile->state == 0 && belowLeftTile->tile == LevelTileTypeSpace) // cmp word ptr [si+18AAh], 0
- || (belowLeftTile->state == 0x88 && belowLeftTile->tile == 0x88) // cmp word ptr [si+18AAh], 8888h
- || (belowLeftTile->state == 0xAA && belowLeftTile->tile == 0xAA)) // cmp word ptr [si+18AAh], 0AAAAh
- {
- // loc_4817A: ; CODE XREF: movefun+1B3j
- // ; movefun+1BBj ...
- if (leftTile->state == 0 && leftTile->tile == LevelTileTypeSpace) // cmp word ptr [si+1832h], 0
- {
- // loc_48184: ; CODE XREF: movefun+1EFj
- currentTile->state = 0x50;
- leftTile->state = 0x88;
- leftTile->tile = 0x88;
- return;
- }
- else
- {
- continue;
- }
- }
- if ((belowRightTile->state == 0 && belowRightTile->tile == LevelTileTypeSpace) // cmp word ptr [si+18AEh], 0
- || (belowRightTile->state == 0x88 && belowRightTile->tile == 0x88) // cmp word ptr [si+18AEh], 8888h
- || (belowRightTile->state == 0xAA && belowRightTile->tile == 0xAA)) // cmp word ptr [si+18AEh], 0AAAAh
- {
- // loc_48190: ; CODE XREF: movefun+1CAj
- // ; movefun+1D2j ...
- if (rightTile->tile == LevelTileTypeSpace && rightTile->state == 0) // cmp word ptr [si+1836h], 0
- {
- // loc_48198: ; CODE XREF: movefun+205j
- currentTile->state = 0x60;
- rightTile->state = 0x88;
- rightTile->tile = 0x88;
- return;
- }
- else
- {
- return;
- }
- }
-
- return;
-
- } while (1);
-
- if (shouldSkipTo_loc_481C6 == 0)
- {
- // loc_481A4: ; CODE XREF: movefun+15Ej
- if (belowTile->state == 0xE || belowTile->state == 0xF || belowTile->state == 0x28 || belowTile->state == 0x29 || belowTile->state == 0x25 || belowTile->state == 0x26)
- {
- return;
- }
- }
-
- // loc_481C6: ; CODE XREF: movefun+173j
- if (belowLeftTile->tile == LevelTileTypeElectron) // cmp byte ptr [si+18AAh], 18h
- {
- belowTile->tile = LevelTileTypeElectron;
- belowTile->state = 0;
- }
- // loc_481D3: ; CODE XREF: movefun+23Bj
- if (belowLeftTile->tile != LevelTileTypeExplosion) // cmp byte ptr [si+18AAh], 1Fh
- {
- belowLeftTile->tile = LevelTileTypeSpace;
- belowLeftTile->state = 0;
- }
-
- // loc_481FE: ; CODE XREF: movefun+168j
- // ; movefun+188j ...
- detonateBigExplosion(position + kLevelWidth); // Tile below
-}
-
-void updateInfotronTiles(int16_t position) // movefun2 proc near ; DATA XREF: data:1612o
-{
- // 01ED:17A5
-
- StatefulLevelTile *currentTile = &gCurrentLevelState[position];
- StatefulLevelTile *belowTile = &gCurrentLevelState[position + kLevelWidth];
- StatefulLevelTile *belowLeftTile = &gCurrentLevelState[position + kLevelWidth - 1];
- StatefulLevelTile *belowRightTile = &gCurrentLevelState[position + kLevelWidth + 1];
- StatefulLevelTile *leftTile = &gCurrentLevelState[position - 1];
- StatefulLevelTile *rightTile = &gCurrentLevelState[position + 1];
-
- if (currentTile->tile != LevelTileTypeInfotron)
- {
- return;
- }
-
- // loc_48410: ; CODE XREF: movefun2+5j
- uint8_t shouldSkipFirstPartOfLoop = 0;
-
- if (currentTile->state != 0 || currentTile->tile != LevelTileTypeInfotron)
- {
- shouldSkipFirstPartOfLoop = 1; // used to emulate "jmp loc_48495"
- }
- else
- {
- // loc_4841B: ; CODE XREF: movefun2+Fj
- if (belowTile->state == 0 && belowTile->tile == LevelTileTypeSpace)
- {
- // loc_48463: ; CODE XREF: movefun2+1Aj
- currentTile->state = 0x40;
- shouldSkipFirstPartOfLoop = 1; // used to emulate "jmp loc_48495"
- }
- else
- {
- if ((belowTile->state != 0 || belowTile->tile != LevelTileTypeZonk) && (belowTile->state != 0 || belowTile->tile != LevelTileTypeInfotron) && (belowTile->state != 0 || belowTile->tile != LevelTileTypeChip))
- {
- return;
- }
-
- // loc_48434: ; CODE XREF: movefun2+1Fj
- // ; movefun2+24j ...
- if ((belowLeftTile->state == 0 && belowLeftTile->tile == LevelTileTypeSpace) || (belowLeftTile->state == 0x88 && belowLeftTile->tile == 0x88) || (belowLeftTile->state == 0xAA && belowLeftTile->tile == 0xAA))
- {
- // loc_4846A: ; CODE XREF: movefun2+31j
- // ; movefun2+39j ...
- if (leftTile->state == 0 && leftTile->tile == LevelTileTypeSpace)
- {
- // loc_48473: ; CODE XREF: movefun2+67j
- currentTile->state = 0x50;
- leftTile->state = 0x88;
- leftTile->tile = 0x88;
- shouldSkipFirstPartOfLoop = 1; // used to emulate "jmp loc_48495"
- }
- }
- }
- }
-
- do
- {
- if (shouldSkipFirstPartOfLoop == 0)
- {
- // loc_4844B: ; CODE XREF: movefun2+69j
- // ; movefun2+1C7j
- if ((belowRightTile->state != 0 || belowRightTile->tile != LevelTileTypeSpace) && (belowRightTile->state != 0x88 || belowRightTile->tile != 0x88) && (belowRightTile->state != 0xAA || belowRightTile->tile != 0xAA))
- {
- return;
- }
-
- // loc_48480: ; CODE XREF: movefun2+48j
- // ; movefun2+50j ...
- if (rightTile->state != 0 || rightTile->tile != LevelTileTypeSpace)
- {
- return;
- }
-
- // loc_48488: ; CODE XREF: movefun2+7Dj
- currentTile->state = 0x60;
- rightTile->state = 0x88;
- rightTile->tile = 0x88;
- }
-
- shouldSkipFirstPartOfLoop = 0;
-
- // loc_48495: ; CODE XREF: movefun2+11j
- // ; movefun2+60j ...
- uint8_t state = currentTile->state;
- uint8_t stateType = state & 0xF0;
-
- if (stateType != 0x10)
- {
- // loc_484A5: ; CODE XREF: movefun2+99j
- if (stateType == 0x20)
- {
- // loc_4861B: ; CODE XREF: movefun2+A1j
- uint8_t stateFrame = state & 0x7; // module 8?
-
- // mov si, 12B6h
- Point frameCoordinates = kInfotronSlideLeftAnimationFrameCoordinates[stateFrame];
-
- uint8_t tileX = (position % kLevelWidth);
- uint8_t tileY = (position / kLevelWidth);
-
- uint16_t dstX = tileX * kTileSize;
- uint16_t dstY = tileY * kTileSize;
-
- drawMovingSpriteFrameInLevel(frameCoordinates.x,
- frameCoordinates.y,
- kTileSize * 2,
- kTileSize,
- dstX,
- dstY);
-
- state = currentTile->state;
- state++;
- if (state == 0x24) // 36
- {
- rightTile->state = 0xAA;
- rightTile->tile = 0xAA;
- }
- // loc_48653: ; CODE XREF: movefun2+243j
- if (state == 0x26) // 38
- {
- currentTile->state = state;
- handleInfotronStateAfterFallingOneTile(position + 1);
- return;
- }
- // loc_48666: ; CODE XREF: movefun2+24Ej
- else if (state < 0x28) // 40
- {
- currentTile->state = state;
- return;
- }
- // loc_48670: ; CODE XREF: movefun2+261j
- else
- {
- currentTile->state = 0x70;
- currentTile->tile = LevelTileTypeInfotron;
- return;
- }
- }
- // loc_484AC: ; CODE XREF: movefun2+9Fj
- else if (stateType == 0x30)
- {
- // loc_48677: ; CODE XREF: movefun2+A8j
- uint8_t stateFrame = state & 0x7;
-
- uint8_t tileX = ((position - 1) % kLevelWidth);
- uint8_t tileY = ((position - 1) / kLevelWidth);
-
- uint16_t dstX = tileX * kTileSize;
- uint16_t dstY = tileY * kTileSize;
-
- // mov si, 12C6h
- Point frameCoordinates = kInfotronSlideRightAnimationFrameCoordinates[stateFrame];
-
- drawMovingSpriteFrameInLevel(frameCoordinates.x,
- frameCoordinates.y,
- kTileSize * 2,
- kTileSize,
- dstX, dstY);
-
- state = currentTile->state;
- state++;
- if (state == 0x34) // 52
- {
- leftTile->state = 0xAA;
- leftTile->tile = 0xAA;
- }
- // loc_486AF: ; CODE XREF: movefun2+29Fj
- if (state == 0x36) // 54
- {
- currentTile->state = state;
- handleInfotronStateAfterFallingOneTile(position - 1); // left tile
- }
- // loc_486C1: ; CODE XREF: movefun2+2AAj
- if (state < 0x38) // 54
- {
- currentTile->state = state;
- return;
- }
- else
- {
- currentTile->state = 0x70;
- currentTile->tile = LevelTileTypeInfotron;
- return;
- }
- }
- // loc_484B3: ; CODE XREF: movefun2+A6j
- else if (stateType == 0x40)
- {
- // loc_486D2: ; CODE XREF: movefun2+AFj
- state++;
- if (state < 0x42)
- {
- currentTile->state = state;
- return;
- }
-
- // loc_486DE: ; CODE XREF: movefun2+2CFj
- if (belowTile->state != 0 || belowTile->tile != LevelTileTypeSpace)
- {
- state--;
- currentTile->state = state;
- return;
- }
-
- // loc_486EC: ; CODE XREF: movefun2+2DBj
- currentTile->state = 0xFF;
- currentTile->tile = 0xFF;
- belowTile->state = 0x10;
- belowTile->tile = LevelTileTypeInfotron;
- return;
- }
- // loc_484BA: ; CODE XREF: movefun2+ADj
- else if (stateType == 0x50)
- {
- // loc_48700: ; CODE XREF: movefun2+B6j
- uint8_t stateFrame = state & 0x7; // module 8?
-
- uint8_t tileX = ((position - 1) % kLevelWidth);
- uint8_t tileY = ((position - 1) / kLevelWidth);
-
- uint16_t dstX = tileX * kTileSize;
- uint16_t dstY = tileY * kTileSize;
-
- // mov si, 12B6h
- Point frameCoordinates = kInfotronSlideLeftAnimationFrameCoordinates[stateFrame];
- drawMovingSpriteFrameInLevel(frameCoordinates.x,
- frameCoordinates.y,
- kTileSize * 2,
- kTileSize,
- dstX, dstY);
- state = currentTile->state;
- state++;
-
- if (state < 0x52) // 82
- {
- currentTile->state = state;
- return;
- }
- // loc_48737: ; CODE XREF: movefun2+328j
- else if (belowLeftTile->state != 0 || belowLeftTile->tile != LevelTileTypeSpace) // cmp word ptr [si+18AAh], 0
- {
- // loc_48767: ; CODE XREF: movefun2+334j
- // ; movefun2+343j
- state--;
- currentTile->state = state;
- return;
- }
- else if ((leftTile->state != 0 || leftTile->tile != LevelTileTypeSpace) // cmp word ptr [si+1832h], 0
- && (leftTile->state != 0x88 || leftTile->tile != 0x88)) // cmp word ptr [si+1832h], 8888h
- {
- // loc_48767: ; CODE XREF: movefun2+334j
- // ; movefun2+343j
- state--;
- currentTile->state = state;
- return;
- }
- else
- {
- // loc_4874D: ; CODE XREF: movefun2+33Bj
- currentTile->state = 0xFF;
- currentTile->tile = 0xFF;
- leftTile->state = 0x22;
- leftTile->tile = LevelTileTypeInfotron;
- belowLeftTile->state = 0x99;
- belowLeftTile->tile = 0x99;
- return;
- }
- }
- // loc_484C1: ; CODE XREF: movefun2+B4j
- else if (stateType == 0x60)
- {
- // loc_4876E: ; CODE XREF: movefun2+BDj
- uint8_t stateFrame = state & 0x7; // module 8?
-
- uint8_t tileX = (position % kLevelWidth);
- uint8_t tileY = (position / kLevelWidth);
-
- uint16_t dstX = tileX * kTileSize;
- uint16_t dstY = tileY * kTileSize;
-
- // mov si, 12C6h
- Point frameCoordinates = kInfotronSlideRightAnimationFrameCoordinates[stateFrame];
-
- drawMovingSpriteFrameInLevel(frameCoordinates.x,
- frameCoordinates.y,
- kTileSize * 2,
- kTileSize,
- dstX, dstY);
-
- state = currentTile->state;
- state++;
-
- if (state < 0x62) // 98
- {
- currentTile->state = state;
- return;
- }
- // loc_487A5: ; CODE XREF: movefun2+396j
- else if (belowRightTile->state != 0 || belowRightTile->tile != LevelTileTypeSpace) // cmp word ptr [si+18AEh], 0
- {
- // loc_487D5: ; CODE XREF: movefun2+3A2j
- // ; movefun2+3B1j
- state--;
- currentTile->state = state;
- return;
- }
- else if ((rightTile->state != 0 || rightTile->tile != LevelTileTypeSpace) // cmp word ptr [si+1836h], 0
- && (rightTile->state != 0x88 || rightTile->tile != 0x88)) // cmp word ptr [si+1836h], 8888h
- {
- // loc_487D5: ; CODE XREF: movefun2+3A2j
- // ; movefun2+3B1j
- state--;
- currentTile->state = state;
- return;
- }
- else
- {
- // loc_487BB: ; CODE XREF: movefun2+3A9j
- currentTile->state = 0xFF;
- currentTile->tile = 0xFF;
- rightTile->state = 0x32;
- rightTile->tile = LevelTileTypeInfotron;
- belowRightTile->state = 0x99;
- belowRightTile->tile = 0x99;
- return;
- }
- }
- // loc_484C8: ; CODE XREF: movefun2+BBj
- else if (stateType == 0x70)
- {
- // loc_487DC: ; CODE XREF: movefun2:loc_484CCj
- if ((belowTile->state != 0 || belowTile->tile != LevelTileTypeSpace) && (belowTile->state != 0x99 || belowTile->tile != 0x99))
- {
- return;
- }
-
- // loc_487EC: ; CODE XREF: movefun2+3D9j
- // ; movefun2+3E1j
- currentTile->state = 0xFF;
- currentTile->tile = 0xFF;
-
- // add si, 78h ; 'x'
- position += kLevelWidth;
-
- currentTile = &gCurrentLevelState[position];
- belowTile = &gCurrentLevelState[position + kLevelWidth];
- belowLeftTile = &gCurrentLevelState[position + kLevelWidth - 1];
- leftTile = &gCurrentLevelState[position - 1];
- belowRightTile = &gCurrentLevelState[position + kLevelWidth + 1];
- rightTile = &gCurrentLevelState[position + 1];
-
- currentTile->state = 0x10;
- currentTile->tile = LevelTileTypeInfotron;
- }
- else
- {
- return;
- }
- }
-
- // loc_484D0: ; CODE XREF: movefun2+9Bj
- // ; movefun2+3F3j
- // This animates the Infotron falling
- uint8_t somePositionThing = state;
- somePositionThing *= 2;
- somePositionThing &= 0x1F;
-
- uint16_t offset = kFallAnimationGravityOffsets[somePositionThing];
-
- uint16_t finalPosition = position - kLevelWidth;
- uint8_t tileX = (finalPosition % kLevelWidth);
- uint8_t tileY = (finalPosition / kLevelWidth);
-
- uint16_t dstX = tileX * kTileSize + (offset % 122);
- uint16_t dstY = tileY * kTileSize + (offset / 122);
-
- // mov si, word_515C4
- drawMovingSpriteFrameInLevel(240, 178,
- kTileSize,
- kTileSize + 2,
- dstX, dstY);
-
- uint8_t newState = currentTile->state;
- newState++;
- if (newState == 0x16) // 22
- {
- currentTile->state = newState;
- handleInfotronStateAfterFallingOneTile(position - kLevelWidth); // Tile above
- return;
- }
- // loc_48513: ; CODE XREF: movefun2+FBj
- else if (newState < 0x18) // 24
- {
- currentTile->state = newState;
- return;
- }
-
- // loc_4851D: ; CODE XREF: movefun2+10Ej
- // This part handles what to do when the Infotron finished falling 1 tile
- currentTile->state = 0;
-
- if ((belowTile->tile == LevelTileTypeSpace && belowTile->state == 0) // cmp word ptr [si+18ACh], 0
- || (belowTile->tile == 0x99 && belowTile->state == 0x99)) // cmp word ptr [si+18ACh], 9999h
- {
- // loc_485BB: ; CODE XREF: movefun2+121j
- // ; movefun2+12Cj
- currentTile->state = 0x70;
- currentTile->tile = LevelTileTypeInfotron;
- belowTile->state = 0x99;
- belowTile->tile = 0x99;
- return;
- }
-
- // loc_48537: ; CODE XREF: movefun2+12Aj
- if (belowTile->tile == LevelTileTypeMurphy) // cmp byte ptr [si+18ACh], 3
- {
- // loc_485F2: ; CODE XREF: movefun2+136j
- if (belowTile->state == 0xE || belowTile->state == 0xF || belowTile->state == 0x28 || belowTile->state == 0x29 || belowTile->state == 0x25 || belowTile->state == 0x26)
- {
- return;
- }
-
- // loc_48614: ; CODE XREF: movefun2+140j
- // ; movefun2+14Aj ...
- detonateBigExplosion(position + kLevelWidth);
- return;
- }
-
- // loc_48541: ; CODE XREF: movefun2+134j
- if ((belowTile->tile == LevelTileTypeRedDisk && belowTile->state == 0) // cmp word ptr [si+18ACh], 14h
- || belowTile->tile == LevelTileTypeSnikSnak // cmp byte ptr [si+18ACh], 11h
- || belowTile->tile == LevelTileTypeElectron // cmp byte ptr [si+18ACh], 18h
- || (belowTile->tile == LevelTileTypeYellowDisk && belowTile->state == 0) // cmp word ptr [si+18ACh], 12h
- || (belowTile->tile == LevelTileTypeOrangeDisk && belowTile->state == 0)) // cmp word ptr [si+18ACh], 8
- {
- // loc_48614: ; CODE XREF: movefun2+140j
- // ; movefun2+14Aj ...
- detonateBigExplosion(position + kLevelWidth);
- return;
- }
-
- // loc_48573: ; CODE XREF: movefun2+166j
- playFallSound();
- if ((belowTile->tile != LevelTileTypeZonk || belowTile->state != 0) // cmp word ptr [si+18ACh], 1
- && (belowTile->tile != LevelTileTypeInfotron || belowTile->state != 0) // cmp word ptr [si+18ACh], 4
- && (belowTile->tile != LevelTileTypeChip || belowTile->state != 0)) // cmp word ptr [si+18ACh], 5
- {
- return;
- }
-
- // loc_4858C: ; CODE XREF: movefun2+173j
- // ; movefun2+17Aj ...
- if ((belowLeftTile->tile == LevelTileTypeSpace && belowLeftTile->state == 0) // cmp word ptr [si+18AAh], 0
- || (belowLeftTile->tile == 0x88 && belowLeftTile->state == 0x88) // cmp word ptr [si+18AAh], 8888h
- || (belowLeftTile->tile == 0xAA && belowLeftTile->state == 0xAA)) // cmp word ptr [si+18AAh], 0AAAAh
- {
- // loc_485C8: ; CODE XREF: movefun2+189j
- // ; movefun2+191j ...
- if (leftTile->state == 0 && leftTile->tile == LevelTileTypeSpace)
- {
- // loc_485D2: ; CODE XREF: movefun2+1C5j
- currentTile->state = 0x50;
- leftTile->state = 0x88;
- leftTile->tile = 0x88;
- return;
- }
- else
- {
- continue; // jmp loc_4844B
- }
- }
- if ((belowRightTile->tile == LevelTileTypeSpace && belowRightTile->state == 0) // cmp word ptr [si+18AEh], 0
- || (belowRightTile->tile == 0x88 && belowRightTile->state == 0x88) // cmp word ptr [si+18AEh], 8888h
- || (belowRightTile->tile == 0xAA && belowRightTile->state == 0xAA)) // cmp word ptr [si+18AEh], 0AAAAh
- {
- // loc_485DE: ; CODE XREF: movefun2+1A0j
- // ; movefun2+1A8j ...
- if (rightTile->state == 0 && rightTile->tile == LevelTileTypeSpace)
- {
- // loc_485E6: ; CODE XREF: movefun2+1DBj
- currentTile->state = 0x60;
- rightTile->state = 0x88;
- rightTile->tile = 0x88;
- return;
- }
- return;
- }
- return;
- } while (1);
-}
-
-void handleMurphyCollisionAfterMovement(int16_t position) // sub_487FE proc near ; CODE XREF: update?+E0Cp update?+E2Ap ...
-{
- // 01ED:1B9B
- StatefulLevelTile *currentTile = &gCurrentLevelState[position];
- StatefulLevelTile *aboveTile = &gCurrentLevelState[position - kLevelWidth];
- StatefulLevelTile *leftTile = &gCurrentLevelState[position - 1];
- StatefulLevelTile *rightTile = &gCurrentLevelState[position + 1];
- StatefulLevelTile *aboveRightTile = &gCurrentLevelState[position - kLevelWidth + 1];
- StatefulLevelTile *aboveLeftTile = &gCurrentLevelState[position - kLevelWidth - 1];
-
- if (currentTile->tile != LevelTileTypeExplosion)
- {
- currentTile->state = 0;
- currentTile->tile = LevelTileTypeSpace;
- }
-
- // loc_4880B: ; CODE XREF: handleMurphyCollisionAfterMovement+5j
- if ((aboveTile->state == 0 && aboveTile->tile == LevelTileTypeSpace) || (aboveTile->state == 0x99 && aboveTile->tile == 0x99))
- {
- // loc_48835: ; CODE XREF: handleMurphyCollisionAfterMovement+12j
- // ; handleMurphyCollisionAfterMovement+1Aj
- if (aboveLeftTile->state == 0 && aboveLeftTile->tile == LevelTileTypeZonk)
- {
- // loc_48852: ; CODE XREF: handleMurphyCollisionAfterMovement+3Cj
- if ((leftTile->state == 0 && leftTile->tile == LevelTileTypeZonk) || (leftTile->state == 0 && leftTile->tile == LevelTileTypeInfotron) || (leftTile->state == 0 && leftTile->tile == LevelTileTypeChip))
- {
- // loc_48869: ; CODE XREF: handleMurphyCollisionAfterMovement+59j
- // ; handleMurphyCollisionAfterMovement+60j ...
- aboveLeftTile->state = 0x60;
- aboveTile->state = 0x88;
- aboveTile->tile = 0x88;
- return;
- }
- }
- else if (aboveLeftTile->state == 0 && aboveLeftTile->tile == LevelTileTypeInfotron)
- {
- // loc_48897: ; CODE XREF: handleMurphyCollisionAfterMovement+43j
- if ((leftTile->state == 0 && leftTile->tile == LevelTileTypeZonk) || (leftTile->state == 0 && leftTile->tile == LevelTileTypeInfotron) || (leftTile->state == 0 && leftTile->tile == LevelTileTypeChip))
- {
- // loc_488AE: ; CODE XREF: handleMurphyCollisionAfterMovement+9Ej
- // ; handleMurphyCollisionAfterMovement+A5j ...
- aboveLeftTile->state = 0x60;
- aboveTile->state = 0x88;
- aboveTile->tile = 0x88;
- return;
- }
- }
- // loc_48843: ; CODE XREF: handleMurphyCollisionAfterMovement+69j
- // ; handleMurphyCollisionAfterMovement+AEj
- if (aboveRightTile->state == 0 && aboveRightTile->tile == LevelTileTypeZonk)
- {
- // loc_48875: ; CODE XREF: handleMurphyCollisionAfterMovement+4Aj
- if ((rightTile->state == 0 && rightTile->tile == LevelTileTypeZonk) || (rightTile->state == 0 && rightTile->tile == LevelTileTypeInfotron) || (rightTile->state == 0 && rightTile->tile == LevelTileTypeChip))
- {
- // loc_4888B: ; CODE XREF: handleMurphyCollisionAfterMovement+7Cj
- // ; handleMurphyCollisionAfterMovement+83j ...
- aboveRightTile->state = 0x50;
- aboveTile->state = 0x88;
- aboveTile->tile = 0x88;
- }
- }
- else if (aboveRightTile->state == 0 && aboveRightTile->tile == LevelTileTypeInfotron)
- {
- // loc_488BA: ; CODE XREF: handleMurphyCollisionAfterMovement+51j
- if ((rightTile->state == 0 && rightTile->tile == LevelTileTypeZonk) || (rightTile->state == 0 && rightTile->tile == LevelTileTypeInfotron) || (rightTile->state == 0 && rightTile->tile == LevelTileTypeChip))
- {
- // loc_488D0: ; CODE XREF: handleMurphyCollisionAfterMovement+C1j
- // ; handleMurphyCollisionAfterMovement+C8j ...
- aboveRightTile->state = 0x50;
- aboveTile->state = 0x88;
- aboveTile->tile = 0x88;
- }
- }
- }
- else if (aboveTile->state == 0 && aboveTile->tile == LevelTileTypeZonk)
- {
- // loc_48829: ; CODE XREF: handleMurphyCollisionAfterMovement+21j
- aboveTile->state = 0x40;
- }
- else if (aboveTile->state == 0 && aboveTile->tile == LevelTileTypeInfotron)
- {
- // loc_4882F: ; CODE XREF: handleMurphyCollisionAfterMovement+28j
- aboveTile->state = 0x40;
- }
-}
-
-void handleZonkStateAfterFallingOneTile(int16_t position) // sub_488DC proc near ; CODE XREF: movefun+124p
- // ; movefun+2C6p ...
-{
- // 01ED:1C79
- StatefulLevelTile *currentTile = &gCurrentLevelState[position];
- StatefulLevelTile *aboveTile = &gCurrentLevelState[position - kLevelWidth];
- StatefulLevelTile *leftTile = &gCurrentLevelState[position - 1];
- StatefulLevelTile *rightTile = &gCurrentLevelState[position + 1];
- StatefulLevelTile *aboveAboveTile = &gCurrentLevelState[position - kLevelWidth * 2];
- StatefulLevelTile *aboveLeftTile = &gCurrentLevelState[position - kLevelWidth - 1];
- StatefulLevelTile *aboveRightTile = &gCurrentLevelState[position - kLevelWidth + 1];
-
- if (currentTile->tile != LevelTileTypeExplosion)
- {
- currentTile->state = 0;
- currentTile->tile = LevelTileTypeSpace;
- }
-
- // loc_488E9: ; CODE XREF: handleZonkStateAfterFallingOneTile+5j
- if (aboveTile->state != 0 || aboveTile->tile != LevelTileTypeSpace) // cmp word ptr [si+17BCh], 0
- {
- if (aboveTile->state != 0x99 || aboveTile->tile != 0x99) // cmp word ptr [si+17BCh], 9999h
- {
- return;
- }
-
- // loc_488F9: ; CODE XREF: handleZonkStateAfterFallingOneTile+1Aj
- if (aboveAboveTile->tile != LevelTileTypeInfotron) // cmp byte ptr [si+1744h], 4
- {
- return;
- }
- }
-
- // loc_48901: ; CODE XREF: handleZonkStateAfterFallingOneTile+12j
- // ; handleZonkStateAfterFallingOneTile+22j
- if (aboveLeftTile->state == 0 && aboveLeftTile->tile == LevelTileTypeZonk) // cmp word ptr [si+17BAh], 1
- {
- // loc_48910: ; CODE XREF: handleZonkStateAfterFallingOneTile+2Aj
- if (leftTile->state == 0 && (leftTile->tile == LevelTileTypeZonk || leftTile->tile == LevelTileTypeInfotron || leftTile->tile == LevelTileTypeChip))
- {
- // loc_48927: ; CODE XREF: handleZonkStateAfterFallingOneTile+39j
- // ; handleZonkStateAfterFallingOneTile+40j ...
- // mov word ptr [si+17BAh], 6001h
- aboveLeftTile->state = 0x60;
- aboveLeftTile->tile = LevelTileTypeZonk;
- // mov word ptr [si+17BCh], 8888h
- aboveTile->state = 0x88;
- aboveTile->tile = 0x88;
- return;
- }
- }
-
- // loc_48908: ; CODE XREF: handleZonkStateAfterFallingOneTile+49j
- if (aboveRightTile->state == 0 && aboveRightTile->tile == LevelTileTypeZonk) // cmp word ptr [si+17BEh], 1
- {
- // loc_48934: ; CODE XREF: handleZonkStateAfterFallingOneTile+31j
- if (rightTile->state != 0 || (rightTile->tile != LevelTileTypeZonk && rightTile->tile != LevelTileTypeInfotron && rightTile->tile != LevelTileTypeChip))
- {
- return;
- }
-
- // loc_4894A: ; CODE XREF: handleZonkStateAfterFallingOneTile+5Dj
- // ; handleZonkStateAfterFallingOneTile+64j ...
- // mov word ptr [si+17BEh], 5001h
- aboveRightTile->state = 0x50;
- aboveRightTile->tile = LevelTileTypeZonk;
- // mov word ptr [si+17BCh], 8888h
- aboveTile->state = 0x88;
- aboveTile->tile = 0x88;
- return;
- }
-}
-
-void handleInfotronStateAfterFallingOneTile(int16_t position) // sub_48957 proc near ; CODE XREF: movefun2+104p
-// ; movefun2+257p ...
-{
- StatefulLevelTile *currentTile = &gCurrentLevelState[position];
- StatefulLevelTile *aboveTile = &gCurrentLevelState[position - kLevelWidth];
- StatefulLevelTile *aboveAboveTile = &gCurrentLevelState[position - kLevelWidth * 2];
- StatefulLevelTile *aboveLeftTile = &gCurrentLevelState[position - kLevelWidth - 1];
- StatefulLevelTile *aboveRightTile = &gCurrentLevelState[position - kLevelWidth + 1];
- StatefulLevelTile *leftTile = &gCurrentLevelState[position - 1];
- StatefulLevelTile *rightTile = &gCurrentLevelState[position + 1];
-
- if (currentTile->tile != LevelTileTypeExplosion)
- {
- currentTile->state = 0;
- currentTile->tile = LevelTileTypeSpace;
- }
- // loc_48964: ; CODE XREF: handleInfotronStateAfterFallingOneTile+5j
- if (aboveTile->state != 0 || aboveTile->tile != LevelTileTypeSpace)
- {
- if (aboveTile->state == 0x99 && aboveTile->tile == 0x99)
- {
- // loc_48974: ; CODE XREF: handleInfotronStateAfterFallingOneTile+1Aj
- if (aboveAboveTile->tile != LevelTileTypeZonk)
- {
- return;
- }
- }
- else
- {
- return;
- }
- }
-
- // loc_4897C: ; CODE XREF: handleInfotronStateAfterFallingOneTile+12j
- // ; handleInfotronStateAfterFallingOneTile+22j
- if (aboveLeftTile->state == 0 && aboveLeftTile->tile == LevelTileTypeInfotron)
- {
- // loc_4898B: ; CODE XREF: handleInfotronStateAfterFallingOneTile+2Aj
- if ((leftTile->state == 0 && leftTile->tile == LevelTileTypeZonk) || (leftTile->state == 0 && leftTile->tile == LevelTileTypeInfotron) || (leftTile->state == 0 && leftTile->tile == LevelTileTypeChip))
- {
- // loc_489A2: ; CODE XREF: handleInfotronStateAfterFallingOneTile+39j
- // ; handleInfotronStateAfterFallingOneTile+40j ...
- aboveLeftTile->state = 0x60;
- aboveLeftTile->tile = LevelTileTypeInfotron;
- aboveTile->state = 0x88;
- aboveTile->tile = 0x88;
- return;
- }
- }
-
- // loc_48983: ; CODE XREF: handleInfotronStateAfterFallingOneTile+49j
- if (aboveRightTile->state != 0 || aboveRightTile->tile != LevelTileTypeInfotron)
- {
- return;
- }
-
- // loc_489AF: ; CODE XREF: handleInfotronStateAfterFallingOneTile+31j
- if ((rightTile->state != 0 || rightTile->tile != LevelTileTypeZonk) && (rightTile->state != 0 || rightTile->tile != LevelTileTypeInfotron) && (rightTile->state != 0 || rightTile->tile != LevelTileTypeChip))
- {
- return;
- }
-
- // loc_489C5: ; CODE XREF: handleInfotronStateAfterFallingOneTile+5Dj
- // ; handleInfotronStateAfterFallingOneTile+64j ...
- aboveRightTile->state = 0x50;
- aboveRightTile->tile = LevelTileTypeInfotron;
- aboveTile->state = 0x88;
- aboveTile->tile = 0x88;
-}
-
-void initializeGameInfo() // sub_48A20 proc near ; CODE XREF: start+32Fp
- // ; runLevel:notFunctionKeyp ...
-{
- // 01ED:1DBD
- // word_510BC = gMurphyTileX;
- // word_510BE = gMurphyTileY;
- gIsMurphyLookingLeft = 0;
- gShouldKillMurphy = 0;
- gShouldExitLevel = 0;
- gQuitLevelCountdown = 0;
- gNumberOfRemainingRedDisks = 0;
- gAdditionalInfoInGamePanelFrameCounter = 0;
- gMurphyYawnAndSleepCounter = 0;
- gLastDrawnMinutesAndSeconds = 0xFFFF;
- gLastDrawnHours = 0xFF; // 255
- gIsGameRunning = 1;
- gAuxGameSeconds20msAccumulator = 0;
- gGameSeconds = 0;
- gGameMinutes = 0;
- gGameHours = 0;
- gIsExplosionStarted = 0;
- gTerminalMaxFramesToNextScroll = 0x7F; // 127
- gAreYellowDisksDetonated = 0;
- gFrameCounter = 0;
- // mov byte ptr word_510C1, 1
- // mov byte ptr word_510C1+1, 0
- gShouldShowGamePanel = 1;
- gCurrentPanelHeight = kPanelBitmapHeight;
- gToggleGamePanelKeyAutoRepeatCounter = 0;
- gAreEnemiesFrozen = 0;
- gIsMurphyGoingThroughPortal &= 0xFF00; // mov byte ptr gIsMurphyGoingThroughPortal, 0
- gPlantedRedDiskCountdown = 0;
- gPlantedRedDiskPosition = 0;
-}
-
-void handleGameIterationStarted()
-{
- gGameIterationStartTime = getTime();
-}
-
-void handleGameIterationFinished()
-{
- const float kOriginalIterationDuration = 1000.0 / 35; // 35 iterations per second in the original game
- float currentIterationDuration = getTime() - gGameIterationStartTime;
-
- float targetIterationDuration = kOriginalIterationDuration * kSpeedTimeFactors[gGameSpeed];
-
- if (gFastMode != FastModeTypeNone)
- {
- targetIterationDuration = 0;
- }
-
- if (currentIterationDuration < targetIterationDuration)
- {
- waitTime(targetIterationDuration - currentIterationDuration);
- }
-
- gNumberOfGameIterations++;
-
- if (gGameIterationRateReferenceTime == 0)
- {
- gGameIterationRateReferenceTime = getTime();
- }
- else
- {
- uint32_t difference = getTime() - gGameIterationRateReferenceTime;
-
- if (difference > 1000)
- {
- gGameIterationRate = gNumberOfGameIterations * 1000.f / difference;
- gNumberOfGameIterations = 0;
- gGameIterationRateReferenceTime = getTime();
- }
- }
-}
-
-void runLevel() // proc near ; CODE XREF: start+35Cp
-{
- // 01ED:1E58
- if (gIsPlayingDemo == 0)
- {
- // loc_48ACE: ; CODE XREF: runLevel+5j
- gIsLevelStartedAsDemo = 0;
- gLevelFailed = 1;
- }
- else
- {
- gIsLevelStartedAsDemo = 1;
- gLevelFailed = 0;
- }
-
- // loc_48AD8: ; CODE XREF: runLevel+11j
- if (gDemoRecordingJustStarted == 1)
- {
- // loc_48ADF: ; CODE XREF: runLevel+BAj
- gDemoRecordingJustStarted = 0;
- drawGameTime();
-
- do
- {
- // isFunctionKey: ; CODE XREF: runLevel+35j
- int9handler(1);
- } while (areAnyF1ToF10KeysPressed());
-
- // notFunctionKey: ; CODE XREF: runLevel+31j
- initializeGameInfo();
- if (isMusicEnabled == 0)
- {
- stopMusic();
- }
-
- // loc_48AFF: ; CODE XREF: runLevel+3Fj
- gIsLevelStartedAsDemo = 0;
- gLevelFailed = 1;
- }
-
- // loc_48B09: ; CODE XREF: runLevel+22j
- gPlantedRedDiskCountdown = 0;
- byte_5A323 = 0;
-
- do
- {
- handleGameIterationStarted();
-
- int9handler(0);
-
- uint16_t mouseButtonsStatus;
-
- // loc_48B23: ; CODE XREF: runLevel+63j
- getMouseStatus(NULL, NULL, &mouseButtonsStatus);
-
- // loc_48B38: ; CODE XREF: runLevel+6Ej runLevel+75j
- if (gIsDebugModeEnabled != 0)
- {
- if (gToggleFancyEasyTilesThrottleCounter != 0)
- {
- gToggleFancyEasyTilesThrottleCounter--;
- }
-
- // loc_48B4A: ; CODE XREF: runLevel+89j
- if (gIsEnterPressed == 0 && mouseButtonsStatus == MouseButtonLeft // cmp bx, 1
- && gToggleFancyEasyTilesThrottleCounter == 0)
- {
- gToggleFancyEasyTilesThrottleCounter = 0xA;
- restoreOriginalFancyTiles(); // 01ED:1EFF
- drawFixedLevel();
- convertToEasyTiles();
- }
- }
-
- // loc_48B6B: ; CODE XREF: runLevel+82j runLevel+94j ...
- handleGameUserInput(); // 01ED:1F08
- if (gDemoRecordingJustStarted == 1)
- {
- // Restart the demo
- // loc_48ADF: ; CODE XREF: runLevel+BAj
- gDemoRecordingJustStarted = 0;
- drawGameTime();
-
- do
- {
- // isFunctionKey: ; CODE XREF: runLevel+35j
- int9handler(1);
- } while (areAnyF1ToF10KeysPressed());
-
- // notFunctionKey: ; CODE XREF: runLevel+31j
- initializeGameInfo();
- if (isMusicEnabled == 0)
- {
- stopMusic();
- }
-
- // loc_48AFF: ; CODE XREF: runLevel+3Fj
- gIsLevelStartedAsDemo = 0;
- gLevelFailed = 1;
-
- // loc_48B09: ; CODE XREF: runLevel+22j
- gPlantedRedDiskCountdown = 0;
- byte_5A323 = 0;
-
- continue;
-
- // All the code in this "if" is equivalent to "jmp loc_48ADF"
- }
-
- // loc_48B78: ; CODE XREF: runLevel+B8j
- if (gIsFlashingBackgroundModeEnabled != 0)
- {
- replaceCurrentPaletteColor(0, (Color){0x35, 0x35, 0x35});
- }
-
- // noFlashing: ; CODE XREF: runLevel+C2j
- updateMovingObjects(); // 01ED:1F28
- if (gIsFlashingBackgroundModeEnabled != 0)
- {
- replaceCurrentPaletteColor(0, (Color){0x21, 0x21, 0x21});
- }
-
- // noFlashing2: ; CODE XREF: runLevel+D8j
- drawGameTime();
- clearAdditionalInfoInGamePanelIfNeeded();
- if (gIsFlashingBackgroundModeEnabled != 0)
- {
- replaceCurrentPaletteColor(0, (Color){0x2d, 0x21, 0x0f});
- }
-
- // noFlashing3: ; CODE XREF: runLevel+F1j
- // 01ED:1F5B
- updatePlantedRedDisk();
- updateExplosionTimers();
- updateScrollOffset();
-
- // loc_48D59: ; CODE XREF: runLevel+19Bj
- // ; runLevel+1D2j ...
- if (gIsFlashingBackgroundModeEnabled != 0)
- {
- replaceCurrentPaletteColor(0, (Color){0x3f, 0x3f, 0x3f});
- }
-
- // noFlashing4: ; CODE XREF: runLevel+2D1j
- drawCurrentLevelViewport(gCurrentPanelHeight); // Added by me
- if (gFastMode != FastModeTypeUltra)
- {
- videoLoop(); // 01ED:2142
- }
- handleGameIterationFinished();
-
- // isFastMode2: ; CODE XREF: runLevel+2E8j
- if (gDebugExtraRenderDelay > 1)
- {
- playBaseSound();
- }
-
- // loc_48DB2: ; CODE XREF: runLevel+2F2j
- // Extra delays in debug mode
- for (int i = 1; i < gDebugExtraRenderDelay; ++i)
- {
- // loc_48DB6: ; CODE XREF: runLevel+310j
- if (gFastMode == FastModeTypeNone)
- {
- videoLoop(); // 01ED:2160
- }
-
- // isFastMode3: ; CODE XREF: runLevel+303j
- handleGameUserInput();
- }
-
- // loc_48DCD: ; CODE XREF: runLevel+2FCj
- if (gIsFlashingBackgroundModeEnabled != 0)
- {
- replaceCurrentPaletteColor(0, (Color){0, 0, 0});
- }
-
- // noFlashing5: ; CODE XREF: runLevel+317j
- if (gShouldExitGame != 0)
- {
- break;
- }
- gFrameCounter++;
- if (gShouldExitLevel == 1)
- {
- break;
- }
- if (gQuitLevelCountdown == 0) // 01ED:218D
- {
- continue;
- }
-
- // loc_48DFA: ; CODE XREF: runLevel+33Aj
- // 01ED:2197
- gQuitLevelCountdown--;
- if (gQuitLevelCountdown == 0)
- {
- break;
- }
- } while (1);
-
- // loc_48E03: ; CODE XREF: runLevel+328j
- // ; runLevel+333j ...
- if (gIsRecordingDemo != 0)
- {
- stopRecordingDemo();
- }
-
- // loc_48E13: ; CODE XREF: runLevel+353j
- uint8_t userDidNotCheat = (gHasUserCheated == 0);
- gHasUserCheated = 0;
- if (userDidNotCheat && gShouldUpdateTotalLevelTime != 0 && byte_5A323 == 0)
- {
- addCurrentGameTimeToPlayer();
- }
-
- // loc_48E30: ; CODE XREF: runLevel+362j
- // ; runLevel+369j ...
- gIsMoveScrollModeEnabled = 0;
- gAdditionalScrollOffsetX = 0;
- gAdditionalScrollOffsetY = 0;
- gIsFlashingBackgroundModeEnabled = 0;
- gDebugExtraRenderDelay = 1;
- replaceCurrentPaletteColor(0, (Color){0, 0, 0});
-}
-
-void updateUserInputInScrollMovementMode() // sub_4914A proc near ; CODE XREF: handleGameUserInput+7p
-{
- if (isLeftButtonPressed())
- {
- gAdditionalScrollOffsetX--;
- gAdditionalScrollOffsetX--;
- }
-
- // loc_49159: ; CODE XREF: updateUserInputInScrollMovementMode+5j
- if (isRightButtonPressed())
- {
- gAdditionalScrollOffsetX++;
- gAdditionalScrollOffsetX++;
- }
-
- // loc_49168: ; CODE XREF: updateUserInputInScrollMovementMode+14j
- if (isUpButtonPressed())
- {
- gAdditionalScrollOffsetY--;
- gAdditionalScrollOffsetY--;
- }
-
- // loc_49177: ; CODE XREF: updateUserInputInScrollMovementMode+23j
- if (isDownButtonPressed())
- {
- gAdditionalScrollOffsetY++;
- gAdditionalScrollOffsetY++;
- }
-
- // loc_49186: ; CODE XREF: updateUserInputInScrollMovementMode+32j
- if (gIsNumpad5Pressed != 0)
- {
- gAdditionalScrollOffsetX = 0;
- gAdditionalScrollOffsetY = 0;
- }
-
- // loc_49199: ; CODE XREF: updateUserInputInScrollMovementMode+41j
- if (gIsInsertKeyPressed != 0)
- {
- gAdditionalScrollOffsetX = -gMurphyScrollOffsetX;
- gAdditionalScrollOffsetY = -gMurphyScrollOffsetY;
- }
-
- // loc_491B3: ; CODE XREF: updateUserInputInScrollMovementMode+60j
- if (gIsHomeKeyPressed != 0)
- {
- gAdditionalScrollOffsetX = (kLevelBitmapWidth / 2) - gMurphyScrollOffsetX;
- gAdditionalScrollOffsetY = -gMurphyScrollOffsetY;
- }
-
- // loc_491C5: ; CODE XREF: updateUserInputInScrollMovementMode+72j
- if (gIsRePagKeyPressed != 0)
- {
- gAdditionalScrollOffsetX = kLevelBitmapWidth - gMurphyScrollOffsetX;
- gAdditionalScrollOffsetY = -gMurphyScrollOffsetY;
- }
-
- // loc_491E8: ; CODE XREF: updateUserInputInScrollMovementMode+99j
- if (gIsDelKeyPressed != 0)
- {
- gAdditionalScrollOffsetX = -gMurphyScrollOffsetX;
- gAdditionalScrollOffsetY = kLevelBitmapHeight - gMurphyScrollOffsetY;
- }
-
- // loc_491F6: ; CODE XREF: updateUserInputInScrollMovementMode+A3j
- if (gIsEndKeyPressed != 0)
- {
- gAdditionalScrollOffsetX = (kLevelBitmapWidth / 2) - gMurphyScrollOffsetX;
- gAdditionalScrollOffsetY = kLevelBitmapHeight - gMurphyScrollOffsetY;
- }
-
- // loc_49208: ; CODE XREF: updateUserInputInScrollMovementMode+B5j
- if (gIsAvPagKeyPressed != 0)
- {
- gAdditionalScrollOffsetX = kLevelBitmapWidth - gMurphyScrollOffsetX;
- gAdditionalScrollOffsetY = kLevelBitmapHeight - gMurphyScrollOffsetY;
- }
-}
-
-void simulateDemoInput() // sub_492A8 proc near ; CODE XREF: handleGameUserInput+27p
- // ; restartLevel+76p
-{
- // 01ED:2645
- if (gDemoCurrentInputRepeatCounter > 1)
- {
- gDemoCurrentInputRepeatCounter--;
- return;
- }
-
- // loc_492B3: ; CODE XREF: simulateDemoInput+5j
- uint8_t newInput = gDemos.demoData[gDemoCurrentInputIndex];
-
- if (newInput == 0xFF)
- {
- gQuitLevelCountdown = 0x64;
- gShouldExitLevel = 1;
- }
- else
- {
- gDemoCurrentInputIndex++;
- }
-
- // loc_492CA: ; CODE XREF: simulateDemoInput+47j
- gCurrentUserInput = newInput & 0xF;
- gDemoCurrentInputRepeatCounter = (newInput >> 4) + 1;
-}
-
-void saveInputForDemo() // sub_492F1 proc near ; CODE XREF: handleGameUserInput+1Dp
-{
- gDemoCurrentInputRepeatCounter++;
-
- if (gDemoCurrentInputRepeatCounter == 0xFF)
- {
- gDemoCurrentInput = gCurrentUserInput;
- gDemoRecordingRandomGeneratorSeed = gRandomGeneratorSeed;
- gDemoRecordingRandomGeneratorSeedLow = (gDemoRecordingRandomGeneratorSeed >> 8); // ah;
- gDemoRecordingRandomGeneratorSeedHigh = (gDemoRecordingRandomGeneratorSeed & 0xFF); // al;
- }
-
- // loc_49311: ; CODE XREF: saveInputForDemo+Dj
- if (gDemoCurrentInput == gCurrentUserInput && gDemoCurrentInputRepeatCounter != 0xF)
- {
- return;
- }
-
- // loc_4931E: ; CODE XREF: saveInputForDemo+24j
- gDemoCurrentInput = (gDemoCurrentInput | (gDemoCurrentInputRepeatCounter << 4));
-
- gDemoRecordingRandomGeneratorSeedHigh += gDemoCurrentInputRepeatCounter;
- gDemoRecordingRandomGeneratorSeedHigh++;
-
- fileWriteUInt8(gDemoCurrentInput, gCurrentRecordingDemoFile);
- gDemoCurrentInputRepeatCounter = 0xFF;
- gDemoCurrentInput = gCurrentUserInput;
-}
-
-void stopRecordingDemo() // somethingspsig proc near ; CODE XREF: runLevel+355p
- // ; recordDemo+30p ...
-{
- uint8_t scrambleSpeedLow = (gDemoRecordingLowestSpeed ^ gDemoRecordingRandomGeneratorSeedLow);
- uint8_t scrambleSpeedHigh = (gDemoRecordingLowestSpeed ^ gDemoRecordingRandomGeneratorSeedHigh);
- uint16_t scrambleSpeed = ((scrambleSpeedHigh << 8) | scrambleSpeedLow);
-
- fseek(gCurrentRecordingDemoFile, 1532, SEEK_SET);
- fileWriteUInt16(scrambleSpeed, gCurrentRecordingDemoFile);
-
- fileWriteUInt16(gDemoRecordingRandomGeneratorSeed, gCurrentRecordingDemoFile);
-
- fseek(gCurrentRecordingDemoFile, 0, SEEK_END);
-
- gDemoCurrentInput = 0xFF;
-
- fileWriteUInt8(gDemoCurrentInput, gCurrentRecordingDemoFile);
- if (byte_5A19B != 0)
- {
- FILE *sigFile = openWritableFileWithReadonlyFallback("MYSPSIG.TXT", "rb");
- if (sigFile != NULL)
- {
- if (fseek(sigFile, 0, SEEK_END) == 0)
- {
- long sigFileSize = ftell(sigFile);
- sigFileSize = MIN(sigFileSize, kMaxDemoSignatureLength);
-
- if (sigFileSize > 0)
- {
- // loc_493EB: ; CODE XREF: stopRecordingDemo+85j
- // ; stopRecordingDemo+8Dj
- if (fseek(sigFile, 0, SEEK_SET) == 0)
- {
- uint8_t signature[kMaxDemoSignatureLength + 1];
- size_t bytes = fileReadBytes(signature, sigFileSize, sigFile);
-
- if (bytes == sigFileSize)
- {
- int idx = 0;
- for (idx = 0; idx < sigFileSize; ++idx)
- {
- if (signature[idx] == 0xFF)
- {
- break;
- }
- }
-
- // Make sure the signature is terminated with 0xFF
- signature[idx] = 0xFF;
- sigFileSize = idx;
-
- // loc_4941C: ; CODE XREF: stopRecordingDemo+BCj
- fileWriteBytes(signature, sigFileSize + 1, gCurrentRecordingDemoFile);
- }
- }
- }
- }
-
- // loc_49430: ; CODE XREF: stopRecordingDemo+7Ej
- // ; stopRecordingDemo+89j ...
- fclose(sigFile);
- }
- }
- // loc_49435: ; CODE XREF: stopRecordingDemo+65j
- // ; stopRecordingDemo+6Fj
- fclose(gCurrentRecordingDemoFile);
- gIsRecordingDemo = 0;
- if (gHasUserInterruptedDemo != 0)
- {
- gIsPlayingDemo = 1;
- }
-
- // loc_4944F: ; CODE XREF: stopRecordingDemo+EEj
- drawGamePanelText();
- gIsGameBusy = 1;
- gIsPlayingDemo = 0;
-}
-
-size_t writeCurrentLevelToFile(FILE *file)
-{
- size_t bytes = fileWriteBytes(gCurrentLevel.tiles, sizeof(gCurrentLevel.tiles), file);
-
- bytes += fileWriteBytes(gCurrentLevel.unused, sizeof(gCurrentLevel.unused), file);
- bytes += fileWriteUInt8(gCurrentLevel.initialGravitation, file);
- bytes += fileWriteUInt8(gCurrentLevel.speedFixMagicNumber, file);
- bytes += fileWriteBytes(gCurrentLevel.name, sizeof(gCurrentLevel.name), file);
- bytes += fileWriteUInt8(gCurrentLevel.freezeZonks, file);
- bytes += fileWriteUInt8(gCurrentLevel.numberOfInfotrons, file);
- bytes += fileWriteUInt8(gCurrentLevel.numberOfSpecialPorts, file);
- for (int idx = 0; idx < kLevelMaxNumberOfSpecialPorts; ++idx)
- {
- bytes += fileWriteUInt16(gCurrentLevel.specialPortsInfo[idx].position, file);
- bytes += fileWriteUInt8(gCurrentLevel.specialPortsInfo[idx].gravity, file);
- bytes += fileWriteUInt8(gCurrentLevel.specialPortsInfo[idx].freezeZonks, file);
- bytes += fileWriteUInt8(gCurrentLevel.specialPortsInfo[idx].freezeEnemies, file);
- bytes += fileWriteUInt8(gCurrentLevel.specialPortsInfo[idx].unused, file);
- }
- bytes += fileWriteUInt8(gCurrentLevel.scrambledSpeed, file);
- bytes += fileWriteUInt8(gCurrentLevel.scrambledChecksum, file);
- bytes += fileWriteUInt16(gCurrentLevel.randomSeed, file);
-
- return bytes;
-}
-
-void recordDemo(uint16_t demoIndex) // sub_4945D proc near ; CODE XREF: handleGameUserInput+294p
- // ; handleGameUserInput+2A4p ...
-{
- // 01ED:27FA
- gIsMoveScrollModeEnabled = 0;
- gAdditionalScrollOffsetX = 0;
- gAdditionalScrollOffsetY = 0;
- gIsFlashingBackgroundModeEnabled = 0;
- gDebugExtraRenderDelay = 1;
-
- replaceCurrentPaletteColor(0, (Color){0, 0, 0});
-
- if (gIsRecordingDemo != 0)
- {
- stopRecordingDemo();
- }
-
- // loc_49490: ; CODE XREF: recordDemo+2Ej
- char demoIndexCharacter = '0' + demoIndex;
- gDemo0BinFilename[4] = demoIndexCharacter;
-
- char *filename = gDemo0BinFilename;
-
- if (supportsSPFileDemoPlayback() && (gShouldRecordWithOriginalDemoFilenames & 0xFF) == 0) // cmp byte ptr gShouldRecordWithOriginalDemoFilenames, 0
- {
- gSPDemoFileName[7] = demoIndexCharacter;
- filename = gSPDemoFileName;
- }
-
- // loc_494A6: ; CODE XREF: recordDemo+41j
- gRecordingDemoMessage[18] = demoIndexCharacter;
-
- FILE *file = openWritableFile(filename, "wb");
- if (file == NULL)
- {
- return;
- }
-
- // loc_494B8: ; CODE XREF: recordDemo+56j
- gCurrentRecordingDemoFile = file; // file handle
- gCurrentLevel.speedFixMagicNumber = 0x20 + kGameVersion;
- // TODO: don't know for sure but this probably is related to adjusting the demo time with the speed or something?
- // bl = speed3;
- // cl = 4;
- // bl = bl << cl;
- // bl |= gGameSpeed;
- // speed2 = bl;
- gDemoRecordingLowestSpeed = gGameSpeed;
-
- size_t bytes = writeCurrentLevelToFile(file);
- if (bytes != kLevelDataLength)
- {
- return;
- }
-
- // The original code sets index | 0x80 in demoCurrentInputRepeatCounter and then writes it to the file
- // but seems to be useless because that value is overriden later.
- //
- uint8_t levelNumber = gCurrentSelectedLevelIndex | 0x80;
- bytes = fileWriteUInt8(levelNumber, file);
- if (bytes < 1)
- {
- return;
- }
- gDemoCurrentInput = UserInputNone;
- gDemoRecordingJustStarted = 1;
- gIsPlayingDemo = 0;
- gDemoCurrentInputRepeatCounter = 0xFE; // 254
- gDebugExtraRenderDelay = 1;
- if (gIsSPDemoAvailableToRun == 0)
- {
- memcpy(gCurrentDemoLevelName, gCurrentLevelName, 3);
- }
-
- // loc_4952A: ; CODE XREF: recordDemo+BCj
- gIsRecordingDemo = 1;
- if (gHasUserInterruptedDemo != 0)
- {
- gIsPlayingDemo = 1;
- }
-
- // loc_4953B: ; CODE XREF: recordDemo+D7j
- fetchAndInitializeLevel();
- gIsPlayingDemo = 0;
-}
-
-void prepareDemoRecordingFilename() // sub_49544 proc near ; CODE XREF: start+3A1p
- // ; handleOkButtonClick:loc_4B40Fp ...
-{
- // 01ED:28E1
-
- char currentSuffix[3] = "AT";
- strcpy(currentSuffix, &gLevelsDatFilename[8]);
-
- // Checks if the last two chars are "00" like LEVELS.D00?
- if (strcmp(currentSuffix, "00") == 0)
- {
- // replaces the content with "--"
- strcpy(currentSuffix, "--");
- }
-
- // loc_4954F: // ; CODE XREF: prepareDemoRecordingFilename+6j
- // Now checks if the last two chars are "AT" like LEVELS.DAT?
- if (strcmp(currentSuffix, "AT") == 0)
- {
- // replaces the content with "00"
- strcpy(currentSuffix, "00");
- }
-
- // loc_49557: // ; CODE XREF: prepareDemoRecordingFilename+Ej
- memcpy(gSPDemoFileName, currentSuffix, 2);
-}
-
-void handleGameUserInput() // sub_4955B proc near ; CODE XREF: runLevel:loc_48B6Bp
- // ; runLevel+30Cp
-{
- // 01ED:28F8
-
- if (gIsMoveScrollModeEnabled != 0)
- {
- updateUserInputInScrollMovementMode(); // 01ED:28FF
- }
- // loc_49567: ; CODE XREF: handleGameUserInput+5j
- else if (gIsPlayingDemo == 0)
- {
- updateUserInput(); // 01ED:290B
- if (gIsRecordingDemo != 0)
- {
- saveInputForDemo(); // 01ED:2915
- }
- }
-
- // loc_4957B: ; CODE XREF: handleGameUserInput+Aj
- // ; handleGameUserInput+11j ...
- if (gIsPlayingDemo != 0)
- {
- simulateDemoInput(); // 01ED:2929
- }
-
- // loc_4958F: ; CODE XREF: handleGameUserInput+2Fj
- if (gToggleGamePanelKeyAutoRepeatCounter != 0) // cmp byte ptr word_510C1+1, 0
- {
- // 01ED:293E
- gToggleGamePanelKeyAutoRepeatCounter--;
- }
-
- // loc_4959A: ; CODE XREF: handleGameUserInput+39j
- if (isToggleGamePanelButtonPressed() == 0)
- {
- gToggleGamePanelKeyAutoRepeatCounter = 0; // mov byte ptr word_510C1+1, 0
- }
- // loc_495A9: ; CODE XREF: handleGameUserInput+44j
- else if (gToggleGamePanelKeyAutoRepeatCounter == 0) // 01ED:2946
- {
- // loc_495B3: ; CODE XREF: handleGameUserInput+53j
- gToggleGamePanelKeyAutoRepeatCounter = 0x20; // mov byte ptr word_510C1+1, 20h ; ' '
- if (gShouldShowGamePanel != 0)
- {
- gShouldShowGamePanel = 0; // mov byte ptr word_510C1, 0
- gCurrentPanelHeight = 0;
- }
- else
- {
- // loc_495FB: ; CODE XREF: handleGameUserInput+62j
- gShouldShowGamePanel = 1; // mov byte ptr word_510C1, 1
- gCurrentPanelHeight = kPanelBitmapHeight;
- }
- }
-
- // loc_49635: ; CODE XREF: handleGameUserInput+4Bj
- // ; handleGameUserInput+55j ...
- if (gIsDebugModeEnabled != 1)
- {
- checkDebugKeys();
- return;
- }
-
- // loc_4963F: ; CODE XREF: handleGameUserInput+DFj
- if (gIsRecordingDemo == 0) // 01ED:29DC
- {
- // loc_49649: ; CODE XREF: handleGameUserInput+E9j
- if (gIsMKeyPressed != 0) // cmp byte ptr gIsMKeyPressed, 0
- {
- gIsMoveScrollModeEnabled = 1;
- }
-
- // loc_49656: ; CODE XREF: handleGameUserInput+F3j
- if (gIsDKeyPressed != 0)
- {
- gIsFlashingBackgroundModeEnabled = 1;
- }
-
- // loc_49663: ; CODE XREF: handleGameUserInput+100j
- if (gIsZKeyPressed != 0) // cmp byte ptr gIsZKeyPressed, 0
- {
- // 01ED:2A07
- removeTiles(LevelTileTypeZonk);
- }
-
- // loc_4966F: ; CODE XREF: handleGameUserInput+10Dj
- if (gIsBKeyPressed != 0) // cmp byte ptr gIsBKeyPressed, 0
- {
- removeTiles(LevelTileTypeBase);
- }
-
- // loc_4967B: ; CODE XREF: handleGameUserInput+119j
- if (gIsHKeyPressed != 0)
- {
- removeTiles(LevelTileTypeHardware);
- }
-
- // loc_49687: ; CODE XREF: handleGameUserInput+125j
- if (gIsCKeyPressed != 0) // cmp byte ptr gIsCKeyPressed, 0
- {
- removeTiles(LevelTileTypeChip);
- }
-
- // loc_49693: ; CODE XREF: handleGameUserInput+131j
- if (gIsSKeyPressed != 0)
- {
- removeTiles(LevelTileTypeSnikSnak);
- }
-
- // loc_4969F: ; CODE XREF: handleGameUserInput+13Dj
- if (gIsRKeyPressed != 0)
- {
- videoLoop();
- restartLevel();
- }
-
- // loc_496AC: ; CODE XREF: handleGameUserInput+149j
- if (gIsPlayingDemo == 0)
- {
- // loc_496C0: ; CODE XREF: handleGameUserInput+160j
- if (gIs1KeyPressed != 0)
- {
- gDebugExtraRenderDelay = 1;
- }
-
- // loc_496CD: ; CODE XREF: handleGameUserInput+16Aj
- if (gIs2KeyPressed != 0)
- {
- gDebugExtraRenderDelay = 2;
- }
-
- // loc_496DA: ; CODE XREF: handleGameUserInput+177j
- if (gIs3KeyPressed != 0)
- {
- gDebugExtraRenderDelay = 3;
- }
-
- // loc_496E7: ; CODE XREF: handleGameUserInput+184j
- if (gIs4KeyPressed != 0)
- {
- gDebugExtraRenderDelay = 4;
- }
-
- // loc_496F4: ; CODE XREF: handleGameUserInput+191j
- if (gIs5KeyPressed != 0)
- {
- gDebugExtraRenderDelay = 6;
- }
-
- // loc_49701: ; CODE XREF: handleGameUserInput+19Ej
- if (gIs6KeyPressed != 0)
- {
- gDebugExtraRenderDelay = 8;
- }
-
- // loc_4970E: ; CODE XREF: handleGameUserInput+1ABj
- if (gIs7KeyPressed != 0)
- {
- gDebugExtraRenderDelay = 0xC;
- }
-
- // loc_4971B: ; CODE XREF: handleGameUserInput+1B8j
- if (gIs8KeyPressed != 0)
- {
- gDebugExtraRenderDelay = 0x10;
- }
-
- // loc_49728: ; CODE XREF: handleGameUserInput+1C5j
- if (gIs9KeyPressed != 0)
- {
- gDebugExtraRenderDelay = 0x18;
- }
-
- // loc_49735: ; CODE XREF: handleGameUserInput+1D2j
- if (gIs0KeyPressed != 0)
- {
- gDebugExtraRenderDelay = 0x20;
- }
- }
- }
-
- // loc_49742: ; CODE XREF: handleGameUserInput+EBj
- // ; handleGameUserInput+158j ...
- // 01ED:2ADF
- if (gIsLeftControlPressed == 1)
- {
- // loc_497D1: ; CODE XREF: handleGameUserInput+1EEj
- if (gIsPlayingDemo != 0)
- {
- loc_4988E();
- return;
- }
- // loc_497DB: ; CODE XREF: handleGameUserInput+27Bj
- // TODO: this if is only relevant when the game is autoadjusting the speed (speed3 != 0), right?
- // else if (speed3 < 0)
- // {
- // loc_4988E();
- // return;
- // }
- // loc_497E5: ; CODE XREF: handleGameUserInput+285j
- else if (gIsF1KeyPressed == 1)
- {
- // 01ED:2B89
- recordDemo(0);
- }
- // loc_497F5: ; CODE XREF: handleGameUserInput+28Fj
- else if (gIsF2KeyPressed == 1)
- {
- // 01ED:2B99
- recordDemo(1);
- }
- // loc_49805: ; CODE XREF: handleGameUserInput+29Fj
- else if (gIsF3KeyPressed == 1)
- {
- recordDemo(2);
- }
- // loc_49814: ; CODE XREF: handleGameUserInput+2AFj
- else if (gIsF4KeyPressed == 1)
- {
- recordDemo(3);
- }
- // loc_49823: ; CODE XREF: handleGameUserInput+2BEj
- else if (gIsF5KeyPressed == 1)
- {
- recordDemo(4);
- }
- // loc_49832: ; CODE XREF: handleGameUserInput+2CDj
- else if (gIsF6KeyPressed == 1)
- {
- recordDemo(5);
- }
- // loc_49841: ; CODE XREF: handleGameUserInput+2DCj
- else if (gIsF7KeyPressed == 1)
- {
- recordDemo(6);
- }
- // loc_49850: ; CODE XREF: handleGameUserInput+2EBj
- else if (gIsF8KeyPressed == 1)
- {
- recordDemo(7);
- }
- // loc_4985F: ; CODE XREF: handleGameUserInput+2FAj
- else if (gIsF9KeyPressed == 1)
- {
- recordDemo(8);
- }
- // loc_4986E: ; CODE XREF: handleGameUserInput+309j
- else if (gIsF10KeyPressed == 1)
- {
- recordDemo(9);
- }
- // loc_4987D: ; CODE XREF: handleGameUserInput+318j
- else if (gIsF12KeyPressed == 1 && gIsRecordingDemo != 0)
- {
- stopRecordingDemo();
- }
- }
- else
- {
- // loc_4974C: ; CODE XREF: handleGameUserInput+1ECj
- if ((gFrameCounter & 7) == 0 && gIsRecordingDemo == 0)
- {
- // loc_49761: ; CODE XREF: handleGameUserInput+201j
- if (gIsF1KeyPressed == 0)
- {
- gToggleGravityAutorepeatFlag = 0;
- }
- // loc_4976F: ; CODE XREF: handleGameUserInput+20Bj
- else if (gToggleGravityAutorepeatFlag == 0)
- {
- gToggleGravityAutorepeatFlag--;
- gIsGravityEnabled &= 1;
- gIsGravityEnabled = gIsGravityEnabled ^ 1;
- }
-
- if (gIsF1KeyPressed == 0 || gToggleGravityAutorepeatFlag != 0)
- {
- // loc_49786: ; CODE XREF: handleGameUserInput+212j
- // ; handleGameUserInput+219j
- if (gIsF2KeyPressed == 0)
- {
- gToggleZonksFrozenAutorepeatFlag = 0;
- }
- // loc_49794: ; CODE XREF: handleGameUserInput+230j
- else if (gToggleZonksFrozenAutorepeatFlag == 0)
- {
- gToggleZonksFrozenAutorepeatFlag--;
- gAreZonksFrozen &= 2;
- gAreZonksFrozen = gAreZonksFrozen ^ 2;
- }
-
- if (gIsF2KeyPressed == 0 || gToggleZonksFrozenAutorepeatFlag != 0)
- {
- // loc_497AB: ; CODE XREF: handleGameUserInput+237j
- // ; handleGameUserInput+23Ej
- if (gIsF3KeyPressed == 0)
- {
- gToggleEnemiesFrozenAutorepeatFlag = 0;
- }
- // loc_497B9: ; CODE XREF: handleGameUserInput+255j
- else if (gToggleEnemiesFrozenAutorepeatFlag == 0)
- {
- gToggleEnemiesFrozenAutorepeatFlag--;
- gAreEnemiesFrozen &= 1;
- gAreEnemiesFrozen = gAreEnemiesFrozen ^ 1;
- }
- }
- }
- }
- }
-
- loc_4988E();
-}
-
-void loc_4988E() // : ; CODE XREF: handleGameUserInput+1F9j
-{
- // ; handleGameUserInput+203j ...
- if (gIsRecordingDemo != 0 || gIsPlayingDemo != 0)
- {
- checkDebugKeys();
- return;
- }
-
- // loc_498A2: ; CODE XREF: handleGameUserInput+342j
- if (gIsMinusKeyPressed == 0)
- {
- gDebugSkipPreviousLevelAutorepeatFlag_1 = 0;
- gDebugSkipPreviousLevelAutorepeatFlag_2 = 5;
- }
- // loc_498B5: ; CODE XREF: handleGameUserInput+34Cj
- else if (gDebugSkipPreviousLevelAutorepeatFlag_1 != 0)
- {
- gDebugSkipPreviousLevelAutorepeatFlag_1--;
- }
- else
- {
- // loc_498C2: ; CODE XREF: handleGameUserInput+35Fj
- if (gDebugSkipPreviousLevelAutorepeatFlag_2 != 0)
- {
- gDebugSkipPreviousLevelAutorepeatFlag_2--;
- gDebugSkipPreviousLevelAutorepeatFlag_1 = 0x10;
- }
-
- // loc_498D2: ; CODE XREF: handleGameUserInput+36Cj
- if (gCurrentSelectedLevelIndex <= 1)
- {
- gCurrentSelectedLevelIndex = 2;
- }
-
- // loc_498DF: ; CODE XREF: handleGameUserInput+37Cj
- gCurrentSelectedLevelIndex--;
- if (gCurrentSelectedLevelIndex > kNumberOfLevels)
- {
- gCurrentSelectedLevelIndex = kNumberOfLevels;
- }
-
- // loc_498F0: ; CODE XREF: handleGameUserInput+38Dj
- // ax = gCurrentSelectedLevelIndex;
- convertLevelNumberTo3DigitStringWithPadding0(gCurrentSelectedLevelIndex);
- drawLevelList();
- debugSkipLevel();
- }
-
- // loc_498FC: ; CODE XREF: handleGameUserInput+358j
- // ; handleGameUserInput+365j
- if (gIsEqualsKeyPressed == 0)
- {
- gDebugSkipNextLevelAutorepeatFlag_1 = 0;
- gDebugSkipNextLevelAutorepeatFlag_2 = 5;
- }
- // loc_4990F: ; CODE XREF: handleGameUserInput+3A6j
- else if (gDebugSkipNextLevelAutorepeatFlag_1 != 0)
- {
- gDebugSkipNextLevelAutorepeatFlag_1--;
- }
- else
- {
- // loc_4991C: ; CODE XREF: handleGameUserInput+3B9j
- if (gDebugSkipNextLevelAutorepeatFlag_2 != 0)
- {
- gDebugSkipNextLevelAutorepeatFlag_2--;
- gDebugSkipNextLevelAutorepeatFlag_1 = 0x10; // 16
- }
-
- // loc_4992C: ; CODE XREF: handleGameUserInput+3C6j
- if (gCurrentSelectedLevelIndex >= kNumberOfLevels)
- {
- gCurrentSelectedLevelIndex = kNumberOfLevels - 1;
- }
-
- // loc_49939: ; CODE XREF: handleGameUserInput+3D6j
- gCurrentSelectedLevelIndex++;
- convertLevelNumberTo3DigitStringWithPadding0(gCurrentSelectedLevelIndex); // 01ED:2CDD
- drawLevelList();
- debugSkipLevel();
- }
-
- checkDebugKeys();
-}
-
-void stopDemoAndPlay()
-{
- gIsPlayingDemo = 0;
- gShouldUpdateTotalLevelTime = 0;
- gHasUserCheated = 1;
- gHasUserInterruptedDemo = 1;
-}
-
-void checkDebugKeys() // loc_49949: ; CODE XREF: handleGameUserInput+E1j
-// ; handleGameUserInput+33Aj ...
-{
- // 01ED:2CE6
-
- uint8_t shouldStartFromSavedSnapshot = (gShouldStartFromSavedSnapshot != 0);
- gShouldStartFromSavedSnapshot = 0;
- if (shouldStartFromSavedSnapshot)
- {
- loadGameSnapshot();
- return;
- }
-
- // loc_49958: ; CODE XREF: handleGameUserInput+3F8j
- if (gIsLeftControlPressed != 1)
- {
- loc_49C41();
- return;
- }
-
- // loc_49962: ; CODE XREF: handleGameUserInput+402j
- // Control + F12: Interrupt demo and continue playing
- if (gIsF12KeyPressed == 1 && gIsPlayingDemo != 0)
- {
- stopDemoAndPlay();
- }
-
- // loc_49984: ; CODE XREF: handleGameUserInput+40Cj
- // ; handleGameUserInput+413j
- if (gIsScrollLockPressed == 1)
- {
- gIsDebugModeEnabled = 1;
- drawTextWithChars8FontToGamePanel(304, 14, 6, "DB"); // Debug mode enabled
- gAdditionalInfoInGamePanelFrameCounter = 0x46; // 70
- }
-
- // loc_499AA: ; CODE XREF: handleGameUserInput+42Ej
- // ; handleGameUserInput+43Bj
- if (gIsWKeyPressed != 1)
- {
- // loc_49A7F: ; CODE XREF: handleGameUserInput+456j
- if (gIsLKeyPressed != 1)
- {
- loc_49C41();
- return;
- }
-
- loadGameSnapshot();
- return;
- }
-
- saveGameSnapshot();
-}
-
-void saveGameSnapshot() // loc_499C8: ; CODE XREF: handleGameUserInput+454j
-{
- gShouldCloseAdvancedMenu = 1;
-
- if (saveGameState() != 0)
- {
- showSavegameOperationError();
- return;
- }
-
- // loc_49A78: ; CODE XREF: handleGameUserInput+518j
- loc_49C2C("WR"); // Means snapshot saved with no issues
-}
-
-void loadGameSnapshot() // loc_49A89: ; CODE XREF: handleGameUserInput+3FAj
-{
- gShouldCloseAdvancedMenu = 1;
-
- if (canLoadGameState() == 0)
- {
- showSavegameOperationError();
- return;
- }
-
- // loc_49A96: ; CODE XREF: handleGameUserInput+536j
- if (gIsRecordingDemo != 0)
- {
- stopRecordingDemo();
- }
-
- gIsRecordingDemo = 0;
-
- if (loadGameState() != 0)
- {
- showSavegameOperationError();
- return;
- }
-
- forceRestoreOriginalFancyTiles();
-
- // loc_49B84: ; CODE XREF: handleGameUserInput+619j
- // ; handleGameUserInput+624j
- gIsPlayingDemo = 0;
- gIsRecordingDemo = 0;
- gCurrentUserInput = UserInputNone;
- gIsMoveScrollModeEnabled = 0;
- gAdditionalScrollOffsetX = 0;
- gAdditionalScrollOffsetY = 0;
- gIsFlashingBackgroundModeEnabled = 0;
- gDebugExtraRenderDelay = 1;
- replaceCurrentPaletteColor(0, (Color){0, 0, 0});
- generateRandomSeedFromClock();
- generateRandomNumber();
- // I commented out all these video transitions because they're not needed in the reimplementation. They were here
- // just to prevent graphical glitches. Also they made loading much slower.
- //
- // setPalette(gBlackPalette);
- // videoLoop();
- drawFixedLevel();
- drawGamePanel();
- convertToEasyTiles();
- scrollToMurphy();
- gLastDrawnMinutesAndSeconds = 0xFFFF;
- gLastDrawnHours = 0xFF;
- drawGameTime();
- gHasUserCheated = 1;
- gIsRecordingDemo = 0;
-
- drawTextWithChars8FontToGamePanel(304, 14, 6, "LD"); // Means snapshot was loaded successfully
- gAdditionalInfoInGamePanelFrameCounter = 0x46; // 70 or '&'
-
- drawCurrentLevelViewport(gCurrentPanelHeight);
- // videoLoop();
-
- // loc_49C12: ; CODE XREF: handleGameUserInput+6A8j
- // fadeToPalette(gGamePalette);
-
- // loc_49C40: ; CODE XREF: handleGameUserInput+6BDj
- // ; handleGameUserInput+6D6j
- loc_49C41();
-}
-
-void showSavegameOperationError() // loc_49C28: ; CODE XREF: handleGameUserInput+47Aj
-{
- // ; handleGameUserInput+51Aj ...
- // push si
- // mov si, 0A007h "XX"
- loc_49C2C("XX"); // Means problem writing/loading snapshot
-}
-
-void loc_49C2C(char text[3]) // : ; CODE XREF: handleGameUserInput+521j
-{
- drawTextWithChars8FontToGamePanel(304, 14, 6, text);
- gAdditionalInfoInGamePanelFrameCounter = 0x46; // 70 or '&'
-
- // loc_49C40: ; CODE XREF: handleGameUserInput+6BDj
- // ; handleGameUserInput+6D6j
- // pop si
-
- loc_49C41();
-}
-
-void loc_49C41() // ; CODE XREF: handleGameUserInput+404j
-// ; handleGameUserInput+52Bj
-{
- if (gIsLeftAltPressed == 1 && gIsScrollLockPressed == 1)
- {
- // 01ED:2FEC
- gIsDebugModeEnabled = 0;
- gIsMoveScrollModeEnabled = 0;
- gAdditionalScrollOffsetX = 0;
- gAdditionalScrollOffsetY = 0;
- gIsFlashingBackgroundModeEnabled = 0;
- gDebugExtraRenderDelay = 1;
- replaceCurrentPaletteColor(0, (Color){0, 0, 0});
-
- drawTextWithChars8FontToGamePanel(304, 14, 6, "--"); // Debug mode disabled
- gAdditionalInfoInGamePanelFrameCounter = 0x46; // 70 or '&'
- }
-
- // loc_49C96: ; CODE XREF: handleGameUserInput+6EBj
- // ; handleGameUserInput+6F2j ...
- if (isPauseButtonPressed())
- {
- // 01ED:303A
- gIsGameRunning = 0;
- runAdvancedOptionsRootMenu();
- gIsGameRunning = 1;
- }
-
- // loc_49CC8: ; CODE XREF: handleGameUserInput+740j
- if (gIsNumLockPressed != 0)
- {
- // 01ED:306C
- gIsGameRunning = 0;
- // mov si, 6095h
- fadeToPalette(gGameDimmedPalette);
-
- do
- {
- // loc_49CDA: ; CODE XREF: handleGameUserInput+784j
- int9handler(1);
- } while (gIsNumLockPressed == 1);
-
- // From the speed fix mod, but in uppercase so I can use characterForLastKeyPressed
- static const char kMagicDisableDebugModeCode[] = "CANT STO";
- uint8_t index = 0;
-
- do
- {
- // loc_49CE4: ; CODE XREF: handleGameUserInput+7A6j
- int9handler(1);
-
- if (index >= strlen(kMagicDisableDebugModeCode))
- {
- gIsDebugModeEnabled = 0;
- break;
- }
- else
- {
- // loc_49CF3: ; CODE XREF: handleGameUserInput+78Ej
- if (characterForLastKeyPressed() == kMagicDisableDebugModeCode[index])
- {
- index++;
- }
-
- // loc_49CFC: ; CODE XREF: handleGameUserInput+79Dj
- if (gIsNumLockPressed != 0)
- {
- break;
- }
- }
- } while (1);
-
- do
- {
- // loc_49D03: ; CODE XREF: handleGameUserInput+796j
- // ; handleGameUserInput+7ADj
- int9handler(1);
- } while (gIsNumLockPressed == 1);
- // mov si, 6015h
- fadeToPalette(gGamePalette);
- gIsGameRunning = 1;
- }
-
- // loc_49D15: ; CODE XREF: handleGameUserInput+772j
- if (isExitLevelButtonPressed() // Select/Back/- controller button -> exit game
- && gQuitLevelCountdown <= 0)
- {
- // This is called when I press ESC to exit the game, but not when I die
- gShouldKillMurphy = 1; // 01ED:30C0
- }
-
- // loc_49D29: ; CODE XREF: handleGameUserInput+7BFj
- // ; handleGameUserInput+7C6j
- if (gIsQKeyPressed != 0 || getGameControllerCancelButton())
- {
- // 01ED:30CD
- gIsMoveScrollModeEnabled = 0;
- gAdditionalScrollOffsetX = 0;
- gAdditionalScrollOffsetY = 0;
- gIsFlashingBackgroundModeEnabled = 0;
- }
-
- // loc_49D48: ; CODE XREF: handleGameUserInput+7D3j
- if (isShowNumberOfRedDisksButtonPressed())
- {
- drawNumberOfRemainingRedDisks(); // 01ED:30EC
- }
-}
-
-void forceRestoreOriginalFancyTiles() // sub_49D53 proc near ; CODE XREF: handleGameUserInput+626p
- // ; removeTiles+21p
-{
- // 01ED:30F0
- gIsShowingFancyTiles = 0;
- restoreOriginalFancyTiles();
-}
-
-void restoreOriginalFancyTiles() // proc near ; CODE XREF: runLevel+A7p
-{
- // 01ED:30F5
-
- for (int i = 0; i < kLevelSize; ++i)
- {
- // loc_49D65: ; CODE XREF: restoreOriginalFancyTiles+18j
- StatefulLevelTile *tile = &gCurrentLevelState[i];
- if (tile->tile == LevelTileTypeExplosion) // 31
- {
- tile->tile = 0xF1; // 241
- }
- }
-
- uint8_t was_gIsShowingFancyTiles_NonZero = (gIsShowingFancyTiles != 0);
- gIsShowingFancyTiles = 0;
- if (was_gIsShowingFancyTiles_NonZero)
- {
- return;
- }
-
- for (int i = 0; i < kLevelSize; ++i)
- {
- // loc_49D84: ; CODE XREF: levelScanThing+4Cj
- StatefulLevelTile *tile = &gCurrentLevelState[i];
- if (tile->state != 0 || tile->tile != LevelTileTypeHardware)
- {
- continue;
- }
-
- LevelTileType originalTile = gCurrentLevel.tiles[i];
-
- if (originalTile >= LevelTileTypeHardware2 // 28
- && originalTile <= LevelTileTypeHardware11) // 37
- {
- tile->tile = originalTile;
- tile->state = 0;
- }
- }
-
- // loc_49DA6: ; CODE XREF: levelScanThing+31j
- for (int i = 0; i < kLevelSize; ++i)
- {
- // loc_49DAC: ; CODE XREF: levelScanThing+7Fj
- StatefulLevelTile *tile = &gCurrentLevelState[i];
- if (tile->state != 0 || tile->tile != LevelTileTypeChip)
- {
- continue;
- }
-
- LevelTileType originalTile = gCurrentLevel.tiles[i];
-
- if (originalTile >= LevelTileTypeHorizontalChipLeft // 26
- && originalTile <= LevelTileTypeHorizontalChipBottom) // 39
- {
- originalTile -= LevelTileTypeHardware2; // 28
- if (originalTile >= LevelTileTypePortDown) // 10
- {
- originalTile += LevelTileTypeHardware2; // 28
- tile->tile = originalTile;
- tile->state = 0;
- }
- }
- }
-
- // loc_49DD9: ; CODE XREF: levelScanThing+59j
- gIsShowingFancyTiles = 1;
-}
-
-void updateMovingObjects() // gameloop proc near ; CODE XREF: runLevel:noFlashingp
-{
- // 01ED:317D
-
- gMurphyLocation = updateMurphy(gMurphyLocation); // 01ED:318B
-
- if (gIsFlashingBackgroundModeEnabled != 0)
- {
- replaceCurrentPaletteColor(0, (Color){0x3f, 0x3f, 0x21});
- }
-
- // loc_49E14:
- if (gIsFlashingBackgroundModeEnabled != 0)
- {
- replaceCurrentPaletteColor(0, (Color){0x3f, 0x21, 0x21});
- }
-
- // loc_49E33:
- uint16_t numberOfMovingObjects = 0;
-
- typedef struct
- {
- MovingFunction function;
- uint16_t tilePosition;
- } MovingObject;
-
- MovingObject movingObjects[kLevelSize];
-
- // This loop doesn't iterate through _every tile_. Instead it only goes from the first tile in the gamefield
- // (located at levelWidth + 1) to the last tile in the gamefield (located at levelSize - levelWidth - 2).
- // From what I've seen some demos depend on this. Demo 00/01s028-1.SP is an example because there is a
- // non-hardware tile at the bottom edge that explodes, and a SnikSnak goes in there.
- //
- for (uint16_t i = kLevelWidth + 1; i < kLevelSize - kLevelWidth - 1; ++i) // starts from si, ends in si + cx
- {
- // checkCellForMovingObject: ; CODE XREF: updateMovingObjects+84j
- LevelTileType tile = gCurrentLevelState[i].tile; // mov bl, byte ptr leveldata[si]
-
- // Does this check filter out values except like 0, 2, 16 and 18??
- if ((tile & LevelTileTypeSportRight) == 0)
- {
- continue;
- }
-
- if (tile >= 0x20) // 32 because there are 32 moving functions, which means this is about moving objects (scissors, the infotrons, etc.)
- {
- continue;
- }
-
- MovingFunction function = movingFunctions[tile];
-
- if (function != NULL)
- {
- // There is a list of predefined functions, one per object, and this goes through
- // every tile, looking for objects with a moving function, and then putting the
- // tile and the function in a list that will be iterated later to update those objects.
- //
- MovingObject *object = &movingObjects[numberOfMovingObjects];
- object->function = function;
- object->tilePosition = i;
- numberOfMovingObjects++; // dx++;
- }
- // moveToNextCell:
- }
-
- if (numberOfMovingObjects != 0)
- {
- // Call all the moving functions
- for (uint16_t i = 0; i < numberOfMovingObjects; ++i)
- {
- // 01ED:3214
- movingObjects[i].function(movingObjects[i].tilePosition);
- }
- }
-
- // doneWithupdateMovingObjects:
-
- // 01ED:3227
- if (gShouldKillMurphy != 1 && gIsMurphyUpdated != 0)
- {
- return;
- }
-
- // loc_49E99: ; CODE XREF: updateMovingObjects+AFj
- // ; updateMovingObjects+B6j
- if (gQuitLevelCountdown == 0) // 01ED:3236
- {
- // 01ED:323D
- gShouldKillMurphy = 0;
- detonateBigExplosion(gMurphyPreviousLocation);
- gQuitLevelCountdown = 0x40; // 64
- }
-
- return;
-}
-
-void updateScrollOffset() // sub_49EBE proc near ; CODE XREF: runLevel+109p
- // ; scrollToMurphy+29p
-{
- // 01ED:325B
- uint16_t randomNumber = 0;
-
- // This random number is used to generate the shaking effect on explosions.
- // The original game generates this random number here for _every_ explosion, even if
- // normally only Murphy's explosion will make the screen shake. However it's necessary
- // to do this here to make sure the right sequence of random numbers is generated when
- // there are explosions in the level.
- //
- if (gIsExplosionStarted == 1)
- {
- randomNumber = generateRandomNumber();
- }
-
- // loc_49ECC: ; CODE XREF: updateScrollOffset+7j
- int16_t scrollX = gMurphyPositionX;
- int16_t scrollY = gMurphyPositionY;
- scrollX -= kScreenWidth / 2; // 152
- if (scrollX < 0)
- {
- scrollX = 0;
- }
-
- // loc_49EDF: ; CODE XREF: updateScrollOffset+1Cj
- uint16_t maxScrollX = kLevelBitmapWidth - kScreenWidth;
- if (scrollX > maxScrollX) // 624
- {
- scrollX = maxScrollX; // 624
- }
-
- // loc_49EE8: ; CODE XREF: updateScrollOffset+25j
- if (gShouldShowGamePanel == 0)
- {
- // loc_49EF4: ; CODE XREF: updateScrollOffset+2Fj
- scrollY -= kScreenHeight / 2;
- }
- else
- {
- scrollY -= (kScreenHeight - kPanelBitmapHeight) / 2;
- }
-
- // loc_49EF7: ; CODE XREF: updateScrollOffset+34j
- if (scrollY < 0)
- {
- scrollY = 0;
- }
-
- // loc_49EFE: ; CODE XREF: updateScrollOffset+3Cj
- uint16_t maxScrollY = 0;
-
- if (gShouldShowGamePanel == 0)
- {
- // loc_49F0F: ; CODE XREF: updateScrollOffset+45j
- maxScrollY = kLevelBitmapHeight - kScreenHeight;
- if (scrollY > maxScrollY)
- {
- scrollY = maxScrollY;
- }
- }
- else
- {
- maxScrollY = kLevelBitmapHeight - kScreenHeight + kPanelBitmapHeight;
- if (scrollY > maxScrollY)
- {
- scrollY = maxScrollY;
- }
- }
-
- // loc_49F17: ; CODE XREF: updateScrollOffset:loc_49F0Dj
- // ; updateScrollOffset+54j
- if (gIsMoveScrollModeEnabled == 0 || gIsNumpad5Pressed != 0)
- {
- // loc_49F25: ; CODE XREF: updateScrollOffset+5Ej
- gMurphyScrollOffsetX = scrollX;
- gMurphyScrollOffsetY = scrollY;
- }
- else
- {
- // loc_49F2E: ; CODE XREF: updateScrollOffset+65j
- scrollX = gMurphyScrollOffsetX;
- scrollY = gMurphyScrollOffsetY;
-
- int16_t additionalScrollX = scrollX;
- scrollX += gAdditionalScrollOffsetX;
- if (scrollX < 0)
- {
- // 01ED:32DD
- scrollX = 0;
- }
- else
- {
- // loc_49F56: ; CODE XREF: updateScrollOffset+80j
- if (scrollX > maxScrollX)
- {
- scrollX = maxScrollX;
- }
- }
-
- // loc_49F66: ; CODE XREF: updateScrollOffset+8Cj
- // ; updateScrollOffset+96j
- additionalScrollX -= scrollX;
- additionalScrollX = -additionalScrollX;
- gAdditionalScrollOffsetX = additionalScrollX;
-
- // loc_49F6E: ; CODE XREF: updateScrollOffset+9Dj
- // ; updateScrollOffset+A3j
- int16_t additionalScrollY = scrollY;
- scrollY += gAdditionalScrollOffsetY;
- if (scrollY < 0) // in asm there wasn't a explicit "cmp", just the "add" above
- {
- scrollY = 0;
- }
- else
- {
- // loc_49F99: ; CODE XREF: updateScrollOffset+CFj
- if (scrollY > maxScrollY) // 168
- {
- scrollY = maxScrollY; // 168
- }
- }
-
- // loc_49FA1: ; CODE XREF: updateScrollOffset+C1j
- // ; updateScrollOffset+D9j
- additionalScrollY -= scrollY;
- additionalScrollY = -additionalScrollY;
- gAdditionalScrollOffsetY = additionalScrollY;
- }
-
- // loc_49FA9: ; CODE XREF: updateScrollOffset+6Ej
- // ; updateScrollOffset+BDj ...
- // This makes the screen shake on an explosion
- if (gShouldShakeWithAllExplosions != 0 || (gShakeWithExplosionsDisabled == 0 && (gQuitLevelCountdown & 0xFF) != 0))
- {
- // loc_49FBE: ; CODE XREF: updateScrollOffset+F0j
- randomNumber = randomNumber & 0x101;
-
- uint16_t scrollShakeYOffset = randomNumber >> 8;
- uint16_t scrollShakeXOffset = (randomNumber & 0xFF);
-
- scrollY += scrollShakeYOffset;
- if (scrollX > 0x13C) // 316
- {
- scrollShakeXOffset = -scrollShakeXOffset;
- }
-
- // loc_49FD0: ; CODE XREF: updateScrollOffset+10Ej
- scrollX += scrollShakeXOffset;
- }
-
- // loc_49FD2: ; CODE XREF: updateScrollOffset+F7j
- // ; updateScrollOffset+FEj
- gScrollOffsetX = scrollX;
- gScrollOffsetY = scrollY;
-}
-
-void updateBugTiles(int16_t position) // movefun7 proc near ; DATA XREF: data:163Co
-{
- // 01ED:33DA
- StatefulLevelTile *currentTile = &gCurrentLevelState[position];
- StatefulLevelTile *aboveTile = &gCurrentLevelState[position - kLevelWidth];
- StatefulLevelTile *belowTile = &gCurrentLevelState[position + kLevelWidth];
- StatefulLevelTile *leftTile = &gCurrentLevelState[position - 1];
- StatefulLevelTile *rightTile = &gCurrentLevelState[position + 1];
- StatefulLevelTile *aboveLeftTile = &gCurrentLevelState[position - kLevelWidth - 1];
- StatefulLevelTile *aboveRightTile = &gCurrentLevelState[position - kLevelWidth + 1];
- StatefulLevelTile *belowLeftTile = &gCurrentLevelState[position + kLevelWidth - 1];
- StatefulLevelTile *belowRightTile = &gCurrentLevelState[position + kLevelWidth + 1];
-
- if (currentTile->tile != LevelTileTypeBug)
- {
- return;
- }
-
- // loc_4A045: ; CODE XREF: movefun7+5j
- if ((gFrameCounter & 3) != 0)
- {
- return;
- }
-
- // loc_4A051: ; CODE XREF: movefun7+11j
- int8_t frameNumber = currentTile->state;
- frameNumber++;
- if (frameNumber >= 0xE)
- {
- // 01ED:33F9
- uint8_t value = generateRandomNumber() & 0xFF;
- value &= 0x3F;
- value += 0x20;
- value = -value;
- frameNumber = value;
- }
-
- // loc_4A067: ; CODE XREF: movefun7+1Dj
- currentTile->state = frameNumber;
- if (frameNumber < 0)
- {
- return;
- }
-
- // loc_4A071: ; CODE XREF: movefun7+31j
- if (aboveLeftTile->tile == LevelTileTypeMurphy || aboveTile->tile == LevelTileTypeMurphy || aboveRightTile->tile == LevelTileTypeMurphy || leftTile->tile == LevelTileTypeMurphy || rightTile->tile == LevelTileTypeMurphy || belowLeftTile->tile == LevelTileTypeMurphy || belowTile->tile == LevelTileTypeMurphy || belowRightTile->tile == LevelTileTypeMurphy)
- {
- // loc_4A0AB: ; CODE XREF: movefun7+39j
- // ; movefun7+40j ...
- playBugSound();
- }
-
- // loc_4A0AE: ; CODE XREF: movefun7+6Cj
- Point frameCoordinates = kBugFrameCoordinates[frameNumber];
- drawMovingFrame(frameCoordinates.x, frameCoordinates.y, position);
-}
-
-void updateTerminalTiles(int16_t position) // movefun5 proc near ; DATA XREF: data:1630o
-{
- // 01ED:346F
- StatefulLevelTile *currentTile = &gCurrentLevelState[position];
-
- if (currentTile->tile != LevelTileTypeTerminal)
- {
- return;
- }
-
- // loc_4A0DA: ; CODE XREF: updateTerminalTiles+5j
- int8_t state = currentTile->state;
- state++;
- if (state <= 0)
- {
- currentTile->state = state;
- return;
- }
-
- // loc_4A0EA: ; CODE XREF: updateTerminalTiles+11j
- uint8_t value = generateRandomNumber() & 0xFF;
- value &= gTerminalMaxFramesToNextScroll;
- value = -value;
- currentTile->state = value;
-
- scrollTerminalScreen(position);
-}
-
-/// Updates the random seed using the clock
-void generateRandomSeedFromClock() // getTime proc near ; CODE XREF: start:doesNotHaveCommandLinep
- // ; handleGameUserInput+669p ...
-{
- uint32_t timeInMilliseconds = getTime();
- // In order to keep the same behavior and values, this code will convert
- // the time in milliseconds to the clock count, as described in
- // http://vitaly_filatov.tripod.com/ng/asm/asm_029.1.html
- // If 1 second is 18.2 clock counts, we need to divide the time
- // by 1000 to get the seconds, and then multiply by 18.2.
- //
- uint32_t clockCount = timeInMilliseconds * 18.2 / 1000;
- uint16_t lowValue = (clockCount & 0xFFFF);
- uint16_t highValue = ((clockCount >> 16) & 0xFFFF);
- gRandomGeneratorSeed = highValue ^ lowValue;
-}
-
-/// Generates a random number based on time?
-uint16_t generateRandomNumber() // sub_4A1AE proc near ; CODE XREF: handleGameUserInput+66Cp
- // ; updateScrollOffset+9p ...
-{
- uint16_t someValue = gRandomGeneratorSeed;
- someValue *= 0x5E5; // 1509
- someValue += 0x31; // '1' or 49
- gRandomGeneratorSeed = someValue;
- return someValue / 2;
-}
-
-void updateUserInput() // sub_4A1BF proc near ; CODE XREF: handleGameUserInput+13p
- // ; runMainMenu+BDp ...
-{
- // 01ED:355C
- uint8_t directionKeyWasPressed = 0;
-
- gCurrentUserInput = UserInputNone;
-
- if (isUpButtonPressed())
- {
- // loc_4A1CF: ; CODE XREF: updateUserInput+7j
- gCurrentUserInput = UserInputUp;
- directionKeyWasPressed = 1;
- }
-
- // loc_4A1D6: ; CODE XREF: updateUserInput+Ej
- if (isLeftButtonPressed())
- {
- // loc_4A1E4: ; CODE XREF: updateUserInput+1Cj
- gCurrentUserInput = UserInputLeft;
- directionKeyWasPressed = 1;
- }
-
- // loc_4A1EB: ; CODE XREF: updateUserInput+23j
- if (isDownButtonPressed())
- {
- // loc_4A1F9: ; CODE XREF: updateUserInput+31j
- gCurrentUserInput = UserInputDown;
- directionKeyWasPressed = 1;
- }
-
- // loc_4A200: ; CODE XREF: updateUserInput+38j
- if (isRightButtonPressed())
- {
- // loc_4A20E: ; CODE XREF: updateUserInput+46j
- gCurrentUserInput = UserInputRight;
- directionKeyWasPressed = 1;
- }
-
- // loc_4A215: ; CODE XREF: updateUserInput+4Dj
- if (isActionButtonPressed())
- {
- // loc_4A22A: ; CODE XREF: updateUserInput+5Bj
- // ; updateUserInput+62j
- if (directionKeyWasPressed == 1)
- {
- gCurrentUserInput += kUserInputSpaceAndDirectionOffset;
- }
- else
- {
- // loc_4A236: ; CODE XREF: updateUserInput+6Ej
- gCurrentUserInput = UserInputSpaceOnly;
- }
- }
-}
-
-void removeTiles(LevelTileType tileType) // sub_4A23C proc near ; CODE XREF: handleGameUserInput+111p
- // ; handleGameUserInput+11Dp ...
-{
- // 01ED:35D9
- // Looks like this function goes through every tile and clears those that match the parameter
- for (uint16_t i = 0; i < kLevelSize; ++i)
- {
- // loc_4A242: ; CODE XREF: removeTiles+1Fj
- StatefulLevelTile *tile = &gCurrentLevelState[i];
- if (tile->tile != tileType)
- {
- if (tileType != LevelTileTypeSnikSnak || tile->tile != 0xBB)
- {
- continue;
- }
- }
-
- // loc_4A253: ; CODE XREF: removeTiles+Cj
- tile->state = 0;
- tile->tile = LevelTileTypeSpace;
- }
- forceRestoreOriginalFancyTiles();
- drawFixedLevel();
- convertToEasyTiles();
- gShouldUpdateTotalLevelTime = 0;
- gHasUserCheated = 1;
-}
-
-void findMurphy() // proc near ; CODE XREF: start+344p fetchAndInitializeLevel+22p
-{
- // 01ED:360E
- for (int i = 0; i < kLevelSize; ++i)
- {
- if (gCurrentLevel.tiles[i] == LevelTileTypeMurphy)
- {
- gMurphyLocation = i;
- break;
- }
- }
-
- scrollToMurphy();
-}
-
-void scrollToMurphy() // sub_4A291 proc near ; CODE XREF: handleGameUserInput+686p
-{
- // 01ED:362E
- // Parameters:
- // - si: murphy location * 2
- // - al: murphy location
-
- gMurphyTileX = gMurphyLocation % kLevelWidth; // stores X coord
- gMurphyTileY = gMurphyLocation / kLevelWidth; // stores Y coord
-
- gMurphyPositionX = gMurphyTileX * kTileSize;
- gMurphyPositionY = gMurphyTileY * kTileSize;
- // di = si[0x6155];
- // si = kMurphyStillSpriteCoordinates;
- drawMovingFrame(304, 132, gMurphyLocation);
- updateScrollOffset();
-
- videoLoop();
-}
-
-uint16_t convertToEasyTiles() // sub_4A2E6 proc near ; CODE XREF: start+33Bp runLevel+ADp ...
-{
- // 01ED:3683
- uint16_t numberOfInfotrons = 0;
- uint16_t numberOfSomething = 0; // this is bx, just counts the number of tiles so technically is same as cx at this point… probably a return value but I don't see it used anywhere???
-
- for (int i = 0; i < kLevelSize; ++i)
- {
- // loc_4A2F0: ; CODE XREF: convertToEasyTiles+D1j
- StatefulLevelTile *currentTile = &gCurrentLevelState[i];
- numberOfSomething++;
-
- if (currentTile->tile == 0xF1)
- {
- currentTile->tile = LevelTileTypeExplosion;
- continue; // jmp short loc_4A3B0
- }
-
- // loc_4A2FC: ; CODE XREF: convertToEasyTiles+Ej
- if (gIsGameBusy != 1)
- {
- if (currentTile->tile == LevelTileTypeInfotron)
- {
- // loc_4A33C: ; CODE XREF: convertToEasyTiles+20j
- numberOfInfotrons++;
- continue; // jmp short loc_4A3B0
- }
- }
- // TODO: what are these gIsGameBusy for??
- if (gIsGameBusy == 1 || currentTile->state != 0 || currentTile->tile != LevelTileTypeSnikSnak) // jz short loc_4A34B
- {
- if (gIsGameBusy == 1 || currentTile->state != 0 || currentTile->tile != LevelTileTypeElectron) // jz short loc_4A379
- {
- // loc_4A312: ; CODE XREF: convertToEasyTiles+1Bj
- if ((currentTile->state == 0 && currentTile->tile == LevelTileTypeHorizontalChipLeft) || (currentTile->state == 0 && currentTile->tile == LevelTileTypeHorizontalChipRight) || (currentTile->state == 0 && currentTile->tile == LevelTileTypeHorizontalChipTop) || (currentTile->state == 0 && currentTile->tile == LevelTileTypeHorizontalChipBottom))
- {
- // loc_4A33F: ; CODE XREF: convertToEasyTiles+2Fj
- // ; convertToEasyTiles+34j ...
- currentTile->tile = LevelTileTypeChip; // mov word ptr [si], 5
- currentTile->state = 0;
- continue; // jmp short loc_4A3B0
- }
- if (currentTile->state == 0 && currentTile->tile >= LevelTileTypeHardware2 && currentTile->tile <= LevelTileTypeHardware11)
- {
- // loc_4A345: ; CODE XREF: convertToEasyTiles+48j
- currentTile->tile = LevelTileTypeHardware; // mov word ptr [si], 6
- currentTile->state = 0;
- continue; // jmp short loc_4A3B0
- }
-
- // loc_4A330: ; CODE XREF: convertToEasyTiles+43j
- if (currentTile->state == 0 && currentTile->tile >= LevelTileTypeSportRight && currentTile->tile <= LevelTileTypeSportUp)
- {
- // loc_4A3A7: ; CODE XREF: convertToEasyTiles+52j
- currentTile->tile -= 4; // Converts Sport[Direction] to Port[Direction]
- currentTile->state = 1;
- continue;
- }
-
- // loc_4A33A: ; CODE XREF: convertToEasyTiles+4Dj
- continue;
- }
- }
-
- StatefulLevelTile *leftTile = &gCurrentLevelState[i - 1];
- StatefulLevelTile *aboveTile = &gCurrentLevelState[i - kLevelWidth];
- StatefulLevelTile *rightTile = &gCurrentLevelState[i + 1];
-
- if (currentTile->state != 0 || currentTile->tile != LevelTileTypeElectron) // jz short loc_4A379
- {
- // loc_4A34B: ; CODE XREF: convertToEasyTiles+25j
- if (leftTile->tile == LevelTileTypeSpace && leftTile->state == 0) // cmp word ptr [si-2], 0
- {
- currentTile->state = 1;
- // si[1] = 1; //mov byte ptr [si+1], 1
- continue; // jmp short loc_4A3B0
- }
- // loc_4A357: ; CODE XREF: convertToEasyTiles+69j
- // 0x78 = 120
- if (aboveTile->tile == LevelTileTypeSpace && aboveTile->state == 0) // cmp word ptr [si-78h], 0
- {
- // 01ED:36FA
- // mov word ptr [si-78h], 1011h
- aboveTile->state = 0x10;
- aboveTile->tile = LevelTileTypeSnikSnak;
- // mov word ptr [si], 0FFFFh
- currentTile->state = 0xFF;
- currentTile->tile = 0xFF;
- continue; // jmp short loc_4A3B0
- }
- // loc_4A368: ; CODE XREF: convertToEasyTiles+75j
- if (rightTile->tile == LevelTileTypeSpace && rightTile->state == 0) // cmp word ptr [si+2], 0
- {
- // 01ED:370B
- // mov word ptr [si+2], 2811h
- rightTile->state = 0x28;
- rightTile->tile = LevelTileTypeSnikSnak;
- // mov word ptr [si], 0FFFFh
- currentTile->state = 0xFF;
- currentTile->tile = 0xFF;
- continue; // jmp short loc_4A3B0
- }
-
- continue;
- }
- // loc_4A379: ; CODE XREF: convertToEasyTiles+2Aj
- if (leftTile->tile == LevelTileTypeSpace && leftTile->state == 0) // cmp word ptr [si-2], 0
- {
- currentTile->state = 1; // mov byte ptr [si+1], 1
- continue; // jmp short loc_4A3B0
- }
- // loc_4A385: ; CODE XREF: convertToEasyTiles+97j
- if (aboveTile->tile == LevelTileTypeSpace && aboveTile->state == 0) // cmp word ptr [si-78h], 0
- {
- // mov word ptr [si-78h], 1018h
- aboveTile->state = 0x10;
- aboveTile->tile = LevelTileTypeElectron;
- // mov word ptr [si], 0FFFFh
- currentTile->state = 0xFF;
- currentTile->tile = 0xFF;
- continue; // jmp short loc_4A3B0
- }
- // loc_4A396: ; CODE XREF: convertToEasyTiles+A3j
- if (rightTile->tile == LevelTileTypeSpace && rightTile->state == 0) // cmp word ptr [si+2], 0
- {
- // mov word ptr [si+2], 2818h
- rightTile->state = 0x28;
- rightTile->tile = LevelTileTypeElectron;
- // mov word ptr [si], 0FFFFh
- currentTile->state = 0xFF;
- currentTile->tile = 0xFF;
- continue; // jmp short loc_4A3B0
- }
- }
-
- return numberOfInfotrons;
-}
-
-void resetNumberOfInfotrons(uint16_t numberOfInfotronsFoundInLevel) // sub_4A3BB proc near ; CODE XREF: start+33Ep fetchAndInitializeLevel+17p
-{
- // In the original game, the number of infotrons found in a level is stored in a 2-bytes variable,
- // however, when stored for its use in the game, it's stored in a 1-byte variable.
- //
- uint8_t numberOfInfotrons = (numberOfInfotronsFoundInLevel & 0xFF);
- if (gNumberOfInfoTrons != 0)
- {
- numberOfInfotrons = gNumberOfInfoTrons;
- }
-
- // loc_4A3C6: ; CODE XREF: resetNumberOfInfotrons+5j
- gNumberOfRemainingInfotrons = numberOfInfotrons;
- gTotalNumberOfInfotrons = numberOfInfotrons;
- drawNumberOfRemainingInfotrons();
-}
-
-void debugSkipLevel() // sub_4A3D2 proc near ; CODE XREF: handleGameUserInput+39Ep
- // ; handleGameUserInput+3EBp
-{
- gIsSPDemoAvailableToRun = 0;
- gSelectedOriginalDemoLevelNumber = 0;
- uint8_t wasNotZero = (gHasUserInterruptedDemo != 0);
- gHasUserInterruptedDemo = 0;
- gHasUserCheated = 1;
- if (wasNotZero)
- {
- restartLevelWithoutAddingCurrentGameTimeToPlayer();
- }
-
- restartLevel();
-}
-
-void restartLevel() // sub_4A3E9 proc near ; CODE XREF: handleGameUserInput+14Ep
-{
- if (gHasUserInterruptedDemo == 0)
- {
- addCurrentGameTimeToPlayer();
- }
-
- if (gIsRecordingDemo != 0)
- {
- stopRecordingDemo();
- }
-
- restartLevelWithoutAddingCurrentGameTimeToPlayer();
-}
-
-void restartLevelWithoutAddingCurrentGameTimeToPlayer() // loc_4A3F3: ; CODE XREF: debugSkipLevel+15j
-// ; restartLevel+5j
-{
- gIsMoveScrollModeEnabled = 0;
- gAdditionalScrollOffsetX = 0;
- gAdditionalScrollOffsetY = 0;
- gIsFlashingBackgroundModeEnabled = 0;
- gDebugExtraRenderDelay = 1;
- replaceCurrentPaletteColor(0, (Color){0, 0, 0});
-
- if (gHasUserInterruptedDemo != 0)
- {
- gIsPlayingDemo = 1;
- }
-
- // loc_4A427: ; CODE XREF: restartLevel+37j
- gIsGameBusy = 0;
- fetchAndInitializeLevel();
- gIsGameBusy = 1;
- if (gHasUserInterruptedDemo >= 1)
- {
- gIsPlayingDemo = 0;
- if (gHasUserInterruptedDemo == 0) // WTF? this makes no sense...
- {
- gHasUserInterruptedDemo++;
- }
- }
-
- // loc_4A446: ; CODE XREF: restartLevel+50j
- // ; restartLevel+57j
- gCurrentUserInput = UserInputNone;
- if (gIsPlayingDemo == 0)
- {
- return;
- }
-
- gDemoCurrentInputIndex = word_5A33C;
- gDemoCurrentInputRepeatCounter = 1;
- simulateDemoInput();
-}
-
-void fetchAndInitializeLevel() // sub_4A463 proc near ; CODE XREF: recordDemo:loc_4953Bp
- // ; restartLevel+43p
-{
- readLevels();
- drawFixedLevel();
- drawGamePanel();
- gIsGameBusy = -gIsGameBusy;
- uint16_t numberOfInfotrons = convertToEasyTiles();
- gIsGameBusy = -gIsGameBusy;
- resetNumberOfInfotrons(numberOfInfotrons);
- gIsShowingFancyTiles = 1;
- initializeGameInfo();
- findMurphy();
-}
-
-void updateOrangeDiskTiles(int16_t position) // movefun3 proc near ; DATA XREF: data:161Ao
-{
- // 01ED:3826
- StatefulLevelTile *currentTile = &gCurrentLevelState[position];
- StatefulLevelTile *belowTile = &gCurrentLevelState[position + kLevelWidth];
-
- if (currentTile->tile != LevelTileTypeOrangeDisk)
- {
- return;
- }
-
- // loc_4A491: ; CODE XREF: movefun3+5j
- uint16_t tileValue = ((currentTile->state << 8) | currentTile->tile);
-
- if (tileValue >= 0x3008)
- {
- // loc_4A4D4: ; CODE XREF: movefun3+Fj
- // push si
- uint8_t stateFrame = currentTile->state;
- // bh = 0;
- // al = state;
- // bx *= 2;
- // ;and bx, byte ptr 0Fh
- // db 83h, 0E3h, 0Fh
- stateFrame *= 2;
- stateFrame &= 0xF; // 16 frames?
-
- // mov di, [si+6155h]
- // shl bx, 1
- // add di, [bx+6C95h]
- // mov si, 12F6h
- // mov si, [si]
- uint16_t offset = kFallAnimationGravityOffsets[stateFrame];
-
- uint8_t tileX = (position % kLevelWidth);
- uint8_t tileY = (position / kLevelWidth);
-
- uint16_t dstX = tileX * kTileSize + (offset % 122);
- uint16_t dstY = tileY * kTileSize + (offset / 122);
-
- drawMovingSpriteFrameInLevel(128, 64,
- kTileSize,
- kTileSize + 2,
- dstX, dstY);
-
- uint8_t state = currentTile->state;
- state++;
- uint8_t otherMovingObject = state;
- otherMovingObject &= 7;
- if (otherMovingObject != 0)
- {
- currentTile->state = state;
- return;
- }
-
- // loc_4A516: ; CODE XREF: movefun3+86j
- currentTile->state = 0;
- currentTile->tile = LevelTileTypeSpace;
- belowTile->state = 0;
- belowTile->tile = LevelTileTypeOrangeDisk;
-
- position += kLevelWidth;
-
- // Update tiles
- currentTile = &gCurrentLevelState[position];
- belowTile = &gCurrentLevelState[position + kLevelWidth];
-
- if (belowTile->state == 0 && belowTile->tile == LevelTileTypeSpace)
- {
- currentTile->state = 0x30;
- belowTile->state = 8;
- }
- // loc_4A537: ; CODE XREF: movefun3+A1j
- else if (belowTile->tile != LevelTileTypeExplosion)
- {
- // loc_4A53F: ; CODE XREF: movefun3+B3j
- // 01ED:38DC
- detonateBigExplosion(position);
- }
-
- return;
- }
- else if (tileValue >= 0x2008)
- {
- // loc_4A4B4: ; CODE XREF: movefun3+14j
- if (belowTile->state != 0 || belowTile->tile != LevelTileTypeSpace)
- {
- // loc_4A4C2: ; CODE XREF: movefun3+30j
- uint8_t state = currentTile->state;
- state++;
- if (state == 0x22)
- {
- state = 0x30;
- }
-
- // loc_4A4CF: ; CODE XREF: movefun3+42j
- currentTile->state = state;
- return;
- }
- currentTile->state = 0;
- currentTile->tile = LevelTileTypeOrangeDisk;
- return;
- }
-
- if (belowTile->state == 0 && belowTile->tile == LevelTileTypeSpace)
- {
- // loc_4A4A9: ; CODE XREF: movefun3+1Dj
- currentTile->state = 0x20;
- belowTile->state = 8;
- }
-}
-
-void updateExplosionTiles(int16_t position) // loc_4A543: ; DATA XREF: data:1648o
-{
- // 01ED:38E0
- StatefulLevelTile *currentTile = &gCurrentLevelState[position];
-
- if (currentTile->tile != LevelTileTypeExplosion)
- {
- return;
- }
-
- // loc_4A54B: ; CODE XREF: code:3928j
- if ((gFrameCounter & 3) != 0)
- {
- return;
- }
-
- // loc_4A557: ; CODE XREF: code:3934j
- uint8_t state = currentTile->state;
- if ((state & 0x80) != 0)
- {
- // loc_4A5A0: ; CODE XREF: code:393Ej
- state++;
- if (state != 0x89)
- {
- // loc_4A5B3: ; CODE XREF: code:3985j
- currentTile->state = state;
- state--;
- state &= 0xF;
- // 12e6
- Point frameCoordinates = kInfotronExplosionAnimationFrameCoordinates[state];
-
- drawMovingFrame(frameCoordinates.x,
- frameCoordinates.y,
- position);
- }
- else
- {
- currentTile->state = 0;
- currentTile->tile = LevelTileTypeInfotron;
- gIsExplosionStarted = 0;
- }
- }
- else
- {
- state++;
- currentTile->state = state;
- state--;
-
- // 12d6
- Point frameCoordinates = kRegularExplosionAnimationFrameCoordinates[state];
-
- drawMovingFrame(frameCoordinates.x,
- frameCoordinates.y,
- position);
-
- // loc_4A582: ; CODE XREF: code:396Aj
- if (currentTile->state == 8)
- {
- currentTile->state = 0;
- currentTile->tile = LevelTileTypeSpace;
- gIsExplosionStarted = 0;
- }
- }
-}
-
-void updateExplosionTimers() // sub_4A5E0 proc near ; CODE XREF: runLevel+106p
-{
- // 01ED:397D
- for (int i = 0; i < kLevelSize; ++i)
- {
- // loc_4A5E9: ; CODE XREF: updateExplosionTimers+25j
- int8_t timer = gExplosionTimers[i];
-
- if (timer == 0)
- {
- continue;
- }
-
- if (timer < 0)
- {
- // loc_4A608: ; CODE XREF: updateExplosionTimers+10j
- gExplosionTimers[i] = timer + 1;
-
- if (gExplosionTimers[i] == 0)
- {
- StatefulLevelTile *tile = &gCurrentLevelState[i];
- tile->state = 0xFF;
- tile->tile = LevelTileTypeElectron;
- detonateBigExplosion(i);
- }
- }
- else
- {
- gExplosionTimers[i] = timer - 1;
-
- if (gExplosionTimers[i] == 0)
- {
- detonateBigExplosion(i);
- }
- }
- }
-}
-
-void detonateBigExplosionTile(int16_t position, uint8_t newTile, uint8_t newState, uint8_t newExplosionTimer)
-{
- StatefulLevelTile *currentTile = &gCurrentLevelState[position];
-
- // loc_4A64C: ; CODE XREF: detonateBigExplosion+26j
- uint8_t hasChangedCurrentTile = 0;
-
- if (currentTile->tile == LevelTileTypeOrangeDisk || currentTile->tile == LevelTileTypeYellowDisk || currentTile->tile == LevelTileTypeSnikSnak)
- {
- // loc_4A680: ; CODE XREF: detonateBigExplosion+3Aj
- // ; detonateBigExplosion+3Ej ...
- if (currentTile->tile != LevelTileTypeHardware)
- {
- gExplosionTimers[position] = newExplosionTimer; // mov [bx+23F7h], dh
- }
- }
- else if (currentTile->tile == LevelTileTypeZonk)
- {
- // loc_4A69C: ; CODE XREF: detonateBigExplosion+46j
- // 01ED:3A39
- detonateZonk(position, newState, newTile);
- hasChangedCurrentTile = 1; // to emulate jmp loc_4A6A6
- }
- else if (currentTile->tile == LevelTileTypeInfotron)
- {
- // loc_4A692: ; CODE XREF: detonateBigExplosion+4Aj 01ED:3A2F
- sub_4AA34(position, newState, newTile);
- hasChangedCurrentTile = 1; // to emulate jmp loc_4A6A6
- }
- else if (currentTile->tile == LevelTileTypeElectron)
- {
- newExplosionTimer = -newExplosionTimer; // dh = -dh;
- newState = 0x80;
- newTile = LevelTileTypeExplosion;
- // loc_4A680: ; CODE XREF: detonateBigExplosion+3Aj
- // ; detonateBigExplosion+3Ej ...
- if (currentTile->tile != LevelTileTypeHardware)
- {
- gExplosionTimers[position] = newExplosionTimer; // mov [bx+23F7h], dh
- }
- }
- // loc_4A676: ; CODE XREF: detonateBigExplosion+4Ej
- else if (currentTile->tile == LevelTileTypeMurphy)
- {
- gShouldKillMurphy = 1;
-
- // loc_4A680: ; CODE XREF: detonateBigExplosion+3Aj
- // ; detonateBigExplosion+3Ej ...
- if (currentTile->tile != LevelTileTypeHardware)
- {
- gExplosionTimers[position] = newExplosionTimer; // mov [bx+23F7h], dh
- }
- }
-
- if (hasChangedCurrentTile == 0)
- {
- // loc_4A688: ; CODE XREF: detonateBigExplosion+59j
- // ; detonateBigExplosion+63j
- if (currentTile->tile != LevelTileTypeHardware)
- {
- // mov [si+17BAh], cx
- currentTile->state = newState;
- currentTile->tile = newTile;
- }
- }
-}
-
-// Creates an explossion of 3x3 tiles around a position
-void detonateBigExplosion(int16_t position) // sub_4A61F proc near ; CODE XREF: movefun+271p
- // ; movefun2+20Fp ...
-{
- // 01ED:39BC
- StatefulLevelTile *currentTile = &gCurrentLevelState[position];
-
- if (currentTile->state == 0 && currentTile->tile == LevelTileTypeHardware)
- {
- return;
- }
-
- // These indicate the kind of the explosion created by this tile.
- // Tiles around may create a different explosion if needed (like Electrons create Infotrons).
- //
- uint8_t newState = 0;
- uint8_t newTile = 0;
- uint8_t newExplosionTimer = 0;
-
- // loc_4A627: ; CODE XREF: detonateBigExplosion+5j
- gIsExplosionStarted = 1;
- if (currentTile->tile == LevelTileTypeMurphy)
- {
- gShouldKillMurphy = 1;
- }
-
- // loc_4A639: ; CODE XREF: detonateBigExplosion+12j
- if (currentTile->tile == LevelTileTypeElectron)
- {
- newState = 0x80;
- newTile = LevelTileTypeExplosion;
- newExplosionTimer = -13;
- }
- else
- {
- // loc_4A647: ; CODE XREF: detonateBigExplosion+1Fj
- // cx = 0x1F; // 31
- newState = 0;
- newTile = LevelTileTypeExplosion;
- newExplosionTimer = 13;
- }
-
- detonateBigExplosionTile(position - kLevelWidth - 1, newTile, newState, newExplosionTimer);
- detonateBigExplosionTile(position - kLevelWidth, newTile, newState, newExplosionTimer);
- detonateBigExplosionTile(position - kLevelWidth + 1, newTile, newState, newExplosionTimer);
- detonateBigExplosionTile(position - 1, newTile, newState, newExplosionTimer);
-
- // loc_4A7AB: ; CODE XREF: detonateBigExplosion:loc_4A795j
- // ; detonateBigExplosion+180j ...
- currentTile->state = newState;
- currentTile->tile = newTile;
-
- detonateBigExplosionTile(position + 1, newTile, newState, newExplosionTimer);
- detonateBigExplosionTile(position + kLevelWidth - 1, newTile, newState, newExplosionTimer);
- detonateBigExplosionTile(position + kLevelWidth, newTile, newState, newExplosionTimer);
- detonateBigExplosionTile(position + kLevelWidth + 1, newTile, newState, newExplosionTimer);
-
- // loc_4A90B: ; CODE XREF: detonateBigExplosion:loc_4A8F5j
- // ; detonateBigExplosion+2E0j ...
- playExplosionSound();
- // 01ED:3CAC
-}
-
-void updatePlantedRedDisk() // sub_4A910 proc near ; CODE XREF: runLevel:noFlashing3p
-{
- // 01ED:3CAD
- if (gPlantedRedDiskCountdown <= 1)
- {
- return;
- }
-
- StatefulLevelTile *tile = &gCurrentLevelState[gPlantedRedDiskPosition];
-
- if (tile->state == 0 && tile->tile == LevelTileTypeSpace)
- {
- tile->state = 0;
- tile->tile = LevelTileTypeRedDisk;
- }
-
- // loc_4A932: ; CODE XREF: updatePlantedRedDisk+1Aj
- // si = word_5177E;
- drawMovingFrame(256, 164, gPlantedRedDiskPosition);
- gPlantedRedDiskCountdown++;
- if (gPlantedRedDiskCountdown >= 0x28)
- {
- detonateBigExplosion(gPlantedRedDiskPosition);
- gPlantedRedDiskCountdown = 0;
- }
-}
-
-void addCurrentGameTimeToPlayer() // sub_4A95F proc near ; CODE XREF: runLevel+372p
- // ; restartLevel+7p ...
-{
- uint8_t seconds = gGameSeconds;
- uint8_t minutes = gGameMinutes;
- uint16_t hours = gGameHours;
- if (gIsPlayingDemo != 0)
- {
- return;
- }
-
- if (gIsSPDemoAvailableToRun != 0)
- {
- return;
- }
-
- // loc_4A980: ; CODE XREF: addCurrentGameTimeToPlayer+1Ej
- PlayerEntry *playerEntry = &gPlayerListData[gCurrentPlayerIndex];
- seconds += playerEntry->seconds;
-
- do
- {
- // loc_4A994: ; CODE XREF: addCurrentGameTimeToPlayer+3Ej
- if (seconds < 60)
- {
- break;
- }
- seconds -= 60;
- playerEntry->minutes++;
- } while (1);
-
- // loc_4A99F: ; CODE XREF: addCurrentGameTimeToPlayer+37j
- playerEntry->seconds = seconds;
-
- minutes += playerEntry->minutes;
-
- do
- {
- // loc_4A9A8: ; CODE XREF: addCurrentGameTimeToPlayer+52j
- if (minutes < 60)
- {
- break;
- }
- minutes -= 60;
- playerEntry->hours++;
- } while (1);
-
- // loc_4A9B3: ; CODE XREF: addCurrentGameTimeToPlayer+4Bj
- playerEntry->minutes = minutes;
-
- hours += playerEntry->hours;
-
- if (hours > 0xFF)
- {
- hours = 0xFF;
- }
-
- // loc_4A9C0: ; CODE XREF: addCurrentGameTimeToPlayer+5Dj
- playerEntry->hours = hours;
-}
-
-void detonateZonk(int16_t position, uint8_t state, uint8_t tile) // sub_4A9C4 proc near ; CODE XREF: detonateBigExplosion+81p
- // ; detonateBigExplosion+D8p ...
-{
- // 01ED:3D61
- StatefulLevelTile *currentTile = &gCurrentLevelState[position];
- StatefulLevelTile *belowTile = &gCurrentLevelState[position + kLevelWidth];
-
- uint8_t stateType = currentTile->state & 0xF0;
-
- currentTile->state = state;
- currentTile->tile = tile;
-
- if (stateType == 0x10 || stateType == 0x70)
- {
- // loc_4A9EF: ; CODE XREF: detonateZonk+Aj detonateZonk+Fj
- sub_4AAB4(position - kLevelWidth);
- if (belowTile->state == 0x99 && belowTile->tile == 0x99)
- {
- sub_4AAB4(position + kLevelWidth);
- }
- }
- else if (stateType == 0x20)
- {
- // loc_4AA05: ; CODE XREF: detonateZonk+14j
- sub_4AAB4(position + 1);
- sub_4AAB4(position + kLevelWidth);
- }
- else if (stateType == 0x30)
- {
- // loc_4AA12: ; CODE XREF: detonateZonk+19j
- sub_4AAB4(position - 1);
- sub_4AAB4(position + kLevelWidth);
- }
- else if (stateType == 0x50)
- {
- // loc_4AA1F: ; CODE XREF: detonateZonk+1Ej
- sub_4AAB4(position - 1);
- }
- else if (stateType == 0x60)
- {
- // loc_4AA26: ; CODE XREF: detonateZonk+23j
- sub_4AAB4(position + 1);
- }
- else if (stateType == 0x70)
- {
- // loc_4AA2D: ; CODE XREF: detonateZonk+28j
- sub_4AAB4(position + kLevelWidth);
- }
-}
-
-void sub_4AA34(int16_t position, uint8_t state, uint8_t tile) // proc near ; CODE XREF: detonateBigExplosion+77p
- // ; detonateBigExplosion+CEp ...
-{
- // Parameters:
- // - si: position
- // - cx: state (ch) and tile (cl)
-
- StatefulLevelTile *currentTile = &gCurrentLevelState[position];
- StatefulLevelTile *belowTile = &gCurrentLevelState[position + kLevelWidth];
-
- uint8_t stateType = currentTile->state & 0xF0;
-
- currentTile->state = state;
- currentTile->tile = tile;
-
- if (stateType == 0x10 || stateType == 0x70)
- {
- // loc_4AA5F: ; CODE XREF: sub_4AA34+Aj sub_4AA34+Fj
- sub_4AAB4(position - kLevelWidth);
- if (belowTile->state == 0x99 && belowTile->tile == 0x99)
- {
- sub_4AAB4(position + kLevelWidth);
- }
- }
- else if (stateType == 0x20)
- {
- // loc_4AA75: ; CODE XREF: sub_4AA34+14j
- sub_4AAB4(position + 1);
- if (belowTile->state == 0x99 && belowTile->tile == 0x99)
- {
- sub_4AAB4(position + kLevelWidth);
- }
- }
- else if (stateType == 0x30)
- {
- // loc_4AA8A: ; CODE XREF: sub_4AA34+19j
- sub_4AAB4(position - 1);
- if (belowTile->state == 0x99 && belowTile->tile == 0x99)
- {
- sub_4AAB4(position + kLevelWidth);
- }
- }
- else if (stateType == 0x50)
- {
- // loc_4AA9F: ; CODE XREF: sub_4AA34+1Ej
- sub_4AAB4(position - 1);
- }
- else if (stateType == 0x60)
- {
- // loc_4AAA6: ; CODE XREF: sub_4AA34+23j
- sub_4AAB4(position + 1);
- }
- else if (stateType == 0x70)
- {
- // loc_4AAAD: ; CODE XREF: sub_4AA34+28j
- sub_4AAB4(position + kLevelWidth);
- }
-}
-
-void sub_4AAB4(int16_t position) // proc near ; CODE XREF: detonateZonk+2Ep
- // ; detonateZonk+3Dp ...
-{
- // 01ED:3DD1
- StatefulLevelTile *currentTile = &gCurrentLevelState[position];
-
- if (currentTile->tile == LevelTileTypeExplosion)
- {
- return;
- }
-
- // loc_4AABC: ; CODE XREF: sub_4AAB4+5j
- currentTile->state = 0;
- currentTile->tile = LevelTileTypeSpace;
-
- // si = word_51580;
- uint16_t dstX = (position % kLevelWidth) * kTileSize;
- uint16_t dstY = (position / kLevelWidth) * kTileSize;
-
- drawMovingSpriteFrameInLevel(0, 32, kTileSize, kTileSize, dstX, dstY);
-}
-
-void handleNewPlayerOptionClick() // sub_4AB1B proc near ; CODE XREF: runMainMenu+28Fp
-// ; DATA XREF: data:off_50318o
-{
- // 01ED:3EB8
- if (gIsForcedCheatMode != 0)
- {
- // jnz short loc_4AB4A
- drawTextWithChars6FontWithOpaqueBackgroundIfPossible(168, 127, 6, "PLAYER LIST FULL ");
- return;
- }
-
- int newPlayerIndex = -1;
-
- for (int i = 0; i < kNumberOfPlayers; ++i)
- {
- PlayerEntry currentPlayerEntry = gPlayerListData[i];
-
- // loc_4AB2D: ; CODE XREF: handleNewPlayerOptionClick+2Dj
- if (strcmp(currentPlayerEntry.name, "--------") == 0)
- {
- newPlayerIndex = i;
- break;
- }
- // loc_4AB42: ; CODE XREF: handleNewPlayerOptionClick+14j
- // ; handleNewPlayerOptionClick+19j ...
- }
-
- if (newPlayerIndex == -1)
- {
- // loc_4AB4A: ; CODE XREF: handleNewPlayerOptionClick+5j
- // mov di, 89F7h
- drawTextWithChars6FontWithOpaqueBackgroundIfPossible(168, 127, 6, "PLAYER LIST FULL ");
- return;
- }
-
- // loc_4AB56: ; CODE XREF: handleNewPlayerOptionClick+25j
- gNewPlayerEntryIndex = newPlayerIndex;
-
- char newPlayerName[kPlayerNameLength + 1] = " ";
- gNewPlayerNameLength = 0;
- uint16_t mouseX, mouseY;
- uint16_t mouseButtonStatus;
-
- restoreLastMouseAreaBitmap();
-
- if (supportsRealKeyboard())
- {
- // mov di, 89F7h
- drawTextWithChars6FontWithOpaqueBackgroundIfPossible(168, 127, 4, "YOUR NAME: ");
-
- do
- {
- // loc_4AB7F: ; CODE XREF: handleNewPlayerOptionClick+6Aj
- getMouseStatus(&mouseX, &mouseY, &mouseButtonStatus);
- } while (mouseButtonStatus != 0);
-
- char lastPressedCharacter = '\0';
-
- do
- {
- // noKeyPressed: ; CODE XREF: handleNewPlayerOptionClick+79j
- // ; handleNewPlayerOptionClick+8Aj ...
- videoLoop();
-
- int9handler(0);
- getMouseStatus(&mouseX, &mouseY, &mouseButtonStatus);
- if (mouseButtonStatus != 0)
- {
- break;
- }
- if (isAnyKeyPressed() == 0)
- {
- lastPressedCharacter = '\0';
- continue;
- }
-
- char character = characterForLastKeyPressed();
-
- if (lastPressedCharacter == character)
- {
- continue;
- }
-
- lastPressedCharacter = character;
-
- if (character == 0) // For keys without a valid representation
- {
- continue;
- }
- if (character == '\n') // \n -> enter -> create player
- {
- break;
- }
- if (character == '\b') // backspace -> delete last char
- {
- // loc_4ABCC: ; CODE XREF: handleNewPlayerOptionClick+92j
- if (gNewPlayerNameLength == 0)
- {
- continue;
- }
- gNewPlayerNameLength--;
- newPlayerName[gNewPlayerNameLength] = ' ';
- drawTextWithChars6FontWithOpaqueBackgroundIfPossible(232, 127, 6, newPlayerName);
- continue;
- }
- if (gNewPlayerNameLength >= 8) // when more than 8 chars were entered, ignore the rest?
- {
- continue;
- }
- newPlayerName[gNewPlayerNameLength] = character; // mov [bx+si], al
- gNewPlayerNameLength++;
- drawTextWithChars6FontWithOpaqueBackgroundIfPossible(232, 127, 6, newPlayerName);
- } while (1);
-
- do
- {
- // loc_4ABEB: ; CODE XREF: handleNewPlayerOptionClick+72j
- // ; handleNewPlayerOptionClick+8Ej ...
- getMouseStatus(&mouseX, &mouseY, &mouseButtonStatus);
- } while (mouseButtonStatus != 0);
- }
- else if (supportsVirtualKeyboard())
- {
- char inputBuffer[kPlayerNameLength + 1] = "";
- uint8_t result = inputVirtualKeyboardText("Enter your name", kPlayerNameLength, inputBuffer);
-
- if (result == 0)
- {
- drawTextWithChars6FontWithOpaqueBackgroundIfPossible(168, 127, 8, " ");
- saveLastMouseAreaBitmap();
- drawMouseCursor();
- return;
- }
-
- const char *allowedCharacters = "0123456789QWERTYUIOPASDFGHJKLZXCVBNM -";
-
- // Fill name with spaces, convert it to uppercase, and replace invalid characters with '-'
- size_t numberOfSpaces = kPlayerNameLength - strlen(inputBuffer);
-
- for (size_t idx = 0; idx < kPlayerNameLength; ++idx)
- {
- char inputCharacter = ' ';
-
- if (idx >= numberOfSpaces)
- {
- inputCharacter = toupper(inputBuffer[idx - numberOfSpaces]);
-
- if (strchr(allowedCharacters, inputCharacter) == NULL)
- {
- inputCharacter = '-';
- }
- }
-
- newPlayerName[idx] = inputCharacter;
- }
- }
- else
- {
- do
- {
- // loc_4AB7F: ; CODE XREF: handleNewPlayerOptionClick+6Aj
- getMouseStatus(&mouseX, &mouseY, &mouseButtonStatus);
- } while (mouseButtonStatus != 0);
-
- // Limit the player number value to avoid -Wformat-truncation warning
- snprintf(newPlayerName, sizeof(newPlayerName), "PLAYER%2d", MIN(gNewPlayerEntryIndex + 1, kNumberOfPlayers));
- gNewPlayerNameLength = strlen(newPlayerName);
- }
-
- // Completely empty name: ignore
- if (strcmp(newPlayerName, " ") == 0)
- {
- drawTextWithChars6FontWithOpaqueBackgroundIfPossible(168, 127, 8, " ");
- saveLastMouseAreaBitmap();
- drawMouseCursor();
- return;
- }
-
- // loc_4AC1E: ; CODE XREF: handleNewPlayerOptionClick+E0j
- // ; handleNewPlayerOptionClick+E5j ...
- // Name with all dashes: invalid
- if (strcmp(newPlayerName, "--------") == 0)
- {
- drawTextWithChars6FontWithOpaqueBackgroundIfPossible(168, 127, 6, "INVALID NAME ");
- saveLastMouseAreaBitmap();
- drawMouseCursor();
- return;
- }
-
- // loc_4AC46: ; CODE XREF: handleNewPlayerOptionClick+108j
- // ; handleNewPlayerOptionClick+10Dj ...
-
- // Move spaces at the end of the name to the beginning
- const int kLastNameCharacterIndex = sizeof(newPlayerName) - 2;
- while (newPlayerName[kLastNameCharacterIndex] == ' ')
- {
- // loc_4AC4B: ; CODE XREF: handleNewPlayerOptionClick+14Cj
- for (int i = kLastNameCharacterIndex; i >= 1; --i)
- {
- newPlayerName[i] = newPlayerName[i - 1];
- }
- newPlayerName[0] = ' ';
- }
-
- // loc_4AC69: ; CODE XREF: handleNewPlayerOptionClick+137j
-
- for (int i = 0; i < kNumberOfPlayers; ++i)
- {
- PlayerEntry player = gPlayerListData[i];
- // loc_4AC73: ; CODE XREF: handleNewPlayerOptionClick+18Cj
- if (strcmp(player.name, newPlayerName) == 0)
- {
- drawTextWithChars6FontWithOpaqueBackgroundIfPossible(168, 127, 6, "PLAYER EXISTS ");
- saveLastMouseAreaBitmap();
- drawMouseCursor();
- return;
- }
- // loc_4ACA3: ; CODE XREF: handleNewPlayerOptionClick+15Cj
- // ; handleNewPlayerOptionClick+164j ...
- }
- gCurrentPlayerIndex = gNewPlayerEntryIndex;
- PlayerEntry *newPlayerEntry = &gPlayerListData[gCurrentPlayerIndex];
- memcpy(newPlayerEntry->name, newPlayerName, sizeof(newPlayerName));
-
- drawTextWithChars6FontWithOpaqueBackgroundIfPossible(168, 127, 8, " ");
- savePlayerListData();
- saveHallOfFameData();
- gShouldAutoselectNextLevelToPlay = 1;
- prepareLevelDataForCurrentPlayer();
- drawPlayerList();
- drawLevelList();
- drawRankings();
- saveLastMouseAreaBitmap();
- drawMouseCursor();
-}
-
-void handleDeletePlayerOptionClick() // sub_4AD0E proc near
-{
- if (gIsForcedCheatMode != 0)
- {
- // loc_4AD3C: ; CODE XREF: handleDeletePlayerOptionClick+5j
- drawTextWithChars6FontWithOpaqueBackgroundIfPossible(168, 127, 8, "NO PLAYER SELECTED ");
- return;
- }
-
- PlayerEntry *currentPlayerEntry = &gPlayerListData[gCurrentPlayerIndex];
- // *dword_58477 = currentPlayerEntry; // mov word ptr dword_58477, si
- if (strcmp(currentPlayerEntry->name, "--------") == 0)
- {
- // loc_4AD3C: ; CODE XREF: handleDeletePlayerOptionClick+5j
- drawTextWithChars6FontWithOpaqueBackgroundIfPossible(168, 127, 8, "NO PLAYER SELECTED ");
- return;
- }
-
- // loc_4AD48: ; CODE XREF: handleDeletePlayerOptionClick+1Dj
- // ; handleDeletePlayerOptionClick+22j ...
- char message[24] = "";
- sprintf(message, "DELETE '%s' ??? ", currentPlayerEntry->name);
-
- drawTextWithChars6FontWithOpaqueBackgroundIfPossible(168, 127, 8, message);
-
- uint16_t mouseX, mouseY;
- uint16_t mouseButtonStatus;
-
- do
- {
- // loc_4AD6C: ; CODE XREF: handleDeletePlayerOptionClick+64j
- getMouseStatus(&mouseX, &mouseY, &mouseButtonStatus);
- } while (mouseButtonStatus != 0);
-
- do
- {
- // loc_4AD74: ; CODE XREF: handleDeletePlayerOptionClick+88j
- videoLoop();
- getMouseStatus(&mouseX, &mouseY, &mouseButtonStatus);
- gMouseButtonStatus = mouseButtonStatus;
- gMouseX = mouseX;
- gMouseY = mouseY;
- restoreLastMouseAreaBitmap();
- saveLastMouseAreaBitmap();
- drawMouseCursor();
- } while (gMouseButtonStatus == 0);
-
- ButtonDescriptor okButtonDescriptor = kMainMenuButtonDescriptors[9];
-
- if (gMouseX >= okButtonDescriptor.startX && gMouseY >= okButtonDescriptor.startY && gMouseX <= okButtonDescriptor.endX && gMouseY <= okButtonDescriptor.endY)
- {
- // mov di, word ptr dword_58477 // recover current player entry pointer
- memset(currentPlayerEntry, 0, sizeof(PlayerEntry));
- memset(currentPlayerEntry->name, '-', sizeof(currentPlayerEntry->name) - 1);
- }
-
- // loc_4ADCE: ; CODE XREF: handleDeletePlayerOptionClick+97j
- // ; handleDeletePlayerOptionClick+9Cj ...
- restoreLastMouseAreaBitmap();
- drawTextWithChars6FontWithOpaqueBackgroundIfPossible(168, 127, 8, " ");
- savePlayerListData();
- saveHallOfFameData();
- gShouldAutoselectNextLevelToPlay = 1;
- prepareLevelDataForCurrentPlayer();
- drawPlayerList();
- drawLevelList();
- drawRankings();
-
- do
- {
- // loc_4ADF3: ; CODE XREF: handleDeletePlayerOptionClick+EBj
- getMouseStatus(&mouseX, &mouseY, &mouseButtonStatus);
- } while (mouseButtonStatus != 0);
- saveLastMouseAreaBitmap();
-}
-
-void handleSkipLevelOptionClick() // sub_4ADFF proc near
-{
- // 01ED:419C
- PlayerEntry currentPlayerEntry = gPlayerListData[gCurrentPlayerIndex];
-
- if (strcmp(currentPlayerEntry.name, "--------") == 0)
- {
- drawTextWithChars6FontWithOpaqueBackgroundIfPossible(168, 127, 8, "NO PLAYER SELECTED ");
- return;
- }
-
- // loc_4AE2E: ; CODE XREF: handleSkipLevelOptionClick+12j
- // ; handleSkipLevelOptionClick+17j ...
- int numberOfSkippedLevels = 0;
-
- for (int i = 0; i < kNumberOfLevels; ++i)
- {
- // loc_4AE38: ; CODE XREF: handleSkipLevelOptionClick+40j
- if (currentPlayerEntry.levelState[i] == PlayerLevelStateSkipped)
- {
- numberOfSkippedLevels++;
- }
- // loc_4AE3E: ; CODE XREF: handleSkipLevelOptionClick+3Bj
- }
- if (gIsDebugModeEnabled == 0)
- {
- // loc_4AE4A: ; CODE XREF: handleSkipLevelOptionClick+47j
- if (numberOfSkippedLevels >= 3)
- {
- drawTextWithChars6FontWithOpaqueBackgroundIfPossible(168, 127, 6, "SKIP NOT POSSIBLE ");
- return;
- }
- }
-
- // loc_4AE5B: ; CODE XREF: handleSkipLevelOptionClick+49j
- // ; handleSkipLevelOptionClick+4Ej
- if (gCurrentPlayerLevelData[gCurrentSelectedLevelIndex - 1] != kNotCompletedLevelEntryColor)
- {
- drawTextWithChars6FontWithOpaqueBackgroundIfPossible(168, 127, 4, "COLORBLIND I GUESS ");
- return;
- }
-
- // loc_4AE75: ; CODE XREF: handleSkipLevelOptionClick+68j
- char levelNumber[4] = "000";
- convertNumberTo3DigitStringWithPadding0(gCurrentSelectedLevelIndex, levelNumber);
-
- char message[24];
- sprintf(message, "SKIP LEVEL %s ??? ", levelNumber);
- drawTextWithChars6FontWithOpaqueBackgroundIfPossible(168, 127, 8, message);
-
- uint16_t mouseX, mouseY;
- uint16_t mouseButtonStatus;
-
- do
- {
- // loc_4AE89: ; CODE XREF: handleSkipLevelOptionClick+90j
- getMouseStatus(&mouseX, &mouseY, &mouseButtonStatus);
- } while (mouseButtonStatus != 0);
-
- do
- {
- // loc_4AE91: ; CODE XREF: handleSkipLevelOptionClick+B4j
- videoLoop();
- getMouseStatus(&mouseX, &mouseY, &mouseButtonStatus);
- gMouseButtonStatus = mouseButtonStatus;
- gMouseX = mouseX;
- gMouseY = mouseY;
- restoreLastMouseAreaBitmap();
- saveLastMouseAreaBitmap();
- drawMouseCursor();
- } while (gMouseButtonStatus == 0);
-
- ButtonDescriptor okButtonDescriptor = kMainMenuButtonDescriptors[9];
-
- if (gMouseX >= okButtonDescriptor.startX && gMouseY >= okButtonDescriptor.startY && gMouseX <= okButtonDescriptor.endX && gMouseY <= okButtonDescriptor.endY)
- {
- gCurrentPlayerLevelState = PlayerLevelStateSkipped;
- changePlayerCurrentLevelState(); // 01ED:4275
- gShouldAutoselectNextLevelToPlay = 0;
- prepareLevelDataForCurrentPlayer();
- }
-
- // loc_4AEE9: ; CODE XREF: handleSkipLevelOptionClick+C3j
- // ; handleSkipLevelOptionClick+C8j ...
- restoreLastMouseAreaBitmap();
- drawTextWithChars6FontWithOpaqueBackgroundIfPossible(168, 127, 8, " ");
- drawPlayerList();
- drawLevelList();
- drawRankings();
-
- do
- {
- // loc_4AF00: ; CODE XREF: handleSkipLevelOptionClick+107j
- getMouseStatus(&mouseX, &mouseY, &mouseButtonStatus);
- } while (mouseButtonStatus != 0);
- saveLastMouseAreaBitmap();
-}
-
-void handleStatisticsOptionClick() // sub_4AF0C proc near
-{
- PlayerEntry currentPlayerEntry = gPlayerListData[gCurrentPlayerIndex];
- if (strcmp(currentPlayerEntry.name, "--------") == 0)
- {
- drawTextWithChars6FontWithOpaqueBackgroundIfPossible(168, 127, 8, "NO PLAYER SELECTED ");
- return;
- }
-
- // loc_4AFE3: ; CODE XREF: handleStatisticsOptionClick+58j
- fadeToPalette(gBlackPalette);
-
- uint8_t *screenPixelsBackup = malloc(kFullScreenFramebufferLength);
- memcpy(screenPixelsBackup, gScreenPixels, kFullScreenFramebufferLength);
-
- drawBackBackground();
-
- byte_5091A = 0;
- drawTextWithChars6FontWithTransparentBackgroundIfPossible(80, 20, 15, "SUPAPLEX BY DREAM FACTORY");
- drawTextWithChars6FontWithTransparentBackgroundIfPossible(64, 50, 15, "(C) DIGITAL INTEGRATION LTD 1991");
- drawTextWithChars6FontWithTransparentBackgroundIfPossible(16, 60, 15, "________________________________________________");
- drawTextWithChars6FontWithTransparentBackgroundIfPossible(80, 80, 15, "SUPAPLEX PLAYER STATISTICS");
-
- char currentPlayerText[27] = "";
- sprintf(currentPlayerText, "CURRENT PLAYER : %s", currentPlayerEntry.name);
- drawTextWithChars6FontWithTransparentBackgroundIfPossible(80, 100, 15, currentPlayerText);
-
- if (currentPlayerEntry.nextLevelToPlay == kLastLevelIndex)
- {
- byte_5091A = 1;
- }
-
- // loc_4B046: ; CODE XREF: handleStatisticsOptionClick+133j
- char levelNumberString[4] = "000";
- convertNumberTo3DigitStringWithPadding0(currentPlayerEntry.nextLevelToPlay, levelNumberString);
-
- char currentLevelText[27] = "";
- sprintf(currentLevelText, "CURRENT LEVEL : %s", levelNumberString);
- drawTextWithChars6FontWithTransparentBackgroundIfPossible(80, 110, 15, currentLevelText);
-
- char secondsNumberString[4] = ":00";
- char minutesNumberString[4] = ":00";
- char hoursNumberString[4] = " 0";
-
- convertNumberTo3DigitStringWithPadding0(currentPlayerEntry.seconds, secondsNumberString);
- secondsNumberString[0] = ':';
-
- convertNumberTo3DigitStringWithPadding0(currentPlayerEntry.minutes, minutesNumberString);
- minutesNumberString[0] = ':';
-
- convertNumberTo3DigitPaddedString(currentPlayerEntry.hours, hoursNumberString, 1);
-
- char usedTimeText[27] = "";
- sprintf(usedTimeText, "USED TIME : %s%s%s", hoursNumberString, minutesNumberString, secondsNumberString);
-
- drawTextWithChars6FontWithTransparentBackgroundIfPossible(80, 120, 15, usedTimeText);
-
- uint32_t totalMinutes = currentPlayerEntry.hours * 60 + currentPlayerEntry.minutes;
-
- if (currentPlayerEntry.seconds >= 30)
- {
- totalMinutes++;
- }
-
- // loc_4B0A1: ; CODE XREF: handleStatisticsOptionClick+192j
-
- char averageTimeString[6] = "000.0";
- uint16_t averageMinutesWhole = totalMinutes / currentPlayerEntry.nextLevelToPlay;
- uint16_t averageMinutesFraction = (totalMinutes % currentPlayerEntry.nextLevelToPlay);
- averageMinutesFraction = averageMinutesFraction * 10 / currentPlayerEntry.nextLevelToPlay;
- convertNumberTo3DigitStringWithPadding0(averageMinutesFraction, &averageTimeString[2]);
-
- if (averageMinutesWhole == 0)
- {
- byte_5091A = 2;
- }
-
- // loc_4B0C2: ; CODE XREF: handleStatisticsOptionClick+1AFj
- averageTimeString[3] = '.';
-
- convertNumberTo3DigitPaddedString(averageMinutesWhole, averageTimeString, 1);
- if (byte_5091A == 1)
- {
- drawTextWithChars6FontWithTransparentBackgroundIfPossible(24, 140, 15, "YOU'VE COMPLETED ALL LEVELS! CONGRATULATIONS!!!");
- }
- // loc_4B0E2: ; CODE XREF: handleStatisticsOptionClick+1C7j
- else if (byte_5091A == 2)
- {
- drawTextWithChars6FontWithTransparentBackgroundIfPossible(40, 140, 15, "STILL UNDER ONE MINUTE (KEEP IT UP...)");
- }
- // loc_4B0F6: ; CODE XREF: handleStatisticsOptionClick+1DBj
- else
- {
- char averageTimeMessage[44] = "";
- sprintf(averageTimeMessage, "AVERAGE TIME USED PER LEVEL %s MINUTES", averageTimeString);
- drawTextWithChars6FontWithTransparentBackgroundIfPossible(32, 140, 15, averageTimeMessage);
- }
- // loc_4B105: ; CODE XREF: handleStatisticsOptionClick+1D4j
- // ; handleStatisticsOptionClick+1E8j
- fadeToPalette(gInformationScreenPalette);
- waitForKeyMouseOrJoystick();
- fadeToPalette(gBlackPalette);
- memcpy(gScreenPixels, screenPixelsBackup, kFullScreenFramebufferLength);
- fadeToPalette(gGamePalette);
-
- free(screenPixelsBackup);
-}
-
-void handleGfxTutorOptionClick() // sub_4B149 proc near
-{
- drawGfxTutorBackground(gScrollDestinationScreenBitmapData);
- scrollRightToNewScreen();
- waitForKeyMouseOrJoystick();
- scrollLeftToMainMenu();
- drawMenuTitleAndDemoLevelResult();
-}
-
-void handleDemoOptionClick() // sub_4B159 proc near ; CODE XREF: runMainMenu+6Fp
-{
- // 01ED:44F6
- if (readDemoFiles() == 0)
- {
- return;
- }
-
- // loc_4B163: ; CODE XREF: handleDemoOptionClick+5j
- gShouldLeaveMainMenu = 1;
- gIsPlayingDemo = 1;
-
- uint8_t numberOfDemos = 0;
-
- uint8_t idx = 0;
- do
- {
- // loc_4B17A: ; CODE XREF: handleDemoOptionClick+2Dj
- if (gDemos.demoFirstIndices[idx] == 0xFFFF)
- {
- break;
- }
- idx++;
- numberOfDemos++;
- } while (1);
- // 01ED:4525
-
- // loc_4B188: ; CODE XREF: handleDemoOptionClick+2Aj
- // This picks a random demo
- generateRandomSeedFromClock();
- uint16_t demoIndex = generateRandomNumber() % numberOfDemos;
- uint16_t demoFirstIndex = gDemos.demoFirstIndices[demoIndex];
-
- // This only happens if there are no demos...
- if (demoFirstIndex == 0xFFFF)
- {
- gShouldLeaveMainMenu = 0;
- gIsPlayingDemo = 0;
- }
-
- // loc_4B1AE: ; CODE XREF: handleDemoOptionClick+48j
- uint8_t demoLevelNumber = gDemos.demoData[demoFirstIndex];
- uint8_t finalLevelNumber = demoIndex;
-
- gSelectedOriginalDemoIndex = demoIndex;
- gSelectedOriginalDemoLevelNumber = 0;
-
- // This checks if the level number has its MSB to 0 and is a valid level number (1-111) for the original DEMO format
- if (demoLevelNumber <= 0x6F // 111
- && demoLevelNumber != 0)
- {
- gSelectedOriginalDemoLevelNumber = (gSelectedOriginalDemoLevelNumber & 0xFF00) | demoLevelNumber; // mov byte ptr gSelectedOriginalDemoLevelNumber, al
- finalLevelNumber = demoLevelNumber;
- }
-
- // loc_4B1CF: ; CODE XREF: handleDemoOptionClick+6Bj
- // ; handleDemoOptionClick+6Fj
- gRandomGeneratorSeed = gDemoRandomSeeds[demoIndex];
- gDemoIndexOrDemoLevelNumber = finalLevelNumber;
-
- demoFirstIndex++; // To skip the level number
- gDemoCurrentInputIndex = demoFirstIndex;
- word_5A33C = demoFirstIndex;
- gDemoCurrentInput = UserInputNone;
- gDemoCurrentInputRepeatCounter = 1;
-}
-
-void playDemo(uint16_t demoIndex) // demoSomething proc near ; CODE XREF: start+3BAp
- // ; runMainMenu+12Ep ...
-{
- readDemoFiles();
-
- gRandomGeneratorSeed = gDemoRandomSeeds[demoIndex];
- gShouldLeaveMainMenu = 1;
- gIsPlayingDemo = 1;
-
- uint16_t demoFirstIndex = gDemos.demoFirstIndices[demoIndex];
- if (demoFirstIndex == 0xFFFF)
- {
- gShouldLeaveMainMenu = 0;
- gIsPlayingDemo = 0;
- }
-
- // loc_4B22F: ; CODE XREF: playDemo+30j
- gSelectedOriginalDemoLevelNumber = 0;
-
- uint8_t demoLevelNumber = gDemos.demoData[demoFirstIndex];
- uint8_t finalLevelNumber = demoIndex;
-
- if (demoLevelNumber <= kNumberOfLevels // 111
- && demoLevelNumber != 0)
- {
- finalLevelNumber = demoLevelNumber;
- gSelectedOriginalDemoLevelNumber = (gSelectedOriginalDemoLevelNumber & 0xFF00) | finalLevelNumber; // mov byte ptr gSelectedOriginalDemoLevelNumber, al
- }
-
- // loc_4B248: ; CODE XREF: playDemo+4Bj
- // ; playDemo+4Fj
- gDemoIndexOrDemoLevelNumber = finalLevelNumber;
-
- demoFirstIndex++; // To skip the level number
- gDemoCurrentInputIndex = demoFirstIndex;
- word_5A33C = demoFirstIndex;
- gDemoCurrentInput = UserInputNone;
- gDemoCurrentInputRepeatCounter = 1;
-}
-
-void handleRankingListScrollUp() // loc_4B262
-{
- gRankingListButtonPressed = 1;
- gRankingListDownButtonPressed = 0;
- gRankingListUpButtonPressed = 1;
-
- if (gFrameCounter - gRankingListThrottleCurrentCounter < gRankingListThrottleNextCounter)
- {
- return;
- }
-
- // loc_4B27F: ; CODE XREF: code:465Cj
- restoreLastMouseAreaBitmap();
- gRankingListThrottleNextCounter = gFrameCounter;
- if (gRankingListThrottleCurrentCounter > 1)
- {
- gRankingListThrottleCurrentCounter--;
- }
-
- // loc_4B293: ; CODE XREF: code:466Dj
- if (gIsForcedCheatMode == 0 && byte_58D46 > 0)
- {
- byte_58D46--;
- }
-
- // loc_4B2A5: ; CODE XREF: code:4678j code:467Fj
- drawRankings();
- saveLastMouseAreaBitmap();
- drawMouseCursor();
-}
-
-void handleRankingListScrollDown() // loc_4B2AF
-{
- gRankingListButtonPressed = 1;
- gRankingListDownButtonPressed = 1;
- gRankingListUpButtonPressed = 0;
-
- if (gFrameCounter - gRankingListThrottleCurrentCounter < gRankingListThrottleNextCounter)
- {
- return;
- }
-
- // loc_4B2CC: ; CODE XREF: code:46A9j
- restoreLastMouseAreaBitmap();
- gRankingListThrottleNextCounter = gFrameCounter;
- if (gRankingListThrottleCurrentCounter > 1)
- {
- gRankingListThrottleCurrentCounter--;
- }
-
- // loc_4B2E0: ; CODE XREF: code:46BAj
- if (gIsForcedCheatMode == 0 && byte_58D46 < kNumberOfPlayers - 1)
- {
- byte_58D46++;
- }
-
- // loc_4B2F2: ; CODE XREF: code:46C5j code:46CCj
- drawRankings();
- saveLastMouseAreaBitmap();
- drawMouseCursor();
-}
-
-void showCongratulationsScreen() // sub_4B2FC proc near ; CODE XREF: handleOkButtonClick+56p
-{
- fadeToPalette(gBlackPalette);
-
- uint8_t *screenPixelsBackup = malloc(kFullScreenFramebufferLength);
- memcpy(screenPixelsBackup, gScreenPixels, kFullScreenFramebufferLength);
-
- drawBackBackground();
- drawTextWithChars6FontWithTransparentBackgroundIfPossible(120, 30, 15, "CONGRATULATIONS");
- drawTextWithChars6FontWithTransparentBackgroundIfPossible(24, 70, 15, "YOU HAVE COMPLETED ALL 111 LEVELS OF SUPAPLEX");
- drawTextWithChars6FontWithTransparentBackgroundIfPossible(64, 85, 15, "YOUR BRAIN IS IN FANTASTIC SHAPE");
- drawTextWithChars6FontWithTransparentBackgroundIfPossible(40, 100, 15, "NOT MANY PEOPLE ARE ABLE TO MANAGE THIS");
- fadeToPalette(gInformationScreenPalette);
- waitForKeyMouseOrJoystick();
- fadeToPalette(gBlackPalette);
- memcpy(gScreenPixels, screenPixelsBackup, kFullScreenFramebufferLength);
-
- fadeToPalette(gGamePalette); // 6015h
-
- free(screenPixelsBackup);
-}
-
-void handleOkButtonClick() // sub_4B375 proc near ; CODE XREF: runMainMenu+11Ep
-{
- // 01ED:4712
- PlayerEntry currentPlayerEntry = gPlayerListData[gCurrentPlayerIndex];
-
- if (strcmp(currentPlayerEntry.name, "--------") == 0)
- {
- drawTextWithChars6FontWithOpaqueBackgroundIfPossible(168, 127, 8, "NO PLAYER SELECTED ");
- return;
- }
-
- // loc_4B3A4: ; CODE XREF: handleOkButtonClick+12j
- // ; handleOkButtonClick+17j ...
- if (gCurrentSelectedLevelIndex == kLastLevelIndex)
- {
- // loc_4B3B4: ; CODE XREF: handleOkButtonClick+3Cj
- uint8_t numberOfCompletedLevels = 0;
-
- for (int i = 0; i < kNumberOfLevels; ++i)
- {
- // loc_4B3BC: ; CODE XREF: handleOkButtonClick+4Fj
- if (currentPlayerEntry.levelState[i] == PlayerLevelStateCompleted)
- {
- numberOfCompletedLevels++;
- }
- // loc_4B3C3: ; CODE XREF: handleOkButtonClick+4Aj
- }
- if (numberOfCompletedLevels == kNumberOfLevels)
- {
- showCongratulationsScreen();
- return;
- }
- else
- {
- // loc_4B3CF: ; CODE XREF: handleOkButtonClick+54j
- drawTextWithChars6FontWithOpaqueBackgroundIfPossible(168, 127, 2, "COLORBLIND I GUESS ");
- return;
- }
- }
- else if (gCurrentSelectedLevelIndex > kNumberOfLevels)
- {
- return;
- }
-
- // loc_4B3DB: ; CODE XREF: handleOkButtonClick+35j
- uint8_t currentLevelColor = gCurrentPlayerLevelData[gCurrentSelectedLevelIndex - 1];
-
- if (currentLevelColor == kBlockedLevelEntryColor)
- {
- // loc_4B404: ; CODE XREF: handleOkButtonClick+70j
- drawTextWithChars6FontWithOpaqueBackgroundIfPossible(168, 127, 8, "COLORBLIND I GUESS ");
- return;
- }
- gShouldLeaveMainMenu = 1;
- gIsPlayingDemo = 0;
-
- if (currentLevelColor == kCompletedLevelEntryColor)
- {
- gShouldUpdateTotalLevelTime = 0;
- }
- else
- {
- // loc_4B3FD: ; CODE XREF: handleOkButtonClick+7Fj
- gShouldUpdateTotalLevelTime = 1;
- }
-
- // loc_4B40F: ; CODE XREF: handleOkButtonClick+86j
- // ; handleOkButtonClick+8Dj
- prepareDemoRecordingFilename(); // 01ED:47AC
- convertLevelNumberTo3DigitStringWithPadding0(gCurrentSelectedLevelIndex); // 01ED:47B2
-}
-
-void throttledRotateLevelSet(uint8_t descending) // sub_4B419 proc near
-{
- // 01ED:47B6
- // loc_4B433: ; CODE XREF: sub_4B419+15j
- if (gFrameCounter - gLevelSetRotationThrottleCurrentCounter < gLevelSetRotationThrottleNextCounter)
- {
- return;
- }
-
- // loc_4B443: ; CODE XREF: sub_4B419+25j
- gLevelSetRotationThrottleNextCounter = gFrameCounter;
- if (gLevelSetRotationThrottleCurrentCounter > 1)
- {
- gLevelSetRotationThrottleCurrentCounter--;
- }
-
- rotateLevelSet(descending);
-}
-
-void rotateLevelSet(uint8_t descending) // sub_4B419 proc near
-{
- FILE *file = NULL;
- char currentSuffix[3] = "AT";
-
- do
- {
- // loc_4B454: ; CODE XREF: sub_4B419+35j
- // ; sub_4B419+9Aj
- strcpy(currentSuffix, &gLevelsDatFilename[8]);
-
- if (descending)
- {
- // loc_4B482: ; CODE XREF: sub_4B419+46j
- if (strcmp(currentSuffix, "AT") == 0) // "AT"
- {
- strcpy(currentSuffix, "99");
- }
- // loc_4B48C: ; CODE XREF: sub_4B419+6Cj
- else if (strcmp(currentSuffix, "00") == 0) // "00"
- {
- strcpy(currentSuffix, "AT");
- }
- else
- {
- // loc_4B496: ; CODE XREF: sub_4B419+76j
- currentSuffix[1]--;
- if (currentSuffix[1] < '0' && currentSuffix[0] > '0')
- {
- currentSuffix[1] = '9'; // '9'
- currentSuffix[0]--;
- }
- }
- }
- else
- {
- if (strcmp(currentSuffix, "AT") == 0) // "AT"
- {
- strcpy(currentSuffix, "00");
- }
- // loc_4B46B: ; CODE XREF: sub_4B419+4Bj
- else if (strcmp(currentSuffix, "99") == 0) // "99"
- {
- strcpy(currentSuffix, "AT");
- }
- else
- {
- // loc_4B475: ; CODE XREF: sub_4B419+55j
- currentSuffix[1]++;
- if (currentSuffix[1] > '9' && currentSuffix[0] < '9')
- {
- currentSuffix[1] = '0'; // '0'
- currentSuffix[0]++;
- }
- }
- }
-
- // loc_4B4A3: ; CODE XREF: sub_4B419+50j
- // ; sub_4B419+5Aj ...
- strcpy(&gLevelsDatFilename[8], currentSuffix);
-
- file = openReadonlyFile(gLevelsDatFilename, "rb");
- if (file == NULL)
- {
- if (errno != ENOENT)
- {
- exitWithError("Error opening %s\n", gLevelsDatFilename);
- }
- }
- } while (file == NULL);
-
- // loc_4B4B8: ; CODE XREF: sub_4B419+95j
- if (fclose(file) != 0)
- {
- exitWithError("Error closing %s\n", gLevelsDatFilename);
- }
-
- if (strcmp(currentSuffix, "AT") == 0)
- {
- strcpy(currentSuffix, "ST");
- }
-
- // loc_4B4D3: ; CODE XREF: sub_4B419+B6j
- strcpy(&gLevelLstFilename[7], currentSuffix);
- strcpy(&gPlayerLstFilename[8], currentSuffix);
- strcpy(&gHallfameLstFilename[0xA], currentSuffix);
-
- if (strcmp(currentSuffix, "ST") == 0)
- {
- strcpy(currentSuffix, "IN");
- }
-
- // loc_4B4E4: ; CODE XREF: sub_4B419+C6j
- strcpy(&gDemo0BinFilename[7], currentSuffix);
-
- if (strcmp(currentSuffix, "IN") == 0)
- {
- strcpy(currentSuffix, "AV");
- }
-
- // loc_4B4EF: ; CODE XREF: sub_4B419+D1j
- if (gShouldAlwaysWriteSavegameSav == 0)
- {
- strcpy(&gSavegameSavFilename[0xA], currentSuffix);
- }
-
- // loc_4B504: ; CODE XREF: sub_4B419+E6j
- readLevelsLst();
- readDemoFiles();
-
- // 01ED:48B2
- if (gIsForcedCheatMode != 0)
- {
- PlayerEntry *entry = &gPlayerListData[0];
- memset(entry->levelState, PlayerLevelStateSkipped, sizeof(entry->levelState));
- }
- else
- {
- // loc_4B52A: ; CODE XREF: sub_4B419+101j
- for (int i = 0; i < kNumberOfPlayers; ++i)
- {
- PlayerEntry *entry = &gPlayerListData[i];
- // loc_4B531: ; CODE XREF: sub_4B419+129j
- memset(entry, 0, sizeof(PlayerEntry));
- strcpy(entry->name, "--------");
- }
-
- for (int i = 0; i < kNumberOfHallOfFameEntries; ++i)
- {
- HallOfFameEntry *entry = &gHallOfFameData[i];
-
- // loc_4B54B: ; CODE XREF: sub_4B419+143j
- memset(entry, 0, sizeof(HallOfFameEntry));
- strcpy(entry->playerName, " ");
- }
-
- readHallfameLst();
- readPlayersLst();
- }
-
- updateMenuAfterLevelSetChanged();
-}
-
-void updateMenuAfterLevelSetChanged() // loc_4B565: ; CODE XREF: sub_4B419+10Fj
-{
- // loc_4B4C6: ; CODE XREF: sub_4B419+A8j
- char message[] = " LEVEL SET ?? ";
- char currentSuffix[3] = "AT";
- strcpy(currentSuffix, &gLevelsDatFilename[8]);
-
- memcpy(&message[0xF], currentSuffix, 2);
-
- // loc_4B4F9: ; CODE XREF: sub_4B419+DBj
- if (strcmp(currentSuffix, "AT") == 0)
- {
- strcpy(message, " SUPAPLEX LEVEL SET ");
- }
-
- drawTextWithChars6FontWithOpaqueBackgroundIfPossible(168, 127, 4, message);
-
- gShouldAutoselectNextLevelToPlay = 1;
- prepareLevelDataForCurrentPlayer();
- drawPlayerList();
- drawLevelList();
- drawHallOfFame();
- drawRankings();
- restoreLastMouseAreaBitmap();
- saveLastMouseAreaBitmap();
- drawMouseCursor();
-}
-
-void handleFloppyDiskButtonClick()
-{
- // Pressing the shift key will show the level sets in descending order
- throttledRotateLevelSet(gIsRightShiftPressed || gIsLeftShiftPressed);
-}
-
-void handlePlayerListScrollDown() // sub_4B671 proc near
-{
- gPlayerListButtonPressed = 1;
- gPlayerListDownButtonPressed = 1;
- gPlayerListUpButtonPressed = 0;
-
- if (gFrameCounter - gPlayerListThrottleCurrentCounter < gPlayerListThrottleNextCounter)
- {
- return;
- }
-
- // loc_4B68E: ; CODE XREF: handlePlayerListScrollDown+1Aj
- gPlayerListThrottleNextCounter = gFrameCounter;
- if (gPlayerListThrottleCurrentCounter > 1)
- {
- gPlayerListThrottleCurrentCounter--;
- }
-
- // loc_4B69F: ; CODE XREF: handlePlayerListScrollDown+28j
- if (gIsForcedCheatMode == 0 && gCurrentPlayerIndex < kNumberOfPlayers - 1)
- {
- gCurrentPlayerIndex++;
- }
-
- // loc_4B6B1: ; CODE XREF: handlePlayerListScrollDown+33j
- // ; handlePlayerListScrollDown+3Aj
- restoreLastMouseAreaBitmap();
- gShouldAutoselectNextLevelToPlay = 1;
- prepareLevelDataForCurrentPlayer();
- drawPlayerList();
- drawLevelList();
- saveLastMouseAreaBitmap();
- drawMouseCursor();
-}
-
-void handlePlayerListScrollUp() // sub_4B6C9 proc near
-{
- gPlayerListButtonPressed = 1;
- gPlayerListDownButtonPressed = 0;
- gPlayerListUpButtonPressed = 1;
-
- if (gFrameCounter - gPlayerListThrottleCurrentCounter < gPlayerListThrottleNextCounter)
- {
- return;
- }
-
- // loc_4B6E6: ; CODE XREF: handlePlayerListScrollUp+1Aj
- gPlayerListThrottleNextCounter = gFrameCounter;
- if (gPlayerListThrottleCurrentCounter > 1)
- {
- gPlayerListThrottleCurrentCounter--;
- }
-
- // loc_4B6F7: ; CODE XREF: handlePlayerListScrollUp+28j
- if (gIsForcedCheatMode == 0 && gCurrentPlayerIndex > 0)
- {
- gCurrentPlayerIndex--;
- }
-
- // loc_4B709: ; CODE XREF: handlePlayerListScrollUp+33j
- // ; handlePlayerListScrollUp+3Aj
- restoreLastMouseAreaBitmap(); // Clears mouse trail
- gShouldAutoselectNextLevelToPlay = 1;
- prepareLevelDataForCurrentPlayer();
- drawPlayerList();
- drawLevelList();
- saveLastMouseAreaBitmap();
- drawMouseCursor();
-}
-
-void handlePlayerListClick() // sub_4B721 proc near
-{
- byte_58D46 = byte_58D47;
- drawRankings();
-}
-
-void handleLevelListScrollDown() // sub_4B72B proc near
-{
- gLevelListButtonPressed = 1;
- gLevelListDownButtonPressed = 1;
- gLevelListUpButtonPressed = 0;
-
- if (gFrameCounter - gLevelListThrottleCurrentCounter < gLevelListThrottleNextCounter)
- {
- return;
- }
-
- // loc_4B748: ; CODE XREF: handleLevelListScrollDown+1Aj
- gLevelListThrottleNextCounter = gFrameCounter;
- if (gLevelListThrottleCurrentCounter > 1)
- {
- gLevelListThrottleCurrentCounter--;
- }
-
- // loc_4B759: ; CODE XREF: handleLevelListScrollDown+28j
- if (gCurrentSelectedLevelIndex >= 113)
- {
- return;
- }
- gCurrentSelectedLevelIndex++;
- restoreLastMouseAreaBitmap();
- drawLevelList();
- saveLastMouseAreaBitmap();
- drawMouseCursor();
-}
-
-void handleLevelListScrollUp() // sub_4B771 proc near
-{
- gLevelListButtonPressed = 1;
- gLevelListDownButtonPressed = 0;
- gLevelListUpButtonPressed = 1;
-
- if (gFrameCounter - gLevelListThrottleCurrentCounter < gLevelListThrottleNextCounter)
- {
- return;
- }
-
- // loc_4B78E: ; CODE XREF: handleLevelListScrollUp+1Aj
- gLevelListThrottleNextCounter = gFrameCounter;
- if (gLevelListThrottleCurrentCounter > 1)
- {
- gLevelListThrottleCurrentCounter--;
- }
-
- // loc_4B79F: ; CODE XREF: handleLevelListScrollUp+28j
- if (gCurrentSelectedLevelIndex <= 1)
- {
- return;
- }
- gCurrentSelectedLevelIndex--;
- restoreLastMouseAreaBitmap();
- drawLevelList();
- saveLastMouseAreaBitmap();
- drawMouseCursor();
- // locret_4B7B6: ; CODE XREF: handleLevelListScrollUp+33j
-}
-
-void handleLevelCreditsClick() // sub_4B7B7 proc near
-{
- fadeToPalette(gBlackPalette);
-
- uint8_t *screenPixelsBackup = malloc(kFullScreenFramebufferLength);
- memcpy(screenPixelsBackup, gScreenPixels, kFullScreenFramebufferLength);
-
- drawBackBackground();
-
- drawTextWithChars6FontWithTransparentBackgroundIfPossible(80, 10, 15, "SUPAPLEX BY DREAM FACTORY");
- drawTextWithChars6FontWithTransparentBackgroundIfPossible(56, 40, 15, "ORIGINAL DESIGN BY PHILIP JESPERSEN");
- drawTextWithChars6FontWithTransparentBackgroundIfPossible(88, 50, 15, "AND MICHAEL STOPP");
- drawTextWithChars6FontWithTransparentBackgroundIfPossible(56, 90, 15, "NEARLY ALL LEVELS BY MICHEAL STOPP");
- drawTextWithChars6FontWithTransparentBackgroundIfPossible(64, 100, 15, "A FEW LEVELS BY PHILIP JESPERSEN");
- drawTextWithChars6FontWithTransparentBackgroundIfPossible(56, 110, 15, "HARDLY ANY LEVELS BY BARBARA STOPP");
- drawTextWithChars6FontWithTransparentBackgroundIfPossible(64, 170, 15, "NOTE: PRESS ENTER TO REMOVE PANEL");
- drawTextWithChars6FontWithTransparentBackgroundIfPossible(64, 190, 15, "(C) DIGITAL INTEGRATION LTD 1991");
- fadeToPalette(gInformationScreenPalette);
- waitForKeyMouseOrJoystick();
- fadeToPalette(gBlackPalette);
- memcpy(gScreenPixels, screenPixelsBackup, kFullScreenFramebufferLength);
- fadeToPalette(gGamePalette);
-
- free(screenPixelsBackup);
-}
-
-void drawTextWithChars6FontWithOpaqueBackgroundIfPossible(size_t destX, size_t destY, uint8_t color, const char *text) // sub_4BA5F proc near ; CODE XREF: handleNewPlayerOptionClick+37p
- // ; handleNewPlayerOptionClick+4Ap ...
-{
- // Parameters:
- // - di is the destination surface
- // - si is the text to be rendered
- // - ah is the color index in the current palette
-
- // Address: 01ED:4DFC
- if (gIsGameBusy == 1)
- {
- return;
- }
-
- drawTextWithChars6FontWithOpaqueBackground(destX, destY, color, text);
-}
-
-void drawTextWithChars6FontWithTransparentBackgroundIfPossible(size_t destX, size_t destY, uint8_t color, const char *text) // sub_4BDF0 proc near ; CODE XREF: recoverFilesFromFloppyDisk+2Ap
- // ; handleStatisticsOptionClick+EDp ...
-{
- if (gIsGameBusy == 1)
- {
- return;
- }
-
- drawTextWithChars6FontWithTransparentBackground(destX, destY, color, text);
-}
-
-void convertLevelNumberTo3DigitStringWithPadding0(uint8_t number) // sub_4BF4A proc near ; CODE XREF: start+3F7p handleGameUserInput+398p ...
-{
- convertNumberTo3DigitStringWithPadding0(number, &gSPDemoFileName[3]);
-}
-
-void convertNumberTo3DigitStringWithPadding0(uint8_t number, char numberString[3]) // proc near ; CODE XREF: handleSkipLevelOptionClick+7Cp
- // ; handleStatisticsOptionClick+13Dp ...
-{
- convertNumberTo3DigitPaddedString(number, numberString, 0);
-}
-
-void convertNumberTo3DigitPaddedString(uint8_t number, char numberString[3], char useSpacesForPadding) // sub_4BF4F proc near ; CODE XREF: handleStatisticsOptionClick+16Fp
- // ; handleStatisticsOptionClick+1BFp ...
-{
- // This function converts a number to a 3-digit string, so basically 123 to "123".
- // It also adds padding to the left, so 7 is converted to "007", with the option
- // to turn 0's in padding into spaces: 7 to " 7"
- // Parameters in the original implementation:
- // - al: number to convert
- // - ah: can be ' ' (space) or anything else. When it's ' ', the padding will be done with spaces, otherwise 0's will be used.
- // - si: the string to write the result
-
- numberString[0] = (number / 100) + '0';
- numberString[1] = ((number % 100) / 10) + '0';
- numberString[2] = (number % 10) + '0';
-
- if (useSpacesForPadding)
- {
- if (numberString[0] == '0')
- {
- numberString[0] = ' ';
-
- if (numberString[1] == '0')
- {
- numberString[1] = ' ';
- }
- }
- }
-}
-
-void prepareRankingTextEntries() // sub_4BF8D proc near ; CODE XREF: drawRankingsp
-{
- // 01ED:532A
- if (gIsForcedCheatMode != 0)
- {
- return;
- }
-
- typedef struct
- {
- uint8_t playerIndex;
- uint8_t nextLevelToPlay;
- uint8_t hours;
- uint8_t minutes;
- uint8_t seconds;
- } RankingEntry;
-
- RankingEntry rankingEntries[kNumberOfPlayers];
-
- for (int i = 0; i < 20; ++i)
- {
- // loc_4BFA2: ; CODE XREF: prepareRankingTextEntries+38j
- RankingEntry *rankingEntry = &rankingEntries[i];
- PlayerEntry *player = &gPlayerListData[i];
-
- rankingEntry->playerIndex = i;
- rankingEntry->nextLevelToPlay = player->nextLevelToPlay;
- rankingEntry->hours = player->hours;
- rankingEntry->minutes = player->minutes;
- rankingEntry->seconds = player->seconds;
- }
-
- // loc_4BFC7: ; CODE XREF: prepareRankingTextEntries+B4j
- uint8_t numberOfChanges = 0;
-
- do
- {
- numberOfChanges = 0;
-
- for (int i = 0; i < kNumberOfPlayers - 1; ++i)
- {
- // loc_4BFD3: ; CODE XREF: prepareRankingTextEntries+AFj
- RankingEntry *rankingEntry = &rankingEntries[i];
- RankingEntry *nextRankingEntry = &rankingEntries[i + 1];
-
- uint32_t totalSeconds = rankingEntry->hours * 3600 + rankingEntry->minutes * 60 + rankingEntry->seconds;
- uint32_t nextTotalSeconds = rankingEntry->hours * 3600 + rankingEntry->minutes * 60 + rankingEntry->seconds;
-
- if (nextRankingEntry->nextLevelToPlay > rankingEntry->nextLevelToPlay || (nextRankingEntry->nextLevelToPlay == rankingEntry->nextLevelToPlay && nextTotalSeconds > totalSeconds))
- {
- // loc_4BFFD: ; CODE XREF: prepareRankingTextEntries+4Ej
- // ; prepareRankingTextEntries+58j ...
- RankingEntry aux = *nextRankingEntry;
- *nextRankingEntry = *rankingEntry;
- *rankingEntry = aux;
- numberOfChanges++;
- }
- }
- } while (numberOfChanges > 0);
-
- for (int i = 0; i < 20; ++i)
- {
- // loc_4C04B: ; CODE XREF: prepareRankingTextEntries+CFj
- if (rankingEntries[i].playerIndex == gCurrentPlayerIndex)
- {
- byte_58D47 = i;
- }
- }
-
- // loc_4C061:
- // di = 0x883C; <- entry 2 of gRankingTextEntries
-
- for (int i = 0; i < 20; ++i)
- {
- RankingEntry *rankingEntry = &rankingEntries[i];
- char *textEntry = gRankingTextEntries[i + 2]; // No idea why the first two are always empty
- // loc_4C067: ; CODE XREF: prepareRankingTextEntries+14Dj
- if (rankingEntry->nextLevelToPlay == 0x71) // 113
- {
- textEntry[0] =
- textEntry[1] =
- textEntry[2] = '9';
- }
- else
- {
- // loc_4C078: ; CODE XREF: prepareRankingTextEntries+DFj
- convertNumberTo3DigitPaddedString(rankingEntry->nextLevelToPlay, textEntry, 0);
- }
-
- // loc_4C07F: ; CODE XREF: prepareRankingTextEntries+E9j
- PlayerEntry playerEntry = gPlayerListData[rankingEntry->playerIndex];
-
- // loc_4C091: ; CODE XREF: prepareRankingTextEntries+10Bj
- memcpy(&textEntry[4], playerEntry.name, sizeof(playerEntry.name) - 1);
-
- convertNumberTo3DigitStringWithPadding0(rankingEntry->seconds, &textEntry[19]);
- textEntry[19] = ':';
-
- convertNumberTo3DigitStringWithPadding0(rankingEntry->minutes, &textEntry[16]);
- textEntry[16] = ':';
-
- convertNumberTo3DigitStringWithPadding0(rankingEntry->hours, &textEntry[13]);
- }
-}
-
-void drawRankings() // sub_4C0DD proc near ; CODE XREF: handleNewPlayerOptionClick+1E9p
-// ; handleDeletePlayerOptionClick+E2p ...
-{
- // 01ED:547A
- prepareRankingTextEntries();
-
- const uint8_t kDistanceBetweenLines = 9;
-
- for (int i = 0; i < 5; ++i)
- {
- const uint8_t y = 110 + kDistanceBetweenLines * (i - 2);
- const uint8_t color = (i == 2 ? 6 : 8);
- drawTextWithChars6FontWithOpaqueBackgroundIfPossible(8, y, color, gRankingTextEntries[byte_58D46 + i]);
- }
-
- char numberString[4] = "001"; // 0x8359
- convertNumberTo3DigitStringWithPadding0(byte_58D46 + 1, numberString);
- drawTextWithChars6FontWithOpaqueBackgroundIfPossible(144, 110, 6, &numberString[1]); // Remove the first (left most) digit
-}
-
-void drawLevelList() // sub_4C141 proc near ; CODE XREF: start+41Ap handleGameUserInput+39Bp ...
-{
- // 01ED:54DE
- byte_59821 = gCurrentPlayerLevelData[gCurrentSelectedLevelIndex - 2];
- byte_59822 = gCurrentPlayerLevelData[gCurrentSelectedLevelIndex - 1];
- byte_59823 = gCurrentPlayerLevelData[gCurrentSelectedLevelIndex];
-
- char *previousLevelName = (char *)&gLevelListData[(gCurrentSelectedLevelIndex - 2) * kListLevelNameLength];
- drawTextWithChars6FontWithOpaqueBackgroundIfPossible(144, 155, byte_59821, previousLevelName);
-
- char *currentLevelName = (char *)&gLevelListData[(gCurrentSelectedLevelIndex - 1) * kListLevelNameLength];
- drawTextWithChars6FontWithOpaqueBackgroundIfPossible(144, 164, byte_59822, currentLevelName);
-
- memcpy(gCurrentLevelName, currentLevelName, kListLevelNameLength);
-
- char *nextLevelName = (char *)&gLevelListData[gCurrentSelectedLevelIndex * kListLevelNameLength];
- drawTextWithChars6FontWithOpaqueBackgroundIfPossible(144, 173, byte_59823, nextLevelName);
-}
-
-void drawHallOfFame() // sub_4C1A9 proc near ; CODE XREF: handleFloppyDiskButtonClick+15Ap
-// ; drawMenuTitleAndDemoLevelResult+11p
-{
- // 01ED:5546
- char text[19] = {'\0'};
-
- for (int i = 0; i < kNumberOfHallOfFameEntries; ++i)
- {
- // loc_4C1B7: ; CODE XREF: drawHallOfFame+56j
- strcpy(text, " ");
- HallOfFameEntry entry = gHallOfFameData[i];
-
- convertNumberTo3DigitStringWithPadding0(entry.seconds, &text[15]);
- text[15] = ':';
-
- convertNumberTo3DigitStringWithPadding0(entry.minutes, &text[12]);
- text[12] = ':';
-
- convertNumberTo3DigitPaddedString(entry.hours, &text[9], 1);
-
- uint8_t playerNameLength = MIN(strlen(entry.playerName), sizeof(entry.playerName) - 1);
- memcpy(text, entry.playerName, playerNameLength);
-
- drawTextWithChars6FontWithOpaqueBackgroundIfPossible(184, 28 + i * 9, 8, text);
- }
-}
-
-void drawCurrentPlayerRanking() // proc near ; CODE XREF: drawPlayerList+5Bp // sub_4C224
-{
- // 01ED:55C1
- PlayerEntry currentPlayerEntry = gPlayerListData[gCurrentPlayerIndex];
- drawTextWithChars6FontWithOpaqueBackgroundIfPossible(168, 93, 8, currentPlayerEntry.name);
-
- char timeText[10] = "000:00:00";
-
- // Seconds
- convertNumberTo3DigitStringWithPadding0(currentPlayerEntry.seconds, &timeText[6]);
- timeText[6] = ':';
-
- // Minutes
- convertNumberTo3DigitStringWithPadding0(currentPlayerEntry.minutes, &timeText[3]);
- timeText[3] = ':';
-
- // Hours
- convertNumberTo3DigitStringWithPadding0(currentPlayerEntry.hours, timeText);
-
- drawTextWithChars6FontWithOpaqueBackgroundIfPossible(224, 93, 8, timeText);
-
- char nextLevelText[4] = "000";
- convertNumberTo3DigitStringWithPadding0(currentPlayerEntry.nextLevelToPlay, nextLevelText);
- drawTextWithChars6FontWithOpaqueBackgroundIfPossible(288, 93, 8, nextLevelText);
-}
-
-void drawPlayerList() // sub_4C293 proc near ; CODE XREF: start+32Cp start+407p ...
-{
- // 01ED:5630
- PlayerEntry currentPlayer = gPlayerListData[gCurrentPlayerIndex];
- memcpy(gPlayerName, currentPlayer.name, sizeof(currentPlayer.name) - 1);
- drawTextWithChars6FontWithOpaqueBackgroundIfPossible(16, 164, 6, currentPlayer.name);
-
- char *prevPlayerName = "";
-
- if (gCurrentPlayerIndex <= 0)
- {
- prevPlayerName = " "; // just 8 spaces :shrug:
- }
- else
- {
- prevPlayerName = gPlayerListData[gCurrentPlayerIndex - 1].name;
- }
-
- // loc_4C2CD: // ; CODE XREF: drawPlayerList+35j
- drawTextWithChars6FontWithOpaqueBackgroundIfPossible(16, 155, 8, prevPlayerName);
-
- char *nextPlayerName = "";
-
- if (gCurrentPlayerIndex >= kNumberOfPlayers - 1) // 19
- {
- nextPlayerName = " "; // just 8 spaces :shrug:
- }
- else
- {
- nextPlayerName = gPlayerListData[gCurrentPlayerIndex + 1].name;
- }
-
- // loc_4C2E6: // ; CODE XREF: drawPlayerList+4Ej
- drawTextWithChars6FontWithOpaqueBackgroundIfPossible(16, 173, 8, nextPlayerName);
- drawCurrentPlayerRanking();
-}
-
-void drawMenuTitleAndDemoLevelResult() // sub_4C2F2 proc near ; CODE XREF: handleGfxTutorOptionClick+Cp
- // ; sub_4C407+1Fp ...
-{
- // 01ED:568F
- drawTextWithChars6FontWithOpaqueBackgroundIfPossible(168, 127, 4, " WELCOME TO SUPAPLEX ");
- drawPlayerList();
- drawLevelList();
- drawHallOfFame();
- drawRankings();
- if (byte_59B83 == 0)
- {
- return;
- }
- byte_59B83 = 0;
-
- char *message = "";
- if (byte_5A19B == 0)
- {
- if (gIsLevelStartedAsDemo == 0)
- {
- message = " LEVEL FAILED ";
- }
- else
- {
- message = " DEMO FAILED ";
- }
- }
- else
- {
- if (gIsLevelStartedAsDemo == 0)
- {
- message = " LEVEL SUCCESSFUL ";
- }
- else
- {
- message = " DEMO SUCCESSFUL ";
- }
- }
-
- // loc_4C33C: // ; CODE XREF: drawMenuTitleAndDemoLevelResult+34j
- // ; drawMenuTitleAndDemoLevelResult+39j ...
- drawTextWithChars6FontWithOpaqueBackgroundIfPossible(168, 127, 4, message);
- byte_5A19B = 0;
-}
-
-void prepareLevelDataForCurrentPlayer() // sub_4C34A proc near ; CODE XREF: start+404p handleNewPlayerOptionClick+1E0p ...
-{
- // 01ED:56E7
- PlayerEntry *currentPlayerEntry = &gPlayerListData[gCurrentPlayerIndex];
-
- uint8_t *currentPlayerLevelState = currentPlayerEntry->levelState;
-
- // Sets everything to 6 which seems to mean all levels are blocked
- memset(gCurrentPlayerPaddedLevelData, kSkippedLevelEntryColor, kNumberOfLevelsWithPadding);
- memset(gCurrentPlayerLevelData, kBlockedLevelEntryColor, kCurrentPlayerkLevelDataLength);
-
- char isFirstUncompletedLevel = 1;
-
- for (int i = 0; i < kNumberOfLevels; ++i)
- {
- // loc_4C373: // ; CODE XREF: prepareLevelDataForCurrentPlayer+53j
- if (currentPlayerLevelState[i] == PlayerLevelStateSkipped)
- {
- gCurrentPlayerLevelData[i] = kSkippedLevelEntryColor;
- }
- // loc_4C37F: // ; CODE XREF: prepareLevelDataForCurrentPlayer+2Ej
- else if (currentPlayerLevelState[i] == PlayerLevelStateCompleted) // Completed levels
- {
- gCurrentPlayerLevelData[i] = kCompletedLevelEntryColor;
- }
- // loc_4C389: // ; CODE XREF: prepareLevelDataForCurrentPlayer+38j
- else if (currentPlayerLevelState[i] == PlayerLevelStateNotCompleted) // Levels not completed
- {
- if (isFirstUncompletedLevel == 1)
- {
- // The first uncompleted is not blocked
- gCurrentPlayerLevelData[i] = kNotCompletedLevelEntryColor;
- }
- else
- {
- // The rest uncompleted levels are blocked
- gCurrentPlayerLevelData[i] = kBlockedLevelEntryColor;
- }
- isFirstUncompletedLevel = 0;
- }
- }
-
- char hasCompletedAllLevels = 1;
- uint8_t nextLevelToPlay = 1;
-
- // Looks for the first uncompleted level
- for (int i = 0; i < kNumberOfLevels; ++i)
- {
- // loc_4C3A7: // ; CODE XREF: prepareLevelDataForCurrentPlayer+65j
- if (currentPlayerLevelState[i] == PlayerLevelStateNotCompleted) // not completed
- {
- hasCompletedAllLevels = 0;
- break;
- }
-
- nextLevelToPlay++;
- }
-
- if (hasCompletedAllLevels == 1)
- {
- nextLevelToPlay = 1;
-
- // Looks for the first completed level
- for (int i = 0; i < kNumberOfLevels; ++i)
- {
- // loc_4C3BA: // ; CODE XREF: prepareLevelDataForCurrentPlayer+78j
- if (currentPlayerLevelState[i] == PlayerLevelStateSkipped)
- {
- hasCompletedAllLevels = 0;
- break;
- }
- nextLevelToPlay++;
- }
- }
-
- if (hasCompletedAllLevels == 1)
- {
- if (gShouldAutoselectNextLevelToPlay != 0)
- {
- gCurrentSelectedLevelIndex = kLastLevelIndex;
- }
-
- // loc_4C3D1: // ; CODE XREF: prepareLevelDataForCurrentPlayer+7Fj
- currentPlayerEntry->nextLevelToPlay = kLastLevelIndex;
- return;
- }
-
- // loc_4C3D6: // ; CODE XREF: prepareLevelDataForCurrentPlayer+61j
- // ; prepareLevelDataForCurrentPlayer+74j
- if (gShouldAutoselectNextLevelToPlay != 0)
- {
- gCurrentSelectedLevelIndex = nextLevelToPlay;
- }
-
- // loc_4C3E1: // ; CODE XREF: prepareLevelDataForCurrentPlayer+91j
- if (nextLevelToPlay == 1)
- {
- if (strcmp(currentPlayerEntry->name, "--------") == 0)
- {
- nextLevelToPlay = 0;
- }
- }
-
- // loc_4C403: // ; CODE XREF: prepareLevelDataForCurrentPlayer+9Aj
- // ; prepareLevelDataForCurrentPlayer+A0j ...
- currentPlayerEntry->nextLevelToPlay = nextLevelToPlay; // 0x7e = 126
-}
-
-void sub_4C407() // proc near ; CODE XREF: runMainMenu+5Dp
-{
- // 01ED:57A4
- if (gLevelFailed != 0)
- {
- gLevelFailed = 0;
- drawFailedLevelResultScreen(); // 01ED:57B5
- drawMenuBackground(); // 01ED:57B8
- gShouldAutoselectNextLevelToPlay = 0;
- prepareLevelDataForCurrentPlayer();
- drawMenuTitleAndDemoLevelResult();
- // mov si, 6015h
- fadeToPalette(gGamePalette);
-
- videoLoop();
-
- // This will prevent to leave traces of the options menu
- // area in the main menu.
- //
- saveLastMouseAreaBitmap();
- }
- else
- {
- // loc_4C449: ; CODE XREF: sub_4C407+3Aj
- scrollLeftToMainMenu();
- }
-}
-
-void scrollLeftToMainMenu() // loc_4C44F: ; CODE XREF: handleGfxTutorOptionClick+9p
-{
- uint8_t *currentScreenPixels = malloc(kFullScreenFramebufferLength);
- memcpy(currentScreenPixels, gScreenPixels, kFullScreenFramebufferLength);
-
- drawMenuBackground();
- gShouldAutoselectNextLevelToPlay = 0;
-
- prepareLevelDataForCurrentPlayer();
- drawMenuTitleAndDemoLevelResult();
-
- uint8_t *menuScreenPixels = malloc(kFullScreenFramebufferLength);
- memcpy(menuScreenPixels, gScreenPixels, kFullScreenFramebufferLength);
-
- const int kNumberOfSteps = 80;
-
- const uint32_t kAnimationDuration = kNumberOfSteps * 1000 / 70; // ~571 ms
- uint32_t animationTime = 0;
-
- startTrackingRenderDeltaTime();
-
- // Draws the current scroll animation step
- while (animationTime < kAnimationDuration)
- {
- animationTime += updateRenderDeltaTime();
- animationTime = MIN(animationTime, kAnimationDuration);
-
- float animationFactor = (float)animationTime / kAnimationDuration;
-
- int limitFromLeft = animationFactor * kScreenWidth;
- int limitFromRight = kScreenWidth - limitFromLeft;
-
- for (int y = 0; y < kScreenHeight; ++y)
- {
- // Main menu side
- for (int x = 0; x < kScreenWidth - limitFromRight; ++x)
- {
- gScreenPixels[y * kScreenWidth + x] = menuScreenPixels[y * kScreenWidth + x + limitFromRight];
- }
-
- // GFX background side
- for (int x = limitFromLeft; x < kScreenWidth; ++x)
- {
- gScreenPixels[y * kScreenWidth + x] = currentScreenPixels[y * kScreenWidth + x - limitFromLeft];
- }
- }
-
- // loc_4C466: ; CODE XREF: sub_4C407+90j
- videoLoop();
- }
-
- free(currentScreenPixels);
- free(menuScreenPixels);
-
- // loc_4C499: ; CODE XREF: sub_4C407+28j
- // This will prevent to leave traces of the options menu
- // area in the main menu.
- //
- saveLastMouseAreaBitmap();
-}
-
-void drawFailedLevelResultScreen() // sub_4C4F9 proc near ; CODE XREF: sub_4C407+11p
-{
- setPalette(gBlackPalette);
- drawBackBackground();
-
- drawTextWithChars6FontWithTransparentBackgroundIfPossible(128, 60, 0xF, "HARD LUCK!");
- if (gNumberOfRemainingInfotrons == 0)
- {
- drawTextWithChars6FontWithTransparentBackgroundIfPossible(40, 80, 0xF, "YOU COMPLETED ALL THE NECESSARY INFOTRONS");
- drawTextWithChars6FontWithTransparentBackgroundIfPossible(72, 100, 0xF, "BUT FAILED TO REACH THE EXIT");
- }
- else
- {
- // loc_4C52C: ; CODE XREF: drawFailedLevelResultScreen+19j
- char message[] = "YOU HAVE COLLECTED ??? OUT OF THE ???";
-
- uint8_t collectedInfotrons = gTotalNumberOfInfotrons - gNumberOfRemainingInfotrons;
- convertNumberTo3DigitPaddedString(collectedInfotrons, &message[19], 1);
-
- convertNumberTo3DigitPaddedString(gTotalNumberOfInfotrons, &message[34], 1);
-
- drawTextWithChars6FontWithTransparentBackgroundIfPossible(40, 80, 0xF, message);
- drawTextWithChars6FontWithTransparentBackgroundIfPossible(104, 100, 0xF, "INFOTRONS NEEDED");
- }
-
- // loc_4C55C: ; CODE XREF: drawFailedLevelResultScreen+31j
- drawTextWithChars6FontWithTransparentBackgroundIfPossible(72, 120, 0xF, "WHY NOT GIVE IT ANOTHER TRY?");
-
- videoLoop();
- setPalette(gInformationScreenPalette);
- if (gShouldExitGame != 1)
- {
- waitForKeyMouseOrJoystick();
- }
-
- // loc_4C591: ; CODE XREF: drawFailedLevelResultScreen+93j
- setPalette(gBlackPalette);
-}
-
-void scrollRightToNewScreen() // sub_4C5AF proc near ; CODE XREF: handleGfxTutorOptionClick+3p
-{
- videoLoop();
-
- uint8_t *screenPixelsBackup = malloc(kFullScreenFramebufferLength);
- memcpy(screenPixelsBackup, gScreenPixels, kFullScreenFramebufferLength);
-
- const int kNumberOfSteps = 80;
-
- const uint32_t kAnimationDuration = kNumberOfSteps * 1000 / 70; // ~571 ms
- uint32_t animationTime = 0;
-
- startTrackingRenderDeltaTime();
-
- // Draws the current scroll animation step
- while (animationTime < kAnimationDuration)
- {
- animationTime += updateRenderDeltaTime();
- animationTime = MIN(animationTime, kAnimationDuration);
-
- float animationFactor = (float)animationTime / kAnimationDuration;
-
- int limitFromRight = animationFactor * kScreenWidth;
- int limitFromLeft = kScreenWidth - limitFromRight;
-
- for (int y = 0; y < kScreenHeight; ++y)
- {
- // Main menu side
- for (int x = 0; x < kScreenWidth - limitFromRight; ++x)
- {
- gScreenPixels[y * kScreenWidth + x] = screenPixelsBackup[y * kScreenWidth + x + limitFromRight];
- }
-
- // GFX background side
- for (int x = limitFromLeft; x < kScreenWidth; ++x)
- {
- gScreenPixels[y * kScreenWidth + x] = gScrollDestinationScreenBitmapData[y * kScreenWidth + x - limitFromLeft];
- }
- }
-
- // loc_4C5BA: ; CODE XREF: scrollRightToGfxTutor+3Cj
- videoLoop();
- }
-
- free(screenPixelsBackup);
-}
-
-void handleOptionsStandardClick() // sub_4C705 proc near ; CODE XREF: code:5ADBp
-{
- activateInternalStandardSound();
- playExplosionSound();
- drawSoundTypeOptionsSelection(gScreenPixels);
-}
-
-void handleOptionsInternalClick() // loc_4C6FB
-{
- handleOptionsStandardClick();
- playExplosionSound();
- drawSoundTypeOptionsSelection(gScreenPixels);
-}
-
-void handleOptionsSamplesClick() // sub_4C70F proc near
-{
- activateInternalSamplesSound();
- playExplosionSound();
- drawSoundTypeOptionsSelection(gScreenPixels);
-}
-
-void handleOptionsSoundBlasterClick() // sub_4C719 proc near
-{
- activateSoundBlasterSound();
- playExplosionSound();
- drawSoundTypeOptionsSelection(gScreenPixels);
-}
-
-void handleOptionsAdlibClick() // sub_4C723 proc near
-{
- activateAdlibSound();
- playExplosionSound();
- drawSoundTypeOptionsSelection(gScreenPixels);
-}
-
-void handleOptionsRolandClick() // sub_4C72D proc near
-{
- activateRolandSound();
- playExplosionSound();
- drawSoundTypeOptionsSelection(gScreenPixels);
-}
-
-void handleOptionsCombinedClick() // sub_4C737 proc near
-{
- activateCombinedSound();
- playExplosionSound();
- drawSoundTypeOptionsSelection(gScreenPixels);
-}
-
-void handleOptionsMusicClick() // sub_4C741 proc near
-{
- if (isMusicEnabled == 1)
- {
- stopMusic();
- isMusicEnabled = 0;
- }
- else
- {
- // loc_4C752: ; CODE XREF: handleOptionsAdlibClick+5j
- isMusicEnabled = 1;
- playMusicIfNeeded();
- }
-
- // loc_4C75A: ; CODE XREF: handleOptionsAdlibClick+Fj
- drawAudioOptionsSelection(gScreenPixels);
- return;
-}
-
-void handleOptionsFXClick() // loc_4C75E
-{
- if (isFXEnabled == 1)
- {
- isFXEnabled = 0;
- }
- else
- {
- // loc_4C76C: ; CODE XREF: code:5B43j
- isFXEnabled = 1;
- playExplosionSound();
- }
-
- // loc_4C774: ; CODE XREF: code:5B4Aj
- drawAudioOptionsSelection(gScreenPixels);
-}
-
-void handleOptionsKeyboardClick() // loc_4C778
-{
- isJoystickEnabled = 0;
- drawInputOptionsSelection(gScreenPixels);
-}
-
-void handleOptionsJoystickClick() // loc_4C781
-{
- isJoystickEnabled = 1;
- // calibrateJoystick(); not needed anymore
- drawInputOptionsSelection(gScreenPixels);
-}
-
-void handleOptionsExitAreaClick() // loc_4C78D
-{
- word_58463 = 1;
-}
-
-void runMainMenu() // proc near ; CODE XREF: start+43Ap
-{
- // 01ED:5B31
- gIsInMainMenu = 1;
- gHasUserInterruptedDemo = 0;
- gSelectedOriginalDemoLevelNumber = 0;
- gIsSPDemoAvailableToRun = 0;
- gAutomaticDemoPlaybackCountdown = 4200;
- if (word_58467 != 0)
- {
- drawMenuBackground(); // 01ED:5B4E
- gShouldAutoselectNextLevelToPlay = 1;
- prepareLevelDataForCurrentPlayer(); // 01ED:5B56
- drawMenuTitleAndDemoLevelResult(); // 01ED:5B59
-
- videoLoop();
- fadeToPalette(gGamePalette); // 6015h
- word_58467 = 0;
- }
- else
- {
- // loc_4C7EC: // ; CODE XREF: runMainMenu+1Bj
- byte_59B83 = 1;
- sub_4C407(); // 01ED:5B8E
- }
-
- // loc_4C7F4: // ; CODE XREF: runMainMenu+56j
- playMusicIfNeeded(); // 01ED:5B91
- saveLastMouseAreaBitmap();
- drawMouseCursor();
-
- while (1)
- {
- int9handler(0);
-
- // loc_4C7FD: // ; CODE XREF: runMainMenu+121j
- // ; runMainMenu+219j ...
- gAutomaticDemoPlaybackCountdown--;
- if (gAutomaticDemoPlaybackCountdown == 0)
- {
- handleDemoOptionClick();
- }
-
- // loc_4C806: // ; CODE XREF: runMainMenu+6Dj
- if (gShouldLeaveMainMenu != 0)
- {
- gShouldLeaveMainMenu = 0;
- break;
- }
-
- // loc_4C81A: // ; CODE XREF: runMainMenu+77j
- videoLoop();
-
- gFrameCounter++;
- uint16_t mouseX, mouseY;
- uint16_t mouseButtonStatus;
- getMouseStatus(&mouseX, &mouseY, &mouseButtonStatus);
- gMouseButtonStatus = mouseButtonStatus;
- if (gMouseX != mouseX || gMouseY != mouseY)
- {
- // loc_4C834: // ; CODE XREF: runMainMenu+98j
- gAutomaticDemoPlaybackCountdown = 4200;
- }
-
- // loc_4C83A: // ; CODE XREF: runMainMenu+9Ej
- gMouseX = mouseX;
- gMouseY = mouseY;
- restoreLastMouseAreaBitmap(); // 01ED:5BDF
- saveLastMouseAreaBitmap(); // 01ED:5BE2
- drawMouseCursor(); // 01ED:5BE5 Draws mouse cursor too?
- drawMainMenuButtonBorders(); // 01ED:5BE8
- updateUserInput();
- if (gPlayerListDownButtonPressed != 0 || gPlayerListUpButtonPressed != 0)
- {
- // loc_4C862: // ; CODE XREF: runMainMenu+C5j
- gPlayerListButtonPressed = 1;
- }
-
- // loc_4C867: // ; CODE XREF: runMainMenu+CCj
- gPlayerListDownButtonPressed = 0;
- gPlayerListUpButtonPressed = 0;
- if (gRankingListDownButtonPressed != 0 || gRankingListUpButtonPressed != 0)
- {
- // loc_4C87F: // ; CODE XREF: runMainMenu+E2j
- gRankingListButtonPressed = 1;
- }
-
- // loc_4C884: // ; CODE XREF: runMainMenu+E9j
- gRankingListDownButtonPressed = 0;
- gRankingListUpButtonPressed = 0;
- if (gLevelListDownButtonPressed != 0 || gLevelListUpButtonPressed != 0)
- {
- // loc_4C89C: // ; CODE XREF: runMainMenu+FFj
- gLevelListButtonPressed = 1;
- }
-
- // loc_4C8A1: // ; CODE XREF: runMainMenu+106j
- gLevelListDownButtonPressed = 0;
- gLevelListUpButtonPressed = 0;
- if (gCurrentUserInput > kUserInputSpaceAndDirectionOffset || isStartButtonPressed())
- {
- handleOkButtonClick();
- }
- // loc_4C8B8: // ; CODE XREF: runMainMenu+11Cj
- else if (gIsF1KeyPressed == 1)
- {
- playDemo(0);
- }
- // loc_4C8C8: // ; CODE XREF: runMainMenu+129j
- else if (gIsF2KeyPressed == 1)
- {
- playDemo(1);
- }
- // loc_4C8D8: // ; CODE XREF: runMainMenu+139j
- else if (gIsF3KeyPressed == 1)
- {
- playDemo(2);
- }
- // loc_4C8E8: // ; CODE XREF: runMainMenu+149j
- else if (gIsF4KeyPressed == 1)
- {
- playDemo(3);
- }
- // loc_4C8F8: // ; CODE XREF: runMainMenu+159j
- else if (gIsF5KeyPressed == 1)
- {
- playDemo(4);
- }
- // loc_4C908: // ; CODE XREF: runMainMenu+169j
- else if (gIsF6KeyPressed == 1)
- {
- playDemo(5);
- }
- // loc_4C918: // ; CODE XREF: runMainMenu+179j
- else if (gIsF7KeyPressed == 1)
- {
- playDemo(6);
- }
- // loc_4C928: // ; CODE XREF: runMainMenu+189j
- else if (gIsF8KeyPressed == 1)
- {
- playDemo(7);
- }
- // loc_4C937: // ; CODE XREF: runMainMenu+199j
- else if (gIsF9KeyPressed == 1)
- {
- playDemo(8);
- }
- // loc_4C946: // ; CODE XREF: runMainMenu+1A8j
- else if (gIsF10KeyPressed == 1)
- {
- playDemo(9);
- }
- // loc_4C955: // ; CODE XREF: runMainMenu+1B7j
- else if (gIsNumpadDividePressed == 1 && strlen(demoFileName) != 0 && fileIsDemo == 1)
- {
- gIsSPDemoAvailableToRun = 1;
- playDemo(0);
- }
- // loc_4C977: // ; CODE XREF: runMainMenu+1C6j
- // ; runMainMenu+1CDj ...
- else if (gIsF12KeyPressed == 1 && strlen(demoFileName) != 0)
- {
- gIsSPDemoAvailableToRun = 1;
- gShouldLeaveMainMenu = 1;
- gIsPlayingDemo = 0;
- gShouldUpdateTotalLevelTime = 0;
- gHasUserCheated = 1;
- prepareDemoRecordingFilename();
- // This adds dashes to the level name or something?
- gSPDemoFileName[3] = 0x2D; // '-' ; "001$0.SP"
- gSPDemoFileName[4] = 0x2D; // '-' ; "01$0.SP"
- gSPDemoFileName[5] = 0x2D; // '-' ; "1$0.SP"
- continue;
- }
- // loc_4C9B0: // ; CODE XREF: runMainMenu+131j
- // ; runMainMenu+141j ...
- // if (gMouseButtonStatus == MouseButtonRight) // Right button -> exit game
- // {
- // gShouldExitGame = 1;
- // break;
- // }
- else if (getGameControllerButtonBack() // Select/Back/- controller button -> exit game
- || gIsEscapeKeyPressed == 1)
- {
- runAdvancedOptionsRootMenu();
- }
- else if (isRotateLevelSetAscendingButtonPressed())
- {
- throttledRotateLevelSet(0);
- continue; // This allows the throttling effect to act
- }
- else if (isRotateLevelSetDescendingButtonPressed())
- {
- throttledRotateLevelSet(1);
- continue; // This allows the throttling effect to act
- }
- if (gShouldExitGame == 1)
- {
- break;
- }
-
- if (gMouseButtonStatus == MouseButtonLeft)
- {
- // loc_4C9FF: // ; CODE XREF: runMainMenu+236j
- gAutomaticDemoPlaybackCountdown = 4200;
-
- for (int i = 0; i < kNumberOfMainMenuButtons; ++i)
- {
- ButtonDescriptor buttonDescriptor = kMainMenuButtonDescriptors[i];
-
- // checkmousecoords: // ; CODE XREF: runMainMenu+29Bj
- if (gMouseX >= buttonDescriptor.startX && gMouseY >= buttonDescriptor.startY && gMouseX <= buttonDescriptor.endX && gMouseY <= buttonDescriptor.endY)
- {
- buttonDescriptor.handler(); // 01ED:5DC0
- break;
- }
- }
- }
- else
- {
- // Reset throttle counters
- gLevelListThrottleCurrentCounter = 0x10;
- gLevelListThrottleNextCounter = 0;
- gPlayerListThrottleCurrentCounter = 0x10;
- gPlayerListThrottleNextCounter = 0;
- gRankingListThrottleCurrentCounter = 0x10;
- gRankingListThrottleNextCounter = 0;
- gLevelSetRotationThrottleCurrentCounter = 0x10;
- gLevelSetRotationThrottleNextCounter = 0;
- }
- }
-
- // loc_4CA34: // ; CODE XREF: runMainMenu+223j
- // ; runMainMenu+22Aj ...
- gIsInMainMenu = 0;
- savePlayerListData();
- saveHallOfFameData();
-}
-
-void handleControlsOptionClick() // showControls: ; DATA XREF: data:0044o
-{
- // 01ED:5DDE;
- byte_50919 = 0xFF;
- drawOptionsBackground(gScrollDestinationScreenBitmapData);
- drawSoundTypeOptionsSelection(gScrollDestinationScreenBitmapData);
- drawAudioOptionsSelection(gScrollDestinationScreenBitmapData);
- drawInputOptionsSelection(gScrollDestinationScreenBitmapData);
- // mov si, 6055h
- setPalette(gControlsScreenPalette);
- scrollRightToNewScreen();
- word_58463 = 0;
- saveLastMouseAreaBitmap();
- drawMouseCursor();
-
- uint16_t mouseX, mouseY;
- uint16_t mouseButtonStatus;
-
- do
- {
- int9handler(0);
-
- // loc_4CA67: ; CODE XREF: code:5E89j
- // ; code:5EBFj ...
- videoLoop(); // 01ED:5E04
- updateOptionsMenuState(gScreenPixels);
- gFrameCounter++;
- getMouseStatus(&mouseX, &mouseY, &mouseButtonStatus);
-
- gMouseButtonStatus = mouseButtonStatus;
- gMouseX = mouseX;
- gMouseY = mouseY;
-
- restoreLastMouseAreaBitmap();
- saveLastMouseAreaBitmap();
- drawMouseCursor();
- if (gMouseButtonStatus == MouseButtonRight)
- {
- break;
- }
- if (isMenuBackButtonPressed()) // Select/Back/- controller button -> go back
- {
- break;
- }
- if (word_58463 == 1)
- {
- break;
- }
- if (gMouseButtonStatus == MouseButtonLeft)
- {
- // loc_4CAAB: ; CODE XREF: code:5E87j
- // mov si, offset controlsbuttons ; 0ACh // 01ED:5E54
-
- for (int i = 0; i < kNumberOfOptionsMenuButtons; ++i)
- {
- ButtonDescriptor buttonDescriptor = kOptionsMenuButtonDescriptors[i];
- // loc_4CABA: ; CODE XREF: code:5EC7j
- if (gMouseX >= buttonDescriptor.startX && gMouseY >= buttonDescriptor.startY && gMouseX <= buttonDescriptor.endX && gMouseY <= buttonDescriptor.endY)
- {
- buttonDescriptor.handler(); // 01ED:5E6A
-
- do
- {
- // loc_4CAD0: ; CODE XREF: code:5EBDj
- videoLoop();
- gFrameCounter++;
- getMouseStatus(&mouseX, &mouseY, &mouseButtonStatus);
- } while (mouseButtonStatus != 0);
- }
- }
- }
- } while (1);
-
- // loc_4CAEC: ; CODE XREF: code:5E74j
- // ; code:5E7Bj ...
- saveConfiguration();
- scrollLeftToMainMenu();
- drawMenuTitleAndDemoLevelResult();
- setPalette(gGamePalette);
-}
-
-void drawSoundTypeOptionsSelection(uint8_t *destBuffer) // sub_4CAFC proc near ; CODE XREF: code:5AE1p handleOptionsStandardClick+6p ...
-{
- dimOptionsButtonText(40, 21, 40, 8, destBuffer);
- drawOptionsMenuLine(kOptionsMenuBorders[0], 4, destBuffer);
- drawOptionsMenuLine(kOptionsMenuBorders[1], 4, destBuffer);
-
- dimOptionsButtonText(24, 57, 72, 8, destBuffer);
- drawOptionsMenuLine(kOptionsMenuBorders[2], 4, destBuffer);
- drawOptionsMenuLine(kOptionsMenuBorders[3], 4, destBuffer);
-
- dimOptionsButtonText(32, 93, 56, 8, destBuffer);
- drawOptionsMenuLine(kOptionsMenuBorders[4], 4, destBuffer);
- drawOptionsMenuLine(kOptionsMenuBorders[5], 4, destBuffer);
-
- dimOptionsButtonText(24, 129, 64, 8, destBuffer);
- dimOptionsButtonText(136, 18, 72, 8, destBuffer);
- drawOptionsMenuLine(kOptionsMenuBorders[7], 4, destBuffer);
-
- dimOptionsButtonText(128, 46, 40, 5, destBuffer);
- drawOptionsMenuLine(kOptionsMenuBorders[8], 4, destBuffer);
-
- dimOptionsButtonText(176, 46, 40, 5, destBuffer);
- drawOptionsMenuLine(kOptionsMenuBorders[9], 4, destBuffer);
- drawOptionsMenuLine(kOptionsMenuBorders[6], 4, destBuffer);
-
- if (sndType == SoundTypeAdlib)
- {
- highlightOptionsButtonText(40, 21, 40, 8, destBuffer);
- drawOptionsMenuLine(kOptionsMenuBorders[0], 6, destBuffer);
- drawOptionsMenuLine(kOptionsMenuBorders[1], 6, destBuffer);
- return;
- }
-
- // loc_4CBC6: ; CODE XREF: drawSoundTypeOptionsSelection+A9j
- if (sndType == SoundTypeSoundBlaster)
- {
- drawOptionsMenuLine(kOptionsMenuBorders[3], 6, destBuffer);
-
- if (musType == SoundTypeAdlib)
- {
- highlightOptionsButtonText(24, 57, 72, 8, destBuffer);
- drawOptionsMenuLine(kOptionsMenuBorders[2], 6, destBuffer);
- return;
- }
-
- // loc_4CBF3: ; CODE XREF: drawSoundTypeOptionsSelection+DEj
- highlightOptionsButtonText(24, 129, 64, 8, destBuffer);
- drawOptionsMenuLine(kOptionsMenuBorders[4], 6, destBuffer);
- drawOptionsMenuLine(kOptionsMenuBorders[6], 6, destBuffer);
- return;
- }
-
- // loc_4CC11: ; CODE XREF: drawSoundTypeOptionsSelection+CFj
- if (sndType == SoundTypeRoland)
- {
- highlightOptionsButtonText(32, 93, 56, 8, destBuffer);
- drawOptionsMenuLine(kOptionsMenuBorders[4], 6, destBuffer);
- drawOptionsMenuLine(kOptionsMenuBorders[5], 6, destBuffer);
- return;
- }
-
- // loc_4CC36: ; CODE XREF: drawSoundTypeOptionsSelection+11Aj
- highlightOptionsButtonText(136, 18, 72, 8, destBuffer);
- drawOptionsMenuLine(kOptionsMenuBorders[7], 6, destBuffer);
-
- if (sndType == SoundTypeInternalStandard)
- {
- highlightOptionsButtonText(128, 46, 40, 5, destBuffer); // Standard
- drawOptionsMenuLine(kOptionsMenuBorders[8], 6, destBuffer);
- return;
- }
-
- // loc_4CC67: ; CODE XREF: drawSoundTypeOptionsSelection+153j
- highlightOptionsButtonText(176, 46, 40, 5, destBuffer);
- drawOptionsMenuLine(kOptionsMenuBorders[9], 6, destBuffer);
-}
-
-void drawAudioOptionsSelection(uint8_t *destBuffer) // sub_4CC7C proc near ; CODE XREF: handleOptionsAdlibClick:loc_4C75Ap
- // ; code:loc_4C774p
-{
- if (isMusicEnabled == 1)
- {
- highlightOptionsButtonText(134, 99, 40, 8, destBuffer);
- drawOptionsMenuLine(kOptionsMenuBorders[10], 6, destBuffer);
- }
- else
- {
- // loc_4CC99: ; CODE XREF: drawAudioOptionsSelection+5j
- dimOptionsButtonText(134, 99, 40, 8, destBuffer);
- drawOptionsMenuLine(kOptionsMenuBorders[10], 4, destBuffer);
- }
-
- // loc_4CCAD: ; CODE XREF: drawAudioOptionsSelection+1Bj
- if (isFXEnabled == 1)
- {
- highlightOptionsButtonText(136, 138, 24, 8, destBuffer);
- drawOptionsMenuLine(kOptionsMenuBorders[11], 6, destBuffer);
- return;
- }
-
- // loc_4CCCA: ; CODE XREF: drawAudioOptionsSelection+36j
- dimOptionsButtonText(136, 138, 24, 8, destBuffer);
- drawOptionsMenuLine(kOptionsMenuBorders[11], 4, destBuffer);
-}
-
-void drawInputOptionsSelection(uint8_t *destBuffer) // sub_4CCDF proc near ; CODE XREF: code:5B5Dp code:5B69p
-{
- if (isJoystickEnabled == 0)
- {
- highlightOptionsButtonText(208, 87, 8, 62, destBuffer);
- dimOptionsButtonText(240, 88, 8, 58, destBuffer);
- drawOptionsMenuLine(kOptionsMenuBorders[18], 4, destBuffer);
- drawOptionsMenuLine(kOptionsMenuBorders[19], 6, destBuffer);
- }
- else
- {
- // loc_4CD10: ; CODE XREF: drawInputOptionsSelection+5j
- dimOptionsButtonText(208, 87, 8, 62, destBuffer);
- highlightOptionsButtonText(240, 88, 8, 58, destBuffer);
- drawOptionsMenuLine(kOptionsMenuBorders[18], 6, destBuffer);
- drawOptionsMenuLine(kOptionsMenuBorders[19], 4, destBuffer);
- }
-
- // loc_4CD38: ; CODE XREF: drawInputOptionsSelection+2Fj
- updateOptionsMenuState(destBuffer);
-}
-
-void updateOptionsMenuState(uint8_t *destBuffer) // sub_4CD3C proc near ; CODE XREF: drawInputOptionsSelection:loc_4CD38p
-{
- // 01ED:60D9
- updateUserInput();
- if (gCurrentUserInput == byte_50919)
- {
- return;
- }
-
- // loc_4CD4D: ; CODE XREF: updateOptionsMenuState+Ej
- byte_50919 = gCurrentUserInput;
- if (gCurrentUserInput == UserInputNone)
- {
- drawOptionsMenuLine(kOptionsMenuBorders[12], 6, destBuffer);
- drawOptionsMenuLine(kOptionsMenuBorders[17], 4, destBuffer);
- }
- else
- {
- // loc_4CD6A: ; CODE XREF: updateOptionsMenuState+1Aj
- if (gCurrentUserInput <= kUserInputSpaceAndDirectionOffset)
- {
- // loc_4CD9E: ; CODE XREF: updateOptionsMenuState+33j
- drawOptionsMenuLine(kOptionsMenuBorders[12], 4, destBuffer);
- drawOptionsMenuLine(kOptionsMenuBorders[17], 4, destBuffer);
- }
- else
- {
- drawOptionsMenuLine(kOptionsMenuBorders[17], 6, destBuffer);
- if (gCurrentUserInput != UserInputSpaceOnly)
- {
- // loc_4CD8F: ; CODE XREF: updateOptionsMenuState+42j
- gCurrentUserInput -= kUserInputSpaceAndDirectionOffset;
- drawOptionsMenuLine(kOptionsMenuBorders[12], 4, destBuffer);
- }
- else
- {
- gCurrentUserInput = UserInputNone;
- drawOptionsMenuLine(kOptionsMenuBorders[12], 6, destBuffer);
- }
- }
- }
-
- // loc_4CDAE: ; CODE XREF: updateOptionsMenuState+2Cj
- // ; updateOptionsMenuState+51j ...
- drawOptionsMenuLine(kOptionsMenuBorders[13], 4, destBuffer);
- drawOptionsMenuLine(kOptionsMenuBorders[14], 4, destBuffer);
- drawOptionsMenuLine(kOptionsMenuBorders[15], 4, destBuffer);
- drawOptionsMenuLine(kOptionsMenuBorders[16], 4, destBuffer);
- if (gCurrentUserInput == UserInputUp)
- {
- drawOptionsMenuLine(kOptionsMenuBorders[13], 6, destBuffer);
- }
- // loc_4CDDF: ; CODE XREF: updateOptionsMenuState+97j
- else if (gCurrentUserInput == UserInputLeft)
- {
- drawOptionsMenuLine(kOptionsMenuBorders[14], 6, destBuffer);
- }
- // loc_4CDF0: ; CODE XREF: updateOptionsMenuState+A8j
- else if (gCurrentUserInput == UserInputDown)
- {
- drawOptionsMenuLine(kOptionsMenuBorders[15], 6, destBuffer);
- }
- // loc_4CE01: ; CODE XREF: updateOptionsMenuState+B9j
- else if (gCurrentUserInput == UserInputRight)
- {
- drawOptionsMenuLine(kOptionsMenuBorders[16], 6, destBuffer);
- }
-}
-
-void highlightOptionsButtonText(size_t startX, size_t startY, size_t width, size_t height, uint8_t *destBuffer) // sub_4CE11 proc near ; CODE XREF: drawSoundTypeOptionsSelection+B4p
- // ; drawSoundTypeOptionsSelection+E9p ...
-{
- // Copies a portion of the buffer replacing color 0xF (not selected)
- // with color 0x1 (selected).
- // Used in the options screen to highlight text from buttons.
- //
- // Parameters:
- // - si: origin coordinates
- // - cx: width / 8
- // - dx: height
-
- restoreLastMouseAreaBitmap();
-
- for (size_t y = startY; y < startY + height; ++y)
- {
- for (size_t x = startX; x < startX + width; ++x)
- {
- // loc_4CE68: ; CODE XREF: highlightOptionsButtonText+5Aj
- // ; highlightOptionsButtonText+6Aj
- size_t addr = (y * kScreenWidth) + x;
- destBuffer[addr] = (destBuffer[addr] == 0xF
- ? 0x1
- : destBuffer[addr]);
- }
- }
-
- saveLastMouseAreaBitmap();
- drawMouseCursor();
-}
-
-void dimOptionsButtonText(size_t startX, size_t startY, size_t width, size_t height, uint8_t *destBuffer) // sub_4CE9C proc near ; CODE XREF: drawSoundTypeOptionsSelection+9p
- // ; drawSoundTypeOptionsSelection+25p ...
-{
- // Copies a portion of the buffer replacing color 0x1 (selected)
- // with color 0xF (not selected).
- // Used in the options screen to dim text from buttons.
- //
- // Parameters:
- // - si: coordinates
- // - cx: width / 8
- // - dx: height
-
- restoreLastMouseAreaBitmap();
-
- for (size_t y = startY; y < startY + height; ++y)
- {
- for (size_t x = startX; x < startX + width; ++x)
- {
- // loc_4CEF3: ; CODE XREF: dimOptionsButtonText+5Aj
- // ; dimOptionsButtonText+6Aj
- size_t addr = (y * kScreenWidth) + x;
-
- destBuffer[addr] = (destBuffer[addr] == 0x1
- ? 0xF
- : destBuffer[addr]);
- }
- }
-
- saveLastMouseAreaBitmap();
- drawMouseCursor();
-}
-
-void drawOptionsMenuLine(ButtonBorderDescriptor border, uint8_t color, uint8_t *destBuffer) // sub_4CF13 proc near ; CODE XREF: drawSoundTypeOptionsSelection+11p
- // ; drawSoundTypeOptionsSelection+19p ...
-{
- // Parameters:
- // - ah: color
- // - si: pointer to ButtonBorderDescriptor item
-
- restoreLastMouseAreaBitmap();
-
- // loc_4CF38: ; CODE XREF: drawOptionsMenuLine+96j
- for (int i = 0; i < border.numberOfLines; ++i)
- {
- ButtonBorderLineDescriptor line = border.lines[i];
-
- for (int j = 0; j < line.length; ++j)
- {
- size_t destAddress = 0;
- if (line.type == ButtonBorderLineTypeHorizontal)
- {
- destAddress = line.y * kScreenWidth + line.x + j;
- }
- else if (line.type == ButtonBorderLineTypeVertical)
- {
- destAddress = (line.y - j) * kScreenWidth + line.x;
- }
- else if (line.type == ButtonBorderLineTypeBottomLeftToTopRightDiagonal)
- {
- destAddress = (line.y - j) * kScreenWidth + line.x + j;
- }
- else if (line.type == ButtonBorderLineTypeTopLeftToBottomRightDiagonal)
- {
- destAddress = (line.y + j) * kScreenWidth + line.x + j;
- }
-
- destBuffer[destAddress] = color;
-
- // loc_4CFA4: ; CODE XREF: drawOptionsMenuLine:loc_4CF7Cj
- // ; drawOptionsMenuLine+73j ...
- }
- }
-
- // loc_4CFAB: ; CODE XREF: drawOptionsMenuLine+2Aj
- saveLastMouseAreaBitmap();
- drawMouseCursor();
-}
-
-void savePlayerListData() // sub_4CFB2 proc near ; CODE XREF: handleNewPlayerOptionClick+1D5p
-// ; handleDeletePlayerOptionClick+CEp ...
-{
- if (gIsForcedCheatMode != 0)
- {
- return;
- }
-
- FILE *file = openWritableFile(gPlayerLstFilename, "wb");
- if (file == NULL)
- {
- return;
- }
-
- assert(sizeof(gPlayerListData) == 0xA00);
-
- fileWriteBytes(gPlayerListData, sizeof(gPlayerListData), file);
-
- fclose(file);
-}
-
-void saveHallOfFameData() // proc near ; CODE XREF: handleNewPlayerOptionClick+1D8p
-// ; handleDeletePlayerOptionClick+D1p ...
-{
- if (gIsForcedCheatMode != 0)
- {
- return;
- }
-
- FILE *file = openWritableFile(gHallfameLstFilename, "wb");
- if (file == NULL)
- {
- return;
- }
-
- assert(sizeof(gHallOfFameData) == 0x24);
-
- fileWriteBytes(gHallOfFameData, sizeof(gHallOfFameData), file);
-
- fclose(file);
-}
-
-void drawMainMenuButtonBorder(ButtonBorderDescriptor border, uint8_t color) // sub_4D004 proc near ; CODE XREF: drawMainMenuButtonBorders+17p
-// ; drawMainMenuButtonBorders+2Ap ...
-{
- // 01ED:63A1
- // Parameters:
- // - si: button border descriptor
- // - ah: color??? it's either 7 or 0xD in drawMainMenuButtonBorders
-
- restoreLastMouseAreaBitmap();
-
- // loc_4D029: ; CODE XREF: drawButtonBorder+96j
- for (int i = 0; i < border.numberOfLines; ++i)
- {
- ButtonBorderLineDescriptor line = border.lines[i];
-
- for (int j = 0; j < line.length; ++j)
- {
- size_t destAddress = 0;
- if (line.type == ButtonBorderLineTypeHorizontal)
- {
- destAddress = line.y * kScreenWidth + line.x + j;
- }
- else if (line.type == ButtonBorderLineTypeVertical)
- {
- destAddress = (line.y - j) * kScreenWidth + line.x;
- }
- else if (line.type == ButtonBorderLineTypeBottomLeftToTopRightDiagonal)
- {
- destAddress = (line.y - j) * kScreenWidth + line.x + j;
- }
- else if (line.type == ButtonBorderLineTypeTopLeftToBottomRightDiagonal)
- {
- destAddress = (line.y + j) * kScreenWidth + line.x + j;
- }
-
- gScreenPixels[destAddress] = color;
-
- // loc_4D095: ; CODE XREF: drawButtonBorder:loc_4D06Dj
- // ; drawButtonBorder+73j ...
- }
- }
-
- // loc_4D09C: ; CODE XREF: drawButtonBorder+2Aj
- saveLastMouseAreaBitmap();
- drawMouseCursor();
-}
-
-void drawMainMenuButtonBorders() // sub_4D0AD proc near ; CODE XREF: runMainMenu+B7p
-{
- // 01ED:644A
- uint8_t color = 0;
-
- if (gPlayerListButtonPressed != 0)
- {
- if (gPlayerListUpButtonPressed == 0)
- {
- color = 7;
- }
- else
- {
- color = 0xD; // 13
- }
-
- // loc_4D0C1: ; CODE XREF: drawMainMenuButtonBorders+10j
- drawMainMenuButtonBorder(kMainMenuButtonBorders[0], color);
- if (gPlayerListUpButtonPressed == 0)
- {
- color = 0xD; // 13
- }
- else
- {
- color = 7;
- }
-
- // loc_4D0D4: ; CODE XREF: drawMainMenuButtonBorders+23j
- drawMainMenuButtonBorder(kMainMenuButtonBorders[1], color);
- if (gPlayerListDownButtonPressed == 0)
- {
- color = 7;
- }
- else
- {
- color = 0xD; // 13
- }
-
- // loc_4D0E7: ; CODE XREF: drawMainMenuButtonBorders+36j
- drawMainMenuButtonBorder(kMainMenuButtonBorders[2], color);
- if (gPlayerListDownButtonPressed == 0)
- {
- color = 0xD; // 13
- }
- else
- {
- color = 7;
- }
-
- // loc_4D0FA: ; CODE XREF: drawMainMenuButtonBorders+49j
- drawMainMenuButtonBorder(kMainMenuButtonBorders[3], color);
- gPlayerListButtonPressed = 0;
- }
-
- // loc_4D105: ; CODE XREF: drawMainMenuButtonBorders+5j
- if (gRankingListButtonPressed != 0)
- {
- if (gRankingListUpButtonPressed == 0)
- {
- color = 7;
- }
- else
- {
- color = 0xD; // 13
- }
-
- // loc_4D119: ; CODE XREF: drawMainMenuButtonBorders+68j
- // si = 0x558; // 1368
- drawMainMenuButtonBorder(kMainMenuButtonBorders[4], color);
- if (gRankingListUpButtonPressed == 0)
- {
- color = 0xD;
- }
- else
- {
- color = 7;
- }
-
- // loc_4D12C: ; CODE XREF: drawMainMenuButtonBorders+7Bj
- // si = 0x56D;
- drawMainMenuButtonBorder(kMainMenuButtonBorders[5], color);
- if (gRankingListDownButtonPressed == 0)
- {
- color = 7;
- }
- else
- {
- color = 0xD;
- }
-
- // loc_4D13F: ; CODE XREF: drawMainMenuButtonBorders+8Ej
- // si = 0x582;
- drawMainMenuButtonBorder(kMainMenuButtonBorders[6], color);
- if (gRankingListDownButtonPressed == 0)
- {
- color = 0xD;
- }
- else
- {
- color = 7;
- }
-
- // loc_4D152: ; CODE XREF: drawMainMenuButtonBorders+A1j
- // si = 0x597;
- drawMainMenuButtonBorder(kMainMenuButtonBorders[7], color);
- gRankingListButtonPressed = 0;
- }
-
- // loc_4D15D: ; CODE XREF: drawMainMenuButtonBorders+5Dj
- if (gLevelListButtonPressed == 0)
- {
- return;
- }
- if (gLevelListUpButtonPressed == 0)
- {
- color = 7;
- }
- else
- {
- color = 0xD;
- }
-
- // loc_4D171: ; CODE XREF: drawMainMenuButtonBorders+C0j
- // si = 0x5AC;
- drawMainMenuButtonBorder(kMainMenuButtonBorders[8], color);
- if (gLevelListUpButtonPressed == 0)
- {
- color = 0xD;
- }
- else
- {
- color = 7;
- }
-
- // loc_4D184: ; CODE XREF: drawMainMenuButtonBorders+D3j
- // si = 0x5C1;
- drawMainMenuButtonBorder(kMainMenuButtonBorders[9], color);
- if (gLevelListDownButtonPressed == 0)
- {
- color = 7;
- }
- else
- {
- color = 0xD;
- }
-
- // loc_4D197: ; CODE XREF: drawMainMenuButtonBorders+E6j
- // si = 0x5D6;
- drawMainMenuButtonBorder(kMainMenuButtonBorders[10], color);
- if (gLevelListDownButtonPressed == 0)
- {
- color = 0xD;
- }
- else
- {
- color = 7;
- }
-
- // loc_4D1AA: ; CODE XREF: drawMainMenuButtonBorders+F9j
- // si = 0x5EB;
- drawMainMenuButtonBorder(kMainMenuButtonBorders[11], color);
- gLevelListButtonPressed = 0;
-}
-
-void updateHallOfFameEntries() // sub_4D1B6 proc near ; CODE XREF: changePlayerCurrentLevelState+2Ep
-{
- // 01ED:6553
- if (gIsPlayingDemo != 0)
- {
- return;
- }
-
- // loc_4D1BE: ; CODE XREF: updateHallOfFameEntries+5j
- PlayerEntry *currentPlayerEntry = &gPlayerListData[gCurrentPlayerIndex];
- if (currentPlayerEntry->completedAllLevels != 0)
- {
- return;
- }
-
- // loc_4D1D2: ; CODE XREF: updateHallOfFameEntries+19j
- int numberOfCompletedLevels = 0;
-
- for (int i = 0; i < kNumberOfLevels; ++i)
- {
- // loc_4D1E2: ; CODE XREF: updateHallOfFameEntries+33j
- if (currentPlayerEntry->levelState[i] == PlayerLevelStateCompleted)
- {
- numberOfCompletedLevels++;
- }
- // loc_4D1E8: ; CODE XREF: updateHallOfFameEntries+2Fj
- }
-
- if (numberOfCompletedLevels != kNumberOfLevels)
- {
- return;
- }
-
- currentPlayerEntry->completedAllLevels = 1;
-
- int newEntryInsertIndex = -1;
- // mov cx, 3
- // mov di, hallFameDataBuffer
- for (int i = 0; i < kNumberOfHallOfFameEntries; ++i)
- {
- HallOfFameEntry entry = gHallOfFameData[i];
-
- // loc_4D1FA: ; CODE XREF: updateHallOfFameEntries+78j
- if (entry.hours == 0 && entry.minutes == 0 && entry.seconds == 0)
- {
- newEntryInsertIndex = i;
- break;
- }
-
- // loc_4D20E: ; CODE XREF: updateHallOfFameEntries+48j
- // ; updateHallOfFameEntries+4Ej ...
- if (currentPlayerEntry->hours < entry.hours)
- {
- newEntryInsertIndex = i;
- break;
- }
- else if (currentPlayerEntry->hours == entry.hours)
- {
- if (currentPlayerEntry->minutes < entry.minutes)
- {
- newEntryInsertIndex = i;
- break;
- }
- else if (currentPlayerEntry->minutes == entry.minutes)
- {
- if (currentPlayerEntry->seconds < entry.seconds)
- {
- newEntryInsertIndex = i;
- break;
- }
- }
- }
-
- // loc_4D22A: ; CODE XREF: updateHallOfFameEntries+60j
- // ; updateHallOfFameEntries+6Aj
- }
-
- if (newEntryInsertIndex != -1)
- {
- // loc_4D232: ; CODE XREF: updateHallOfFameEntries+56j
- // ; updateHallOfFameEntries+5Ej ...
-
- // Shift the list to the right to make room for the new entry
- for (int i = kNumberOfHallOfFameEntries - 1; i >= newEntryInsertIndex + 1; --i)
- {
- memcpy(&gHallOfFameData[i], &gHallOfFameData[i - 1], sizeof(HallOfFameEntry));
- }
-
- // Copy the player info into the new entry
- HallOfFameEntry *newEntry = &gHallOfFameData[newEntryInsertIndex];
-
- memcpy(newEntry->playerName,
- currentPlayerEntry->name,
- sizeof(currentPlayerEntry->name));
- newEntry->hours = currentPlayerEntry->hours;
- newEntry->minutes = currentPlayerEntry->minutes;
- newEntry->seconds = currentPlayerEntry->seconds;
- }
-
- // loc_4D24B: ; CODE XREF: updateHallOfFameEntries+38j
- // ; updateHallOfFameEntries+7Aj
-}
-
-void changePlayerCurrentLevelState() // sub_4D24D proc near ; CODE XREF: handleSkipLevelOptionClick+D9p
-// ; update?:loc_4E6A4p
-{
- // 01ED:65EA
- if (gIsPlayingDemo != 0)
- {
- return;
- }
- if (gHasUserCheated != 0)
- {
- return;
- }
- uint8_t previousState = gCurrentPlayerLevelState;
- gCurrentPlayerLevelState = PlayerLevelStateNotCompleted;
-
- PlayerEntry *currentPlayerEntry = &gPlayerListData[gCurrentPlayerIndex];
- currentPlayerEntry->levelState[gCurrentSelectedLevelIndex - 1] = previousState;
- gCurrentSelectedLevelIndex++;
- updateHallOfFameEntries(); // 01ED:6618
-
- // Added by me to prevent losing progress when switching levelsets after finishing a level
- savePlayerListData();
- saveHallOfFameData();
-}
-
-void initializeFadePalette() // proc near ; CODE XREF: start+296p
-{
- setPalette(gBlackPalette);
-}
-
-void initializeMouse() // proc near ; CODE XREF: start+299p
-{
- gMouseX = kScreenWidth / 2;
- gMouseY = kScreenHeight / 2;
- if (getFullscreenMode())
- {
- centerMouse();
- }
- hideMouse();
- handleSystemEvents();
-}
-
-void getMouseStatus(uint16_t *mouseX, uint16_t *mouseY, uint16_t *mouseButtonStatus) // proc near ; CODE XREF: waitForKeyMouseOrJoystick:mouseIsClickedp
-// ; waitForKeyMouseOrJoystick+3Ep ...
-{
- // Returns coordinate X in CX (0-320) and coordinate Y in DX (0-200).
- // Also button status in BX.
-
- handleSystemEvents();
-
- int x, y;
- uint8_t leftButtonPressed, rightButtonPressed;
-
- getMouseState(&x, &y, &leftButtonPressed, &rightButtonPressed);
-
- int windowWidth, windowHeight;
- getWindowSize(&windowWidth, &windowHeight);
-
- float controllerX = 0, controllerY = 0;
- uint8_t controllerLeftButton = 0;
- uint8_t controllerRightButton = 0;
- gameControllerEmulateMouse(&controllerX,
- &controllerY,
- &controllerLeftButton,
- &controllerRightButton);
-
- uint8_t shouldCorrectMousePosition = 0;
-
- if (controllerX != 0.0 || controllerY != 0.0)
- {
- float speed = (float)windowWidth / 1280;
-
- x += speed * controllerX;
- y += speed * controllerY;
-
- shouldCorrectMousePosition = 1;
- }
-
- // Read touch screen where available
- float touchScreenX, touchScreenY;
- uint8_t touchScreenPressed = readTouchScreen(&touchScreenX, &touchScreenY);
- if (touchScreenPressed)
- {
- x = touchScreenX * windowWidth;
- y = touchScreenY * windowHeight;
-
- shouldCorrectMousePosition = 1;
- }
-
- if (shouldCorrectMousePosition)
- {
- x = CLAMP(x, 0, windowWidth);
- y = CLAMP(y, 0, windowHeight);
-
- // Correct mouse position for future events
- moveMouse(x, y);
- }
-
- if (windowWidth != 0 && windowHeight != 0)
- {
- x = x * kScreenWidth / windowWidth;
- y = y * kScreenHeight / windowHeight;
- }
-
- leftButtonPressed = (leftButtonPressed || controllerLeftButton || touchScreenPressed);
- rightButtonPressed = (rightButtonPressed || controllerRightButton);
-
- // Limit coordinates as in the original game
- x = CLAMP(x, 16, 304);
- y = CLAMP(y, 8, 192);
-
- if (mouseX != NULL)
- {
- *mouseX = x;
- }
- if (mouseY != NULL)
- {
- *mouseY = y;
- }
-
- if (mouseButtonStatus != NULL)
- {
- *mouseButtonStatus = (rightButtonPressed << 1 | leftButtonPressed);
- }
-}
-
-#define COPY_LEVEL_DATA(__dest, __size) \
- do \
- { \
- memcpy(__dest, &levelFileData[pointer], __size); \
- pointer += __size; \
- } while (0)
-
-#define COPY_LEVEL_DATA_UINT16(__dest) \
- do \
- { \
- COPY_LEVEL_DATA(&__dest, sizeof(uint16_t)); \
- __dest = convert16LE(__dest); \
- } while (0)
-
-void mapLevelFileData(char *levelFileData, Level *level)
-{
- size_t pointer = 0;
-
- COPY_LEVEL_DATA(level->tiles, sizeof(level->tiles));
- COPY_LEVEL_DATA(level->unused, sizeof(level->unused));
- COPY_LEVEL_DATA(&level->initialGravitation, sizeof(level->initialGravitation));
- COPY_LEVEL_DATA(&level->speedFixMagicNumber, sizeof(level->speedFixMagicNumber));
- COPY_LEVEL_DATA(level->name, sizeof(level->name));
- COPY_LEVEL_DATA(&level->freezeZonks, sizeof(level->freezeZonks));
- COPY_LEVEL_DATA(&level->numberOfInfotrons, sizeof(level->numberOfInfotrons));
- COPY_LEVEL_DATA(&level->numberOfSpecialPorts, sizeof(level->numberOfSpecialPorts));
- for (int idx = 0; idx < kLevelMaxNumberOfSpecialPorts; ++idx)
- {
- SpecialPortInfo *specialPortInfo = &level->specialPortsInfo[idx];
- COPY_LEVEL_DATA_UINT16(specialPortInfo->position);
- COPY_LEVEL_DATA(&specialPortInfo->gravity, sizeof(specialPortInfo->gravity));
- COPY_LEVEL_DATA(&specialPortInfo->freezeZonks, sizeof(specialPortInfo->freezeZonks));
- COPY_LEVEL_DATA(&specialPortInfo->freezeEnemies, sizeof(specialPortInfo->freezeEnemies));
- COPY_LEVEL_DATA(&specialPortInfo->unused, sizeof(specialPortInfo->unused));
- }
- COPY_LEVEL_DATA(&level->scrambledSpeed, sizeof(level->scrambledSpeed));
- COPY_LEVEL_DATA(&level->scrambledChecksum, sizeof(level->scrambledChecksum));
- COPY_LEVEL_DATA_UINT16(level->randomSeed);
-
- assert(pointer == kLevelDataLength);
-}
-
-#undef COPY_LEVEL_DATA_UINT16
-#undef COPY_LEVEL_DATA
-
-void readLevels() // proc near ; CODE XREF: start:loc_46F3Ep
- // ; fetchAndInitializeLevelp
-{
- // 01ED:68E5
- char *filename = "";
- FILE *file = NULL;
- char levelFileData[kLevelDataLength];
-
- if (gIsPlayingDemo != 0 && (gSelectedOriginalDemoLevelNumber & 0xFF) == 0 && gIsSPDemoAvailableToRun == 0)
- {
- // Demos with the new format
- Level *level = &gDemos.level[gDemoIndexOrDemoLevelNumber];
-
- memcpy(&levelFileData, level, kLevelDataLength);
-
- strcpy(gCurrentDemoLevelName, ".SP");
-
- memcpy(&gCurrentDemoLevelName[4], level->name, sizeof(level->name));
- }
- else
- {
- if (gIsPlayingDemo == 0 || gIsSPDemoAvailableToRun != 0)
- {
- // loc_4D59F: ; CODE XREF: readLevels+5j
- // ; readLevels+13j
- filename = gLevelsDatFilename; // lea dx, aLevels_dat_0 ; "LEVELS.DAT"
- }
-
- if (gIsPlayingDemo != 0 && (gSelectedOriginalDemoLevelNumber & 0xFF) != 0) // cmp byte ptr gSelectedOriginalDemoLevelNumber, 0
- {
- // loc_4D599: ; CODE XREF: readLevels+Cj
- filename = gLevelsDatFilename; // lea dx, aLevels_dat ; "LEVELS.DAT"
- }
- // loc_4D5A3: ; CODE XREF: readLevels+55j
- else if (gIsSPDemoAvailableToRun != 0)
- {
- filename = demoFileName;
- }
- else if (gSelectedOriginalDemoFromCommandLineLevelNumber != 0)
- {
- filename = gLevelsDatFilename; // lea dx, aLevels_dat ; "LEVELS.DAT"
- }
-
- // loc_4D5BB: ; CODE XREF: readLevels+63j
- file = openWritableFileWithReadonlyFallback(filename, "rb");
- if (file == NULL)
- {
- exitWithError("Error opening %s\n", filename);
- }
-
- uint8_t levelIndex = 0;
-
- // loc_4D5C2: ; CODE XREF: readLevels+75j
- if (gIsPlayingDemo != 0)
- {
- levelIndex = gDemoIndexOrDemoLevelNumber;
- }
- else
- {
- // loc_4D5D1: ; CODE XREF: readLevels+82j
- levelIndex = gCurrentSelectedLevelIndex;
- }
-
- // loc_4D5D4: ; CODE XREF: readLevels+87j
- if (gIsSPDemoAvailableToRun != 0)
- {
- levelIndex = gSelectedOriginalDemoFromCommandLineLevelNumber;
- if (levelIndex == 0)
- {
- levelIndex++;
- }
- }
-
- // loc_4D5E3: ; CODE XREF: readLevels+91j
- // ; readLevels+98j
- levelIndex--; // Levels anywhere else are 1-index, we need them to start from 0 here
- size_t fileOffset = levelIndex * kLevelDataLength;
- // 01ED:699A
- int result = fseek(file, fileOffset, SEEK_SET);
- if (result != 0)
- {
- exitWithError("Error seeking %s\n", filename);
- }
-
- // loc_4D604: ; CODE XREF: readLevels+B7j
- // 01ED:69AE
- size_t bytes = fileReadBytes(levelFileData, kLevelDataLength, file);
- if (bytes < kLevelDataLength)
- {
- exitWithError("Error reading %s\n", filename);
- }
-
- Level tmpLevel;
- mapLevelFileData(levelFileData, &tmpLevel);
- gIsGravityEnabled = tmpLevel.initialGravitation;
- gAreZonksFrozen = tmpLevel.freezeZonks;
- gNumberOfInfoTrons = tmpLevel.numberOfInfotrons;
- gNumberOfSpecialPorts = tmpLevel.numberOfSpecialPorts;
- gRandomSeed = tmpLevel.randomSeed;
-
- // loc_4D618: ; CODE XREF: readLevels+CBj
- if ((gSelectedOriginalDemoLevelNumber & 0xFF) != 0) // cmp byte ptr gSelectedOriginalDemoLevelNumber, 0
- {
- gSelectedOriginalDemoLevelNumber |= 0xFF00; // mov byte ptr gSelectedOriginalDemoLevelNumber+1, 0FFh
-
- gDemoIndexOrDemoLevelNumber = gSelectedOriginalDemoIndex;
-
- Level *level = &gDemos.level[gDemoIndexOrDemoLevelNumber];
- mapLevelFileData(levelFileData, level);
- }
- }
-
- char *levelName = NULL;
-
- // loc_4D64B: ; CODE XREF: readLevels+4Ej
- // ; readLevels+D5j
- if (gIsPlayingDemo != 0)
- {
- gRandomGeneratorSeed = gRandomSeed;
- levelName = gCurrentDemoLevelName;
- }
- else
- {
- // loc_4D65D: ; CODE XREF: readLevels+108j
- levelName = gCurrentLevelName;
- }
-
- // loc_4D660: ; CODE XREF: readLevels+113j
- if (gSelectedOriginalDemoLevelNumber != 0 || (gIsSPDemoAvailableToRun != 0 && gSelectedOriginalDemoFromCommandLineLevelNumber != 0))
- {
- // loc_4D679: ; CODE XREF: readLevels+121j
- strcpy(gCurrentDemoLevelName, "BIN");
- levelName += 4;
- }
- else if (gIsSPDemoAvailableToRun == 0)
- {
- // loc_4D68C: ; CODE XREF: readLevels+128j
- levelName += 4; // Skips the number directly to the title (from pointing "005 ------- EASY DEAL -------" to pointing "------- EASY DEAL -------")
- }
- else if (gSelectedOriginalDemoFromCommandLineLevelNumber == 0)
- {
- // loc_4D682: ; CODE XREF: readLevels+12Fj
- strcpy(gCurrentDemoLevelName, ".SP");
- levelName += 4;
- }
-
- // loc_4D68F: ; CODE XREF: readLevels+142j
- mapLevelFileData(levelFileData, &gCurrentLevel);
- memcpy(levelName, gCurrentLevel.name, sizeof(gCurrentLevel.name));
-
- // The reason this is 1536 (level file size) and not 1440 (actual gamefield size of 60 * 24 tiles) is because
- // the game was written like this, and some levels rely on this behavior by removing the bottom border of the level
- // and using some unused bytes from those 1536 to store tile data.
- // An example of this is "HIDDEN TRACK" (07/07s062-1.sp)
- //
- for (int i = 0; i < kLevelDataLength; ++i)
- {
- // loc_4D6B8: ; CODE XREF: readLevels+172j
- StatefulLevelTile *tile = &gCurrentLevelState[i];
- tile->tile = levelFileData[i];
- tile->state = 0;
- }
-
- memset(&gExplosionTimers, 0, sizeof(gExplosionTimers)); // rep stosb
-
- if (gIsPlayingDemo == 0 || (gSelectedOriginalDemoLevelNumber & 0xFF) != 0 || gIsSPDemoAvailableToRun != 0)
- {
- // loc_4D6DC: ; CODE XREF: readLevels+184j
- // ; readLevels+18Bj
- if (fclose(file) != 0)
- {
- exitWithError("Error closing %s\n", filename);
- }
- }
-
- // loc_4D6EA: ; CODE XREF: readLevels+192j
- // ; readLevels+19Dj
- gSelectedOriginalDemoLevelNumber &= 0xFF00; // mov byte ptr gSelectedOriginalDemoLevelNumber, 0
-}
-
-void soundShutdown() // proc near ; CODE XREF: start+48Ep
-// ; loadScreen2-7DAp
-{
- stopMusicAndSounds();
-}
-
-void activateInternalStandardSound() // loadBeep proc near ; CODE XREF: readConfig:loc_4751Ap
-// ; readConfig:loc_47551p ...
-{
- stopMusicAndSounds();
- setSoundType(SoundTypeInternalStandard, SoundTypeInternalStandard);
- playMusicIfNeeded();
- gCurrentSoundPriority = 0;
- gCurrentSoundDuration = 0;
-}
-
-void activateInternalSamplesSound() // loadBeep2 proc near ; CODE XREF: readConfig+4Cp handleOptionsSamplesClickp
-{
- stopMusicAndSounds();
- setSoundType(SoundTypeInternalStandard, SoundTypeInternalSamples);
- playMusicIfNeeded();
- gCurrentSoundPriority = 0;
- gCurrentSoundDuration = 0;
-}
-
-void activateAdlibSound() // loadAdlib proc near ; CODE XREF: readConfig+56p handleOptionsAdlibClickp
-{
- stopMusicAndSounds(); // 01ED:6D06
- setSoundType(SoundTypeAdlib, SoundTypeAdlib);
- playMusicIfNeeded();
- gCurrentSoundPriority = 0;
- gCurrentSoundDuration = 0;
-}
-
-void activateSoundBlasterSound() // loadBlaster proc near ; CODE XREF: readConfig+60p handleOptionsSoundBlasterClickp
-{
- // 01ED:6D39
- stopMusicAndSounds();
- setSoundType(SoundTypeAdlib, SoundTypeSoundBlaster);
- playMusicIfNeeded();
- gCurrentSoundPriority = 0;
- gCurrentSoundDuration = 0;
-}
-
-void activateRolandSound() // loadRoland proc near ; CODE XREF: readConfig+6Ap handleOptionsRolandClickp
-{
- stopMusicAndSounds();
- setSoundType(SoundTypeRoland, SoundTypeRoland);
- playMusicIfNeeded();
- gCurrentSoundPriority = 0;
- gCurrentSoundDuration = 0;
-}
-
-void activateCombinedSound() // loadCombined proc near ; CODE XREF: readConfig+74p handleOptionsCombinedClickp
-{
- stopMusicAndSounds();
- setSoundType(SoundTypeRoland, SoundTypeSoundBlaster);
- playMusicIfNeeded();
- gCurrentSoundPriority = 0;
- gCurrentSoundDuration = 0;
-}
-
-void stopMusicAndSounds() // sound1 proc near ; CODE XREF: soundShutdown?+5p
- // ; code:6CC7p ...
-{
- // 01ED:6E4E
- setSoundType(SoundTypeNone, SoundTypeNone);
-}
-
-void playMusicIfNeeded() // sound2 proc near ; CODE XREF: start+39Bp start+410p ...
-{
- // 01ED:6EA8
- if (isMusicEnabled != 1)
- {
- return;
- }
-
- playMusic();
-}
-
-void playExplosionSound() // sound4 proc near ; CODE XREF: detonateBigExplosion+2EDp code:5ADEp ...
-{
- if (isFXEnabled != 1)
- {
- return;
- }
-
- // loc_4DB7F: ; CODE XREF: playExplosionSound+5j
- if (gCurrentSoundPriority >= 5)
- {
- return;
- }
-
- // loc_4DB87: ; CODE XREF: playExplosionSound+Dj
- gCurrentSoundDuration = 0xF;
- gCurrentSoundPriority = 5;
-
- playSoundEffect(SoundEffectExplosion);
-}
-
-void playInfotronSound() // sound5 proc near ; CODE XREF: update?:loc_4E55Cp
- // ; update?:loc_4E588p ...
-{
- if (isFXEnabled == 0)
- {
- return;
- }
-
- // loc_4DBE8: ; CODE XREF: playInfotronSound+5j
- if (gCurrentSoundPriority >= 5)
- {
- return;
- }
-
- // loc_4DBF0: ; CODE XREF: playInfotronSound+Dj
- gCurrentSoundDuration = 0xF;
- gCurrentSoundPriority = 4;
-
- playSoundEffect(SoundEffectInfotron);
-}
-
-void playPushSound() // sound6 proc near ; CODE XREF: update?+B8Bp
- // ; update?+136Cp
-{
- if (isFXEnabled == 0)
- {
- return;
- }
-
- // loc_4DC51: ; CODE XREF: playPushSound+5j
- if (gCurrentSoundPriority >= 2)
- {
- return;
- }
-
- // loc_4DC59: ; CODE XREF: playPushSound+Dj
- gCurrentSoundDuration = 7;
- gCurrentSoundPriority = 2;
- playSoundEffect(SoundEffectPush);
-}
-
-void playFallSound() // sound7 proc near ; CODE XREF: movefun:loc_48125p
-// ; movefun2:loc_48573p
-{
- if (isFXEnabled == 0)
- {
- return;
- }
-
- // loc_4DCBA: ; CODE XREF: playFallSound+5j
- if (gCurrentSoundPriority >= 2)
- {
- return;
- }
-
- // loc_4DCC2: ; CODE XREF: playFallSound+Dj
- gCurrentSoundDuration = 7;
- gCurrentSoundPriority = 2;
- playSoundEffect(SoundEffectFall);
-}
-
-void playBugSound() // sound8 proc near ; CODE XREF: movefun7:loc_4A0ABp
-{
- if (isFXEnabled == 0)
- {
- return;
- }
-
- // loc_4DD23: ; CODE XREF: playBugSound+5j
- if (gCurrentSoundPriority >= 3)
- {
- return;
- }
-
- // loc_4DD2B: ; CODE XREF: playBugSound+Dj
- gCurrentSoundDuration = 3;
- gCurrentSoundPriority = 3;
-
- playSoundEffect(SoundEffectBug);
-}
-
-void playBaseSound() // sound9 proc near ; CODE XREF: runLevel+2F4p
- // ; update?:loc_4E3E1p ...
-{
- if (isFXEnabled == 0)
- {
- return;
- }
-
- // xxxxxxxxdcdc: ; CODE XREF: playBaseSound+5j
- if (gCurrentSoundPriority >= 1)
- {
- return;
- }
-
- // loc_4DD94: ; CODE XREF: playBaseSound+Dj
- gCurrentSoundDuration = 3;
- gCurrentSoundPriority = 1;
-
- playSoundEffect(SoundEffectBase);
-}
-
-void playExitSound() // sound10 proc near ; CODE XREF: update?+7EBp
-{
- if (isFXEnabled == 0)
- {
- return;
- }
-
- // loc_4DDF5: ; CODE XREF: playExitSound+5j
- gCurrentSoundDuration = 0xFA;
- gCurrentSoundPriority = 0xA;
- stopMusic();
-
- playSoundEffect(SoundEffectExit);
-}
-
-int16_t updateMurphy(int16_t position) // update? proc near ; CODE XREF: updateMovingObjects+Ep
-{
- // 01ED:722D
-
- StatefulLevelTile *murphyTile = &gCurrentLevelState[position];
- StatefulLevelTile *leftTile = &gCurrentLevelState[position - 1];
- StatefulLevelTile *rightTile = &gCurrentLevelState[position + 1];
- StatefulLevelTile *belowTile = &gCurrentLevelState[position + kLevelWidth];
- StatefulLevelTile *aboveTile = &gCurrentLevelState[position - kLevelWidth];
-
- if (murphyTile->tile != LevelTileTypeMurphy)
- {
- gIsMurphyUpdated = 0;
- return position;
- }
-
- // hasValidMurphy: ; CODE XREF: update?+5j
- gIsMurphyUpdated = 1;
- gMurphyPreviousLocation = position;
- if (murphyTile->state != 0 || murphyTile->tile != LevelTileTypeMurphy)
- {
- return updateMurphyAnimation(position);
- }
-
- // loc_4DEB4: ; CODE XREF: update?+1Fj
- gScratchGravity = 0;
-
- if (gIsGravityEnabled != 0 && aboveTile->tile != LevelTileTypePortUp && aboveTile->tile != LevelTileTypePortVertical && aboveTile->tile != LevelTileTypePort4Way && (belowTile->state == 0 && belowTile->tile == LevelTileTypeSpace))
- {
- gScratchGravity = 1;
- }
-
- // loc_4DEE1: ; CODE XREF: update?+2Ej update?+35j ...
- UserInput userInput = gCurrentUserInput;
-
- if (userInput == UserInputNone)
- {
- // loc_4DEED: ; CODE XREF: update?+58j
- gPreviousUserInputWasNone = 1;
- if (gScratchGravity != 0)
- {
- MurphyAnimationDescriptor unknownMurphyData;
- // loc_4E38A: ; CODE XREF: update?+69j update?+2FFj
- if (gIsMurphyLookingLeft != 0)
- {
- unknownMurphyData = kMurphyAnimationDescriptors[3]; // dx = 0x0E2E;
- }
- else
- {
- // loc_4E396: ; CODE XREF: update?+4FFj
- unknownMurphyData = kMurphyAnimationDescriptors[4]; // dx = 0x0E3E;
- }
-
- // loc_4E399: ; CODE XREF: update?+504j
- belowTile->state = 3;
- belowTile->tile = LevelTileTypeMurphy;
- murphyTile->state = 3;
- murphyTile->tile = LevelTileTypeSpace;
- gMurphyCounterToStartPushAnimation = 0;
- gIsMurphyGoingThroughPortal = 0;
- return updateMurphyAnimationInfo(position + kLevelWidth, unknownMurphyData);
- }
-
- // loc_4DEFC: ; CODE XREF: update?+67j
- if ((gFrameCounter & 3) != 0)
- {
- return position;
- }
-
- // loc_4DF05: ; CODE XREF: update?+72j
- gMurphyYawnAndSleepCounter++;
- if (gMurphyYawnAndSleepCounter == 4)
- {
- // si = kMurphyStillSpriteCoordinates;
- drawMovingFrame(304, 132, position);
-
- return position;
- }
- // loc_4DF1E: ; CODE XREF: update?+7Ej
- else if (gMurphyYawnAndSleepCounter <= 0x01F4)
- {
- return position;
- }
- // loc_4DF27: ; CODE XREF: update?+94j
- else if (gMurphyYawnAndSleepCounter <= 0x020A)
- {
- // Yawning animation
- uint16_t currentFrame = gMurphyYawnAndSleepCounter - 0x1F4;
- currentFrame = currentFrame >> 1;
-
- AnimationFrameCoordinates animationFrameCoordinates = kMurphyAnimationFrameCoordinates[34];
- Point frameCoordinates = animationFrameCoordinates.coordinates[currentFrame];
-
- drawMovingFrame(frameCoordinates.x, frameCoordinates.y, position);
-
- return position;
- }
- // loc_4DF4A: ; CODE XREF: update?+9Dj
- else if (gMurphyYawnAndSleepCounter <= 0x03E8)
- {
- return position;
- }
- // loc_4DF53: ; CODE XREF: update?+C0j
- else if (gMurphyYawnAndSleepCounter <= 0x03FE)
- {
- // Yawning animation
- uint16_t currentFrame = gMurphyYawnAndSleepCounter - 0x3E8;
- currentFrame = currentFrame >> 1;
-
- AnimationFrameCoordinates animationFrameCoordinates = kMurphyAnimationFrameCoordinates[34];
- Point frameCoordinates = animationFrameCoordinates.coordinates[currentFrame];
-
- drawMovingFrame(frameCoordinates.x, frameCoordinates.y, position);
-
- return position;
- }
- // loc_4DF76: ; CODE XREF: update?+C9j
- else if (gMurphyYawnAndSleepCounter <= 0x0640)
- {
- return position;
- }
- // loc_4DF7F: ; CODE XREF: update?+ECj
- else if (gMurphyYawnAndSleepCounter <= 0x0656)
- {
- // Yawning animation
- uint16_t currentFrame = gMurphyYawnAndSleepCounter - 0x640;
- currentFrame = currentFrame >> 1;
-
- AnimationFrameCoordinates animationFrameCoordinates = kMurphyAnimationFrameCoordinates[34];
- Point frameCoordinates = animationFrameCoordinates.coordinates[currentFrame];
-
- drawMovingFrame(frameCoordinates.x, frameCoordinates.y, position);
-
- return position;
- }
- // loc_4DFA2: ; CODE XREF: update?+F5j
- else if (gMurphyYawnAndSleepCounter > 0x0676)
- {
- return position;
- }
- else if (leftTile->state != 0 || leftTile->tile != LevelTileTypeSpace)
- {
- // loc_4DFBF: ; CODE XREF: update?+11Fj
- // Sleep to left animation
- uint16_t currentFrame = gMurphyYawnAndSleepCounter - 0x656;
- currentFrame = currentFrame >> 4;
-
- AnimationFrameCoordinates animationFrameCoordinates = kMurphyAnimationFrameCoordinates[35];
- Point frameCoordinates = animationFrameCoordinates.coordinates[currentFrame];
-
- drawMovingFrame(frameCoordinates.x, frameCoordinates.y, position);
-
- return position;
- }
- else if (rightTile->state != 0 || rightTile->tile != LevelTileTypeSpace)
- {
- // loc_4DFE0: ; CODE XREF: update?+126j
- // Sleep to right animation
- uint16_t currentFrame = gMurphyYawnAndSleepCounter - 0x656;
- currentFrame = currentFrame >> 4;
-
- AnimationFrameCoordinates animationFrameCoordinates = kMurphyAnimationFrameCoordinates[36];
- Point frameCoordinates = animationFrameCoordinates.coordinates[currentFrame];
-
- drawMovingFrame(frameCoordinates.x, frameCoordinates.y, position);
-
- return position;
- }
- else
- {
- gMurphyYawnAndSleepCounter = 0x24; // 36
- return position;
- }
- }
-
- // loc_4E001: ; CODE XREF: update?+5Aj
- // 01ED:739E
- if (gScratchGravity != 0 && (belowTile->state == 0 && belowTile->tile == LevelTileTypeSpace))
- {
- if (userInput != UserInputUp || (aboveTile->state != 0 || aboveTile->tile != LevelTileTypeBase))
- {
- // loc_4E01B: ; CODE XREF: update?+182j
- if (userInput != UserInputLeft || (leftTile->state != 0 || leftTile->tile != LevelTileTypeBase))
- {
- // loc_4E027: ; CODE XREF: update?+18Ej
- if (userInput != UserInputRight || (rightTile->state != 0 || rightTile->tile != LevelTileTypeBase))
- {
- // loc_4E033: ; CODE XREF: update?+19Aj
- userInput = UserInputDown;
- }
- }
- }
- }
-
- // loc_4E035: ; CODE XREF: update?+176j update?+17Dj ...
- // 01ED:73D2
- if (userInput == UserInputUp)
- {
- gPreviousUserInputWasNone = 0;
- return handleMurphyDirectionUp(position);
- }
- // loc_4E041: ; CODE XREF: update?+1A8j
- else if (userInput == UserInputLeft)
- {
- gPreviousUserInputWasNone = 0;
- return handleMurphyDirectionLeft(position);
- }
- // loc_4E04E: ; CODE XREF: update?+1B4j
- else if (userInput == UserInputDown)
- {
- gPreviousUserInputWasNone = 0;
- return handleMurphyDirectionDown(position);
- }
- // loc_4E05B: ; CODE XREF: update?+1C1j
- else if (userInput == UserInputRight)
- {
- gPreviousUserInputWasNone = 0;
- return handleMurphyDirectionRight(position);
- }
- // loc_4E068: ; CODE XREF: update?+1CEj
- else if (userInput == UserInputSpaceUp)
- {
- gPreviousUserInputWasNone = 0;
- // loc_4E260: ; CODE XREF: update?+1E2j
- // mov ax, leveldata[si-78h]
- if (aboveTile->state == 0 && aboveTile->tile == LevelTileTypeBase)
- {
- // loc_4E4BD: ; CODE XREF: update?+3D9j
- // push si
- // mov di, [si+6155h]
- // si = word_51840;
- drawMovingFrame(160, 64, position);
- // pop si
- playBaseSound();
- // dx = 0x0ECE;
- murphyTile->state = 0x10;
- gMurphyCounterToStartPushAnimation = 0;
- gIsMurphyGoingThroughPortal = 0;
- return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[13]);
- }
- // loc_4E26C: ; CODE XREF: update?+3D7j
- else if (aboveTile->tile == LevelTileTypeBug)
- {
- // loc_4E4AC: ; CODE XREF: update?+3E0j
- if (aboveTile->state < 0x80)
- {
- detonateBigExplosion(position);
- return position;
- }
-
- // loc_4E4B7: ; CODE XREF: update?+621j
- aboveTile->state = 0;
- aboveTile->tile = LevelTileTypeBase;
-
- // loc_4E4BD: ; CODE XREF: update?+3D9j
- // push si
- // mov di, [si+6155h]
- // si = word_51840;
- drawMovingFrame(160, 64, position);
- // pop si
- playBaseSound();
- // dx = 0x0ECE;
- murphyTile->state = 0x10;
- gMurphyCounterToStartPushAnimation = 0;
- gIsMurphyGoingThroughPortal = 0;
- return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[13]);
- }
- // loc_4E273: ; CODE XREF: update?+3DEj
- else if (aboveTile->state == 0 && aboveTile->tile == LevelTileTypeInfotron)
- {
- // loc_4E5F4: ; CODE XREF: update?+3E8j
- // push si
- // mov di, [si+6155h]
- // si = word_51840;
- drawMovingFrame(160, 64, position);
- // pop si
- playInfotronSound();
- // dx = 0x0F6E;
- murphyTile->state = 0x14;
- aboveTile->state = 0xFF;
- gMurphyCounterToStartPushAnimation = 0;
- gIsMurphyGoingThroughPortal = 0;
- return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[23]);
- }
- // loc_4E27B: ; CODE XREF: update?+3E6j
- else if (aboveTile->tile == LevelTileTypeTerminal)
- {
- // loc_4E712: ; CODE XREF: update?+249j update?+3EFj
- // push si
- // mov di, [si+6155h]
- // si = word_51840;
- drawMovingFrame(160, 64, position);
- // pop si
- if (gAreYellowDisksDetonated != 0)
- {
- gMurphyYawnAndSleepCounter = 0xA;
- return position;
- }
-
- // loc_4E72D: ; CODE XREF: update?+894j
- // push si
- // mov di, [si+60DDh]
- // si = kTerminalOnSpriteCoordinates;
- drawMovingFrame(256, 388, position - kLevelWidth);
- // pop si
- detonateYellowDisks();
- return position;
- }
- // loc_4E282: ; CODE XREF: update?+3EDj
- else if (aboveTile->tile == LevelTileTypeRedDisk)
- {
- // loc_4E8B6: ; CODE XREF: update?+3F6j
- // dx = 0x106E;
- murphyTile->state = 0x20;
- aboveTile->state = 3;
- gMurphyCounterToStartPushAnimation = 0;
- gIsMurphyGoingThroughPortal = 0;
- return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[39]);
- }
- else
- {
- return position;
- }
- }
- // loc_4E075: ; CODE XREF: update?+1DBj
- else if (userInput == UserInputSpaceLeft)
- {
- gPreviousUserInputWasNone = 0;
- // loc_4E28A: ; CODE XREF: update?+1EFj
- gIsMurphyLookingLeft = 1;
- // mov ax, [si+1832h]
- if (leftTile->state == 0 && leftTile->tile == LevelTileTypeBase)
- {
- // loc_4E4E9: ; CODE XREF: update?+409j
- // push si
- // mov di, [si+6155h]
- // si = word_51842;
- drawMovingFrame(208, 16, position);
- // pop si
- playBaseSound();
- // dx = 0x0EDE;
- murphyTile->state = 0x11;
- gMurphyCounterToStartPushAnimation = 0;
- gIsMurphyGoingThroughPortal = 0;
- return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[14]);
- }
- // loc_4E29C: ; CODE XREF: update?+407j
- else if (leftTile->tile == LevelTileTypeBug)
- {
- // loc_4E4D8: ; CODE XREF: update?+410j
- if (leftTile->state < 0x80)
- {
- detonateBigExplosion(position);
- return position;
- }
-
- // loc_4E4E3: ; CODE XREF: update?+64Dj
- leftTile->state = 0;
- leftTile->tile = LevelTileTypeBase;
-
- // loc_4E4E9: ; CODE XREF: update?+409j
- // push si
- // mov di, [si+6155h]
- // si = word_51842;
- drawMovingFrame(208, 16, position);
- // pop si
- playBaseSound();
- // dx = 0x0EDE;
- murphyTile->state = 0x11;
- gMurphyCounterToStartPushAnimation = 0;
- gIsMurphyGoingThroughPortal = 0;
- return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[14]);
- }
- // loc_4E2A3: ; CODE XREF: update?+40Ej
- else if (leftTile->state == 0 && leftTile->tile == LevelTileTypeInfotron)
- {
- // loc_4E614: ; CODE XREF: update?+418j
- // push si
- // mov di, [si+6155h]
- // si = word_51842;
- drawMovingFrame(208, 16, position);
- // pop si
- playInfotronSound();
- // dx = 0x0F7E;
- murphyTile->state = 0x15;
- leftTile->state = 0xFF;
- gMurphyCounterToStartPushAnimation = 0;
- gIsMurphyGoingThroughPortal = 0;
- return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[24]);
- }
- // loc_4E2AB: ; CODE XREF: update?+416j
- else if (leftTile->tile == LevelTileTypeTerminal)
- {
- // loc_4E73C: ; CODE XREF: update?+2B9j update?+41Fj
- // push si
- // mov di, [si+6155h]
- // si = word_51842;
- drawMovingFrame(208, 16, position);
- // pop si
- if (gAreYellowDisksDetonated != 0)
- {
- gMurphyYawnAndSleepCounter = 0xA;
- return position;
- }
-
- // loc_4E757: ; CODE XREF: update?+8BEj
- // push si
- // mov di, [si+6153h]
- // si = kTerminalOnSpriteCoordinates;
- drawMovingFrame(256, 388, position - 1);
- // pop si
- detonateYellowDisks();
- return position;
- }
- // loc_4E2B2: ; CODE XREF: update?+41Dj
- else if (leftTile->tile == LevelTileTypeRedDisk)
- {
- // loc_4E8C5: ; CODE XREF: update?+426j
- // dx = 0x107E;
- murphyTile->state = 0x21;
- leftTile->state = 3;
- gMurphyCounterToStartPushAnimation = 0;
- gIsMurphyGoingThroughPortal = 0;
- return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[40]);
- }
- else
- {
- return position;
- }
- }
- // loc_4E082: ; CODE XREF: update?+1E8j
- else if (userInput == UserInputSpaceDown)
- {
- gPreviousUserInputWasNone = 0;
- // loc_4E2BA: ; CODE XREF: update?+1FCj
- // mov ax, [si+18ACh]
- if (belowTile->state == 0 && belowTile->tile == LevelTileTypeBase)
- {
- // loc_4E515: ; CODE XREF: update?+433j
- // push si
- // mov di, [si+6155h]
- // si = word_51844;
- drawMovingFrame(176, 64, position);
- // pop si
- playBaseSound();
- // dx = 0x0EEE;
- murphyTile->state = 0x12;
- gMurphyCounterToStartPushAnimation = 0;
- gIsMurphyGoingThroughPortal = 0;
- return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[15]);
- }
- // loc_4E2C6: ; CODE XREF: update?+431j
- else if (belowTile->tile == LevelTileTypeBug)
- {
- // loc_4E504: ; CODE XREF: update?+43Aj
- if (belowTile->state < 0x80)
- {
- detonateBigExplosion(position);
- return position;
- }
-
- // loc_4E50F: ; CODE XREF: update?+679j
- belowTile->state = 0;
- belowTile->tile = LevelTileTypeBase;
-
- // loc_4E515: ; CODE XREF: update?+433j
- // push si
- // mov di, [si+6155h]
- // si = word_51844;
- drawMovingFrame(176, 64, position);
- // pop si
- playBaseSound();
- // dx = 0x0EEE;
- murphyTile->state = 0x12;
- gMurphyCounterToStartPushAnimation = 0;
- gIsMurphyGoingThroughPortal = 0;
- return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[15]);
- }
- // loc_4E2CD: ; CODE XREF: update?+438j
- else if (belowTile->state == 0 && belowTile->tile == LevelTileTypeInfotron)
- {
- // loc_4E634: ; CODE XREF: update?+442j
- // push si
- // mov di, [si+6155h]
- // si = word_51844;
- drawMovingFrame(176, 64, position);
- // pop si
- playInfotronSound();
- // dx = 0x0F8E;
- murphyTile->state = 0x16;
- belowTile->state = 0xFF;
- gMurphyCounterToStartPushAnimation = 0;
- gIsMurphyGoingThroughPortal = 0;
- return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[25]);
- }
- // loc_4E2D5: ; CODE XREF: update?+440j
- else if (belowTile->tile == LevelTileTypeTerminal)
- {
- // loc_4E766: ; CODE XREF: update?+325j update?+449j
- // push si
- // mov di, [si+6155h]
- // si = word_51844;
- drawMovingFrame(176, 64, position);
- // pop si
- if (gAreYellowDisksDetonated != 0)
- {
- gMurphyYawnAndSleepCounter = 0xA;
- return position;
- }
-
- // loc_4E781: ; CODE XREF: update?+8E8j
- // push si
- // mov di, [si+61CDh]
- // si = kTerminalOnSpriteCoordinates;
- drawMovingFrame(256, 388, position + kLevelWidth);
- // pop si
- detonateYellowDisks();
- return position;
- }
- // loc_4E2DC: ; CODE XREF: update?+447j
- else if (belowTile->tile == LevelTileTypeRedDisk)
- {
- // loc_4E8D4: ; CODE XREF: update?+450j
- // dx = 0x108E;
- murphyTile->state = 0x22;
- belowTile->state = 3;
- gMurphyCounterToStartPushAnimation = 0;
- gIsMurphyGoingThroughPortal = 0;
- return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[41]);
- }
- else
- {
- return position;
- }
- }
- // loc_4E08F: ; CODE XREF: update?+1F5j
- else if (userInput == UserInputSpaceRight)
- {
- gPreviousUserInputWasNone = 0;
- // loc_4E2E4: ; CODE XREF: update?+209j
- gIsMurphyLookingLeft = 0;
- // mov ax, [si+1836h]
- if (rightTile->state == 0 && rightTile->tile == LevelTileTypeBase)
- {
- // loc_4E541: ; CODE XREF: update?+463j
- // push si
- // mov di, [si+6155h]
- // si = word_51846;
- drawMovingFrame(192, 16, position);
- // pop si
- playBaseSound();
- // dx = 0x0EFE;
- murphyTile->state = 0x13;
- gMurphyCounterToStartPushAnimation = 0;
- gIsMurphyGoingThroughPortal = 0;
- return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[16]);
- }
- // loc_4E2F6: ; CODE XREF: update?+461j
- else if (rightTile->tile == LevelTileTypeBug)
- {
- // loc_4E530: ; CODE XREF: update?+46Aj
- if (rightTile->state < 0x80)
- {
- detonateBigExplosion(position);
- return position;
- }
-
- // loc_4E53B: ; CODE XREF: update?+6A5j
- rightTile->state = 0;
- rightTile->tile = LevelTileTypeBase;
-
- // loc_4E541: ; CODE XREF: update?+463j
- // push si
- // mov di, [si+6155h]
- // si = word_51846;
- drawMovingFrame(192, 16, position);
- // pop si
- playBaseSound();
- // dx = 0x0EFE;
- murphyTile->state = 0x13;
- gMurphyCounterToStartPushAnimation = 0;
- gIsMurphyGoingThroughPortal = 0;
- return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[16]);
- }
- // loc_4E2FD: ; CODE XREF: update?+468j
- else if (rightTile->state == 0 && rightTile->tile == LevelTileTypeInfotron)
- {
- // loc_4E654: ; CODE XREF: update?+472j
- // push si
- // mov di, [si+6155h]
- // si = word_51846;
- drawMovingFrame(192, 16, position);
- // pop si
- playInfotronSound();
- // dx = 0x0F9E;
- murphyTile->state = 0x17;
- rightTile->state = 0xFF;
- gMurphyCounterToStartPushAnimation = 0;
- gIsMurphyGoingThroughPortal = 0;
- return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[26]);
- }
- // loc_4E305: ; CODE XREF: update?+470j
- else if (rightTile->tile != LevelTileTypeTerminal)
- {
- // loc_4E30C: ; CODE XREF: update?+477j
- if (rightTile->tile != LevelTileTypeRedDisk)
- {
- return position;
- }
- // loc_4E8E3: ; CODE XREF: update?+480j
- // dx = 0x109E;
- murphyTile->state = 0x23;
- rightTile->state = 3;
-
- // loc_4E8F0: ; CODE XREF: update?+4DAj update?+4F7j ...
- gMurphyCounterToStartPushAnimation = 0;
- gIsMurphyGoingThroughPortal = 0;
- return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[42]);
- }
- else
- {
- // loc_4E790: ; CODE XREF: update?+395j update?+479j
- // push si
- // mov di, [si+6155h]
- // si = word_51846;
- drawMovingFrame(192, 16, position);
- // pop si
- if (gAreYellowDisksDetonated != 0)
- {
- gMurphyYawnAndSleepCounter = 0xA;
- return position;
- }
-
- // loc_4E7AB: ; CODE XREF: update?+912j
- // push si
- // mov di, [si+6157h]
- // si = kTerminalOnSpriteCoordinates;
- drawMovingFrame(256, 388, position + 1);
- // pop si
-
- detonateYellowDisks();
- return position;
- }
- }
- // loc_4E09C: ; CODE XREF: update?+202j
- else if (userInput == UserInputSpaceOnly)
- {
- // loc_4E314: ; CODE XREF: update?+211j
- if (gNumberOfRemainingRedDisks == 0 || gPlantedRedDiskCountdown != 0 || gPreviousUserInputWasNone != 1)
- {
- return position;
- }
- murphyTile->state = 0x2A;
- gMurphyCounterToStartPushAnimation = 0x40; // 64
- // dx = 0x110E;
- gPlantedRedDiskCountdown = 1;
- gPlantedRedDiskPosition = position;
- return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[49]);
- }
- else
- {
- // loc_4E0A4: ; CODE XREF: update?+20Fj
- gPreviousUserInputWasNone = 0;
- return position;
- }
-}
-
-int16_t handleMurphyDirectionUp(int16_t position)
-{
- // 01ED:7447
- StatefulLevelTile *murphyTile = &gCurrentLevelState[position];
- StatefulLevelTile *aboveTile = &gCurrentLevelState[position - kLevelWidth];
- StatefulLevelTile *aboveAboveTile = &gCurrentLevelState[position - kLevelWidth * 2];
-
- // loc_4E0AA: ; CODE XREF: update?+1AFj update?+279j
- if (aboveTile->state == 0 && aboveTile->tile == LevelTileTypeSpace)
- {
- MurphyAnimationDescriptor animationDescriptor;
-
- // loc_4E344: ; CODE XREF: update?+223j
- if (gIsMurphyLookingLeft != 0)
- {
- animationDescriptor = kMurphyAnimationDescriptors[0]; // dx = 0x0DFE;
- }
- else
- {
- // loc_4E350: ; CODE XREF: update?+4B9j
- animationDescriptor = kMurphyAnimationDescriptors[1]; // dx = 0x0E0E;
- }
-
- // loc_4E353: ; CODE XREF: update?+4BEj
- aboveTile->state = 1;
- aboveTile->tile = LevelTileTypeMurphy;
- murphyTile->state = 3;
- murphyTile->tile = LevelTileTypeSpace;
-
- gMurphyCounterToStartPushAnimation = 0;
- gIsMurphyGoingThroughPortal = 0;
- return updateMurphyAnimationInfo(position - kLevelWidth, animationDescriptor);
- }
- // loc_4E0B6: ; CODE XREF: update?+221j
- else if (aboveTile->state == 0 && aboveTile->tile == LevelTileTypeBase)
- {
- MurphyAnimationDescriptor unknownMurphyData;
-
- // loc_4E3E1: ; CODE XREF: update?+22Bj
- playBaseSound();
- if (gIsMurphyLookingLeft != 0)
- {
- unknownMurphyData = kMurphyAnimationDescriptors[7]; // dx = 0x0E6E;
- }
- else
- {
- // loc_4E3F0: ; CODE XREF: update?+559j
- unknownMurphyData = kMurphyAnimationDescriptors[8]; // dx = 0x0E7E;
- }
-
- // loc_4E3F3: ; CODE XREF: update?+55Ej
-
- aboveTile->state = 5;
- aboveTile->tile = LevelTileTypeMurphy;
- murphyTile->state = 3;
- murphyTile->tile = LevelTileTypeSpace;
- gMurphyCounterToStartPushAnimation = 0;
- gIsMurphyGoingThroughPortal = 0;
- return updateMurphyAnimationInfo(position - kLevelWidth, unknownMurphyData);
- }
- // loc_4E0BE: ; CODE XREF: update?+229j
- else if (aboveTile->tile == LevelTileTypeBug)
- {
- // loc_4E3D0: ; CODE XREF: update?+232j
- // cmp byte ptr [si+17BDh], 0
- // jl short loc_4E3DB
- if (aboveTile->state < 0x80)
- {
- detonateBigExplosion(position);
- return position;
- }
-
- // loc_4E3DB: ; CODE XREF: update?+545j
- aboveTile->state = 0;
- aboveTile->tile = LevelTileTypeBase;
-
- // loc_4E3E1: ; CODE XREF: update?+22Bj
- MurphyAnimationDescriptor animationDescriptor;
- playBaseSound();
- if (gIsMurphyLookingLeft != 0)
- {
- animationDescriptor = kMurphyAnimationDescriptors[7]; // dx = 0x0E6E;
- }
- else
- {
- // loc_4E3F0: ; CODE XREF: update?+559j
- animationDescriptor = kMurphyAnimationDescriptors[8]; // dx = 0x0E7E;
- }
-
- // loc_4E3F3: ; CODE XREF: update?+55Ej
-
- aboveTile->state = 5;
- aboveTile->tile = LevelTileTypeMurphy;
- murphyTile->state = 3;
- murphyTile->tile = LevelTileTypeSpace;
- gMurphyCounterToStartPushAnimation = 0;
- gIsMurphyGoingThroughPortal = 0;
- return updateMurphyAnimationInfo(position - kLevelWidth, animationDescriptor);
- }
- // loc_4E0C5: ; CODE XREF: update?+230j
- else if (aboveTile->state == 0 && aboveTile->tile == LevelTileTypeInfotron)
- {
- MurphyAnimationDescriptor unknownMurphyData;
-
- // loc_4E55C: ; CODE XREF: update?+23Aj
- playInfotronSound();
- if (gIsMurphyLookingLeft != 0)
- {
- unknownMurphyData = kMurphyAnimationDescriptors[17]; // dx = 0x0F0E;
- }
- else
- {
- // loc_4E56B: ; CODE XREF: update?+6D4j
- unknownMurphyData = kMurphyAnimationDescriptors[18]; // dx = 0x0F1E;
- }
-
- // loc_4E56E: ; CODE XREF: update?+6D9j
- aboveTile->state = 9;
- aboveTile->tile = LevelTileTypeMurphy;
- murphyTile->state = 3;
- murphyTile->tile = LevelTileTypeSpace;
- gMurphyCounterToStartPushAnimation = 0;
- gIsMurphyGoingThroughPortal = 0;
- return updateMurphyAnimationInfo(position - kLevelWidth, unknownMurphyData);
- }
- // loc_4E0CD: ; CODE XREF: update?+238j
- else if (aboveTile->state == 0 && aboveTile->tile == LevelTileTypeExit)
- {
- // loc_4E674: ; CODE XREF: update?+242j update?+2AAj ...
- if (gNumberOfRemainingInfotrons != 0)
- {
- return position;
- }
- playExitSound();
- byte_5A19B = 1;
- gCurrentPlayerLevelState = PlayerLevelStateCompleted;
- gLevelFailed = 0;
- if (gHasUserCheated == 0 && gShouldUpdateTotalLevelTime != 0)
- {
- byte_5A323 = 1;
- addCurrentGameTimeToPlayer();
- }
-
- // loc_4E6A4: ; CODE XREF: update?+803j update?+80Aj
- changePlayerCurrentLevelState();
- gQuitLevelCountdown = 0x40;
- // pop si
- // dx = 0x0E5E;
- murphyTile->state = 0xD;
- gMurphyCounterToStartPushAnimation = 0;
- gIsMurphyGoingThroughPortal = 0;
- return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[6]);
- }
- // loc_4E0D5: ; CODE XREF: update?+240j
- else if (aboveTile->tile == LevelTileTypeTerminal)
- {
- // loc_4E712: ; CODE XREF: update?+249j update?+3EFj
- // 01ED:7AAF
- // push si
- // mov di, [si+6155h]
- // si = word_51840;
- drawMovingFrame(160, 64, position);
- // pop si
- if (gAreYellowDisksDetonated != 0)
- {
- gMurphyYawnAndSleepCounter = 0xA;
- return position;
- }
-
- // loc_4E72D: ; CODE XREF: update?+894j
- // push si
- // mov di, [si+60DDh]
- // si = kTerminalOnSpriteCoordinates;
- drawMovingFrame(256, 388, position - kLevelWidth);
- // pop si
- detonateYellowDisks();
- return position;
- }
- // loc_4E0DC: ; CODE XREF: update?+247j
- else if (aboveTile->tile == LevelTileTypePortUp || aboveTile->tile == LevelTileTypePortVertical || aboveTile->tile == LevelTileTypePort4Way)
- {
- // loc_4E7DE: ; CODE XREF: update?+250j update?+257j ...
- if (aboveAboveTile->state != 0 || aboveAboveTile->tile != LevelTileTypeSpace)
- {
- return position;
- }
-
- // loc_4E7E6: ; CODE XREF: update?+953j
- // dx = 0x0FCE;
- murphyTile->state = 0x18;
- aboveAboveTile->state = 3;
- gMurphyCounterToStartPushAnimation = 0;
- gIsMurphyGoingThroughPortal = 1;
- return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[29]);
- }
- // loc_4E0F1: ; CODE XREF: update?+25Cj
- else if (aboveTile->tile == LevelTileTypeRedDisk)
- {
- MurphyAnimationDescriptor unknownMurphyData;
-
- // loc_4E847: ; CODE XREF: update?+265j
- if (gIsMurphyLookingLeft != 0)
- {
- unknownMurphyData = kMurphyAnimationDescriptors[33]; // dx = 0x100E;
- }
- else
- {
- // loc_4E853: ; CODE XREF: update?+9BCj
- unknownMurphyData = kMurphyAnimationDescriptors[34]; // dx = 0x101E;
- }
-
- // loc_4E856: ; CODE XREF: update?+9C1j
- murphyTile->state = 0x1C;
- aboveTile->state = 3;
- gMurphyCounterToStartPushAnimation = 0;
- gIsMurphyGoingThroughPortal = 0;
- return updateMurphyAnimationInfo(position, unknownMurphyData);
- }
- // loc_4E0F8: ; CODE XREF: update?+263j
- else if (aboveTile->tile == LevelTileTypeYellowDisk)
- {
- // loc_4E8F9: ; CODE XREF: update?+26Cj
- if (aboveAboveTile->state != 0 || aboveAboveTile->tile != LevelTileTypeSpace)
- {
- return position;
- }
-
- // loc_4E903: ; CODE XREF: update?+A70j
- aboveAboveTile->state = 0x12;
- // push si
- // mov di, [si+6155h]
- // si = word_5157C;
- drawMovingFrame(97, 132, position);
- // pop si
- // dx = 0x10AE;
- murphyTile->state = 0x24;
- gMurphyCounterToStartPushAnimation = 8;
- gIsMurphyGoingThroughPortal = 0;
- return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[43]);
- }
- // loc_4E0FF: ; CODE XREF: update?+26Aj
- else if (checkMurphyMovementToPosition(position - kLevelWidth, UserInputUp) != 1)
- {
- return handleMurphyDirectionUp(position);
- }
- else
- {
- return position;
- }
-}
-
-int16_t handleMurphyDirectionLeft(int16_t position)
-{
- // 01ED:74A9
- StatefulLevelTile *murphyTile = &gCurrentLevelState[position];
- StatefulLevelTile *leftLeftTile = &gCurrentLevelState[position - 2];
- StatefulLevelTile *leftTile = &gCurrentLevelState[position - 1];
-
- // loc_4E10C: ; CODE XREF: update?+1BBj update?+2F3j
- gIsMurphyLookingLeft = 1;
- // mov ax, [si+1832h]
- if (leftTile->state == 0 && leftTile->tile == LevelTileTypeSpace)
- {
- // loc_4E36D: ; CODE XREF: update?+28Bj
- // dx = 0x0E1E;
- leftTile->state = 2;
- leftTile->tile = LevelTileTypeMurphy;
- murphyTile->state = 3;
- murphyTile->tile = LevelTileTypeSpace;
- gMurphyCounterToStartPushAnimation = 0;
- gIsMurphyGoingThroughPortal = 0;
- return updateMurphyAnimationInfo(position - 1, kMurphyAnimationDescriptors[2]);
- }
- // loc_4E11E: ; CODE XREF: update?+289j
- else if (leftTile->state == 0 && leftTile->tile == LevelTileTypeBase) // 01ED:7634
- {
- // loc_4E41E: ; CODE XREF: update?+293j
- // 01ED:77BB
- playBaseSound();
- // dx = 0x0E8E;
- leftTile->state = 2;
- leftTile->tile = LevelTileTypeMurphy;
- murphyTile->state = 3;
- murphyTile->tile = LevelTileTypeSpace;
- gMurphyCounterToStartPushAnimation = 0;
- gIsMurphyGoingThroughPortal = 0;
- return updateMurphyAnimationInfo(position - 1, kMurphyAnimationDescriptors[9]);
- }
- // loc_4E126: ; CODE XREF: update?+291j
- else if (leftTile->tile == LevelTileTypeBug) // 01ED:763B
- {
- // loc_4E40D: ; CODE XREF: update?+29Aj
- if (leftTile->state < 0x80)
- {
- detonateBigExplosion(position);
- return position;
- }
-
- // loc_4E418: ; CODE XREF: update?+582j
- // 01ED:77B5
- leftTile->state = 0;
- leftTile->tile = LevelTileTypeBase;
-
- // loc_4E41E: ; CODE XREF: update?+293j
- playBaseSound();
- // dx = 0x0E8E;
- leftTile->state = 2;
- leftTile->tile = LevelTileTypeMurphy;
- murphyTile->state = 3;
- murphyTile->tile = LevelTileTypeSpace;
- gMurphyCounterToStartPushAnimation = 0;
- gIsMurphyGoingThroughPortal = 0;
- return updateMurphyAnimationInfo(position - 1, kMurphyAnimationDescriptors[9]);
- }
- // loc_4E12D: ; CODE XREF: update?+298j
- else if (leftTile->state == 0 && leftTile->tile == LevelTileTypeInfotron)
- {
- // loc_4E588: ; CODE XREF: update?+2A2j
- playInfotronSound();
- // dx = 0x0F2E;
- leftTile->state = 10;
- leftTile->tile = LevelTileTypeMurphy;
- murphyTile->state = 3;
- murphyTile->tile = LevelTileTypeSpace;
- gMurphyCounterToStartPushAnimation = 0;
- gIsMurphyGoingThroughPortal = 0;
- return updateMurphyAnimationInfo(position - 1, kMurphyAnimationDescriptors[19]);
- }
- // loc_4E135: ; CODE XREF: update?+2A0j
- else if (leftTile->state == 0 && leftTile->tile == LevelTileTypeExit)
- {
- // loc_4E674: ; CODE XREF: update?+242j update?+2AAj ...
- if (gNumberOfRemainingInfotrons != 0)
- {
- return position;
- }
- playExitSound();
- byte_5A19B = 1;
- gCurrentPlayerLevelState = PlayerLevelStateCompleted;
- gLevelFailed = 0;
- if (gHasUserCheated == 0 && gShouldUpdateTotalLevelTime != 0)
- {
- byte_5A323 = 1;
- addCurrentGameTimeToPlayer();
- }
-
- // loc_4E6A4: ; CODE XREF: update?+803j update?+80Aj
- changePlayerCurrentLevelState();
- gQuitLevelCountdown = 0x40;
- // pop si
- // dx = 0x0E5E;
- murphyTile->state = 0xD;
- gMurphyCounterToStartPushAnimation = 0;
- gIsMurphyGoingThroughPortal = 0;
- return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[6]);
- }
- // loc_4E13D: ; CODE XREF: update?+2A8j
- else if (leftTile->state == 0 && leftTile->tile == LevelTileTypeZonk)
- {
- // loc_4E6BA: ; CODE XREF: update?+2B2j
- // mov ax, [si+1830h]
- if (leftLeftTile->state != 0 || leftLeftTile->tile != LevelTileTypeSpace)
- {
- return position;
- }
-
- // loc_4E6C4: ; CODE XREF: update?+831j
- leftLeftTile->state = 1;
- // push si
- // mov di, [si+6155h]
- // si = word_5157A;
- drawMovingFrame(64, 132, position);
- // pop si
- // dx = 0x0FAE;
- murphyTile->state = 0xE;
- gMurphyCounterToStartPushAnimation = 8;
- gIsMurphyGoingThroughPortal = 0;
- return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[27]);
- }
- // loc_4E145: ; CODE XREF: update?+2B0j
- else if (leftTile->tile == LevelTileTypeTerminal)
- {
- // loc_4E73C: ; CODE XREF: update?+2B9j update?+41Fj
- // push si
- // mov di, [si+6155h]
- // si = word_51842;
- drawMovingFrame(208, 16, position);
- // pop si
- if (gAreYellowDisksDetonated != 0)
- {
- gMurphyYawnAndSleepCounter = 0xA;
- return position;
- }
-
- // loc_4E757: ; CODE XREF: update?+8BEj
- // push si
- // mov di, [si+6153h]
- // si = kTerminalOnSpriteCoordinates;
- drawMovingFrame(256, 388, position - 1);
- // pop si
- detonateYellowDisks();
- return position;
- }
- // loc_4E14C: ; CODE XREF: update?+2B7j
- else if (leftTile->tile == LevelTileTypePortLeft || leftTile->tile == LevelTileTypePortHorizontal || leftTile->tile == LevelTileTypePort4Way)
- {
- // loc_4E7F5: ; CODE XREF: update?+2C0j update?+2C7j ...
- if (leftLeftTile->state != 0 || leftLeftTile->tile != LevelTileTypeSpace)
- {
- return position;
- }
-
- // loc_4E7FD: ; CODE XREF: update?+96Aj
- // dx = 0x0FDE;
- murphyTile->state = 0x19;
- leftLeftTile->state = 3;
- gMurphyCounterToStartPushAnimation = 0;
- gIsMurphyGoingThroughPortal = 1;
- return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[30]);
- }
- // loc_4E161: ; CODE XREF: update?+2CCj
- else if (leftTile->state == 0 && leftTile->tile == LevelTileTypeRedDisk)
- {
- // loc_4E863: ; CODE XREF: update?+2D6j
- // dx = 0x102E;
- leftTile->state = 0x1D;
- leftTile->tile = LevelTileTypeMurphy;
- murphyTile->state = 3;
- murphyTile->tile = LevelTileTypeSpace;
- gMurphyCounterToStartPushAnimation = 0;
- gIsMurphyGoingThroughPortal = 0;
- return updateMurphyAnimationInfo(position - 1, kMurphyAnimationDescriptors[35]);
- }
- // loc_4E169: ; CODE XREF: update?+2D4j
- else if (leftTile->state == 0 && leftTile->tile == LevelTileTypeYellowDisk)
- {
- // loc_4E920: ; CODE XREF: update?+2DEj
- if (leftLeftTile->state != 0 || leftLeftTile->tile != LevelTileTypeSpace)
- {
- return position;
- }
-
- // loc_4E92A: ; CODE XREF: update?+A97j
- leftLeftTile->state = 0x12;
- // push si
- // mov di, [si+6155h]
- // si = word_5157A;
- drawMovingFrame(64, 132, position);
- // pop si
- // dx = 0x10BE;
- murphyTile->state = 0x25;
- gMurphyCounterToStartPushAnimation = 8;
- gIsMurphyGoingThroughPortal = 0;
- return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[44]);
- }
- // loc_4E171: ; CODE XREF: update?+2DCj
- else if (leftTile->state == 0 && leftTile->tile == LevelTileTypeOrangeDisk)
- {
- // loc_4E993: ; CODE XREF: update?+2E6j
- if (leftLeftTile->state != 0 || leftLeftTile->tile != LevelTileTypeSpace)
- {
- return position;
- }
-
- // loc_4E99D: ; CODE XREF: update?+B0Aj
- leftLeftTile->state = 8;
- // push si
- // mov di, [si+6155h]
- // si = word_5157A;
- drawMovingFrame(64, 132, position);
- // pop si
- // dx = 0x10EE;
- murphyTile->state = 0x28;
- gMurphyCounterToStartPushAnimation = 8;
- gIsMurphyGoingThroughPortal = 0;
- return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[47]);
- }
- // loc_4E179: ; CODE XREF: update?+2E4j
- else if (checkMurphyMovementToPosition(position - 1, UserInputLeft) != 1)
- {
- return handleMurphyDirectionLeft(position);
- }
- else
- {
- return position;
- }
-}
-
-int16_t handleMurphyDirectionDown(int16_t position)
-{
- StatefulLevelTile *murphyTile = &gCurrentLevelState[position];
- StatefulLevelTile *belowTile = &gCurrentLevelState[position + kLevelWidth];
- StatefulLevelTile *belowBelowTile = &gCurrentLevelState[position + kLevelWidth * 2];
-
- // loc_4E186: ; CODE XREF: update?+1C8j update?+355j
- // mov ax, leveldata[si+78h]
- if (belowTile->state == 0 && belowTile->tile == LevelTileTypeSpace)
- {
- MurphyAnimationDescriptor unknownMurphyData;
-
- // loc_4E38A: ; CODE XREF: update?+69j update?+2FFj
- if (gIsMurphyLookingLeft != 0)
- {
- unknownMurphyData = kMurphyAnimationDescriptors[3]; // dx = 0x0E2E;
- }
- else
- {
- // loc_4E396: ; CODE XREF: update?+4FFj
- unknownMurphyData = kMurphyAnimationDescriptors[4]; // dx = 0x0E3E;
- }
-
- // loc_4E399: ; CODE XREF: update?+504j
- belowTile->state = 3;
- belowTile->tile = LevelTileTypeMurphy;
- murphyTile->state = 3;
- murphyTile->tile = LevelTileTypeSpace;
- gMurphyCounterToStartPushAnimation = 0;
- gIsMurphyGoingThroughPortal = 0;
- return updateMurphyAnimationInfo(position + kLevelWidth, unknownMurphyData);
- }
- // loc_4E192: ; CODE XREF: update?+2FDj
- else if (belowTile->state == 0 && belowTile->tile == LevelTileTypeBase)
- {
- MurphyAnimationDescriptor unknownMurphyData;
-
- // loc_4E44F: ; CODE XREF: update?+307j
- playBaseSound();
- if (gIsMurphyLookingLeft != 0)
- {
- unknownMurphyData = kMurphyAnimationDescriptors[10]; // dx = 0x0E9E;
- }
- else
- {
- // loc_4E45E: ; CODE XREF: update?+5C7j
- unknownMurphyData = kMurphyAnimationDescriptors[11]; // dx = 0x0EAE;
- }
-
- // loc_4E461: ; CODE XREF: update?+5CCj
- belowTile->state = 7;
- belowTile->tile = LevelTileTypeMurphy;
- murphyTile->state = 3;
- murphyTile->tile = LevelTileTypeSpace;
- gMurphyCounterToStartPushAnimation = 0;
- gIsMurphyGoingThroughPortal = 0;
- return updateMurphyAnimationInfo(position + kLevelWidth, unknownMurphyData);
- }
- // loc_4E19A: ; CODE XREF: update?+305j
- else if (belowTile->tile == LevelTileTypeBug)
- {
- // loc_4E43E: ; CODE XREF: update?+30Ej
- if (belowTile->state < 0x80)
- {
- detonateBigExplosion(position);
- return position;
- }
-
- // loc_4E449: ; CODE XREF: update?+5B3j
- belowTile->state = 0;
- belowTile->tile = LevelTileTypeBase;
-
- MurphyAnimationDescriptor unknownMurphyData;
-
- // loc_4E44F: ; CODE XREF: update?+307j
- playBaseSound();
- if (gIsMurphyLookingLeft != 0)
- {
- unknownMurphyData = kMurphyAnimationDescriptors[10]; // dx = 0x0E9E;
- }
- else
- {
- // loc_4E45E: ; CODE XREF: update?+5C7j
- unknownMurphyData = kMurphyAnimationDescriptors[11]; // dx = 0x0EAE;
- }
-
- // loc_4E461: ; CODE XREF: update?+5CCj
- belowTile->state = 7;
- belowTile->tile = LevelTileTypeMurphy;
- murphyTile->state = 3;
- murphyTile->tile = LevelTileTypeSpace;
- gMurphyCounterToStartPushAnimation = 0;
- gIsMurphyGoingThroughPortal = 0;
- return updateMurphyAnimationInfo(position + kLevelWidth, unknownMurphyData);
- }
- // loc_4E1A1: ; CODE XREF: update?+30Cj
- else if (belowTile->tile == LevelTileTypeInfotron)
- {
- MurphyAnimationDescriptor unknownMurphyData;
-
- // loc_4E5A8: ; CODE XREF: update?+316j
- playInfotronSound();
- if (gIsMurphyLookingLeft != 0)
- {
- unknownMurphyData = kMurphyAnimationDescriptors[20]; // dx = 0x0F3E;
- }
- else
- {
- // loc_4E5B7: ; CODE XREF: update?+720j
- unknownMurphyData = kMurphyAnimationDescriptors[21]; // dx = 0x0F4E;
- }
-
- // loc_4E5BA: ; CODE XREF: update?+725j
- belowTile->state = 11;
- belowTile->tile = LevelTileTypeMurphy;
- murphyTile->state = 3;
- murphyTile->tile = LevelTileTypeBase;
- gMurphyCounterToStartPushAnimation = 0;
- gIsMurphyGoingThroughPortal = 0;
- return updateMurphyAnimationInfo(position + kLevelWidth, unknownMurphyData);
- }
- // loc_4E1A9: ; CODE XREF: update?+314j
- else if (belowTile->tile == LevelTileTypeExit)
- {
- // loc_4E674: ; CODE XREF: update?+242j update?+2AAj ...
- if (gNumberOfRemainingInfotrons != 0)
- {
- return position;
- }
- playExitSound();
- byte_5A19B = 1;
- gCurrentPlayerLevelState = PlayerLevelStateCompleted;
- gLevelFailed = 0;
- if (gHasUserCheated == 0 && gShouldUpdateTotalLevelTime != 0)
- {
- byte_5A323 = 1;
- addCurrentGameTimeToPlayer();
- }
-
- // loc_4E6A4: ; CODE XREF: update?+803j update?+80Aj
- changePlayerCurrentLevelState();
- gQuitLevelCountdown = 0x40;
- // pop si
- // dx = 0x0E5E;
- murphyTile->state = 0xD;
- gMurphyCounterToStartPushAnimation = 0;
- gIsMurphyGoingThroughPortal = 0;
- return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[6]);
- }
- // loc_4E1B1: ; CODE XREF: update?+31Cj
- else if (belowTile->tile == LevelTileTypeTerminal)
- {
- // loc_4E766: ; CODE XREF: update?+325j update?+449j
- // push si
- // mov di, [si+6155h]
- // si = word_51844;
- drawMovingFrame(176, 64, position);
- // pop si
- if (gAreYellowDisksDetonated != 0)
- {
- gMurphyYawnAndSleepCounter = 0xA;
- return position;
- }
-
- // loc_4E781: ; CODE XREF: update?+8E8j
- // push si
- // mov di, [si+61CDh]
- // si = kTerminalOnSpriteCoordinates;
- drawMovingFrame(256, 388, position + kLevelWidth);
- // pop si
- detonateYellowDisks();
- return position;
- }
- // loc_4E1B8: ; CODE XREF: update?+323j
- else if (belowTile->tile == LevelTileTypePortDown || belowTile->tile == LevelTileTypePortVertical || belowTile->tile == LevelTileTypePort4Way)
- {
- // loc_4E80C: ; CODE XREF: update?+32Cj update?+333j ...
- if (belowBelowTile->state != 0 || belowBelowTile->tile != LevelTileTypeSpace)
- {
- return position;
- }
-
- // loc_4E814: ; CODE XREF: update?+981j
- // dx = 0x0FEE;
- murphyTile->state = 0x1A;
- belowBelowTile->state = 3;
- gMurphyCounterToStartPushAnimation = 0;
- gIsMurphyGoingThroughPortal = 1;
- return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[31]);
- }
- // loc_4E1CD: ; CODE XREF: update?+338j
- else if (belowTile->tile == LevelTileTypeRedDisk)
- {
- MurphyAnimationDescriptor unknownMurphyData;
-
- // loc_4E87F: ; CODE XREF: update?+341j
- if (gIsMurphyLookingLeft != 0)
- {
- unknownMurphyData = kMurphyAnimationDescriptors[36]; // dx = 0x103E;
- }
- else
- {
- // loc_4E88B: ; CODE XREF: update?+9F4j
- unknownMurphyData = kMurphyAnimationDescriptors[37]; // dx = 0x104E;
- }
-
- // loc_4E88E: ; CODE XREF: update?+9F9j
- murphyTile->state = 0x1E;
- belowTile->state = 3;
- gMurphyCounterToStartPushAnimation = 0;
- gIsMurphyGoingThroughPortal = 0;
- return updateMurphyAnimationInfo(position, unknownMurphyData);
- }
- // loc_4E1D4: ; CODE XREF: update?+33Fj
- else if (belowTile->tile == LevelTileTypeYellowDisk)
- {
- // loc_4E947: ; CODE XREF: update?+348j
- if (belowBelowTile->state != 0 || belowBelowTile->tile != LevelTileTypeSpace)
- {
- return position;
- }
-
- // loc_4E951: ; CODE XREF: update?+ABEj
- belowBelowTile->state = 0x12;
- // push si
- // mov di, [si+6155h]
- // si = word_5157C;
- drawMovingFrame(97, 132, position);
- // pop si
- // dx = 0x10CE;
- murphyTile->state = 0x27;
- gMurphyCounterToStartPushAnimation = 8;
- gIsMurphyGoingThroughPortal = 0;
- return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[45]);
- }
- // loc_4E1DB: ; CODE XREF: update?+346j
- else if (checkMurphyMovementToPosition(position + kLevelWidth, UserInputDown) != 1)
- {
- return handleMurphyDirectionDown(position);
- }
- else
- {
- return position;
- }
-}
-
-int16_t handleMurphyDirectionRight(int16_t position)
-{
- StatefulLevelTile *murphyTile = &gCurrentLevelState[position];
- StatefulLevelTile *rightTile = &gCurrentLevelState[position + 1];
- StatefulLevelTile *rightRightTile = &gCurrentLevelState[position + 2];
- StatefulLevelTile *belowRightTile = &gCurrentLevelState[position + kLevelWidth + 1];
-
- // loc_4E1E8: ; CODE XREF: update?+1D5j update?+3CDj
- gIsMurphyLookingLeft = 0;
- // mov ax, leveldata[si+2]
- if (rightTile->state == 0 && rightTile->tile == LevelTileTypeSpace)
- {
- // loc_4E3B3: ; CODE XREF: update?+367j
- // dx = 0x0E4E;
- rightTile->state = 4;
- rightTile->tile = LevelTileTypeMurphy;
- murphyTile->state = 3;
- murphyTile->tile = LevelTileTypeSpace;
- gMurphyCounterToStartPushAnimation = 0;
- gIsMurphyGoingThroughPortal = 0;
- return updateMurphyAnimationInfo(position + 1, kMurphyAnimationDescriptors[5]);
- }
- // loc_4E1FA: ; CODE XREF: update?+365j
- else if (rightTile->state == 0 && rightTile->tile == LevelTileTypeBase)
- {
- // loc_4E48C: ; CODE XREF: update?+36Fj
- playBaseSound();
- // dx = 0x0EBE;
- rightTile->state = 8;
- rightTile->tile = LevelTileTypeMurphy;
- murphyTile->state = 3;
- murphyTile->tile = LevelTileTypeSpace;
- gMurphyCounterToStartPushAnimation = 0;
- gIsMurphyGoingThroughPortal = 0;
- return updateMurphyAnimationInfo(position + 1, kMurphyAnimationDescriptors[12]);
- }
- // loc_4E202: ; CODE XREF: update?+36Dj
- else if (rightTile->tile == LevelTileTypeBug)
- {
- // loc_4E47B: ; CODE XREF: update?+376j
- if (rightTile->state < 0x80)
- {
- detonateBigExplosion(position);
- return position;
- }
-
- // loc_4E486: ; CODE XREF: update?+5F0j
- rightTile->state = 0;
- rightTile->tile = LevelTileTypeBase;
-
- // loc_4E48C: ; CODE XREF: update?+36Fj
- playBaseSound();
- // dx = 0x0EBE;
- rightTile->state = 8;
- rightTile->tile = LevelTileTypeMurphy;
- murphyTile->state = 3;
- murphyTile->tile = LevelTileTypeSpace;
- gMurphyCounterToStartPushAnimation = 0;
- gIsMurphyGoingThroughPortal = 0;
- return updateMurphyAnimationInfo(position + 1, kMurphyAnimationDescriptors[12]);
- }
- // loc_4E209: ; CODE XREF: update?+374j
- else if (rightTile->state == 0 && rightTile->tile == LevelTileTypeInfotron)
- {
- // loc_4E5D4: ; CODE XREF: update?+37Ej
- playInfotronSound();
- // dx = 0x0F5E;
- rightTile->state = 12;
- rightTile->tile = LevelTileTypeMurphy;
- murphyTile->state = 3;
- murphyTile->tile = LevelTileTypeSpace;
- gMurphyCounterToStartPushAnimation = 0;
- gIsMurphyGoingThroughPortal = 0;
- return updateMurphyAnimationInfo(position + 1, kMurphyAnimationDescriptors[22]);
- }
- // loc_4E211: ; CODE XREF: update?+37Cj
- else if (rightTile->state == 0 && rightTile->tile == LevelTileTypeExit)
- {
- // loc_4E674: ; CODE XREF: update?+242j update?+2AAj ...
- if (gNumberOfRemainingInfotrons != 0)
- {
- return position;
- }
- playExitSound();
- byte_5A19B = 1;
- gCurrentPlayerLevelState = PlayerLevelStateCompleted;
- gLevelFailed = 0;
- if (gHasUserCheated == 0 && gShouldUpdateTotalLevelTime != 0)
- {
- byte_5A323 = 1;
- addCurrentGameTimeToPlayer();
- }
-
- // loc_4E6A4: ; CODE XREF: update?+803j update?+80Aj
- changePlayerCurrentLevelState();
- gQuitLevelCountdown = 0x40;
- murphyTile->state = 0xD;
- gMurphyCounterToStartPushAnimation = 0;
- gIsMurphyGoingThroughPortal = 0;
- return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[6]);
- }
- // loc_4E219: ; CODE XREF: update?+384j
- else if (rightTile->state == 0 && rightTile->tile == LevelTileTypeZonk)
- {
- // loc_4E6E1: ; CODE XREF: update?+38Ej
- // mov ax, [si+1838h]
- if (rightRightTile->state != 0 || rightRightTile->tile != LevelTileTypeSpace)
- {
- return position;
- }
-
- // loc_4E6EB: ; CODE XREF: update?+858j
- // mov ax, [si+18AEh]
- if (belowRightTile->state == 0 && belowRightTile->tile == LevelTileTypeSpace)
- {
- return position;
- }
-
- // loc_4E6F5: ; CODE XREF: update?+862j
- rightRightTile->state = 1;
- // push si
- // mov di, [si+6155h]
- // si = word_5157C;
- drawMovingFrame(97, 132, position);
- // pop si
- // dx = 0x0FBE;
- murphyTile->state = 0xF;
- gMurphyCounterToStartPushAnimation = 8;
- gIsMurphyGoingThroughPortal = 0;
- return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[28]);
- }
- // loc_4E221: ; CODE XREF: update?+38Cj
- else if (rightTile->tile == LevelTileTypeTerminal)
- {
- // loc_4E790: ; CODE XREF: update?+395j update?+479j
- // push si
- // mov di, [si+6155h]
- // si = word_51846;
- drawMovingFrame(192, 16, position);
- // pop si
- if (gAreYellowDisksDetonated != 0)
- {
- gMurphyYawnAndSleepCounter = 0xA;
- return position;
- }
-
- // loc_4E7AB: ; CODE XREF: update?+912j
- // push si
- // mov di, [si+6157h]
- // si = kTerminalOnSpriteCoordinates;
- drawMovingFrame(256, 388, position + 1);
- // pop si
-
- detonateYellowDisks();
- return position;
- }
- // loc_4E228: ; CODE XREF: update?+393j
- else if (rightTile->tile == LevelTileTypePortRight || rightTile->tile == LevelTileTypePortHorizontal || rightTile->tile == LevelTileTypePort4Way)
- {
- // loc_4E823: ; CODE XREF: update?+39Cj update?+3A3j ...
- if (rightRightTile->state != 0 || rightRightTile->tile != LevelTileTypeSpace)
- {
- return position;
- }
-
- // loc_4E82B: ; CODE XREF: update?+998j
- // dx = 0x0FFE;
- murphyTile->state = 0x1B;
- rightRightTile->state = 3;
- gMurphyCounterToStartPushAnimation = 0;
- gIsMurphyGoingThroughPortal = 1;
- return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[32]);
- }
- // loc_4E23D: ; CODE XREF: update?+3A8j
- else if (rightTile->tile == LevelTileTypeRedDisk)
- {
- // loc_4E89A: ; CODE XREF: update?+3B1j
- // dx = 0x105E;
- rightTile->state = 0x1F;
- rightTile->tile = LevelTileTypeMurphy;
- murphyTile->state = 3;
- murphyTile->tile = LevelTileTypeSpace;
- gMurphyCounterToStartPushAnimation = 0;
- gIsMurphyGoingThroughPortal = 0;
- return updateMurphyAnimationInfo(position + 1, kMurphyAnimationDescriptors[38]);
- }
- // loc_4E244: ; CODE XREF: update?+3AFj
- else if (rightTile->tile == LevelTileTypeYellowDisk)
- {
- // loc_4E96D: ; CODE XREF: update?+3B8j
- if (rightRightTile->state != 0 || rightRightTile->tile != LevelTileTypeSpace)
- {
- return position;
- }
-
- // loc_4E977: ; CODE XREF: update?+AE4j
- rightRightTile->state = 0x12;
- // push si
- // mov di, [si+6155h]
- // si = word_5157C;
- drawMovingFrame(97, 132, position);
- // pop si
- // dx = 0x10DE;
- murphyTile->state = 0x26;
- gMurphyCounterToStartPushAnimation = 8;
- gIsMurphyGoingThroughPortal = 0;
- return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[46]);
- }
- // loc_4E24B: ; CODE XREF: update?+3B6j
- else if (rightTile->state == 0 && rightTile->tile == LevelTileTypeOrangeDisk)
- {
- // loc_4E9B9: ; CODE XREF: update?+3C0j
- if (rightRightTile->state != 0 || rightRightTile->tile != LevelTileTypeSpace)
- {
- return position;
- }
-
- // loc_4E9C3: ; CODE XREF: update?+B30j
- if (belowRightTile->state == 0 && belowRightTile->tile == LevelTileTypeSpace)
- {
- return position;
- }
-
- // loc_4E9CD: ; CODE XREF: update?+B3Aj
- rightRightTile->state = 1;
- // push si
- // mov di, [si+6155h]
- // si = word_5157C;
- drawMovingFrame(97, 132, position);
- // pop si
- // dx = 0x10FE;
- murphyTile->state = 0x29;
-
- // loc_4E9E7: ; CODE XREF: update?+84Ej update?+87Fj ...
- gMurphyCounterToStartPushAnimation = 8;
-
- // loc_4E9ED: ; CODE XREF: update?+A66j
- gIsMurphyGoingThroughPortal = 0;
-
- return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[48]);
- }
- // loc_4E253: ; CODE XREF: update?+3BEj
- else if (checkMurphyMovementToPosition(position + 1, UserInputRight) != 1)
- {
- return handleMurphyDirectionRight(position);
- }
- else
- {
- return position;
- }
-}
-
-int16_t updateMurphyAnimationInfo(int16_t position, MurphyAnimationDescriptor unknownMurphyData)
-{
- // 01ED:7D9F
-
- // loc_4E9F3: ; CODE XREF: update?+4B0j update?+9B4j
- // di = 0x0DE0;
- // memcpy(di, si, 7 * 2); // rep movsw
-
- gCurrentMurphyAnimation = unknownMurphyData;
-
- return updateMurphyAnimation(position);
-}
-
-int16_t updateMurphyAnimation(int16_t position)
-{
- // 01ED:7DA4
-
- StatefulLevelTile *murphyTile = &gCurrentLevelState[position];
- StatefulLevelTile *leftLeftTile = &gCurrentLevelState[position - 2];
- StatefulLevelTile *leftTile = &gCurrentLevelState[position - 1];
- StatefulLevelTile *rightTile = &gCurrentLevelState[position + 1];
- StatefulLevelTile *rightRightTile = &gCurrentLevelState[position + 2];
- StatefulLevelTile *belowTile = &gCurrentLevelState[position + kLevelWidth];
- StatefulLevelTile *belowBelowTile = &gCurrentLevelState[position + kLevelWidth * 2];
- StatefulLevelTile *aboveTile = &gCurrentLevelState[position - kLevelWidth];
- StatefulLevelTile *aboveAboveTile = &gCurrentLevelState[position - kLevelWidth * 2];
- StatefulLevelTile *belowRightRightTile = &gCurrentLevelState[position + kLevelWidth + 2];
-
- // loc_4EA07: ; CODE XREF: update?+21j
- gMurphyYawnAndSleepCounter = 0;
-
- if (gMurphyCounterToStartPushAnimation == 0)
- {
- // 01ED:7E08
- uint8_t currentFrame = gCurrentMurphyAnimation.currentFrame;
- AnimationFrameCoordinates animationFrameCoordinates = kMurphyAnimationFrameCoordinates[gCurrentMurphyAnimation.animationIndex];
- Point frameCoordinates = animationFrameCoordinates.coordinates[currentFrame];
-
- // loc_4EA6B: ; CODE XREF: update?+B83j
- gMurphyPositionX += gCurrentMurphyAnimation.speedX;
- gMurphyPositionY += gCurrentMurphyAnimation.speedY;
- gCurrentMurphyAnimation.currentFrame++;
-
- uint16_t dstX = (position % kLevelWidth) * kTileSize;
- uint16_t dstY = (position / kLevelWidth) * kTileSize;
-
- int16_t offsetX = (gCurrentMurphyAnimation.animationCoordinatesOffset % 122) * 8;
- int16_t offsetY = (gCurrentMurphyAnimation.animationCoordinatesOffset / 122);
-
- // loc_4EA9F: ; CODE XREF: update?+C28j
- drawMovingSpriteFrameInLevel(frameCoordinates.x,
- frameCoordinates.y,
- gCurrentMurphyAnimation.width * 8,
- gCurrentMurphyAnimation.height,
- dstX + offsetX,
- dstY + offsetY);
-
- if (gIsMurphyGoingThroughPortal != 0)
- {
- // This +1 is because the "opposite" portal animation is always the next one
- AnimationFrameCoordinates animationFrameCoordinates = kMurphyAnimationFrameCoordinates[gCurrentMurphyAnimation.animationIndex + 1];
- Point frameCoordinates = animationFrameCoordinates.coordinates[currentFrame];
-
- int16_t offsetX = (gCurrentMurphyAnimation.animationCoordinatesOffsetIncrement % 122) * 8;
- int16_t offsetY = (gCurrentMurphyAnimation.animationCoordinatesOffsetIncrement / 122);
-
- drawMovingSpriteFrameInLevel(frameCoordinates.x,
- frameCoordinates.y,
- gCurrentMurphyAnimation.width * 8,
- gCurrentMurphyAnimation.height,
- dstX + offsetX,
- dstY + offsetY);
- }
- else
- {
- // loc_4EAFA: ; CODE XREF: update?+C32j
- gCurrentMurphyAnimation.animationCoordinatesOffset += gCurrentMurphyAnimation.animationCoordinatesOffsetIncrement;
- }
-
- // loc_4EB04: ; CODE XREF: update?+C68j
- if (gCurrentMurphyAnimation.currentFrame < animationFrameCoordinates.numberOfCoordinates)
- {
- return position;
- }
-
- // loc_4EB10: ; CODE XREF: update?+C7Bj
- // 01ED:7EAD
- gMurphyTileX += gCurrentMurphyAnimation.speedX / 2;
- gMurphyTileY += gCurrentMurphyAnimation.speedY / 2;
- uint8_t previousMurphyMovingObject = murphyTile->state;
- murphyTile->state = 0;
- if (previousMurphyMovingObject == 1)
- {
- // loc_4EC93: ; CODE XREF: update?+CA3j update?+CC3j
- murphyTile->tile = LevelTileTypeMurphy;
- handleMurphyCollisionAfterMovement(position + kLevelWidth);
- return position;
- }
- // loc_4EB36: ; CODE XREF: update?+CA1j
- else if (previousMurphyMovingObject == 2)
- {
- // loc_4ECB1: ; CODE XREF: update?+CABj update?+CCBj
- murphyTile->tile = LevelTileTypeMurphy;
- handleMurphyCollisionAfterMovement(position + 1);
- return position;
- }
- // loc_4EB3E: ; CODE XREF: update?+CA9j
- else if (previousMurphyMovingObject == 3)
- {
- // loc_4ECCF: ; CODE XREF: update?+CB3j update?+CD3j
- if (aboveTile->tile != LevelTileTypeExplosion)
- {
- aboveTile->state = 0;
- aboveTile->tile = LevelTileTypeSpace;
- }
-
- // loc_4ECDC: ; CODE XREF: update?+E44j
- murphyTile->state = 0;
- murphyTile->tile = LevelTileTypeMurphy;
- return position;
- }
- // loc_4EB46: ; CODE XREF: update?+CB1j
- else if (previousMurphyMovingObject == 4)
- {
- // loc_4EF53: ; CODE XREF: update?+CBBj update?+CDBj
- handleMurphyCollisionAfterMovement(position - 1);
- murphyTile->state = 0;
- murphyTile->tile = LevelTileTypeMurphy;
- return position;
- }
- // loc_4EB4E: ; CODE XREF: update?+CB9j
- else if (previousMurphyMovingObject == 5)
- {
- // loc_4EC93: ; CODE XREF: update?+CA3j update?+CC3j
- murphyTile->tile = LevelTileTypeMurphy;
- handleMurphyCollisionAfterMovement(position + kLevelWidth);
- return position;
- }
- // loc_4EB56: ; CODE XREF: update?+CC1j
- else if (previousMurphyMovingObject == 6)
- {
- // loc_4ECB1: ; CODE XREF: update?+CABj update?+CCBj
- murphyTile->tile = LevelTileTypeMurphy;
- handleMurphyCollisionAfterMovement(position + 1);
- return position;
- }
- // loc_4EB5E: ; CODE XREF: update?+CC9j
- else if (previousMurphyMovingObject == 7)
- {
- // loc_4ECCF: ; CODE XREF: update?+CB3j update?+CD3j
- if (aboveTile->tile != LevelTileTypeExplosion)
- {
- aboveTile->state = 0;
- aboveTile->tile = LevelTileTypeSpace;
- }
-
- // loc_4ECDC: ; CODE XREF: update?+E44j
- murphyTile->state = 0;
- murphyTile->tile = LevelTileTypeMurphy;
- return position;
- }
- // loc_4EB66: ; CODE XREF: update?+CD1j
- else if (previousMurphyMovingObject == 8)
- {
- // loc_4EF53: ; CODE XREF: update?+CBBj update?+CDBj
- // 01ED:82F0
- handleMurphyCollisionAfterMovement(position - 1);
- murphyTile->state = 0;
- murphyTile->tile = LevelTileTypeMurphy;
- return position;
- }
- // loc_4EB6E: ; CODE XREF: update?+CD9j
- else if (previousMurphyMovingObject == 9)
- {
- // loc_4EC85: ; CODE XREF: update?+CE3j
- if (gNumberOfRemainingInfotrons > 0)
- {
- gNumberOfRemainingInfotrons--;
- }
-
- // loc_4EC90: ; CODE XREF: update?+DFAj
- drawNumberOfRemainingInfotrons();
-
- // loc_4EC93: ; CODE XREF: update?+CA3j update?+CC3j
- murphyTile->tile = LevelTileTypeMurphy;
- handleMurphyCollisionAfterMovement(position + kLevelWidth);
- return position;
- }
- // loc_4EB76: ; CODE XREF: update?+CE1j
- else if (previousMurphyMovingObject == 10)
- {
- // loc_4ECA3: ; CODE XREF: update?+CEBj
- if (gNumberOfRemainingInfotrons > 0)
- {
- gNumberOfRemainingInfotrons--;
- }
-
- // loc_4ECAE: ; CODE XREF: update?+E18j
- drawNumberOfRemainingInfotrons();
-
- // loc_4ECB1: ; CODE XREF: update?+CABj update?+CCBj
- murphyTile->tile = LevelTileTypeMurphy;
- handleMurphyCollisionAfterMovement(position + 1);
- return position;
- }
- // loc_4EB7E: ; CODE XREF: update?+CE9j
- else if (previousMurphyMovingObject == 11)
- {
- // loc_4ECC1: ; CODE XREF: update?+CF3j
- if (gNumberOfRemainingInfotrons > 0)
- {
- gNumberOfRemainingInfotrons--;
- }
-
- // loc_4ECCC: ; CODE XREF: update?+E36j
- drawNumberOfRemainingInfotrons();
-
- // loc_4ECCF: ; CODE XREF: update?+CB3j update?+CD3j
- if (aboveTile->tile != LevelTileTypeExplosion)
- {
- aboveTile->state = 0;
- aboveTile->tile = LevelTileTypeSpace;
- }
-
- // loc_4ECDC: ; CODE XREF: update?+E44j
- murphyTile->state = 0;
- murphyTile->tile = LevelTileTypeMurphy;
- return position;
- }
- // loc_4EB86: ; CODE XREF: update?+CF1j
- else if (previousMurphyMovingObject == 12)
- {
- // loc_4EF45: ; CODE XREF: update?+CFBj
- if (gNumberOfRemainingInfotrons > 0)
- {
- gNumberOfRemainingInfotrons--;
- }
-
- // loc_4EF50: ; CODE XREF: update?+10BAj
- drawNumberOfRemainingInfotrons();
-
- // loc_4EF53: ; CODE XREF: update?+CBBj update?+CDBj
- handleMurphyCollisionAfterMovement(position - 1);
- murphyTile->state = 0;
- murphyTile->tile = LevelTileTypeMurphy;
- return position;
- }
- // loc_4EB8E: ; CODE XREF: update?+CF9j
- else if (previousMurphyMovingObject == 14)
- {
- // loc_4ECE3: ; CODE XREF: update?+D03j
- if (murphyTile->tile != LevelTileTypeExplosion)
- {
- murphyTile->state = 0;
- murphyTile->tile = LevelTileTypeSpace;
- }
-
- // loc_4ECF0: ; CODE XREF: update?+E58j
- leftTile->state = 0;
- leftTile->tile = LevelTileTypeMurphy;
- leftLeftTile->state = 0;
- leftLeftTile->tile = LevelTileTypeZonk;
- handleZonkPushedByMurphy(position - 2);
- return position - 1;
- }
- // loc_4EB96: ; CODE XREF: update?+D01j
- else if (previousMurphyMovingObject == 15)
- {
- // loc_4ED06: ; CODE XREF: update?+D0Bj
- if (murphyTile->tile != LevelTileTypeExplosion)
- {
- murphyTile->state = 0;
- murphyTile->tile = LevelTileTypeSpace;
- }
-
- // loc_4ED13: ; CODE XREF: update?+E7Bj
- rightTile->state = 0;
- rightTile->tile = LevelTileTypeMurphy;
- rightRightTile->state = 0;
- rightRightTile->tile = LevelTileTypeZonk;
- handleZonkPushedByMurphy(position + 2);
- return position + 1;
- }
- // loc_4EB9E: ; CODE XREF: update?+D09j
- else if (previousMurphyMovingObject == 16)
- {
- // loc_4EF71: ; CODE XREF: update?+D13j
- if (aboveTile->tile != LevelTileTypeExplosion)
- {
- aboveTile->state = 0;
- aboveTile->tile = LevelTileTypeSpace;
- }
-
- return position;
- }
- // loc_4EBA6: ; CODE XREF: update?+D11j
- else if (previousMurphyMovingObject == 17)
- {
- // loc_4EF8D: ; CODE XREF: update?+D1Bj
- if (leftTile->tile != LevelTileTypeExplosion)
- {
- leftTile->state = 0;
- leftTile->tile = LevelTileTypeSpace;
- }
-
- return position;
- }
- // loc_4EBAE: ; CODE XREF: update?+D19j
- else if (previousMurphyMovingObject == 19)
- {
- // loc_4EFC5: ; CODE XREF: update?+D23j
- if (rightTile->tile != LevelTileTypeExplosion)
- {
- rightTile->state = 0;
- rightTile->tile = LevelTileTypeSpace;
- }
-
- return position;
- }
- // loc_4EBB6: ; CODE XREF: update?+D21j
- else if (previousMurphyMovingObject == 18)
- {
- // loc_4EFA9: ; CODE XREF: update?+D2Bj
- if (belowTile->tile != LevelTileTypeExplosion)
- {
- belowTile->state = 0;
- belowTile->tile = LevelTileTypeSpace;
- }
-
- return position;
- }
- // loc_4EBBE: ; CODE XREF: update?+D29j
- else if (previousMurphyMovingObject == 20)
- {
- // loc_4EF63: ; CODE XREF: update?+D33j
- if (gNumberOfRemainingInfotrons > 0)
- {
- gNumberOfRemainingInfotrons--;
- }
-
- // loc_4EF6E: ; CODE XREF: update?+10D8j
- drawNumberOfRemainingInfotrons();
-
- // loc_4EF71: ; CODE XREF: update?+D13j
- if (aboveTile->tile != LevelTileTypeExplosion)
- {
- aboveTile->state = 0;
- aboveTile->tile = LevelTileTypeSpace;
- }
-
- return position;
- }
- // loc_4EBC6: ; CODE XREF: update?+D31j
- else if (previousMurphyMovingObject == 21)
- {
- // loc_4EF7F: ; CODE XREF: update?+D3Bj
- if (gNumberOfRemainingInfotrons > 0)
- {
- gNumberOfRemainingInfotrons--;
- }
-
- // loc_4EF8A: ; CODE XREF: update?+10F4j
- drawNumberOfRemainingInfotrons();
-
- // loc_4EF8D: ; CODE XREF: update?+D1Bj
- if (leftTile->tile != LevelTileTypeExplosion)
- {
- leftTile->state = 0;
- leftTile->tile = LevelTileTypeSpace;
- }
-
- return position;
- }
- // loc_4EBCE: ; CODE XREF: update?+D39j
- else if (previousMurphyMovingObject == 23)
- {
- // loc_4EFB7: ; CODE XREF: update?+D43j
- if (gNumberOfRemainingInfotrons > 0)
- {
- gNumberOfRemainingInfotrons--;
- }
-
- // loc_4EFC2: ; CODE XREF: update?+112Cj
- drawNumberOfRemainingInfotrons();
-
- // loc_4EFC5: ; CODE XREF: update?+D23j
- if (rightTile->tile != LevelTileTypeExplosion)
- {
- rightTile->state = 0;
- rightTile->tile = LevelTileTypeSpace;
- }
-
- return position;
- }
- // loc_4EBD6: ; CODE XREF: update?+D41j
- else if (previousMurphyMovingObject == 22)
- {
- // loc_4EF9B: ; CODE XREF: update?+D4Bj
- if (gNumberOfRemainingInfotrons > 0)
- {
- gNumberOfRemainingInfotrons--;
- }
-
- // loc_4EFA6: ; CODE XREF: update?+1110j
- drawNumberOfRemainingInfotrons();
-
- // loc_4EFA9: ; CODE XREF: update?+D2Bj
- if (belowTile->tile != LevelTileTypeExplosion)
- {
- belowTile->state = 0;
- belowTile->tile = LevelTileTypeSpace;
- }
-
- return position;
- }
- // loc_4EBDE: ; CODE XREF: update?+D49j
- else if (previousMurphyMovingObject == 13)
- {
- // loc_4ED42: ; CODE XREF: update?+D53j
- gShouldExitLevel = 1;
- return position;
- }
- // loc_4EBE6: ; CODE XREF: update?+D51j
- else if (previousMurphyMovingObject == 24)
- {
- // loc_4EFD3: ; CODE XREF: update?+D5Bj
- if (murphyTile->tile != LevelTileTypeExplosion)
- {
- murphyTile->state = 0;
- murphyTile->tile = LevelTileTypeSpace;
- }
-
- // loc_4EFE0: ; CODE XREF: update?+1148j
- aboveAboveTile->state = 0;
- aboveAboveTile->tile = LevelTileTypeMurphy;
- gIsMurphyGoingThroughPortal = 0;
- position -= kLevelWidth * 2;
- if (aboveTile->state == 1)
- {
- updateSpecialPort(position + kLevelWidth);
- }
-
- return position;
- }
- // loc_4EBEE: ; CODE XREF: update?+D59j
- else if (previousMurphyMovingObject == 25)
- {
- // loc_4F001: ; CODE XREF: update?+D63j
- if (murphyTile->tile != LevelTileTypeExplosion)
- {
- murphyTile->state = 0;
- murphyTile->tile = LevelTileTypeSpace;
- }
-
- // loc_4F00E: ; CODE XREF: update?+1176j
- leftLeftTile->state = 0;
- leftLeftTile->tile = LevelTileTypeMurphy;
- gIsMurphyGoingThroughPortal = 0;
- position -= 2;
- if (leftTile->state == 1)
- {
- updateSpecialPort(position + 1);
- }
-
- return position;
- }
- // loc_4EBF6: ; CODE XREF: update?+D61j
- else if (previousMurphyMovingObject == 26)
- {
- // loc_4F02E: ; CODE XREF: update?+D6Bj
- if (murphyTile->tile != LevelTileTypeExplosion)
- {
- murphyTile->state = 0;
- murphyTile->tile = LevelTileTypeSpace;
- }
-
- // loc_4F03B: ; CODE XREF: update?+11A3j
- belowBelowTile->state = 0;
- belowBelowTile->tile = LevelTileTypeMurphy;
- gIsMurphyGoingThroughPortal = 0;
- position += kLevelWidth * 2;
- if (belowTile->state == 1)
- {
- updateSpecialPort(position - kLevelWidth);
- }
-
- return position;
- }
- // loc_4EBFE: ; CODE XREF: update?+D69j
- else if (previousMurphyMovingObject == 27)
- {
- // loc_4F05C: ; CODE XREF: update?+D73j
- if (murphyTile->tile != LevelTileTypeExplosion)
- {
- murphyTile->state = 0;
- murphyTile->tile = LevelTileTypeSpace;
- }
-
- // loc_4F069: ; CODE XREF: update?+11D1j
- rightRightTile->state = 0;
- rightRightTile->tile = LevelTileTypeMurphy;
- gIsMurphyGoingThroughPortal = 0;
- position += 2;
- if (rightTile->state == 1)
- {
- updateSpecialPort(position - 1);
- }
-
- return position;
- }
- // loc_4EC06: ; CODE XREF: update?+D71j
- else if (previousMurphyMovingObject == 28)
- {
- // loc_4F089: ; CODE XREF: update?+D7Bj
- if (murphyTile->tile != LevelTileTypeExplosion)
- {
- murphyTile->state = 0;
- murphyTile->tile = LevelTileTypeSpace;
- }
-
- // loc_4F096: ; CODE XREF: update?+11FEj
- position -= kLevelWidth;
-
- // loc_4FDAF: ; CODE XREF: update?+1209j
- // ; update?:loc_4F0A9j ...
- aboveTile->state = 0;
- aboveTile->tile = LevelTileTypeMurphy;
- decreaseRemainingRedDisksIfNeeded(position);
- return position;
- }
- // loc_4EC0E: ; CODE XREF: update?+D79j
- else if (previousMurphyMovingObject == 29)
- {
- // loc_4F09C: ; CODE XREF: update?+D83j
- if (rightTile->tile != LevelTileTypeExplosion)
- {
- rightTile->state = 0;
- rightTile->tile = LevelTileTypeSpace;
- }
-
- // loc_4FDAF: ; CODE XREF: update?+1209j
- // ; update?:loc_4F0A9j ...
- murphyTile->state = 0;
- murphyTile->tile = LevelTileTypeMurphy;
- decreaseRemainingRedDisksIfNeeded(position);
- return position;
- }
- // loc_4EC16: ; CODE XREF: update?+D81j
- else if (previousMurphyMovingObject == 30)
- {
- // loc_4F0AC: ; CODE XREF: update?+D8Bj
- if (murphyTile->tile != LevelTileTypeExplosion)
- {
- murphyTile->state = 0;
- murphyTile->tile = LevelTileTypeSpace;
- }
-
- // loc_4F0B9: ; CODE XREF: update?+1221j
- position += kLevelWidth;
-
- // loc_4FDAF: ; CODE XREF: update?+1209j
- // ; update?:loc_4F0A9j ...
- belowTile->state = 0;
- belowTile->tile = LevelTileTypeMurphy;
- decreaseRemainingRedDisksIfNeeded(position);
- return position;
- }
- // loc_4EC1E: ; CODE XREF: update?+D89j
- else if (previousMurphyMovingObject == 31)
- {
- // loc_4F0BF: ; CODE XREF: update?+D93j
- if (leftTile->tile != LevelTileTypeExplosion)
- {
- leftTile->state = 0;
- leftTile->tile = LevelTileTypeSpace;
- }
-
- // loc_4FDAF: ; CODE XREF: update?+1209j
- // ; update?:loc_4F0A9j ...
- murphyTile->state = 0;
- murphyTile->tile = LevelTileTypeMurphy;
- decreaseRemainingRedDisksIfNeeded(position);
- return position;
- }
- // loc_4EC26: ; CODE XREF: update?+D91j
- else if (previousMurphyMovingObject == 32)
- {
- // loc_4F0CF: ; CODE XREF: update?+D9Bj
- if (aboveTile->tile != LevelTileTypeExplosion)
- {
- aboveTile->state = 0;
- aboveTile->tile = LevelTileTypeSpace;
- }
-
- // loc_4F0DC: ; CODE XREF: update?+1244j
- decreaseRemainingRedDisksIfNeeded(position - kLevelWidth);
- return position;
- }
- // loc_4EC2E: ; CODE XREF: update?+D99j
- else if (previousMurphyMovingObject == 33)
- {
- // loc_4F0E6: ; CODE XREF: update?+DA3j
- if (leftTile->tile != LevelTileTypeExplosion)
- {
- leftTile->state = 0;
- leftTile->tile = LevelTileTypeSpace;
- }
-
- // loc_4F0F3: ; CODE XREF: update?+125Bj
- decreaseRemainingRedDisksIfNeeded(position - 1);
- return position;
- }
- // loc_4EC36: ; CODE XREF: update?+DA1j
- else if (previousMurphyMovingObject == 34)
- {
- // loc_4F0FD: ; CODE XREF: update?+DABj
- if (belowTile->tile != LevelTileTypeExplosion)
- {
- belowTile->state = 0;
- belowTile->tile = LevelTileTypeSpace;
- }
-
- // loc_4F10A: ; CODE XREF: update?+1272j
- decreaseRemainingRedDisksIfNeeded(position + kLevelWidth);
- return position;
- }
- // loc_4EC3E: ; CODE XREF: update?+DA9j
- else if (previousMurphyMovingObject == 35)
- {
- // loc_4F114: ; CODE XREF: update?+DB3j
- if (rightTile->tile != LevelTileTypeExplosion)
- {
- rightTile->state = 0;
- rightTile->tile = LevelTileTypeSpace;
- }
-
- // loc_4F121: ; CODE XREF: update?+1289j
- decreaseRemainingRedDisksIfNeeded(position + 1);
- return position;
- }
- // loc_4EC46: ; CODE XREF: update?+DB1j
- else if (previousMurphyMovingObject == 36)
- {
- // loc_4F12B: ; CODE XREF: update?+DBBj
- if (murphyTile->tile != LevelTileTypeExplosion)
- {
- murphyTile->state = 0;
- murphyTile->tile = LevelTileTypeSpace;
- }
-
- // loc_4F138: ; CODE XREF: update?+12A0j
- aboveTile->state = 0;
- aboveTile->tile = LevelTileTypeMurphy;
- aboveAboveTile->state = 0;
- aboveAboveTile->tile = LevelTileTypeYellowDisk;
- return position - kLevelWidth;
- }
- // loc_4EC4E: ; CODE XREF: update?+DB9j
- else if (previousMurphyMovingObject == 37)
- {
- // loc_4F148: ; CODE XREF: update?+DC3j
- if (murphyTile->tile != LevelTileTypeExplosion)
- {
- murphyTile->state = 0;
- murphyTile->tile = LevelTileTypeSpace;
- }
-
- // loc_4F155: ; CODE XREF: update?+12BDj
- leftTile->state = 0;
- leftTile->tile = LevelTileTypeMurphy;
- leftLeftTile->state = 0;
- leftLeftTile->tile = LevelTileTypeYellowDisk;
- return position - 1;
- }
- // loc_4EC56: ; CODE XREF: update?+DC1j
- else if (previousMurphyMovingObject == 39)
- {
- // loc_4F165: ; CODE XREF: update?+DCBj
- if (murphyTile->tile != LevelTileTypeExplosion)
- {
- murphyTile->state = 0;
- murphyTile->tile = LevelTileTypeSpace;
- }
-
- // loc_4F172: ; CODE XREF: update?+12DAj
- belowTile->state = 0;
- belowTile->tile = LevelTileTypeMurphy;
- belowBelowTile->state = 0;
- belowBelowTile->tile = LevelTileTypeYellowDisk;
- return position + kLevelWidth;
- }
- // loc_4EC5E: ; CODE XREF: update?+DC9j
- else if (previousMurphyMovingObject == 38)
- {
- // loc_4F182: ; CODE XREF: update?+DD3j
- if (murphyTile->tile != LevelTileTypeExplosion)
- {
- murphyTile->state = 0;
- murphyTile->tile = LevelTileTypeSpace;
- }
-
- // loc_4F18F: ; CODE XREF: update?+12F7j
- rightTile->state = 0;
- rightTile->tile = LevelTileTypeMurphy;
- rightRightTile->state = 0;
- rightRightTile->tile = LevelTileTypeYellowDisk;
- return position + 1;
- }
- // loc_4EC66: ; CODE XREF: update?+DD1j
- else if (previousMurphyMovingObject == 40)
- {
- // loc_4F19F: ; CODE XREF: update?+DDBj
- if (murphyTile->tile != LevelTileTypeExplosion)
- {
- murphyTile->state = 0;
- murphyTile->tile = LevelTileTypeSpace;
- }
-
- // loc_4F1AC: ; CODE XREF: update?+1314j
- leftTile->state = 0;
- leftTile->tile = LevelTileTypeMurphy;
- leftLeftTile->state = 0;
- leftLeftTile->tile = LevelTileTypeOrangeDisk;
- return position - 1;
- }
- // loc_4EC6E: ; CODE XREF: update?+DD9j
- else if (previousMurphyMovingObject == 41)
- {
- // loc_4F1BC: ; CODE XREF: update?+DE3j
- if (murphyTile->tile != LevelTileTypeExplosion)
- {
- murphyTile->state = 0;
- murphyTile->tile = LevelTileTypeSpace;
- }
-
- // loc_4F1C9: ; CODE XREF: update?+1331j
- rightTile->state = 0;
- rightTile->tile = LevelTileTypeMurphy;
- rightRightTile->state = 0;
- rightRightTile->tile = LevelTileTypeOrangeDisk;
- if (belowRightRightTile->state == 0 && belowRightRightTile->tile == LevelTileTypeSpace)
- {
- rightRightTile->state = 0x20;
- belowRightRightTile->state = 8;
- }
-
- // loc_4F1E6: ; CODE XREF: update?+134Aj
- return position + 1;
- }
- // loc_4EC76: ; CODE XREF: update?+DE1j
- else if (previousMurphyMovingObject == 42)
- {
- // loc_4F1EA: ; CODE XREF: update?+DEBj
- murphyTile->state = 0;
- murphyTile->tile = LevelTileTypeMurphy;
- gPlantedRedDiskCountdown = 2;
- gNumberOfRemainingRedDisks--;
- drawNumberOfRemainingRedDisks();
- playPushSound();
- return position;
- }
- else
- {
- // loc_4EC7E: ; CODE XREF: update?+DE9j
- gShouldExitLevel = 1;
- return position;
- }
- }
-
- gMurphyCounterToStartPushAnimation--;
- if (gMurphyCounterToStartPushAnimation == 0)
- {
- playPushSound();
- }
-
- // loc_4EA1E: ; CODE XREF: update?+B89j
- if (murphyTile->state == 0xE)
- {
- // loc_4ED49: ; CODE XREF: update?+B97j
- if (gCurrentUserInput == UserInputLeft && (leftTile->state == 0 && leftTile->tile == LevelTileTypeZonk))
- {
- return position;
- }
-
- // loc_4ED5A: ; CODE XREF: update?+EC0j update?+EC7j
- murphyTile->state = 0;
- murphyTile->tile = LevelTileTypeMurphy;
- leftTile->state = 0;
- leftTile->tile = LevelTileTypeZonk;
- if (leftLeftTile->tile != LevelTileTypeExplosion)
- {
- leftLeftTile->state = 0;
- leftLeftTile->tile = LevelTileTypeSpace;
- }
-
- // loc_4ED73: ; CODE XREF: update?+EDBj
- // si = kMurphyStillSpriteCoordinates;
- drawMovingFrame(304, 132, position);
-
- return position;
- }
- // loc_4EA2A: ; CODE XREF: update?+B95j
- else if (murphyTile->state == 0xF)
- {
- // loc_4ED81: ; CODE XREF: update?+B9Fj
- if (gCurrentUserInput == UserInputRight && (rightTile->state == 0 && rightTile->tile == LevelTileTypeZonk))
- {
- return position;
- }
-
- // loc_4ED92: ; CODE XREF: update?+EF8j update?+EFFj
- murphyTile->state = 0;
- murphyTile->tile = LevelTileTypeMurphy;
- rightTile->state = 0;
- rightTile->tile = LevelTileTypeZonk;
- if (rightRightTile->tile != LevelTileTypeExplosion)
- {
- rightRightTile->state = 0;
- rightRightTile->tile = LevelTileTypeSpace;
- }
-
- // loc_4EDAB: ; CODE XREF: update?+F13j
- // si = kMurphyStillSpriteCoordinates;
- drawMovingFrame(304, 132, position);
-
- return position;
- }
- // loc_4EA32: ; CODE XREF: update?+B9Dj
- else if (murphyTile->state == 0x28)
- {
- // loc_4EDB9: ; CODE XREF: update?+BA7j
- if (gCurrentUserInput == UserInputLeft && (leftTile->state == 0 && leftTile->tile == LevelTileTypeOrangeDisk))
- {
- return position;
- }
-
- // loc_4EDCA: ; CODE XREF: update?+F30j update?+F37j
- murphyTile->state = 0;
- murphyTile->tile = LevelTileTypeMurphy;
- leftTile->state = 0;
- leftTile->tile = LevelTileTypeOrangeDisk;
- if (leftLeftTile->tile != LevelTileTypeExplosion)
- {
- leftLeftTile->state = 0;
- leftLeftTile->tile = LevelTileTypeSpace;
- }
-
- // loc_4EDE3: ; CODE XREF: update?+F4Bj
- // si = kMurphyStillSpriteCoordinates;
- drawMovingFrame(304, 132, position);
-
- return position;
- }
- // loc_4EA3A: ; CODE XREF: update?+BA5j
- else if (murphyTile->state == 0x29)
- {
- // loc_4EDF1: ; CODE XREF: update?+BAFj
- if (gCurrentUserInput == UserInputRight && (rightTile->state == 0 && rightTile->tile == LevelTileTypeOrangeDisk))
- {
- return position;
- }
-
- // loc_4EE02: ; CODE XREF: update?+F68j update?+F6Fj
- murphyTile->state = 0;
- murphyTile->tile = LevelTileTypeMurphy;
- rightTile->state = 0;
- rightTile->tile = LevelTileTypeOrangeDisk;
- if (rightRightTile->tile != LevelTileTypeExplosion)
- {
- rightRightTile->state = 0;
- rightRightTile->tile = LevelTileTypeSpace;
- }
-
- // loc_4EE1B: ; CODE XREF: update?+F83j
- // si = kMurphyStillSpriteCoordinates;
- drawMovingFrame(304, 132, position);
-
- return position;
- }
- // loc_4EA42: ; CODE XREF: update?+BADj
- else if (murphyTile->state == 0x24)
- {
- // loc_4EE29: ; CODE XREF: update?+BB7j
- if (gCurrentUserInput == UserInputUp && (aboveTile->state == 0 && aboveTile->tile == LevelTileTypeYellowDisk))
- {
- return position;
- }
-
- // loc_4EE3A: ; CODE XREF: update?+FA0j update?+FA7j
- murphyTile->state = 0;
- murphyTile->tile = LevelTileTypeMurphy;
- aboveTile->state = 0;
- aboveTile->tile = LevelTileTypeYellowDisk;
- if (aboveAboveTile->tile != LevelTileTypeExplosion)
- {
- aboveAboveTile->state = 0;
- aboveAboveTile->tile = LevelTileTypeSpace;
- }
-
- // loc_4EE53: ; CODE XREF: update?+FBBj
- // si = kMurphyStillSpriteCoordinates;
- drawMovingFrame(304, 132, position);
-
- return position;
- }
- // loc_4EA4A: ; CODE XREF: update?+BB5j
- else if (murphyTile->state == 0x25)
- {
- // loc_4EE61: ; CODE XREF: update?+BBFj
- if (gCurrentUserInput == UserInputLeft && (leftTile->state == 0 && leftTile->tile == LevelTileTypeYellowDisk))
- {
- return position;
- }
-
- // loc_4EE72: ; CODE XREF: update?+FD8j update?+FDFj
- murphyTile->state = 0;
- murphyTile->tile = LevelTileTypeMurphy;
- leftTile->state = 0;
- leftTile->tile = LevelTileTypeYellowDisk;
- if (leftLeftTile->tile != LevelTileTypeExplosion)
- {
- leftLeftTile->state = 0;
- leftLeftTile->tile = LevelTileTypeSpace;
- }
-
- // loc_4EE8B: ; CODE XREF: update?+FF3j
- // si = kMurphyStillSpriteCoordinates;
- drawMovingFrame(304, 132, position);
-
- return position;
- }
- // loc_4EA52: ; CODE XREF: update?+BBDj
- else if (murphyTile->state == 0x27)
- {
- // loc_4EE99: ; CODE XREF: update?+BC7j
- if (gCurrentUserInput == UserInputDown && (belowTile->state == 0 && belowTile->tile == LevelTileTypeYellowDisk))
- {
- return position;
- }
-
- // loc_4EEAA: ; CODE XREF: update?+1010j
- // ; update?+1017j
- murphyTile->state = 0;
- murphyTile->tile = LevelTileTypeMurphy;
- belowTile->state = 0;
- belowTile->tile = LevelTileTypeYellowDisk;
- if (belowBelowTile->tile != LevelTileTypeExplosion)
- {
- belowBelowTile->state = 0;
- belowBelowTile->tile = LevelTileTypeSpace;
- }
-
- // loc_4EEC3: ; CODE XREF: update?+102Bj
- // si = kMurphyStillSpriteCoordinates;
- drawMovingFrame(304, 132, position);
-
- return position;
- }
- // loc_4EA5A: ; CODE XREF: update?+BC5j
- else if (murphyTile->state == 0x26)
- {
- // loc_4EED1: ; CODE XREF: update?+BCFj
- if (gCurrentUserInput == UserInputRight && (rightTile->state == 0 && rightTile->tile == LevelTileTypeYellowDisk))
- {
- return position;
- }
-
- // loc_4EEE2: ; CODE XREF: update?+1048j
- // ; update?+104Fj
- murphyTile->state = 0;
- murphyTile->tile = LevelTileTypeMurphy;
- rightTile->state = 0;
- rightTile->tile = LevelTileTypeYellowDisk;
- if (rightRightTile->tile != LevelTileTypeExplosion)
- {
- rightRightTile->state = 0;
- rightRightTile->tile = LevelTileTypeSpace;
- }
-
- // loc_4EEFB: ; CODE XREF: update?+1063j
- // si = kMurphyStillSpriteCoordinates;
- drawMovingFrame(304, 132, position);
-
- return position;
- }
- // loc_4EA62: ; CODE XREF: update?+BCDj
- else if (murphyTile->state == 0x2A)
- {
- // loc_4EF09: ; CODE XREF: update?+BD7j
- if (gCurrentUserInput == UserInputSpaceOnly)
- {
- if (gMurphyCounterToStartPushAnimation > 0x20)
- {
- return position;
- }
-
- // si = word_51790;
- drawMovingFrame(288, 132, position);
- gPlantedRedDiskCountdown = 1;
-
- return position;
- }
-
- // loc_4EF2C: ; CODE XREF: update?+1080j
- murphyTile->state = 0;
- murphyTile->tile = LevelTileTypeMurphy;
-
- // si = kMurphyStillSpriteCoordinates;
- drawMovingFrame(304, 132, position);
- gPlantedRedDiskCountdown = 0;
-
- return position;
- }
- else
- {
- return position;
- }
-}
-
-void detonateYellowDisks()
-{
- // loc_4E7B8: ; CODE XREF: update?+8AAj update?+8D4j ...
- gTerminalMaxFramesToNextScroll = 7;
- gAreYellowDisksDetonated = 1;
-
- for (int i = 0; i < kLevelSize; ++i)
- {
- // loc_4E7C9: ; CODE XREF: update?+94Aj
- StatefulLevelTile *tile = &gCurrentLevelState[i];
- if (tile->state == 0 && tile->tile == LevelTileTypeYellowDisk)
- {
- detonateBigExplosion(i);
- }
- }
-}
-
-void handleZonkPushedByMurphy(int16_t position) // sub_4ED29 proc near ; CODE XREF: update?+E6Fp update?+E92p
-{
- StatefulLevelTile *belowTile = &gCurrentLevelState[position + kLevelWidth];
-
- if (belowTile->tile == LevelTileTypeSnikSnak || belowTile->tile == 0xBB)
- {
- // loc_4ED38: ; CODE XREF: handleZonkPushedByMurphy+5j handleZonkPushedByMurphy+Cj
- detonateBigExplosion(position + kLevelWidth);
- }
-}
-
-uint8_t checkMurphyMovementToPosition(int16_t position, UserInput userInput) // sub_4F21F proc near ; CODE XREF: update?+273p update?+2EDp ...
-{
- // 01ED:85BC
- // Parameters:
- // - si: position
- // - ax: value of that position (state + tile)
- // - bl: user input to process
- StatefulLevelTile *tile = &gCurrentLevelState[position];
-
- if ((tile->state == 0xFF && tile->tile == 0xFF) || (tile->state == 0xAA && tile->tile == 0xAA) || (tile->state == 0))
- {
- // loc_4F296: ; CODE XREF: checkMurphyMovementToPosition+3j checkMurphyMovementToPosition+8j ...
- return 1;
- }
- else if (tile->tile == LevelTileTypeZonk)
- {
- // loc_4F24F: ; CODE XREF: checkMurphyMovementToPosition+11j
- if (userInput == UserInputLeft)
- {
- // loc_4F25E: ; CODE XREF: checkMurphyMovementToPosition+33j
- uint8_t stateType = (tile->state & 0xF0);
- if (stateType == 0x20 || stateType == 0x40 || stateType == 0x50 || stateType == 0x70)
- {
- // loc_4F278: ; CODE XREF: checkMurphyMovementToPosition+45j
- // ; checkMurphyMovementToPosition+4Aj ...
- return 1;
- }
- detonateBigExplosion(position);
-
- // loc_4F278: ; CODE XREF: checkMurphyMovementToPosition+45j
- // ; checkMurphyMovementToPosition+4Aj ...
- return 1;
- }
- else if (userInput != UserInputRight)
- {
- detonateBigExplosion(position);
- return 1;
- }
-
- // loc_4F27A: ; CODE XREF: checkMurphyMovementToPosition+38j
- uint8_t stateType = (tile->state & 0xF0);
- if (stateType == 0x30 || stateType == 0x40 || stateType == 0x60 || stateType == 0x70)
- {
- // loc_4F294: ; CODE XREF: checkMurphyMovementToPosition+61j
- // ; checkMurphyMovementToPosition+66j ...
- return 1;
- }
- detonateBigExplosion(position);
-
- // loc_4F294: ; CODE XREF: checkMurphyMovementToPosition+61j
- // ; checkMurphyMovementToPosition+66j ...
- return 1;
- }
- else if (tile->tile == LevelTileTypeExplosion)
- {
- // loc_4F298: ; CODE XREF: checkMurphyMovementToPosition+15j
- // 01ED:8635
- if ((tile->state & 0x80) != 0 || tile->state < 4)
- {
- // loc_4F2A2: ; CODE XREF: checkMurphyMovementToPosition+7Cj
- detonateBigExplosion(position);
- return 1;
- }
- else
- {
- // loc_4F2A7: ; CODE XREF: checkMurphyMovementToPosition+81j
- tile->state = 0;
- tile->tile = LevelTileTypeSpace;
- return 0;
- }
- }
- else if (tile->tile == LevelTileTypeOrangeDisk || tile->tile == LevelTileTypePortRight || tile->tile == LevelTileTypePortDown || tile->tile == LevelTileTypePortLeft || tile->tile == LevelTileTypePortUp)
- {
- // loc_4F296: ; CODE XREF: checkMurphyMovementToPosition+3j checkMurphyMovementToPosition+8j ...
- return 1;
- }
- else
- {
- detonateBigExplosion(position);
- return 1;
- }
-}
-
-void updateSpecialPort(int16_t position) // sub_4F2AF proc near ; CODE XREF: update?+116Ap
- // ; update?+1197p ...
-{
- // 01ED:864C
- if (gNumberOfSpecialPorts == 0)
- {
- return;
- }
-
- uint8_t isPortInPosition = 0;
- uint8_t portIndex = 0;
-
- for (uint8_t i = 0; i < gNumberOfSpecialPorts; ++i)
- {
- // loc_4F2BD: ; CODE XREF: updateSpecialPort+19j
- SpecialPortInfo portInfo = gCurrentLevel.specialPortsInfo[i];
- // For _reasons_ the port position has its bytes inverted (first high, then low), so we must reverse them
- uint16_t portPosition = swap16(portInfo.position);
- portPosition /= 2; // We must divide by 2 because the level format works with words
-
- if (portPosition == position)
- {
- isPortInPosition = 1;
- portIndex = i;
- break;
- }
- }
-
- if (isPortInPosition == 0)
- {
- return;
- }
-
- // loc_4F2CB: ; CODE XREF: updateSpecialPort+14j
- SpecialPortInfo portInfo = gCurrentLevel.specialPortsInfo[portIndex];
- gIsGravityEnabled = portInfo.gravity;
- gAreZonksFrozen = portInfo.freezeZonks;
- gAreEnemiesFrozen = portInfo.freezeEnemies;
- // I still don't know where word_510AC is read :fearful:
- // I tried with a breakpoint on memory read and it was never accessed :shrug:
- // I can probably forget about it...
- // word_510AC = word_510AC ^ gRandomGeneratorSeed;
-}
-
-void updateSnikSnakTiles(int16_t position) // movefun4 proc near ; DATA XREF: data:162Co
-{
- // 01ED:868D
- if (gAreEnemiesFrozen == 1)
- {
- return;
- }
-
- StatefulLevelTile *currentTile = &gCurrentLevelState[position];
-
- if (currentTile->tile != LevelTileTypeSnikSnak)
- {
- return;
- }
-
- uint8_t frame = currentTile->state;
-
- FrameBasedMovingFunction function = kSnikSnakMovingFunctions[frame];
-
- if (function != NULL)
- {
- // 01ED:86AC
- function(position, frame);
- }
-}
-
-void updateSnikSnakTurnLeft(int16_t position, uint8_t frame) // sub_4F312 proc near ; DATA XREF: data:movingFunctions3o
-{
- // 01ED:86AF
- StatefulLevelTile *currentTile = &gCurrentLevelState[position];
- StatefulLevelTile *aboveTile = &gCurrentLevelState[position - kLevelWidth];
- StatefulLevelTile *leftTile = &gCurrentLevelState[position - 1];
- StatefulLevelTile *belowTile = &gCurrentLevelState[position + kLevelWidth];
- StatefulLevelTile *rightTile = &gCurrentLevelState[position + 1];
-
- uint16_t value = gFrameCounter & 3;
-
- if (value == 0)
- {
- // loc_4F320: ; CODE XREF: updateSnikSnakTurnLeft+6j
- Point frameCoordinates = kSnikSnakAnimationFrameCoordinates[frame];
- drawMovingFrame(frameCoordinates.x, frameCoordinates.y, position);
- frame++;
- frame &= 7;
- currentTile->state = frame;
- return;
- }
- else if (value != 3)
- {
- return;
- }
-
- // loc_4F362: ; CODE XREF: updateSnikSnakTurnLeft+Bj
- uint8_t state = currentTile->state;
- uint8_t nextMovingObject = 0;
-
- if (state == 0)
- {
- // loc_4F37B: ; CODE XREF: updateSnikSnakTurnLeft+57j
- if (aboveTile->state == 0 && aboveTile->tile == LevelTileTypeSpace)
- {
- // loc_4F38E: ; CODE XREF: updateSnikSnakTurnLeft+6Ej
- currentTile->state = 0x1;
- currentTile->tile = 0xBB;
- aboveTile->state = 0x10;
- aboveTile->tile = LevelTileTypeSnikSnak;
- return;
- }
- nextMovingObject = aboveTile->state;
- if (aboveTile->tile != LevelTileTypeMurphy)
- {
- return;
- }
- }
- else if (state == 2)
- {
- // loc_4F39E: ; CODE XREF: updateSnikSnakTurnLeft+5Cj
- if (leftTile->state == 0 && leftTile->tile == LevelTileTypeSpace)
- {
- // loc_4F3B1: ; CODE XREF: updateSnikSnakTurnLeft+91j
- currentTile->state = 0x2;
- currentTile->tile = 0xBB;
- leftTile->state = 0x18;
- leftTile->tile = LevelTileTypeSnikSnak;
- return;
- }
- nextMovingObject = leftTile->state;
- if (leftTile->tile != LevelTileTypeMurphy)
- {
- return;
- }
- }
- else if (state == 4)
- {
- // loc_4F3C1: ; CODE XREF: updateSnikSnakTurnLeft+61j
- if (belowTile->state == 0 && belowTile->tile == LevelTileTypeSpace)
- {
- // loc_4F3D7: ; CODE XREF: updateSnikSnakTurnLeft+B4j
- currentTile->state = 0x3;
- currentTile->tile = 0xBB;
- belowTile->state = 0x20;
- belowTile->tile = LevelTileTypeSnikSnak;
- return;
- }
- nextMovingObject = belowTile->state;
- if (belowTile->tile != LevelTileTypeMurphy)
- {
- return;
- }
- }
- else if (state == 6)
- {
- // loc_4F3E7: ; CODE XREF: updateSnikSnakTurnLeft+66j
- if (rightTile->state == 0 && rightTile->tile == LevelTileTypeSpace)
- {
- // loc_4F3FD: ; CODE XREF: updateSnikSnakTurnLeft+DAj
- currentTile->state = 0x4;
- currentTile->tile = 0xBB;
- rightTile->state = 0x28;
- rightTile->tile = LevelTileTypeSnikSnak;
- return;
- }
- nextMovingObject = rightTile->state;
- if (rightTile->tile != LevelTileTypeMurphy)
- {
- return;
- }
- }
- else
- {
- return;
- }
-
- // loc_4F34A: ; CODE XREF: updateSnikSnakTurnLeft+79j
- // ; updateSnikSnakTurnLeft+9Cj ...
- if (nextMovingObject != 0x1B && nextMovingObject != 0x19 && nextMovingObject != 0x18 && nextMovingObject != 0x1A)
- {
- detonateBigExplosion(position);
- }
-}
-
-void updateSnikSnakTurnRight(int16_t position, uint8_t frame) // sub_4F40D proc near ; DATA XREF: data:155Ao
-{
- // 01ED:87AA
- StatefulLevelTile *currentTile = &gCurrentLevelState[position];
- StatefulLevelTile *aboveTile = &gCurrentLevelState[position - kLevelWidth];
- StatefulLevelTile *rightTile = &gCurrentLevelState[position + 1];
- StatefulLevelTile *leftTile = &gCurrentLevelState[position - 1];
- StatefulLevelTile *belowTile = &gCurrentLevelState[position + kLevelWidth];
-
- uint16_t value = gFrameCounter & 3;
-
- if (value == 0)
- {
- // loc_4F41B: ; CODE XREF: updateSnikSnakTurnRight+6j
- Point frameCoordinates = kSnikSnakAnimationFrameCoordinates[frame];
- drawMovingFrame(frameCoordinates.x, frameCoordinates.y, position);
- frame++;
- frame &= 7;
- frame |= 8;
- currentTile->state = frame;
- return;
- }
- else if (value != 3)
- {
- return;
- }
-
- // loc_4F45F: ; CODE XREF: updateSnikSnakTurnRight+Bj
- uint8_t state = currentTile->state;
- uint8_t nextMovingObject = 0;
- if (state == 8)
- {
- // loc_4F478: ; CODE XREF: updateSnikSnakTurnRight+59j
- if (aboveTile->state == 0 && aboveTile->tile == LevelTileTypeSpace)
- {
- // loc_4F48B: ; CODE XREF: updateSnikSnakTurnRight+70j
- currentTile->state = 0x1;
- currentTile->tile = 0xBB;
- aboveTile->state = 0x10;
- aboveTile->tile = LevelTileTypeSnikSnak;
- return;
- }
- nextMovingObject = aboveTile->state;
- if (aboveTile->tile != LevelTileTypeMurphy)
- {
- return;
- }
- }
- else if (state == 0xA)
- {
- // loc_4F4E4: ; CODE XREF: updateSnikSnakTurnRight+5Ej
- if (rightTile->state == 0 && rightTile->tile == LevelTileTypeSpace)
- {
- // loc_4F4FA: ; CODE XREF: updateSnikSnakTurnLeft+DAj
- currentTile->state = 0x4;
- currentTile->tile = 0xBB;
- rightTile->state = 0x28;
- rightTile->tile = LevelTileTypeSnikSnak;
- return;
- }
- nextMovingObject = rightTile->state;
- if (rightTile->tile != LevelTileTypeMurphy)
- {
- return;
- }
- }
- else if (state == 0xC)
- {
- // loc_4F4BE: ; CODE XREF: updateSnikSnakTurnRight+63j
- if (belowTile->state == 0 && belowTile->tile == LevelTileTypeSpace)
- {
- // loc_4F4D4: ; CODE XREF: updateSnikSnakTurnRight+B6j
- currentTile->state = 0x3;
- currentTile->tile = 0xBB;
- belowTile->state = 0x20;
- belowTile->tile = LevelTileTypeSnikSnak;
- return;
- }
- nextMovingObject = belowTile->state;
- if (belowTile->tile != LevelTileTypeMurphy)
- {
- return;
- }
- }
- else if (state == 0xE)
- {
- // loc_4F49B: ; CODE XREF: updateSnikSnakTurnRight+68j
- if (leftTile->state == 0 && leftTile->tile == LevelTileTypeSpace)
- {
- // loc_4F4AE: ; CODE XREF: updateSnikSnakTurnRight+93j
- currentTile->state = 0x2;
- currentTile->tile = 0xBB;
- leftTile->state = 0x18;
- leftTile->tile = LevelTileTypeSnikSnak;
- return;
- }
- nextMovingObject = leftTile->state;
- if (leftTile->tile != LevelTileTypeMurphy)
- {
- return;
- }
- }
- else
- {
- return;
- }
-
- // loc_4F447: ; CODE XREF: updateSnikSnakTurnRight+7Bj
- // ; updateSnikSnakTurnRight+9Ej ...
- if (nextMovingObject != 0x1B && nextMovingObject != 0x19 && nextMovingObject != 0x18 && nextMovingObject != 0x1A)
- {
- detonateBigExplosion(position);
- }
-}
-
-void updateSnikSnakMovementUp(int16_t position, uint8_t frame) // sub_4F50A proc near ; DATA XREF: data:156Ao
-{
- // 01ED:88A7
- StatefulLevelTile *currentTile = &gCurrentLevelState[position];
- StatefulLevelTile *belowTile = &gCurrentLevelState[position + kLevelWidth];
- StatefulLevelTile *leftTile = &gCurrentLevelState[position - 1];
- StatefulLevelTile *aboveTile = &gCurrentLevelState[position - kLevelWidth];
- StatefulLevelTile *rightTile = &gCurrentLevelState[position + 1];
-
- uint16_t finalPosition = position + kLevelWidth;
- Point frameCoordinates = kSnikSnakAnimationFrameCoordinates[frame];
- // sub bx, 1Eh
- frame -= 15; // 0x1E / 2
- uint16_t offset = kFallAnimationGravityOffsets[frame];
-
- uint8_t tileX = (finalPosition % kLevelWidth);
- uint8_t tileY = (finalPosition / kLevelWidth);
-
- uint16_t dstX = tileX * kTileSize - (offset % 122) * 2;
- uint16_t dstY = tileY * kTileSize - (offset / 122) * 2;
-
- drawMovingSpriteFrameInLevel(frameCoordinates.x, frameCoordinates.y,
- kTileSize,
- kTileSize + 2,
- dstX, dstY);
- if (frame == 7)
- {
- if (belowTile->tile != LevelTileTypeExplosion)
- {
- belowTile->state = 0;
- belowTile->tile = LevelTileTypeSpace;
- }
- }
- // loc_4F546: ; CODE XREF: updateSnikSnakMovementUp+2Dj
- // ; updateSnikSnakMovementUp+34j
- if (frame < 8)
- {
- frame += 0x10;
- currentTile->state = frame;
- return;
- }
-
- // loc_4F553: ; CODE XREF: updateSnikSnakMovementUp+3Fj
- currentTile->state = 0;
- currentTile->tile = LevelTileTypeSnikSnak;
-
- if (leftTile->state == 0 && leftTile->tile == LevelTileTypeSpace)
- {
- currentTile->state = 1;
- return;
- }
-
- // loc_4F566: ; CODE XREF: updateSnikSnakMovementUp+54j
- if (leftTile->tile == LevelTileTypeMurphy)
- {
- currentTile->state = 1;
- return;
- }
-
- // loc_4F573: ; CODE XREF: updateSnikSnakMovementUp+61j
- if (aboveTile->state == 0 && aboveTile->tile == LevelTileTypeSpace)
- {
- currentTile->state = 1;
- currentTile->tile = 0xBB;
- aboveTile->state = 0x10;
- aboveTile->tile = LevelTileTypeSnikSnak;
- return;
- }
-
- // loc_4F58A: ; CODE XREF: updateSnikSnakMovementUp+6Ej
- if (aboveTile->tile == LevelTileTypeMurphy)
- {
- detonateBigExplosion(position);
- return;
- }
-
- // loc_4F595: ; CODE XREF: updateSnikSnakMovementUp+85j
- if (rightTile->state == 0 && rightTile->tile == LevelTileTypeSpace)
- {
- // 01ED:8939
- currentTile->state = 9;
- return;
- }
-
- // loc_4F5A2: ; CODE XREF: updateSnikSnakMovementUp+90j
- if (rightTile->tile == LevelTileTypeMurphy)
- {
- currentTile->state = 9;
- return;
- }
-
- // loc_4F5AF: ; CODE XREF: updateSnikSnakMovementUp+9Dj
- currentTile->state = 1;
-}
-
-void updateSnikSnakMovementLeft(int16_t position, uint8_t frame) // sub_4F5B5 proc near ; DATA XREF: data:157Ao
-{
- // 01ED:8952
- StatefulLevelTile *currentTile = &gCurrentLevelState[position];
- StatefulLevelTile *belowTile = &gCurrentLevelState[position + kLevelWidth];
- StatefulLevelTile *leftTile = &gCurrentLevelState[position - 1];
- StatefulLevelTile *aboveTile = &gCurrentLevelState[position - kLevelWidth];
- StatefulLevelTile *rightTile = &gCurrentLevelState[position + 1];
-
- Point frameCoordinates = kSnikSnakAnimationFrameCoordinates[frame];
-
- uint8_t tileX = (position % kLevelWidth);
- uint8_t tileY = (position / kLevelWidth);
-
- uint16_t dstX = tileX * kTileSize;
- uint16_t dstY = tileY * kTileSize;
-
- drawMovingSpriteFrameInLevel(frameCoordinates.x, frameCoordinates.y,
- kTileSize * 2,
- kTileSize,
- dstX, dstY);
-
- frame &= 7;
- frame++;
- if (frame == 7)
- {
- if (rightTile->tile != LevelTileTypeExplosion)
- {
- rightTile->state = 0;
- rightTile->tile = LevelTileTypeSpace;
- }
- }
- // loc_4F5EC: ; CODE XREF: updateSnikSnakMovementLeft+28j
- // ; updateSnikSnakMovementLeft+2Fj
- if (frame < 8)
- {
- frame += 0x18;
- currentTile->state = frame;
- return;
- }
-
- // loc_4F5F9: ; CODE XREF: updateSnikSnakMovementLeft+3Aj
- currentTile->state = 0;
- currentTile->tile = LevelTileTypeSnikSnak;
-
- if (belowTile->state == 0 && belowTile->tile == LevelTileTypeSpace)
- {
- currentTile->state = 3;
- return;
- }
-
- // loc_4F60C: ; CODE XREF: updateSnikSnakMovementLeft+4Fj
- if (belowTile->tile == LevelTileTypeMurphy)
- {
- currentTile->state = 3;
- return;
- }
-
- // loc_4F619: ; CODE XREF: updateSnikSnakMovementLeft+5Cj
- if (leftTile->state == 0 && leftTile->tile == LevelTileTypeSpace)
- {
- currentTile->state = 2;
- currentTile->tile = 0xBB;
- leftTile->state = 0x18;
- leftTile->tile = LevelTileTypeSnikSnak;
- return;
- }
-
- // loc_4F630: ; CODE XREF: updateSnikSnakMovementLeft+69j
- if (leftTile->tile == LevelTileTypeMurphy)
- {
- detonateBigExplosion(position);
- return;
- }
-
- // loc_4F63B: ; CODE XREF: updateSnikSnakMovementLeft+80j
- if (aboveTile->state == 0 && aboveTile->tile == LevelTileTypeSpace)
- {
- currentTile->state = 0xF;
- return;
- }
-
- // loc_4F648: ; CODE XREF: updateSnikSnakMovementLeft+8Bj
- if (aboveTile->tile == LevelTileTypeMurphy)
- {
- currentTile->state = 0xF;
- return;
- }
-
- // loc_4F655: ; CODE XREF: updateSnikSnakMovementLeft+98j
- currentTile->state = 3;
-}
-
-void updateSnikSnakMovementDown(int16_t position, uint8_t frame) // sub_4F65B proc near ; DATA XREF: data:158Ao
-{
- // 01ED:89F8
- StatefulLevelTile *currentTile = &gCurrentLevelState[position];
- StatefulLevelTile *belowTile = &gCurrentLevelState[position + kLevelWidth];
- StatefulLevelTile *leftTile = &gCurrentLevelState[position - 1];
- StatefulLevelTile *aboveTile = &gCurrentLevelState[position - kLevelWidth];
- StatefulLevelTile *rightTile = &gCurrentLevelState[position + 1];
-
- uint16_t finalPosition = position - kLevelWidth;
- Point frameCoordinates = kSnikSnakAnimationFrameCoordinates[frame];
- // sub bx, 40h ; '@'
- frame -= 0x20; // 0x40 / 2
-
- uint16_t offset = kFallAnimationGravityOffsets[frame];
-
- uint8_t tileX = (finalPosition % kLevelWidth);
- uint8_t tileY = (finalPosition / kLevelWidth);
-
- uint16_t dstX = tileX * kTileSize + (offset % 122) * 2;
- uint16_t dstY = tileY * kTileSize + (offset / 122) * 2;
-
- drawMovingSpriteFrameInLevel(frameCoordinates.x, frameCoordinates.y,
- kTileSize,
- kTileSize + 2,
- dstX, dstY);
-
- frame++;
- if (frame == 7)
- {
- if (aboveTile->tile != LevelTileTypeExplosion)
- {
- aboveTile->state = 0;
- aboveTile->tile = LevelTileTypeSpace;
- }
- }
- // loc_4F699: ; CODE XREF: sub_4F66B+1Fj
- // ; sub_4F66B+26j
- if (frame < 8)
- {
- frame += 0x20;
- currentTile->state = frame;
- return;
- }
-
- // loc_4F6A6: ; CODE XREF: sub_4F66B+31j
- currentTile->state = 0;
- currentTile->tile = LevelTileTypeSnikSnak;
-
- if (rightTile->state == 0 && rightTile->tile == LevelTileTypeSpace)
- {
- currentTile->state = 5;
- return;
- }
-
- // loc_4F6B9: ; CODE XREF: sub_4F66B+46j
- if (rightTile->tile == LevelTileTypeMurphy)
- {
- currentTile->state = 5;
- return;
- }
-
- // loc_4F6C6: ; CODE XREF: sub_4F66B+53j
- if (belowTile->state == 0 && belowTile->tile == LevelTileTypeSpace)
- {
- currentTile->state = 3;
- currentTile->tile = 0xBB;
- belowTile->state = 0x20;
- belowTile->tile = LevelTileTypeSnikSnak;
- return;
- }
-
- // loc_4F6DD: ; CODE XREF: sub_4F66B+60j
- if (belowTile->tile == LevelTileTypeMurphy)
- {
- detonateBigExplosion(position);
- return;
- }
-
- // loc_4F6E8: ; CODE XREF: sub_4F66B+77j
- if (leftTile->state == 0 && leftTile->tile == LevelTileTypeSpace)
- {
- // 01ED:8A8C
- currentTile->state = 0xD;
- return;
- }
-
- // loc_4F6F5: ; CODE XREF: sub_4F66B+82j
- if (leftTile->tile == LevelTileTypeMurphy)
- {
- currentTile->state = 0xD;
- return;
- }
-
- // loc_4F702: ; CODE XREF: sub_4F66B+8Fj
- currentTile->state = 5;
-}
-
-void updateSnikSnakMovementRight(int16_t position, uint8_t frame) // sub_4F708 proc near ; DATA XREF: data:159Ao
-{
- // 01ED:8AA5
- StatefulLevelTile *currentTile = &gCurrentLevelState[position];
- StatefulLevelTile *belowTile = &gCurrentLevelState[position + kLevelWidth];
- StatefulLevelTile *leftTile = &gCurrentLevelState[position - 1];
- StatefulLevelTile *aboveTile = &gCurrentLevelState[position - kLevelWidth];
- StatefulLevelTile *rightTile = &gCurrentLevelState[position + 1];
-
- Point frameCoordinates = kSnikSnakAnimationFrameCoordinates[frame];
-
- uint16_t finalPosition = position - 1;
-
- uint8_t tileX = (finalPosition % kLevelWidth);
- uint8_t tileY = (finalPosition / kLevelWidth);
-
- uint16_t dstX = tileX * kTileSize;
- uint16_t dstY = tileY * kTileSize;
-
- drawMovingSpriteFrameInLevel(frameCoordinates.x, frameCoordinates.y,
- kTileSize * 2,
- kTileSize,
- dstX, dstY);
- frame &= 7;
- frame++;
- if (frame == 7)
- {
- if (leftTile->tile != LevelTileTypeExplosion)
- {
- leftTile->state = 0;
- leftTile->tile = LevelTileTypeSpace;
- }
- }
- // loc_4F740: ; CODE XREF: updateSnikSnakMovementRight+29j
- // ; updateSnikSnakMovementRight+30j
- if (frame < 8)
- {
- frame += 0x28;
- currentTile->state = frame;
- return;
- }
-
- // loc_4F74D: ; CODE XREF: updateSnikSnakMovementRight+3Bj
- currentTile->state = 0;
- currentTile->tile = LevelTileTypeSnikSnak;
-
- if (aboveTile->state == 0 && aboveTile->tile == LevelTileTypeSpace)
- {
- currentTile->state = 7;
- return;
- }
-
- // loc_4F760: ; CODE XREF: updateSnikSnakMovementRight+50j
- if (aboveTile->tile == LevelTileTypeMurphy)
- {
- currentTile->state = 7;
- return;
- }
-
- // loc_4F76D: ; CODE XREF: updateSnikSnakMovementRight+5Dj
- if (rightTile->state == 0 && rightTile->tile == LevelTileTypeSpace)
- {
- currentTile->state = 4;
- currentTile->tile = 0xBB;
- rightTile->state = 0x28;
- rightTile->tile = LevelTileTypeSnikSnak;
- return;
- }
-
- // loc_4F784: ; CODE XREF: updateSnikSnakMovementRight+6Aj
- if (rightTile->tile == LevelTileTypeMurphy)
- {
- detonateBigExplosion(position);
- return;
- }
-
- // loc_4F78F: ; CODE XREF: updateSnikSnakMovementRight+81j
- if (belowTile->state == 0 && belowTile->tile == LevelTileTypeSpace)
- {
- currentTile->state = 0xB;
- return;
- }
-
- // loc_4F79C: ; CODE XREF: updateSnikSnakMovementRight+8Cj
- if (belowTile->tile == LevelTileTypeMurphy)
- {
- currentTile->state = 0xB;
- return;
- }
-
- // loc_4F7A9: ; CODE XREF: updateSnikSnakMovementRight+99j
- currentTile->state = 7;
-}
-
-void updateElectronTiles(int16_t position) // movefun6 proc near ; DATA XREF: data:163Ao
-{
- // 01ED:8B4C
- if (gAreEnemiesFrozen == 1)
- {
- return;
- }
- StatefulLevelTile *currentTile = &gCurrentLevelState[position];
-
- if (currentTile->tile != LevelTileTypeElectron)
- {
- return;
- }
-
- uint8_t frame = currentTile->state;
-
- FrameBasedMovingFunction function = kElectronMovingFunctions[frame];
-
- if (function != NULL)
- {
- // 01ED:8B6B
- function(position, frame);
- }
-}
-
-void updateElectronTurnLeft(int16_t position, uint8_t frame) // sub_4F7D1 proc near ; DATA XREF: data:movingFunctions2o
-{
- // 01ED:8B6E
- StatefulLevelTile *currentTile = &gCurrentLevelState[position];
- StatefulLevelTile *aboveTile = &gCurrentLevelState[position - kLevelWidth];
- StatefulLevelTile *leftTile = &gCurrentLevelState[position - 1];
- StatefulLevelTile *belowTile = &gCurrentLevelState[position + kLevelWidth];
- StatefulLevelTile *rightTile = &gCurrentLevelState[position + 1];
-
- uint16_t value = gFrameCounter & 3;
-
- if (value == 0)
- {
- // loc_4F7DF: ; CODE XREF: updateElectronTurnLeft+6j
- Point frameCoordinates = kElectronAnimationFrameCoordinates[frame];
- drawMovingFrame(frameCoordinates.x, frameCoordinates.y, position);
- frame++;
- frame &= 7;
- currentTile->state = frame;
- return;
- }
- else if (value != 3)
- {
- return;
- }
-
- // loc_4F80D: ; CODE XREF: updateElectronTurnLeft+Bj
- uint8_t state = currentTile->state;
- if (state == 0)
- {
- // loc_4F826: ; CODE XREF: updateElectronTurnLeft+43j
- if (aboveTile->state == 0 && aboveTile->tile == LevelTileTypeSpace)
- {
- // loc_4F835: ; CODE XREF: updateElectronTurnLeft+5Aj
- currentTile->state = 0x1;
- currentTile->tile = 0xBB;
- aboveTile->state = 0x10;
- aboveTile->tile = LevelTileTypeElectron;
- return;
- }
- else if (aboveTile->tile != LevelTileTypeMurphy)
- {
- return;
- }
- }
- else if (state == 2)
- {
- // loc_4F845: ; CODE XREF: updateElectronTurnLeft+48j
- if (leftTile->state == 0 && leftTile->tile == LevelTileTypeSpace)
- {
- // loc_4F854: ; CODE XREF: updateElectronTurnLeft+79j
- currentTile->state = 0x2;
- currentTile->tile = 0xBB;
- leftTile->state = 0x18;
- leftTile->tile = LevelTileTypeElectron;
- return;
- }
- else if (leftTile->tile != LevelTileTypeMurphy)
- {
- return;
- }
- }
- else if (state == 4)
- {
- // loc_4F864: ; CODE XREF: updateElectronTurnLeft+4Dj
- if (belowTile->state == 0 && belowTile->tile == LevelTileTypeSpace)
- {
- // loc_4F873: ; CODE XREF: updateElectronTurnLeft+98j
- currentTile->state = 0x3;
- currentTile->tile = 0xBB;
- belowTile->state = 0x20;
- belowTile->tile = LevelTileTypeElectron;
- return;
- }
- else if (belowTile->tile != LevelTileTypeMurphy)
- {
- return;
- }
- }
- else if (state == 6)
- {
- // loc_4F883: ; CODE XREF: updateElectronTurnLeft+52j
- if (rightTile->state == 0 && rightTile->tile == LevelTileTypeSpace)
- {
- // loc_4F895: ; CODE XREF: updateElectronTurnLeft+B7j
- currentTile->state = 0x4;
- currentTile->tile = 0xBB;
- rightTile->state = 0x28;
- rightTile->tile = LevelTileTypeElectron;
- return;
- }
- else if (rightTile->tile != LevelTileTypeMurphy)
- {
- return;
- }
- }
- else
- {
- return;
- }
-
- // loc_4F809: ; CODE XREF: updateElectronTurnLeft+61j
- // ; updateElectronTurnLeft+80j ...
- detonateBigExplosion(position);
-}
-
-void updateElectronTurnRight(int16_t position, uint8_t frame) // sub_4F8A5 proc near ; DATA XREF: data:15BAo
-{
- // 01ED:8C42
- StatefulLevelTile *currentTile = &gCurrentLevelState[position];
- StatefulLevelTile *aboveTile = &gCurrentLevelState[position - kLevelWidth];
- StatefulLevelTile *leftTile = &gCurrentLevelState[position - 1];
- StatefulLevelTile *belowTile = &gCurrentLevelState[position + kLevelWidth];
- StatefulLevelTile *rightTile = &gCurrentLevelState[position + 1];
-
- uint16_t value = gFrameCounter & 3;
-
- if (value == 0)
- {
- // loc_4F8B3: ; CODE XREF: updateElectronTurnRight+6j
- Point frameCoordinates = kElectronAnimationFrameCoordinates[frame];
- drawMovingFrame(frameCoordinates.x, frameCoordinates.y, position);
- frame++;
- frame &= 7;
- frame |= 8;
- currentTile->state = frame;
- return;
- }
- else if (value != 3)
- {
- return;
- }
-
- // loc_4F8E3: ; CODE XREF: updateElectronTurnRight+Bj
- uint8_t state = currentTile->state;
- if (state == 8)
- {
- // loc_4F8FC: ; CODE XREF: updateElectronTurnRight+45j
- if (aboveTile->state == 0 && aboveTile->tile == LevelTileTypeSpace)
- {
- // loc_4F90B: ; CODE XREF: updateElectronTurnRight+5Cj
- currentTile->state = 0x1;
- currentTile->tile = 0xBB;
- aboveTile->state = 0x10;
- aboveTile->tile = LevelTileTypeElectron;
- return;
- }
- else if (aboveTile->tile != LevelTileTypeMurphy)
- {
- return;
- }
- }
- else if (state == 0xA)
- {
- // loc_4F959: ; CODE XREF: updateElectronTurnRight+4Aj
- if (rightTile->state == 0 && rightTile->tile == LevelTileTypeSpace)
- {
- // loc_4F96B: ; CODE XREF: updateElectronTurnRight+B9j
- currentTile->state = 0x4;
- currentTile->tile = 0xBB;
- rightTile->state = 0x28;
- rightTile->tile = LevelTileTypeElectron;
- return;
- }
- else if (rightTile->tile != LevelTileTypeMurphy)
- {
- return;
- }
- }
- else if (state == 0xC)
- {
- // loc_4F93A: ; CODE XREF: updateElectronTurnRight+4Fj
- if (belowTile->state == 0 && belowTile->tile == LevelTileTypeSpace)
- {
- // loc_4F949: ; CODE XREF: updateElectronTurnRight+9Aj
- currentTile->state = 0x3;
- currentTile->tile = 0xBB;
- belowTile->state = 0x20;
- belowTile->tile = LevelTileTypeElectron;
- return;
- }
- else if (belowTile->tile != LevelTileTypeMurphy)
- {
- return;
- }
- }
- else if (state == 0xE)
- {
- // loc_4F91B: ; CODE XREF: updateElectronTurnRight+54j
- if (leftTile->state == 0 && leftTile->tile == LevelTileTypeSpace)
- {
- // loc_4F92A: ; CODE XREF: updateElectronTurnRight+7Bj
- currentTile->state = 0x2;
- currentTile->tile = 0xBB;
- leftTile->state = 0x18;
- leftTile->tile = LevelTileTypeElectron;
- return;
- }
- else if (leftTile->tile != LevelTileTypeMurphy)
- {
- return;
- }
- }
- else
- {
- return;
- }
-
- // loc_4F8DF: ; CODE XREF: updateElectronTurnRight+63j
- // ; updateElectronTurnRight+82j ...
- detonateBigExplosion(position);
-}
-
-void updateElectronMovementUp(int16_t position, uint8_t frame) // sub_4F97B proc near ; DATA XREF: data:15CAo
-{
- // 01ED:8D18
- StatefulLevelTile *currentTile = &gCurrentLevelState[position];
- StatefulLevelTile *belowTile = &gCurrentLevelState[position + kLevelWidth];
- StatefulLevelTile *leftTile = &gCurrentLevelState[position - 1];
- StatefulLevelTile *aboveTile = &gCurrentLevelState[position - kLevelWidth];
- StatefulLevelTile *rightTile = &gCurrentLevelState[position + 1];
-
- uint16_t finalPosition = position + kLevelWidth;
- Point frameCoordinates = kElectronAnimationFrameCoordinates[frame];
- // sub bx, 1Eh
- frame -= 15; // 0x1E / 2
- uint16_t offset = kFallAnimationGravityOffsets[frame];
-
- uint8_t tileX = (finalPosition % kLevelWidth);
- uint8_t tileY = (finalPosition / kLevelWidth);
-
- uint16_t dstX = tileX * kTileSize - (offset % 122) * 2;
- uint16_t dstY = tileY * kTileSize - (offset / 122) * 2;
-
- drawMovingSpriteFrameInLevel(frameCoordinates.x, frameCoordinates.y,
- kTileSize,
- kTileSize + 2,
- dstX, dstY);
-
- if (frame == 7)
- {
- if (belowTile->tile != LevelTileTypeExplosion)
- {
- belowTile->state = 0;
- belowTile->tile = LevelTileTypeSpace;
- }
- }
-
- // loc_4F9B7: ; CODE XREF: updateElectronMovementUp+2Dj
- // ; updateElectronMovementUp+34j
- if (frame < 8)
- {
- frame += 0x10;
- currentTile->state = frame;
- return;
- }
-
- // loc_4F9C4: ; CODE XREF: updateElectronMovementUp+3Fj
- currentTile->state = 0;
- currentTile->tile = LevelTileTypeElectron;
-
- if (leftTile->state == 0 && leftTile->tile == LevelTileTypeSpace)
- {
- currentTile->state = 1;
- return;
- }
-
- // loc_4F9D7: ; CODE XREF: updateElectronMovementUp+54j
- if (leftTile->tile == LevelTileTypeMurphy)
- {
- currentTile->state = 1;
- return;
- }
-
- // loc_4F9E4: ; CODE XREF: updateElectronMovementUp+61j
- if (aboveTile->state == 0 && aboveTile->tile == LevelTileTypeSpace)
- {
- currentTile->state = 1;
- currentTile->tile = 0xBB;
- aboveTile->state = 0x10;
- aboveTile->tile = LevelTileTypeElectron;
- return;
- }
-
- // loc_4F9FB: ; CODE XREF: updateElectronMovementUp+6Ej
- if (aboveTile->tile == LevelTileTypeMurphy)
- {
- detonateBigExplosion(position);
- return;
- }
-
- // loc_4FA06: ; CODE XREF: updateElectronMovementUp+85j
- if (rightTile->state == 0 && rightTile->tile == LevelTileTypeSpace)
- {
- currentTile->state = 9;
- return;
- }
-
- // loc_4FA13: ; CODE XREF: updateElectronMovementUp+90j
- if (rightTile->tile == LevelTileTypeMurphy)
- {
- currentTile->state = 9;
- return;
- }
-
- // loc_4FA20: ; CODE XREF: updateElectronMovementUp+9Dj
- currentTile->state = 1;
-}
-
-void updateElectronMovementDown(int16_t position, uint8_t frame) // sub_4FA26 proc near ; DATA XREF: data:15DAo
-{
- // 01ED:8DC3
- StatefulLevelTile *currentTile = &gCurrentLevelState[position];
- StatefulLevelTile *belowTile = &gCurrentLevelState[position + kLevelWidth];
- StatefulLevelTile *leftTile = &gCurrentLevelState[position - 1];
- StatefulLevelTile *aboveTile = &gCurrentLevelState[position - kLevelWidth];
- StatefulLevelTile *rightTile = &gCurrentLevelState[position + 1];
-
- Point frameCoordinates = kElectronAnimationFrameCoordinates[frame];
-
- uint8_t tileX = (position % kLevelWidth);
- uint8_t tileY = (position / kLevelWidth);
-
- uint16_t dstX = tileX * kTileSize;
- uint16_t dstY = tileY * kTileSize;
-
- drawMovingSpriteFrameInLevel(frameCoordinates.x, frameCoordinates.y,
- kTileSize * 2,
- kTileSize,
- dstX, dstY);
-
- frame &= 7;
- frame++;
- if (frame == 7)
- {
- if (rightTile->tile != LevelTileTypeExplosion)
- {
- rightTile->state = 0;
- rightTile->tile = LevelTileTypeSpace;
- }
- }
-
- // loc_4FA5D: ; CODE XREF: updateElectronMovementDown+28j
- // ; updateElectronMovementDown+2Fj
- if (frame < 8)
- {
- frame += 0x18;
- currentTile->state = frame;
- return;
- }
-
- // loc_4FA6A: ; CODE XREF: updateElectronMovementDown+3Aj
- currentTile->state = 0;
- currentTile->tile = LevelTileTypeElectron;
-
- if (belowTile->state == 0 && belowTile->tile == LevelTileTypeSpace)
- {
- currentTile->state = 3;
- return;
- }
-
- // loc_4FA7D: ; CODE XREF: updateElectronMovementDown+4Fj
- if (belowTile->tile == LevelTileTypeMurphy)
- {
- currentTile->state = 3;
- return;
- }
-
- // loc_4FA8A: ; CODE XREF: updateElectronMovementDown+5Cj
- if (leftTile->state == 0 && leftTile->tile == LevelTileTypeSpace)
- {
- currentTile->state = 2;
- currentTile->tile = 0xBB;
- leftTile->state = 0x18;
- leftTile->tile = LevelTileTypeElectron;
- return;
- }
-
- // loc_4FAA1: ; CODE XREF: updateElectronMovementDown+69j
- if (leftTile->tile == LevelTileTypeMurphy)
- {
- detonateBigExplosion(position);
- return;
- }
-
- // loc_4FAAC: ; CODE XREF: updateElectronMovementDown+80j
- if (aboveTile->state == 0 && aboveTile->tile == LevelTileTypeSpace)
- {
- currentTile->state = 0xF;
- return;
- }
-
- // loc_4FAB9: ; CODE XREF: updateElectronMovementDown+8Bj
- if (aboveTile->tile == LevelTileTypeMurphy)
- {
- currentTile->state = 0xF;
- return;
- }
-
- // loc_4FAC6: ; CODE XREF: updateElectronMovementDown+98j
- currentTile->state = 3;
-}
-
-void updateElectronMovementRight(int16_t position, uint8_t frame) // sub_4FACC proc near ; DATA XREF: data:15EAo
-{
- // 01ED:8E69
- StatefulLevelTile *currentTile = &gCurrentLevelState[position];
- StatefulLevelTile *belowTile = &gCurrentLevelState[position + kLevelWidth];
- StatefulLevelTile *leftTile = &gCurrentLevelState[position - 1];
- StatefulLevelTile *aboveTile = &gCurrentLevelState[position - kLevelWidth];
- StatefulLevelTile *rightTile = &gCurrentLevelState[position + 1];
-
- uint16_t finalPosition = position - kLevelWidth;
- Point frameCoordinates = kElectronAnimationFrameCoordinates[frame];
- // sub bx, 40h ; '@'
- frame -= 0x20; // 0x40 / 2
-
- uint16_t offset = kFallAnimationGravityOffsets[frame];
-
- uint8_t tileX = (finalPosition % kLevelWidth);
- uint8_t tileY = (finalPosition / kLevelWidth);
-
- uint16_t dstX = tileX * kTileSize + (offset % 122) * 2;
- uint16_t dstY = tileY * kTileSize + (offset / 122) * 2;
-
- drawMovingSpriteFrameInLevel(frameCoordinates.x, frameCoordinates.y,
- kTileSize,
- kTileSize + 2,
- dstX, dstY);
-
- frame++;
- if (frame == 7)
- {
- if (aboveTile->tile != LevelTileTypeExplosion)
- {
- aboveTile->state = 0;
- aboveTile->tile = LevelTileTypeSpace;
- }
- }
-
- // loc_4FB0A: ; CODE XREF: updateElectronMovementRight+2Fj
- // ; updateElectronMovementRight+36j
- if (frame < 8)
- {
- frame += 0x20;
- currentTile->state = frame;
- return;
- }
-
- // loc_4FB17: ; CODE XREF: updateElectronMovementRight+41j
- currentTile->state = 0;
- currentTile->tile = LevelTileTypeElectron;
-
- if (rightTile->state == 0 && rightTile->tile == LevelTileTypeSpace)
- {
- currentTile->state = 5;
- return;
- }
-
- // loc_4FB2A: ; CODE XREF: updateElectronMovementRight+56j
- if (rightTile->tile == LevelTileTypeMurphy)
- {
- currentTile->state = 5;
- return;
- }
-
- // loc_4FB37: ; CODE XREF: updateElectronMovementRight+63j
- if (belowTile->state == 0 && belowTile->tile == LevelTileTypeSpace)
- {
- currentTile->state = 3;
- currentTile->tile = 0xBB;
- belowTile->state = 0x20;
- belowTile->tile = LevelTileTypeElectron;
- return;
- }
-
- // loc_4FB4E: ; CODE XREF: updateElectronMovementRight+70j
- if (belowTile->tile == LevelTileTypeMurphy)
- {
- detonateBigExplosion(position);
- return;
- }
-
- // loc_4FB59: ; CODE XREF: updateElectronMovementRight+87j
- if (leftTile->state == 0 && leftTile->tile == LevelTileTypeSpace)
- {
- // 01ED:8A8C
- currentTile->state = 0xD;
- return;
- }
-
- // loc_4FB66: ; CODE XREF: updateElectronMovementRight+92j
- if (leftTile->tile == LevelTileTypeMurphy)
- {
- currentTile->state = 0xD;
- return;
- }
-
- // loc_4FB73: ; CODE XREF: updateElectronMovementRight+9Fj
- currentTile->state = 5;
-}
-
-void updateElectronMovementLeft(int16_t position, uint8_t frame) // sub_4FB79 proc near ; DATA XREF: data:15FAo
-{
- // 01ED:8F16
- StatefulLevelTile *currentTile = &gCurrentLevelState[position];
- StatefulLevelTile *belowTile = &gCurrentLevelState[position + kLevelWidth];
- StatefulLevelTile *leftTile = &gCurrentLevelState[position - 1];
- StatefulLevelTile *aboveTile = &gCurrentLevelState[position - kLevelWidth];
- StatefulLevelTile *rightTile = &gCurrentLevelState[position + 1];
-
- Point frameCoordinates = kElectronAnimationFrameCoordinates[frame];
-
- uint16_t finalPosition = position - 1;
-
- uint8_t tileX = (finalPosition % kLevelWidth);
- uint8_t tileY = (finalPosition / kLevelWidth);
-
- uint16_t dstX = tileX * kTileSize;
- uint16_t dstY = tileY * kTileSize;
-
- drawMovingSpriteFrameInLevel(frameCoordinates.x, frameCoordinates.y,
- kTileSize * 2,
- kTileSize,
- dstX, dstY);
- frame &= 7;
- frame++;
- if (frame == 7)
- {
- if (leftTile->tile != LevelTileTypeExplosion)
- {
- leftTile->state = 0;
- leftTile->tile = LevelTileTypeSpace;
- }
- }
-
- // loc_4FBB1: ; CODE XREF: updateElectronMovementLeft+29j
- // ; updateElectronMovementLeft+30j
- if (frame < 8)
- {
- frame += 0x28;
- currentTile->state = frame;
- return;
- }
-
- // loc_4FBBE: ; CODE XREF: updateElectronMovementLeft+3Bj
- currentTile->state = 0;
- currentTile->tile = LevelTileTypeElectron;
-
- if (aboveTile->state == 0 && aboveTile->tile == LevelTileTypeSpace)
- {
- currentTile->state = 7;
- return;
- }
-
- // loc_4FBD1: ; CODE XREF: updateElectronMovementLeft+50j
- if (aboveTile->tile == LevelTileTypeMurphy)
- {
- currentTile->state = 7;
- return;
- }
-
- // loc_4FBDE: ; CODE XREF: updateElectronMovementLeft+5Dj
- if (rightTile->state == 0 && rightTile->tile == LevelTileTypeSpace)
- {
- currentTile->state = 4;
- currentTile->tile = 0xBB;
- rightTile->state = 0x28;
- rightTile->tile = LevelTileTypeElectron;
- return;
- }
-
- // loc_4FBF5: ; CODE XREF: updateElectronMovementLeft+6Aj
- if (rightTile->tile == LevelTileTypeMurphy)
- {
- detonateBigExplosion(position);
- return;
- }
-
- // loc_4FC00: ; CODE XREF: updateElectronMovementLeft+81j
- if (belowTile->state == 0 && belowTile->tile == LevelTileTypeSpace)
- {
- currentTile->state = 0xB;
- return;
- }
-
- // loc_4FC0D: ; CODE XREF: updateElectronMovementLeft+8Cj
- if (belowTile->tile == LevelTileTypeMurphy)
- {
- currentTile->state = 0xB;
- return;
- }
-
- // loc_4FC1A: ; CODE XREF: updateElectronMovementLeft+99j
- currentTile->state = 7;
-}
-
-void drawGamePanelText() // sub_4FC20 proc near ; CODE XREF: stopRecordingDemo:loc_4944Fp
- // ; drawGamePanel+22p ...
-{
- if (gFastMode == FastModeTypeUltra)
- {
- return;
- }
-
- // 01ED:8FBD
- if (gIsRecordingDemo != 0) // Recording demo?
- {
- // mov si, 87D1h // " DEMO "
- drawTextWithChars8FontToGamePanel(72, 3, 8, " DEMO ");
- // mov si, 87DAh // "000" -> this address is the ".SP" text
- drawTextWithChars8FontToGamePanel(16, 14, 8, gCurrentDemoLevelName);
- // mov si, 87F6h // "--- RECORDING DEMO0 ---"
- drawTextWithChars8FontToGamePanel(64, 14, 8, gRecordingDemoMessage);
- }
- // loc_4FC6F: ; CODE XREF: drawGamePanelText+5j
- else if (gIsPlayingDemo != 0) // Playing demo?
- {
- drawTextWithChars8FontToGamePanel(72, 3, 8, " DEMO ");
- // mov si, 87DAh // "000" -> this address is the ".SP" text
- drawTextWithChars8FontToGamePanel(16, 14, 8, gCurrentDemoLevelName);
- // mov si, 87DEh // "----- DEMO LEVEL! -----"
- drawTextWithChars8FontToGamePanel(64, 14, 8, &gCurrentDemoLevelName[4]);
- }
- else
- {
- // loc_4FCD6: ; CODE XREF: drawGamePanelText+B1j
- drawTextWithChars8FontToGamePanel(72, 3, 6, gPlayerName);
- char levelNumber[4] = "000";
- memcpy(levelNumber, gCurrentLevelName, 3);
- drawTextWithChars8FontToGamePanel(16, 14, 8, levelNumber);
- drawTextWithChars8FontToGamePanel(64, 14, 8, &gCurrentLevelName[4]);
- }
-
- // loc_4FD1A: ; CODE XREF: drawGamePanelText+4Cj
- // ; drawGamePanelText+A0j ...
- drawNumberOfRemainingInfotrons();
- drawGameTime();
-}
-
-void drawNumberOfRemainingInfotrons() // sub_4FD21 proc near ; CODE XREF: resetNumberOfInfotrons+13p
- // ; update?:loc_4EC90p ...
-{
- if (gFastMode == FastModeTypeUltra)
- {
- return;
- }
-
- if (gNumberOfRemainingInfotrons < 1)
- {
- gNumberOfRemainingInfotrons = 0; // WTF? Can this be negative? In theory not...
- }
-
- // loc_4FD2E: ; CODE XREF: drawNumberOfRemainingInfotrons+6j
- char number[4] = "000";
- convertNumberTo3DigitStringWithPadding0(gNumberOfRemainingInfotrons, number);
-
- // loc_4FD46: ; CODE XREF: drawNumberOfRemainingInfotrons+20j
- uint8_t color = (gNumberOfRemainingInfotrons == 0
- ? 6
- : 8);
-
- // loc_4FD56: ; CODE XREF: drawNumberOfRemainingInfotrons+31j
- drawTextWithChars8FontToGamePanel(272, 14, color, number);
-}
-
-void clearAdditionalInfoInGamePanelIfNeeded() // sub_4FD65 proc near ; CODE XREF: runLevel+E9p
-{
- // loc_4FD6D: ; CODE XREF: clearAdditionalInfoInGamePanelIfNeeded+5j
- if (gAdditionalInfoInGamePanelFrameCounter == 0)
- {
- return;
- }
-
- // loc_4FD75: ; CODE XREF: clearAdditionalInfoInGamePanelIfNeeded+Dj
- gAdditionalInfoInGamePanelFrameCounter--;
- if (gAdditionalInfoInGamePanelFrameCounter != 0)
- {
- return;
- }
-
- clearAdditionalInfoInGamePanel();
-}
-
-void decreaseRemainingRedDisksIfNeeded(int16_t position) // sub_4FDB5 proc near ; CODE XREF: update?+124Fp
- // ; update?+1266p ...
-{
- if (word_59B73 == 0 && gPlantedRedDiskCountdown != 0 && gPlantedRedDiskPosition == position)
- {
- return;
- }
-
- // loc_4FDCA: ; CODE XREF: decreaseRemainingRedDisksIfNeeded+5j decreaseRemainingRedDisksIfNeeded+Cj ...
- gNumberOfRemainingRedDisks++;
- drawNumberOfRemainingRedDisks();
-}
-
-void drawNumberOfRemainingRedDisks() // sub_4FDCE proc near ; CODE XREF: handleGameUserInput+7F4p
- // ; update?+1369p
-{
- // loc_4FDD6: ; CODE XREF: drawNumberOfRemainingRedDisks+5j
- char numberString[4] = "000";
- convertNumberTo3DigitStringWithPadding0(gNumberOfRemainingRedDisks, numberString);
- // mov di, 6D2h
- // mov si, 87CAh
- uint8_t color = 0;
- if (gNumberOfRemainingRedDisks != 0)
- {
- // loc_4FDF1: ; CODE XREF: drawNumberOfRemainingRedDisks+1Dj
- color = 6;
- }
- else
- {
- color = 8;
- }
-
- // loc_4FDF3: ; CODE XREF: drawNumberOfRemainingRedDisks+21j
- drawTextWithChars8FontToGamePanel(304, 14, color, &numberString[1]);
- gAdditionalInfoInGamePanelFrameCounter = 0x46; // 70
-}
-
-void drawGameTime() // sub_4FDFD proc near ; CODE XREF: runLevel+29p
- // ; runLevel:noFlashing2p ...
-{
- if (gFastMode == FastModeTypeUltra)
- {
- return;
- }
-
- // Only the 2 last digits will be printed, hence why it will be used with &number[1] everywhere
- char number[4] = "000";
-
- if ((gLastDrawnMinutesAndSeconds & 0xFF) != gGameSeconds) // byte
- {
- gLastDrawnMinutesAndSeconds = (gLastDrawnMinutesAndSeconds & 0xFF00) + gGameSeconds; // byte
- convertNumberTo3DigitStringWithPadding0(gGameSeconds, number);
- // loc_4FE2C: ; CODE XREF: drawGameTime+2Aj
- drawTextWithChars8FontToGamePanel(208, 3, 6, &number[1]); // seconds
- }
-
- // loc_4FE36: ; CODE XREF: drawGameTime+12j
- if ((gLastDrawnMinutesAndSeconds >> 8) != gGameMinutes) // byte
- {
- gLastDrawnMinutesAndSeconds = (gGameMinutes << 8) + (gLastDrawnMinutesAndSeconds & 0x00FF); // byte
- convertNumberTo3DigitStringWithPadding0(gGameMinutes, number);
- drawTextWithChars8FontToGamePanel(184, 3, 6, &number[1]); // minutes
- }
-
- // loc_4FE5F: ; CODE XREF: drawGameTime+40j
- if (gLastDrawnHours != gGameHours)
- {
- gLastDrawnHours = gGameHours;
- convertNumberTo3DigitStringWithPadding0(gGameHours, number);
- drawTextWithChars8FontToGamePanel(160, 3, 6, &number[1]); // hours
- }
-}
-
-void drawGamePanel() // sub_501C0 proc near ; CODE XREF: start+338p handleGameUserInput+678p ...
-{
- clearGamePanel();
- drawGamePanelText();
-}
-
-void drawSpeedFixTitleAndVersion() // proc near ; CODE XREF: start+2E6p
-{
- drawTextWithChars6FontWithOpaqueBackgroundIfPossible(102, 11, 1, "SUPAPLEX VERSION " VERSION_STRING);
-}
-
-void drawSpeedFixCredits() // showNewCredits proc near ; CODE XREF: start+2ECp
-{
- drawTextWithChars6FontWithOpaqueBackgroundIfPossible(60, 168, 0xE, "VERSIONS 1-4 + 6.X BY HERMAN PERK");
- drawTextWithChars6FontWithOpaqueBackgroundIfPossible(60, 176, 0xE, "VERSIONS 5.X BY ELMER PRODUCTIONS");
- drawTextWithChars6FontWithOpaqueBackgroundIfPossible(60, 184, 0xE, " VERSION 7.X BY SERGIO PADRINO ");
-
- videoLoop();
-
- do
- {
- // loc_502F1: // ; CODE XREF: drawSpeedFixCredits+28j
- int9handler(1);
-
- if (gIsScrollLockPressed == 1)
- {
- gIsDebugModeEnabled = 1;
- }
- // loc_50301: // ; CODE XREF: drawSpeedFixCredits+1Ej
- } while (isAnyKeyPressed() == 0 && isAnyGameControllerButtonPressed() == 0);
-}
+/*
+ * This file is part of the OpenSupaplex distribution (https://github.com/sergiou87/open-supaplex).
+ * Copyright (c) 2020 Sergio Padrino
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "animations.h"
+#include "audio.h"
+#include "buttonBorders.h"
+#include "commandLineParser.h"
+#include "conditionals.h"
+#include "config.h"
+#include "controller.h"
+#include "demo.h"
+#include "file.h"
+#include "globals.h"
+#include "graphics.h"
+#include "input.h"
+#include "keyboard.h"
+#include "logging.h"
+#include "menu.h"
+#include "savegame.h"
+#include "touchscreen.h"
+#include "utils.h"
+#include "video.h"
+#include "virtualKeyboard.h"
+#include "system.h"
+
+#ifdef __PSP__
+#include
+
+// PSP_MODULE_INFO("OpenSupaplex", 0, 7, 1); -> SDL_main sets this for us, for now at least
+PSP_MAIN_THREAD_ATTR(THREAD_ATTR_USER | THREAD_ATTR_VFPU);
+PSP_HEAP_SIZE_KB(-1024);
+#endif
+
+// title1DataBuffer -> A000:4DAC - A000:CAAC
+// title2DataBuffer -> 0x4DD4 - 0xCAD4
+
+// maps are 58 x 22 tiles
+
+typedef enum
+{
+ UserInputNone = 0,
+ UserInputUp = 1,
+ UserInputLeft = 2,
+ UserInputDown = 3,
+ UserInputRight = 4,
+ UserInputSpaceUp = 5,
+ UserInputSpaceLeft = 6,
+ UserInputSpaceDown = 7,
+ UserInputSpaceRight = 8,
+ UserInputSpaceOnly = 9,
+} UserInput;
+
+static const uint8_t kUserInputSpaceAndDirectionOffset = (UserInputSpaceUp - 1);
+
+uint8_t byte_50919 = 0;
+uint8_t byte_5091A = 0;
+UserInput gCurrentUserInput = 0; // byte_50941 -> 0x0631
+uint8_t byte_50946 = 0;
+uint16_t word_50947 = 0;
+uint16_t word_50949 = 0;
+uint8_t byte_50953 = 0;
+uint8_t byte_50954 = 0;
+uint8_t gShouldAutoselectNextLevelToPlay = 0; // byte_51ABE
+uint8_t gHasChangedLevelSetFromAdvancedMenu = 0;
+uint8_t byte_58D47 = 0; //
+uint8_t byte_59821 = 0; //
+uint8_t byte_59822 = 0; //
+uint8_t byte_59823 = 0; //
+uint8_t gDemoRecordingRandomGeneratorSeedHigh = 0; // byte_59B5C
+uint8_t gDemoRecordingRandomGeneratorSeedLow = 0; // byte_59B5F
+uint8_t gToggleFancyEasyTilesThrottleCounter = 0; // byte_59B7A -> data_subrest
+uint8_t gIsShowingFancyTiles = 1; // byte_59B7B -> data_subrstflg
+uint8_t gToggleGravityAutorepeatFlag = 0; // byte_59B7C
+uint8_t gToggleZonksFrozenAutorepeatFlag = 0; // byte_59B7D
+uint8_t gToggleEnemiesFrozenAutorepeatFlag = 0; // byte_59B7E
+uint8_t gDebugSkipPreviousLevelAutorepeatFlag_1 = 0; // byte_59B7F
+uint8_t gDebugSkipPreviousLevelAutorepeatFlag_2 = 0; // byte_59B80
+uint8_t gDebugSkipNextLevelAutorepeatFlag_1 = 0; // byte_59B81
+uint8_t gDebugSkipNextLevelAutorepeatFlag_2 = 0; // byte_59B82
+uint8_t byte_59B83 = 0;
+uint8_t byte_59B86 = 0;
+uint16_t gDemoRecordingRandomGeneratorSeed = 0; // word_5A199
+// uint8_t byte_5A140 = 0; // speedFixMagicNumber inside of level
+uint8_t byte_5A19B = 0;
+uint8_t gIsLevelStartedAsDemo = 0; // byte_5A19C
+uint8_t gDemoRecordingJustStarted = 0; // byte_5A2F8
+uint8_t gHasUserCheated = 0; // byte_5A2F9
+uint8_t byte_5A323 = 0;
+uint16_t word_5A33C = 0;
+uint8_t gHasUserInterruptedDemo = 0; // byte_5A33E
+uint8_t gIsGameBusy = 0; // byte_5A33F -> this was used mainly to avoid some graphic glitches when some text from the main menu was written on the game field
+// uint8_t gIsMouseAvailable = 0; // byte_58487
+uint8_t gLevelListButtonPressed = 0; // byte_50918
+uint8_t gLevelListDownButtonPressed = 0; // byte_50916
+uint8_t gLevelListUpButtonPressed = 0; // byte_50917
+uint8_t gNewPlayerEntryIndex = 0; // byte_59820
+uint8_t gPlayerListButtonPressed = 0; // byte_50912
+uint8_t gPlayerListDownButtonPressed = 0; // byte_50910
+uint8_t gPlayerListUpButtonPressed = 0; // byte_50911
+uint8_t gRankingListButtonPressed = 0; // byte_50915
+uint8_t gRankingListDownButtonPressed = 0; // byte_50913
+uint8_t gRankingListUpButtonPressed = 0; // byte_50914
+uint16_t gCurrentSelectedLevelIndex = 0; // word_51ABC
+uint16_t gNewPlayerNameLength = 0; // word_58475
+
+uint16_t word_5157A = 0x4A62; // -> 0x126A -> (64, 132)
+uint16_t word_5157C = 0x0502; // -> 0x126C -> (97, 132)
+uint16_t kMurphyStillSpriteCoordinates = 0x4A80; // word_5157E -> 0x126E -> (304, 132)
+uint16_t word_51580 = 0x1AB2; // -> 0x1270 -> (0, 32)
+uint16_t word_515A2 = 0x32A2; // -> 0x1292 -> (224, 82) ??
+
+uint16_t word_515C4 = 0x1358; // (240, 178) -> 0x12B4
+
+// { 128, 64 }, // -> 0x2a02 -> 12f6 -> orange disk falling
+
+uint16_t word_5177E = 0x0CAE; // -> 0x129 -> (256, 164)
+uint16_t word_51790 = 0x4A7E; // -> 0x129 -> (288, 132)
+uint16_t word_5182E = 0x2A64; // -> (272, 388)
+uint16_t word_51840 = 0x2A06; // -> 0x129 -> (160, 64)
+uint16_t word_51842 = 0x132C; // -> 0x129 -> (208, 16) confirmed
+uint16_t word_51844 = 0x2A08; // -> 0x129 -> (176, 64)
+uint16_t word_51846 = 0x132A; // -> 0x129 -> (192, 16)
+uint16_t kTerminalOnSpriteCoordinates = 0x2A62; // word_51848 -> 0x1268 -> (256, 388) I don't get the math for this one, but the coordinates are right
+uint16_t word_5184A = 0x2A66; // -> 0x1268 -> (
+uint16_t word_5184C = 0x2A67; // -> 0x1268 -> (
+uint16_t word_5184E = 0x2E36; // -> 0x1268 -> (
+uint16_t word_51850 = 0x2E37; // -> 0x1268 -> (
+uint16_t word_51852 = 0x2A68; // -> 0x1268 -> (
+uint16_t word_51854 = 0x2A69; // -> 0x1268 -> (
+uint16_t word_51856 = 0x2E38; // -> 0x1268 -> (
+uint16_t word_51858 = 0x2E39; // -> 0x1268 -> (
+uint16_t gIsMoveScrollModeEnabled = 0; // word_51A01
+uint16_t gDebugExtraRenderDelay = 1; // this was used to add an extra delay in debug mode using keys 1-9
+uint16_t word_58463 = 0;
+uint8_t gIsInMainMenu = 0;
+uint16_t gAutomaticDemoPlaybackCountdown = 0; // word_58465
+uint16_t word_58467 = 0;
+uint16_t gLevelListThrottleCurrentCounter = 0; // word_58469
+uint16_t gLevelListThrottleNextCounter = 0;
+uint16_t gPlayerListThrottleCurrentCounter = 0; // word_5846D
+uint16_t gPlayerListThrottleNextCounter = 0; // word_5846F
+uint16_t gRankingListThrottleCurrentCounter = 0; // word_58471
+uint16_t gRankingListThrottleNextCounter = 0; // word_58473
+uint16_t gSelectedOriginalDemoIndex = 0; // word_599D6 -> used loading old demo files demo
+uint16_t gSelectedOriginalDemoLevelNumber = 0; // word_599D8 -> used loading old demo files demo -> the high byte is set to -1 in readLevels for some unknown reason
+// These two store the scroll offset to get back to Murphy when we're in "free mode"
+uint16_t gMurphyScrollOffsetX = 0; // word_59B88
+uint16_t gMurphyScrollOffsetY = 0; // word_59B8A
+uint16_t gLevelSetRotationThrottleCurrentCounter = 0; // word_59B8C
+uint16_t gLevelSetRotationThrottleNextCounter = 0; // word_59B8E
+uint16_t gLastDrawnMinutesAndSeconds; // word_510B7
+uint8_t gLastDrawnHours; // byte_510B9
+FILE *gCurrentRecordingDemoFile; // word_510E4
+uint8_t gDemoRecordingLowestSpeed; // speed?2
+int16_t gAdditionalScrollOffsetX; // word_51963
+int16_t gAdditionalScrollOffsetY; // word_51965
+uint8_t isJoystickEnabled = 0; // byte_50940
+uint8_t isMusicEnabled = 0; // byte_59886
+uint8_t isFXEnabled = 0; // byte_59885
+
+uint8_t gIsFlashingBackgroundModeEnabled = 0; // flashingbackgroundon
+const float kSpeedTimeFactors[kNumberOfGameSpeeds] = {3.5, 3.0, 2.5, 2.0, 1.5, 1.0, 0.75, 2.0 / 3.0, 5.0 / 8.0, 3.0 / 5.0, 1.0 / 70.0};
+
+// Used to measure game speed (reference is 35 iterations per second)
+float gGameIterationRate = 0.f;
+uint32_t gGameIterationRateReferenceTime = 0;
+
+// Used to limit game speed
+uint32_t gGameIterationStartTime = 0;
+uint32_t gNumberOfGameIterations = 0;
+
+static const uint16_t kFallAnimationGravityOffsets[18] = {
+ 0x0000, // -> 0x6C95
+ 0x007A, // -> 0x6C97
+ 0x00F4, // -> 0x6C99
+ 0x016E, // -> 0x6C9B
+ 0x01E8, // -> 0x6C9D
+ 0x0262, // -> 0x6C9F
+ 0x02DC, // -> 0x6CA1
+ 0x0356, // -> 0x6CA3
+ 0x03D0, // -> 0x6CA5
+ 0x044A, // -> 0x6CA7
+ 0x04C4, // -> 0x6CA9
+ 0x053E, // -> 0x6CAB
+ 0x05B8, // -> 0x6CAD
+ 0x0632, // -> 0x6CAF
+ 0x06AC, // -> 0x6CB1
+ 0x0726, // -> 0x6CB3
+ 0x07A0, // -> 0x6CB5
+ 0x081A, // -> 0x6CB7
+};
+
+static const int kConfigDataLength = 4;
+
+uint16_t gRandomGeneratorSeed = 0;
+
+enum MouseButton
+{
+ MouseButtonLeft = 1 << 0,
+ MouseButtonRight = 1 << 1,
+};
+
+uint8_t gShouldCloseAdvancedMenu = 0;
+uint8_t gAdvancedMenuRecordDemoIndex = 0;
+uint8_t gAdvancedMenuPlayDemoIndex = 0;
+
+typedef struct
+{
+ uint16_t startX, startY;
+ uint16_t endX, endY;
+ void (*handler)(void);
+} ButtonDescriptor;
+
+void handleNewPlayerOptionClick(void);
+void handlePlayerListScrollUp(void);
+void handlePlayerListScrollDown(void);
+void handlePlayerListClick(void);
+void handleLevelListScrollUp(void);
+void handleLevelListScrollDown(void);
+void handleRankingListScrollUp(void);
+void handleRankingListScrollDown(void);
+void handleLevelCreditsClick(void);
+void handleGfxTutorOptionClick(void);
+void handleDemoOptionClick(void);
+void handleSkipLevelOptionClick(void);
+void handleFloppyDiskButtonClick(void);
+void handleDeletePlayerOptionClick(void);
+void handleStatisticsOptionClick(void);
+void handleControlsOptionClick(void);
+void handleOkButtonClick(void);
+
+#define kNumberOfMainMenuButtons 17
+static const ButtonDescriptor kMainMenuButtonDescriptors[kNumberOfMainMenuButtons] = { // located in DS:0000
+ {
+ 5, 6,
+ 157, 14,
+ handleNewPlayerOptionClick, // New player
+ },
+ {
+ 5, 15,
+ 157, 23,
+ handleDeletePlayerOptionClick, // Delete player
+ },
+ {
+ 5, 24,
+ 157, 32,
+ handleSkipLevelOptionClick, // Skip level
+ },
+ {
+ 5, 33,
+ 157, 41,
+ handleStatisticsOptionClick, // Statistics
+ },
+ {
+ 5, 42,
+ 157, 50,
+ handleGfxTutorOptionClick, // GFX-tutor
+ },
+ {
+ 5, 51,
+ 157, 59,
+ handleDemoOptionClick, // Demo
+ },
+ {
+ 5, 60,
+ 157, 69,
+ handleControlsOptionClick, // Controls
+ },
+ {
+ 140, 90,
+ 155, 108,
+ handleRankingListScrollUp, // Rankings arrow up
+ },
+ {
+ 140, 121,
+ 155, 138,
+ handleRankingListScrollDown, // Rankings arrow down
+ },
+ {
+ 96, 140,
+ 115, 163,
+ handleOkButtonClick, // Ok button
+ },
+ {
+ 83, 168,
+ 126, 192,
+ handleFloppyDiskButtonClick, // Insert data disk according to https://supaplex.fandom.com/wiki/Main_menu
+ },
+ {
+ 11, 142,
+ 67, 153,
+ handlePlayerListScrollUp, // Players arrow up
+ },
+ {
+ 11, 181,
+ 67, 192,
+ handlePlayerListScrollDown, // Players arrow down
+ },
+ {
+ 11, 154,
+ 67, 180,
+ handlePlayerListClick, // Players list area
+ },
+ {
+ 142, 142,
+ 306, 153,
+ handleLevelListScrollUp, // Levels arrow up
+ },
+ {
+ 142, 181,
+ 306, 192,
+ handleLevelListScrollDown, // Levels arrow down
+ },
+ {
+ 297, 37,
+ 312, 52,
+ handleLevelCreditsClick, // Credits
+ }};
+
+void handleOptionsExitAreaClick(void);
+void handleOptionsMusicClick(void);
+void handleOptionsAdlibClick(void);
+void handleOptionsSoundBlasterClick(void);
+void handleOptionsRolandClick(void);
+void handleOptionsCombinedClick(void);
+void handleOptionsStandardClick(void);
+void handleOptionsSamplesClick(void);
+void handleOptionsInternalClick(void);
+void handleOptionsFXClick(void);
+void handleOptionsKeyboardClick(void);
+void handleOptionsJoystickClick(void);
+
+#define kNumberOfOptionsMenuButtons 13
+static const ButtonDescriptor kOptionsMenuButtonDescriptors[kNumberOfOptionsMenuButtons] = {
+ // located in DS:00AC
+ {
+ 12, 13,
+ 107, 36,
+ handleOptionsAdlibClick, // Adlib
+ },
+ {
+ 12, 49,
+ 107, 72,
+ handleOptionsSoundBlasterClick, // Sound Blaster
+ },
+ {
+ 12, 85,
+ 107, 108,
+ handleOptionsRolandClick, // Roland
+ },
+ {
+ 12, 121,
+ 107, 144,
+ handleOptionsCombinedClick, // Combined
+ },
+ {
+ 132, 13,
+ 211, 31,
+ handleOptionsInternalClick, // Internal
+ },
+ {
+ 126, 43,
+ 169, 54,
+ handleOptionsStandardClick, // Standard
+ },
+ {
+ 174, 43,
+ 217, 54,
+ handleOptionsSamplesClick, // Samples
+ },
+ {
+ 132, 86,
+ 175, 120,
+ handleOptionsMusicClick, // Music
+ },
+ {
+ 134, 132,
+ 168, 152,
+ handleOptionsFXClick, // FX
+ },
+ {
+ 201, 80,
+ 221, 154,
+ handleOptionsKeyboardClick, // Keyboard
+ },
+ {
+ 233, 80,
+ 252, 154,
+ handleOptionsJoystickClick, // Joystick
+ },
+ {
+ 0, 181,
+ 319, 199,
+ handleOptionsExitAreaClick, // Exit (bottom)
+ },
+ {
+ 284, 0,
+ 319, 180,
+ handleOptionsExitAreaClick, // Exit (right)
+ },
+};
+
+typedef void (*MovingFunction)(int16_t);
+typedef void (*FrameBasedMovingFunction)(int16_t, uint8_t);
+
+void updateZonkTiles(int16_t position);
+void updateInfotronTiles(int16_t position);
+void updateOrangeDiskTiles(int16_t position);
+void updateSnikSnakTiles(int16_t position);
+void updateTerminalTiles(int16_t position);
+void updateElectronTiles(int16_t position);
+void updateBugTiles(int16_t position);
+void updateExplosionTiles(int16_t position);
+
+static const MovingFunction movingFunctions[32] = {
+ NULL,
+ updateZonkTiles,
+ NULL,
+ NULL,
+ updateInfotronTiles,
+ NULL,
+ NULL,
+ NULL,
+ updateOrangeDiskTiles,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ updateSnikSnakTiles,
+ NULL,
+ updateTerminalTiles,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ updateElectronTiles,
+ updateBugTiles,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ updateExplosionTiles,
+};
+
+void updateElectronTurnLeft(int16_t position, uint8_t frame);
+void updateElectronTurnRight(int16_t position, uint8_t frame);
+void updateElectronMovementUp(int16_t position, uint8_t frame);
+void updateElectronMovementDown(int16_t position, uint8_t frame);
+void updateElectronMovementRight(int16_t position, uint8_t frame);
+void updateElectronMovementLeft(int16_t position, uint8_t frame);
+
+static const FrameBasedMovingFunction kElectronMovingFunctions[] = {
+ updateElectronTurnLeft,
+ updateElectronTurnLeft,
+ updateElectronTurnLeft,
+ updateElectronTurnLeft,
+ updateElectronTurnLeft,
+ updateElectronTurnLeft,
+ updateElectronTurnLeft,
+ updateElectronTurnLeft,
+ updateElectronTurnRight,
+ updateElectronTurnRight,
+ updateElectronTurnRight,
+ updateElectronTurnRight,
+ updateElectronTurnRight,
+ updateElectronTurnRight,
+ updateElectronTurnRight,
+ updateElectronTurnRight,
+ updateElectronMovementUp,
+ updateElectronMovementUp,
+ updateElectronMovementUp,
+ updateElectronMovementUp,
+ updateElectronMovementUp,
+ updateElectronMovementUp,
+ updateElectronMovementUp,
+ updateElectronMovementUp,
+ updateElectronMovementDown,
+ updateElectronMovementDown,
+ updateElectronMovementDown,
+ updateElectronMovementDown,
+ updateElectronMovementDown,
+ updateElectronMovementDown,
+ updateElectronMovementDown,
+ updateElectronMovementDown,
+ updateElectronMovementRight,
+ updateElectronMovementRight,
+ updateElectronMovementRight,
+ updateElectronMovementRight,
+ updateElectronMovementRight,
+ updateElectronMovementRight,
+ updateElectronMovementRight,
+ updateElectronMovementRight,
+ updateElectronMovementLeft,
+ updateElectronMovementLeft,
+ updateElectronMovementLeft,
+ updateElectronMovementLeft,
+ updateElectronMovementLeft,
+ updateElectronMovementLeft,
+ updateElectronMovementLeft,
+ updateElectronMovementLeft,
+};
+
+void updateSnikSnakTurnLeft(int16_t position, uint8_t frame);
+void updateSnikSnakTurnRight(int16_t position, uint8_t frame);
+void updateSnikSnakMovementUp(int16_t position, uint8_t frame);
+void updateSnikSnakMovementLeft(int16_t position, uint8_t frame);
+void updateSnikSnakMovementDown(int16_t position, uint8_t frame);
+void updateSnikSnakMovementRight(int16_t position, uint8_t frame);
+
+static const FrameBasedMovingFunction kSnikSnakMovingFunctions[48] = {
+ updateSnikSnakTurnLeft,
+ updateSnikSnakTurnLeft,
+ updateSnikSnakTurnLeft,
+ updateSnikSnakTurnLeft,
+ updateSnikSnakTurnLeft,
+ updateSnikSnakTurnLeft,
+ updateSnikSnakTurnLeft,
+ updateSnikSnakTurnLeft,
+ updateSnikSnakTurnRight,
+ updateSnikSnakTurnRight,
+ updateSnikSnakTurnRight,
+ updateSnikSnakTurnRight,
+ updateSnikSnakTurnRight,
+ updateSnikSnakTurnRight,
+ updateSnikSnakTurnRight,
+ updateSnikSnakTurnRight,
+ updateSnikSnakMovementUp,
+ updateSnikSnakMovementUp,
+ updateSnikSnakMovementUp,
+ updateSnikSnakMovementUp,
+ updateSnikSnakMovementUp,
+ updateSnikSnakMovementUp,
+ updateSnikSnakMovementUp,
+ updateSnikSnakMovementUp,
+ updateSnikSnakMovementLeft,
+ updateSnikSnakMovementLeft,
+ updateSnikSnakMovementLeft,
+ updateSnikSnakMovementLeft,
+ updateSnikSnakMovementLeft,
+ updateSnikSnakMovementLeft,
+ updateSnikSnakMovementLeft,
+ updateSnikSnakMovementLeft,
+ updateSnikSnakMovementDown,
+ updateSnikSnakMovementDown,
+ updateSnikSnakMovementDown,
+ updateSnikSnakMovementDown,
+ updateSnikSnakMovementDown,
+ updateSnikSnakMovementDown,
+ updateSnikSnakMovementDown,
+ updateSnikSnakMovementDown,
+ updateSnikSnakMovementRight,
+ updateSnikSnakMovementRight,
+ updateSnikSnakMovementRight,
+ updateSnikSnakMovementRight,
+ updateSnikSnakMovementRight,
+ updateSnikSnakMovementRight,
+ updateSnikSnakMovementRight,
+ updateSnikSnakMovementRight,
+};
+
+void throttledRotateLevelSet(uint8_t descending);
+void rotateLevelSet(uint8_t descending);
+void updateMenuAfterLevelSetChanged(void);
+void initializeGameStateData(void);
+void startDirectlyFromLevel(uint8_t levelNumber);
+void stopDemoAndPlay(void);
+void emulateClock(void);
+void loadScreen2(void);
+void readEverything(void);
+void drawSpeedFixTitleAndVersion(void);
+void openCreditsBlock(void);
+void drawSpeedFixCredits(void);
+void readConfig(void);
+void activateAdlibSound(void);
+void activateSoundBlasterSound(void);
+void activateRolandSound(void);
+void activateCombinedSound(void);
+void activateInternalStandardSound(void);
+void activateInternalSamplesSound(void);
+void prepareDemoRecordingFilename(void);
+void runMainMenu(void);
+void convertNumberTo3DigitPaddedString(uint8_t number, char numberString[3], char useSpacesForPadding);
+void stopMusicAndSounds(void);
+void playMusicIfNeeded(void);
+void stopMusic(void);
+void playExplosionSound(void);
+void playInfotronSound(void);
+void playPushSound(void);
+void playFallSound(void);
+void playBugSound(void);
+void playBaseSound(void);
+void playExitSound(void);
+void sound11(void);
+void savePlayerListData(void);
+void saveHallOfFameData(void);
+void getMouseStatus(uint16_t *mouseX, uint16_t *mouseY, uint16_t *mouseButtonStatus);
+void drawMainMenuButtonBorders(void);
+void drawMainMenuButtonBorder(ButtonBorderDescriptor border, uint8_t color);
+void generateRandomSeedFromClock(void);
+void initializeFadePalette(void);
+void initializeMouse(void);
+void prepareLevelDataForCurrentPlayer(void);
+void drawPlayerList(void);
+void drawLevelList(void);
+void drawHallOfFame(void);
+void drawRankings(void);
+void drawTextWithChars6FontWithOpaqueBackgroundIfPossible(size_t destX, size_t destY, uint8_t color, const char *text);
+void drawTextWithChars6FontWithTransparentBackgroundIfPossible(size_t destX, size_t destY, uint8_t color, const char *text);
+void waitForKeyMouseOrJoystick(void);
+void drawMenuTitleAndDemoLevelResult(void);
+void scrollRightToNewScreen(void);
+void scrollLeftToMainMenu(void);
+void convertNumberTo3DigitStringWithPadding0(uint8_t number, char numberString[3]);
+void changePlayerCurrentLevelState(void);
+void updateHallOfFameEntries(void);
+void drawSoundTypeOptionsSelection(uint8_t *destBuffer);
+void dimOptionsButtonText(size_t startX, size_t startY, size_t width, size_t height, uint8_t *destBuffer);
+void drawOptionsMenuLine(ButtonBorderDescriptor border, uint8_t color, uint8_t *destBuffer);
+void highlightOptionsButtonText(size_t startX, size_t startY, size_t width, size_t height, uint8_t *destBuffer);
+void drawAudioOptionsSelection(uint8_t *destBuffer);
+void drawInputOptionsSelection(uint8_t *destBuffer);
+void updateOptionsMenuState(uint8_t *destBuffer);
+void convertLevelNumberTo3DigitStringWithPadding0(uint8_t number);
+void readLevels(void);
+void initializeGameInfo(void);
+void drawGamePanel(void);
+void drawNumberOfRemainingInfotrons(void);
+void drawGameTime(void);
+uint16_t convertToEasyTiles(void);
+void resetNumberOfInfotrons(uint16_t numberOfInfotronsInGameField);
+void findMurphy(void);
+void drawGamePanelText(void);
+void scrollToMurphy(void);
+void runLevel(void);
+void slideDownGameDash(void);
+void updateScrollOffset(void);
+uint16_t generateRandomNumber(void);
+void handleGameUserInput(void);
+void loc_49C41(void);
+void loc_49C2C(char text[3]);
+void showSavegameOperationError(void);
+void saveGameSnapshot(void);
+void loadGameSnapshot(void);
+void checkDebugKeys(void);
+void loc_4988E(void);
+void restoreOriginalFancyTiles(void);
+void updateMovingObjects(void);
+int16_t updateMurphy(int16_t position);
+int16_t updateMurphyAnimation(int16_t position);
+int16_t updateMurphyAnimationInfo(int16_t position, MurphyAnimationDescriptor unknownMurphyData);
+int16_t handleMurphyDirectionRight(int16_t position);
+int16_t handleMurphyDirectionDown(int16_t position);
+int16_t handleMurphyDirectionLeft(int16_t position);
+int16_t handleMurphyDirectionUp(int16_t position);
+void detonateYellowDisks(void);
+void updateUserInputInScrollMovementMode(void);
+void updateUserInput(void);
+void saveInputForDemo(void);
+void simulateDemoInput(void);
+void fetchAndInitializeLevel(void);
+void removeTiles(LevelTileType tileType);
+void restartLevel(void);
+void restartLevelWithoutAddingCurrentGameTimeToPlayer(void);
+void recordDemo(uint16_t demoIndex);
+void stopRecordingDemo(void);
+void debugSkipLevel(void);
+void forceRestoreOriginalFancyTiles(void);
+void drawNumberOfRemainingRedDisks(void);
+void clearAdditionalInfoInGamePanelIfNeeded(void);
+void updatePlantedRedDisk(void);
+void updateExplosionTimers(void);
+void addCurrentGameTimeToPlayer(void);
+void drawFailedLevelResultScreen(void);
+void handleZonkStateAfterFallingOneTile(int16_t position);
+void detonateBigExplosion(int16_t position);
+void detonateZonk(int16_t position, uint8_t state, uint8_t tile);
+void sub_4AA34(int16_t position, uint8_t state, uint8_t tile);
+void sub_4AAB4(int16_t position);
+uint8_t checkMurphyMovementToPosition(int16_t position, UserInput userInput);
+void handleZonkPushedByMurphy(int16_t position);
+void decreaseRemainingRedDisksIfNeeded(int16_t position);
+void updateSpecialPort(int16_t position);
+void handleInfotronStateAfterFallingOneTile(int16_t position);
+void int9handler(uint8_t shouldYieldCpu);
+void updateDemoRecordingLowestSpeed(void);
+void playDemo(uint16_t demoIndex);
+
+static const char *kAdvancedConfigGeneralSection = "general";
+static const char *kAdvancedConfigDebugSection = "debug";
+
+static const char *kAdvancedConfigPlayerKey = "player";
+static const char *kAdvancedConfigLevelSetKey = "levelSet";
+static const char *kAdvancedConfigGameSpeedKey = "gameSpeed";
+static const char *kAdvancedConfigMusicVolumeKey = "musicVolume";
+static const char *kAdvancedConfigFXVolumeKey = "fxVolume";
+static const char *kAdvancedConfigScalingModeKey = "scalingMode";
+static const char *kAdvancedConfigFullscreenKey = "fullscreen";
+static const char *kAdvancedConfigDisplayFPSKey = "displayFPS";
+static const char *kAdvancedConfigLimitFPSKey = "limitFPS";
+
+void readAdvancedConfig()
+{
+ Config *config = initializeConfigForReading("ADVANCED.CFG");
+
+ if (config == NULL)
+ {
+ spLogInfo("Couldn't read advanced config");
+ return;
+ }
+
+ char currentSuffix[3] = "AT";
+ strcpy(currentSuffix, &gLevelsDatFilename[8]);
+
+ int player = readConfigInt(config, kAdvancedConfigGeneralSection, kAdvancedConfigPlayerKey, 0);
+ player = CLAMP(player, 0, kNumberOfPlayers - 1);
+ gCurrentPlayerIndex = player;
+
+ // Only apply level set from config if it wasn't overriden before (by command line)
+ if (strcmp(currentSuffix, "AT") == 0)
+ {
+ int levelSet = readConfigInt(config, kAdvancedConfigGeneralSection, kAdvancedConfigLevelSetKey, 0);
+
+ if (levelSet != 0)
+ {
+ char newSuffix[3] = "00";
+ snprintf(newSuffix, 3, "%02d", levelSet);
+
+ strcpy(&gLevelsDatFilename[8], newSuffix);
+ strcpy(&gLevelLstFilename[7], newSuffix);
+ strcpy(&gDemo0BinFilename[7], newSuffix);
+ strcpy(&gPlayerLstFilename[8], newSuffix);
+ strcpy(&gHallfameLstFilename[10], newSuffix);
+
+ if (gShouldAlwaysWriteSavegameSav == 0) // cmp byte ptr gShouldAlwaysWriteSavegameSav, 0
+ {
+ strcpy(&gSavegameSavFilename[10], newSuffix);
+ }
+ }
+ }
+
+ gGameSpeed = readConfigInt(config, kAdvancedConfigGeneralSection, kAdvancedConfigGameSpeedKey, kDefaultGameSpeed);
+
+ int volume = readConfigInt(config, kAdvancedConfigGeneralSection, kAdvancedConfigMusicVolumeKey, getMusicVolume());
+ setMusicVolume(volume);
+
+ volume = readConfigInt(config, kAdvancedConfigGeneralSection, kAdvancedConfigFXVolumeKey, getSoundEffectsVolume());
+ setSoundEffectsVolume(volume);
+
+ int scalingMode = readConfigInt(config, kAdvancedConfigGeneralSection, kAdvancedConfigScalingModeKey, getScalingMode());
+ setScalingMode(scalingMode);
+
+ gShouldShowFPS = readConfigInt(config, kAdvancedConfigDebugSection, kAdvancedConfigDisplayFPSKey, gShouldShowFPS);
+ gShouldLimitFPS = readConfigInt(config, kAdvancedConfigDebugSection, kAdvancedConfigLimitFPSKey, gShouldLimitFPS);
+
+ int fullscreen = readConfigInt(config, kAdvancedConfigGeneralSection, kAdvancedConfigFullscreenKey, getFullscreenMode());
+ setFullscreenMode(fullscreen);
+
+ destroyConfig(config);
+}
+
+void writeAdvancedConfig()
+{
+ Config *config = initializeConfigForWriting("ADVANCED.CFG");
+
+ if (config == NULL)
+ {
+ spLogInfo("Couldn't write advanced config");
+ return;
+ }
+
+ writeConfigSection(config, kAdvancedConfigGeneralSection);
+
+ char currentSuffix[3] = "AT";
+ strcpy(currentSuffix, &gLevelsDatFilename[8]);
+
+ int levelSet = 0;
+
+ if (strcmp(currentSuffix, "AT") != 0)
+ {
+ levelSet = atoi(currentSuffix);
+ }
+
+ writeConfigInt(config, kAdvancedConfigPlayerKey, gCurrentPlayerIndex);
+ writeConfigInt(config, kAdvancedConfigLevelSetKey, levelSet);
+ writeConfigInt(config, kAdvancedConfigGameSpeedKey, gGameSpeed);
+ writeConfigInt(config, kAdvancedConfigMusicVolumeKey, getMusicVolume());
+ writeConfigInt(config, kAdvancedConfigFXVolumeKey, getSoundEffectsVolume());
+ writeConfigInt(config, kAdvancedConfigScalingModeKey, getScalingMode());
+ writeConfigInt(config, kAdvancedConfigFullscreenKey, getFullscreenMode());
+
+ writeConfigSection(config, kAdvancedConfigDebugSection);
+ writeConfigInt(config, kAdvancedConfigDisplayFPSKey, gShouldShowFPS);
+ writeConfigInt(config, kAdvancedConfigLimitFPSKey, gShouldLimitFPS);
+
+ destroyConfig(config);
+}
+
+/// @return 1 if the action was to go back / close the menu
+uint8_t handleAdvancedOptionsMenuInput(AdvancedOptionsMenu *menu)
+{
+ if (isUpButtonPressed())
+ {
+ playBaseSound();
+ moveUpAdvancedOptionsSelectedEntry(menu);
+ }
+
+ if (isDownButtonPressed())
+ {
+ playBaseSound();
+ moveDownAdvancedOptionsSelectedEntry(menu);
+ }
+
+ if (isLeftButtonPressed())
+ {
+ playBaseSound();
+ decreaseAdvancedOptionsSelectedEntry(menu);
+ }
+
+ if (isRightButtonPressed())
+ {
+ playBaseSound();
+ increaseAdvancedOptionsSelectedEntry(menu);
+ }
+
+ if (isMenuConfirmButtonPressed())
+ {
+ playInfotronSound();
+ selectAdvancedOptionsSelectedEntry(menu);
+ }
+
+ if (isMenuCancelButtonPressed())
+ {
+ playPushSound();
+ return 1;
+ }
+
+ if (isPauseButtonPressed())
+ {
+ playPushSound();
+ gShouldCloseAdvancedMenu = 1;
+ return 1;
+ }
+
+ if (gShouldCloseAdvancedMenu)
+ {
+ return 1;
+ }
+
+ return 0;
+}
+
+void advancedOptionsMenuWaitForKeyPress()
+{
+ do
+ {
+ int9handler(1);
+ } while (isUpButtonPressed() == 0 && isDownButtonPressed() == 0 && isLeftButtonPressed() == 0 && isRightButtonPressed() == 0 && isMenuBackButtonPressed() == 0 && isMenuConfirmButtonPressed() == 0 && isMenuCancelButtonPressed() == 0 && isPauseButtonPressed() == 0);
+}
+
+void advancedOptionsMenuWaitForKeyRelease()
+{
+ do
+ {
+ int9handler(1);
+ } while (isUpButtonPressed() || isDownButtonPressed() || isLeftButtonPressed() || isRightButtonPressed() || isMenuBackButtonPressed() || isMenuConfirmButtonPressed() || isMenuCancelButtonPressed() || isPauseButtonPressed());
+}
+
+void runAdvancedOptionsMenu(AdvancedOptionsMenu *menu)
+{
+ playFallSound();
+
+ advancedOptionsMenuWaitForKeyRelease();
+
+ do
+ {
+ if (handleAdvancedOptionsMenuInput(menu))
+ {
+ break;
+ }
+
+ renderAdvancedOptionsMenu(menu);
+ advancedOptionsMenuWaitForKeyRelease();
+ advancedOptionsMenuWaitForKeyPress();
+ } while (1);
+
+ advancedOptionsMenuWaitForKeyRelease();
+}
+
+void buildGameSpeedOptionTitle(char output[kMaxAdvancedOptionsMenuEntryTitleLength])
+{
+ snprintf(output, kMaxAdvancedOptionsMenuEntryTitleLength, "GAME SPEED: %d", gGameSpeed);
+}
+
+void buildMusicVolumeOptionTitle(char output[kMaxAdvancedOptionsMenuEntryTitleLength])
+{
+ snprintf(output, kMaxAdvancedOptionsMenuEntryTitleLength, "MUSIC VOLUME: %d", getMusicVolume());
+}
+
+void buildFXVolumeOptionTitle(char output[kMaxAdvancedOptionsMenuEntryTitleLength])
+{
+ snprintf(output, kMaxAdvancedOptionsMenuEntryTitleLength, "FX VOLUME: %d", getSoundEffectsVolume());
+}
+
+void buildScalingModeOptionTitle(char output[kMaxAdvancedOptionsMenuEntryTitleLength])
+{
+ static const char *kAspectFitScalingModeString = "NORMAL";
+ static const char *kAspectFillScalingModeString = "ZOOM";
+ static const char *kIntegerFactorScalingModeString = "INTEGER FACTOR";
+ static const char *kFullscreenScalingModeString = "FULLSCREEN";
+ static const char *kAspectCorrectScalingModeString = "4:3";
+
+ const char *mode = kAspectFitScalingModeString;
+
+ switch (getScalingMode())
+ {
+ case ScalingModeAspectFill:
+ mode = kAspectFillScalingModeString;
+ break;
+ case ScalingModeIntegerFactor:
+ mode = kIntegerFactorScalingModeString;
+ break;
+ case ScalingModeFullscreen:
+ mode = kFullscreenScalingModeString;
+ break;
+ case ScalingModeAspectCorrect:
+ mode = kAspectCorrectScalingModeString;
+ break;
+ default:
+ mode = kAspectFitScalingModeString;
+ break;
+ }
+
+ snprintf(output, kMaxAdvancedOptionsMenuEntryTitleLength, "SCALING MODE: %s", mode);
+}
+
+void buildBooleanOptionTitle(char output[kMaxAdvancedOptionsMenuEntryTitleLength], char title[kMaxAdvancedOptionsMenuEntryTitleLength], uint8_t value)
+{
+ snprintf(output, kMaxAdvancedOptionsMenuEntryTitleLength, "%s: %s", title, (value == 0 ? "OFF" : "ON"));
+}
+
+void buildDisplayFPSOptionTitle(char output[kMaxAdvancedOptionsMenuEntryTitleLength])
+{
+ buildBooleanOptionTitle(output, "DISPLAY FPS", gShouldShowFPS);
+}
+
+void buildLimitFPSOptionTitle(char output[kMaxAdvancedOptionsMenuEntryTitleLength])
+{
+ buildBooleanOptionTitle(output, "LIMIT FPS", gShouldLimitFPS);
+}
+
+void buildPlayDemoOptionTitle(char output[kMaxAdvancedOptionsMenuEntryTitleLength])
+{
+ snprintf(output, kMaxAdvancedOptionsMenuEntryTitleLength, "PLAY DEMO: %d", gAdvancedMenuPlayDemoIndex);
+}
+
+void buildRecordDemoOptionTitle(char output[kMaxAdvancedOptionsMenuEntryTitleLength])
+{
+ snprintf(output, kMaxAdvancedOptionsMenuEntryTitleLength, "RECORD DEMO: %d", gAdvancedMenuRecordDemoIndex);
+}
+
+void increaseGameSpeed()
+{
+ if (gGameSpeed < kNumberOfGameSpeeds - 1)
+ {
+ gGameSpeed++;
+ }
+
+ updateDemoRecordingLowestSpeed();
+}
+
+void decreaseGameSpeed()
+{
+ if (gGameSpeed > 0)
+ {
+ gGameSpeed--;
+ }
+
+ updateDemoRecordingLowestSpeed();
+}
+
+void updateDemoRecordingLowestSpeed()
+{
+ if (gGameSpeed < gDemoRecordingLowestSpeed)
+ {
+ gDemoRecordingLowestSpeed = gGameSpeed;
+ }
+}
+
+void increaseMusicVolume()
+{
+ uint8_t volume = getMusicVolume();
+
+ if (volume < kMaxVolume)
+ {
+ setMusicVolume(volume + 1);
+ }
+}
+
+void decreaseMusicVolume()
+{
+ uint8_t volume = getMusicVolume();
+
+ if (volume > 0)
+ {
+ setMusicVolume(volume - 1);
+ }
+}
+
+void increaseFXVolume()
+{
+ uint8_t volume = getSoundEffectsVolume();
+
+ if (volume < kMaxVolume)
+ {
+ setSoundEffectsVolume(volume + 1);
+ }
+}
+
+void decreaseAdvancedMenuRecordDemoIndex()
+{
+ if (gAdvancedMenuRecordDemoIndex > 0)
+ {
+ gAdvancedMenuRecordDemoIndex--;
+ }
+}
+
+void increaseAdvancedMenuRecordDemoIndex()
+{
+ if (gAdvancedMenuRecordDemoIndex < kNumberOfDemos - 1)
+ {
+ gAdvancedMenuRecordDemoIndex++;
+ }
+}
+
+void decreaseAdvancedMenuPlayDemoIndex()
+{
+ if (gAdvancedMenuPlayDemoIndex > 0)
+ {
+ gAdvancedMenuPlayDemoIndex--;
+ }
+}
+
+void increaseAdvancedMenuPlayDemoIndex()
+{
+ if (gAdvancedMenuPlayDemoIndex < kNumberOfDemos - 1)
+ {
+ gAdvancedMenuPlayDemoIndex++;
+ }
+}
+
+void decreaseAdvancedMenuScalingMode()
+{
+ ScalingMode mode = getScalingMode();
+ if (mode > 0)
+ {
+ setScalingMode(mode - 1);
+ }
+ else
+ {
+ setScalingMode(ScalingModeCount - 1);
+ }
+}
+
+void increaseAdvancedMenuScalingMode()
+{
+ ScalingMode mode = getScalingMode();
+ if (mode < ScalingModeCount - 1)
+ {
+ setScalingMode(mode + 1);
+ }
+ else
+ {
+ setScalingMode(0);
+ }
+}
+
+void decreaseFXVolume()
+{
+ uint8_t volume = getSoundEffectsVolume();
+
+ if (volume > 0)
+ {
+ setSoundEffectsVolume(volume - 1);
+ }
+}
+
+void toggleDisplayFPSOption()
+{
+ TOGGLE_BOOL(gShouldShowFPS);
+}
+
+void toggleLimitFPSOption()
+{
+ TOGGLE_BOOL(gShouldLimitFPS);
+}
+
+void handleResumeOptionSelection()
+{
+ gShouldCloseAdvancedMenu = 1;
+}
+
+void handleRestartLevelOptionSelection()
+{
+ restartLevel();
+ gShouldCloseAdvancedMenu = 1;
+}
+
+void handleRemoveZonksOptionSelection()
+{
+ removeTiles(LevelTileTypeZonk);
+ gShouldCloseAdvancedMenu = 1;
+}
+
+void handleRemoveHardwareOptionSelection()
+{
+ removeTiles(LevelTileTypeHardware);
+ gShouldCloseAdvancedMenu = 1;
+}
+
+void handleRemoveBaseOptionSelection()
+{
+ removeTiles(LevelTileTypeBase);
+ gShouldCloseAdvancedMenu = 1;
+}
+
+void handleRemoveChipsOptionSelection()
+{
+ removeTiles(LevelTileTypeChip);
+ gShouldCloseAdvancedMenu = 1;
+}
+
+void handleRemoveSnikSnakOptionSelection()
+{
+ removeTiles(LevelTileTypeSnikSnak);
+ gShouldCloseAdvancedMenu = 1;
+}
+
+void handleMoveScrollOptionSelection()
+{
+ gIsMoveScrollModeEnabled = 1;
+ gShouldCloseAdvancedMenu = 1;
+}
+
+void handleRecordDemoOptionSelection()
+{
+ recordDemo(gAdvancedMenuRecordDemoIndex);
+ gShouldCloseAdvancedMenu = 1;
+}
+
+void handlePlayDemoOptionSelection()
+{
+ playDemo(gAdvancedMenuPlayDemoIndex);
+ gShouldCloseAdvancedMenu = 1;
+}
+
+void handleStopDemoAndPlayOptionSelection()
+{
+ stopDemoAndPlay();
+ gShouldCloseAdvancedMenu = 1;
+}
+
+void handleStopRecordingDemoOptionSelection()
+{
+ stopRecordingDemo();
+ gShouldCloseAdvancedMenu = 1;
+}
+
+void handleExitLevelOptionSelection()
+{
+ gShouldKillMurphy = 1;
+ gShouldCloseAdvancedMenu = 1;
+}
+
+void handleExitGameOptionSelection()
+{
+ gShouldExitGame = 1;
+ gShouldCloseAdvancedMenu = 1;
+}
+
+void runAdvancedOptionsSubMenu(AdvancedOptionsMenu menu)
+{
+ runAdvancedOptionsMenu(&menu);
+}
+
+void handleDebugOptionSelection()
+{
+ AdvancedOptionsMenu menu;
+ initializeAdvancedOptionsMenu(&menu);
+
+ strncpy(menu.title, "DEBUG (DANGER)", kMaxAdvancedOptionsMenuEntryTitleLength);
+
+ addAdvancedOptionsEntry(&menu, (AdvancedOptionsMenuEntry){
+ "",
+ buildDisplayFPSOptionTitle,
+ toggleDisplayFPSOption,
+ toggleDisplayFPSOption,
+ toggleDisplayFPSOption,
+ });
+ addAdvancedOptionsEntry(&menu, (AdvancedOptionsMenuEntry){
+ "",
+ buildLimitFPSOptionTitle,
+ toggleLimitFPSOption,
+ toggleLimitFPSOption,
+ toggleLimitFPSOption,
+ });
+ if (gIsInMainMenu == 0)
+ {
+ addAdvancedOptionsEntry(&menu, (AdvancedOptionsMenuEntry){
+ "LOAD GAME STATE",
+ NULL,
+ loadGameSnapshot,
+ NULL,
+ NULL,
+ });
+ addAdvancedOptionsEntry(&menu, (AdvancedOptionsMenuEntry){
+ "SAVE GAME STATE",
+ NULL,
+ saveGameSnapshot,
+ NULL,
+ NULL,
+ });
+ addAdvancedOptionsEntry(&menu, (AdvancedOptionsMenuEntry){
+ "MOVE FREELY",
+ NULL,
+ handleMoveScrollOptionSelection,
+ NULL,
+ NULL,
+ });
+ addAdvancedOptionsEntry(&menu, (AdvancedOptionsMenuEntry){
+ "REMOVE ZONKS",
+ NULL,
+ handleRemoveZonksOptionSelection,
+ NULL,
+ NULL,
+ });
+ addAdvancedOptionsEntry(&menu, (AdvancedOptionsMenuEntry){
+ "REMOVE HARDWARE",
+ NULL,
+ handleRemoveHardwareOptionSelection,
+ NULL,
+ NULL,
+ });
+ addAdvancedOptionsEntry(&menu, (AdvancedOptionsMenuEntry){
+ "REMOVE SNIK SNAKS",
+ NULL,
+ handleRemoveSnikSnakOptionSelection,
+ NULL,
+ NULL,
+ });
+ addAdvancedOptionsEntry(&menu, (AdvancedOptionsMenuEntry){
+ "REMOVE CHIPS",
+ NULL,
+ handleRemoveChipsOptionSelection,
+ NULL,
+ NULL,
+ });
+ addAdvancedOptionsEntry(&menu, (AdvancedOptionsMenuEntry){
+ "REMOVE HARDWARE",
+ NULL,
+ handleRemoveHardwareOptionSelection,
+ NULL,
+ NULL,
+ });
+ addAdvancedOptionsEntry(&menu, (AdvancedOptionsMenuEntry){
+ "REMOVE BASE",
+ NULL,
+ handleRemoveBaseOptionSelection,
+ NULL,
+ NULL,
+ });
+ }
+
+ runAdvancedOptionsSubMenu(menu);
+}
+
+void buildLevelSetOptionTitle(char output[kMaxAdvancedOptionsMenuEntryTitleLength])
+{
+ const char *kOriginalLevelSetName = "ORIGINAL";
+ char currentSuffix[3] = "AT";
+ strcpy(currentSuffix, &gLevelsDatFilename[8]);
+
+ const char *levelSetName = kOriginalLevelSetName;
+ if (strcmp(currentSuffix, "AT") != 0)
+ {
+ levelSetName = currentSuffix;
+ }
+
+ snprintf(output, kMaxAdvancedOptionsMenuEntryTitleLength, "LEVEL SET: %s", levelSetName);
+}
+
+void decreaseLevelSet()
+{
+ rotateLevelSet(1);
+ gHasChangedLevelSetFromAdvancedMenu = 1;
+}
+
+void increaseLevelSet()
+{
+ rotateLevelSet(0);
+ gHasChangedLevelSetFromAdvancedMenu = 1;
+}
+
+void runAdvancedOptionsRootMenu()
+{
+ AdvancedOptionsMenu menu;
+ initializeAdvancedOptionsMenu(&menu);
+
+ strncpy(menu.title, "OPENSUPAPLEX " VERSION_STRING, kMaxAdvancedOptionsMenuEntryTitleLength);
+
+ if (gIsPlayingDemo)
+ {
+ addAdvancedOptionsEntry(&menu, (AdvancedOptionsMenuEntry){
+ "RESUME DEMO",
+ NULL,
+ handleResumeOptionSelection,
+ NULL,
+ NULL,
+ });
+ }
+ else
+ {
+ addAdvancedOptionsEntry(&menu, (AdvancedOptionsMenuEntry){
+ "RESUME GAME",
+ NULL,
+ handleResumeOptionSelection,
+ NULL,
+ NULL,
+ });
+ }
+
+ if (gIsInMainMenu)
+ {
+ addAdvancedOptionsEntry(&menu, (AdvancedOptionsMenuEntry){
+ "",
+ buildLevelSetOptionTitle,
+ NULL,
+ decreaseLevelSet,
+ increaseLevelSet,
+ });
+ }
+
+ if (gIsPlayingDemo && gIsInMainMenu == 0)
+ {
+ addAdvancedOptionsEntry(&menu, (AdvancedOptionsMenuEntry){
+ "STOP DEMO AND PLAY",
+ NULL,
+ handleStopDemoAndPlayOptionSelection,
+ NULL,
+ NULL,
+ });
+ }
+
+ if (gIsInMainMenu == 0)
+ {
+ if (gIsPlayingDemo)
+ {
+ addAdvancedOptionsEntry(&menu, (AdvancedOptionsMenuEntry){
+ "RESTART DEMO",
+ NULL,
+ handleRestartLevelOptionSelection,
+ NULL,
+ NULL,
+ });
+ }
+ else
+ {
+ addAdvancedOptionsEntry(&menu, (AdvancedOptionsMenuEntry){
+ "RESTART LEVEL",
+ NULL,
+ handleRestartLevelOptionSelection,
+ NULL,
+ NULL,
+ });
+ }
+ addAdvancedOptionsEntry(&menu, (AdvancedOptionsMenuEntry){
+ "",
+ buildGameSpeedOptionTitle,
+ NULL,
+ decreaseGameSpeed,
+ increaseGameSpeed,
+ });
+ }
+ addAdvancedOptionsEntry(&menu, (AdvancedOptionsMenuEntry){
+ "",
+ buildMusicVolumeOptionTitle,
+ NULL,
+ decreaseMusicVolume,
+ increaseMusicVolume,
+ });
+ addAdvancedOptionsEntry(&menu, (AdvancedOptionsMenuEntry){
+ "",
+ buildFXVolumeOptionTitle,
+ NULL,
+ decreaseFXVolume,
+ increaseFXVolume,
+ });
+ addAdvancedOptionsEntry(&menu, (AdvancedOptionsMenuEntry){
+ "",
+ buildScalingModeOptionTitle,
+ NULL,
+ decreaseAdvancedMenuScalingMode,
+ increaseAdvancedMenuScalingMode,
+ });
+ if (gIsInMainMenu)
+ {
+ addAdvancedOptionsEntry(&menu, (AdvancedOptionsMenuEntry){
+ "",
+ buildPlayDemoOptionTitle,
+ handlePlayDemoOptionSelection,
+ decreaseAdvancedMenuPlayDemoIndex,
+ increaseAdvancedMenuPlayDemoIndex,
+ });
+ }
+ if (gIsInMainMenu == 0 && gIsPlayingDemo == 0)
+ {
+ if (gIsRecordingDemo)
+ {
+ addAdvancedOptionsEntry(&menu, (AdvancedOptionsMenuEntry){
+ "STOP RECORDING DEMO",
+ NULL,
+ handleStopRecordingDemoOptionSelection,
+ NULL,
+ NULL,
+ });
+ }
+ else
+ {
+ addAdvancedOptionsEntry(&menu, (AdvancedOptionsMenuEntry){
+ "",
+ buildRecordDemoOptionTitle,
+ handleRecordDemoOptionSelection,
+ decreaseAdvancedMenuRecordDemoIndex,
+ increaseAdvancedMenuRecordDemoIndex,
+ });
+ }
+ }
+ addAdvancedOptionsEntry(&menu, (AdvancedOptionsMenuEntry){
+ "DEBUG (DANGER)",
+ NULL,
+ handleDebugOptionSelection,
+ NULL,
+ NULL,
+ });
+ if (gIsInMainMenu == 0)
+ {
+ addAdvancedOptionsEntry(&menu, (AdvancedOptionsMenuEntry){
+ "EXIT LEVEL",
+ NULL,
+ handleExitLevelOptionSelection,
+ NULL,
+ NULL,
+ });
+ }
+ else
+ {
+ addAdvancedOptionsEntry(&menu, (AdvancedOptionsMenuEntry){
+ "EXIT GAME",
+ NULL,
+ handleExitGameOptionSelection,
+ NULL,
+ NULL,
+ });
+ }
+
+ gShouldCloseAdvancedMenu = 0;
+
+ saveScreenForAdvancedMenu();
+
+ setPalette(gGameDimmedPalette);
+
+ runAdvancedOptionsMenu(&menu);
+
+ writeAdvancedConfig();
+
+ restoreScreenFromAdvancedMenu();
+ videoLoop();
+
+ setPalette(gGamePalette);
+
+ if (gHasChangedLevelSetFromAdvancedMenu)
+ {
+ updateMenuAfterLevelSetChanged();
+ gHasChangedLevelSetFromAdvancedMenu = 0;
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ parseCommandLineOptions(argc, argv);
+
+ if (gFastMode == FastModeTypeUltra)
+ {
+ setLogLevel(LogLevelDemo);
+ }
+
+ initializeLogging();
+ initializeSystem();
+ initializeVideo(gFastMode);
+ initializeControllers();
+
+ if (gFastMode != FastModeTypeUltra)
+ {
+ initializeAudio();
+ readAdvancedConfig();
+ }
+
+ // Override the initial game speed with the one from the command line if needed
+ if (gForcedInitialGameSpeed != kInvalidForcedInitialGameSpeed)
+ {
+ gGameSpeed = gForcedInitialGameSpeed;
+ }
+
+ handleSystemEvents();
+
+ initializeGameStateData();
+
+ // doesNotHaveCommandLine: //; CODE XREF: start+13j start+23Fj ...
+ generateRandomSeedFromClock();
+ // checkVideo();
+
+ // leaveVideoStatus: //; CODE XREF: start+28Aj
+ initializeFadePalette(); // 01ED:026F
+ initializeMouse();
+
+ if (gFastMode == FastModeTypeNone)
+ {
+ setPalette(gBlackPalette);
+ videoLoop();
+ readAndRenderTitleDat();
+ ColorPalette titleDatPalette; // si = 0x5F15;
+ convertPaletteDataToPalette(gTitlePaletteData, titleDatPalette);
+ fadeToPalette(titleDatPalette);
+ }
+
+ // isFastMode: //; CODE XREF: start+2ADj
+ loadMurphySprites(); // 01ED:029D
+ // Conditions to whether show
+ if (gShouldStartFromSavedSnapshot || gIsForcedLevel || gIsSPDemoAvailableToRun || gFastMode)
+ {
+ readEverything();
+ }
+ else
+ {
+ // openingSequence:
+ loadScreen2(); // 01ED:02B9
+ readEverything(); // 01ED:02BC
+ drawSpeedFixTitleAndVersion(); // 01ED:02BF
+ openCreditsBlock(); // credits inside the block // 01ED:02C2
+ drawSpeedFixCredits(); // credits below the block (herman perk and elmer productions) // 01ED:02C5
+ }
+
+ // afterOpeningSequence: //; CODE XREF: start+2DEj
+ readConfig();
+ if (byte_50946 == 0)
+ {
+ isJoystickEnabled = 0;
+ }
+ else
+ {
+ isJoystickEnabled = 1;
+ }
+
+ // loc_46F25: //; CODE XREF: start+2FEj
+ if (gFastMode == FastModeTypeNone)
+ {
+ // isNotFastMode: //; CODE XREF: start+30Aj
+ fadeToPalette(gBlackPalette);
+ word_58467 = 1;
+ }
+
+ // Can't think of a better way to handle this right now, but at this point we had a "jmp loc_46FBE" which basically
+ // skipped a few lines of the loop in the first iteration. Will revisit later when I have more knowledge of the code.
+ //
+ uint8_t shouldSkipFirstPart = 1;
+
+ do
+ {
+ // loc_46F3E: //; CODE XREF: start+428j start+444j
+ if (shouldSkipFirstPart == 0)
+ {
+ readLevels(); // 01ED:02F7
+ fadeToPalette(gBlackPalette);
+ gIsGameBusy = 0;
+ drawPlayerList();
+ initializeGameInfo();
+ drawFixedLevel();
+ drawGamePanel(); // 01ED:0311
+ uint16_t numberOfInfotrons = convertToEasyTiles();
+ resetNumberOfInfotrons(numberOfInfotrons);
+ findMurphy();
+ gCurrentPanelHeight = kPanelBitmapHeight;
+ drawCurrentLevelViewport(gCurrentPanelHeight); // Added by me
+ if (gFastMode != FastModeTypeUltra)
+ {
+ fadeToPalette(gGamePalette); // At this point the screen fades in and shows the game
+ }
+
+ if (isMusicEnabled == 0)
+ {
+ stopMusic();
+ }
+
+ // loc_46F77: //; CODE XREF: start+352j
+ gIsGameBusy = 1;
+ runLevel();
+ gIsSPDemoAvailableToRun = 0;
+ if (gShouldExitGame != 0)
+ {
+ break; // goto loc_47067;
+ }
+
+ // loc_46F8E: //; CODE XREF: start+369j
+ if (gFastMode != FastModeTypeNone)
+ {
+ break;
+ }
+
+ // isNotFastMode2: //; CODE XREF: start+373j
+ slideDownGameDash(); // 01ED:0351
+ if (byte_59B71 != 0)
+ {
+ loadMurphySprites();
+ }
+
+ // loc_46FA5: //; CODE XREF: start+380j
+ gIsGameBusy = 0;
+ if (gShouldExitGame != 0)
+ {
+ break; // goto loc_47067;
+ }
+
+ // loc_46FB4: //; CODE XREF: start+38Fj
+ if (isMusicEnabled == 0)
+ {
+ playMusicIfNeeded();
+ }
+ }
+
+ shouldSkipFirstPart = 0;
+
+ // loc_46FBE: //; CODE XREF: start+30Cj start+31Bj ...
+ prepareDemoRecordingFilename(); // 01ED:037A
+
+ uint8_t levelNumberForcedToLoad = 0;
+
+ if (gIsSPDemoAvailableToRun == 2)
+ {
+ gIsSPDemoAvailableToRun = 1;
+ if (fileIsDemo == 1)
+ {
+ playDemo(0);
+ }
+ else
+ {
+ // loc_46FDF: //; CODE XREF: start+3B5j
+ gIsPlayingDemo = 0;
+ }
+
+ // loc_46FE4: //; CODE XREF: start+3BDj
+ gShouldUpdateTotalLevelTime = 0;
+ gHasUserCheated = 1;
+ memcpy(&gSPDemoFileName[3], "---", 3);
+ // loc_4701A: //; CODE XREF: start+3DDj start+433j
+ startDirectlyFromLevel(1);
+ continue;
+ }
+ else
+ {
+ // loc_46FFF: //; CODE XREF: start+3A9j
+ levelNumberForcedToLoad = gIsForcedLevel;
+ gIsForcedLevel = 0;
+ gIsPlayingDemo = 0;
+
+ if (levelNumberForcedToLoad > 0)
+ {
+ convertLevelNumberTo3DigitStringWithPadding0(levelNumberForcedToLoad);
+ }
+ }
+
+ if (levelNumberForcedToLoad > 0)
+ {
+ // loc_4701A: //; CODE XREF: start+3DDj start+433j
+ startDirectlyFromLevel(levelNumberForcedToLoad);
+ continue;
+ }
+
+ // loc_4704B: //; CODE XREF: start+3EEj start+3F2j
+ if (gShouldStartFromSavedSnapshot != 0)
+ {
+ // loc_4701A: //; CODE XREF: start+3DDj start+433j
+ startDirectlyFromLevel(1);
+ continue;
+ }
+ gHasUserCheated = 0;
+ runMainMenu();
+ } while (gShouldExitGame == 0);
+
+ int runResult = 0;
+
+ if (gFastMode != FastModeTypeNone)
+ {
+ char *message = "";
+ if (gIsLevelStartedAsDemo == 0)
+ {
+ // loc_47094: //; CODE XREF: start+45Fj
+ if (byte_5A19B == 0)
+ {
+ // loc_470A1: //; CODE XREF: start+479j
+ message = "\"@\"-ERROR: Level(?) failed: ";
+ runResult = 1;
+ }
+ else
+ {
+ message = "\"@\"-ERROR: Level(?) successful: ";
+ }
+ }
+ else if (byte_5A19B == 0)
+ {
+ // loc_4708E: //; CODE XREF: start+466j
+ message = "Demo failed: ";
+ runResult = 1;
+ }
+ else
+ {
+ message = "Demo successful: ";
+ }
+
+ // printMessageAfterward: //; CODE XREF: start+46Cj start+472j ...
+ spLogDemo("%s%s", message, demoFileName);
+ }
+ else
+ {
+ // loc_47067: //; CODE XREF: start+36Bj start+391j ...
+ fadeToPalette(gBlackPalette); // 0x60D5
+
+ writeAdvancedConfig();
+ }
+
+ // Tidy up
+ destroyAudio();
+ destroyLogging();
+ destroyVideo();
+ destroySystem();
+
+ return runResult;
+}
+
+void initializeGameStateData()
+{
+ // Initialize game state with the same values as in the original game
+ static const uint16_t kLevelStatePrecedingPadding[kSizeOfLevelStatePrecedingPadding] = {
+ 0x8995, 0x8995, 0x8995, 0x8a3b, 0x8a3b, 0x8a3b, 0x8a3b, 0x8a3b,
+ 0x8a3b, 0x8a3b, 0x8a3b, 0x8ae8, 0x8ae8, 0x8ae8, 0x8ae8, 0x8ae8,
+ 0x8ae8, 0x8ae8, 0x8ae8, 0x8bb1, 0x8bb1, 0x8bb1, 0x8bb1, 0x8bb1,
+ 0x8bb1, 0x8bb1, 0x8bb1, 0x8c85, 0x8c85, 0x8c85, 0x8c85, 0x8c85,
+ 0x8c85, 0x8c85, 0x8c85, 0x8d5b, 0x8d5b, 0x8d5b, 0x8d5b, 0x8d5b,
+ 0x8d5b, 0x8d5b, 0x8d5b, 0x8e06, 0x8e06, 0x8e06, 0x8e06, 0x8e06,
+ 0x8e06, 0x8e06, 0x8e06, 0x8eac, 0x8eac, 0x8eac, 0x8eac, 0x8eac,
+ 0x8eac, 0x8eac, 0x8eac, 0x8f59, 0x8f59, 0x8f59, 0x8f59, 0x8f59,
+ 0x8f59, 0x8f59, 0x8f59, 0x0000, 0x1370, 0x0000, 0x0000, 0x17e8,
+ 0x0000, 0x0000, 0x0000, 0x3869, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x86d0, 0x0000, 0x34b2, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x8b8f, 0x341d, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x3923, 0x0909, 0x0c00, 0x0800, 0x5800, 0x0000,
+ 0x0000, 0x2500, 0x0677, 0x007f, 0x0000, 0x0001, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0xec00, 0x2606, 0x0005, 0x0000,
+ 0x0000, 0x0100, 0x0000, 0x0000, 0x3231, 0x3433, 0x3635, 0x3837,
+ 0x3039, 0x002d, 0x0008, 0x5751, 0x5245, 0x5954, 0x4955, 0x504f,
+ 0x0000, 0x000a, 0x5341, 0x4644, 0x4847, 0x4b4a, 0x004c, 0x0000,
+ 0x0000, 0x585a, 0x5643, 0x4e42, 0x004d, 0x0000, 0x0000, 0x2000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x002e, 0x001e, 0x0031, 0x0014, 0x0039,
+ 0x001f, 0x0014, 0x0018, 0xffff, 0x0001, 0x4c01, 0x5645, 0x4c45,
+ 0x2e53, 0x4144, 0x0054, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000};
+
+ for (int idx = 0; idx < kSizeOfLevelStatePrecedingPadding; ++idx)
+ {
+ uint16_t value = kLevelStatePrecedingPadding[idx];
+ value = convert16LE(value);
+ StatefulLevelTile *tile = &gCurrentLevelStateWithPadding[idx];
+ tile->tile = (value & 0xFF);
+ tile->state = (value >> 8);
+ }
+
+ gFrameCounter = 0xF000;
+}
+
+void startDirectlyFromLevel(uint8_t levelNumber)
+{
+ gIsGameBusy = 1;
+ gShouldAutoselectNextLevelToPlay = 1;
+ prepareLevelDataForCurrentPlayer();
+ drawPlayerList();
+ word_58467 = 0;
+ playMusicIfNeeded();
+ gCurrentSelectedLevelIndex = levelNumber;
+ restoreLastMouseAreaBitmap();
+ drawLevelList();
+ gShouldLeaveMainMenu = 0;
+ byte_5A19B = 0;
+}
+
+void slideDownGameDash() // proc near ; CODE XREF: start:isNotFastMode2p
+{
+ // 01ED:04ED
+ if (gShouldShowGamePanel == 0)
+ {
+ return;
+ }
+
+ for (int panelHeight = kPanelBitmapHeight; panelHeight > 0; --panelHeight)
+ {
+ // Move the bottom panel line by line to the bottom of the screen
+ for (int y = kScreenHeight - 2; y >= kScreenHeight - panelHeight; --y)
+ {
+ uint16_t srcAddress = y * kScreenWidth;
+ uint16_t dstAddress = (y + 1) * kScreenWidth;
+ memcpy(&gScreenPixels[dstAddress], &gScreenPixels[srcAddress], kScreenWidth);
+ }
+
+ drawCurrentLevelViewport(panelHeight);
+ videoLoop();
+ }
+}
+
+/// This alternative int9 handler seems to control the keys +, -, *, / in the numpad
+/// to alter the game speed, and also the key X for something else.
+/// @param shouldYieldCpu If 1, will sleep the thread for a bit to prevent 100% CPU usage.
+void int9handler(uint8_t shouldYieldCpu) // proc far ; DATA XREF: setint9+1Fo
+{
+ updateKeyboardState();
+
+ // 01ED:0659
+
+ if (gIsLeftAltPressed && gIsEnterPressed)
+ {
+ toggleFullscreen();
+ }
+
+ // storeKey: ; CODE XREF: int9handler+2Bj
+ if (gIsInMainMenu == 0)
+ {
+ if (gIsNumpadMultiplyPressed) // Key * in the numpad, restore speed
+ {
+ gGameSpeed = kDefaultGameSpeed;
+ updateDemoRecordingLowestSpeed();
+ }
+ // checkSlash: ; CODE XREF: int9handler+3Ej
+ // ; int9handler+45j
+ else if (gIsNumpadDividePressed) // Keypad / -> fastest playback seed
+ {
+ gGameSpeed = kNumberOfGameSpeeds - 1;
+ updateDemoRecordingLowestSpeed();
+ }
+ // checkPlus: ; CODE XREF: int9handler+54j
+ else if (gIsGameSpeedChangeButtonPressed == 0)
+ {
+ if (isIncreaseGameSpeedButtonPressed())
+ {
+ increaseGameSpeed();
+ }
+ // checkMinus: ; CODE XREF: int9handler+65j
+ // ; int9handler+71j
+ else if (isDecreaseGameSpeedButtonPressed())
+ {
+ decreaseGameSpeed();
+ }
+ }
+
+ gIsGameSpeedChangeButtonPressed = (isIncreaseGameSpeedButtonPressed() || isDecreaseGameSpeedButtonPressed());
+ }
+
+ // checkX: ; CODE XREF: int9handler+39j
+ // ; int9handler+60j ...
+ if (gIsXKeyPressed && gIsLeftAltPressed != 0)
+ {
+ gShouldExitLevel = 1;
+ gShouldExitGame = 1;
+ }
+
+ if (shouldYieldCpu)
+ {
+ waitTime(10); // Avoid burning the CPU
+ }
+}
+
+void readConfig() // proc near ; CODE XREF: start:loc_46F0Fp
+{
+ if (gFastMode == FastModeTypeUltra)
+ {
+ return;
+ }
+
+ FILE *file = openWritableFile("SUPAPLEX.CFG", "rb");
+ if (file == NULL)
+ {
+ if (errno == ENOENT || errno == ENOSYS || errno == EIO) // ax == 2? ax has error code, 2 is file not found (http://stanislavs.org/helppc/dos_error_codes.html)
+ {
+ // loc_47551: //; CODE XREF: readConfig+Fj
+ // ; readConfig+17j
+ activateCombinedSound();
+ isMusicEnabled = 1;
+ isFXEnabled = 1;
+ isJoystickEnabled = 0;
+ return;
+ }
+ else
+ {
+ exitWithError("Error opening SUPAPLEX.CFG\n");
+ }
+ }
+
+ // loc_474BE: // ; CODE XREF: readConfig+8j
+
+ uint8_t configData[kConfigDataLength];
+
+ size_t bytes = fileReadBytes(configData, sizeof(configData), file);
+
+ if (fclose(file) != 0)
+ {
+ exitWithError("Error closing SUPAPLEX.CFG\n");
+ }
+
+ // loc_474DF: // ; CODE XREF: readConfig+39j
+ if (bytes < sizeof(configData))
+ {
+ exitWithError("Error reading SUPAPLEX.CFG\n");
+ }
+
+ // loc_474E5: // ; CODE XREF: readConfig+3Fj
+ uint8_t soundSetting = configData[0];
+
+ if (soundSetting == 's')
+ {
+ activateInternalSamplesSound();
+ }
+ else if (soundSetting == 'a')
+ {
+ activateAdlibSound();
+ }
+ else if (soundSetting == 'b')
+ {
+ activateSoundBlasterSound();
+ }
+ else if (soundSetting == 'r')
+ {
+ activateRolandSound();
+ }
+ else if (soundSetting == 'c')
+ {
+ activateCombinedSound();
+ }
+ else
+ {
+ activateInternalStandardSound();
+ }
+
+ // loc_4751D: // ; CODE XREF: readConfig+4Fj
+ // ; readConfig+59j ...
+
+ isJoystickEnabled = 0;
+ if (configData[1] == 'j')
+ {
+ isJoystickEnabled = 1;
+ // calibrateJoystick(); not needed anymore
+ }
+
+ // loc_47530: //; CODE XREF: readConfig+85j
+ isMusicEnabled = (configData[2] == 'm');
+
+ // loc_47540: //; CODE XREF: readConfig+98j
+ isFXEnabled = (configData[3] == 'x');
+}
+
+void saveConfiguration() // sub_4755A proc near ; CODE XREF: code:loc_4CAECp
+{
+ FILE *file = openWritableFile("SUPAPLEX.CFG", "wb");
+ if (file == NULL)
+ {
+ spLogInfo("Error opening SUPAPLEX.CFG\n");
+ return;
+ }
+
+ // loc_4756A: ; CODE XREF: saveConfiguration+Bj
+ uint8_t configData[kConfigDataLength];
+
+ if (sndType == SoundTypeInternalSamples)
+ {
+ configData[0] = 's';
+ }
+ else if (sndType == SoundTypeInternalStandard)
+ {
+ configData[0] = 'i';
+ }
+ else if (sndType == SoundTypeAdlib)
+ {
+ configData[0] = 'a';
+ }
+ else if (sndType == SoundTypeRoland)
+ {
+ configData[0] = 'r';
+ }
+ else if (musType == SoundTypeRoland)
+ {
+ configData[0] = 'c';
+ }
+ else
+ {
+ configData[0] = 'b';
+ }
+
+ // loc_475AF: ; CODE XREF: saveConfiguration+20j
+ // ; saveConfiguration+2Cj ...
+ if (isJoystickEnabled == 0)
+ {
+ configData[1] = 'k';
+ }
+ else
+ {
+ configData[1] = 'j';
+ }
+
+ // loc_475BF: ; CODE XREF: saveConfiguration+60j
+ if (isMusicEnabled != 0)
+ {
+ configData[2] = 'm';
+ }
+ else
+ {
+ configData[2] = 'n';
+ }
+
+ // loc_475CF: ; CODE XREF: saveConfiguration+70j
+ if (isFXEnabled != 0)
+ {
+ configData[3] = 'x';
+ }
+ else
+ {
+ configData[3] = 'y';
+ }
+
+ // loc_475DF: ; CODE XREF: saveConfiguration+80j
+ size_t bytes = fileWriteBytes(configData, sizeof(configData), file);
+ if (bytes < sizeof(configData))
+ {
+ exitWithError("Error writing SUPAPLEX.CFG\n");
+ }
+
+ if (fclose(file) != 0)
+ {
+ exitWithError("Error closing SUPAPLEX.CFG\n");
+ }
+}
+
+/// @return Number of demo files read
+uint8_t readDemoFiles() // proc near ; CODE XREF: readEverything+12p
+ // ; handleDemoOptionClickp ...
+{
+ // 01ED:09A6
+
+ gDemoCurrentInputIndex = 0;
+ word_5A33C = 0;
+
+ memset(&gDemos.demoFirstIndices, 0xFF, sizeof(gDemos.demoFirstIndices)); // fills 11 words (22 bytes) with 0xFFFF
+
+ for (int i = 0; i < kNumberOfDemos; ++i)
+ {
+ // loc_47629: //; CODE XREF: readDemoFiles+175j
+ gSelectedOriginalDemoLevelNumber = 0;
+ char *filename = gDemo0BinFilename;
+
+ if (gIsSPDemoAvailableToRun == 1)
+ {
+ filename = demoFileName;
+ }
+ else
+ {
+ // loc_4763C: // ; CODE XREF: readDemoFiles+2Cj
+ gDemo0BinFilename[4] = '0' + i; // Replaces the number in "DEMO0.BIN" with the right value
+ }
+
+ // loc_47647: // ; CODE XREF: readDemoFiles+31j
+ FILE *file = openWritableFileWithReadonlyFallback(filename, "rb");
+ if (file == NULL)
+ {
+ return i;
+ }
+
+ // loc_47651: //; CODE XREF: readDemoFiles+43j
+ if (gIsSPDemoAvailableToRun == 1)
+ {
+ if (gSelectedOriginalDemoFromCommandLineLevelNumber == 0)
+ {
+ fseek(file, kLevelDataLength, SEEK_SET);
+ }
+ }
+ else
+ {
+ // loc_47674: // ; CODE XREF: readDemoFiles+52j
+ int result = fseek(file, 0, SEEK_END);
+ long fileSize = ftell(file);
+
+ // this is probably to support old level formats
+ if (result == 0 && fileSize < kLevelDataLength)
+ {
+ gSelectedOriginalDemoLevelNumber = getLevelNumberFromOriginalDemoFile(file, fileSize);
+ }
+
+ // loc_47690: // ; CODE XREF: readDemoFiles+76j readDemoFiles+7Aj ...
+ fseek(file, 0, SEEK_SET);
+
+ if (gSelectedOriginalDemoLevelNumber == 0)
+ {
+ Level *level = &gDemos.level[i];
+ size_t bytes = fileReadBytes(level, kLevelDataLength, file);
+
+ if (bytes < kLevelDataLength)
+ {
+ return i;
+ }
+
+ // loc_476D3: // ; CODE XREF: readDemoFiles+C5j
+ gDemoRandomSeeds[i] = level->randomSeed;
+ }
+ }
+
+ // loc_476DB: // ; CODE XREF: readDemoFiles+59j readDemoFiles+69j ...
+ uint16_t maxNumberOfBytesToRead = kMaxDemoInputSteps + 1; // 48649
+ maxNumberOfBytesToRead -= gDemoCurrentInputIndex;
+
+ if (maxNumberOfBytesToRead > kMaxDemoInputSteps + 1) // weird way of checking if gDemoCurrentInputIndex < 0 ????
+ {
+ maxNumberOfBytesToRead = 0;
+ }
+
+ uint16_t numberOfDemoBytesRead = 0;
+
+ // loc_476EA: // ; CODE XREF: readDemoFiles+DDj
+ if (maxNumberOfBytesToRead == 0)
+ {
+ numberOfDemoBytesRead = 0;
+ }
+ else
+ {
+ // loc_476F3: // ; CODE XREF: readDemoFiles+E4j
+ numberOfDemoBytesRead = fileReadBytes(&gDemos.demoData[gDemoCurrentInputIndex], maxNumberOfBytesToRead, file);
+
+ if (numberOfDemoBytesRead == 0)
+ {
+ if (fclose(file) != 0)
+ {
+ exitWithError("Error closing DEMO file");
+ }
+ return i;
+ }
+
+ // loc_47719: // ; CODE XREF: readDemoFiles+FCj
+ }
+
+ // loc_4771A: // ; CODE XREF: readDemoFiles+E8j
+ if (fclose(file) != 0)
+ {
+ exitWithError("Error closing DEMO file");
+ }
+
+ // loc_47729: ; CODE XREF: readDemoFiles+11Bj
+ gDemos.demoData[gDemoCurrentInputIndex] = gDemos.demoData[gDemoCurrentInputIndex] & 0x7F; // this removes the MSB from the levelNumber that was added in the speed fix mods
+ int isZero = (gSelectedOriginalDemoLevelNumber == 0);
+ gSelectedOriginalDemoLevelNumber = 0;
+ if (isZero)
+ {
+ gDemos.demoData[gDemoCurrentInputIndex] = gDemos.demoData[gDemoCurrentInputIndex] | 0x80; // This sets the MSB?? maybe the "interpreter" later needs it
+ }
+
+ // loc_47743: // ; CODE XREF: readDemoFiles+134j
+ uint16_t demoLastByteIndex = gDemoCurrentInputIndex + numberOfDemoBytesRead - 1;
+ // cx = bx; // bx here has the value of gDemoCurrentInputIndex
+ // bx += numberOfDemoBytesRead; // ax here has the number of bytes read regarding the level itself (levelNumber + inputSteps)
+ // push(ds);
+ // push(es);
+ // pop(ds);
+ // assume ds:nothing
+ // bx--;
+ if (demoLastByteIndex == 0xFFFF // this would mean bx was 0. is this possible?
+ || numberOfDemoBytesRead <= 1 // this means the demo is empty (only has levelNumber or nothing)
+ || gDemos.demoData[demoLastByteIndex] != 0xFF)
+ {
+ // loc_4775A: // ; CODE XREF: readDemoFiles+145j
+ // ; readDemoFiles+14Aj
+ if (demoLastByteIndex < sizeof(BaseDemo))
+ {
+ numberOfDemoBytesRead++;
+ gDemos.demoData[demoLastByteIndex + 1] = 0xFF;
+ }
+ }
+
+ // loc_47765: // ; CODE XREF: readDemoFiles+14Fj
+ // ; readDemoFiles+155j
+ gDemos.demoFirstIndices[i] = gDemoCurrentInputIndex;
+ gDemoCurrentInputIndex += numberOfDemoBytesRead;
+ }
+
+ return kNumberOfDemos;
+}
+
+void openCreditsBlock() // proc near ; CODE XREF: start+2E9p
+{
+ static const int kEdgeWidth = 13;
+ static const int kEdgeHeight = 148;
+ static const int kEdgeStep = 4;
+ static const int kEdgeTopY = 26;
+ static const int kNumberOfFrames = 60;
+
+ const uint32_t kAnimationDuration = kNumberOfFrames * 1000 / 70; // ~429 ms
+
+ uint32_t animationTime = 0;
+
+ static const int kInitialLeftEdgeX = 147;
+ const int kInitialRightEdgeX = kInitialLeftEdgeX + kEdgeWidth + 1;
+
+ const int kEdgeAnimationDistance = (kEdgeStep * kNumberOfFrames) / 2 + 1;
+
+ int leftEdgeX = kInitialLeftEdgeX;
+ int rightEdgeX = kInitialRightEdgeX;
+
+ startTrackingRenderDeltaTime();
+
+ while (animationTime < kAnimationDuration)
+ {
+ // loc_47800: // ; CODE XREF: openCreditsBlock+AFj
+ animationTime += updateRenderDeltaTime();
+ animationTime = MIN(animationTime, kAnimationDuration);
+
+ float animationFactor = (float)animationTime / kAnimationDuration;
+
+ int previousLeftEdgeX = leftEdgeX;
+ int previousRightEdgeX = rightEdgeX;
+
+ int distance = ceilf(kEdgeAnimationDistance * animationFactor);
+
+ leftEdgeX = kInitialLeftEdgeX - distance;
+ rightEdgeX = kInitialRightEdgeX + distance;
+
+ int leftEdgeStep = previousLeftEdgeX - leftEdgeX;
+ int rightEdgeStep = rightEdgeX - previousRightEdgeX;
+
+ // This loop moves both edges of the panel, and fills the inside of the panel with the contents of TITLE2.DAT
+ for (int y = kEdgeTopY; y < kEdgeTopY + kEdgeHeight; ++y)
+ {
+ // Left edge
+ for (int x = leftEdgeX; x < previousLeftEdgeX + kEdgeWidth - leftEdgeStep; ++x)
+ {
+ long addr = y * kScreenWidth + x;
+ gScreenPixels[addr] = gScreenPixels[addr + leftEdgeStep]; // Move panel edge
+ }
+
+ // Content of visible panel unveiled by left edge
+ for (int x = leftEdgeX + kEdgeWidth; x < previousLeftEdgeX + kEdgeWidth + 1; ++x)
+ {
+ long addr = y * kScreenWidth + x;
+ gScreenPixels[addr] = gTitle2DecodedBitmapData[addr];
+ }
+
+ // Right edge
+ for (int x = rightEdgeX + kEdgeWidth; x > previousRightEdgeX; --x)
+ {
+ long addr = y * kScreenWidth + x;
+ gScreenPixels[addr] = gScreenPixels[addr - rightEdgeStep]; // Move panel edge
+ }
+
+ // Content of visible panel unveiled by right edge
+ for (int x = previousRightEdgeX; x < rightEdgeX; ++x)
+ {
+ long addr = y * kScreenWidth + x;
+ gScreenPixels[addr] = gTitle2DecodedBitmapData[addr];
+ }
+ }
+
+ videoLoop();
+ }
+
+ // loc_47884: // ; CODE XREF: openCreditsBlock+C7j
+ // Display now the contents of TITLE2.DAT starting at the y=panel_edge_top_y (to prevent removing the top title)
+ // This basically makes the edges of the panel docked at the sides of the screen look better (as intended in TITLE2.DAT
+ // compared to how they look when the "crafted" animation concludes).
+ //
+ size_t copyOffset = kEdgeTopY * kScreenWidth;
+ memcpy(gScreenPixels + copyOffset, gTitle2DecodedBitmapData + copyOffset, sizeof(gTitle2DecodedBitmapData) - copyOffset);
+
+ ColorPalette title2Palette;
+ convertPaletteDataToPalette(gTitle2PaletteData, title2Palette);
+ fadeToPalette(title2Palette); // fades current frame buffer into the title 2.dat (screen with the credits)
+}
+
+void loadScreen2() // proc near ; CODE XREF: start:loc_46F00p
+{
+ readAndRenderTitle1Dat();
+
+ // loc_4792E: //; CODE XREF: loadScreen2+76j
+ ColorPalette title1DatPalette;
+ convertPaletteDataToPalette(gTitle1PaletteData, title1DatPalette);
+ setPalette(title1DatPalette);
+ videoLoop();
+
+ readTitle2Dat();
+}
+
+void readLevelsLst() // proc near ; CODE XREF: readLevelsLst+CCj
+ // ; readEverything+Fp ...
+{
+ // 01ED:1038
+
+ char paddingEntryText[kListLevelNameLength] = " ";
+ for (int i = 0; i < kNumberOfLevelsWithPadding; ++i)
+ {
+ memcpy(&gPaddedLevelListData[i * kListLevelNameLength], paddingEntryText, sizeof(paddingEntryText));
+ }
+ memcpy(&gPaddedLevelListData[kLastLevelIndex * kListLevelNameLength],
+ "- REPLAY SKIPPED LEVELS!! -",
+ kListLevelNameLength);
+ memcpy(&gPaddedLevelListData[(kLastLevelIndex + 1) * kListLevelNameLength],
+ "---- UNBELIEVEABLE!!!! ----",
+ kListLevelNameLength);
+
+ FILE *file = openWritableFile(gLevelLstFilename, "rb");
+ if (file == NULL)
+ {
+ // errorOpeningLevelLst: // ; CODE XREF: readLevelsLst+8j
+ FILE *file = openReadonlyFile(gLevelsDatFilename, "rb");
+ if (file == NULL)
+ {
+ // errorOpeningLevelsDat: // ; CODE XREF: readLevelsLst+17j
+ exitWithError("Error opening LEVELS.DAT\n");
+ }
+ // successOpeningLevelsDat: // ; CODE XREF: readLevelsLst+15j
+
+ for (int i = 0; i < kNumberOfLevels; ++i)
+ {
+ // loc_47CC4: // ; CODE XREF: readLevelsLst:loc_47CE4j
+ char number[5];
+ sprintf(number, "%03d ", i + 1);
+
+ memcpy(gLevelListData + i * kListLevelNameLength, number, sizeof(number) - 1);
+ gLevelListData[i * kListLevelNameLength + kListLevelNameLength - 1] = '\n';
+ // loc_47CE4: // ; CODE XREF: readLevelsLst+3Aj
+ }
+
+ for (int i = 0; i < kNumberOfLevels; ++i)
+ {
+ // loc_47CF1: // ; CODE XREF: readLevelsLst+83j
+
+ int seekOffset = 0x5A6 + i * kLevelDataLength;
+
+ fseek(file, seekOffset, SEEK_SET); // position 1446
+ size_t bytes = fileReadBytes(gLevelListData + i * kListLevelNameLength + 4, kLevelNameLength - 1, file);
+
+ if (bytes < kLevelNameLength - 1)
+ {
+ fclose(file);
+ exitWithError("Error reading LEVELS.DAT\n");
+ }
+ }
+ if (fclose(file) != 0)
+ {
+ exitWithError("Error closing LEVELS.DAT\n");
+ }
+
+ if (gShouldRecreateLevelLstIfNeeded == 0)
+ {
+ return;
+ }
+
+ // loc_47D35: // ; CODE XREF: readLevelsLst+95j
+ file = openWritableFile(gLevelLstFilename, "wb");
+ if (file == NULL)
+ {
+ exitWithError("Error opening %s\n", gLevelLstFilename);
+ }
+
+ // writeLevelLstData: // ; CODE XREF: readLevelsLst+A5j
+ size_t bytes = fileWriteBytes(gLevelListData, kLevelListDataLength, file);
+ if (bytes < kLevelListDataLength)
+ {
+ exitWithError("Error writing %s\n", gLevelLstFilename);
+ }
+
+ // loc_47D5B: // ; CODE XREF: readLevelsLst+BBj
+ if (fclose(file) != 0)
+ {
+ exitWithError("Error closing %s\n", gLevelLstFilename);
+ }
+ return;
+ }
+
+ // successOpeningLevelLst: // ; CODE XREF: readLevelsLst+Aj
+ size_t bytes = fileReadBytes(gLevelListData, kLevelListDataLength, file);
+ if (bytes < kLevelListDataLength)
+ {
+ fclose(file);
+ exitWithError("Error reading LEVEL.LST\n");
+ }
+
+ // loc_47D8D: // ; CODE XREF: readLevelsLst+8Aj
+ // ; readLevelsLst:loc_47D5Bj ...
+ if (fclose(file) != 0)
+ {
+ exitWithError("Error closing LEVEL.LST\n");
+ }
+}
+
+void readPlayersLst() // proc near ; CODE XREF: readEverything+1Bp
+ // ; handleFloppyDiskButtonClick+149p
+{
+ if (gIsForcedCheatMode != 0)
+ {
+ return;
+ }
+
+ for (int i = 0; i < kNumberOfPlayers; ++i)
+ {
+ strcpy(gPlayerListData[i].name, "--------");
+ }
+
+ FILE *file = openWritableFile(gPlayerLstFilename, "rb");
+ if (file == NULL)
+ {
+ return;
+ }
+
+ size_t bytes = fileReadBytes(gPlayerListData, sizeof(gPlayerListData), file);
+ if (bytes == 0)
+ {
+ return;
+ }
+
+ fclose(file);
+}
+
+void readHallfameLst() // proc near ; CODE XREF: readEverything+18p
+ // ; handleFloppyDiskButtonClick+146p
+{
+ if (gIsForcedCheatMode != 0)
+ {
+ return;
+ }
+
+ FILE *file = openWritableFile(gHallfameLstFilename, "rb");
+ if (file == NULL)
+ {
+ return;
+ }
+
+ size_t bytes = fileReadBytes(gHallOfFameData, sizeof(gHallOfFameData), file);
+ if (bytes == 0)
+ {
+ return;
+ }
+
+ fclose(file);
+}
+
+void readEverything() // proc near ; CODE XREF: start+2DBp start+2E3p ...
+{
+ // 01ED:1213
+ readPalettes();
+ readBitmapFonts();
+ readPanelDat();
+ readMenuDat();
+ readControlsDat();
+ readLevelsLst();
+ readDemoFiles();
+ readBackDat();
+ readHallfameLst();
+ readPlayersLst();
+ readGfxDat();
+}
+
+void waitForKeyMouseOrJoystick() // sub_47E98 proc near ; CODE XREF: recoverFilesFromFloppyDisk+4Ap
+// ; handleStatisticsOptionClick+216p ...
+{
+ byte_59B86 = 0;
+
+ do
+ {
+ // keyIsPressed: ; CODE XREF: waitForKeyMouseOrJoystick+16j
+ if (gIsEscapeKeyPressed != 0)
+ {
+ byte_59B86 = 0xFF;
+ }
+
+ int9handler(1);
+ // loc_47EA9: ; CODE XREF: waitForKeyMouseOrJoystick+Aj
+ } while (isAnyKeyPressed());
+
+ uint16_t mouseButtonsStatus = 0;
+
+ do
+ {
+ // mouseIsClicked: ; CODE XREF: waitForKeyMouseOrJoystick+1Ej
+ getMouseStatus(NULL, NULL, &mouseButtonsStatus);
+ } while (mouseButtonsStatus != 0);
+
+ for (int i = 0; i < 4200; ++i)
+ {
+ // loc_47EC6: ; CODE XREF: waitForKeyMouseOrJoystick+57j
+ videoLoop();
+
+ getMouseStatus(NULL, NULL, &mouseButtonsStatus);
+ int9handler(0);
+ updateUserInput();
+
+ if (mouseButtonsStatus != 0)
+ {
+ break;
+ }
+
+ if (isAnyKeyPressed())
+ {
+ break;
+ }
+
+ if (gCurrentUserInput > kUserInputSpaceAndDirectionOffset)
+ {
+ break;
+ }
+ }
+
+ if (mouseButtonsStatus != 0)
+ {
+ // loc_47F02: ; CODE XREF: waitForKeyMouseOrJoystick+44j
+ // ; waitForKeyMouseOrJoystick+70j
+ do
+ {
+ getMouseStatus(NULL, NULL, &mouseButtonsStatus);
+ } while (mouseButtonsStatus != 0);
+ }
+ else if (isAnyKeyPressed())
+ {
+ do
+ {
+ // loc_47F0C: ; CODE XREF: waitForKeyMouseOrJoystick+4Bj
+ // ; waitForKeyMouseOrJoystick+85j
+ if (gIsEscapeKeyPressed != 0)
+ {
+ byte_59B86 = 0xFF;
+ }
+
+ int9handler(1);
+ } while (isAnyKeyPressed());
+ }
+ else if (gCurrentUserInput > kUserInputSpaceAndDirectionOffset)
+ {
+ do
+ {
+ // loc_47F21: ; CODE XREF: waitForKeyMouseOrJoystick+55j
+ // ; waitForKeyMouseOrJoystick+9Dj
+ if (gIsEscapeKeyPressed != 0)
+ {
+ byte_59B86 = 0xFF;
+ }
+
+ int9handler(1);
+ updateUserInput();
+ } while (gCurrentUserInput > kUserInputSpaceAndDirectionOffset);
+ }
+}
+
+void updateZonkTiles(int16_t position) // proc near ; DATA XREF: data:160Co
+{
+ // 01ED:132D
+
+ StatefulLevelTile *currentTile = &gCurrentLevelState[position];
+ StatefulLevelTile *belowTile = &gCurrentLevelState[position + kLevelWidth];
+ StatefulLevelTile *belowLeftTile = &gCurrentLevelState[position + kLevelWidth - 1];
+ StatefulLevelTile *leftTile = &gCurrentLevelState[position - 1];
+ StatefulLevelTile *belowRightTile = &gCurrentLevelState[position + kLevelWidth + 1];
+ StatefulLevelTile *rightTile = &gCurrentLevelState[position + 1];
+ StatefulLevelTile *aboveRightTile = &gCurrentLevelState[position - kLevelWidth + 1];
+
+ if (currentTile->tile != LevelTileTypeZonk) // cmp byte ptr leveldata[si], 1
+ {
+ return;
+ }
+
+ // loc_47F98: ; CODE XREF: movefun+5j
+ uint8_t shouldSkipFirstPartOfLoop = 0;
+
+ if (currentTile->tile != LevelTileTypeZonk || currentTile->state != 0)
+ {
+ shouldSkipFirstPartOfLoop = 1; // used to emulate "jmp loc_48035"
+ }
+ else
+ {
+ // loc_47FA4: ; CODE XREF: movefun+Fj
+ if (gAreZonksFrozen == 2)
+ {
+ return;
+ }
+
+ // loc_47FAC: ; CODE XREF: movefun+19j
+ // Check if the zonk can just fall vertically
+ if (belowTile->tile == LevelTileTypeSpace && belowTile->state == 0)
+ {
+ // loc_47FF4: ; CODE XREF: movefun+23j
+ currentTile->state = 0x40;
+ shouldSkipFirstPartOfLoop = 1; // used to emulate "jmp loc_48035"
+ }
+ else
+ {
+ // Check if below the zonk is another object that could be used to slide down left or right
+ if (belowTile->state != 0 || (belowTile->tile != LevelTileTypeZonk && belowTile->tile != LevelTileTypeInfotron && belowTile->tile != LevelTileTypeChip))
+ {
+ return;
+ }
+
+ // loc_47FC5: ; CODE XREF: movefun+28j
+ // ; movefun+2Dj ...
+ // Check if it can fall to the left side...
+ if ((belowLeftTile->tile == LevelTileTypeSpace && belowLeftTile->state == 0) || (belowLeftTile->tile == 0x88 && belowLeftTile->state == 0x88) || (belowLeftTile->tile == 0xAA && belowLeftTile->state == 0xAA))
+ {
+ // loc_47FFB: ; CODE XREF: movefun+3Aj
+ // ; movefun+42j ...
+ // ...but only if the left tile is empty
+ if (leftTile->state == 0 && leftTile->tile == LevelTileTypeSpace)
+ {
+ // loc_48004: ; CODE XREF: movefun+70j
+ currentTile->state = 0x50;
+ leftTile->state = 0x88;
+ leftTile->tile = 0x88;
+ shouldSkipFirstPartOfLoop = 1; // used to emulate "jmp loc_48035"
+ }
+ }
+ }
+ }
+
+ uint8_t shouldSkipTo_loc_481C6 = 0;
+
+ do
+ {
+ // loc_47FDC: ; CODE XREF: movefun+72j
+ // ; movefun+1F1j
+ if (shouldSkipFirstPartOfLoop == 0) // used to emulate "jmp loc_48035"
+ {
+ // Checks if it can fall to the right side
+ if ((belowRightTile->state != 0 || belowRightTile->tile != LevelTileTypeSpace) && (belowRightTile->state != 0x88 || belowRightTile->tile != 0x88) && (belowRightTile->state != 0xAA || belowRightTile->tile != 0xAA))
+ {
+ return;
+ }
+ else
+ {
+ // loc_48011: ; CODE XREF: movefun+51j
+ // ; movefun+59j ...
+ // Only if the right tile is empty or... other circumstances?
+ if ((rightTile->state != 0 || rightTile->tile != LevelTileTypeSpace) && ((rightTile->state != 0x99 || rightTile->tile != 0x99) || (aboveRightTile->state != 0 || aboveRightTile->tile != LevelTileTypeZonk)))
+ {
+ return;
+ }
+
+ // loc_48028: ; CODE XREF: movefun+86j
+ // ; movefun+95j
+ currentTile->state = 0x60;
+ rightTile->state = 0x88;
+ rightTile->tile = 0x88;
+ }
+ }
+
+ shouldSkipFirstPartOfLoop = 0; // don't skip the first part in the next iteration
+
+ // loc_48035: ; CODE XREF: movefun+11j
+ // ; movefun+69j ...
+ uint8_t state = currentTile->state;
+ uint8_t stateType = state & 0xF0;
+
+ if (stateType != 0x10) // 16
+ {
+ // loc_48045: ; CODE XREF: movefun+B1j
+ if (stateType == 0x20) // 32
+ {
+ // loc_48212: ; CODE XREF: movefun+B9j
+ // 01ED:15AF
+ uint8_t stateFrame = state & 0x7; // module 8?
+
+ uint8_t tileX = (position % kLevelWidth);
+ uint8_t tileY = (position / kLevelWidth);
+
+ uint16_t dstX = tileX * kTileSize;
+ uint16_t dstY = tileY * kTileSize;
+
+ // mov si, 1294h
+ // mov si, [bx+si]
+ Point frameCoordinates = kZonkSlideLeftAnimationFrameCoordinates[stateFrame];
+
+ drawMovingSpriteFrameInLevel(frameCoordinates.x,
+ frameCoordinates.y,
+ kTileSize * 2,
+ kTileSize,
+ dstX, dstY);
+
+ // 01ED:15D6
+ state = currentTile->state; // mov bl, [si+1835h]
+ state++;
+
+ if (state == 0x24) // 36
+ {
+ rightTile->state = 0xAA;
+ rightTile->tile = 0xAA;
+ }
+ // loc_4824A: ; CODE XREF: movefun+2B2j
+ if (state == 0x26) // 38
+ {
+ currentTile->state = state;
+ handleZonkStateAfterFallingOneTile(position + 1);
+ return;
+ }
+ // loc_4825D: ; CODE XREF: movefun+2BDj
+ else if (state < 0x28) // 40
+ {
+ currentTile->state = state;
+ return;
+ }
+ // loc_48267: ; CODE XREF: movefun+2D0j
+ else
+ {
+ currentTile->state = 0xFF;
+ currentTile->tile = 0xFF;
+ belowTile->state = 0x10;
+ belowTile->tile = LevelTileTypeZonk;
+ return;
+ }
+ }
+ // loc_4804C: ; CODE XREF: movefun+B7j
+ else if (stateType == 0x30) // 48
+ {
+ // loc_48277: ; CODE XREF: movefun+C0j
+ uint8_t stateFrame = state & 0x7; // module 8?
+
+ uint8_t tileX = ((position - 1) % kLevelWidth);
+ uint8_t tileY = ((position - 1) / kLevelWidth);
+
+ uint16_t dstX = tileX * kTileSize;
+ uint16_t dstY = tileY * kTileSize;
+
+ // mov si, 12A4h
+ // mov si, [bx+si]
+ Point frameCoordinates = kZonkSlideRightAnimationFrameCoordinates[stateFrame];
+
+ drawMovingSpriteFrameInLevel(frameCoordinates.x,
+ frameCoordinates.y,
+ kTileSize * 2,
+ kTileSize,
+ dstX, dstY);
+
+ state = currentTile->state;
+ state++;
+ if (state == 0x34) // 52
+ {
+ leftTile->state = 0xAA;
+ leftTile->tile = 0xAA;
+ }
+ // loc_482AF: ; CODE XREF: movefun+317j
+ if (state == 0x36) // 54
+ {
+ currentTile->state = state;
+ handleZonkStateAfterFallingOneTile(position - 1); // left tile
+ return;
+ }
+ // loc_482C2: ; CODE XREF: movefun+322j
+ else if (state < 0x38) // 54
+ {
+ currentTile->state = state;
+ return;
+ }
+ // loc_482CC: ; CODE XREF: movefun+335j
+ else
+ {
+ currentTile->state = 0xFF;
+ currentTile->tile = 0xFF;
+ belowTile->state = 0x10;
+ belowTile->tile = LevelTileTypeZonk;
+ return;
+ }
+ }
+ // loc_48053: ; CODE XREF: movefun+BEj
+ else if (gAreZonksFrozen == 2)
+ {
+ return;
+ }
+ // loc_4805B: ; CODE XREF: movefun+C8j
+ else if (stateType == 0x40) // 64
+ {
+ // loc_482DC: ; CODE XREF: movefun+CFj
+ state++;
+ if (state < 0x42) // 66
+ {
+ currentTile->state = state;
+ return;
+ }
+ // loc_482E8: ; CODE XREF: movefun+351j
+ else if (belowTile->tile != LevelTileTypeSpace || belowTile->state != 0) // cmp word ptr [si+18ACh], 0
+ {
+ state--;
+ currentTile->state = state;
+ return;
+ }
+ // loc_482F6: ; CODE XREF: movefun+35Dj
+ else
+ {
+ currentTile->state = 0xFF;
+ currentTile->tile = 0xFF;
+ belowTile->state = 0x10;
+ belowTile->tile = LevelTileTypeZonk;
+
+ return;
+ }
+ }
+ // loc_48062: ; CODE XREF: movefun+CDj
+ else if (stateType == 0x50) // Zonk sliding left
+ {
+ // loc_4830A: ; CODE XREF: movefun+D6j
+ uint8_t stateFrame = state & 0x7; // module 8?
+
+ uint8_t tileX = ((position - 1) % kLevelWidth);
+ uint8_t tileY = ((position - 1) / kLevelWidth);
+
+ uint16_t dstX = tileX * kTileSize;
+ uint16_t dstY = tileY * kTileSize;
+
+ // mov si, 1294h
+ // mov si, [bx+si]
+ Point frameCoordinates = kZonkSlideLeftAnimationFrameCoordinates[stateFrame];
+
+ drawMovingSpriteFrameInLevel(frameCoordinates.x,
+ frameCoordinates.y,
+ kTileSize * 2,
+ kTileSize,
+ dstX, dstY);
+
+ state = currentTile->state;
+ state++;
+
+ if (state < 0x52) // 82
+ {
+ currentTile->state = state;
+ return;
+ }
+ // loc_48341: ; CODE XREF: movefun+3AAj
+ else if (belowLeftTile->state != 0 || belowLeftTile->tile != LevelTileTypeSpace) // cmp word ptr [si+18AAh], 0
+ {
+ // loc_48371: ; CODE XREF: movefun+3B6j
+ // ; movefun+3C5j
+ state--;
+ currentTile->state = state;
+ return;
+ }
+ else if ((leftTile->state != 0 || leftTile->tile != LevelTileTypeSpace) // cmp word ptr [si+1832h], 0
+ && (leftTile->state != 0x88 || leftTile->tile != 0x88)) // cmp word ptr [si+1832h], 8888h
+ {
+ // loc_48371: ; CODE XREF: movefun+3B6j
+ // ; movefun+3C5j
+ state--;
+ currentTile->state = state;
+ return;
+ }
+ else
+ {
+ // loc_48357: ; CODE XREF: movefun+3BDj
+ currentTile->state = 0xFF;
+ currentTile->tile = 0xFF;
+ leftTile->state = 0x22;
+ leftTile->tile = LevelTileTypeZonk;
+ belowLeftTile->state = 0xFF;
+ belowLeftTile->tile = 0xFF;
+ return;
+ }
+ }
+ // loc_48069: ; CODE XREF: movefun+D4j
+ else if (stateType == 0x60) // 96
+ {
+ // loc_48378: ; CODE XREF: movefun+DDj
+ uint8_t stateFrame = state & 0x7; // module 8?
+
+ uint8_t tileX = (position % kLevelWidth);
+ uint8_t tileY = (position / kLevelWidth);
+
+ uint16_t dstX = tileX * kTileSize;
+ uint16_t dstY = tileY * kTileSize;
+
+ // mov si, 12A4h
+ Point frameCoordinates = kZonkSlideRightAnimationFrameCoordinates[stateFrame];
+
+ drawMovingSpriteFrameInLevel(frameCoordinates.x,
+ frameCoordinates.y,
+ kTileSize * 2,
+ kTileSize,
+ dstX, dstY);
+
+ state = currentTile->state;
+ state++;
+
+ if (state < 0x62) // 98
+ {
+ currentTile->state = state;
+ return;
+ }
+ // loc_483AF: ; CODE XREF: movefun+418j
+ else if (belowRightTile->state != 0 || belowRightTile->tile != LevelTileTypeSpace) // cmp word ptr [si+18AEh], 0
+ {
+ // loc_483DF: ; CODE XREF: movefun+424j
+ // ; movefun+433j
+ state--;
+ currentTile->state = state;
+ return;
+ }
+ else if ((rightTile->state != 0 || rightTile->tile != LevelTileTypeSpace) // cmp word ptr [si+1836h], 0
+ && (rightTile->state != 0x88 || rightTile->tile != 0x88)) // cmp word ptr [si+1836h], 8888h
+ {
+ // loc_483DF: ; CODE XREF: movefun+424j
+ // ; movefun+433j
+ state--;
+ currentTile->state = state;
+ return;
+ }
+ else
+ {
+ // loc_483C5: ; CODE XREF: movefun+42Bj
+ currentTile->state = 0xFF;
+ currentTile->tile = 0xFF;
+ rightTile->state = 0x32;
+ rightTile->tile = LevelTileTypeZonk;
+ belowRightTile->state = 0xFF;
+ belowRightTile->tile = 0xFF;
+ return;
+ }
+ }
+ // loc_48070: ; CODE XREF: movefun+DBj
+ else if (stateType == 0x70) // 112
+ {
+ // loc_483E6: ; CODE XREF: movefun+E4j
+ if ((belowTile->state != 0 || belowTile->tile != LevelTileTypeSpace) // cmp word ptr [si+18ACh], 0
+ && (belowTile->state != 0x99 && belowTile->tile != 0x99)) // cmp word ptr [si+18ACh], 9999h
+ {
+ return;
+ }
+
+ // loc_483F6: ; CODE XREF: movefun+45Bj
+ // ; movefun+463j
+ currentTile->state = 0xFF;
+ currentTile->tile = 0xFF;
+
+ // Move down and update tiles
+ position += kLevelWidth;
+
+ currentTile = &gCurrentLevelState[position];
+ belowTile = &gCurrentLevelState[position + kLevelWidth];
+ belowLeftTile = &gCurrentLevelState[position + kLevelWidth - 1];
+ leftTile = &gCurrentLevelState[position - 1];
+ belowRightTile = &gCurrentLevelState[position + kLevelWidth + 1];
+ rightTile = &gCurrentLevelState[position + 1];
+ aboveRightTile = &gCurrentLevelState[position - kLevelWidth + 1];
+
+ currentTile->state = 0x10;
+ currentTile->tile = LevelTileTypeZonk;
+ }
+ // locret_48077: ; CODE XREF: movefun+E2j
+ else
+ {
+ return;
+ }
+ }
+
+ // loc_48078: ; CODE XREF: movefun+B3j
+ // ; movefun+475j
+ // This animates the Zonk falling
+ // 01ED:1415
+ uint8_t somePositionThing = state;
+ somePositionThing *= 2;
+ somePositionThing &= 0x1F;
+
+ uint16_t offset = kFallAnimationGravityOffsets[somePositionThing];
+
+ uint16_t finalPosition = position - kLevelWidth;
+ uint8_t tileX = (finalPosition % kLevelWidth);
+ uint8_t tileY = (finalPosition / kLevelWidth);
+
+ uint16_t dstX = tileX * kTileSize + (offset % 122);
+ uint16_t dstY = tileY * kTileSize + (offset / 122);
+
+ drawMovingSpriteFrameInLevel(224, 82,
+ kTileSize,
+ kTileSize + 2,
+ dstX, dstY);
+
+ uint8_t newState = currentTile->state;
+ newState++;
+ if (newState == 0x16) // 22
+ {
+ currentTile->state = newState;
+ handleZonkStateAfterFallingOneTile(position - kLevelWidth); // Tile above
+ return;
+ }
+ // loc_480BB: ; CODE XREF: movefun+11Bj
+ else if (newState < 0x18) // 24
+ {
+ currentTile->state = newState;
+ return;
+ }
+
+ // loc_480C5: ; CODE XREF: movefun+12Ej
+ // This part handles what to do when the zonk finished falling 1 tile
+ // 01ED:1462
+ currentTile->state = 0;
+ if (gAreZonksFrozen == 2)
+ {
+ return;
+ }
+
+ // loc_480D2: ; CODE XREF: movefun+13Fj
+ if ((belowTile->tile == LevelTileTypeSpace && belowTile->state == 0) // cmp word ptr [si+18ACh], 0
+ || (belowTile->tile == 0x99 && belowTile->state == 0x99)) // cmp word ptr [si+18ACh], 9999h
+ {
+ // loc_4816D: ; CODE XREF: movefun+149j
+ // ; movefun+154j
+ currentTile->state = 0x70;
+ currentTile->tile = LevelTileTypeZonk;
+ belowTile->state = 0x99;
+ belowTile->tile = 0x99;
+ return;
+ }
+
+ // loc_480E7: ; CODE XREF: movefun+152j
+ if (belowTile->tile == LevelTileTypeMurphy) // cmp byte ptr [si+18ACh], 3
+ {
+ break;
+ }
+
+ // loc_480F1: ; CODE XREF: movefun+15Cj
+ if (belowTile->tile == LevelTileTypeSnikSnak) // cmp byte ptr [si+18ACh], 11h
+ {
+ // loc_481FE: ; CODE XREF: movefun+168j
+ // ; movefun+188j ...
+ detonateBigExplosion(position + kLevelWidth); // Tile below
+ return;
+ }
+
+ // loc_480FB: ; CODE XREF: movefun+166j
+ if (belowTile->tile == 0xBB && belowTile->state == 0x2) // cmp word ptr [si+18ACh], 2BBh
+ {
+ shouldSkipTo_loc_481C6 = 1;
+ break;
+ }
+
+ // loc_48106: ; CODE XREF: movefun+171j
+ if (belowTile->tile == 0xBB && belowTile->state == 0x4) // cmp word ptr [si+18ACh], 4BBh
+ {
+ // loc_481E2: ; CODE XREF: movefun+17Ej
+ if (belowRightTile->tile == LevelTileTypeElectron) // cmp byte ptr [si+18AEh], 18h
+ {
+ belowTile->tile = LevelTileTypeElectron;
+ belowTile->state = 0;
+ }
+ // loc_481EF: ; CODE XREF: movefun+257j
+ if (belowRightTile->tile != LevelTileTypeExplosion) // cmp byte ptr [si+18AEh], 1Fh
+ {
+ belowRightTile->tile = LevelTileTypeSpace;
+ belowRightTile->state = 0;
+ }
+ // loc_481FE: ; CODE XREF: movefun+168j
+ // ; movefun+188j ...
+ detonateBigExplosion(position + kLevelWidth); // Tile below
+ return;
+ }
+
+ // loc_48111: ; CODE XREF: movefun+17Cj
+ if (belowTile->tile == LevelTileTypeElectron) // cmp byte ptr [si+18ACh], 18h
+ {
+ // loc_481FE: ; CODE XREF: movefun+168j
+ // ; movefun+188j ...
+ detonateBigExplosion(position + kLevelWidth); // Tile below
+ return;
+ }
+
+ // loc_4811B: ; CODE XREF: movefun+186j
+ if (belowTile->tile == LevelTileTypeOrangeDisk && belowTile->state == 0) // cmp word ptr [si+18ACh], 8
+ {
+ // loc_48205: ; CODE XREF: movefun+192j
+ gExplosionTimers[position + kLevelWidth] = 6;
+ return;
+ }
+
+ // loc_48125: ; CODE XREF: movefun+190j
+ playFallSound();
+ if ((belowTile->tile != LevelTileTypeZonk || belowTile->state != 0) // cmp word ptr [si+18ACh], 1
+ && (belowTile->tile != LevelTileTypeInfotron || belowTile->state != 0) // cmp word ptr [si+18ACh], 4
+ && (belowTile->tile != LevelTileTypeChip || belowTile->state != 0)) // cmp word ptr [si+18ACh], 5
+ {
+ return;
+ }
+
+ // loc_4813E: ; CODE XREF: movefun+19Dj
+ // ; movefun+1A4j ...
+ if ((belowLeftTile->state == 0 && belowLeftTile->tile == LevelTileTypeSpace) // cmp word ptr [si+18AAh], 0
+ || (belowLeftTile->state == 0x88 && belowLeftTile->tile == 0x88) // cmp word ptr [si+18AAh], 8888h
+ || (belowLeftTile->state == 0xAA && belowLeftTile->tile == 0xAA)) // cmp word ptr [si+18AAh], 0AAAAh
+ {
+ // loc_4817A: ; CODE XREF: movefun+1B3j
+ // ; movefun+1BBj ...
+ if (leftTile->state == 0 && leftTile->tile == LevelTileTypeSpace) // cmp word ptr [si+1832h], 0
+ {
+ // loc_48184: ; CODE XREF: movefun+1EFj
+ currentTile->state = 0x50;
+ leftTile->state = 0x88;
+ leftTile->tile = 0x88;
+ return;
+ }
+ else
+ {
+ continue;
+ }
+ }
+ if ((belowRightTile->state == 0 && belowRightTile->tile == LevelTileTypeSpace) // cmp word ptr [si+18AEh], 0
+ || (belowRightTile->state == 0x88 && belowRightTile->tile == 0x88) // cmp word ptr [si+18AEh], 8888h
+ || (belowRightTile->state == 0xAA && belowRightTile->tile == 0xAA)) // cmp word ptr [si+18AEh], 0AAAAh
+ {
+ // loc_48190: ; CODE XREF: movefun+1CAj
+ // ; movefun+1D2j ...
+ if (rightTile->tile == LevelTileTypeSpace && rightTile->state == 0) // cmp word ptr [si+1836h], 0
+ {
+ // loc_48198: ; CODE XREF: movefun+205j
+ currentTile->state = 0x60;
+ rightTile->state = 0x88;
+ rightTile->tile = 0x88;
+ return;
+ }
+ else
+ {
+ return;
+ }
+ }
+
+ return;
+
+ } while (1);
+
+ if (shouldSkipTo_loc_481C6 == 0)
+ {
+ // loc_481A4: ; CODE XREF: movefun+15Ej
+ if (belowTile->state == 0xE || belowTile->state == 0xF || belowTile->state == 0x28 || belowTile->state == 0x29 || belowTile->state == 0x25 || belowTile->state == 0x26)
+ {
+ return;
+ }
+ }
+
+ // loc_481C6: ; CODE XREF: movefun+173j
+ if (belowLeftTile->tile == LevelTileTypeElectron) // cmp byte ptr [si+18AAh], 18h
+ {
+ belowTile->tile = LevelTileTypeElectron;
+ belowTile->state = 0;
+ }
+ // loc_481D3: ; CODE XREF: movefun+23Bj
+ if (belowLeftTile->tile != LevelTileTypeExplosion) // cmp byte ptr [si+18AAh], 1Fh
+ {
+ belowLeftTile->tile = LevelTileTypeSpace;
+ belowLeftTile->state = 0;
+ }
+
+ // loc_481FE: ; CODE XREF: movefun+168j
+ // ; movefun+188j ...
+ detonateBigExplosion(position + kLevelWidth); // Tile below
+}
+
+void updateInfotronTiles(int16_t position) // movefun2 proc near ; DATA XREF: data:1612o
+{
+ // 01ED:17A5
+
+ StatefulLevelTile *currentTile = &gCurrentLevelState[position];
+ StatefulLevelTile *belowTile = &gCurrentLevelState[position + kLevelWidth];
+ StatefulLevelTile *belowLeftTile = &gCurrentLevelState[position + kLevelWidth - 1];
+ StatefulLevelTile *belowRightTile = &gCurrentLevelState[position + kLevelWidth + 1];
+ StatefulLevelTile *leftTile = &gCurrentLevelState[position - 1];
+ StatefulLevelTile *rightTile = &gCurrentLevelState[position + 1];
+
+ if (currentTile->tile != LevelTileTypeInfotron)
+ {
+ return;
+ }
+
+ // loc_48410: ; CODE XREF: movefun2+5j
+ uint8_t shouldSkipFirstPartOfLoop = 0;
+
+ if (currentTile->state != 0 || currentTile->tile != LevelTileTypeInfotron)
+ {
+ shouldSkipFirstPartOfLoop = 1; // used to emulate "jmp loc_48495"
+ }
+ else
+ {
+ // loc_4841B: ; CODE XREF: movefun2+Fj
+ if (belowTile->state == 0 && belowTile->tile == LevelTileTypeSpace)
+ {
+ // loc_48463: ; CODE XREF: movefun2+1Aj
+ currentTile->state = 0x40;
+ shouldSkipFirstPartOfLoop = 1; // used to emulate "jmp loc_48495"
+ }
+ else
+ {
+ if ((belowTile->state != 0 || belowTile->tile != LevelTileTypeZonk) && (belowTile->state != 0 || belowTile->tile != LevelTileTypeInfotron) && (belowTile->state != 0 || belowTile->tile != LevelTileTypeChip))
+ {
+ return;
+ }
+
+ // loc_48434: ; CODE XREF: movefun2+1Fj
+ // ; movefun2+24j ...
+ if ((belowLeftTile->state == 0 && belowLeftTile->tile == LevelTileTypeSpace) || (belowLeftTile->state == 0x88 && belowLeftTile->tile == 0x88) || (belowLeftTile->state == 0xAA && belowLeftTile->tile == 0xAA))
+ {
+ // loc_4846A: ; CODE XREF: movefun2+31j
+ // ; movefun2+39j ...
+ if (leftTile->state == 0 && leftTile->tile == LevelTileTypeSpace)
+ {
+ // loc_48473: ; CODE XREF: movefun2+67j
+ currentTile->state = 0x50;
+ leftTile->state = 0x88;
+ leftTile->tile = 0x88;
+ shouldSkipFirstPartOfLoop = 1; // used to emulate "jmp loc_48495"
+ }
+ }
+ }
+ }
+
+ do
+ {
+ if (shouldSkipFirstPartOfLoop == 0)
+ {
+ // loc_4844B: ; CODE XREF: movefun2+69j
+ // ; movefun2+1C7j
+ if ((belowRightTile->state != 0 || belowRightTile->tile != LevelTileTypeSpace) && (belowRightTile->state != 0x88 || belowRightTile->tile != 0x88) && (belowRightTile->state != 0xAA || belowRightTile->tile != 0xAA))
+ {
+ return;
+ }
+
+ // loc_48480: ; CODE XREF: movefun2+48j
+ // ; movefun2+50j ...
+ if (rightTile->state != 0 || rightTile->tile != LevelTileTypeSpace)
+ {
+ return;
+ }
+
+ // loc_48488: ; CODE XREF: movefun2+7Dj
+ currentTile->state = 0x60;
+ rightTile->state = 0x88;
+ rightTile->tile = 0x88;
+ }
+
+ shouldSkipFirstPartOfLoop = 0;
+
+ // loc_48495: ; CODE XREF: movefun2+11j
+ // ; movefun2+60j ...
+ uint8_t state = currentTile->state;
+ uint8_t stateType = state & 0xF0;
+
+ if (stateType != 0x10)
+ {
+ // loc_484A5: ; CODE XREF: movefun2+99j
+ if (stateType == 0x20)
+ {
+ // loc_4861B: ; CODE XREF: movefun2+A1j
+ uint8_t stateFrame = state & 0x7; // module 8?
+
+ // mov si, 12B6h
+ Point frameCoordinates = kInfotronSlideLeftAnimationFrameCoordinates[stateFrame];
+
+ uint8_t tileX = (position % kLevelWidth);
+ uint8_t tileY = (position / kLevelWidth);
+
+ uint16_t dstX = tileX * kTileSize;
+ uint16_t dstY = tileY * kTileSize;
+
+ drawMovingSpriteFrameInLevel(frameCoordinates.x,
+ frameCoordinates.y,
+ kTileSize * 2,
+ kTileSize,
+ dstX,
+ dstY);
+
+ state = currentTile->state;
+ state++;
+ if (state == 0x24) // 36
+ {
+ rightTile->state = 0xAA;
+ rightTile->tile = 0xAA;
+ }
+ // loc_48653: ; CODE XREF: movefun2+243j
+ if (state == 0x26) // 38
+ {
+ currentTile->state = state;
+ handleInfotronStateAfterFallingOneTile(position + 1);
+ return;
+ }
+ // loc_48666: ; CODE XREF: movefun2+24Ej
+ else if (state < 0x28) // 40
+ {
+ currentTile->state = state;
+ return;
+ }
+ // loc_48670: ; CODE XREF: movefun2+261j
+ else
+ {
+ currentTile->state = 0x70;
+ currentTile->tile = LevelTileTypeInfotron;
+ return;
+ }
+ }
+ // loc_484AC: ; CODE XREF: movefun2+9Fj
+ else if (stateType == 0x30)
+ {
+ // loc_48677: ; CODE XREF: movefun2+A8j
+ uint8_t stateFrame = state & 0x7;
+
+ uint8_t tileX = ((position - 1) % kLevelWidth);
+ uint8_t tileY = ((position - 1) / kLevelWidth);
+
+ uint16_t dstX = tileX * kTileSize;
+ uint16_t dstY = tileY * kTileSize;
+
+ // mov si, 12C6h
+ Point frameCoordinates = kInfotronSlideRightAnimationFrameCoordinates[stateFrame];
+
+ drawMovingSpriteFrameInLevel(frameCoordinates.x,
+ frameCoordinates.y,
+ kTileSize * 2,
+ kTileSize,
+ dstX, dstY);
+
+ state = currentTile->state;
+ state++;
+ if (state == 0x34) // 52
+ {
+ leftTile->state = 0xAA;
+ leftTile->tile = 0xAA;
+ }
+ // loc_486AF: ; CODE XREF: movefun2+29Fj
+ if (state == 0x36) // 54
+ {
+ currentTile->state = state;
+ handleInfotronStateAfterFallingOneTile(position - 1); // left tile
+ }
+ // loc_486C1: ; CODE XREF: movefun2+2AAj
+ if (state < 0x38) // 54
+ {
+ currentTile->state = state;
+ return;
+ }
+ else
+ {
+ currentTile->state = 0x70;
+ currentTile->tile = LevelTileTypeInfotron;
+ return;
+ }
+ }
+ // loc_484B3: ; CODE XREF: movefun2+A6j
+ else if (stateType == 0x40)
+ {
+ // loc_486D2: ; CODE XREF: movefun2+AFj
+ state++;
+ if (state < 0x42)
+ {
+ currentTile->state = state;
+ return;
+ }
+
+ // loc_486DE: ; CODE XREF: movefun2+2CFj
+ if (belowTile->state != 0 || belowTile->tile != LevelTileTypeSpace)
+ {
+ state--;
+ currentTile->state = state;
+ return;
+ }
+
+ // loc_486EC: ; CODE XREF: movefun2+2DBj
+ currentTile->state = 0xFF;
+ currentTile->tile = 0xFF;
+ belowTile->state = 0x10;
+ belowTile->tile = LevelTileTypeInfotron;
+ return;
+ }
+ // loc_484BA: ; CODE XREF: movefun2+ADj
+ else if (stateType == 0x50)
+ {
+ // loc_48700: ; CODE XREF: movefun2+B6j
+ uint8_t stateFrame = state & 0x7; // module 8?
+
+ uint8_t tileX = ((position - 1) % kLevelWidth);
+ uint8_t tileY = ((position - 1) / kLevelWidth);
+
+ uint16_t dstX = tileX * kTileSize;
+ uint16_t dstY = tileY * kTileSize;
+
+ // mov si, 12B6h
+ Point frameCoordinates = kInfotronSlideLeftAnimationFrameCoordinates[stateFrame];
+ drawMovingSpriteFrameInLevel(frameCoordinates.x,
+ frameCoordinates.y,
+ kTileSize * 2,
+ kTileSize,
+ dstX, dstY);
+ state = currentTile->state;
+ state++;
+
+ if (state < 0x52) // 82
+ {
+ currentTile->state = state;
+ return;
+ }
+ // loc_48737: ; CODE XREF: movefun2+328j
+ else if (belowLeftTile->state != 0 || belowLeftTile->tile != LevelTileTypeSpace) // cmp word ptr [si+18AAh], 0
+ {
+ // loc_48767: ; CODE XREF: movefun2+334j
+ // ; movefun2+343j
+ state--;
+ currentTile->state = state;
+ return;
+ }
+ else if ((leftTile->state != 0 || leftTile->tile != LevelTileTypeSpace) // cmp word ptr [si+1832h], 0
+ && (leftTile->state != 0x88 || leftTile->tile != 0x88)) // cmp word ptr [si+1832h], 8888h
+ {
+ // loc_48767: ; CODE XREF: movefun2+334j
+ // ; movefun2+343j
+ state--;
+ currentTile->state = state;
+ return;
+ }
+ else
+ {
+ // loc_4874D: ; CODE XREF: movefun2+33Bj
+ currentTile->state = 0xFF;
+ currentTile->tile = 0xFF;
+ leftTile->state = 0x22;
+ leftTile->tile = LevelTileTypeInfotron;
+ belowLeftTile->state = 0x99;
+ belowLeftTile->tile = 0x99;
+ return;
+ }
+ }
+ // loc_484C1: ; CODE XREF: movefun2+B4j
+ else if (stateType == 0x60)
+ {
+ // loc_4876E: ; CODE XREF: movefun2+BDj
+ uint8_t stateFrame = state & 0x7; // module 8?
+
+ uint8_t tileX = (position % kLevelWidth);
+ uint8_t tileY = (position / kLevelWidth);
+
+ uint16_t dstX = tileX * kTileSize;
+ uint16_t dstY = tileY * kTileSize;
+
+ // mov si, 12C6h
+ Point frameCoordinates = kInfotronSlideRightAnimationFrameCoordinates[stateFrame];
+
+ drawMovingSpriteFrameInLevel(frameCoordinates.x,
+ frameCoordinates.y,
+ kTileSize * 2,
+ kTileSize,
+ dstX, dstY);
+
+ state = currentTile->state;
+ state++;
+
+ if (state < 0x62) // 98
+ {
+ currentTile->state = state;
+ return;
+ }
+ // loc_487A5: ; CODE XREF: movefun2+396j
+ else if (belowRightTile->state != 0 || belowRightTile->tile != LevelTileTypeSpace) // cmp word ptr [si+18AEh], 0
+ {
+ // loc_487D5: ; CODE XREF: movefun2+3A2j
+ // ; movefun2+3B1j
+ state--;
+ currentTile->state = state;
+ return;
+ }
+ else if ((rightTile->state != 0 || rightTile->tile != LevelTileTypeSpace) // cmp word ptr [si+1836h], 0
+ && (rightTile->state != 0x88 || rightTile->tile != 0x88)) // cmp word ptr [si+1836h], 8888h
+ {
+ // loc_487D5: ; CODE XREF: movefun2+3A2j
+ // ; movefun2+3B1j
+ state--;
+ currentTile->state = state;
+ return;
+ }
+ else
+ {
+ // loc_487BB: ; CODE XREF: movefun2+3A9j
+ currentTile->state = 0xFF;
+ currentTile->tile = 0xFF;
+ rightTile->state = 0x32;
+ rightTile->tile = LevelTileTypeInfotron;
+ belowRightTile->state = 0x99;
+ belowRightTile->tile = 0x99;
+ return;
+ }
+ }
+ // loc_484C8: ; CODE XREF: movefun2+BBj
+ else if (stateType == 0x70)
+ {
+ // loc_487DC: ; CODE XREF: movefun2:loc_484CCj
+ if ((belowTile->state != 0 || belowTile->tile != LevelTileTypeSpace) && (belowTile->state != 0x99 || belowTile->tile != 0x99))
+ {
+ return;
+ }
+
+ // loc_487EC: ; CODE XREF: movefun2+3D9j
+ // ; movefun2+3E1j
+ currentTile->state = 0xFF;
+ currentTile->tile = 0xFF;
+
+ // add si, 78h ; 'x'
+ position += kLevelWidth;
+
+ currentTile = &gCurrentLevelState[position];
+ belowTile = &gCurrentLevelState[position + kLevelWidth];
+ belowLeftTile = &gCurrentLevelState[position + kLevelWidth - 1];
+ leftTile = &gCurrentLevelState[position - 1];
+ belowRightTile = &gCurrentLevelState[position + kLevelWidth + 1];
+ rightTile = &gCurrentLevelState[position + 1];
+
+ currentTile->state = 0x10;
+ currentTile->tile = LevelTileTypeInfotron;
+ }
+ else
+ {
+ return;
+ }
+ }
+
+ // loc_484D0: ; CODE XREF: movefun2+9Bj
+ // ; movefun2+3F3j
+ // This animates the Infotron falling
+ uint8_t somePositionThing = state;
+ somePositionThing *= 2;
+ somePositionThing &= 0x1F;
+
+ uint16_t offset = kFallAnimationGravityOffsets[somePositionThing];
+
+ uint16_t finalPosition = position - kLevelWidth;
+ uint8_t tileX = (finalPosition % kLevelWidth);
+ uint8_t tileY = (finalPosition / kLevelWidth);
+
+ uint16_t dstX = tileX * kTileSize + (offset % 122);
+ uint16_t dstY = tileY * kTileSize + (offset / 122);
+
+ // mov si, word_515C4
+ drawMovingSpriteFrameInLevel(240, 178,
+ kTileSize,
+ kTileSize + 2,
+ dstX, dstY);
+
+ uint8_t newState = currentTile->state;
+ newState++;
+ if (newState == 0x16) // 22
+ {
+ currentTile->state = newState;
+ handleInfotronStateAfterFallingOneTile(position - kLevelWidth); // Tile above
+ return;
+ }
+ // loc_48513: ; CODE XREF: movefun2+FBj
+ else if (newState < 0x18) // 24
+ {
+ currentTile->state = newState;
+ return;
+ }
+
+ // loc_4851D: ; CODE XREF: movefun2+10Ej
+ // This part handles what to do when the Infotron finished falling 1 tile
+ currentTile->state = 0;
+
+ if ((belowTile->tile == LevelTileTypeSpace && belowTile->state == 0) // cmp word ptr [si+18ACh], 0
+ || (belowTile->tile == 0x99 && belowTile->state == 0x99)) // cmp word ptr [si+18ACh], 9999h
+ {
+ // loc_485BB: ; CODE XREF: movefun2+121j
+ // ; movefun2+12Cj
+ currentTile->state = 0x70;
+ currentTile->tile = LevelTileTypeInfotron;
+ belowTile->state = 0x99;
+ belowTile->tile = 0x99;
+ return;
+ }
+
+ // loc_48537: ; CODE XREF: movefun2+12Aj
+ if (belowTile->tile == LevelTileTypeMurphy) // cmp byte ptr [si+18ACh], 3
+ {
+ // loc_485F2: ; CODE XREF: movefun2+136j
+ if (belowTile->state == 0xE || belowTile->state == 0xF || belowTile->state == 0x28 || belowTile->state == 0x29 || belowTile->state == 0x25 || belowTile->state == 0x26)
+ {
+ return;
+ }
+
+ // loc_48614: ; CODE XREF: movefun2+140j
+ // ; movefun2+14Aj ...
+ detonateBigExplosion(position + kLevelWidth);
+ return;
+ }
+
+ // loc_48541: ; CODE XREF: movefun2+134j
+ if ((belowTile->tile == LevelTileTypeRedDisk && belowTile->state == 0) // cmp word ptr [si+18ACh], 14h
+ || belowTile->tile == LevelTileTypeSnikSnak // cmp byte ptr [si+18ACh], 11h
+ || belowTile->tile == LevelTileTypeElectron // cmp byte ptr [si+18ACh], 18h
+ || (belowTile->tile == LevelTileTypeYellowDisk && belowTile->state == 0) // cmp word ptr [si+18ACh], 12h
+ || (belowTile->tile == LevelTileTypeOrangeDisk && belowTile->state == 0)) // cmp word ptr [si+18ACh], 8
+ {
+ // loc_48614: ; CODE XREF: movefun2+140j
+ // ; movefun2+14Aj ...
+ detonateBigExplosion(position + kLevelWidth);
+ return;
+ }
+
+ // loc_48573: ; CODE XREF: movefun2+166j
+ playFallSound();
+ if ((belowTile->tile != LevelTileTypeZonk || belowTile->state != 0) // cmp word ptr [si+18ACh], 1
+ && (belowTile->tile != LevelTileTypeInfotron || belowTile->state != 0) // cmp word ptr [si+18ACh], 4
+ && (belowTile->tile != LevelTileTypeChip || belowTile->state != 0)) // cmp word ptr [si+18ACh], 5
+ {
+ return;
+ }
+
+ // loc_4858C: ; CODE XREF: movefun2+173j
+ // ; movefun2+17Aj ...
+ if ((belowLeftTile->tile == LevelTileTypeSpace && belowLeftTile->state == 0) // cmp word ptr [si+18AAh], 0
+ || (belowLeftTile->tile == 0x88 && belowLeftTile->state == 0x88) // cmp word ptr [si+18AAh], 8888h
+ || (belowLeftTile->tile == 0xAA && belowLeftTile->state == 0xAA)) // cmp word ptr [si+18AAh], 0AAAAh
+ {
+ // loc_485C8: ; CODE XREF: movefun2+189j
+ // ; movefun2+191j ...
+ if (leftTile->state == 0 && leftTile->tile == LevelTileTypeSpace)
+ {
+ // loc_485D2: ; CODE XREF: movefun2+1C5j
+ currentTile->state = 0x50;
+ leftTile->state = 0x88;
+ leftTile->tile = 0x88;
+ return;
+ }
+ else
+ {
+ continue; // jmp loc_4844B
+ }
+ }
+ if ((belowRightTile->tile == LevelTileTypeSpace && belowRightTile->state == 0) // cmp word ptr [si+18AEh], 0
+ || (belowRightTile->tile == 0x88 && belowRightTile->state == 0x88) // cmp word ptr [si+18AEh], 8888h
+ || (belowRightTile->tile == 0xAA && belowRightTile->state == 0xAA)) // cmp word ptr [si+18AEh], 0AAAAh
+ {
+ // loc_485DE: ; CODE XREF: movefun2+1A0j
+ // ; movefun2+1A8j ...
+ if (rightTile->state == 0 && rightTile->tile == LevelTileTypeSpace)
+ {
+ // loc_485E6: ; CODE XREF: movefun2+1DBj
+ currentTile->state = 0x60;
+ rightTile->state = 0x88;
+ rightTile->tile = 0x88;
+ return;
+ }
+ return;
+ }
+ return;
+ } while (1);
+}
+
+void handleMurphyCollisionAfterMovement(int16_t position) // sub_487FE proc near ; CODE XREF: update?+E0Cp update?+E2Ap ...
+{
+ // 01ED:1B9B
+ StatefulLevelTile *currentTile = &gCurrentLevelState[position];
+ StatefulLevelTile *aboveTile = &gCurrentLevelState[position - kLevelWidth];
+ StatefulLevelTile *leftTile = &gCurrentLevelState[position - 1];
+ StatefulLevelTile *rightTile = &gCurrentLevelState[position + 1];
+ StatefulLevelTile *aboveRightTile = &gCurrentLevelState[position - kLevelWidth + 1];
+ StatefulLevelTile *aboveLeftTile = &gCurrentLevelState[position - kLevelWidth - 1];
+
+ if (currentTile->tile != LevelTileTypeExplosion)
+ {
+ currentTile->state = 0;
+ currentTile->tile = LevelTileTypeSpace;
+ }
+
+ // loc_4880B: ; CODE XREF: handleMurphyCollisionAfterMovement+5j
+ if ((aboveTile->state == 0 && aboveTile->tile == LevelTileTypeSpace) || (aboveTile->state == 0x99 && aboveTile->tile == 0x99))
+ {
+ // loc_48835: ; CODE XREF: handleMurphyCollisionAfterMovement+12j
+ // ; handleMurphyCollisionAfterMovement+1Aj
+ if (aboveLeftTile->state == 0 && aboveLeftTile->tile == LevelTileTypeZonk)
+ {
+ // loc_48852: ; CODE XREF: handleMurphyCollisionAfterMovement+3Cj
+ if ((leftTile->state == 0 && leftTile->tile == LevelTileTypeZonk) || (leftTile->state == 0 && leftTile->tile == LevelTileTypeInfotron) || (leftTile->state == 0 && leftTile->tile == LevelTileTypeChip))
+ {
+ // loc_48869: ; CODE XREF: handleMurphyCollisionAfterMovement+59j
+ // ; handleMurphyCollisionAfterMovement+60j ...
+ aboveLeftTile->state = 0x60;
+ aboveTile->state = 0x88;
+ aboveTile->tile = 0x88;
+ return;
+ }
+ }
+ else if (aboveLeftTile->state == 0 && aboveLeftTile->tile == LevelTileTypeInfotron)
+ {
+ // loc_48897: ; CODE XREF: handleMurphyCollisionAfterMovement+43j
+ if ((leftTile->state == 0 && leftTile->tile == LevelTileTypeZonk) || (leftTile->state == 0 && leftTile->tile == LevelTileTypeInfotron) || (leftTile->state == 0 && leftTile->tile == LevelTileTypeChip))
+ {
+ // loc_488AE: ; CODE XREF: handleMurphyCollisionAfterMovement+9Ej
+ // ; handleMurphyCollisionAfterMovement+A5j ...
+ aboveLeftTile->state = 0x60;
+ aboveTile->state = 0x88;
+ aboveTile->tile = 0x88;
+ return;
+ }
+ }
+ // loc_48843: ; CODE XREF: handleMurphyCollisionAfterMovement+69j
+ // ; handleMurphyCollisionAfterMovement+AEj
+ if (aboveRightTile->state == 0 && aboveRightTile->tile == LevelTileTypeZonk)
+ {
+ // loc_48875: ; CODE XREF: handleMurphyCollisionAfterMovement+4Aj
+ if ((rightTile->state == 0 && rightTile->tile == LevelTileTypeZonk) || (rightTile->state == 0 && rightTile->tile == LevelTileTypeInfotron) || (rightTile->state == 0 && rightTile->tile == LevelTileTypeChip))
+ {
+ // loc_4888B: ; CODE XREF: handleMurphyCollisionAfterMovement+7Cj
+ // ; handleMurphyCollisionAfterMovement+83j ...
+ aboveRightTile->state = 0x50;
+ aboveTile->state = 0x88;
+ aboveTile->tile = 0x88;
+ }
+ }
+ else if (aboveRightTile->state == 0 && aboveRightTile->tile == LevelTileTypeInfotron)
+ {
+ // loc_488BA: ; CODE XREF: handleMurphyCollisionAfterMovement+51j
+ if ((rightTile->state == 0 && rightTile->tile == LevelTileTypeZonk) || (rightTile->state == 0 && rightTile->tile == LevelTileTypeInfotron) || (rightTile->state == 0 && rightTile->tile == LevelTileTypeChip))
+ {
+ // loc_488D0: ; CODE XREF: handleMurphyCollisionAfterMovement+C1j
+ // ; handleMurphyCollisionAfterMovement+C8j ...
+ aboveRightTile->state = 0x50;
+ aboveTile->state = 0x88;
+ aboveTile->tile = 0x88;
+ }
+ }
+ }
+ else if (aboveTile->state == 0 && aboveTile->tile == LevelTileTypeZonk)
+ {
+ // loc_48829: ; CODE XREF: handleMurphyCollisionAfterMovement+21j
+ aboveTile->state = 0x40;
+ }
+ else if (aboveTile->state == 0 && aboveTile->tile == LevelTileTypeInfotron)
+ {
+ // loc_4882F: ; CODE XREF: handleMurphyCollisionAfterMovement+28j
+ aboveTile->state = 0x40;
+ }
+}
+
+void handleZonkStateAfterFallingOneTile(int16_t position) // sub_488DC proc near ; CODE XREF: movefun+124p
+ // ; movefun+2C6p ...
+{
+ // 01ED:1C79
+ StatefulLevelTile *currentTile = &gCurrentLevelState[position];
+ StatefulLevelTile *aboveTile = &gCurrentLevelState[position - kLevelWidth];
+ StatefulLevelTile *leftTile = &gCurrentLevelState[position - 1];
+ StatefulLevelTile *rightTile = &gCurrentLevelState[position + 1];
+ StatefulLevelTile *aboveAboveTile = &gCurrentLevelState[position - kLevelWidth * 2];
+ StatefulLevelTile *aboveLeftTile = &gCurrentLevelState[position - kLevelWidth - 1];
+ StatefulLevelTile *aboveRightTile = &gCurrentLevelState[position - kLevelWidth + 1];
+
+ if (currentTile->tile != LevelTileTypeExplosion)
+ {
+ currentTile->state = 0;
+ currentTile->tile = LevelTileTypeSpace;
+ }
+
+ // loc_488E9: ; CODE XREF: handleZonkStateAfterFallingOneTile+5j
+ if (aboveTile->state != 0 || aboveTile->tile != LevelTileTypeSpace) // cmp word ptr [si+17BCh], 0
+ {
+ if (aboveTile->state != 0x99 || aboveTile->tile != 0x99) // cmp word ptr [si+17BCh], 9999h
+ {
+ return;
+ }
+
+ // loc_488F9: ; CODE XREF: handleZonkStateAfterFallingOneTile+1Aj
+ if (aboveAboveTile->tile != LevelTileTypeInfotron) // cmp byte ptr [si+1744h], 4
+ {
+ return;
+ }
+ }
+
+ // loc_48901: ; CODE XREF: handleZonkStateAfterFallingOneTile+12j
+ // ; handleZonkStateAfterFallingOneTile+22j
+ if (aboveLeftTile->state == 0 && aboveLeftTile->tile == LevelTileTypeZonk) // cmp word ptr [si+17BAh], 1
+ {
+ // loc_48910: ; CODE XREF: handleZonkStateAfterFallingOneTile+2Aj
+ if (leftTile->state == 0 && (leftTile->tile == LevelTileTypeZonk || leftTile->tile == LevelTileTypeInfotron || leftTile->tile == LevelTileTypeChip))
+ {
+ // loc_48927: ; CODE XREF: handleZonkStateAfterFallingOneTile+39j
+ // ; handleZonkStateAfterFallingOneTile+40j ...
+ // mov word ptr [si+17BAh], 6001h
+ aboveLeftTile->state = 0x60;
+ aboveLeftTile->tile = LevelTileTypeZonk;
+ // mov word ptr [si+17BCh], 8888h
+ aboveTile->state = 0x88;
+ aboveTile->tile = 0x88;
+ return;
+ }
+ }
+
+ // loc_48908: ; CODE XREF: handleZonkStateAfterFallingOneTile+49j
+ if (aboveRightTile->state == 0 && aboveRightTile->tile == LevelTileTypeZonk) // cmp word ptr [si+17BEh], 1
+ {
+ // loc_48934: ; CODE XREF: handleZonkStateAfterFallingOneTile+31j
+ if (rightTile->state != 0 || (rightTile->tile != LevelTileTypeZonk && rightTile->tile != LevelTileTypeInfotron && rightTile->tile != LevelTileTypeChip))
+ {
+ return;
+ }
+
+ // loc_4894A: ; CODE XREF: handleZonkStateAfterFallingOneTile+5Dj
+ // ; handleZonkStateAfterFallingOneTile+64j ...
+ // mov word ptr [si+17BEh], 5001h
+ aboveRightTile->state = 0x50;
+ aboveRightTile->tile = LevelTileTypeZonk;
+ // mov word ptr [si+17BCh], 8888h
+ aboveTile->state = 0x88;
+ aboveTile->tile = 0x88;
+ return;
+ }
+}
+
+void handleInfotronStateAfterFallingOneTile(int16_t position) // sub_48957 proc near ; CODE XREF: movefun2+104p
+// ; movefun2+257p ...
+{
+ StatefulLevelTile *currentTile = &gCurrentLevelState[position];
+ StatefulLevelTile *aboveTile = &gCurrentLevelState[position - kLevelWidth];
+ StatefulLevelTile *aboveAboveTile = &gCurrentLevelState[position - kLevelWidth * 2];
+ StatefulLevelTile *aboveLeftTile = &gCurrentLevelState[position - kLevelWidth - 1];
+ StatefulLevelTile *aboveRightTile = &gCurrentLevelState[position - kLevelWidth + 1];
+ StatefulLevelTile *leftTile = &gCurrentLevelState[position - 1];
+ StatefulLevelTile *rightTile = &gCurrentLevelState[position + 1];
+
+ if (currentTile->tile != LevelTileTypeExplosion)
+ {
+ currentTile->state = 0;
+ currentTile->tile = LevelTileTypeSpace;
+ }
+ // loc_48964: ; CODE XREF: handleInfotronStateAfterFallingOneTile+5j
+ if (aboveTile->state != 0 || aboveTile->tile != LevelTileTypeSpace)
+ {
+ if (aboveTile->state == 0x99 && aboveTile->tile == 0x99)
+ {
+ // loc_48974: ; CODE XREF: handleInfotronStateAfterFallingOneTile+1Aj
+ if (aboveAboveTile->tile != LevelTileTypeZonk)
+ {
+ return;
+ }
+ }
+ else
+ {
+ return;
+ }
+ }
+
+ // loc_4897C: ; CODE XREF: handleInfotronStateAfterFallingOneTile+12j
+ // ; handleInfotronStateAfterFallingOneTile+22j
+ if (aboveLeftTile->state == 0 && aboveLeftTile->tile == LevelTileTypeInfotron)
+ {
+ // loc_4898B: ; CODE XREF: handleInfotronStateAfterFallingOneTile+2Aj
+ if ((leftTile->state == 0 && leftTile->tile == LevelTileTypeZonk) || (leftTile->state == 0 && leftTile->tile == LevelTileTypeInfotron) || (leftTile->state == 0 && leftTile->tile == LevelTileTypeChip))
+ {
+ // loc_489A2: ; CODE XREF: handleInfotronStateAfterFallingOneTile+39j
+ // ; handleInfotronStateAfterFallingOneTile+40j ...
+ aboveLeftTile->state = 0x60;
+ aboveLeftTile->tile = LevelTileTypeInfotron;
+ aboveTile->state = 0x88;
+ aboveTile->tile = 0x88;
+ return;
+ }
+ }
+
+ // loc_48983: ; CODE XREF: handleInfotronStateAfterFallingOneTile+49j
+ if (aboveRightTile->state != 0 || aboveRightTile->tile != LevelTileTypeInfotron)
+ {
+ return;
+ }
+
+ // loc_489AF: ; CODE XREF: handleInfotronStateAfterFallingOneTile+31j
+ if ((rightTile->state != 0 || rightTile->tile != LevelTileTypeZonk) && (rightTile->state != 0 || rightTile->tile != LevelTileTypeInfotron) && (rightTile->state != 0 || rightTile->tile != LevelTileTypeChip))
+ {
+ return;
+ }
+
+ // loc_489C5: ; CODE XREF: handleInfotronStateAfterFallingOneTile+5Dj
+ // ; handleInfotronStateAfterFallingOneTile+64j ...
+ aboveRightTile->state = 0x50;
+ aboveRightTile->tile = LevelTileTypeInfotron;
+ aboveTile->state = 0x88;
+ aboveTile->tile = 0x88;
+}
+
+void initializeGameInfo() // sub_48A20 proc near ; CODE XREF: start+32Fp
+ // ; runLevel:notFunctionKeyp ...
+{
+ // 01ED:1DBD
+ // word_510BC = gMurphyTileX;
+ // word_510BE = gMurphyTileY;
+ gIsMurphyLookingLeft = 0;
+ gShouldKillMurphy = 0;
+ gShouldExitLevel = 0;
+ gQuitLevelCountdown = 0;
+ gNumberOfRemainingRedDisks = 0;
+ gAdditionalInfoInGamePanelFrameCounter = 0;
+ gMurphyYawnAndSleepCounter = 0;
+ gLastDrawnMinutesAndSeconds = 0xFFFF;
+ gLastDrawnHours = 0xFF; // 255
+ gIsGameRunning = 1;
+ gAuxGameSeconds20msAccumulator = 0;
+ gGameSeconds = 0;
+ gGameMinutes = 0;
+ gGameHours = 0;
+ gIsExplosionStarted = 0;
+ gTerminalMaxFramesToNextScroll = 0x7F; // 127
+ gAreYellowDisksDetonated = 0;
+ gFrameCounter = 0;
+ // mov byte ptr word_510C1, 1
+ // mov byte ptr word_510C1+1, 0
+ gShouldShowGamePanel = 1;
+ gCurrentPanelHeight = kPanelBitmapHeight;
+ gToggleGamePanelKeyAutoRepeatCounter = 0;
+ gAreEnemiesFrozen = 0;
+ gIsMurphyGoingThroughPortal &= 0xFF00; // mov byte ptr gIsMurphyGoingThroughPortal, 0
+ gPlantedRedDiskCountdown = 0;
+ gPlantedRedDiskPosition = 0;
+}
+
+void handleGameIterationStarted()
+{
+ gGameIterationStartTime = getTime();
+}
+
+void handleGameIterationFinished()
+{
+ const float kOriginalIterationDuration = 1000.0 / 35; // 35 iterations per second in the original game
+ float currentIterationDuration = getTime() - gGameIterationStartTime;
+
+ float targetIterationDuration = kOriginalIterationDuration * kSpeedTimeFactors[gGameSpeed];
+
+ if (gFastMode != FastModeTypeNone)
+ {
+ targetIterationDuration = 0;
+ }
+
+ if (currentIterationDuration < targetIterationDuration)
+ {
+ waitTime(targetIterationDuration - currentIterationDuration);
+ }
+
+ gNumberOfGameIterations++;
+
+ if (gGameIterationRateReferenceTime == 0)
+ {
+ gGameIterationRateReferenceTime = getTime();
+ }
+ else
+ {
+ uint32_t difference = getTime() - gGameIterationRateReferenceTime;
+
+ if (difference > 1000)
+ {
+ gGameIterationRate = gNumberOfGameIterations * 1000.f / difference;
+ gNumberOfGameIterations = 0;
+ gGameIterationRateReferenceTime = getTime();
+ }
+ }
+}
+
+void runLevel() // proc near ; CODE XREF: start+35Cp
+{
+ // 01ED:1E58
+ if (gIsPlayingDemo == 0)
+ {
+ // loc_48ACE: ; CODE XREF: runLevel+5j
+ gIsLevelStartedAsDemo = 0;
+ gLevelFailed = 1;
+ }
+ else
+ {
+ gIsLevelStartedAsDemo = 1;
+ gLevelFailed = 0;
+ }
+
+ // loc_48AD8: ; CODE XREF: runLevel+11j
+ if (gDemoRecordingJustStarted == 1)
+ {
+ // loc_48ADF: ; CODE XREF: runLevel+BAj
+ gDemoRecordingJustStarted = 0;
+ drawGameTime();
+
+ do
+ {
+ // isFunctionKey: ; CODE XREF: runLevel+35j
+ int9handler(1);
+ } while (areAnyF1ToF10KeysPressed());
+
+ // notFunctionKey: ; CODE XREF: runLevel+31j
+ initializeGameInfo();
+ if (isMusicEnabled == 0)
+ {
+ stopMusic();
+ }
+
+ // loc_48AFF: ; CODE XREF: runLevel+3Fj
+ gIsLevelStartedAsDemo = 0;
+ gLevelFailed = 1;
+ }
+
+ // loc_48B09: ; CODE XREF: runLevel+22j
+ gPlantedRedDiskCountdown = 0;
+ byte_5A323 = 0;
+
+ do
+ {
+ handleGameIterationStarted();
+
+ int9handler(0);
+
+ uint16_t mouseButtonsStatus;
+
+ // loc_48B23: ; CODE XREF: runLevel+63j
+ getMouseStatus(NULL, NULL, &mouseButtonsStatus);
+
+ // loc_48B38: ; CODE XREF: runLevel+6Ej runLevel+75j
+ if (gIsDebugModeEnabled != 0)
+ {
+ if (gToggleFancyEasyTilesThrottleCounter != 0)
+ {
+ gToggleFancyEasyTilesThrottleCounter--;
+ }
+
+ // loc_48B4A: ; CODE XREF: runLevel+89j
+ if (gIsEnterPressed == 0 && mouseButtonsStatus == MouseButtonLeft // cmp bx, 1
+ && gToggleFancyEasyTilesThrottleCounter == 0)
+ {
+ gToggleFancyEasyTilesThrottleCounter = 0xA;
+ restoreOriginalFancyTiles(); // 01ED:1EFF
+ drawFixedLevel();
+ convertToEasyTiles();
+ }
+ }
+
+ // loc_48B6B: ; CODE XREF: runLevel+82j runLevel+94j ...
+ handleGameUserInput(); // 01ED:1F08
+ if (gDemoRecordingJustStarted == 1)
+ {
+ // Restart the demo
+ // loc_48ADF: ; CODE XREF: runLevel+BAj
+ gDemoRecordingJustStarted = 0;
+ drawGameTime();
+
+ do
+ {
+ // isFunctionKey: ; CODE XREF: runLevel+35j
+ int9handler(1);
+ } while (areAnyF1ToF10KeysPressed());
+
+ // notFunctionKey: ; CODE XREF: runLevel+31j
+ initializeGameInfo();
+ if (isMusicEnabled == 0)
+ {
+ stopMusic();
+ }
+
+ // loc_48AFF: ; CODE XREF: runLevel+3Fj
+ gIsLevelStartedAsDemo = 0;
+ gLevelFailed = 1;
+
+ // loc_48B09: ; CODE XREF: runLevel+22j
+ gPlantedRedDiskCountdown = 0;
+ byte_5A323 = 0;
+
+ continue;
+
+ // All the code in this "if" is equivalent to "jmp loc_48ADF"
+ }
+
+ // loc_48B78: ; CODE XREF: runLevel+B8j
+ if (gIsFlashingBackgroundModeEnabled != 0)
+ {
+ replaceCurrentPaletteColor(0, (Color){0x35, 0x35, 0x35});
+ }
+
+ // noFlashing: ; CODE XREF: runLevel+C2j
+ updateMovingObjects(); // 01ED:1F28
+ if (gIsFlashingBackgroundModeEnabled != 0)
+ {
+ replaceCurrentPaletteColor(0, (Color){0x21, 0x21, 0x21});
+ }
+
+ // noFlashing2: ; CODE XREF: runLevel+D8j
+ drawGameTime();
+ clearAdditionalInfoInGamePanelIfNeeded();
+ if (gIsFlashingBackgroundModeEnabled != 0)
+ {
+ replaceCurrentPaletteColor(0, (Color){0x2d, 0x21, 0x0f});
+ }
+
+ // noFlashing3: ; CODE XREF: runLevel+F1j
+ // 01ED:1F5B
+ updatePlantedRedDisk();
+ updateExplosionTimers();
+ updateScrollOffset();
+
+ // loc_48D59: ; CODE XREF: runLevel+19Bj
+ // ; runLevel+1D2j ...
+ if (gIsFlashingBackgroundModeEnabled != 0)
+ {
+ replaceCurrentPaletteColor(0, (Color){0x3f, 0x3f, 0x3f});
+ }
+
+ // noFlashing4: ; CODE XREF: runLevel+2D1j
+ drawCurrentLevelViewport(gCurrentPanelHeight); // Added by me
+ if (gFastMode != FastModeTypeUltra)
+ {
+ videoLoop(); // 01ED:2142
+ }
+ handleGameIterationFinished();
+
+ // isFastMode2: ; CODE XREF: runLevel+2E8j
+ if (gDebugExtraRenderDelay > 1)
+ {
+ playBaseSound();
+ }
+
+ // loc_48DB2: ; CODE XREF: runLevel+2F2j
+ // Extra delays in debug mode
+ for (int i = 1; i < gDebugExtraRenderDelay; ++i)
+ {
+ // loc_48DB6: ; CODE XREF: runLevel+310j
+ if (gFastMode == FastModeTypeNone)
+ {
+ videoLoop(); // 01ED:2160
+ }
+
+ // isFastMode3: ; CODE XREF: runLevel+303j
+ handleGameUserInput();
+ }
+
+ // loc_48DCD: ; CODE XREF: runLevel+2FCj
+ if (gIsFlashingBackgroundModeEnabled != 0)
+ {
+ replaceCurrentPaletteColor(0, (Color){0, 0, 0});
+ }
+
+ // noFlashing5: ; CODE XREF: runLevel+317j
+ if (gShouldExitGame != 0)
+ {
+ break;
+ }
+ gFrameCounter++;
+ if (gShouldExitLevel == 1)
+ {
+ break;
+ }
+ if (gQuitLevelCountdown == 0) // 01ED:218D
+ {
+ continue;
+ }
+
+ // loc_48DFA: ; CODE XREF: runLevel+33Aj
+ // 01ED:2197
+ gQuitLevelCountdown--;
+ if (gQuitLevelCountdown == 0)
+ {
+ break;
+ }
+ } while (1);
+
+ // loc_48E03: ; CODE XREF: runLevel+328j
+ // ; runLevel+333j ...
+ if (gIsRecordingDemo != 0)
+ {
+ stopRecordingDemo();
+ }
+
+ // loc_48E13: ; CODE XREF: runLevel+353j
+ uint8_t userDidNotCheat = (gHasUserCheated == 0);
+ gHasUserCheated = 0;
+ if (userDidNotCheat && gShouldUpdateTotalLevelTime != 0 && byte_5A323 == 0)
+ {
+ addCurrentGameTimeToPlayer();
+ }
+
+ // loc_48E30: ; CODE XREF: runLevel+362j
+ // ; runLevel+369j ...
+ gIsMoveScrollModeEnabled = 0;
+ gAdditionalScrollOffsetX = 0;
+ gAdditionalScrollOffsetY = 0;
+ gIsFlashingBackgroundModeEnabled = 0;
+ gDebugExtraRenderDelay = 1;
+ replaceCurrentPaletteColor(0, (Color){0, 0, 0});
+}
+
+void updateUserInputInScrollMovementMode() // sub_4914A proc near ; CODE XREF: handleGameUserInput+7p
+{
+ if (isLeftButtonPressed())
+ {
+ gAdditionalScrollOffsetX--;
+ gAdditionalScrollOffsetX--;
+ }
+
+ // loc_49159: ; CODE XREF: updateUserInputInScrollMovementMode+5j
+ if (isRightButtonPressed())
+ {
+ gAdditionalScrollOffsetX++;
+ gAdditionalScrollOffsetX++;
+ }
+
+ // loc_49168: ; CODE XREF: updateUserInputInScrollMovementMode+14j
+ if (isUpButtonPressed())
+ {
+ gAdditionalScrollOffsetY--;
+ gAdditionalScrollOffsetY--;
+ }
+
+ // loc_49177: ; CODE XREF: updateUserInputInScrollMovementMode+23j
+ if (isDownButtonPressed())
+ {
+ gAdditionalScrollOffsetY++;
+ gAdditionalScrollOffsetY++;
+ }
+
+ // loc_49186: ; CODE XREF: updateUserInputInScrollMovementMode+32j
+ if (gIsNumpad5Pressed != 0)
+ {
+ gAdditionalScrollOffsetX = 0;
+ gAdditionalScrollOffsetY = 0;
+ }
+
+ // loc_49199: ; CODE XREF: updateUserInputInScrollMovementMode+41j
+ if (gIsInsertKeyPressed != 0)
+ {
+ gAdditionalScrollOffsetX = -gMurphyScrollOffsetX;
+ gAdditionalScrollOffsetY = -gMurphyScrollOffsetY;
+ }
+
+ // loc_491B3: ; CODE XREF: updateUserInputInScrollMovementMode+60j
+ if (gIsHomeKeyPressed != 0)
+ {
+ gAdditionalScrollOffsetX = (kLevelBitmapWidth / 2) - gMurphyScrollOffsetX;
+ gAdditionalScrollOffsetY = -gMurphyScrollOffsetY;
+ }
+
+ // loc_491C5: ; CODE XREF: updateUserInputInScrollMovementMode+72j
+ if (gIsRePagKeyPressed != 0)
+ {
+ gAdditionalScrollOffsetX = kLevelBitmapWidth - gMurphyScrollOffsetX;
+ gAdditionalScrollOffsetY = -gMurphyScrollOffsetY;
+ }
+
+ // loc_491E8: ; CODE XREF: updateUserInputInScrollMovementMode+99j
+ if (gIsDelKeyPressed != 0)
+ {
+ gAdditionalScrollOffsetX = -gMurphyScrollOffsetX;
+ gAdditionalScrollOffsetY = kLevelBitmapHeight - gMurphyScrollOffsetY;
+ }
+
+ // loc_491F6: ; CODE XREF: updateUserInputInScrollMovementMode+A3j
+ if (gIsEndKeyPressed != 0)
+ {
+ gAdditionalScrollOffsetX = (kLevelBitmapWidth / 2) - gMurphyScrollOffsetX;
+ gAdditionalScrollOffsetY = kLevelBitmapHeight - gMurphyScrollOffsetY;
+ }
+
+ // loc_49208: ; CODE XREF: updateUserInputInScrollMovementMode+B5j
+ if (gIsAvPagKeyPressed != 0)
+ {
+ gAdditionalScrollOffsetX = kLevelBitmapWidth - gMurphyScrollOffsetX;
+ gAdditionalScrollOffsetY = kLevelBitmapHeight - gMurphyScrollOffsetY;
+ }
+}
+
+void simulateDemoInput() // sub_492A8 proc near ; CODE XREF: handleGameUserInput+27p
+ // ; restartLevel+76p
+{
+ // 01ED:2645
+ if (gDemoCurrentInputRepeatCounter > 1)
+ {
+ gDemoCurrentInputRepeatCounter--;
+ return;
+ }
+
+ // loc_492B3: ; CODE XREF: simulateDemoInput+5j
+ uint8_t newInput = gDemos.demoData[gDemoCurrentInputIndex];
+
+ if (newInput == 0xFF)
+ {
+ gQuitLevelCountdown = 0x64;
+ gShouldExitLevel = 1;
+ }
+ else
+ {
+ gDemoCurrentInputIndex++;
+ }
+
+ // loc_492CA: ; CODE XREF: simulateDemoInput+47j
+ gCurrentUserInput = newInput & 0xF;
+ gDemoCurrentInputRepeatCounter = (newInput >> 4) + 1;
+}
+
+void saveInputForDemo() // sub_492F1 proc near ; CODE XREF: handleGameUserInput+1Dp
+{
+ gDemoCurrentInputRepeatCounter++;
+
+ if (gDemoCurrentInputRepeatCounter == 0xFF)
+ {
+ gDemoCurrentInput = gCurrentUserInput;
+ gDemoRecordingRandomGeneratorSeed = gRandomGeneratorSeed;
+ gDemoRecordingRandomGeneratorSeedLow = (gDemoRecordingRandomGeneratorSeed >> 8); // ah;
+ gDemoRecordingRandomGeneratorSeedHigh = (gDemoRecordingRandomGeneratorSeed & 0xFF); // al;
+ }
+
+ // loc_49311: ; CODE XREF: saveInputForDemo+Dj
+ if (gDemoCurrentInput == gCurrentUserInput && gDemoCurrentInputRepeatCounter != 0xF)
+ {
+ return;
+ }
+
+ // loc_4931E: ; CODE XREF: saveInputForDemo+24j
+ gDemoCurrentInput = (gDemoCurrentInput | (gDemoCurrentInputRepeatCounter << 4));
+
+ gDemoRecordingRandomGeneratorSeedHigh += gDemoCurrentInputRepeatCounter;
+ gDemoRecordingRandomGeneratorSeedHigh++;
+
+ fileWriteUInt8(gDemoCurrentInput, gCurrentRecordingDemoFile);
+ gDemoCurrentInputRepeatCounter = 0xFF;
+ gDemoCurrentInput = gCurrentUserInput;
+}
+
+void stopRecordingDemo() // somethingspsig proc near ; CODE XREF: runLevel+355p
+ // ; recordDemo+30p ...
+{
+ uint8_t scrambleSpeedLow = (gDemoRecordingLowestSpeed ^ gDemoRecordingRandomGeneratorSeedLow);
+ uint8_t scrambleSpeedHigh = (gDemoRecordingLowestSpeed ^ gDemoRecordingRandomGeneratorSeedHigh);
+ uint16_t scrambleSpeed = ((scrambleSpeedHigh << 8) | scrambleSpeedLow);
+
+ fseek(gCurrentRecordingDemoFile, 1532, SEEK_SET);
+ fileWriteUInt16(scrambleSpeed, gCurrentRecordingDemoFile);
+
+ fileWriteUInt16(gDemoRecordingRandomGeneratorSeed, gCurrentRecordingDemoFile);
+
+ fseek(gCurrentRecordingDemoFile, 0, SEEK_END);
+
+ gDemoCurrentInput = 0xFF;
+
+ fileWriteUInt8(gDemoCurrentInput, gCurrentRecordingDemoFile);
+ if (byte_5A19B != 0)
+ {
+ FILE *sigFile = openWritableFileWithReadonlyFallback("MYSPSIG.TXT", "rb");
+ if (sigFile != NULL)
+ {
+ if (fseek(sigFile, 0, SEEK_END) == 0)
+ {
+ long sigFileSize = ftell(sigFile);
+ sigFileSize = MIN(sigFileSize, kMaxDemoSignatureLength);
+
+ if (sigFileSize > 0)
+ {
+ // loc_493EB: ; CODE XREF: stopRecordingDemo+85j
+ // ; stopRecordingDemo+8Dj
+ if (fseek(sigFile, 0, SEEK_SET) == 0)
+ {
+ uint8_t signature[kMaxDemoSignatureLength + 1];
+ size_t bytes = fileReadBytes(signature, sigFileSize, sigFile);
+
+ if (bytes == sigFileSize)
+ {
+ int idx = 0;
+ for (idx = 0; idx < sigFileSize; ++idx)
+ {
+ if (signature[idx] == 0xFF)
+ {
+ break;
+ }
+ }
+
+ // Make sure the signature is terminated with 0xFF
+ signature[idx] = 0xFF;
+ sigFileSize = idx;
+
+ // loc_4941C: ; CODE XREF: stopRecordingDemo+BCj
+ fileWriteBytes(signature, sigFileSize + 1, gCurrentRecordingDemoFile);
+ }
+ }
+ }
+ }
+
+ // loc_49430: ; CODE XREF: stopRecordingDemo+7Ej
+ // ; stopRecordingDemo+89j ...
+ fclose(sigFile);
+ }
+ }
+ // loc_49435: ; CODE XREF: stopRecordingDemo+65j
+ // ; stopRecordingDemo+6Fj
+ fclose(gCurrentRecordingDemoFile);
+ gIsRecordingDemo = 0;
+ if (gHasUserInterruptedDemo != 0)
+ {
+ gIsPlayingDemo = 1;
+ }
+
+ // loc_4944F: ; CODE XREF: stopRecordingDemo+EEj
+ drawGamePanelText();
+ gIsGameBusy = 1;
+ gIsPlayingDemo = 0;
+}
+
+size_t writeCurrentLevelToFile(FILE *file)
+{
+ size_t bytes = fileWriteBytes(gCurrentLevel.tiles, sizeof(gCurrentLevel.tiles), file);
+
+ bytes += fileWriteBytes(gCurrentLevel.unused, sizeof(gCurrentLevel.unused), file);
+ bytes += fileWriteUInt8(gCurrentLevel.initialGravitation, file);
+ bytes += fileWriteUInt8(gCurrentLevel.speedFixMagicNumber, file);
+ bytes += fileWriteBytes(gCurrentLevel.name, sizeof(gCurrentLevel.name), file);
+ bytes += fileWriteUInt8(gCurrentLevel.freezeZonks, file);
+ bytes += fileWriteUInt8(gCurrentLevel.numberOfInfotrons, file);
+ bytes += fileWriteUInt8(gCurrentLevel.numberOfSpecialPorts, file);
+ for (int idx = 0; idx < kLevelMaxNumberOfSpecialPorts; ++idx)
+ {
+ bytes += fileWriteUInt16(gCurrentLevel.specialPortsInfo[idx].position, file);
+ bytes += fileWriteUInt8(gCurrentLevel.specialPortsInfo[idx].gravity, file);
+ bytes += fileWriteUInt8(gCurrentLevel.specialPortsInfo[idx].freezeZonks, file);
+ bytes += fileWriteUInt8(gCurrentLevel.specialPortsInfo[idx].freezeEnemies, file);
+ bytes += fileWriteUInt8(gCurrentLevel.specialPortsInfo[idx].unused, file);
+ }
+ bytes += fileWriteUInt8(gCurrentLevel.scrambledSpeed, file);
+ bytes += fileWriteUInt8(gCurrentLevel.scrambledChecksum, file);
+ bytes += fileWriteUInt16(gCurrentLevel.randomSeed, file);
+
+ return bytes;
+}
+
+void recordDemo(uint16_t demoIndex) // sub_4945D proc near ; CODE XREF: handleGameUserInput+294p
+ // ; handleGameUserInput+2A4p ...
+{
+ // 01ED:27FA
+ gIsMoveScrollModeEnabled = 0;
+ gAdditionalScrollOffsetX = 0;
+ gAdditionalScrollOffsetY = 0;
+ gIsFlashingBackgroundModeEnabled = 0;
+ gDebugExtraRenderDelay = 1;
+
+ replaceCurrentPaletteColor(0, (Color){0, 0, 0});
+
+ if (gIsRecordingDemo != 0)
+ {
+ stopRecordingDemo();
+ }
+
+ // loc_49490: ; CODE XREF: recordDemo+2Ej
+ char demoIndexCharacter = '0' + demoIndex;
+ gDemo0BinFilename[4] = demoIndexCharacter;
+
+ char *filename = gDemo0BinFilename;
+
+ if (supportsSPFileDemoPlayback() && (gShouldRecordWithOriginalDemoFilenames & 0xFF) == 0) // cmp byte ptr gShouldRecordWithOriginalDemoFilenames, 0
+ {
+ gSPDemoFileName[7] = demoIndexCharacter;
+ filename = gSPDemoFileName;
+ }
+
+ // loc_494A6: ; CODE XREF: recordDemo+41j
+ gRecordingDemoMessage[18] = demoIndexCharacter;
+
+ FILE *file = openWritableFile(filename, "wb");
+ if (file == NULL)
+ {
+ return;
+ }
+
+ // loc_494B8: ; CODE XREF: recordDemo+56j
+ gCurrentRecordingDemoFile = file; // file handle
+ gCurrentLevel.speedFixMagicNumber = 0x20 + kGameVersion;
+ // TODO: don't know for sure but this probably is related to adjusting the demo time with the speed or something?
+ // bl = speed3;
+ // cl = 4;
+ // bl = bl << cl;
+ // bl |= gGameSpeed;
+ // speed2 = bl;
+ gDemoRecordingLowestSpeed = gGameSpeed;
+
+ size_t bytes = writeCurrentLevelToFile(file);
+ if (bytes != kLevelDataLength)
+ {
+ return;
+ }
+
+ // The original code sets index | 0x80 in demoCurrentInputRepeatCounter and then writes it to the file
+ // but seems to be useless because that value is overriden later.
+ //
+ uint8_t levelNumber = gCurrentSelectedLevelIndex | 0x80;
+ bytes = fileWriteUInt8(levelNumber, file);
+ if (bytes < 1)
+ {
+ return;
+ }
+ gDemoCurrentInput = UserInputNone;
+ gDemoRecordingJustStarted = 1;
+ gIsPlayingDemo = 0;
+ gDemoCurrentInputRepeatCounter = 0xFE; // 254
+ gDebugExtraRenderDelay = 1;
+ if (gIsSPDemoAvailableToRun == 0)
+ {
+ memcpy(gCurrentDemoLevelName, gCurrentLevelName, 3);
+ }
+
+ // loc_4952A: ; CODE XREF: recordDemo+BCj
+ gIsRecordingDemo = 1;
+ if (gHasUserInterruptedDemo != 0)
+ {
+ gIsPlayingDemo = 1;
+ }
+
+ // loc_4953B: ; CODE XREF: recordDemo+D7j
+ fetchAndInitializeLevel();
+ gIsPlayingDemo = 0;
+}
+
+void prepareDemoRecordingFilename() // sub_49544 proc near ; CODE XREF: start+3A1p
+ // ; handleOkButtonClick:loc_4B40Fp ...
+{
+ // 01ED:28E1
+
+ char currentSuffix[3] = "AT";
+ strcpy(currentSuffix, &gLevelsDatFilename[8]);
+
+ // Checks if the last two chars are "00" like LEVELS.D00?
+ if (strcmp(currentSuffix, "00") == 0)
+ {
+ // replaces the content with "--"
+ strcpy(currentSuffix, "--");
+ }
+
+ // loc_4954F: // ; CODE XREF: prepareDemoRecordingFilename+6j
+ // Now checks if the last two chars are "AT" like LEVELS.DAT?
+ if (strcmp(currentSuffix, "AT") == 0)
+ {
+ // replaces the content with "00"
+ strcpy(currentSuffix, "00");
+ }
+
+ // loc_49557: // ; CODE XREF: prepareDemoRecordingFilename+Ej
+ memcpy(gSPDemoFileName, currentSuffix, 2);
+}
+
+void handleGameUserInput() // sub_4955B proc near ; CODE XREF: runLevel:loc_48B6Bp
+ // ; runLevel+30Cp
+{
+ // 01ED:28F8
+
+ if (gIsMoveScrollModeEnabled != 0)
+ {
+ updateUserInputInScrollMovementMode(); // 01ED:28FF
+ }
+ // loc_49567: ; CODE XREF: handleGameUserInput+5j
+ else if (gIsPlayingDemo == 0)
+ {
+ updateUserInput(); // 01ED:290B
+ if (gIsRecordingDemo != 0)
+ {
+ saveInputForDemo(); // 01ED:2915
+ }
+ }
+
+ // loc_4957B: ; CODE XREF: handleGameUserInput+Aj
+ // ; handleGameUserInput+11j ...
+ if (gIsPlayingDemo != 0)
+ {
+ simulateDemoInput(); // 01ED:2929
+ }
+
+ // loc_4958F: ; CODE XREF: handleGameUserInput+2Fj
+ if (gToggleGamePanelKeyAutoRepeatCounter != 0) // cmp byte ptr word_510C1+1, 0
+ {
+ // 01ED:293E
+ gToggleGamePanelKeyAutoRepeatCounter--;
+ }
+
+ // loc_4959A: ; CODE XREF: handleGameUserInput+39j
+ if (isToggleGamePanelButtonPressed() == 0)
+ {
+ gToggleGamePanelKeyAutoRepeatCounter = 0; // mov byte ptr word_510C1+1, 0
+ }
+ // loc_495A9: ; CODE XREF: handleGameUserInput+44j
+ else if (gToggleGamePanelKeyAutoRepeatCounter == 0) // 01ED:2946
+ {
+ // loc_495B3: ; CODE XREF: handleGameUserInput+53j
+ gToggleGamePanelKeyAutoRepeatCounter = 0x20; // mov byte ptr word_510C1+1, 20h ; ' '
+ if (gShouldShowGamePanel != 0)
+ {
+ gShouldShowGamePanel = 0; // mov byte ptr word_510C1, 0
+ gCurrentPanelHeight = 0;
+ }
+ else
+ {
+ // loc_495FB: ; CODE XREF: handleGameUserInput+62j
+ gShouldShowGamePanel = 1; // mov byte ptr word_510C1, 1
+ gCurrentPanelHeight = kPanelBitmapHeight;
+ }
+ }
+
+ // loc_49635: ; CODE XREF: handleGameUserInput+4Bj
+ // ; handleGameUserInput+55j ...
+ if (gIsDebugModeEnabled != 1)
+ {
+ checkDebugKeys();
+ return;
+ }
+
+ // loc_4963F: ; CODE XREF: handleGameUserInput+DFj
+ if (gIsRecordingDemo == 0) // 01ED:29DC
+ {
+ // loc_49649: ; CODE XREF: handleGameUserInput+E9j
+ if (gIsMKeyPressed != 0) // cmp byte ptr gIsMKeyPressed, 0
+ {
+ gIsMoveScrollModeEnabled = 1;
+ }
+
+ // loc_49656: ; CODE XREF: handleGameUserInput+F3j
+ if (gIsDKeyPressed != 0)
+ {
+ gIsFlashingBackgroundModeEnabled = 1;
+ }
+
+ // loc_49663: ; CODE XREF: handleGameUserInput+100j
+ if (gIsZKeyPressed != 0) // cmp byte ptr gIsZKeyPressed, 0
+ {
+ // 01ED:2A07
+ removeTiles(LevelTileTypeZonk);
+ }
+
+ // loc_4966F: ; CODE XREF: handleGameUserInput+10Dj
+ if (gIsBKeyPressed != 0) // cmp byte ptr gIsBKeyPressed, 0
+ {
+ removeTiles(LevelTileTypeBase);
+ }
+
+ // loc_4967B: ; CODE XREF: handleGameUserInput+119j
+ if (gIsHKeyPressed != 0)
+ {
+ removeTiles(LevelTileTypeHardware);
+ }
+
+ // loc_49687: ; CODE XREF: handleGameUserInput+125j
+ if (gIsCKeyPressed != 0) // cmp byte ptr gIsCKeyPressed, 0
+ {
+ removeTiles(LevelTileTypeChip);
+ }
+
+ // loc_49693: ; CODE XREF: handleGameUserInput+131j
+ if (gIsSKeyPressed != 0)
+ {
+ removeTiles(LevelTileTypeSnikSnak);
+ }
+
+ // loc_4969F: ; CODE XREF: handleGameUserInput+13Dj
+ if (gIsRKeyPressed != 0)
+ {
+ videoLoop();
+ restartLevel();
+ }
+
+ // loc_496AC: ; CODE XREF: handleGameUserInput+149j
+ if (gIsPlayingDemo == 0)
+ {
+ // loc_496C0: ; CODE XREF: handleGameUserInput+160j
+ if (gIs1KeyPressed != 0)
+ {
+ gDebugExtraRenderDelay = 1;
+ }
+
+ // loc_496CD: ; CODE XREF: handleGameUserInput+16Aj
+ if (gIs2KeyPressed != 0)
+ {
+ gDebugExtraRenderDelay = 2;
+ }
+
+ // loc_496DA: ; CODE XREF: handleGameUserInput+177j
+ if (gIs3KeyPressed != 0)
+ {
+ gDebugExtraRenderDelay = 3;
+ }
+
+ // loc_496E7: ; CODE XREF: handleGameUserInput+184j
+ if (gIs4KeyPressed != 0)
+ {
+ gDebugExtraRenderDelay = 4;
+ }
+
+ // loc_496F4: ; CODE XREF: handleGameUserInput+191j
+ if (gIs5KeyPressed != 0)
+ {
+ gDebugExtraRenderDelay = 6;
+ }
+
+ // loc_49701: ; CODE XREF: handleGameUserInput+19Ej
+ if (gIs6KeyPressed != 0)
+ {
+ gDebugExtraRenderDelay = 8;
+ }
+
+ // loc_4970E: ; CODE XREF: handleGameUserInput+1ABj
+ if (gIs7KeyPressed != 0)
+ {
+ gDebugExtraRenderDelay = 0xC;
+ }
+
+ // loc_4971B: ; CODE XREF: handleGameUserInput+1B8j
+ if (gIs8KeyPressed != 0)
+ {
+ gDebugExtraRenderDelay = 0x10;
+ }
+
+ // loc_49728: ; CODE XREF: handleGameUserInput+1C5j
+ if (gIs9KeyPressed != 0)
+ {
+ gDebugExtraRenderDelay = 0x18;
+ }
+
+ // loc_49735: ; CODE XREF: handleGameUserInput+1D2j
+ if (gIs0KeyPressed != 0)
+ {
+ gDebugExtraRenderDelay = 0x20;
+ }
+ }
+ }
+
+ // loc_49742: ; CODE XREF: handleGameUserInput+EBj
+ // ; handleGameUserInput+158j ...
+ // 01ED:2ADF
+ if (gIsLeftControlPressed == 1)
+ {
+ // loc_497D1: ; CODE XREF: handleGameUserInput+1EEj
+ if (gIsPlayingDemo != 0)
+ {
+ loc_4988E();
+ return;
+ }
+ // loc_497DB: ; CODE XREF: handleGameUserInput+27Bj
+ // TODO: this if is only relevant when the game is autoadjusting the speed (speed3 != 0), right?
+ // else if (speed3 < 0)
+ // {
+ // loc_4988E();
+ // return;
+ // }
+ // loc_497E5: ; CODE XREF: handleGameUserInput+285j
+ else if (gIsF1KeyPressed == 1)
+ {
+ // 01ED:2B89
+ recordDemo(0);
+ }
+ // loc_497F5: ; CODE XREF: handleGameUserInput+28Fj
+ else if (gIsF2KeyPressed == 1)
+ {
+ // 01ED:2B99
+ recordDemo(1);
+ }
+ // loc_49805: ; CODE XREF: handleGameUserInput+29Fj
+ else if (gIsF3KeyPressed == 1)
+ {
+ recordDemo(2);
+ }
+ // loc_49814: ; CODE XREF: handleGameUserInput+2AFj
+ else if (gIsF4KeyPressed == 1)
+ {
+ recordDemo(3);
+ }
+ // loc_49823: ; CODE XREF: handleGameUserInput+2BEj
+ else if (gIsF5KeyPressed == 1)
+ {
+ recordDemo(4);
+ }
+ // loc_49832: ; CODE XREF: handleGameUserInput+2CDj
+ else if (gIsF6KeyPressed == 1)
+ {
+ recordDemo(5);
+ }
+ // loc_49841: ; CODE XREF: handleGameUserInput+2DCj
+ else if (gIsF7KeyPressed == 1)
+ {
+ recordDemo(6);
+ }
+ // loc_49850: ; CODE XREF: handleGameUserInput+2EBj
+ else if (gIsF8KeyPressed == 1)
+ {
+ recordDemo(7);
+ }
+ // loc_4985F: ; CODE XREF: handleGameUserInput+2FAj
+ else if (gIsF9KeyPressed == 1)
+ {
+ recordDemo(8);
+ }
+ // loc_4986E: ; CODE XREF: handleGameUserInput+309j
+ else if (gIsF10KeyPressed == 1)
+ {
+ recordDemo(9);
+ }
+ // loc_4987D: ; CODE XREF: handleGameUserInput+318j
+ else if (gIsF12KeyPressed == 1 && gIsRecordingDemo != 0)
+ {
+ stopRecordingDemo();
+ }
+ }
+ else
+ {
+ // loc_4974C: ; CODE XREF: handleGameUserInput+1ECj
+ if ((gFrameCounter & 7) == 0 && gIsRecordingDemo == 0)
+ {
+ // loc_49761: ; CODE XREF: handleGameUserInput+201j
+ if (gIsF1KeyPressed == 0)
+ {
+ gToggleGravityAutorepeatFlag = 0;
+ }
+ // loc_4976F: ; CODE XREF: handleGameUserInput+20Bj
+ else if (gToggleGravityAutorepeatFlag == 0)
+ {
+ gToggleGravityAutorepeatFlag--;
+ gIsGravityEnabled &= 1;
+ gIsGravityEnabled = gIsGravityEnabled ^ 1;
+ }
+
+ if (gIsF1KeyPressed == 0 || gToggleGravityAutorepeatFlag != 0)
+ {
+ // loc_49786: ; CODE XREF: handleGameUserInput+212j
+ // ; handleGameUserInput+219j
+ if (gIsF2KeyPressed == 0)
+ {
+ gToggleZonksFrozenAutorepeatFlag = 0;
+ }
+ // loc_49794: ; CODE XREF: handleGameUserInput+230j
+ else if (gToggleZonksFrozenAutorepeatFlag == 0)
+ {
+ gToggleZonksFrozenAutorepeatFlag--;
+ gAreZonksFrozen &= 2;
+ gAreZonksFrozen = gAreZonksFrozen ^ 2;
+ }
+
+ if (gIsF2KeyPressed == 0 || gToggleZonksFrozenAutorepeatFlag != 0)
+ {
+ // loc_497AB: ; CODE XREF: handleGameUserInput+237j
+ // ; handleGameUserInput+23Ej
+ if (gIsF3KeyPressed == 0)
+ {
+ gToggleEnemiesFrozenAutorepeatFlag = 0;
+ }
+ // loc_497B9: ; CODE XREF: handleGameUserInput+255j
+ else if (gToggleEnemiesFrozenAutorepeatFlag == 0)
+ {
+ gToggleEnemiesFrozenAutorepeatFlag--;
+ gAreEnemiesFrozen &= 1;
+ gAreEnemiesFrozen = gAreEnemiesFrozen ^ 1;
+ }
+ }
+ }
+ }
+ }
+
+ loc_4988E();
+}
+
+void loc_4988E() // : ; CODE XREF: handleGameUserInput+1F9j
+{
+ // ; handleGameUserInput+203j ...
+ if (gIsRecordingDemo != 0 || gIsPlayingDemo != 0)
+ {
+ checkDebugKeys();
+ return;
+ }
+
+ // loc_498A2: ; CODE XREF: handleGameUserInput+342j
+ if (gIsMinusKeyPressed == 0)
+ {
+ gDebugSkipPreviousLevelAutorepeatFlag_1 = 0;
+ gDebugSkipPreviousLevelAutorepeatFlag_2 = 5;
+ }
+ // loc_498B5: ; CODE XREF: handleGameUserInput+34Cj
+ else if (gDebugSkipPreviousLevelAutorepeatFlag_1 != 0)
+ {
+ gDebugSkipPreviousLevelAutorepeatFlag_1--;
+ }
+ else
+ {
+ // loc_498C2: ; CODE XREF: handleGameUserInput+35Fj
+ if (gDebugSkipPreviousLevelAutorepeatFlag_2 != 0)
+ {
+ gDebugSkipPreviousLevelAutorepeatFlag_2--;
+ gDebugSkipPreviousLevelAutorepeatFlag_1 = 0x10;
+ }
+
+ // loc_498D2: ; CODE XREF: handleGameUserInput+36Cj
+ if (gCurrentSelectedLevelIndex <= 1)
+ {
+ gCurrentSelectedLevelIndex = 2;
+ }
+
+ // loc_498DF: ; CODE XREF: handleGameUserInput+37Cj
+ gCurrentSelectedLevelIndex--;
+ if (gCurrentSelectedLevelIndex > kNumberOfLevels)
+ {
+ gCurrentSelectedLevelIndex = kNumberOfLevels;
+ }
+
+ // loc_498F0: ; CODE XREF: handleGameUserInput+38Dj
+ // ax = gCurrentSelectedLevelIndex;
+ convertLevelNumberTo3DigitStringWithPadding0(gCurrentSelectedLevelIndex);
+ drawLevelList();
+ debugSkipLevel();
+ }
+
+ // loc_498FC: ; CODE XREF: handleGameUserInput+358j
+ // ; handleGameUserInput+365j
+ if (gIsEqualsKeyPressed == 0)
+ {
+ gDebugSkipNextLevelAutorepeatFlag_1 = 0;
+ gDebugSkipNextLevelAutorepeatFlag_2 = 5;
+ }
+ // loc_4990F: ; CODE XREF: handleGameUserInput+3A6j
+ else if (gDebugSkipNextLevelAutorepeatFlag_1 != 0)
+ {
+ gDebugSkipNextLevelAutorepeatFlag_1--;
+ }
+ else
+ {
+ // loc_4991C: ; CODE XREF: handleGameUserInput+3B9j
+ if (gDebugSkipNextLevelAutorepeatFlag_2 != 0)
+ {
+ gDebugSkipNextLevelAutorepeatFlag_2--;
+ gDebugSkipNextLevelAutorepeatFlag_1 = 0x10; // 16
+ }
+
+ // loc_4992C: ; CODE XREF: handleGameUserInput+3C6j
+ if (gCurrentSelectedLevelIndex >= kNumberOfLevels)
+ {
+ gCurrentSelectedLevelIndex = kNumberOfLevels - 1;
+ }
+
+ // loc_49939: ; CODE XREF: handleGameUserInput+3D6j
+ gCurrentSelectedLevelIndex++;
+ convertLevelNumberTo3DigitStringWithPadding0(gCurrentSelectedLevelIndex); // 01ED:2CDD
+ drawLevelList();
+ debugSkipLevel();
+ }
+
+ checkDebugKeys();
+}
+
+void stopDemoAndPlay()
+{
+ gIsPlayingDemo = 0;
+ gShouldUpdateTotalLevelTime = 0;
+ gHasUserCheated = 1;
+ gHasUserInterruptedDemo = 1;
+}
+
+void checkDebugKeys() // loc_49949: ; CODE XREF: handleGameUserInput+E1j
+// ; handleGameUserInput+33Aj ...
+{
+ // 01ED:2CE6
+
+ uint8_t shouldStartFromSavedSnapshot = (gShouldStartFromSavedSnapshot != 0);
+ gShouldStartFromSavedSnapshot = 0;
+ if (shouldStartFromSavedSnapshot)
+ {
+ loadGameSnapshot();
+ return;
+ }
+
+ // loc_49958: ; CODE XREF: handleGameUserInput+3F8j
+ if (gIsLeftControlPressed != 1)
+ {
+ loc_49C41();
+ return;
+ }
+
+ // loc_49962: ; CODE XREF: handleGameUserInput+402j
+ // Control + F12: Interrupt demo and continue playing
+ if (gIsF12KeyPressed == 1 && gIsPlayingDemo != 0)
+ {
+ stopDemoAndPlay();
+ }
+
+ // loc_49984: ; CODE XREF: handleGameUserInput+40Cj
+ // ; handleGameUserInput+413j
+ if (gIsScrollLockPressed == 1)
+ {
+ gIsDebugModeEnabled = 1;
+ drawTextWithChars8FontToGamePanel(304, 14, 6, "DB"); // Debug mode enabled
+ gAdditionalInfoInGamePanelFrameCounter = 0x46; // 70
+ }
+
+ // loc_499AA: ; CODE XREF: handleGameUserInput+42Ej
+ // ; handleGameUserInput+43Bj
+ if (gIsWKeyPressed != 1)
+ {
+ // loc_49A7F: ; CODE XREF: handleGameUserInput+456j
+ if (gIsLKeyPressed != 1)
+ {
+ loc_49C41();
+ return;
+ }
+
+ loadGameSnapshot();
+ return;
+ }
+
+ saveGameSnapshot();
+}
+
+void saveGameSnapshot() // loc_499C8: ; CODE XREF: handleGameUserInput+454j
+{
+ gShouldCloseAdvancedMenu = 1;
+
+ if (saveGameState() != 0)
+ {
+ showSavegameOperationError();
+ return;
+ }
+
+ // loc_49A78: ; CODE XREF: handleGameUserInput+518j
+ loc_49C2C("WR"); // Means snapshot saved with no issues
+}
+
+void loadGameSnapshot() // loc_49A89: ; CODE XREF: handleGameUserInput+3FAj
+{
+ gShouldCloseAdvancedMenu = 1;
+
+ if (canLoadGameState() == 0)
+ {
+ showSavegameOperationError();
+ return;
+ }
+
+ // loc_49A96: ; CODE XREF: handleGameUserInput+536j
+ if (gIsRecordingDemo != 0)
+ {
+ stopRecordingDemo();
+ }
+
+ gIsRecordingDemo = 0;
+
+ if (loadGameState() != 0)
+ {
+ showSavegameOperationError();
+ return;
+ }
+
+ forceRestoreOriginalFancyTiles();
+
+ // loc_49B84: ; CODE XREF: handleGameUserInput+619j
+ // ; handleGameUserInput+624j
+ gIsPlayingDemo = 0;
+ gIsRecordingDemo = 0;
+ gCurrentUserInput = UserInputNone;
+ gIsMoveScrollModeEnabled = 0;
+ gAdditionalScrollOffsetX = 0;
+ gAdditionalScrollOffsetY = 0;
+ gIsFlashingBackgroundModeEnabled = 0;
+ gDebugExtraRenderDelay = 1;
+ replaceCurrentPaletteColor(0, (Color){0, 0, 0});
+ generateRandomSeedFromClock();
+ generateRandomNumber();
+ // I commented out all these video transitions because they're not needed in the reimplementation. They were here
+ // just to prevent graphical glitches. Also they made loading much slower.
+ //
+ // setPalette(gBlackPalette);
+ // videoLoop();
+ drawFixedLevel();
+ drawGamePanel();
+ convertToEasyTiles();
+ scrollToMurphy();
+ gLastDrawnMinutesAndSeconds = 0xFFFF;
+ gLastDrawnHours = 0xFF;
+ drawGameTime();
+ gHasUserCheated = 1;
+ gIsRecordingDemo = 0;
+
+ drawTextWithChars8FontToGamePanel(304, 14, 6, "LD"); // Means snapshot was loaded successfully
+ gAdditionalInfoInGamePanelFrameCounter = 0x46; // 70 or '&'
+
+ drawCurrentLevelViewport(gCurrentPanelHeight);
+ // videoLoop();
+
+ // loc_49C12: ; CODE XREF: handleGameUserInput+6A8j
+ // fadeToPalette(gGamePalette);
+
+ // loc_49C40: ; CODE XREF: handleGameUserInput+6BDj
+ // ; handleGameUserInput+6D6j
+ loc_49C41();
+}
+
+void showSavegameOperationError() // loc_49C28: ; CODE XREF: handleGameUserInput+47Aj
+{
+ // ; handleGameUserInput+51Aj ...
+ // push si
+ // mov si, 0A007h "XX"
+ loc_49C2C("XX"); // Means problem writing/loading snapshot
+}
+
+void loc_49C2C(char text[3]) // : ; CODE XREF: handleGameUserInput+521j
+{
+ drawTextWithChars8FontToGamePanel(304, 14, 6, text);
+ gAdditionalInfoInGamePanelFrameCounter = 0x46; // 70 or '&'
+
+ // loc_49C40: ; CODE XREF: handleGameUserInput+6BDj
+ // ; handleGameUserInput+6D6j
+ // pop si
+
+ loc_49C41();
+}
+
+void loc_49C41() // ; CODE XREF: handleGameUserInput+404j
+// ; handleGameUserInput+52Bj
+{
+ if (gIsLeftAltPressed == 1 && gIsScrollLockPressed == 1)
+ {
+ // 01ED:2FEC
+ gIsDebugModeEnabled = 0;
+ gIsMoveScrollModeEnabled = 0;
+ gAdditionalScrollOffsetX = 0;
+ gAdditionalScrollOffsetY = 0;
+ gIsFlashingBackgroundModeEnabled = 0;
+ gDebugExtraRenderDelay = 1;
+ replaceCurrentPaletteColor(0, (Color){0, 0, 0});
+
+ drawTextWithChars8FontToGamePanel(304, 14, 6, "--"); // Debug mode disabled
+ gAdditionalInfoInGamePanelFrameCounter = 0x46; // 70 or '&'
+ }
+
+ // loc_49C96: ; CODE XREF: handleGameUserInput+6EBj
+ // ; handleGameUserInput+6F2j ...
+ if (isPauseButtonPressed())
+ {
+ // 01ED:303A
+ gIsGameRunning = 0;
+ runAdvancedOptionsRootMenu();
+ gIsGameRunning = 1;
+ }
+
+ // loc_49CC8: ; CODE XREF: handleGameUserInput+740j
+ if (gIsNumLockPressed != 0)
+ {
+ // 01ED:306C
+ gIsGameRunning = 0;
+ // mov si, 6095h
+ fadeToPalette(gGameDimmedPalette);
+
+ do
+ {
+ // loc_49CDA: ; CODE XREF: handleGameUserInput+784j
+ int9handler(1);
+ } while (gIsNumLockPressed == 1);
+
+ // From the speed fix mod, but in uppercase so I can use characterForLastKeyPressed
+ static const char kMagicDisableDebugModeCode[] = "CANT STO";
+ uint8_t index = 0;
+
+ do
+ {
+ // loc_49CE4: ; CODE XREF: handleGameUserInput+7A6j
+ int9handler(1);
+
+ if (index >= strlen(kMagicDisableDebugModeCode))
+ {
+ gIsDebugModeEnabled = 0;
+ break;
+ }
+ else
+ {
+ // loc_49CF3: ; CODE XREF: handleGameUserInput+78Ej
+ if (characterForLastKeyPressed() == kMagicDisableDebugModeCode[index])
+ {
+ index++;
+ }
+
+ // loc_49CFC: ; CODE XREF: handleGameUserInput+79Dj
+ if (gIsNumLockPressed != 0)
+ {
+ break;
+ }
+ }
+ } while (1);
+
+ do
+ {
+ // loc_49D03: ; CODE XREF: handleGameUserInput+796j
+ // ; handleGameUserInput+7ADj
+ int9handler(1);
+ } while (gIsNumLockPressed == 1);
+ // mov si, 6015h
+ fadeToPalette(gGamePalette);
+ gIsGameRunning = 1;
+ }
+
+ // loc_49D15: ; CODE XREF: handleGameUserInput+772j
+ if (isExitLevelButtonPressed() // Select/Back/- controller button -> exit game
+ && gQuitLevelCountdown <= 0)
+ {
+ // This is called when I press ESC to exit the game, but not when I die
+ gShouldKillMurphy = 1; // 01ED:30C0
+ }
+
+ // loc_49D29: ; CODE XREF: handleGameUserInput+7BFj
+ // ; handleGameUserInput+7C6j
+ if (gIsQKeyPressed != 0 || getGameControllerCancelButton())
+ {
+ // 01ED:30CD
+ gIsMoveScrollModeEnabled = 0;
+ gAdditionalScrollOffsetX = 0;
+ gAdditionalScrollOffsetY = 0;
+ gIsFlashingBackgroundModeEnabled = 0;
+ }
+
+ // loc_49D48: ; CODE XREF: handleGameUserInput+7D3j
+ if (isShowNumberOfRedDisksButtonPressed())
+ {
+ drawNumberOfRemainingRedDisks(); // 01ED:30EC
+ }
+}
+
+void forceRestoreOriginalFancyTiles() // sub_49D53 proc near ; CODE XREF: handleGameUserInput+626p
+ // ; removeTiles+21p
+{
+ // 01ED:30F0
+ gIsShowingFancyTiles = 0;
+ restoreOriginalFancyTiles();
+}
+
+void restoreOriginalFancyTiles() // proc near ; CODE XREF: runLevel+A7p
+{
+ // 01ED:30F5
+
+ for (int i = 0; i < kLevelSize; ++i)
+ {
+ // loc_49D65: ; CODE XREF: restoreOriginalFancyTiles+18j
+ StatefulLevelTile *tile = &gCurrentLevelState[i];
+ if (tile->tile == LevelTileTypeExplosion) // 31
+ {
+ tile->tile = 0xF1; // 241
+ }
+ }
+
+ uint8_t was_gIsShowingFancyTiles_NonZero = (gIsShowingFancyTiles != 0);
+ gIsShowingFancyTiles = 0;
+ if (was_gIsShowingFancyTiles_NonZero)
+ {
+ return;
+ }
+
+ for (int i = 0; i < kLevelSize; ++i)
+ {
+ // loc_49D84: ; CODE XREF: levelScanThing+4Cj
+ StatefulLevelTile *tile = &gCurrentLevelState[i];
+ if (tile->state != 0 || tile->tile != LevelTileTypeHardware)
+ {
+ continue;
+ }
+
+ LevelTileType originalTile = gCurrentLevel.tiles[i];
+
+ if (originalTile >= LevelTileTypeHardware2 // 28
+ && originalTile <= LevelTileTypeHardware11) // 37
+ {
+ tile->tile = originalTile;
+ tile->state = 0;
+ }
+ }
+
+ // loc_49DA6: ; CODE XREF: levelScanThing+31j
+ for (int i = 0; i < kLevelSize; ++i)
+ {
+ // loc_49DAC: ; CODE XREF: levelScanThing+7Fj
+ StatefulLevelTile *tile = &gCurrentLevelState[i];
+ if (tile->state != 0 || tile->tile != LevelTileTypeChip)
+ {
+ continue;
+ }
+
+ LevelTileType originalTile = gCurrentLevel.tiles[i];
+
+ if (originalTile >= LevelTileTypeHorizontalChipLeft // 26
+ && originalTile <= LevelTileTypeHorizontalChipBottom) // 39
+ {
+ originalTile -= LevelTileTypeHardware2; // 28
+ if (originalTile >= LevelTileTypePortDown) // 10
+ {
+ originalTile += LevelTileTypeHardware2; // 28
+ tile->tile = originalTile;
+ tile->state = 0;
+ }
+ }
+ }
+
+ // loc_49DD9: ; CODE XREF: levelScanThing+59j
+ gIsShowingFancyTiles = 1;
+}
+
+void updateMovingObjects() // gameloop proc near ; CODE XREF: runLevel:noFlashingp
+{
+ // 01ED:317D
+
+ gMurphyLocation = updateMurphy(gMurphyLocation); // 01ED:318B
+
+ if (gIsFlashingBackgroundModeEnabled != 0)
+ {
+ replaceCurrentPaletteColor(0, (Color){0x3f, 0x3f, 0x21});
+ }
+
+ // loc_49E14:
+ if (gIsFlashingBackgroundModeEnabled != 0)
+ {
+ replaceCurrentPaletteColor(0, (Color){0x3f, 0x21, 0x21});
+ }
+
+ // loc_49E33:
+ uint16_t numberOfMovingObjects = 0;
+
+ typedef struct
+ {
+ MovingFunction function;
+ uint16_t tilePosition;
+ } MovingObject;
+
+ MovingObject movingObjects[kLevelSize];
+
+ // This loop doesn't iterate through _every tile_. Instead it only goes from the first tile in the gamefield
+ // (located at levelWidth + 1) to the last tile in the gamefield (located at levelSize - levelWidth - 2).
+ // From what I've seen some demos depend on this. Demo 00/01s028-1.SP is an example because there is a
+ // non-hardware tile at the bottom edge that explodes, and a SnikSnak goes in there.
+ //
+ for (uint16_t i = kLevelWidth + 1; i < kLevelSize - kLevelWidth - 1; ++i) // starts from si, ends in si + cx
+ {
+ // checkCellForMovingObject: ; CODE XREF: updateMovingObjects+84j
+ LevelTileType tile = gCurrentLevelState[i].tile; // mov bl, byte ptr leveldata[si]
+
+ // Does this check filter out values except like 0, 2, 16 and 18??
+ if ((tile & LevelTileTypeSportRight) == 0)
+ {
+ continue;
+ }
+
+ if (tile >= 0x20) // 32 because there are 32 moving functions, which means this is about moving objects (scissors, the infotrons, etc.)
+ {
+ continue;
+ }
+
+ MovingFunction function = movingFunctions[tile];
+
+ if (function != NULL)
+ {
+ // There is a list of predefined functions, one per object, and this goes through
+ // every tile, looking for objects with a moving function, and then putting the
+ // tile and the function in a list that will be iterated later to update those objects.
+ //
+ MovingObject *object = &movingObjects[numberOfMovingObjects];
+ object->function = function;
+ object->tilePosition = i;
+ numberOfMovingObjects++; // dx++;
+ }
+ // moveToNextCell:
+ }
+
+ if (numberOfMovingObjects != 0)
+ {
+ // Call all the moving functions
+ for (uint16_t i = 0; i < numberOfMovingObjects; ++i)
+ {
+ // 01ED:3214
+ movingObjects[i].function(movingObjects[i].tilePosition);
+ }
+ }
+
+ // doneWithupdateMovingObjects:
+
+ // 01ED:3227
+ if (gShouldKillMurphy != 1 && gIsMurphyUpdated != 0)
+ {
+ return;
+ }
+
+ // loc_49E99: ; CODE XREF: updateMovingObjects+AFj
+ // ; updateMovingObjects+B6j
+ if (gQuitLevelCountdown == 0) // 01ED:3236
+ {
+ // 01ED:323D
+ gShouldKillMurphy = 0;
+ detonateBigExplosion(gMurphyPreviousLocation);
+ gQuitLevelCountdown = 0x40; // 64
+ }
+
+ return;
+}
+
+void updateScrollOffset() // sub_49EBE proc near ; CODE XREF: runLevel+109p
+ // ; scrollToMurphy+29p
+{
+ // 01ED:325B
+ uint16_t randomNumber = 0;
+
+ // This random number is used to generate the shaking effect on explosions.
+ // The original game generates this random number here for _every_ explosion, even if
+ // normally only Murphy's explosion will make the screen shake. However it's necessary
+ // to do this here to make sure the right sequence of random numbers is generated when
+ // there are explosions in the level.
+ //
+ if (gIsExplosionStarted == 1)
+ {
+ randomNumber = generateRandomNumber();
+ }
+
+ // loc_49ECC: ; CODE XREF: updateScrollOffset+7j
+ int16_t scrollX = gMurphyPositionX;
+ int16_t scrollY = gMurphyPositionY;
+ scrollX -= kScreenWidth / 2; // 152
+ if (scrollX < 0)
+ {
+ scrollX = 0;
+ }
+
+ // loc_49EDF: ; CODE XREF: updateScrollOffset+1Cj
+ uint16_t maxScrollX = kLevelBitmapWidth - kScreenWidth;
+ if (scrollX > maxScrollX) // 624
+ {
+ scrollX = maxScrollX; // 624
+ }
+
+ // loc_49EE8: ; CODE XREF: updateScrollOffset+25j
+ if (gShouldShowGamePanel == 0)
+ {
+ // loc_49EF4: ; CODE XREF: updateScrollOffset+2Fj
+ scrollY -= kScreenHeight / 2;
+ }
+ else
+ {
+ scrollY -= (kScreenHeight - kPanelBitmapHeight) / 2;
+ }
+
+ // loc_49EF7: ; CODE XREF: updateScrollOffset+34j
+ if (scrollY < 0)
+ {
+ scrollY = 0;
+ }
+
+ // loc_49EFE: ; CODE XREF: updateScrollOffset+3Cj
+ uint16_t maxScrollY = 0;
+
+ if (gShouldShowGamePanel == 0)
+ {
+ // loc_49F0F: ; CODE XREF: updateScrollOffset+45j
+ maxScrollY = kLevelBitmapHeight - kScreenHeight;
+ if (scrollY > maxScrollY)
+ {
+ scrollY = maxScrollY;
+ }
+ }
+ else
+ {
+ maxScrollY = kLevelBitmapHeight - kScreenHeight + kPanelBitmapHeight;
+ if (scrollY > maxScrollY)
+ {
+ scrollY = maxScrollY;
+ }
+ }
+
+ // loc_49F17: ; CODE XREF: updateScrollOffset:loc_49F0Dj
+ // ; updateScrollOffset+54j
+ if (gIsMoveScrollModeEnabled == 0 || gIsNumpad5Pressed != 0)
+ {
+ // loc_49F25: ; CODE XREF: updateScrollOffset+5Ej
+ gMurphyScrollOffsetX = scrollX;
+ gMurphyScrollOffsetY = scrollY;
+ }
+ else
+ {
+ // loc_49F2E: ; CODE XREF: updateScrollOffset+65j
+ scrollX = gMurphyScrollOffsetX;
+ scrollY = gMurphyScrollOffsetY;
+
+ int16_t additionalScrollX = scrollX;
+ scrollX += gAdditionalScrollOffsetX;
+ if (scrollX < 0)
+ {
+ // 01ED:32DD
+ scrollX = 0;
+ }
+ else
+ {
+ // loc_49F56: ; CODE XREF: updateScrollOffset+80j
+ if (scrollX > maxScrollX)
+ {
+ scrollX = maxScrollX;
+ }
+ }
+
+ // loc_49F66: ; CODE XREF: updateScrollOffset+8Cj
+ // ; updateScrollOffset+96j
+ additionalScrollX -= scrollX;
+ additionalScrollX = -additionalScrollX;
+ gAdditionalScrollOffsetX = additionalScrollX;
+
+ // loc_49F6E: ; CODE XREF: updateScrollOffset+9Dj
+ // ; updateScrollOffset+A3j
+ int16_t additionalScrollY = scrollY;
+ scrollY += gAdditionalScrollOffsetY;
+ if (scrollY < 0) // in asm there wasn't a explicit "cmp", just the "add" above
+ {
+ scrollY = 0;
+ }
+ else
+ {
+ // loc_49F99: ; CODE XREF: updateScrollOffset+CFj
+ if (scrollY > maxScrollY) // 168
+ {
+ scrollY = maxScrollY; // 168
+ }
+ }
+
+ // loc_49FA1: ; CODE XREF: updateScrollOffset+C1j
+ // ; updateScrollOffset+D9j
+ additionalScrollY -= scrollY;
+ additionalScrollY = -additionalScrollY;
+ gAdditionalScrollOffsetY = additionalScrollY;
+ }
+
+ // loc_49FA9: ; CODE XREF: updateScrollOffset+6Ej
+ // ; updateScrollOffset+BDj ...
+ // This makes the screen shake on an explosion
+ if (gShouldShakeWithAllExplosions != 0 || (gShakeWithExplosionsDisabled == 0 && (gQuitLevelCountdown & 0xFF) != 0))
+ {
+ // loc_49FBE: ; CODE XREF: updateScrollOffset+F0j
+ randomNumber = randomNumber & 0x101;
+
+ uint16_t scrollShakeYOffset = randomNumber >> 8;
+ uint16_t scrollShakeXOffset = (randomNumber & 0xFF);
+
+ scrollY += scrollShakeYOffset;
+ if (scrollX > 0x13C) // 316
+ {
+ scrollShakeXOffset = -scrollShakeXOffset;
+ }
+
+ // loc_49FD0: ; CODE XREF: updateScrollOffset+10Ej
+ scrollX += scrollShakeXOffset;
+ }
+
+ // loc_49FD2: ; CODE XREF: updateScrollOffset+F7j
+ // ; updateScrollOffset+FEj
+ gScrollOffsetX = scrollX;
+ gScrollOffsetY = scrollY;
+}
+
+void updateBugTiles(int16_t position) // movefun7 proc near ; DATA XREF: data:163Co
+{
+ // 01ED:33DA
+ StatefulLevelTile *currentTile = &gCurrentLevelState[position];
+ StatefulLevelTile *aboveTile = &gCurrentLevelState[position - kLevelWidth];
+ StatefulLevelTile *belowTile = &gCurrentLevelState[position + kLevelWidth];
+ StatefulLevelTile *leftTile = &gCurrentLevelState[position - 1];
+ StatefulLevelTile *rightTile = &gCurrentLevelState[position + 1];
+ StatefulLevelTile *aboveLeftTile = &gCurrentLevelState[position - kLevelWidth - 1];
+ StatefulLevelTile *aboveRightTile = &gCurrentLevelState[position - kLevelWidth + 1];
+ StatefulLevelTile *belowLeftTile = &gCurrentLevelState[position + kLevelWidth - 1];
+ StatefulLevelTile *belowRightTile = &gCurrentLevelState[position + kLevelWidth + 1];
+
+ if (currentTile->tile != LevelTileTypeBug)
+ {
+ return;
+ }
+
+ // loc_4A045: ; CODE XREF: movefun7+5j
+ if ((gFrameCounter & 3) != 0)
+ {
+ return;
+ }
+
+ // loc_4A051: ; CODE XREF: movefun7+11j
+ int8_t frameNumber = currentTile->state;
+ frameNumber++;
+ if (frameNumber >= 0xE)
+ {
+ // 01ED:33F9
+ uint8_t value = generateRandomNumber() & 0xFF;
+ value &= 0x3F;
+ value += 0x20;
+ value = -value;
+ frameNumber = value;
+ }
+
+ // loc_4A067: ; CODE XREF: movefun7+1Dj
+ currentTile->state = frameNumber;
+ if (frameNumber < 0)
+ {
+ return;
+ }
+
+ // loc_4A071: ; CODE XREF: movefun7+31j
+ if (aboveLeftTile->tile == LevelTileTypeMurphy || aboveTile->tile == LevelTileTypeMurphy || aboveRightTile->tile == LevelTileTypeMurphy || leftTile->tile == LevelTileTypeMurphy || rightTile->tile == LevelTileTypeMurphy || belowLeftTile->tile == LevelTileTypeMurphy || belowTile->tile == LevelTileTypeMurphy || belowRightTile->tile == LevelTileTypeMurphy)
+ {
+ // loc_4A0AB: ; CODE XREF: movefun7+39j
+ // ; movefun7+40j ...
+ playBugSound();
+ }
+
+ // loc_4A0AE: ; CODE XREF: movefun7+6Cj
+ Point frameCoordinates = kBugFrameCoordinates[frameNumber];
+ drawMovingFrame(frameCoordinates.x, frameCoordinates.y, position);
+}
+
+void updateTerminalTiles(int16_t position) // movefun5 proc near ; DATA XREF: data:1630o
+{
+ // 01ED:346F
+ StatefulLevelTile *currentTile = &gCurrentLevelState[position];
+
+ if (currentTile->tile != LevelTileTypeTerminal)
+ {
+ return;
+ }
+
+ // loc_4A0DA: ; CODE XREF: updateTerminalTiles+5j
+ int8_t state = currentTile->state;
+ state++;
+ if (state <= 0)
+ {
+ currentTile->state = state;
+ return;
+ }
+
+ // loc_4A0EA: ; CODE XREF: updateTerminalTiles+11j
+ uint8_t value = generateRandomNumber() & 0xFF;
+ value &= gTerminalMaxFramesToNextScroll;
+ value = -value;
+ currentTile->state = value;
+
+ scrollTerminalScreen(position);
+}
+
+/// Updates the random seed using the clock
+void generateRandomSeedFromClock() // getTime proc near ; CODE XREF: start:doesNotHaveCommandLinep
+ // ; handleGameUserInput+669p ...
+{
+ uint32_t timeInMilliseconds = getTime();
+ // In order to keep the same behavior and values, this code will convert
+ // the time in milliseconds to the clock count, as described in
+ // http://vitaly_filatov.tripod.com/ng/asm/asm_029.1.html
+ // If 1 second is 18.2 clock counts, we need to divide the time
+ // by 1000 to get the seconds, and then multiply by 18.2.
+ //
+ uint32_t clockCount = timeInMilliseconds * 18.2 / 1000;
+ uint16_t lowValue = (clockCount & 0xFFFF);
+ uint16_t highValue = ((clockCount >> 16) & 0xFFFF);
+ gRandomGeneratorSeed = highValue ^ lowValue;
+}
+
+/// Generates a random number based on time?
+uint16_t generateRandomNumber() // sub_4A1AE proc near ; CODE XREF: handleGameUserInput+66Cp
+ // ; updateScrollOffset+9p ...
+{
+ uint16_t someValue = gRandomGeneratorSeed;
+ someValue *= 0x5E5; // 1509
+ someValue += 0x31; // '1' or 49
+ gRandomGeneratorSeed = someValue;
+ return someValue / 2;
+}
+
+void updateUserInput() // sub_4A1BF proc near ; CODE XREF: handleGameUserInput+13p
+ // ; runMainMenu+BDp ...
+{
+ // 01ED:355C
+ uint8_t directionKeyWasPressed = 0;
+
+ gCurrentUserInput = UserInputNone;
+
+ if (isUpButtonPressed())
+ {
+ // loc_4A1CF: ; CODE XREF: updateUserInput+7j
+ gCurrentUserInput = UserInputUp;
+ directionKeyWasPressed = 1;
+ }
+
+ // loc_4A1D6: ; CODE XREF: updateUserInput+Ej
+ if (isLeftButtonPressed())
+ {
+ // loc_4A1E4: ; CODE XREF: updateUserInput+1Cj
+ gCurrentUserInput = UserInputLeft;
+ directionKeyWasPressed = 1;
+ }
+
+ // loc_4A1EB: ; CODE XREF: updateUserInput+23j
+ if (isDownButtonPressed())
+ {
+ // loc_4A1F9: ; CODE XREF: updateUserInput+31j
+ gCurrentUserInput = UserInputDown;
+ directionKeyWasPressed = 1;
+ }
+
+ // loc_4A200: ; CODE XREF: updateUserInput+38j
+ if (isRightButtonPressed())
+ {
+ // loc_4A20E: ; CODE XREF: updateUserInput+46j
+ gCurrentUserInput = UserInputRight;
+ directionKeyWasPressed = 1;
+ }
+
+ // loc_4A215: ; CODE XREF: updateUserInput+4Dj
+ if (isActionButtonPressed())
+ {
+ // loc_4A22A: ; CODE XREF: updateUserInput+5Bj
+ // ; updateUserInput+62j
+ if (directionKeyWasPressed == 1)
+ {
+ gCurrentUserInput += kUserInputSpaceAndDirectionOffset;
+ }
+ else
+ {
+ // loc_4A236: ; CODE XREF: updateUserInput+6Ej
+ gCurrentUserInput = UserInputSpaceOnly;
+ }
+ }
+}
+
+void removeTiles(LevelTileType tileType) // sub_4A23C proc near ; CODE XREF: handleGameUserInput+111p
+ // ; handleGameUserInput+11Dp ...
+{
+ // 01ED:35D9
+ // Looks like this function goes through every tile and clears those that match the parameter
+ for (uint16_t i = 0; i < kLevelSize; ++i)
+ {
+ // loc_4A242: ; CODE XREF: removeTiles+1Fj
+ StatefulLevelTile *tile = &gCurrentLevelState[i];
+ if (tile->tile != tileType)
+ {
+ if (tileType != LevelTileTypeSnikSnak || tile->tile != 0xBB)
+ {
+ continue;
+ }
+ }
+
+ // loc_4A253: ; CODE XREF: removeTiles+Cj
+ tile->state = 0;
+ tile->tile = LevelTileTypeSpace;
+ }
+ forceRestoreOriginalFancyTiles();
+ drawFixedLevel();
+ convertToEasyTiles();
+ gShouldUpdateTotalLevelTime = 0;
+ gHasUserCheated = 1;
+}
+
+void findMurphy() // proc near ; CODE XREF: start+344p fetchAndInitializeLevel+22p
+{
+ // 01ED:360E
+ for (int i = 0; i < kLevelSize; ++i)
+ {
+ if (gCurrentLevel.tiles[i] == LevelTileTypeMurphy)
+ {
+ gMurphyLocation = i;
+ break;
+ }
+ }
+
+ scrollToMurphy();
+}
+
+void scrollToMurphy() // sub_4A291 proc near ; CODE XREF: handleGameUserInput+686p
+{
+ // 01ED:362E
+ // Parameters:
+ // - si: murphy location * 2
+ // - al: murphy location
+
+ gMurphyTileX = gMurphyLocation % kLevelWidth; // stores X coord
+ gMurphyTileY = gMurphyLocation / kLevelWidth; // stores Y coord
+
+ gMurphyPositionX = gMurphyTileX * kTileSize;
+ gMurphyPositionY = gMurphyTileY * kTileSize;
+ // di = si[0x6155];
+ // si = kMurphyStillSpriteCoordinates;
+ drawMovingFrame(304, 132, gMurphyLocation);
+ updateScrollOffset();
+
+ videoLoop();
+}
+
+uint16_t convertToEasyTiles() // sub_4A2E6 proc near ; CODE XREF: start+33Bp runLevel+ADp ...
+{
+ // 01ED:3683
+ uint16_t numberOfInfotrons = 0;
+ uint16_t numberOfSomething = 0; // this is bx, just counts the number of tiles so technically is same as cx at this point… probably a return value but I don't see it used anywhere???
+
+ for (int i = 0; i < kLevelSize; ++i)
+ {
+ // loc_4A2F0: ; CODE XREF: convertToEasyTiles+D1j
+ StatefulLevelTile *currentTile = &gCurrentLevelState[i];
+ numberOfSomething++;
+
+ if (currentTile->tile == 0xF1)
+ {
+ currentTile->tile = LevelTileTypeExplosion;
+ continue; // jmp short loc_4A3B0
+ }
+
+ // loc_4A2FC: ; CODE XREF: convertToEasyTiles+Ej
+ if (gIsGameBusy != 1)
+ {
+ if (currentTile->tile == LevelTileTypeInfotron)
+ {
+ // loc_4A33C: ; CODE XREF: convertToEasyTiles+20j
+ numberOfInfotrons++;
+ continue; // jmp short loc_4A3B0
+ }
+ }
+ // TODO: what are these gIsGameBusy for??
+ if (gIsGameBusy == 1 || currentTile->state != 0 || currentTile->tile != LevelTileTypeSnikSnak) // jz short loc_4A34B
+ {
+ if (gIsGameBusy == 1 || currentTile->state != 0 || currentTile->tile != LevelTileTypeElectron) // jz short loc_4A379
+ {
+ // loc_4A312: ; CODE XREF: convertToEasyTiles+1Bj
+ if ((currentTile->state == 0 && currentTile->tile == LevelTileTypeHorizontalChipLeft) || (currentTile->state == 0 && currentTile->tile == LevelTileTypeHorizontalChipRight) || (currentTile->state == 0 && currentTile->tile == LevelTileTypeHorizontalChipTop) || (currentTile->state == 0 && currentTile->tile == LevelTileTypeHorizontalChipBottom))
+ {
+ // loc_4A33F: ; CODE XREF: convertToEasyTiles+2Fj
+ // ; convertToEasyTiles+34j ...
+ currentTile->tile = LevelTileTypeChip; // mov word ptr [si], 5
+ currentTile->state = 0;
+ continue; // jmp short loc_4A3B0
+ }
+ if (currentTile->state == 0 && currentTile->tile >= LevelTileTypeHardware2 && currentTile->tile <= LevelTileTypeHardware11)
+ {
+ // loc_4A345: ; CODE XREF: convertToEasyTiles+48j
+ currentTile->tile = LevelTileTypeHardware; // mov word ptr [si], 6
+ currentTile->state = 0;
+ continue; // jmp short loc_4A3B0
+ }
+
+ // loc_4A330: ; CODE XREF: convertToEasyTiles+43j
+ if (currentTile->state == 0 && currentTile->tile >= LevelTileTypeSportRight && currentTile->tile <= LevelTileTypeSportUp)
+ {
+ // loc_4A3A7: ; CODE XREF: convertToEasyTiles+52j
+ currentTile->tile -= 4; // Converts Sport[Direction] to Port[Direction]
+ currentTile->state = 1;
+ continue;
+ }
+
+ // loc_4A33A: ; CODE XREF: convertToEasyTiles+4Dj
+ continue;
+ }
+ }
+
+ StatefulLevelTile *leftTile = &gCurrentLevelState[i - 1];
+ StatefulLevelTile *aboveTile = &gCurrentLevelState[i - kLevelWidth];
+ StatefulLevelTile *rightTile = &gCurrentLevelState[i + 1];
+
+ if (currentTile->state != 0 || currentTile->tile != LevelTileTypeElectron) // jz short loc_4A379
+ {
+ // loc_4A34B: ; CODE XREF: convertToEasyTiles+25j
+ if (leftTile->tile == LevelTileTypeSpace && leftTile->state == 0) // cmp word ptr [si-2], 0
+ {
+ currentTile->state = 1;
+ // si[1] = 1; //mov byte ptr [si+1], 1
+ continue; // jmp short loc_4A3B0
+ }
+ // loc_4A357: ; CODE XREF: convertToEasyTiles+69j
+ // 0x78 = 120
+ if (aboveTile->tile == LevelTileTypeSpace && aboveTile->state == 0) // cmp word ptr [si-78h], 0
+ {
+ // 01ED:36FA
+ // mov word ptr [si-78h], 1011h
+ aboveTile->state = 0x10;
+ aboveTile->tile = LevelTileTypeSnikSnak;
+ // mov word ptr [si], 0FFFFh
+ currentTile->state = 0xFF;
+ currentTile->tile = 0xFF;
+ continue; // jmp short loc_4A3B0
+ }
+ // loc_4A368: ; CODE XREF: convertToEasyTiles+75j
+ if (rightTile->tile == LevelTileTypeSpace && rightTile->state == 0) // cmp word ptr [si+2], 0
+ {
+ // 01ED:370B
+ // mov word ptr [si+2], 2811h
+ rightTile->state = 0x28;
+ rightTile->tile = LevelTileTypeSnikSnak;
+ // mov word ptr [si], 0FFFFh
+ currentTile->state = 0xFF;
+ currentTile->tile = 0xFF;
+ continue; // jmp short loc_4A3B0
+ }
+
+ continue;
+ }
+ // loc_4A379: ; CODE XREF: convertToEasyTiles+2Aj
+ if (leftTile->tile == LevelTileTypeSpace && leftTile->state == 0) // cmp word ptr [si-2], 0
+ {
+ currentTile->state = 1; // mov byte ptr [si+1], 1
+ continue; // jmp short loc_4A3B0
+ }
+ // loc_4A385: ; CODE XREF: convertToEasyTiles+97j
+ if (aboveTile->tile == LevelTileTypeSpace && aboveTile->state == 0) // cmp word ptr [si-78h], 0
+ {
+ // mov word ptr [si-78h], 1018h
+ aboveTile->state = 0x10;
+ aboveTile->tile = LevelTileTypeElectron;
+ // mov word ptr [si], 0FFFFh
+ currentTile->state = 0xFF;
+ currentTile->tile = 0xFF;
+ continue; // jmp short loc_4A3B0
+ }
+ // loc_4A396: ; CODE XREF: convertToEasyTiles+A3j
+ if (rightTile->tile == LevelTileTypeSpace && rightTile->state == 0) // cmp word ptr [si+2], 0
+ {
+ // mov word ptr [si+2], 2818h
+ rightTile->state = 0x28;
+ rightTile->tile = LevelTileTypeElectron;
+ // mov word ptr [si], 0FFFFh
+ currentTile->state = 0xFF;
+ currentTile->tile = 0xFF;
+ continue; // jmp short loc_4A3B0
+ }
+ }
+
+ return numberOfInfotrons;
+}
+
+void resetNumberOfInfotrons(uint16_t numberOfInfotronsFoundInLevel) // sub_4A3BB proc near ; CODE XREF: start+33Ep fetchAndInitializeLevel+17p
+{
+ // In the original game, the number of infotrons found in a level is stored in a 2-bytes variable,
+ // however, when stored for its use in the game, it's stored in a 1-byte variable.
+ //
+ uint8_t numberOfInfotrons = (numberOfInfotronsFoundInLevel & 0xFF);
+ if (gNumberOfInfoTrons != 0)
+ {
+ numberOfInfotrons = gNumberOfInfoTrons;
+ }
+
+ // loc_4A3C6: ; CODE XREF: resetNumberOfInfotrons+5j
+ gNumberOfRemainingInfotrons = numberOfInfotrons;
+ gTotalNumberOfInfotrons = numberOfInfotrons;
+ drawNumberOfRemainingInfotrons();
+}
+
+void debugSkipLevel() // sub_4A3D2 proc near ; CODE XREF: handleGameUserInput+39Ep
+ // ; handleGameUserInput+3EBp
+{
+ gIsSPDemoAvailableToRun = 0;
+ gSelectedOriginalDemoLevelNumber = 0;
+ uint8_t wasNotZero = (gHasUserInterruptedDemo != 0);
+ gHasUserInterruptedDemo = 0;
+ gHasUserCheated = 1;
+ if (wasNotZero)
+ {
+ restartLevelWithoutAddingCurrentGameTimeToPlayer();
+ }
+
+ restartLevel();
+}
+
+void restartLevel() // sub_4A3E9 proc near ; CODE XREF: handleGameUserInput+14Ep
+{
+ if (gHasUserInterruptedDemo == 0)
+ {
+ addCurrentGameTimeToPlayer();
+ }
+
+ if (gIsRecordingDemo != 0)
+ {
+ stopRecordingDemo();
+ }
+
+ restartLevelWithoutAddingCurrentGameTimeToPlayer();
+}
+
+void restartLevelWithoutAddingCurrentGameTimeToPlayer() // loc_4A3F3: ; CODE XREF: debugSkipLevel+15j
+// ; restartLevel+5j
+{
+ gIsMoveScrollModeEnabled = 0;
+ gAdditionalScrollOffsetX = 0;
+ gAdditionalScrollOffsetY = 0;
+ gIsFlashingBackgroundModeEnabled = 0;
+ gDebugExtraRenderDelay = 1;
+ replaceCurrentPaletteColor(0, (Color){0, 0, 0});
+
+ if (gHasUserInterruptedDemo != 0)
+ {
+ gIsPlayingDemo = 1;
+ }
+
+ // loc_4A427: ; CODE XREF: restartLevel+37j
+ gIsGameBusy = 0;
+ fetchAndInitializeLevel();
+ gIsGameBusy = 1;
+ if (gHasUserInterruptedDemo >= 1)
+ {
+ gIsPlayingDemo = 0;
+ if (gHasUserInterruptedDemo == 0) // WTF? this makes no sense...
+ {
+ gHasUserInterruptedDemo++;
+ }
+ }
+
+ // loc_4A446: ; CODE XREF: restartLevel+50j
+ // ; restartLevel+57j
+ gCurrentUserInput = UserInputNone;
+ if (gIsPlayingDemo == 0)
+ {
+ return;
+ }
+
+ gDemoCurrentInputIndex = word_5A33C;
+ gDemoCurrentInputRepeatCounter = 1;
+ simulateDemoInput();
+}
+
+void fetchAndInitializeLevel() // sub_4A463 proc near ; CODE XREF: recordDemo:loc_4953Bp
+ // ; restartLevel+43p
+{
+ readLevels();
+ drawFixedLevel();
+ drawGamePanel();
+ gIsGameBusy = -gIsGameBusy;
+ uint16_t numberOfInfotrons = convertToEasyTiles();
+ gIsGameBusy = -gIsGameBusy;
+ resetNumberOfInfotrons(numberOfInfotrons);
+ gIsShowingFancyTiles = 1;
+ initializeGameInfo();
+ findMurphy();
+}
+
+void updateOrangeDiskTiles(int16_t position) // movefun3 proc near ; DATA XREF: data:161Ao
+{
+ // 01ED:3826
+ StatefulLevelTile *currentTile = &gCurrentLevelState[position];
+ StatefulLevelTile *belowTile = &gCurrentLevelState[position + kLevelWidth];
+
+ if (currentTile->tile != LevelTileTypeOrangeDisk)
+ {
+ return;
+ }
+
+ // loc_4A491: ; CODE XREF: movefun3+5j
+ uint16_t tileValue = ((currentTile->state << 8) | currentTile->tile);
+
+ if (tileValue >= 0x3008)
+ {
+ // loc_4A4D4: ; CODE XREF: movefun3+Fj
+ // push si
+ uint8_t stateFrame = currentTile->state;
+ // bh = 0;
+ // al = state;
+ // bx *= 2;
+ // ;and bx, byte ptr 0Fh
+ // db 83h, 0E3h, 0Fh
+ stateFrame *= 2;
+ stateFrame &= 0xF; // 16 frames?
+
+ // mov di, [si+6155h]
+ // shl bx, 1
+ // add di, [bx+6C95h]
+ // mov si, 12F6h
+ // mov si, [si]
+ uint16_t offset = kFallAnimationGravityOffsets[stateFrame];
+
+ uint8_t tileX = (position % kLevelWidth);
+ uint8_t tileY = (position / kLevelWidth);
+
+ uint16_t dstX = tileX * kTileSize + (offset % 122);
+ uint16_t dstY = tileY * kTileSize + (offset / 122);
+
+ drawMovingSpriteFrameInLevel(128, 64,
+ kTileSize,
+ kTileSize + 2,
+ dstX, dstY);
+
+ uint8_t state = currentTile->state;
+ state++;
+ uint8_t otherMovingObject = state;
+ otherMovingObject &= 7;
+ if (otherMovingObject != 0)
+ {
+ currentTile->state = state;
+ return;
+ }
+
+ // loc_4A516: ; CODE XREF: movefun3+86j
+ currentTile->state = 0;
+ currentTile->tile = LevelTileTypeSpace;
+ belowTile->state = 0;
+ belowTile->tile = LevelTileTypeOrangeDisk;
+
+ position += kLevelWidth;
+
+ // Update tiles
+ currentTile = &gCurrentLevelState[position];
+ belowTile = &gCurrentLevelState[position + kLevelWidth];
+
+ if (belowTile->state == 0 && belowTile->tile == LevelTileTypeSpace)
+ {
+ currentTile->state = 0x30;
+ belowTile->state = 8;
+ }
+ // loc_4A537: ; CODE XREF: movefun3+A1j
+ else if (belowTile->tile != LevelTileTypeExplosion)
+ {
+ // loc_4A53F: ; CODE XREF: movefun3+B3j
+ // 01ED:38DC
+ detonateBigExplosion(position);
+ }
+
+ return;
+ }
+ else if (tileValue >= 0x2008)
+ {
+ // loc_4A4B4: ; CODE XREF: movefun3+14j
+ if (belowTile->state != 0 || belowTile->tile != LevelTileTypeSpace)
+ {
+ // loc_4A4C2: ; CODE XREF: movefun3+30j
+ uint8_t state = currentTile->state;
+ state++;
+ if (state == 0x22)
+ {
+ state = 0x30;
+ }
+
+ // loc_4A4CF: ; CODE XREF: movefun3+42j
+ currentTile->state = state;
+ return;
+ }
+ currentTile->state = 0;
+ currentTile->tile = LevelTileTypeOrangeDisk;
+ return;
+ }
+
+ if (belowTile->state == 0 && belowTile->tile == LevelTileTypeSpace)
+ {
+ // loc_4A4A9: ; CODE XREF: movefun3+1Dj
+ currentTile->state = 0x20;
+ belowTile->state = 8;
+ }
+}
+
+void updateExplosionTiles(int16_t position) // loc_4A543: ; DATA XREF: data:1648o
+{
+ // 01ED:38E0
+ StatefulLevelTile *currentTile = &gCurrentLevelState[position];
+
+ if (currentTile->tile != LevelTileTypeExplosion)
+ {
+ return;
+ }
+
+ // loc_4A54B: ; CODE XREF: code:3928j
+ if ((gFrameCounter & 3) != 0)
+ {
+ return;
+ }
+
+ // loc_4A557: ; CODE XREF: code:3934j
+ uint8_t state = currentTile->state;
+ if ((state & 0x80) != 0)
+ {
+ // loc_4A5A0: ; CODE XREF: code:393Ej
+ state++;
+ if (state != 0x89)
+ {
+ // loc_4A5B3: ; CODE XREF: code:3985j
+ currentTile->state = state;
+ state--;
+ state &= 0xF;
+ // 12e6
+ Point frameCoordinates = kInfotronExplosionAnimationFrameCoordinates[state];
+
+ drawMovingFrame(frameCoordinates.x,
+ frameCoordinates.y,
+ position);
+ }
+ else
+ {
+ currentTile->state = 0;
+ currentTile->tile = LevelTileTypeInfotron;
+ gIsExplosionStarted = 0;
+ }
+ }
+ else
+ {
+ state++;
+ currentTile->state = state;
+ state--;
+
+ // 12d6
+ Point frameCoordinates = kRegularExplosionAnimationFrameCoordinates[state];
+
+ drawMovingFrame(frameCoordinates.x,
+ frameCoordinates.y,
+ position);
+
+ // loc_4A582: ; CODE XREF: code:396Aj
+ if (currentTile->state == 8)
+ {
+ currentTile->state = 0;
+ currentTile->tile = LevelTileTypeSpace;
+ gIsExplosionStarted = 0;
+ }
+ }
+}
+
+void updateExplosionTimers() // sub_4A5E0 proc near ; CODE XREF: runLevel+106p
+{
+ // 01ED:397D
+ for (int i = 0; i < kLevelSize; ++i)
+ {
+ // loc_4A5E9: ; CODE XREF: updateExplosionTimers+25j
+ int8_t timer = gExplosionTimers[i];
+
+ if (timer == 0)
+ {
+ continue;
+ }
+
+ if (timer < 0)
+ {
+ // loc_4A608: ; CODE XREF: updateExplosionTimers+10j
+ gExplosionTimers[i] = timer + 1;
+
+ if (gExplosionTimers[i] == 0)
+ {
+ StatefulLevelTile *tile = &gCurrentLevelState[i];
+ tile->state = 0xFF;
+ tile->tile = LevelTileTypeElectron;
+ detonateBigExplosion(i);
+ }
+ }
+ else
+ {
+ gExplosionTimers[i] = timer - 1;
+
+ if (gExplosionTimers[i] == 0)
+ {
+ detonateBigExplosion(i);
+ }
+ }
+ }
+}
+
+void detonateBigExplosionTile(int16_t position, uint8_t newTile, uint8_t newState, uint8_t newExplosionTimer)
+{
+ StatefulLevelTile *currentTile = &gCurrentLevelState[position];
+
+ // loc_4A64C: ; CODE XREF: detonateBigExplosion+26j
+ uint8_t hasChangedCurrentTile = 0;
+
+ if (currentTile->tile == LevelTileTypeOrangeDisk || currentTile->tile == LevelTileTypeYellowDisk || currentTile->tile == LevelTileTypeSnikSnak)
+ {
+ // loc_4A680: ; CODE XREF: detonateBigExplosion+3Aj
+ // ; detonateBigExplosion+3Ej ...
+ if (currentTile->tile != LevelTileTypeHardware)
+ {
+ gExplosionTimers[position] = newExplosionTimer; // mov [bx+23F7h], dh
+ }
+ }
+ else if (currentTile->tile == LevelTileTypeZonk)
+ {
+ // loc_4A69C: ; CODE XREF: detonateBigExplosion+46j
+ // 01ED:3A39
+ detonateZonk(position, newState, newTile);
+ hasChangedCurrentTile = 1; // to emulate jmp loc_4A6A6
+ }
+ else if (currentTile->tile == LevelTileTypeInfotron)
+ {
+ // loc_4A692: ; CODE XREF: detonateBigExplosion+4Aj 01ED:3A2F
+ sub_4AA34(position, newState, newTile);
+ hasChangedCurrentTile = 1; // to emulate jmp loc_4A6A6
+ }
+ else if (currentTile->tile == LevelTileTypeElectron)
+ {
+ newExplosionTimer = -newExplosionTimer; // dh = -dh;
+ newState = 0x80;
+ newTile = LevelTileTypeExplosion;
+ // loc_4A680: ; CODE XREF: detonateBigExplosion+3Aj
+ // ; detonateBigExplosion+3Ej ...
+ if (currentTile->tile != LevelTileTypeHardware)
+ {
+ gExplosionTimers[position] = newExplosionTimer; // mov [bx+23F7h], dh
+ }
+ }
+ // loc_4A676: ; CODE XREF: detonateBigExplosion+4Ej
+ else if (currentTile->tile == LevelTileTypeMurphy)
+ {
+ gShouldKillMurphy = 1;
+
+ // loc_4A680: ; CODE XREF: detonateBigExplosion+3Aj
+ // ; detonateBigExplosion+3Ej ...
+ if (currentTile->tile != LevelTileTypeHardware)
+ {
+ gExplosionTimers[position] = newExplosionTimer; // mov [bx+23F7h], dh
+ }
+ }
+
+ if (hasChangedCurrentTile == 0)
+ {
+ // loc_4A688: ; CODE XREF: detonateBigExplosion+59j
+ // ; detonateBigExplosion+63j
+ if (currentTile->tile != LevelTileTypeHardware)
+ {
+ // mov [si+17BAh], cx
+ currentTile->state = newState;
+ currentTile->tile = newTile;
+ }
+ }
+}
+
+// Creates an explossion of 3x3 tiles around a position
+void detonateBigExplosion(int16_t position) // sub_4A61F proc near ; CODE XREF: movefun+271p
+ // ; movefun2+20Fp ...
+{
+ // 01ED:39BC
+ StatefulLevelTile *currentTile = &gCurrentLevelState[position];
+
+ if (currentTile->state == 0 && currentTile->tile == LevelTileTypeHardware)
+ {
+ return;
+ }
+
+ // These indicate the kind of the explosion created by this tile.
+ // Tiles around may create a different explosion if needed (like Electrons create Infotrons).
+ //
+ uint8_t newState = 0;
+ uint8_t newTile = 0;
+ uint8_t newExplosionTimer = 0;
+
+ // loc_4A627: ; CODE XREF: detonateBigExplosion+5j
+ gIsExplosionStarted = 1;
+ if (currentTile->tile == LevelTileTypeMurphy)
+ {
+ gShouldKillMurphy = 1;
+ }
+
+ // loc_4A639: ; CODE XREF: detonateBigExplosion+12j
+ if (currentTile->tile == LevelTileTypeElectron)
+ {
+ newState = 0x80;
+ newTile = LevelTileTypeExplosion;
+ newExplosionTimer = -13;
+ }
+ else
+ {
+ // loc_4A647: ; CODE XREF: detonateBigExplosion+1Fj
+ // cx = 0x1F; // 31
+ newState = 0;
+ newTile = LevelTileTypeExplosion;
+ newExplosionTimer = 13;
+ }
+
+ detonateBigExplosionTile(position - kLevelWidth - 1, newTile, newState, newExplosionTimer);
+ detonateBigExplosionTile(position - kLevelWidth, newTile, newState, newExplosionTimer);
+ detonateBigExplosionTile(position - kLevelWidth + 1, newTile, newState, newExplosionTimer);
+ detonateBigExplosionTile(position - 1, newTile, newState, newExplosionTimer);
+
+ // loc_4A7AB: ; CODE XREF: detonateBigExplosion:loc_4A795j
+ // ; detonateBigExplosion+180j ...
+ currentTile->state = newState;
+ currentTile->tile = newTile;
+
+ detonateBigExplosionTile(position + 1, newTile, newState, newExplosionTimer);
+ detonateBigExplosionTile(position + kLevelWidth - 1, newTile, newState, newExplosionTimer);
+ detonateBigExplosionTile(position + kLevelWidth, newTile, newState, newExplosionTimer);
+ detonateBigExplosionTile(position + kLevelWidth + 1, newTile, newState, newExplosionTimer);
+
+ // loc_4A90B: ; CODE XREF: detonateBigExplosion:loc_4A8F5j
+ // ; detonateBigExplosion+2E0j ...
+ playExplosionSound();
+ // 01ED:3CAC
+}
+
+void updatePlantedRedDisk() // sub_4A910 proc near ; CODE XREF: runLevel:noFlashing3p
+{
+ // 01ED:3CAD
+ if (gPlantedRedDiskCountdown <= 1)
+ {
+ return;
+ }
+
+ StatefulLevelTile *tile = &gCurrentLevelState[gPlantedRedDiskPosition];
+
+ if (tile->state == 0 && tile->tile == LevelTileTypeSpace)
+ {
+ tile->state = 0;
+ tile->tile = LevelTileTypeRedDisk;
+ }
+
+ // loc_4A932: ; CODE XREF: updatePlantedRedDisk+1Aj
+ // si = word_5177E;
+ drawMovingFrame(256, 164, gPlantedRedDiskPosition);
+ gPlantedRedDiskCountdown++;
+ if (gPlantedRedDiskCountdown >= 0x28)
+ {
+ detonateBigExplosion(gPlantedRedDiskPosition);
+ gPlantedRedDiskCountdown = 0;
+ }
+}
+
+void addCurrentGameTimeToPlayer() // sub_4A95F proc near ; CODE XREF: runLevel+372p
+ // ; restartLevel+7p ...
+{
+ uint8_t seconds = gGameSeconds;
+ uint8_t minutes = gGameMinutes;
+ uint16_t hours = gGameHours;
+ if (gIsPlayingDemo != 0)
+ {
+ return;
+ }
+
+ if (gIsSPDemoAvailableToRun != 0)
+ {
+ return;
+ }
+
+ // loc_4A980: ; CODE XREF: addCurrentGameTimeToPlayer+1Ej
+ PlayerEntry *playerEntry = &gPlayerListData[gCurrentPlayerIndex];
+ seconds += playerEntry->seconds;
+
+ do
+ {
+ // loc_4A994: ; CODE XREF: addCurrentGameTimeToPlayer+3Ej
+ if (seconds < 60)
+ {
+ break;
+ }
+ seconds -= 60;
+ playerEntry->minutes++;
+ } while (1);
+
+ // loc_4A99F: ; CODE XREF: addCurrentGameTimeToPlayer+37j
+ playerEntry->seconds = seconds;
+
+ minutes += playerEntry->minutes;
+
+ do
+ {
+ // loc_4A9A8: ; CODE XREF: addCurrentGameTimeToPlayer+52j
+ if (minutes < 60)
+ {
+ break;
+ }
+ minutes -= 60;
+ playerEntry->hours++;
+ } while (1);
+
+ // loc_4A9B3: ; CODE XREF: addCurrentGameTimeToPlayer+4Bj
+ playerEntry->minutes = minutes;
+
+ hours += playerEntry->hours;
+
+ if (hours > 0xFF)
+ {
+ hours = 0xFF;
+ }
+
+ // loc_4A9C0: ; CODE XREF: addCurrentGameTimeToPlayer+5Dj
+ playerEntry->hours = hours;
+}
+
+void detonateZonk(int16_t position, uint8_t state, uint8_t tile) // sub_4A9C4 proc near ; CODE XREF: detonateBigExplosion+81p
+ // ; detonateBigExplosion+D8p ...
+{
+ // 01ED:3D61
+ StatefulLevelTile *currentTile = &gCurrentLevelState[position];
+ StatefulLevelTile *belowTile = &gCurrentLevelState[position + kLevelWidth];
+
+ uint8_t stateType = currentTile->state & 0xF0;
+
+ currentTile->state = state;
+ currentTile->tile = tile;
+
+ if (stateType == 0x10 || stateType == 0x70)
+ {
+ // loc_4A9EF: ; CODE XREF: detonateZonk+Aj detonateZonk+Fj
+ sub_4AAB4(position - kLevelWidth);
+ if (belowTile->state == 0x99 && belowTile->tile == 0x99)
+ {
+ sub_4AAB4(position + kLevelWidth);
+ }
+ }
+ else if (stateType == 0x20)
+ {
+ // loc_4AA05: ; CODE XREF: detonateZonk+14j
+ sub_4AAB4(position + 1);
+ sub_4AAB4(position + kLevelWidth);
+ }
+ else if (stateType == 0x30)
+ {
+ // loc_4AA12: ; CODE XREF: detonateZonk+19j
+ sub_4AAB4(position - 1);
+ sub_4AAB4(position + kLevelWidth);
+ }
+ else if (stateType == 0x50)
+ {
+ // loc_4AA1F: ; CODE XREF: detonateZonk+1Ej
+ sub_4AAB4(position - 1);
+ }
+ else if (stateType == 0x60)
+ {
+ // loc_4AA26: ; CODE XREF: detonateZonk+23j
+ sub_4AAB4(position + 1);
+ }
+ else if (stateType == 0x70)
+ {
+ // loc_4AA2D: ; CODE XREF: detonateZonk+28j
+ sub_4AAB4(position + kLevelWidth);
+ }
+}
+
+void sub_4AA34(int16_t position, uint8_t state, uint8_t tile) // proc near ; CODE XREF: detonateBigExplosion+77p
+ // ; detonateBigExplosion+CEp ...
+{
+ // Parameters:
+ // - si: position
+ // - cx: state (ch) and tile (cl)
+
+ StatefulLevelTile *currentTile = &gCurrentLevelState[position];
+ StatefulLevelTile *belowTile = &gCurrentLevelState[position + kLevelWidth];
+
+ uint8_t stateType = currentTile->state & 0xF0;
+
+ currentTile->state = state;
+ currentTile->tile = tile;
+
+ if (stateType == 0x10 || stateType == 0x70)
+ {
+ // loc_4AA5F: ; CODE XREF: sub_4AA34+Aj sub_4AA34+Fj
+ sub_4AAB4(position - kLevelWidth);
+ if (belowTile->state == 0x99 && belowTile->tile == 0x99)
+ {
+ sub_4AAB4(position + kLevelWidth);
+ }
+ }
+ else if (stateType == 0x20)
+ {
+ // loc_4AA75: ; CODE XREF: sub_4AA34+14j
+ sub_4AAB4(position + 1);
+ if (belowTile->state == 0x99 && belowTile->tile == 0x99)
+ {
+ sub_4AAB4(position + kLevelWidth);
+ }
+ }
+ else if (stateType == 0x30)
+ {
+ // loc_4AA8A: ; CODE XREF: sub_4AA34+19j
+ sub_4AAB4(position - 1);
+ if (belowTile->state == 0x99 && belowTile->tile == 0x99)
+ {
+ sub_4AAB4(position + kLevelWidth);
+ }
+ }
+ else if (stateType == 0x50)
+ {
+ // loc_4AA9F: ; CODE XREF: sub_4AA34+1Ej
+ sub_4AAB4(position - 1);
+ }
+ else if (stateType == 0x60)
+ {
+ // loc_4AAA6: ; CODE XREF: sub_4AA34+23j
+ sub_4AAB4(position + 1);
+ }
+ else if (stateType == 0x70)
+ {
+ // loc_4AAAD: ; CODE XREF: sub_4AA34+28j
+ sub_4AAB4(position + kLevelWidth);
+ }
+}
+
+void sub_4AAB4(int16_t position) // proc near ; CODE XREF: detonateZonk+2Ep
+ // ; detonateZonk+3Dp ...
+{
+ // 01ED:3DD1
+ StatefulLevelTile *currentTile = &gCurrentLevelState[position];
+
+ if (currentTile->tile == LevelTileTypeExplosion)
+ {
+ return;
+ }
+
+ // loc_4AABC: ; CODE XREF: sub_4AAB4+5j
+ currentTile->state = 0;
+ currentTile->tile = LevelTileTypeSpace;
+
+ // si = word_51580;
+ uint16_t dstX = (position % kLevelWidth) * kTileSize;
+ uint16_t dstY = (position / kLevelWidth) * kTileSize;
+
+ drawMovingSpriteFrameInLevel(0, 32, kTileSize, kTileSize, dstX, dstY);
+}
+
+void handleNewPlayerOptionClick() // sub_4AB1B proc near ; CODE XREF: runMainMenu+28Fp
+// ; DATA XREF: data:off_50318o
+{
+ // 01ED:3EB8
+ if (gIsForcedCheatMode != 0)
+ {
+ // jnz short loc_4AB4A
+ drawTextWithChars6FontWithOpaqueBackgroundIfPossible(168, 127, 6, "PLAYER LIST FULL ");
+ return;
+ }
+
+ int newPlayerIndex = -1;
+
+ for (int i = 0; i < kNumberOfPlayers; ++i)
+ {
+ PlayerEntry currentPlayerEntry = gPlayerListData[i];
+
+ // loc_4AB2D: ; CODE XREF: handleNewPlayerOptionClick+2Dj
+ if (strcmp(currentPlayerEntry.name, "--------") == 0)
+ {
+ newPlayerIndex = i;
+ break;
+ }
+ // loc_4AB42: ; CODE XREF: handleNewPlayerOptionClick+14j
+ // ; handleNewPlayerOptionClick+19j ...
+ }
+
+ if (newPlayerIndex == -1)
+ {
+ // loc_4AB4A: ; CODE XREF: handleNewPlayerOptionClick+5j
+ // mov di, 89F7h
+ drawTextWithChars6FontWithOpaqueBackgroundIfPossible(168, 127, 6, "PLAYER LIST FULL ");
+ return;
+ }
+
+ // loc_4AB56: ; CODE XREF: handleNewPlayerOptionClick+25j
+ gNewPlayerEntryIndex = newPlayerIndex;
+
+ char newPlayerName[kPlayerNameLength + 1] = " ";
+ gNewPlayerNameLength = 0;
+ uint16_t mouseX, mouseY;
+ uint16_t mouseButtonStatus;
+
+ restoreLastMouseAreaBitmap();
+
+ if (supportsRealKeyboard())
+ {
+ // mov di, 89F7h
+ drawTextWithChars6FontWithOpaqueBackgroundIfPossible(168, 127, 4, "YOUR NAME: ");
+
+ do
+ {
+ // loc_4AB7F: ; CODE XREF: handleNewPlayerOptionClick+6Aj
+ getMouseStatus(&mouseX, &mouseY, &mouseButtonStatus);
+ } while (mouseButtonStatus != 0);
+
+ char lastPressedCharacter = '\0';
+
+ do
+ {
+ // noKeyPressed: ; CODE XREF: handleNewPlayerOptionClick+79j
+ // ; handleNewPlayerOptionClick+8Aj ...
+ videoLoop();
+
+ int9handler(0);
+ getMouseStatus(&mouseX, &mouseY, &mouseButtonStatus);
+ if (mouseButtonStatus != 0)
+ {
+ break;
+ }
+ if (isAnyKeyPressed() == 0)
+ {
+ lastPressedCharacter = '\0';
+ continue;
+ }
+
+ char character = characterForLastKeyPressed();
+
+ if (lastPressedCharacter == character)
+ {
+ continue;
+ }
+
+ lastPressedCharacter = character;
+
+ if (character == 0) // For keys without a valid representation
+ {
+ continue;
+ }
+ if (character == '\n') // \n -> enter -> create player
+ {
+ break;
+ }
+ if (character == '\b') // backspace -> delete last char
+ {
+ // loc_4ABCC: ; CODE XREF: handleNewPlayerOptionClick+92j
+ if (gNewPlayerNameLength == 0)
+ {
+ continue;
+ }
+ gNewPlayerNameLength--;
+ newPlayerName[gNewPlayerNameLength] = ' ';
+ drawTextWithChars6FontWithOpaqueBackgroundIfPossible(232, 127, 6, newPlayerName);
+ continue;
+ }
+ if (gNewPlayerNameLength >= 8) // when more than 8 chars were entered, ignore the rest?
+ {
+ continue;
+ }
+ newPlayerName[gNewPlayerNameLength] = character; // mov [bx+si], al
+ gNewPlayerNameLength++;
+ drawTextWithChars6FontWithOpaqueBackgroundIfPossible(232, 127, 6, newPlayerName);
+ } while (1);
+
+ do
+ {
+ // loc_4ABEB: ; CODE XREF: handleNewPlayerOptionClick+72j
+ // ; handleNewPlayerOptionClick+8Ej ...
+ getMouseStatus(&mouseX, &mouseY, &mouseButtonStatus);
+ } while (mouseButtonStatus != 0);
+ }
+ else if (supportsVirtualKeyboard())
+ {
+ char inputBuffer[kPlayerNameLength + 1] = "";
+ uint8_t result = inputVirtualKeyboardText("Enter your name", kPlayerNameLength, inputBuffer);
+
+ if (result == 0)
+ {
+ drawTextWithChars6FontWithOpaqueBackgroundIfPossible(168, 127, 8, " ");
+ saveLastMouseAreaBitmap();
+ drawMouseCursor();
+ return;
+ }
+
+ const char *allowedCharacters = "0123456789QWERTYUIOPASDFGHJKLZXCVBNM -";
+
+ // Fill name with spaces, convert it to uppercase, and replace invalid characters with '-'
+ size_t numberOfSpaces = kPlayerNameLength - strlen(inputBuffer);
+
+ for (size_t idx = 0; idx < kPlayerNameLength; ++idx)
+ {
+ char inputCharacter = ' ';
+
+ if (idx >= numberOfSpaces)
+ {
+ inputCharacter = toupper(inputBuffer[idx - numberOfSpaces]);
+
+ if (strchr(allowedCharacters, inputCharacter) == NULL)
+ {
+ inputCharacter = '-';
+ }
+ }
+
+ newPlayerName[idx] = inputCharacter;
+ }
+ }
+ else
+ {
+ do
+ {
+ // loc_4AB7F: ; CODE XREF: handleNewPlayerOptionClick+6Aj
+ getMouseStatus(&mouseX, &mouseY, &mouseButtonStatus);
+ } while (mouseButtonStatus != 0);
+
+ // Limit the player number value to avoid -Wformat-truncation warning
+ snprintf(newPlayerName, sizeof(newPlayerName), "PLAYER%2d", MIN(gNewPlayerEntryIndex + 1, kNumberOfPlayers));
+ gNewPlayerNameLength = strlen(newPlayerName);
+ }
+
+ // Completely empty name: ignore
+ if (strcmp(newPlayerName, " ") == 0)
+ {
+ drawTextWithChars6FontWithOpaqueBackgroundIfPossible(168, 127, 8, " ");
+ saveLastMouseAreaBitmap();
+ drawMouseCursor();
+ return;
+ }
+
+ // loc_4AC1E: ; CODE XREF: handleNewPlayerOptionClick+E0j
+ // ; handleNewPlayerOptionClick+E5j ...
+ // Name with all dashes: invalid
+ if (strcmp(newPlayerName, "--------") == 0)
+ {
+ drawTextWithChars6FontWithOpaqueBackgroundIfPossible(168, 127, 6, "INVALID NAME ");
+ saveLastMouseAreaBitmap();
+ drawMouseCursor();
+ return;
+ }
+
+ // loc_4AC46: ; CODE XREF: handleNewPlayerOptionClick+108j
+ // ; handleNewPlayerOptionClick+10Dj ...
+
+ // Move spaces at the end of the name to the beginning
+ const int kLastNameCharacterIndex = sizeof(newPlayerName) - 2;
+ while (newPlayerName[kLastNameCharacterIndex] == ' ')
+ {
+ // loc_4AC4B: ; CODE XREF: handleNewPlayerOptionClick+14Cj
+ for (int i = kLastNameCharacterIndex; i >= 1; --i)
+ {
+ newPlayerName[i] = newPlayerName[i - 1];
+ }
+ newPlayerName[0] = ' ';
+ }
+
+ // loc_4AC69: ; CODE XREF: handleNewPlayerOptionClick+137j
+
+ for (int i = 0; i < kNumberOfPlayers; ++i)
+ {
+ PlayerEntry player = gPlayerListData[i];
+ // loc_4AC73: ; CODE XREF: handleNewPlayerOptionClick+18Cj
+ if (strcmp(player.name, newPlayerName) == 0)
+ {
+ drawTextWithChars6FontWithOpaqueBackgroundIfPossible(168, 127, 6, "PLAYER EXISTS ");
+ saveLastMouseAreaBitmap();
+ drawMouseCursor();
+ return;
+ }
+ // loc_4ACA3: ; CODE XREF: handleNewPlayerOptionClick+15Cj
+ // ; handleNewPlayerOptionClick+164j ...
+ }
+ gCurrentPlayerIndex = gNewPlayerEntryIndex;
+ PlayerEntry *newPlayerEntry = &gPlayerListData[gCurrentPlayerIndex];
+ memcpy(newPlayerEntry->name, newPlayerName, sizeof(newPlayerName));
+
+ drawTextWithChars6FontWithOpaqueBackgroundIfPossible(168, 127, 8, " ");
+ savePlayerListData();
+ saveHallOfFameData();
+ gShouldAutoselectNextLevelToPlay = 1;
+ prepareLevelDataForCurrentPlayer();
+ drawPlayerList();
+ drawLevelList();
+ drawRankings();
+ saveLastMouseAreaBitmap();
+ drawMouseCursor();
+}
+
+void handleDeletePlayerOptionClick() // sub_4AD0E proc near
+{
+ if (gIsForcedCheatMode != 0)
+ {
+ // loc_4AD3C: ; CODE XREF: handleDeletePlayerOptionClick+5j
+ drawTextWithChars6FontWithOpaqueBackgroundIfPossible(168, 127, 8, "NO PLAYER SELECTED ");
+ return;
+ }
+
+ PlayerEntry *currentPlayerEntry = &gPlayerListData[gCurrentPlayerIndex];
+ // *dword_58477 = currentPlayerEntry; // mov word ptr dword_58477, si
+ if (strcmp(currentPlayerEntry->name, "--------") == 0)
+ {
+ // loc_4AD3C: ; CODE XREF: handleDeletePlayerOptionClick+5j
+ drawTextWithChars6FontWithOpaqueBackgroundIfPossible(168, 127, 8, "NO PLAYER SELECTED ");
+ return;
+ }
+
+ // loc_4AD48: ; CODE XREF: handleDeletePlayerOptionClick+1Dj
+ // ; handleDeletePlayerOptionClick+22j ...
+ char message[24] = "";
+ sprintf(message, "DELETE '%s' ??? ", currentPlayerEntry->name);
+
+ drawTextWithChars6FontWithOpaqueBackgroundIfPossible(168, 127, 8, message);
+
+ uint16_t mouseX, mouseY;
+ uint16_t mouseButtonStatus;
+
+ do
+ {
+ // loc_4AD6C: ; CODE XREF: handleDeletePlayerOptionClick+64j
+ getMouseStatus(&mouseX, &mouseY, &mouseButtonStatus);
+ } while (mouseButtonStatus != 0);
+
+ do
+ {
+ // loc_4AD74: ; CODE XREF: handleDeletePlayerOptionClick+88j
+ videoLoop();
+ getMouseStatus(&mouseX, &mouseY, &mouseButtonStatus);
+ gMouseButtonStatus = mouseButtonStatus;
+ gMouseX = mouseX;
+ gMouseY = mouseY;
+ restoreLastMouseAreaBitmap();
+ saveLastMouseAreaBitmap();
+ drawMouseCursor();
+ } while (gMouseButtonStatus == 0);
+
+ ButtonDescriptor okButtonDescriptor = kMainMenuButtonDescriptors[9];
+
+ if (gMouseX >= okButtonDescriptor.startX && gMouseY >= okButtonDescriptor.startY && gMouseX <= okButtonDescriptor.endX && gMouseY <= okButtonDescriptor.endY)
+ {
+ // mov di, word ptr dword_58477 // recover current player entry pointer
+ memset(currentPlayerEntry, 0, sizeof(PlayerEntry));
+ memset(currentPlayerEntry->name, '-', sizeof(currentPlayerEntry->name) - 1);
+ }
+
+ // loc_4ADCE: ; CODE XREF: handleDeletePlayerOptionClick+97j
+ // ; handleDeletePlayerOptionClick+9Cj ...
+ restoreLastMouseAreaBitmap();
+ drawTextWithChars6FontWithOpaqueBackgroundIfPossible(168, 127, 8, " ");
+ savePlayerListData();
+ saveHallOfFameData();
+ gShouldAutoselectNextLevelToPlay = 1;
+ prepareLevelDataForCurrentPlayer();
+ drawPlayerList();
+ drawLevelList();
+ drawRankings();
+
+ do
+ {
+ // loc_4ADF3: ; CODE XREF: handleDeletePlayerOptionClick+EBj
+ getMouseStatus(&mouseX, &mouseY, &mouseButtonStatus);
+ } while (mouseButtonStatus != 0);
+ saveLastMouseAreaBitmap();
+}
+
+void handleSkipLevelOptionClick() // sub_4ADFF proc near
+{
+ // 01ED:419C
+ PlayerEntry currentPlayerEntry = gPlayerListData[gCurrentPlayerIndex];
+
+ if (strcmp(currentPlayerEntry.name, "--------") == 0)
+ {
+ drawTextWithChars6FontWithOpaqueBackgroundIfPossible(168, 127, 8, "NO PLAYER SELECTED ");
+ return;
+ }
+
+ // loc_4AE2E: ; CODE XREF: handleSkipLevelOptionClick+12j
+ // ; handleSkipLevelOptionClick+17j ...
+ int numberOfSkippedLevels = 0;
+
+ for (int i = 0; i < kNumberOfLevels; ++i)
+ {
+ // loc_4AE38: ; CODE XREF: handleSkipLevelOptionClick+40j
+ if (currentPlayerEntry.levelState[i] == PlayerLevelStateSkipped)
+ {
+ numberOfSkippedLevels++;
+ }
+ // loc_4AE3E: ; CODE XREF: handleSkipLevelOptionClick+3Bj
+ }
+ if (gIsDebugModeEnabled == 0)
+ {
+ // loc_4AE4A: ; CODE XREF: handleSkipLevelOptionClick+47j
+ if (numberOfSkippedLevels >= 3)
+ {
+ drawTextWithChars6FontWithOpaqueBackgroundIfPossible(168, 127, 6, "SKIP NOT POSSIBLE ");
+ return;
+ }
+ }
+
+ // loc_4AE5B: ; CODE XREF: handleSkipLevelOptionClick+49j
+ // ; handleSkipLevelOptionClick+4Ej
+ if (gCurrentPlayerLevelData[gCurrentSelectedLevelIndex - 1] != kNotCompletedLevelEntryColor)
+ {
+ drawTextWithChars6FontWithOpaqueBackgroundIfPossible(168, 127, 4, "COLORBLIND I GUESS ");
+ return;
+ }
+
+ // loc_4AE75: ; CODE XREF: handleSkipLevelOptionClick+68j
+ char levelNumber[4] = "000";
+ convertNumberTo3DigitStringWithPadding0(gCurrentSelectedLevelIndex, levelNumber);
+
+ char message[24];
+ sprintf(message, "SKIP LEVEL %s ??? ", levelNumber);
+ drawTextWithChars6FontWithOpaqueBackgroundIfPossible(168, 127, 8, message);
+
+ uint16_t mouseX, mouseY;
+ uint16_t mouseButtonStatus;
+
+ do
+ {
+ // loc_4AE89: ; CODE XREF: handleSkipLevelOptionClick+90j
+ getMouseStatus(&mouseX, &mouseY, &mouseButtonStatus);
+ } while (mouseButtonStatus != 0);
+
+ do
+ {
+ // loc_4AE91: ; CODE XREF: handleSkipLevelOptionClick+B4j
+ videoLoop();
+ getMouseStatus(&mouseX, &mouseY, &mouseButtonStatus);
+ gMouseButtonStatus = mouseButtonStatus;
+ gMouseX = mouseX;
+ gMouseY = mouseY;
+ restoreLastMouseAreaBitmap();
+ saveLastMouseAreaBitmap();
+ drawMouseCursor();
+ } while (gMouseButtonStatus == 0);
+
+ ButtonDescriptor okButtonDescriptor = kMainMenuButtonDescriptors[9];
+
+ if (gMouseX >= okButtonDescriptor.startX && gMouseY >= okButtonDescriptor.startY && gMouseX <= okButtonDescriptor.endX && gMouseY <= okButtonDescriptor.endY)
+ {
+ gCurrentPlayerLevelState = PlayerLevelStateSkipped;
+ changePlayerCurrentLevelState(); // 01ED:4275
+ gShouldAutoselectNextLevelToPlay = 0;
+ prepareLevelDataForCurrentPlayer();
+ }
+
+ // loc_4AEE9: ; CODE XREF: handleSkipLevelOptionClick+C3j
+ // ; handleSkipLevelOptionClick+C8j ...
+ restoreLastMouseAreaBitmap();
+ drawTextWithChars6FontWithOpaqueBackgroundIfPossible(168, 127, 8, " ");
+ drawPlayerList();
+ drawLevelList();
+ drawRankings();
+
+ do
+ {
+ // loc_4AF00: ; CODE XREF: handleSkipLevelOptionClick+107j
+ getMouseStatus(&mouseX, &mouseY, &mouseButtonStatus);
+ } while (mouseButtonStatus != 0);
+ saveLastMouseAreaBitmap();
+}
+
+void handleStatisticsOptionClick() // sub_4AF0C proc near
+{
+ PlayerEntry currentPlayerEntry = gPlayerListData[gCurrentPlayerIndex];
+ if (strcmp(currentPlayerEntry.name, "--------") == 0)
+ {
+ drawTextWithChars6FontWithOpaqueBackgroundIfPossible(168, 127, 8, "NO PLAYER SELECTED ");
+ return;
+ }
+
+ // loc_4AFE3: ; CODE XREF: handleStatisticsOptionClick+58j
+ fadeToPalette(gBlackPalette);
+
+ uint8_t *screenPixelsBackup = malloc(kFullScreenFramebufferLength);
+ memcpy(screenPixelsBackup, gScreenPixels, kFullScreenFramebufferLength);
+
+ drawBackBackground();
+
+ byte_5091A = 0;
+ drawTextWithChars6FontWithTransparentBackgroundIfPossible(80, 20, 15, "SUPAPLEX BY DREAM FACTORY");
+ drawTextWithChars6FontWithTransparentBackgroundIfPossible(64, 50, 15, "(C) DIGITAL INTEGRATION LTD 1991");
+ drawTextWithChars6FontWithTransparentBackgroundIfPossible(16, 60, 15, "________________________________________________");
+ drawTextWithChars6FontWithTransparentBackgroundIfPossible(80, 80, 15, "SUPAPLEX PLAYER STATISTICS");
+
+ char currentPlayerText[27] = "";
+ sprintf(currentPlayerText, "CURRENT PLAYER : %s", currentPlayerEntry.name);
+ drawTextWithChars6FontWithTransparentBackgroundIfPossible(80, 100, 15, currentPlayerText);
+
+ if (currentPlayerEntry.nextLevelToPlay == kLastLevelIndex)
+ {
+ byte_5091A = 1;
+ }
+
+ // loc_4B046: ; CODE XREF: handleStatisticsOptionClick+133j
+ char levelNumberString[4] = "000";
+ convertNumberTo3DigitStringWithPadding0(currentPlayerEntry.nextLevelToPlay, levelNumberString);
+
+ char currentLevelText[27] = "";
+ sprintf(currentLevelText, "CURRENT LEVEL : %s", levelNumberString);
+ drawTextWithChars6FontWithTransparentBackgroundIfPossible(80, 110, 15, currentLevelText);
+
+ char secondsNumberString[4] = ":00";
+ char minutesNumberString[4] = ":00";
+ char hoursNumberString[4] = " 0";
+
+ convertNumberTo3DigitStringWithPadding0(currentPlayerEntry.seconds, secondsNumberString);
+ secondsNumberString[0] = ':';
+
+ convertNumberTo3DigitStringWithPadding0(currentPlayerEntry.minutes, minutesNumberString);
+ minutesNumberString[0] = ':';
+
+ convertNumberTo3DigitPaddedString(currentPlayerEntry.hours, hoursNumberString, 1);
+
+ char usedTimeText[27] = "";
+ sprintf(usedTimeText, "USED TIME : %s%s%s", hoursNumberString, minutesNumberString, secondsNumberString);
+
+ drawTextWithChars6FontWithTransparentBackgroundIfPossible(80, 120, 15, usedTimeText);
+
+ uint32_t totalMinutes = currentPlayerEntry.hours * 60 + currentPlayerEntry.minutes;
+
+ if (currentPlayerEntry.seconds >= 30)
+ {
+ totalMinutes++;
+ }
+
+ // loc_4B0A1: ; CODE XREF: handleStatisticsOptionClick+192j
+
+ char averageTimeString[6] = "000.0";
+ uint16_t averageMinutesWhole = totalMinutes / currentPlayerEntry.nextLevelToPlay;
+ uint16_t averageMinutesFraction = (totalMinutes % currentPlayerEntry.nextLevelToPlay);
+ averageMinutesFraction = averageMinutesFraction * 10 / currentPlayerEntry.nextLevelToPlay;
+ convertNumberTo3DigitStringWithPadding0(averageMinutesFraction, &averageTimeString[2]);
+
+ if (averageMinutesWhole == 0)
+ {
+ byte_5091A = 2;
+ }
+
+ // loc_4B0C2: ; CODE XREF: handleStatisticsOptionClick+1AFj
+ averageTimeString[3] = '.';
+
+ convertNumberTo3DigitPaddedString(averageMinutesWhole, averageTimeString, 1);
+ if (byte_5091A == 1)
+ {
+ drawTextWithChars6FontWithTransparentBackgroundIfPossible(24, 140, 15, "YOU'VE COMPLETED ALL LEVELS! CONGRATULATIONS!!!");
+ }
+ // loc_4B0E2: ; CODE XREF: handleStatisticsOptionClick+1C7j
+ else if (byte_5091A == 2)
+ {
+ drawTextWithChars6FontWithTransparentBackgroundIfPossible(40, 140, 15, "STILL UNDER ONE MINUTE (KEEP IT UP...)");
+ }
+ // loc_4B0F6: ; CODE XREF: handleStatisticsOptionClick+1DBj
+ else
+ {
+ char averageTimeMessage[44] = "";
+ sprintf(averageTimeMessage, "AVERAGE TIME USED PER LEVEL %s MINUTES", averageTimeString);
+ drawTextWithChars6FontWithTransparentBackgroundIfPossible(32, 140, 15, averageTimeMessage);
+ }
+ // loc_4B105: ; CODE XREF: handleStatisticsOptionClick+1D4j
+ // ; handleStatisticsOptionClick+1E8j
+ fadeToPalette(gInformationScreenPalette);
+ waitForKeyMouseOrJoystick();
+ fadeToPalette(gBlackPalette);
+ memcpy(gScreenPixels, screenPixelsBackup, kFullScreenFramebufferLength);
+ fadeToPalette(gGamePalette);
+
+ free(screenPixelsBackup);
+}
+
+void handleGfxTutorOptionClick() // sub_4B149 proc near
+{
+ drawGfxTutorBackground(gScrollDestinationScreenBitmapData);
+ scrollRightToNewScreen();
+ waitForKeyMouseOrJoystick();
+ scrollLeftToMainMenu();
+ drawMenuTitleAndDemoLevelResult();
+}
+
+void handleDemoOptionClick() // sub_4B159 proc near ; CODE XREF: runMainMenu+6Fp
+{
+ // 01ED:44F6
+ if (readDemoFiles() == 0)
+ {
+ return;
+ }
+
+ // loc_4B163: ; CODE XREF: handleDemoOptionClick+5j
+ gShouldLeaveMainMenu = 1;
+ gIsPlayingDemo = 1;
+
+ uint8_t numberOfDemos = 0;
+
+ uint8_t idx = 0;
+ do
+ {
+ // loc_4B17A: ; CODE XREF: handleDemoOptionClick+2Dj
+ if (gDemos.demoFirstIndices[idx] == 0xFFFF)
+ {
+ break;
+ }
+ idx++;
+ numberOfDemos++;
+ } while (1);
+ // 01ED:4525
+
+ // loc_4B188: ; CODE XREF: handleDemoOptionClick+2Aj
+ // This picks a random demo
+ generateRandomSeedFromClock();
+ uint16_t demoIndex = generateRandomNumber() % numberOfDemos;
+ uint16_t demoFirstIndex = gDemos.demoFirstIndices[demoIndex];
+
+ // This only happens if there are no demos...
+ if (demoFirstIndex == 0xFFFF)
+ {
+ gShouldLeaveMainMenu = 0;
+ gIsPlayingDemo = 0;
+ }
+
+ // loc_4B1AE: ; CODE XREF: handleDemoOptionClick+48j
+ uint8_t demoLevelNumber = gDemos.demoData[demoFirstIndex];
+ uint8_t finalLevelNumber = demoIndex;
+
+ gSelectedOriginalDemoIndex = demoIndex;
+ gSelectedOriginalDemoLevelNumber = 0;
+
+ // This checks if the level number has its MSB to 0 and is a valid level number (1-111) for the original DEMO format
+ if (demoLevelNumber <= 0x6F // 111
+ && demoLevelNumber != 0)
+ {
+ gSelectedOriginalDemoLevelNumber = (gSelectedOriginalDemoLevelNumber & 0xFF00) | demoLevelNumber; // mov byte ptr gSelectedOriginalDemoLevelNumber, al
+ finalLevelNumber = demoLevelNumber;
+ }
+
+ // loc_4B1CF: ; CODE XREF: handleDemoOptionClick+6Bj
+ // ; handleDemoOptionClick+6Fj
+ gRandomGeneratorSeed = gDemoRandomSeeds[demoIndex];
+ gDemoIndexOrDemoLevelNumber = finalLevelNumber;
+
+ demoFirstIndex++; // To skip the level number
+ gDemoCurrentInputIndex = demoFirstIndex;
+ word_5A33C = demoFirstIndex;
+ gDemoCurrentInput = UserInputNone;
+ gDemoCurrentInputRepeatCounter = 1;
+}
+
+void playDemo(uint16_t demoIndex) // demoSomething proc near ; CODE XREF: start+3BAp
+ // ; runMainMenu+12Ep ...
+{
+ readDemoFiles();
+
+ gRandomGeneratorSeed = gDemoRandomSeeds[demoIndex];
+ gShouldLeaveMainMenu = 1;
+ gIsPlayingDemo = 1;
+
+ uint16_t demoFirstIndex = gDemos.demoFirstIndices[demoIndex];
+ if (demoFirstIndex == 0xFFFF)
+ {
+ gShouldLeaveMainMenu = 0;
+ gIsPlayingDemo = 0;
+ }
+
+ // loc_4B22F: ; CODE XREF: playDemo+30j
+ gSelectedOriginalDemoLevelNumber = 0;
+
+ uint8_t demoLevelNumber = gDemos.demoData[demoFirstIndex];
+ uint8_t finalLevelNumber = demoIndex;
+
+ if (demoLevelNumber <= kNumberOfLevels // 111
+ && demoLevelNumber != 0)
+ {
+ finalLevelNumber = demoLevelNumber;
+ gSelectedOriginalDemoLevelNumber = (gSelectedOriginalDemoLevelNumber & 0xFF00) | finalLevelNumber; // mov byte ptr gSelectedOriginalDemoLevelNumber, al
+ }
+
+ // loc_4B248: ; CODE XREF: playDemo+4Bj
+ // ; playDemo+4Fj
+ gDemoIndexOrDemoLevelNumber = finalLevelNumber;
+
+ demoFirstIndex++; // To skip the level number
+ gDemoCurrentInputIndex = demoFirstIndex;
+ word_5A33C = demoFirstIndex;
+ gDemoCurrentInput = UserInputNone;
+ gDemoCurrentInputRepeatCounter = 1;
+}
+
+void handleRankingListScrollUp() // loc_4B262
+{
+ gRankingListButtonPressed = 1;
+ gRankingListDownButtonPressed = 0;
+ gRankingListUpButtonPressed = 1;
+
+ if (gFrameCounter - gRankingListThrottleCurrentCounter < gRankingListThrottleNextCounter)
+ {
+ return;
+ }
+
+ // loc_4B27F: ; CODE XREF: code:465Cj
+ restoreLastMouseAreaBitmap();
+ gRankingListThrottleNextCounter = gFrameCounter;
+ if (gRankingListThrottleCurrentCounter > 1)
+ {
+ gRankingListThrottleCurrentCounter--;
+ }
+
+ // loc_4B293: ; CODE XREF: code:466Dj
+ if (gIsForcedCheatMode == 0 && byte_58D46 > 0)
+ {
+ byte_58D46--;
+ }
+
+ // loc_4B2A5: ; CODE XREF: code:4678j code:467Fj
+ drawRankings();
+ saveLastMouseAreaBitmap();
+ drawMouseCursor();
+}
+
+void handleRankingListScrollDown() // loc_4B2AF
+{
+ gRankingListButtonPressed = 1;
+ gRankingListDownButtonPressed = 1;
+ gRankingListUpButtonPressed = 0;
+
+ if (gFrameCounter - gRankingListThrottleCurrentCounter < gRankingListThrottleNextCounter)
+ {
+ return;
+ }
+
+ // loc_4B2CC: ; CODE XREF: code:46A9j
+ restoreLastMouseAreaBitmap();
+ gRankingListThrottleNextCounter = gFrameCounter;
+ if (gRankingListThrottleCurrentCounter > 1)
+ {
+ gRankingListThrottleCurrentCounter--;
+ }
+
+ // loc_4B2E0: ; CODE XREF: code:46BAj
+ if (gIsForcedCheatMode == 0 && byte_58D46 < kNumberOfPlayers - 1)
+ {
+ byte_58D46++;
+ }
+
+ // loc_4B2F2: ; CODE XREF: code:46C5j code:46CCj
+ drawRankings();
+ saveLastMouseAreaBitmap();
+ drawMouseCursor();
+}
+
+void showCongratulationsScreen() // sub_4B2FC proc near ; CODE XREF: handleOkButtonClick+56p
+{
+ fadeToPalette(gBlackPalette);
+
+ uint8_t *screenPixelsBackup = malloc(kFullScreenFramebufferLength);
+ memcpy(screenPixelsBackup, gScreenPixels, kFullScreenFramebufferLength);
+
+ drawBackBackground();
+ drawTextWithChars6FontWithTransparentBackgroundIfPossible(120, 30, 15, "CONGRATULATIONS");
+ drawTextWithChars6FontWithTransparentBackgroundIfPossible(24, 70, 15, "YOU HAVE COMPLETED ALL 111 LEVELS OF SUPAPLEX");
+ drawTextWithChars6FontWithTransparentBackgroundIfPossible(64, 85, 15, "YOUR BRAIN IS IN FANTASTIC SHAPE");
+ drawTextWithChars6FontWithTransparentBackgroundIfPossible(40, 100, 15, "NOT MANY PEOPLE ARE ABLE TO MANAGE THIS");
+ fadeToPalette(gInformationScreenPalette);
+ waitForKeyMouseOrJoystick();
+ fadeToPalette(gBlackPalette);
+ memcpy(gScreenPixels, screenPixelsBackup, kFullScreenFramebufferLength);
+
+ fadeToPalette(gGamePalette); // 6015h
+
+ free(screenPixelsBackup);
+}
+
+void handleOkButtonClick() // sub_4B375 proc near ; CODE XREF: runMainMenu+11Ep
+{
+ // 01ED:4712
+ PlayerEntry currentPlayerEntry = gPlayerListData[gCurrentPlayerIndex];
+
+ if (strcmp(currentPlayerEntry.name, "--------") == 0)
+ {
+ drawTextWithChars6FontWithOpaqueBackgroundIfPossible(168, 127, 8, "NO PLAYER SELECTED ");
+ return;
+ }
+
+ // loc_4B3A4: ; CODE XREF: handleOkButtonClick+12j
+ // ; handleOkButtonClick+17j ...
+ if (gCurrentSelectedLevelIndex == kLastLevelIndex)
+ {
+ // loc_4B3B4: ; CODE XREF: handleOkButtonClick+3Cj
+ uint8_t numberOfCompletedLevels = 0;
+
+ for (int i = 0; i < kNumberOfLevels; ++i)
+ {
+ // loc_4B3BC: ; CODE XREF: handleOkButtonClick+4Fj
+ if (currentPlayerEntry.levelState[i] == PlayerLevelStateCompleted)
+ {
+ numberOfCompletedLevels++;
+ }
+ // loc_4B3C3: ; CODE XREF: handleOkButtonClick+4Aj
+ }
+ if (numberOfCompletedLevels == kNumberOfLevels)
+ {
+ showCongratulationsScreen();
+ return;
+ }
+ else
+ {
+ // loc_4B3CF: ; CODE XREF: handleOkButtonClick+54j
+ drawTextWithChars6FontWithOpaqueBackgroundIfPossible(168, 127, 2, "COLORBLIND I GUESS ");
+ return;
+ }
+ }
+ else if (gCurrentSelectedLevelIndex > kNumberOfLevels)
+ {
+ return;
+ }
+
+ // loc_4B3DB: ; CODE XREF: handleOkButtonClick+35j
+ uint8_t currentLevelColor = gCurrentPlayerLevelData[gCurrentSelectedLevelIndex - 1];
+
+ if (currentLevelColor == kBlockedLevelEntryColor)
+ {
+ // loc_4B404: ; CODE XREF: handleOkButtonClick+70j
+ drawTextWithChars6FontWithOpaqueBackgroundIfPossible(168, 127, 8, "COLORBLIND I GUESS ");
+ return;
+ }
+ gShouldLeaveMainMenu = 1;
+ gIsPlayingDemo = 0;
+
+ if (currentLevelColor == kCompletedLevelEntryColor)
+ {
+ gShouldUpdateTotalLevelTime = 0;
+ }
+ else
+ {
+ // loc_4B3FD: ; CODE XREF: handleOkButtonClick+7Fj
+ gShouldUpdateTotalLevelTime = 1;
+ }
+
+ // loc_4B40F: ; CODE XREF: handleOkButtonClick+86j
+ // ; handleOkButtonClick+8Dj
+ prepareDemoRecordingFilename(); // 01ED:47AC
+ convertLevelNumberTo3DigitStringWithPadding0(gCurrentSelectedLevelIndex); // 01ED:47B2
+}
+
+void throttledRotateLevelSet(uint8_t descending) // sub_4B419 proc near
+{
+ // 01ED:47B6
+ // loc_4B433: ; CODE XREF: sub_4B419+15j
+ if (gFrameCounter - gLevelSetRotationThrottleCurrentCounter < gLevelSetRotationThrottleNextCounter)
+ {
+ return;
+ }
+
+ // loc_4B443: ; CODE XREF: sub_4B419+25j
+ gLevelSetRotationThrottleNextCounter = gFrameCounter;
+ if (gLevelSetRotationThrottleCurrentCounter > 1)
+ {
+ gLevelSetRotationThrottleCurrentCounter--;
+ }
+
+ rotateLevelSet(descending);
+}
+
+void rotateLevelSet(uint8_t descending) // sub_4B419 proc near
+{
+ FILE *file = NULL;
+ char currentSuffix[3] = "AT";
+
+ do
+ {
+ // loc_4B454: ; CODE XREF: sub_4B419+35j
+ // ; sub_4B419+9Aj
+ strcpy(currentSuffix, &gLevelsDatFilename[8]);
+
+ if (descending)
+ {
+ // loc_4B482: ; CODE XREF: sub_4B419+46j
+ if (strcmp(currentSuffix, "AT") == 0) // "AT"
+ {
+ strcpy(currentSuffix, "99");
+ }
+ // loc_4B48C: ; CODE XREF: sub_4B419+6Cj
+ else if (strcmp(currentSuffix, "00") == 0) // "00"
+ {
+ strcpy(currentSuffix, "AT");
+ }
+ else
+ {
+ // loc_4B496: ; CODE XREF: sub_4B419+76j
+ currentSuffix[1]--;
+ if (currentSuffix[1] < '0' && currentSuffix[0] > '0')
+ {
+ currentSuffix[1] = '9'; // '9'
+ currentSuffix[0]--;
+ }
+ }
+ }
+ else
+ {
+ if (strcmp(currentSuffix, "AT") == 0) // "AT"
+ {
+ strcpy(currentSuffix, "00");
+ }
+ // loc_4B46B: ; CODE XREF: sub_4B419+4Bj
+ else if (strcmp(currentSuffix, "99") == 0) // "99"
+ {
+ strcpy(currentSuffix, "AT");
+ }
+ else
+ {
+ // loc_4B475: ; CODE XREF: sub_4B419+55j
+ currentSuffix[1]++;
+ if (currentSuffix[1] > '9' && currentSuffix[0] < '9')
+ {
+ currentSuffix[1] = '0'; // '0'
+ currentSuffix[0]++;
+ }
+ }
+ }
+
+ // loc_4B4A3: ; CODE XREF: sub_4B419+50j
+ // ; sub_4B419+5Aj ...
+ strcpy(&gLevelsDatFilename[8], currentSuffix);
+
+ file = openReadonlyFile(gLevelsDatFilename, "rb");
+ if (file == NULL)
+ {
+ if (errno != ENOENT)
+ {
+ exitWithError("Error opening %s\n", gLevelsDatFilename);
+ }
+ }
+ } while (file == NULL);
+
+ // loc_4B4B8: ; CODE XREF: sub_4B419+95j
+ if (fclose(file) != 0)
+ {
+ exitWithError("Error closing %s\n", gLevelsDatFilename);
+ }
+
+ if (strcmp(currentSuffix, "AT") == 0)
+ {
+ strcpy(currentSuffix, "ST");
+ }
+
+ // loc_4B4D3: ; CODE XREF: sub_4B419+B6j
+ strcpy(&gLevelLstFilename[7], currentSuffix);
+ strcpy(&gPlayerLstFilename[8], currentSuffix);
+ strcpy(&gHallfameLstFilename[0xA], currentSuffix);
+
+ if (strcmp(currentSuffix, "ST") == 0)
+ {
+ strcpy(currentSuffix, "IN");
+ }
+
+ // loc_4B4E4: ; CODE XREF: sub_4B419+C6j
+ strcpy(&gDemo0BinFilename[7], currentSuffix);
+
+ if (strcmp(currentSuffix, "IN") == 0)
+ {
+ strcpy(currentSuffix, "AV");
+ }
+
+ // loc_4B4EF: ; CODE XREF: sub_4B419+D1j
+ if (gShouldAlwaysWriteSavegameSav == 0)
+ {
+ strcpy(&gSavegameSavFilename[0xA], currentSuffix);
+ }
+
+ // loc_4B504: ; CODE XREF: sub_4B419+E6j
+ readLevelsLst();
+ readDemoFiles();
+
+ // 01ED:48B2
+ if (gIsForcedCheatMode != 0)
+ {
+ PlayerEntry *entry = &gPlayerListData[0];
+ memset(entry->levelState, PlayerLevelStateSkipped, sizeof(entry->levelState));
+ }
+ else
+ {
+ // loc_4B52A: ; CODE XREF: sub_4B419+101j
+ for (int i = 0; i < kNumberOfPlayers; ++i)
+ {
+ PlayerEntry *entry = &gPlayerListData[i];
+ // loc_4B531: ; CODE XREF: sub_4B419+129j
+ memset(entry, 0, sizeof(PlayerEntry));
+ strcpy(entry->name, "--------");
+ }
+
+ for (int i = 0; i < kNumberOfHallOfFameEntries; ++i)
+ {
+ HallOfFameEntry *entry = &gHallOfFameData[i];
+
+ // loc_4B54B: ; CODE XREF: sub_4B419+143j
+ memset(entry, 0, sizeof(HallOfFameEntry));
+ strcpy(entry->playerName, " ");
+ }
+
+ readHallfameLst();
+ readPlayersLst();
+ }
+
+ updateMenuAfterLevelSetChanged();
+}
+
+void updateMenuAfterLevelSetChanged() // loc_4B565: ; CODE XREF: sub_4B419+10Fj
+{
+ // loc_4B4C6: ; CODE XREF: sub_4B419+A8j
+ char message[] = " LEVEL SET ?? ";
+ char currentSuffix[3] = "AT";
+ strcpy(currentSuffix, &gLevelsDatFilename[8]);
+
+ memcpy(&message[0xF], currentSuffix, 2);
+
+ // loc_4B4F9: ; CODE XREF: sub_4B419+DBj
+ if (strcmp(currentSuffix, "AT") == 0)
+ {
+ strcpy(message, " SUPAPLEX LEVEL SET ");
+ }
+
+ drawTextWithChars6FontWithOpaqueBackgroundIfPossible(168, 127, 4, message);
+
+ gShouldAutoselectNextLevelToPlay = 1;
+ prepareLevelDataForCurrentPlayer();
+ drawPlayerList();
+ drawLevelList();
+ drawHallOfFame();
+ drawRankings();
+ restoreLastMouseAreaBitmap();
+ saveLastMouseAreaBitmap();
+ drawMouseCursor();
+}
+
+void handleFloppyDiskButtonClick()
+{
+ // Pressing the shift key will show the level sets in descending order
+ throttledRotateLevelSet(gIsRightShiftPressed || gIsLeftShiftPressed);
+}
+
+void handlePlayerListScrollDown() // sub_4B671 proc near
+{
+ gPlayerListButtonPressed = 1;
+ gPlayerListDownButtonPressed = 1;
+ gPlayerListUpButtonPressed = 0;
+
+ if (gFrameCounter - gPlayerListThrottleCurrentCounter < gPlayerListThrottleNextCounter)
+ {
+ return;
+ }
+
+ // loc_4B68E: ; CODE XREF: handlePlayerListScrollDown+1Aj
+ gPlayerListThrottleNextCounter = gFrameCounter;
+ if (gPlayerListThrottleCurrentCounter > 1)
+ {
+ gPlayerListThrottleCurrentCounter--;
+ }
+
+ // loc_4B69F: ; CODE XREF: handlePlayerListScrollDown+28j
+ if (gIsForcedCheatMode == 0 && gCurrentPlayerIndex < kNumberOfPlayers - 1)
+ {
+ gCurrentPlayerIndex++;
+ }
+
+ // loc_4B6B1: ; CODE XREF: handlePlayerListScrollDown+33j
+ // ; handlePlayerListScrollDown+3Aj
+ restoreLastMouseAreaBitmap();
+ gShouldAutoselectNextLevelToPlay = 1;
+ prepareLevelDataForCurrentPlayer();
+ drawPlayerList();
+ drawLevelList();
+ saveLastMouseAreaBitmap();
+ drawMouseCursor();
+}
+
+void handlePlayerListScrollUp() // sub_4B6C9 proc near
+{
+ gPlayerListButtonPressed = 1;
+ gPlayerListDownButtonPressed = 0;
+ gPlayerListUpButtonPressed = 1;
+
+ if (gFrameCounter - gPlayerListThrottleCurrentCounter < gPlayerListThrottleNextCounter)
+ {
+ return;
+ }
+
+ // loc_4B6E6: ; CODE XREF: handlePlayerListScrollUp+1Aj
+ gPlayerListThrottleNextCounter = gFrameCounter;
+ if (gPlayerListThrottleCurrentCounter > 1)
+ {
+ gPlayerListThrottleCurrentCounter--;
+ }
+
+ // loc_4B6F7: ; CODE XREF: handlePlayerListScrollUp+28j
+ if (gIsForcedCheatMode == 0 && gCurrentPlayerIndex > 0)
+ {
+ gCurrentPlayerIndex--;
+ }
+
+ // loc_4B709: ; CODE XREF: handlePlayerListScrollUp+33j
+ // ; handlePlayerListScrollUp+3Aj
+ restoreLastMouseAreaBitmap(); // Clears mouse trail
+ gShouldAutoselectNextLevelToPlay = 1;
+ prepareLevelDataForCurrentPlayer();
+ drawPlayerList();
+ drawLevelList();
+ saveLastMouseAreaBitmap();
+ drawMouseCursor();
+}
+
+void handlePlayerListClick() // sub_4B721 proc near
+{
+ byte_58D46 = byte_58D47;
+ drawRankings();
+}
+
+void handleLevelListScrollDown() // sub_4B72B proc near
+{
+ gLevelListButtonPressed = 1;
+ gLevelListDownButtonPressed = 1;
+ gLevelListUpButtonPressed = 0;
+
+ if (gFrameCounter - gLevelListThrottleCurrentCounter < gLevelListThrottleNextCounter)
+ {
+ return;
+ }
+
+ // loc_4B748: ; CODE XREF: handleLevelListScrollDown+1Aj
+ gLevelListThrottleNextCounter = gFrameCounter;
+ if (gLevelListThrottleCurrentCounter > 1)
+ {
+ gLevelListThrottleCurrentCounter--;
+ }
+
+ // loc_4B759: ; CODE XREF: handleLevelListScrollDown+28j
+ if (gCurrentSelectedLevelIndex >= 113)
+ {
+ return;
+ }
+ gCurrentSelectedLevelIndex++;
+ restoreLastMouseAreaBitmap();
+ drawLevelList();
+ saveLastMouseAreaBitmap();
+ drawMouseCursor();
+}
+
+void handleLevelListScrollUp() // sub_4B771 proc near
+{
+ gLevelListButtonPressed = 1;
+ gLevelListDownButtonPressed = 0;
+ gLevelListUpButtonPressed = 1;
+
+ if (gFrameCounter - gLevelListThrottleCurrentCounter < gLevelListThrottleNextCounter)
+ {
+ return;
+ }
+
+ // loc_4B78E: ; CODE XREF: handleLevelListScrollUp+1Aj
+ gLevelListThrottleNextCounter = gFrameCounter;
+ if (gLevelListThrottleCurrentCounter > 1)
+ {
+ gLevelListThrottleCurrentCounter--;
+ }
+
+ // loc_4B79F: ; CODE XREF: handleLevelListScrollUp+28j
+ if (gCurrentSelectedLevelIndex <= 1)
+ {
+ return;
+ }
+ gCurrentSelectedLevelIndex--;
+ restoreLastMouseAreaBitmap();
+ drawLevelList();
+ saveLastMouseAreaBitmap();
+ drawMouseCursor();
+ // locret_4B7B6: ; CODE XREF: handleLevelListScrollUp+33j
+}
+
+void handleLevelCreditsClick() // sub_4B7B7 proc near
+{
+ fadeToPalette(gBlackPalette);
+
+ uint8_t *screenPixelsBackup = malloc(kFullScreenFramebufferLength);
+ memcpy(screenPixelsBackup, gScreenPixels, kFullScreenFramebufferLength);
+
+ drawBackBackground();
+
+ drawTextWithChars6FontWithTransparentBackgroundIfPossible(80, 10, 15, "SUPAPLEX BY DREAM FACTORY");
+ drawTextWithChars6FontWithTransparentBackgroundIfPossible(56, 40, 15, "ORIGINAL DESIGN BY PHILIP JESPERSEN");
+ drawTextWithChars6FontWithTransparentBackgroundIfPossible(88, 50, 15, "AND MICHAEL STOPP");
+ drawTextWithChars6FontWithTransparentBackgroundIfPossible(56, 90, 15, "NEARLY ALL LEVELS BY MICHEAL STOPP");
+ drawTextWithChars6FontWithTransparentBackgroundIfPossible(64, 100, 15, "A FEW LEVELS BY PHILIP JESPERSEN");
+ drawTextWithChars6FontWithTransparentBackgroundIfPossible(56, 110, 15, "HARDLY ANY LEVELS BY BARBARA STOPP");
+ drawTextWithChars6FontWithTransparentBackgroundIfPossible(64, 170, 15, "NOTE: PRESS ENTER TO REMOVE PANEL");
+ drawTextWithChars6FontWithTransparentBackgroundIfPossible(64, 190, 15, "(C) DIGITAL INTEGRATION LTD 1991");
+ fadeToPalette(gInformationScreenPalette);
+ waitForKeyMouseOrJoystick();
+ fadeToPalette(gBlackPalette);
+ memcpy(gScreenPixels, screenPixelsBackup, kFullScreenFramebufferLength);
+ fadeToPalette(gGamePalette);
+
+ free(screenPixelsBackup);
+}
+
+void drawTextWithChars6FontWithOpaqueBackgroundIfPossible(size_t destX, size_t destY, uint8_t color, const char *text) // sub_4BA5F proc near ; CODE XREF: handleNewPlayerOptionClick+37p
+ // ; handleNewPlayerOptionClick+4Ap ...
+{
+ // Parameters:
+ // - di is the destination surface
+ // - si is the text to be rendered
+ // - ah is the color index in the current palette
+
+ // Address: 01ED:4DFC
+ if (gIsGameBusy == 1)
+ {
+ return;
+ }
+
+ drawTextWithChars6FontWithOpaqueBackground(destX, destY, color, text);
+}
+
+void drawTextWithChars6FontWithTransparentBackgroundIfPossible(size_t destX, size_t destY, uint8_t color, const char *text) // sub_4BDF0 proc near ; CODE XREF: recoverFilesFromFloppyDisk+2Ap
+ // ; handleStatisticsOptionClick+EDp ...
+{
+ if (gIsGameBusy == 1)
+ {
+ return;
+ }
+
+ drawTextWithChars6FontWithTransparentBackground(destX, destY, color, text);
+}
+
+void convertLevelNumberTo3DigitStringWithPadding0(uint8_t number) // sub_4BF4A proc near ; CODE XREF: start+3F7p handleGameUserInput+398p ...
+{
+ convertNumberTo3DigitStringWithPadding0(number, &gSPDemoFileName[3]);
+}
+
+void convertNumberTo3DigitStringWithPadding0(uint8_t number, char numberString[3]) // proc near ; CODE XREF: handleSkipLevelOptionClick+7Cp
+ // ; handleStatisticsOptionClick+13Dp ...
+{
+ convertNumberTo3DigitPaddedString(number, numberString, 0);
+}
+
+void convertNumberTo3DigitPaddedString(uint8_t number, char numberString[3], char useSpacesForPadding) // sub_4BF4F proc near ; CODE XREF: handleStatisticsOptionClick+16Fp
+ // ; handleStatisticsOptionClick+1BFp ...
+{
+ // This function converts a number to a 3-digit string, so basically 123 to "123".
+ // It also adds padding to the left, so 7 is converted to "007", with the option
+ // to turn 0's in padding into spaces: 7 to " 7"
+ // Parameters in the original implementation:
+ // - al: number to convert
+ // - ah: can be ' ' (space) or anything else. When it's ' ', the padding will be done with spaces, otherwise 0's will be used.
+ // - si: the string to write the result
+
+ numberString[0] = (number / 100) + '0';
+ numberString[1] = ((number % 100) / 10) + '0';
+ numberString[2] = (number % 10) + '0';
+
+ if (useSpacesForPadding)
+ {
+ if (numberString[0] == '0')
+ {
+ numberString[0] = ' ';
+
+ if (numberString[1] == '0')
+ {
+ numberString[1] = ' ';
+ }
+ }
+ }
+}
+
+void prepareRankingTextEntries() // sub_4BF8D proc near ; CODE XREF: drawRankingsp
+{
+ // 01ED:532A
+ if (gIsForcedCheatMode != 0)
+ {
+ return;
+ }
+
+ typedef struct
+ {
+ uint8_t playerIndex;
+ uint8_t nextLevelToPlay;
+ uint8_t hours;
+ uint8_t minutes;
+ uint8_t seconds;
+ } RankingEntry;
+
+ RankingEntry rankingEntries[kNumberOfPlayers];
+
+ for (int i = 0; i < 20; ++i)
+ {
+ // loc_4BFA2: ; CODE XREF: prepareRankingTextEntries+38j
+ RankingEntry *rankingEntry = &rankingEntries[i];
+ PlayerEntry *player = &gPlayerListData[i];
+
+ rankingEntry->playerIndex = i;
+ rankingEntry->nextLevelToPlay = player->nextLevelToPlay;
+ rankingEntry->hours = player->hours;
+ rankingEntry->minutes = player->minutes;
+ rankingEntry->seconds = player->seconds;
+ }
+
+ // loc_4BFC7: ; CODE XREF: prepareRankingTextEntries+B4j
+ uint8_t numberOfChanges = 0;
+
+ do
+ {
+ numberOfChanges = 0;
+
+ for (int i = 0; i < kNumberOfPlayers - 1; ++i)
+ {
+ // loc_4BFD3: ; CODE XREF: prepareRankingTextEntries+AFj
+ RankingEntry *rankingEntry = &rankingEntries[i];
+ RankingEntry *nextRankingEntry = &rankingEntries[i + 1];
+
+ uint32_t totalSeconds = rankingEntry->hours * 3600 + rankingEntry->minutes * 60 + rankingEntry->seconds;
+ uint32_t nextTotalSeconds = rankingEntry->hours * 3600 + rankingEntry->minutes * 60 + rankingEntry->seconds;
+
+ if (nextRankingEntry->nextLevelToPlay > rankingEntry->nextLevelToPlay || (nextRankingEntry->nextLevelToPlay == rankingEntry->nextLevelToPlay && nextTotalSeconds > totalSeconds))
+ {
+ // loc_4BFFD: ; CODE XREF: prepareRankingTextEntries+4Ej
+ // ; prepareRankingTextEntries+58j ...
+ RankingEntry aux = *nextRankingEntry;
+ *nextRankingEntry = *rankingEntry;
+ *rankingEntry = aux;
+ numberOfChanges++;
+ }
+ }
+ } while (numberOfChanges > 0);
+
+ for (int i = 0; i < 20; ++i)
+ {
+ // loc_4C04B: ; CODE XREF: prepareRankingTextEntries+CFj
+ if (rankingEntries[i].playerIndex == gCurrentPlayerIndex)
+ {
+ byte_58D47 = i;
+ }
+ }
+
+ // loc_4C061:
+ // di = 0x883C; <- entry 2 of gRankingTextEntries
+
+ for (int i = 0; i < 20; ++i)
+ {
+ RankingEntry *rankingEntry = &rankingEntries[i];
+ char *textEntry = gRankingTextEntries[i + 2]; // No idea why the first two are always empty
+ // loc_4C067: ; CODE XREF: prepareRankingTextEntries+14Dj
+ if (rankingEntry->nextLevelToPlay == 0x71) // 113
+ {
+ textEntry[0] =
+ textEntry[1] =
+ textEntry[2] = '9';
+ }
+ else
+ {
+ // loc_4C078: ; CODE XREF: prepareRankingTextEntries+DFj
+ convertNumberTo3DigitPaddedString(rankingEntry->nextLevelToPlay, textEntry, 0);
+ }
+
+ // loc_4C07F: ; CODE XREF: prepareRankingTextEntries+E9j
+ PlayerEntry playerEntry = gPlayerListData[rankingEntry->playerIndex];
+
+ // loc_4C091: ; CODE XREF: prepareRankingTextEntries+10Bj
+ memcpy(&textEntry[4], playerEntry.name, sizeof(playerEntry.name) - 1);
+
+ convertNumberTo3DigitStringWithPadding0(rankingEntry->seconds, &textEntry[19]);
+ textEntry[19] = ':';
+
+ convertNumberTo3DigitStringWithPadding0(rankingEntry->minutes, &textEntry[16]);
+ textEntry[16] = ':';
+
+ convertNumberTo3DigitStringWithPadding0(rankingEntry->hours, &textEntry[13]);
+ }
+}
+
+void drawRankings() // sub_4C0DD proc near ; CODE XREF: handleNewPlayerOptionClick+1E9p
+// ; handleDeletePlayerOptionClick+E2p ...
+{
+ // 01ED:547A
+ prepareRankingTextEntries();
+
+ const uint8_t kDistanceBetweenLines = 9;
+
+ for (int i = 0; i < 5; ++i)
+ {
+ const uint8_t y = 110 + kDistanceBetweenLines * (i - 2);
+ const uint8_t color = (i == 2 ? 6 : 8);
+ drawTextWithChars6FontWithOpaqueBackgroundIfPossible(8, y, color, gRankingTextEntries[byte_58D46 + i]);
+ }
+
+ char numberString[4] = "001"; // 0x8359
+ convertNumberTo3DigitStringWithPadding0(byte_58D46 + 1, numberString);
+ drawTextWithChars6FontWithOpaqueBackgroundIfPossible(144, 110, 6, &numberString[1]); // Remove the first (left most) digit
+}
+
+void drawLevelList() // sub_4C141 proc near ; CODE XREF: start+41Ap handleGameUserInput+39Bp ...
+{
+ // 01ED:54DE
+ byte_59821 = gCurrentPlayerLevelData[gCurrentSelectedLevelIndex - 2];
+ byte_59822 = gCurrentPlayerLevelData[gCurrentSelectedLevelIndex - 1];
+ byte_59823 = gCurrentPlayerLevelData[gCurrentSelectedLevelIndex];
+
+ char *previousLevelName = (char *)&gLevelListData[(gCurrentSelectedLevelIndex - 2) * kListLevelNameLength];
+ drawTextWithChars6FontWithOpaqueBackgroundIfPossible(144, 155, byte_59821, previousLevelName);
+
+ char *currentLevelName = (char *)&gLevelListData[(gCurrentSelectedLevelIndex - 1) * kListLevelNameLength];
+ drawTextWithChars6FontWithOpaqueBackgroundIfPossible(144, 164, byte_59822, currentLevelName);
+
+ memcpy(gCurrentLevelName, currentLevelName, kListLevelNameLength);
+
+ char *nextLevelName = (char *)&gLevelListData[gCurrentSelectedLevelIndex * kListLevelNameLength];
+ drawTextWithChars6FontWithOpaqueBackgroundIfPossible(144, 173, byte_59823, nextLevelName);
+}
+
+void drawHallOfFame() // sub_4C1A9 proc near ; CODE XREF: handleFloppyDiskButtonClick+15Ap
+// ; drawMenuTitleAndDemoLevelResult+11p
+{
+ // 01ED:5546
+ char text[19] = {'\0'};
+
+ for (int i = 0; i < kNumberOfHallOfFameEntries; ++i)
+ {
+ // loc_4C1B7: ; CODE XREF: drawHallOfFame+56j
+ strcpy(text, " ");
+ HallOfFameEntry entry = gHallOfFameData[i];
+
+ convertNumberTo3DigitStringWithPadding0(entry.seconds, &text[15]);
+ text[15] = ':';
+
+ convertNumberTo3DigitStringWithPadding0(entry.minutes, &text[12]);
+ text[12] = ':';
+
+ convertNumberTo3DigitPaddedString(entry.hours, &text[9], 1);
+
+ uint8_t playerNameLength = MIN(strlen(entry.playerName), sizeof(entry.playerName) - 1);
+ memcpy(text, entry.playerName, playerNameLength);
+
+ drawTextWithChars6FontWithOpaqueBackgroundIfPossible(184, 28 + i * 9, 8, text);
+ }
+}
+
+void drawCurrentPlayerRanking() // proc near ; CODE XREF: drawPlayerList+5Bp // sub_4C224
+{
+ // 01ED:55C1
+ PlayerEntry currentPlayerEntry = gPlayerListData[gCurrentPlayerIndex];
+ drawTextWithChars6FontWithOpaqueBackgroundIfPossible(168, 93, 8, currentPlayerEntry.name);
+
+ char timeText[10] = "000:00:00";
+
+ // Seconds
+ convertNumberTo3DigitStringWithPadding0(currentPlayerEntry.seconds, &timeText[6]);
+ timeText[6] = ':';
+
+ // Minutes
+ convertNumberTo3DigitStringWithPadding0(currentPlayerEntry.minutes, &timeText[3]);
+ timeText[3] = ':';
+
+ // Hours
+ convertNumberTo3DigitStringWithPadding0(currentPlayerEntry.hours, timeText);
+
+ drawTextWithChars6FontWithOpaqueBackgroundIfPossible(224, 93, 8, timeText);
+
+ char nextLevelText[4] = "000";
+ convertNumberTo3DigitStringWithPadding0(currentPlayerEntry.nextLevelToPlay, nextLevelText);
+ drawTextWithChars6FontWithOpaqueBackgroundIfPossible(288, 93, 8, nextLevelText);
+}
+
+void drawPlayerList() // sub_4C293 proc near ; CODE XREF: start+32Cp start+407p ...
+{
+ // 01ED:5630
+ PlayerEntry currentPlayer = gPlayerListData[gCurrentPlayerIndex];
+ memcpy(gPlayerName, currentPlayer.name, sizeof(currentPlayer.name) - 1);
+ drawTextWithChars6FontWithOpaqueBackgroundIfPossible(16, 164, 6, currentPlayer.name);
+
+ char *prevPlayerName = "";
+
+ if (gCurrentPlayerIndex <= 0)
+ {
+ prevPlayerName = " "; // just 8 spaces :shrug:
+ }
+ else
+ {
+ prevPlayerName = gPlayerListData[gCurrentPlayerIndex - 1].name;
+ }
+
+ // loc_4C2CD: // ; CODE XREF: drawPlayerList+35j
+ drawTextWithChars6FontWithOpaqueBackgroundIfPossible(16, 155, 8, prevPlayerName);
+
+ char *nextPlayerName = "";
+
+ if (gCurrentPlayerIndex >= kNumberOfPlayers - 1) // 19
+ {
+ nextPlayerName = " "; // just 8 spaces :shrug:
+ }
+ else
+ {
+ nextPlayerName = gPlayerListData[gCurrentPlayerIndex + 1].name;
+ }
+
+ // loc_4C2E6: // ; CODE XREF: drawPlayerList+4Ej
+ drawTextWithChars6FontWithOpaqueBackgroundIfPossible(16, 173, 8, nextPlayerName);
+ drawCurrentPlayerRanking();
+}
+
+void drawMenuTitleAndDemoLevelResult() // sub_4C2F2 proc near ; CODE XREF: handleGfxTutorOptionClick+Cp
+ // ; sub_4C407+1Fp ...
+{
+ // 01ED:568F
+ drawTextWithChars6FontWithOpaqueBackgroundIfPossible(168, 127, 4, " WELCOME TO SUPAPLEX ");
+ drawPlayerList();
+ drawLevelList();
+ drawHallOfFame();
+ drawRankings();
+ if (byte_59B83 == 0)
+ {
+ return;
+ }
+ byte_59B83 = 0;
+
+ char *message = "";
+ if (byte_5A19B == 0)
+ {
+ if (gIsLevelStartedAsDemo == 0)
+ {
+ message = " LEVEL FAILED ";
+ }
+ else
+ {
+ message = " DEMO FAILED ";
+ }
+ }
+ else
+ {
+ if (gIsLevelStartedAsDemo == 0)
+ {
+ message = " LEVEL SUCCESSFUL ";
+ }
+ else
+ {
+ message = " DEMO SUCCESSFUL ";
+ }
+ }
+
+ // loc_4C33C: // ; CODE XREF: drawMenuTitleAndDemoLevelResult+34j
+ // ; drawMenuTitleAndDemoLevelResult+39j ...
+ drawTextWithChars6FontWithOpaqueBackgroundIfPossible(168, 127, 4, message);
+ byte_5A19B = 0;
+}
+
+void prepareLevelDataForCurrentPlayer() // sub_4C34A proc near ; CODE XREF: start+404p handleNewPlayerOptionClick+1E0p ...
+{
+ // 01ED:56E7
+ PlayerEntry *currentPlayerEntry = &gPlayerListData[gCurrentPlayerIndex];
+
+ uint8_t *currentPlayerLevelState = currentPlayerEntry->levelState;
+
+ // Sets everything to 6 which seems to mean all levels are blocked
+ memset(gCurrentPlayerPaddedLevelData, kSkippedLevelEntryColor, kNumberOfLevelsWithPadding);
+ memset(gCurrentPlayerLevelData, kBlockedLevelEntryColor, kCurrentPlayerkLevelDataLength);
+
+ char isFirstUncompletedLevel = 1;
+
+ for (int i = 0; i < kNumberOfLevels; ++i)
+ {
+ // loc_4C373: // ; CODE XREF: prepareLevelDataForCurrentPlayer+53j
+ if (currentPlayerLevelState[i] == PlayerLevelStateSkipped)
+ {
+ gCurrentPlayerLevelData[i] = kSkippedLevelEntryColor;
+ }
+ // loc_4C37F: // ; CODE XREF: prepareLevelDataForCurrentPlayer+2Ej
+ else if (currentPlayerLevelState[i] == PlayerLevelStateCompleted) // Completed levels
+ {
+ gCurrentPlayerLevelData[i] = kCompletedLevelEntryColor;
+ }
+ // loc_4C389: // ; CODE XREF: prepareLevelDataForCurrentPlayer+38j
+ else if (currentPlayerLevelState[i] == PlayerLevelStateNotCompleted) // Levels not completed
+ {
+ if (isFirstUncompletedLevel == 1)
+ {
+ // The first uncompleted is not blocked
+ gCurrentPlayerLevelData[i] = kNotCompletedLevelEntryColor;
+ }
+ else
+ {
+ // The rest uncompleted levels are blocked
+ gCurrentPlayerLevelData[i] = kBlockedLevelEntryColor;
+ }
+ isFirstUncompletedLevel = 0;
+ }
+ }
+
+ char hasCompletedAllLevels = 1;
+ uint8_t nextLevelToPlay = 1;
+
+ // Looks for the first uncompleted level
+ for (int i = 0; i < kNumberOfLevels; ++i)
+ {
+ // loc_4C3A7: // ; CODE XREF: prepareLevelDataForCurrentPlayer+65j
+ if (currentPlayerLevelState[i] == PlayerLevelStateNotCompleted) // not completed
+ {
+ hasCompletedAllLevels = 0;
+ break;
+ }
+
+ nextLevelToPlay++;
+ }
+
+ if (hasCompletedAllLevels == 1)
+ {
+ nextLevelToPlay = 1;
+
+ // Looks for the first completed level
+ for (int i = 0; i < kNumberOfLevels; ++i)
+ {
+ // loc_4C3BA: // ; CODE XREF: prepareLevelDataForCurrentPlayer+78j
+ if (currentPlayerLevelState[i] == PlayerLevelStateSkipped)
+ {
+ hasCompletedAllLevels = 0;
+ break;
+ }
+ nextLevelToPlay++;
+ }
+ }
+
+ if (hasCompletedAllLevels == 1)
+ {
+ if (gShouldAutoselectNextLevelToPlay != 0)
+ {
+ gCurrentSelectedLevelIndex = kLastLevelIndex;
+ }
+
+ // loc_4C3D1: // ; CODE XREF: prepareLevelDataForCurrentPlayer+7Fj
+ currentPlayerEntry->nextLevelToPlay = kLastLevelIndex;
+ return;
+ }
+
+ // loc_4C3D6: // ; CODE XREF: prepareLevelDataForCurrentPlayer+61j
+ // ; prepareLevelDataForCurrentPlayer+74j
+ if (gShouldAutoselectNextLevelToPlay != 0)
+ {
+ gCurrentSelectedLevelIndex = nextLevelToPlay;
+ }
+
+ // loc_4C3E1: // ; CODE XREF: prepareLevelDataForCurrentPlayer+91j
+ if (nextLevelToPlay == 1)
+ {
+ if (strcmp(currentPlayerEntry->name, "--------") == 0)
+ {
+ nextLevelToPlay = 0;
+ }
+ }
+
+ // loc_4C403: // ; CODE XREF: prepareLevelDataForCurrentPlayer+9Aj
+ // ; prepareLevelDataForCurrentPlayer+A0j ...
+ currentPlayerEntry->nextLevelToPlay = nextLevelToPlay; // 0x7e = 126
+}
+
+void sub_4C407() // proc near ; CODE XREF: runMainMenu+5Dp
+{
+ // 01ED:57A4
+ if (gLevelFailed != 0)
+ {
+ gLevelFailed = 0;
+ drawFailedLevelResultScreen(); // 01ED:57B5
+ drawMenuBackground(); // 01ED:57B8
+ gShouldAutoselectNextLevelToPlay = 0;
+ prepareLevelDataForCurrentPlayer();
+ drawMenuTitleAndDemoLevelResult();
+ // mov si, 6015h
+ fadeToPalette(gGamePalette);
+
+ videoLoop();
+
+ // This will prevent to leave traces of the options menu
+ // area in the main menu.
+ //
+ saveLastMouseAreaBitmap();
+ }
+ else
+ {
+ // loc_4C449: ; CODE XREF: sub_4C407+3Aj
+ scrollLeftToMainMenu();
+ }
+}
+
+void scrollLeftToMainMenu() // loc_4C44F: ; CODE XREF: handleGfxTutorOptionClick+9p
+{
+ uint8_t *currentScreenPixels = malloc(kFullScreenFramebufferLength);
+ memcpy(currentScreenPixels, gScreenPixels, kFullScreenFramebufferLength);
+
+ drawMenuBackground();
+ gShouldAutoselectNextLevelToPlay = 0;
+
+ prepareLevelDataForCurrentPlayer();
+ drawMenuTitleAndDemoLevelResult();
+
+ uint8_t *menuScreenPixels = malloc(kFullScreenFramebufferLength);
+ memcpy(menuScreenPixels, gScreenPixels, kFullScreenFramebufferLength);
+
+ const int kNumberOfSteps = 80;
+
+ const uint32_t kAnimationDuration = kNumberOfSteps * 1000 / 70; // ~571 ms
+ uint32_t animationTime = 0;
+
+ startTrackingRenderDeltaTime();
+
+ // Draws the current scroll animation step
+ while (animationTime < kAnimationDuration)
+ {
+ animationTime += updateRenderDeltaTime();
+ animationTime = MIN(animationTime, kAnimationDuration);
+
+ float animationFactor = (float)animationTime / kAnimationDuration;
+
+ int limitFromLeft = animationFactor * kScreenWidth;
+ int limitFromRight = kScreenWidth - limitFromLeft;
+
+ for (int y = 0; y < kScreenHeight; ++y)
+ {
+ // Main menu side
+ for (int x = 0; x < kScreenWidth - limitFromRight; ++x)
+ {
+ gScreenPixels[y * kScreenWidth + x] = menuScreenPixels[y * kScreenWidth + x + limitFromRight];
+ }
+
+ // GFX background side
+ for (int x = limitFromLeft; x < kScreenWidth; ++x)
+ {
+ gScreenPixels[y * kScreenWidth + x] = currentScreenPixels[y * kScreenWidth + x - limitFromLeft];
+ }
+ }
+
+ // loc_4C466: ; CODE XREF: sub_4C407+90j
+ videoLoop();
+ }
+
+ free(currentScreenPixels);
+ free(menuScreenPixels);
+
+ // loc_4C499: ; CODE XREF: sub_4C407+28j
+ // This will prevent to leave traces of the options menu
+ // area in the main menu.
+ //
+ saveLastMouseAreaBitmap();
+}
+
+void drawFailedLevelResultScreen() // sub_4C4F9 proc near ; CODE XREF: sub_4C407+11p
+{
+ setPalette(gBlackPalette);
+ drawBackBackground();
+
+ drawTextWithChars6FontWithTransparentBackgroundIfPossible(128, 60, 0xF, "HARD LUCK!");
+ if (gNumberOfRemainingInfotrons == 0)
+ {
+ drawTextWithChars6FontWithTransparentBackgroundIfPossible(40, 80, 0xF, "YOU COMPLETED ALL THE NECESSARY INFOTRONS");
+ drawTextWithChars6FontWithTransparentBackgroundIfPossible(72, 100, 0xF, "BUT FAILED TO REACH THE EXIT");
+ }
+ else
+ {
+ // loc_4C52C: ; CODE XREF: drawFailedLevelResultScreen+19j
+ char message[] = "YOU HAVE COLLECTED ??? OUT OF THE ???";
+
+ uint8_t collectedInfotrons = gTotalNumberOfInfotrons - gNumberOfRemainingInfotrons;
+ convertNumberTo3DigitPaddedString(collectedInfotrons, &message[19], 1);
+
+ convertNumberTo3DigitPaddedString(gTotalNumberOfInfotrons, &message[34], 1);
+
+ drawTextWithChars6FontWithTransparentBackgroundIfPossible(40, 80, 0xF, message);
+ drawTextWithChars6FontWithTransparentBackgroundIfPossible(104, 100, 0xF, "INFOTRONS NEEDED");
+ }
+
+ // loc_4C55C: ; CODE XREF: drawFailedLevelResultScreen+31j
+ drawTextWithChars6FontWithTransparentBackgroundIfPossible(72, 120, 0xF, "WHY NOT GIVE IT ANOTHER TRY?");
+
+ videoLoop();
+ setPalette(gInformationScreenPalette);
+ if (gShouldExitGame != 1)
+ {
+ waitForKeyMouseOrJoystick();
+ }
+
+ // loc_4C591: ; CODE XREF: drawFailedLevelResultScreen+93j
+ setPalette(gBlackPalette);
+}
+
+void scrollRightToNewScreen() // sub_4C5AF proc near ; CODE XREF: handleGfxTutorOptionClick+3p
+{
+ videoLoop();
+
+ uint8_t *screenPixelsBackup = malloc(kFullScreenFramebufferLength);
+ memcpy(screenPixelsBackup, gScreenPixels, kFullScreenFramebufferLength);
+
+ const int kNumberOfSteps = 80;
+
+ const uint32_t kAnimationDuration = kNumberOfSteps * 1000 / 70; // ~571 ms
+ uint32_t animationTime = 0;
+
+ startTrackingRenderDeltaTime();
+
+ // Draws the current scroll animation step
+ while (animationTime < kAnimationDuration)
+ {
+ animationTime += updateRenderDeltaTime();
+ animationTime = MIN(animationTime, kAnimationDuration);
+
+ float animationFactor = (float)animationTime / kAnimationDuration;
+
+ int limitFromRight = animationFactor * kScreenWidth;
+ int limitFromLeft = kScreenWidth - limitFromRight;
+
+ for (int y = 0; y < kScreenHeight; ++y)
+ {
+ // Main menu side
+ for (int x = 0; x < kScreenWidth - limitFromRight; ++x)
+ {
+ gScreenPixels[y * kScreenWidth + x] = screenPixelsBackup[y * kScreenWidth + x + limitFromRight];
+ }
+
+ // GFX background side
+ for (int x = limitFromLeft; x < kScreenWidth; ++x)
+ {
+ gScreenPixels[y * kScreenWidth + x] = gScrollDestinationScreenBitmapData[y * kScreenWidth + x - limitFromLeft];
+ }
+ }
+
+ // loc_4C5BA: ; CODE XREF: scrollRightToGfxTutor+3Cj
+ videoLoop();
+ }
+
+ free(screenPixelsBackup);
+}
+
+void handleOptionsStandardClick() // sub_4C705 proc near ; CODE XREF: code:5ADBp
+{
+ activateInternalStandardSound();
+ playExplosionSound();
+ drawSoundTypeOptionsSelection(gScreenPixels);
+}
+
+void handleOptionsInternalClick() // loc_4C6FB
+{
+ handleOptionsStandardClick();
+ playExplosionSound();
+ drawSoundTypeOptionsSelection(gScreenPixels);
+}
+
+void handleOptionsSamplesClick() // sub_4C70F proc near
+{
+ activateInternalSamplesSound();
+ playExplosionSound();
+ drawSoundTypeOptionsSelection(gScreenPixels);
+}
+
+void handleOptionsSoundBlasterClick() // sub_4C719 proc near
+{
+ activateSoundBlasterSound();
+ playExplosionSound();
+ drawSoundTypeOptionsSelection(gScreenPixels);
+}
+
+void handleOptionsAdlibClick() // sub_4C723 proc near
+{
+ activateAdlibSound();
+ playExplosionSound();
+ drawSoundTypeOptionsSelection(gScreenPixels);
+}
+
+void handleOptionsRolandClick() // sub_4C72D proc near
+{
+ activateRolandSound();
+ playExplosionSound();
+ drawSoundTypeOptionsSelection(gScreenPixels);
+}
+
+void handleOptionsCombinedClick() // sub_4C737 proc near
+{
+ activateCombinedSound();
+ playExplosionSound();
+ drawSoundTypeOptionsSelection(gScreenPixels);
+}
+
+void handleOptionsMusicClick() // sub_4C741 proc near
+{
+ if (isMusicEnabled == 1)
+ {
+ stopMusic();
+ isMusicEnabled = 0;
+ }
+ else
+ {
+ // loc_4C752: ; CODE XREF: handleOptionsAdlibClick+5j
+ isMusicEnabled = 1;
+ playMusicIfNeeded();
+ }
+
+ // loc_4C75A: ; CODE XREF: handleOptionsAdlibClick+Fj
+ drawAudioOptionsSelection(gScreenPixels);
+ return;
+}
+
+void handleOptionsFXClick() // loc_4C75E
+{
+ if (isFXEnabled == 1)
+ {
+ isFXEnabled = 0;
+ }
+ else
+ {
+ // loc_4C76C: ; CODE XREF: code:5B43j
+ isFXEnabled = 1;
+ playExplosionSound();
+ }
+
+ // loc_4C774: ; CODE XREF: code:5B4Aj
+ drawAudioOptionsSelection(gScreenPixels);
+}
+
+void handleOptionsKeyboardClick() // loc_4C778
+{
+ isJoystickEnabled = 0;
+ drawInputOptionsSelection(gScreenPixels);
+}
+
+void handleOptionsJoystickClick() // loc_4C781
+{
+ isJoystickEnabled = 1;
+ // calibrateJoystick(); not needed anymore
+ drawInputOptionsSelection(gScreenPixels);
+}
+
+void handleOptionsExitAreaClick() // loc_4C78D
+{
+ word_58463 = 1;
+}
+
+void runMainMenu() // proc near ; CODE XREF: start+43Ap
+{
+ // 01ED:5B31
+ gIsInMainMenu = 1;
+ gHasUserInterruptedDemo = 0;
+ gSelectedOriginalDemoLevelNumber = 0;
+ gIsSPDemoAvailableToRun = 0;
+ gAutomaticDemoPlaybackCountdown = 4200;
+ if (word_58467 != 0)
+ {
+ drawMenuBackground(); // 01ED:5B4E
+ gShouldAutoselectNextLevelToPlay = 1;
+ prepareLevelDataForCurrentPlayer(); // 01ED:5B56
+ drawMenuTitleAndDemoLevelResult(); // 01ED:5B59
+
+ videoLoop();
+ fadeToPalette(gGamePalette); // 6015h
+ word_58467 = 0;
+ }
+ else
+ {
+ // loc_4C7EC: // ; CODE XREF: runMainMenu+1Bj
+ byte_59B83 = 1;
+ sub_4C407(); // 01ED:5B8E
+ }
+
+ // loc_4C7F4: // ; CODE XREF: runMainMenu+56j
+ playMusicIfNeeded(); // 01ED:5B91
+ saveLastMouseAreaBitmap();
+ drawMouseCursor();
+
+ while (1)
+ {
+ int9handler(0);
+
+ // loc_4C7FD: // ; CODE XREF: runMainMenu+121j
+ // ; runMainMenu+219j ...
+ gAutomaticDemoPlaybackCountdown--;
+ if (gAutomaticDemoPlaybackCountdown == 0)
+ {
+ handleDemoOptionClick();
+ }
+
+ // loc_4C806: // ; CODE XREF: runMainMenu+6Dj
+ if (gShouldLeaveMainMenu != 0)
+ {
+ gShouldLeaveMainMenu = 0;
+ break;
+ }
+
+ // loc_4C81A: // ; CODE XREF: runMainMenu+77j
+ videoLoop();
+
+ gFrameCounter++;
+ uint16_t mouseX, mouseY;
+ uint16_t mouseButtonStatus;
+ getMouseStatus(&mouseX, &mouseY, &mouseButtonStatus);
+ gMouseButtonStatus = mouseButtonStatus;
+ if (gMouseX != mouseX || gMouseY != mouseY)
+ {
+ // loc_4C834: // ; CODE XREF: runMainMenu+98j
+ gAutomaticDemoPlaybackCountdown = 4200;
+ }
+
+ // loc_4C83A: // ; CODE XREF: runMainMenu+9Ej
+ gMouseX = mouseX;
+ gMouseY = mouseY;
+ restoreLastMouseAreaBitmap(); // 01ED:5BDF
+ saveLastMouseAreaBitmap(); // 01ED:5BE2
+ drawMouseCursor(); // 01ED:5BE5 Draws mouse cursor too?
+ drawMainMenuButtonBorders(); // 01ED:5BE8
+ updateUserInput();
+ if (gPlayerListDownButtonPressed != 0 || gPlayerListUpButtonPressed != 0)
+ {
+ // loc_4C862: // ; CODE XREF: runMainMenu+C5j
+ gPlayerListButtonPressed = 1;
+ }
+
+ // loc_4C867: // ; CODE XREF: runMainMenu+CCj
+ gPlayerListDownButtonPressed = 0;
+ gPlayerListUpButtonPressed = 0;
+ if (gRankingListDownButtonPressed != 0 || gRankingListUpButtonPressed != 0)
+ {
+ // loc_4C87F: // ; CODE XREF: runMainMenu+E2j
+ gRankingListButtonPressed = 1;
+ }
+
+ // loc_4C884: // ; CODE XREF: runMainMenu+E9j
+ gRankingListDownButtonPressed = 0;
+ gRankingListUpButtonPressed = 0;
+ if (gLevelListDownButtonPressed != 0 || gLevelListUpButtonPressed != 0)
+ {
+ // loc_4C89C: // ; CODE XREF: runMainMenu+FFj
+ gLevelListButtonPressed = 1;
+ }
+
+ // loc_4C8A1: // ; CODE XREF: runMainMenu+106j
+ gLevelListDownButtonPressed = 0;
+ gLevelListUpButtonPressed = 0;
+ if (gCurrentUserInput > kUserInputSpaceAndDirectionOffset || isStartButtonPressed())
+ {
+ handleOkButtonClick();
+ }
+ // loc_4C8B8: // ; CODE XREF: runMainMenu+11Cj
+ else if (gIsF1KeyPressed == 1)
+ {
+ playDemo(0);
+ }
+ // loc_4C8C8: // ; CODE XREF: runMainMenu+129j
+ else if (gIsF2KeyPressed == 1)
+ {
+ playDemo(1);
+ }
+ // loc_4C8D8: // ; CODE XREF: runMainMenu+139j
+ else if (gIsF3KeyPressed == 1)
+ {
+ playDemo(2);
+ }
+ // loc_4C8E8: // ; CODE XREF: runMainMenu+149j
+ else if (gIsF4KeyPressed == 1)
+ {
+ playDemo(3);
+ }
+ // loc_4C8F8: // ; CODE XREF: runMainMenu+159j
+ else if (gIsF5KeyPressed == 1)
+ {
+ playDemo(4);
+ }
+ // loc_4C908: // ; CODE XREF: runMainMenu+169j
+ else if (gIsF6KeyPressed == 1)
+ {
+ playDemo(5);
+ }
+ // loc_4C918: // ; CODE XREF: runMainMenu+179j
+ else if (gIsF7KeyPressed == 1)
+ {
+ playDemo(6);
+ }
+ // loc_4C928: // ; CODE XREF: runMainMenu+189j
+ else if (gIsF8KeyPressed == 1)
+ {
+ playDemo(7);
+ }
+ // loc_4C937: // ; CODE XREF: runMainMenu+199j
+ else if (gIsF9KeyPressed == 1)
+ {
+ playDemo(8);
+ }
+ // loc_4C946: // ; CODE XREF: runMainMenu+1A8j
+ else if (gIsF10KeyPressed == 1)
+ {
+ playDemo(9);
+ }
+ // loc_4C955: // ; CODE XREF: runMainMenu+1B7j
+ else if (gIsNumpadDividePressed == 1 && strlen(demoFileName) != 0 && fileIsDemo == 1)
+ {
+ gIsSPDemoAvailableToRun = 1;
+ playDemo(0);
+ }
+ // loc_4C977: // ; CODE XREF: runMainMenu+1C6j
+ // ; runMainMenu+1CDj ...
+ else if (gIsF12KeyPressed == 1 && strlen(demoFileName) != 0)
+ {
+ gIsSPDemoAvailableToRun = 1;
+ gShouldLeaveMainMenu = 1;
+ gIsPlayingDemo = 0;
+ gShouldUpdateTotalLevelTime = 0;
+ gHasUserCheated = 1;
+ prepareDemoRecordingFilename();
+ // This adds dashes to the level name or something?
+ gSPDemoFileName[3] = 0x2D; // '-' ; "001$0.SP"
+ gSPDemoFileName[4] = 0x2D; // '-' ; "01$0.SP"
+ gSPDemoFileName[5] = 0x2D; // '-' ; "1$0.SP"
+ continue;
+ }
+ // loc_4C9B0: // ; CODE XREF: runMainMenu+131j
+ // ; runMainMenu+141j ...
+ // if (gMouseButtonStatus == MouseButtonRight) // Right button -> exit game
+ // {
+ // gShouldExitGame = 1;
+ // break;
+ // }
+ else if (getGameControllerButtonBack() // Select/Back/- controller button -> exit game
+ || gIsEscapeKeyPressed == 1)
+ {
+ runAdvancedOptionsRootMenu();
+ }
+ else if (isRotateLevelSetAscendingButtonPressed())
+ {
+ throttledRotateLevelSet(0);
+ continue; // This allows the throttling effect to act
+ }
+ else if (isRotateLevelSetDescendingButtonPressed())
+ {
+ throttledRotateLevelSet(1);
+ continue; // This allows the throttling effect to act
+ }
+ if (gShouldExitGame == 1)
+ {
+ break;
+ }
+
+ if (gMouseButtonStatus == MouseButtonLeft)
+ {
+ // loc_4C9FF: // ; CODE XREF: runMainMenu+236j
+ gAutomaticDemoPlaybackCountdown = 4200;
+
+ for (int i = 0; i < kNumberOfMainMenuButtons; ++i)
+ {
+ ButtonDescriptor buttonDescriptor = kMainMenuButtonDescriptors[i];
+
+ // checkmousecoords: // ; CODE XREF: runMainMenu+29Bj
+ if (gMouseX >= buttonDescriptor.startX && gMouseY >= buttonDescriptor.startY && gMouseX <= buttonDescriptor.endX && gMouseY <= buttonDescriptor.endY)
+ {
+ buttonDescriptor.handler(); // 01ED:5DC0
+ break;
+ }
+ }
+ }
+ else
+ {
+ // Reset throttle counters
+ gLevelListThrottleCurrentCounter = 0x10;
+ gLevelListThrottleNextCounter = 0;
+ gPlayerListThrottleCurrentCounter = 0x10;
+ gPlayerListThrottleNextCounter = 0;
+ gRankingListThrottleCurrentCounter = 0x10;
+ gRankingListThrottleNextCounter = 0;
+ gLevelSetRotationThrottleCurrentCounter = 0x10;
+ gLevelSetRotationThrottleNextCounter = 0;
+ }
+ }
+
+ // loc_4CA34: // ; CODE XREF: runMainMenu+223j
+ // ; runMainMenu+22Aj ...
+ gIsInMainMenu = 0;
+ savePlayerListData();
+ saveHallOfFameData();
+}
+
+void handleControlsOptionClick() // showControls: ; DATA XREF: data:0044o
+{
+ // 01ED:5DDE;
+ byte_50919 = 0xFF;
+ drawOptionsBackground(gScrollDestinationScreenBitmapData);
+ drawSoundTypeOptionsSelection(gScrollDestinationScreenBitmapData);
+ drawAudioOptionsSelection(gScrollDestinationScreenBitmapData);
+ drawInputOptionsSelection(gScrollDestinationScreenBitmapData);
+ // mov si, 6055h
+ setPalette(gControlsScreenPalette);
+ scrollRightToNewScreen();
+ word_58463 = 0;
+ saveLastMouseAreaBitmap();
+ drawMouseCursor();
+
+ uint16_t mouseX, mouseY;
+ uint16_t mouseButtonStatus;
+
+ do
+ {
+ int9handler(0);
+
+ // loc_4CA67: ; CODE XREF: code:5E89j
+ // ; code:5EBFj ...
+ videoLoop(); // 01ED:5E04
+ updateOptionsMenuState(gScreenPixels);
+ gFrameCounter++;
+ getMouseStatus(&mouseX, &mouseY, &mouseButtonStatus);
+
+ gMouseButtonStatus = mouseButtonStatus;
+ gMouseX = mouseX;
+ gMouseY = mouseY;
+
+ restoreLastMouseAreaBitmap();
+ saveLastMouseAreaBitmap();
+ drawMouseCursor();
+ if (gMouseButtonStatus == MouseButtonRight)
+ {
+ break;
+ }
+ if (isMenuBackButtonPressed()) // Select/Back/- controller button -> go back
+ {
+ break;
+ }
+ if (word_58463 == 1)
+ {
+ break;
+ }
+ if (gMouseButtonStatus == MouseButtonLeft)
+ {
+ // loc_4CAAB: ; CODE XREF: code:5E87j
+ // mov si, offset controlsbuttons ; 0ACh // 01ED:5E54
+
+ for (int i = 0; i < kNumberOfOptionsMenuButtons; ++i)
+ {
+ ButtonDescriptor buttonDescriptor = kOptionsMenuButtonDescriptors[i];
+ // loc_4CABA: ; CODE XREF: code:5EC7j
+ if (gMouseX >= buttonDescriptor.startX && gMouseY >= buttonDescriptor.startY && gMouseX <= buttonDescriptor.endX && gMouseY <= buttonDescriptor.endY)
+ {
+ buttonDescriptor.handler(); // 01ED:5E6A
+
+ do
+ {
+ // loc_4CAD0: ; CODE XREF: code:5EBDj
+ videoLoop();
+ gFrameCounter++;
+ getMouseStatus(&mouseX, &mouseY, &mouseButtonStatus);
+ } while (mouseButtonStatus != 0);
+ }
+ }
+ }
+ } while (1);
+
+ // loc_4CAEC: ; CODE XREF: code:5E74j
+ // ; code:5E7Bj ...
+ saveConfiguration();
+ scrollLeftToMainMenu();
+ drawMenuTitleAndDemoLevelResult();
+ setPalette(gGamePalette);
+}
+
+void drawSoundTypeOptionsSelection(uint8_t *destBuffer) // sub_4CAFC proc near ; CODE XREF: code:5AE1p handleOptionsStandardClick+6p ...
+{
+ dimOptionsButtonText(40, 21, 40, 8, destBuffer);
+ drawOptionsMenuLine(kOptionsMenuBorders[0], 4, destBuffer);
+ drawOptionsMenuLine(kOptionsMenuBorders[1], 4, destBuffer);
+
+ dimOptionsButtonText(24, 57, 72, 8, destBuffer);
+ drawOptionsMenuLine(kOptionsMenuBorders[2], 4, destBuffer);
+ drawOptionsMenuLine(kOptionsMenuBorders[3], 4, destBuffer);
+
+ dimOptionsButtonText(32, 93, 56, 8, destBuffer);
+ drawOptionsMenuLine(kOptionsMenuBorders[4], 4, destBuffer);
+ drawOptionsMenuLine(kOptionsMenuBorders[5], 4, destBuffer);
+
+ dimOptionsButtonText(24, 129, 64, 8, destBuffer);
+ dimOptionsButtonText(136, 18, 72, 8, destBuffer);
+ drawOptionsMenuLine(kOptionsMenuBorders[7], 4, destBuffer);
+
+ dimOptionsButtonText(128, 46, 40, 5, destBuffer);
+ drawOptionsMenuLine(kOptionsMenuBorders[8], 4, destBuffer);
+
+ dimOptionsButtonText(176, 46, 40, 5, destBuffer);
+ drawOptionsMenuLine(kOptionsMenuBorders[9], 4, destBuffer);
+ drawOptionsMenuLine(kOptionsMenuBorders[6], 4, destBuffer);
+
+ if (sndType == SoundTypeAdlib)
+ {
+ highlightOptionsButtonText(40, 21, 40, 8, destBuffer);
+ drawOptionsMenuLine(kOptionsMenuBorders[0], 6, destBuffer);
+ drawOptionsMenuLine(kOptionsMenuBorders[1], 6, destBuffer);
+ return;
+ }
+
+ // loc_4CBC6: ; CODE XREF: drawSoundTypeOptionsSelection+A9j
+ if (sndType == SoundTypeSoundBlaster)
+ {
+ drawOptionsMenuLine(kOptionsMenuBorders[3], 6, destBuffer);
+
+ if (musType == SoundTypeAdlib)
+ {
+ highlightOptionsButtonText(24, 57, 72, 8, destBuffer);
+ drawOptionsMenuLine(kOptionsMenuBorders[2], 6, destBuffer);
+ return;
+ }
+
+ // loc_4CBF3: ; CODE XREF: drawSoundTypeOptionsSelection+DEj
+ highlightOptionsButtonText(24, 129, 64, 8, destBuffer);
+ drawOptionsMenuLine(kOptionsMenuBorders[4], 6, destBuffer);
+ drawOptionsMenuLine(kOptionsMenuBorders[6], 6, destBuffer);
+ return;
+ }
+
+ // loc_4CC11: ; CODE XREF: drawSoundTypeOptionsSelection+CFj
+ if (sndType == SoundTypeRoland)
+ {
+ highlightOptionsButtonText(32, 93, 56, 8, destBuffer);
+ drawOptionsMenuLine(kOptionsMenuBorders[4], 6, destBuffer);
+ drawOptionsMenuLine(kOptionsMenuBorders[5], 6, destBuffer);
+ return;
+ }
+
+ // loc_4CC36: ; CODE XREF: drawSoundTypeOptionsSelection+11Aj
+ highlightOptionsButtonText(136, 18, 72, 8, destBuffer);
+ drawOptionsMenuLine(kOptionsMenuBorders[7], 6, destBuffer);
+
+ if (sndType == SoundTypeInternalStandard)
+ {
+ highlightOptionsButtonText(128, 46, 40, 5, destBuffer); // Standard
+ drawOptionsMenuLine(kOptionsMenuBorders[8], 6, destBuffer);
+ return;
+ }
+
+ // loc_4CC67: ; CODE XREF: drawSoundTypeOptionsSelection+153j
+ highlightOptionsButtonText(176, 46, 40, 5, destBuffer);
+ drawOptionsMenuLine(kOptionsMenuBorders[9], 6, destBuffer);
+}
+
+void drawAudioOptionsSelection(uint8_t *destBuffer) // sub_4CC7C proc near ; CODE XREF: handleOptionsAdlibClick:loc_4C75Ap
+ // ; code:loc_4C774p
+{
+ if (isMusicEnabled == 1)
+ {
+ highlightOptionsButtonText(134, 99, 40, 8, destBuffer);
+ drawOptionsMenuLine(kOptionsMenuBorders[10], 6, destBuffer);
+ }
+ else
+ {
+ // loc_4CC99: ; CODE XREF: drawAudioOptionsSelection+5j
+ dimOptionsButtonText(134, 99, 40, 8, destBuffer);
+ drawOptionsMenuLine(kOptionsMenuBorders[10], 4, destBuffer);
+ }
+
+ // loc_4CCAD: ; CODE XREF: drawAudioOptionsSelection+1Bj
+ if (isFXEnabled == 1)
+ {
+ highlightOptionsButtonText(136, 138, 24, 8, destBuffer);
+ drawOptionsMenuLine(kOptionsMenuBorders[11], 6, destBuffer);
+ return;
+ }
+
+ // loc_4CCCA: ; CODE XREF: drawAudioOptionsSelection+36j
+ dimOptionsButtonText(136, 138, 24, 8, destBuffer);
+ drawOptionsMenuLine(kOptionsMenuBorders[11], 4, destBuffer);
+}
+
+void drawInputOptionsSelection(uint8_t *destBuffer) // sub_4CCDF proc near ; CODE XREF: code:5B5Dp code:5B69p
+{
+ if (isJoystickEnabled == 0)
+ {
+ highlightOptionsButtonText(208, 87, 8, 62, destBuffer);
+ dimOptionsButtonText(240, 88, 8, 58, destBuffer);
+ drawOptionsMenuLine(kOptionsMenuBorders[18], 4, destBuffer);
+ drawOptionsMenuLine(kOptionsMenuBorders[19], 6, destBuffer);
+ }
+ else
+ {
+ // loc_4CD10: ; CODE XREF: drawInputOptionsSelection+5j
+ dimOptionsButtonText(208, 87, 8, 62, destBuffer);
+ highlightOptionsButtonText(240, 88, 8, 58, destBuffer);
+ drawOptionsMenuLine(kOptionsMenuBorders[18], 6, destBuffer);
+ drawOptionsMenuLine(kOptionsMenuBorders[19], 4, destBuffer);
+ }
+
+ // loc_4CD38: ; CODE XREF: drawInputOptionsSelection+2Fj
+ updateOptionsMenuState(destBuffer);
+}
+
+void updateOptionsMenuState(uint8_t *destBuffer) // sub_4CD3C proc near ; CODE XREF: drawInputOptionsSelection:loc_4CD38p
+{
+ // 01ED:60D9
+ updateUserInput();
+ if (gCurrentUserInput == byte_50919)
+ {
+ return;
+ }
+
+ // loc_4CD4D: ; CODE XREF: updateOptionsMenuState+Ej
+ byte_50919 = gCurrentUserInput;
+ if (gCurrentUserInput == UserInputNone)
+ {
+ drawOptionsMenuLine(kOptionsMenuBorders[12], 6, destBuffer);
+ drawOptionsMenuLine(kOptionsMenuBorders[17], 4, destBuffer);
+ }
+ else
+ {
+ // loc_4CD6A: ; CODE XREF: updateOptionsMenuState+1Aj
+ if (gCurrentUserInput <= kUserInputSpaceAndDirectionOffset)
+ {
+ // loc_4CD9E: ; CODE XREF: updateOptionsMenuState+33j
+ drawOptionsMenuLine(kOptionsMenuBorders[12], 4, destBuffer);
+ drawOptionsMenuLine(kOptionsMenuBorders[17], 4, destBuffer);
+ }
+ else
+ {
+ drawOptionsMenuLine(kOptionsMenuBorders[17], 6, destBuffer);
+ if (gCurrentUserInput != UserInputSpaceOnly)
+ {
+ // loc_4CD8F: ; CODE XREF: updateOptionsMenuState+42j
+ gCurrentUserInput -= kUserInputSpaceAndDirectionOffset;
+ drawOptionsMenuLine(kOptionsMenuBorders[12], 4, destBuffer);
+ }
+ else
+ {
+ gCurrentUserInput = UserInputNone;
+ drawOptionsMenuLine(kOptionsMenuBorders[12], 6, destBuffer);
+ }
+ }
+ }
+
+ // loc_4CDAE: ; CODE XREF: updateOptionsMenuState+2Cj
+ // ; updateOptionsMenuState+51j ...
+ drawOptionsMenuLine(kOptionsMenuBorders[13], 4, destBuffer);
+ drawOptionsMenuLine(kOptionsMenuBorders[14], 4, destBuffer);
+ drawOptionsMenuLine(kOptionsMenuBorders[15], 4, destBuffer);
+ drawOptionsMenuLine(kOptionsMenuBorders[16], 4, destBuffer);
+ if (gCurrentUserInput == UserInputUp)
+ {
+ drawOptionsMenuLine(kOptionsMenuBorders[13], 6, destBuffer);
+ }
+ // loc_4CDDF: ; CODE XREF: updateOptionsMenuState+97j
+ else if (gCurrentUserInput == UserInputLeft)
+ {
+ drawOptionsMenuLine(kOptionsMenuBorders[14], 6, destBuffer);
+ }
+ // loc_4CDF0: ; CODE XREF: updateOptionsMenuState+A8j
+ else if (gCurrentUserInput == UserInputDown)
+ {
+ drawOptionsMenuLine(kOptionsMenuBorders[15], 6, destBuffer);
+ }
+ // loc_4CE01: ; CODE XREF: updateOptionsMenuState+B9j
+ else if (gCurrentUserInput == UserInputRight)
+ {
+ drawOptionsMenuLine(kOptionsMenuBorders[16], 6, destBuffer);
+ }
+}
+
+void highlightOptionsButtonText(size_t startX, size_t startY, size_t width, size_t height, uint8_t *destBuffer) // sub_4CE11 proc near ; CODE XREF: drawSoundTypeOptionsSelection+B4p
+ // ; drawSoundTypeOptionsSelection+E9p ...
+{
+ // Copies a portion of the buffer replacing color 0xF (not selected)
+ // with color 0x1 (selected).
+ // Used in the options screen to highlight text from buttons.
+ //
+ // Parameters:
+ // - si: origin coordinates
+ // - cx: width / 8
+ // - dx: height
+
+ restoreLastMouseAreaBitmap();
+
+ for (size_t y = startY; y < startY + height; ++y)
+ {
+ for (size_t x = startX; x < startX + width; ++x)
+ {
+ // loc_4CE68: ; CODE XREF: highlightOptionsButtonText+5Aj
+ // ; highlightOptionsButtonText+6Aj
+ size_t addr = (y * kScreenWidth) + x;
+ destBuffer[addr] = (destBuffer[addr] == 0xF
+ ? 0x1
+ : destBuffer[addr]);
+ }
+ }
+
+ saveLastMouseAreaBitmap();
+ drawMouseCursor();
+}
+
+void dimOptionsButtonText(size_t startX, size_t startY, size_t width, size_t height, uint8_t *destBuffer) // sub_4CE9C proc near ; CODE XREF: drawSoundTypeOptionsSelection+9p
+ // ; drawSoundTypeOptionsSelection+25p ...
+{
+ // Copies a portion of the buffer replacing color 0x1 (selected)
+ // with color 0xF (not selected).
+ // Used in the options screen to dim text from buttons.
+ //
+ // Parameters:
+ // - si: coordinates
+ // - cx: width / 8
+ // - dx: height
+
+ restoreLastMouseAreaBitmap();
+
+ for (size_t y = startY; y < startY + height; ++y)
+ {
+ for (size_t x = startX; x < startX + width; ++x)
+ {
+ // loc_4CEF3: ; CODE XREF: dimOptionsButtonText+5Aj
+ // ; dimOptionsButtonText+6Aj
+ size_t addr = (y * kScreenWidth) + x;
+
+ destBuffer[addr] = (destBuffer[addr] == 0x1
+ ? 0xF
+ : destBuffer[addr]);
+ }
+ }
+
+ saveLastMouseAreaBitmap();
+ drawMouseCursor();
+}
+
+void drawOptionsMenuLine(ButtonBorderDescriptor border, uint8_t color, uint8_t *destBuffer) // sub_4CF13 proc near ; CODE XREF: drawSoundTypeOptionsSelection+11p
+ // ; drawSoundTypeOptionsSelection+19p ...
+{
+ // Parameters:
+ // - ah: color
+ // - si: pointer to ButtonBorderDescriptor item
+
+ restoreLastMouseAreaBitmap();
+
+ // loc_4CF38: ; CODE XREF: drawOptionsMenuLine+96j
+ for (int i = 0; i < border.numberOfLines; ++i)
+ {
+ ButtonBorderLineDescriptor line = border.lines[i];
+
+ for (int j = 0; j < line.length; ++j)
+ {
+ size_t destAddress = 0;
+ if (line.type == ButtonBorderLineTypeHorizontal)
+ {
+ destAddress = line.y * kScreenWidth + line.x + j;
+ }
+ else if (line.type == ButtonBorderLineTypeVertical)
+ {
+ destAddress = (line.y - j) * kScreenWidth + line.x;
+ }
+ else if (line.type == ButtonBorderLineTypeBottomLeftToTopRightDiagonal)
+ {
+ destAddress = (line.y - j) * kScreenWidth + line.x + j;
+ }
+ else if (line.type == ButtonBorderLineTypeTopLeftToBottomRightDiagonal)
+ {
+ destAddress = (line.y + j) * kScreenWidth + line.x + j;
+ }
+
+ destBuffer[destAddress] = color;
+
+ // loc_4CFA4: ; CODE XREF: drawOptionsMenuLine:loc_4CF7Cj
+ // ; drawOptionsMenuLine+73j ...
+ }
+ }
+
+ // loc_4CFAB: ; CODE XREF: drawOptionsMenuLine+2Aj
+ saveLastMouseAreaBitmap();
+ drawMouseCursor();
+}
+
+void savePlayerListData() // sub_4CFB2 proc near ; CODE XREF: handleNewPlayerOptionClick+1D5p
+// ; handleDeletePlayerOptionClick+CEp ...
+{
+ if (gIsForcedCheatMode != 0)
+ {
+ return;
+ }
+
+ FILE *file = openWritableFile(gPlayerLstFilename, "wb");
+ if (file == NULL)
+ {
+ return;
+ }
+
+ assert(sizeof(gPlayerListData) == 0xA00);
+
+ fileWriteBytes(gPlayerListData, sizeof(gPlayerListData), file);
+
+ fclose(file);
+}
+
+void saveHallOfFameData() // proc near ; CODE XREF: handleNewPlayerOptionClick+1D8p
+// ; handleDeletePlayerOptionClick+D1p ...
+{
+ if (gIsForcedCheatMode != 0)
+ {
+ return;
+ }
+
+ FILE *file = openWritableFile(gHallfameLstFilename, "wb");
+ if (file == NULL)
+ {
+ return;
+ }
+
+ assert(sizeof(gHallOfFameData) == 0x24);
+
+ fileWriteBytes(gHallOfFameData, sizeof(gHallOfFameData), file);
+
+ fclose(file);
+}
+
+void drawMainMenuButtonBorder(ButtonBorderDescriptor border, uint8_t color) // sub_4D004 proc near ; CODE XREF: drawMainMenuButtonBorders+17p
+// ; drawMainMenuButtonBorders+2Ap ...
+{
+ // 01ED:63A1
+ // Parameters:
+ // - si: button border descriptor
+ // - ah: color??? it's either 7 or 0xD in drawMainMenuButtonBorders
+
+ restoreLastMouseAreaBitmap();
+
+ // loc_4D029: ; CODE XREF: drawButtonBorder+96j
+ for (int i = 0; i < border.numberOfLines; ++i)
+ {
+ ButtonBorderLineDescriptor line = border.lines[i];
+
+ for (int j = 0; j < line.length; ++j)
+ {
+ size_t destAddress = 0;
+ if (line.type == ButtonBorderLineTypeHorizontal)
+ {
+ destAddress = line.y * kScreenWidth + line.x + j;
+ }
+ else if (line.type == ButtonBorderLineTypeVertical)
+ {
+ destAddress = (line.y - j) * kScreenWidth + line.x;
+ }
+ else if (line.type == ButtonBorderLineTypeBottomLeftToTopRightDiagonal)
+ {
+ destAddress = (line.y - j) * kScreenWidth + line.x + j;
+ }
+ else if (line.type == ButtonBorderLineTypeTopLeftToBottomRightDiagonal)
+ {
+ destAddress = (line.y + j) * kScreenWidth + line.x + j;
+ }
+
+ gScreenPixels[destAddress] = color;
+
+ // loc_4D095: ; CODE XREF: drawButtonBorder:loc_4D06Dj
+ // ; drawButtonBorder+73j ...
+ }
+ }
+
+ // loc_4D09C: ; CODE XREF: drawButtonBorder+2Aj
+ saveLastMouseAreaBitmap();
+ drawMouseCursor();
+}
+
+void drawMainMenuButtonBorders() // sub_4D0AD proc near ; CODE XREF: runMainMenu+B7p
+{
+ // 01ED:644A
+ uint8_t color = 0;
+
+ if (gPlayerListButtonPressed != 0)
+ {
+ if (gPlayerListUpButtonPressed == 0)
+ {
+ color = 7;
+ }
+ else
+ {
+ color = 0xD; // 13
+ }
+
+ // loc_4D0C1: ; CODE XREF: drawMainMenuButtonBorders+10j
+ drawMainMenuButtonBorder(kMainMenuButtonBorders[0], color);
+ if (gPlayerListUpButtonPressed == 0)
+ {
+ color = 0xD; // 13
+ }
+ else
+ {
+ color = 7;
+ }
+
+ // loc_4D0D4: ; CODE XREF: drawMainMenuButtonBorders+23j
+ drawMainMenuButtonBorder(kMainMenuButtonBorders[1], color);
+ if (gPlayerListDownButtonPressed == 0)
+ {
+ color = 7;
+ }
+ else
+ {
+ color = 0xD; // 13
+ }
+
+ // loc_4D0E7: ; CODE XREF: drawMainMenuButtonBorders+36j
+ drawMainMenuButtonBorder(kMainMenuButtonBorders[2], color);
+ if (gPlayerListDownButtonPressed == 0)
+ {
+ color = 0xD; // 13
+ }
+ else
+ {
+ color = 7;
+ }
+
+ // loc_4D0FA: ; CODE XREF: drawMainMenuButtonBorders+49j
+ drawMainMenuButtonBorder(kMainMenuButtonBorders[3], color);
+ gPlayerListButtonPressed = 0;
+ }
+
+ // loc_4D105: ; CODE XREF: drawMainMenuButtonBorders+5j
+ if (gRankingListButtonPressed != 0)
+ {
+ if (gRankingListUpButtonPressed == 0)
+ {
+ color = 7;
+ }
+ else
+ {
+ color = 0xD; // 13
+ }
+
+ // loc_4D119: ; CODE XREF: drawMainMenuButtonBorders+68j
+ // si = 0x558; // 1368
+ drawMainMenuButtonBorder(kMainMenuButtonBorders[4], color);
+ if (gRankingListUpButtonPressed == 0)
+ {
+ color = 0xD;
+ }
+ else
+ {
+ color = 7;
+ }
+
+ // loc_4D12C: ; CODE XREF: drawMainMenuButtonBorders+7Bj
+ // si = 0x56D;
+ drawMainMenuButtonBorder(kMainMenuButtonBorders[5], color);
+ if (gRankingListDownButtonPressed == 0)
+ {
+ color = 7;
+ }
+ else
+ {
+ color = 0xD;
+ }
+
+ // loc_4D13F: ; CODE XREF: drawMainMenuButtonBorders+8Ej
+ // si = 0x582;
+ drawMainMenuButtonBorder(kMainMenuButtonBorders[6], color);
+ if (gRankingListDownButtonPressed == 0)
+ {
+ color = 0xD;
+ }
+ else
+ {
+ color = 7;
+ }
+
+ // loc_4D152: ; CODE XREF: drawMainMenuButtonBorders+A1j
+ // si = 0x597;
+ drawMainMenuButtonBorder(kMainMenuButtonBorders[7], color);
+ gRankingListButtonPressed = 0;
+ }
+
+ // loc_4D15D: ; CODE XREF: drawMainMenuButtonBorders+5Dj
+ if (gLevelListButtonPressed == 0)
+ {
+ return;
+ }
+ if (gLevelListUpButtonPressed == 0)
+ {
+ color = 7;
+ }
+ else
+ {
+ color = 0xD;
+ }
+
+ // loc_4D171: ; CODE XREF: drawMainMenuButtonBorders+C0j
+ // si = 0x5AC;
+ drawMainMenuButtonBorder(kMainMenuButtonBorders[8], color);
+ if (gLevelListUpButtonPressed == 0)
+ {
+ color = 0xD;
+ }
+ else
+ {
+ color = 7;
+ }
+
+ // loc_4D184: ; CODE XREF: drawMainMenuButtonBorders+D3j
+ // si = 0x5C1;
+ drawMainMenuButtonBorder(kMainMenuButtonBorders[9], color);
+ if (gLevelListDownButtonPressed == 0)
+ {
+ color = 7;
+ }
+ else
+ {
+ color = 0xD;
+ }
+
+ // loc_4D197: ; CODE XREF: drawMainMenuButtonBorders+E6j
+ // si = 0x5D6;
+ drawMainMenuButtonBorder(kMainMenuButtonBorders[10], color);
+ if (gLevelListDownButtonPressed == 0)
+ {
+ color = 0xD;
+ }
+ else
+ {
+ color = 7;
+ }
+
+ // loc_4D1AA: ; CODE XREF: drawMainMenuButtonBorders+F9j
+ // si = 0x5EB;
+ drawMainMenuButtonBorder(kMainMenuButtonBorders[11], color);
+ gLevelListButtonPressed = 0;
+}
+
+void updateHallOfFameEntries() // sub_4D1B6 proc near ; CODE XREF: changePlayerCurrentLevelState+2Ep
+{
+ // 01ED:6553
+ if (gIsPlayingDemo != 0)
+ {
+ return;
+ }
+
+ // loc_4D1BE: ; CODE XREF: updateHallOfFameEntries+5j
+ PlayerEntry *currentPlayerEntry = &gPlayerListData[gCurrentPlayerIndex];
+ if (currentPlayerEntry->completedAllLevels != 0)
+ {
+ return;
+ }
+
+ // loc_4D1D2: ; CODE XREF: updateHallOfFameEntries+19j
+ int numberOfCompletedLevels = 0;
+
+ for (int i = 0; i < kNumberOfLevels; ++i)
+ {
+ // loc_4D1E2: ; CODE XREF: updateHallOfFameEntries+33j
+ if (currentPlayerEntry->levelState[i] == PlayerLevelStateCompleted)
+ {
+ numberOfCompletedLevels++;
+ }
+ // loc_4D1E8: ; CODE XREF: updateHallOfFameEntries+2Fj
+ }
+
+ if (numberOfCompletedLevels != kNumberOfLevels)
+ {
+ return;
+ }
+
+ currentPlayerEntry->completedAllLevels = 1;
+
+ int newEntryInsertIndex = -1;
+ // mov cx, 3
+ // mov di, hallFameDataBuffer
+ for (int i = 0; i < kNumberOfHallOfFameEntries; ++i)
+ {
+ HallOfFameEntry entry = gHallOfFameData[i];
+
+ // loc_4D1FA: ; CODE XREF: updateHallOfFameEntries+78j
+ if (entry.hours == 0 && entry.minutes == 0 && entry.seconds == 0)
+ {
+ newEntryInsertIndex = i;
+ break;
+ }
+
+ // loc_4D20E: ; CODE XREF: updateHallOfFameEntries+48j
+ // ; updateHallOfFameEntries+4Ej ...
+ if (currentPlayerEntry->hours < entry.hours)
+ {
+ newEntryInsertIndex = i;
+ break;
+ }
+ else if (currentPlayerEntry->hours == entry.hours)
+ {
+ if (currentPlayerEntry->minutes < entry.minutes)
+ {
+ newEntryInsertIndex = i;
+ break;
+ }
+ else if (currentPlayerEntry->minutes == entry.minutes)
+ {
+ if (currentPlayerEntry->seconds < entry.seconds)
+ {
+ newEntryInsertIndex = i;
+ break;
+ }
+ }
+ }
+
+ // loc_4D22A: ; CODE XREF: updateHallOfFameEntries+60j
+ // ; updateHallOfFameEntries+6Aj
+ }
+
+ if (newEntryInsertIndex != -1)
+ {
+ // loc_4D232: ; CODE XREF: updateHallOfFameEntries+56j
+ // ; updateHallOfFameEntries+5Ej ...
+
+ // Shift the list to the right to make room for the new entry
+ for (int i = kNumberOfHallOfFameEntries - 1; i >= newEntryInsertIndex + 1; --i)
+ {
+ memcpy(&gHallOfFameData[i], &gHallOfFameData[i - 1], sizeof(HallOfFameEntry));
+ }
+
+ // Copy the player info into the new entry
+ HallOfFameEntry *newEntry = &gHallOfFameData[newEntryInsertIndex];
+
+ memcpy(newEntry->playerName,
+ currentPlayerEntry->name,
+ sizeof(currentPlayerEntry->name));
+ newEntry->hours = currentPlayerEntry->hours;
+ newEntry->minutes = currentPlayerEntry->minutes;
+ newEntry->seconds = currentPlayerEntry->seconds;
+ }
+
+ // loc_4D24B: ; CODE XREF: updateHallOfFameEntries+38j
+ // ; updateHallOfFameEntries+7Aj
+}
+
+void changePlayerCurrentLevelState() // sub_4D24D proc near ; CODE XREF: handleSkipLevelOptionClick+D9p
+// ; update?:loc_4E6A4p
+{
+ // 01ED:65EA
+ if (gIsPlayingDemo != 0)
+ {
+ return;
+ }
+ if (gHasUserCheated != 0)
+ {
+ return;
+ }
+ uint8_t previousState = gCurrentPlayerLevelState;
+ gCurrentPlayerLevelState = PlayerLevelStateNotCompleted;
+
+ PlayerEntry *currentPlayerEntry = &gPlayerListData[gCurrentPlayerIndex];
+ currentPlayerEntry->levelState[gCurrentSelectedLevelIndex - 1] = previousState;
+ gCurrentSelectedLevelIndex++;
+ updateHallOfFameEntries(); // 01ED:6618
+
+ // Added by me to prevent losing progress when switching levelsets after finishing a level
+ savePlayerListData();
+ saveHallOfFameData();
+}
+
+void initializeFadePalette() // proc near ; CODE XREF: start+296p
+{
+ setPalette(gBlackPalette);
+}
+
+void initializeMouse() // proc near ; CODE XREF: start+299p
+{
+ gMouseX = kScreenWidth / 2;
+ gMouseY = kScreenHeight / 2;
+ if (getFullscreenMode())
+ {
+ centerMouse();
+ }
+ hideMouse();
+ handleSystemEvents();
+}
+
+void getMouseStatus(uint16_t *mouseX, uint16_t *mouseY, uint16_t *mouseButtonStatus) // proc near ; CODE XREF: waitForKeyMouseOrJoystick:mouseIsClickedp
+// ; waitForKeyMouseOrJoystick+3Ep ...
+{
+ // Returns coordinate X in CX (0-320) and coordinate Y in DX (0-200).
+ // Also button status in BX.
+
+ handleSystemEvents();
+
+ int x, y;
+ uint8_t leftButtonPressed, rightButtonPressed;
+
+ getMouseState(&x, &y, &leftButtonPressed, &rightButtonPressed);
+
+ int windowWidth, windowHeight;
+ getWindowSize(&windowWidth, &windowHeight);
+
+ float controllerX = 0, controllerY = 0;
+ uint8_t controllerLeftButton = 0;
+ uint8_t controllerRightButton = 0;
+ gameControllerEmulateMouse(&controllerX,
+ &controllerY,
+ &controllerLeftButton,
+ &controllerRightButton);
+
+ uint8_t shouldCorrectMousePosition = 0;
+
+ if (controllerX != 0.0 || controllerY != 0.0)
+ {
+ float speed = (float)windowWidth / 1280;
+
+ x += speed * controllerX;
+ y += speed * controllerY;
+
+ shouldCorrectMousePosition = 1;
+ }
+
+ // Read touch screen where available
+ float touchScreenX, touchScreenY;
+ uint8_t touchScreenPressed = readTouchScreen(&touchScreenX, &touchScreenY);
+ if (touchScreenPressed)
+ {
+ x = touchScreenX * windowWidth;
+ y = touchScreenY * windowHeight;
+
+ shouldCorrectMousePosition = 1;
+ }
+
+ if (shouldCorrectMousePosition)
+ {
+ x = CLAMP(x, 0, windowWidth);
+ y = CLAMP(y, 0, windowHeight);
+
+ // Correct mouse position for future events
+ moveMouse(x, y);
+ }
+
+ if (windowWidth != 0 && windowHeight != 0)
+ {
+ x = x * kScreenWidth / windowWidth;
+ y = y * kScreenHeight / windowHeight;
+ }
+
+ leftButtonPressed = (leftButtonPressed || controllerLeftButton || touchScreenPressed);
+ rightButtonPressed = (rightButtonPressed || controllerRightButton);
+
+ // Limit coordinates as in the original game
+ x = CLAMP(x, 16, 304);
+ y = CLAMP(y, 8, 192);
+
+ if (mouseX != NULL)
+ {
+ *mouseX = x;
+ }
+ if (mouseY != NULL)
+ {
+ *mouseY = y;
+ }
+
+ if (mouseButtonStatus != NULL)
+ {
+ *mouseButtonStatus = (rightButtonPressed << 1 | leftButtonPressed);
+ }
+}
+
+#define COPY_LEVEL_DATA(__dest, __size) \
+ do \
+ { \
+ memcpy(__dest, &levelFileData[pointer], __size); \
+ pointer += __size; \
+ } while (0)
+
+#define COPY_LEVEL_DATA_UINT16(__dest) \
+ do \
+ { \
+ COPY_LEVEL_DATA(&__dest, sizeof(uint16_t)); \
+ __dest = convert16LE(__dest); \
+ } while (0)
+
+void mapLevelFileData(char *levelFileData, Level *level)
+{
+ size_t pointer = 0;
+
+ COPY_LEVEL_DATA(level->tiles, sizeof(level->tiles));
+ COPY_LEVEL_DATA(level->unused, sizeof(level->unused));
+ COPY_LEVEL_DATA(&level->initialGravitation, sizeof(level->initialGravitation));
+ COPY_LEVEL_DATA(&level->speedFixMagicNumber, sizeof(level->speedFixMagicNumber));
+ COPY_LEVEL_DATA(level->name, sizeof(level->name));
+ COPY_LEVEL_DATA(&level->freezeZonks, sizeof(level->freezeZonks));
+ COPY_LEVEL_DATA(&level->numberOfInfotrons, sizeof(level->numberOfInfotrons));
+ COPY_LEVEL_DATA(&level->numberOfSpecialPorts, sizeof(level->numberOfSpecialPorts));
+ for (int idx = 0; idx < kLevelMaxNumberOfSpecialPorts; ++idx)
+ {
+ SpecialPortInfo *specialPortInfo = &level->specialPortsInfo[idx];
+ COPY_LEVEL_DATA_UINT16(specialPortInfo->position);
+ COPY_LEVEL_DATA(&specialPortInfo->gravity, sizeof(specialPortInfo->gravity));
+ COPY_LEVEL_DATA(&specialPortInfo->freezeZonks, sizeof(specialPortInfo->freezeZonks));
+ COPY_LEVEL_DATA(&specialPortInfo->freezeEnemies, sizeof(specialPortInfo->freezeEnemies));
+ COPY_LEVEL_DATA(&specialPortInfo->unused, sizeof(specialPortInfo->unused));
+ }
+ COPY_LEVEL_DATA(&level->scrambledSpeed, sizeof(level->scrambledSpeed));
+ COPY_LEVEL_DATA(&level->scrambledChecksum, sizeof(level->scrambledChecksum));
+ COPY_LEVEL_DATA_UINT16(level->randomSeed);
+
+ assert(pointer == kLevelDataLength);
+}
+
+#undef COPY_LEVEL_DATA_UINT16
+#undef COPY_LEVEL_DATA
+
+void readLevels() // proc near ; CODE XREF: start:loc_46F3Ep
+ // ; fetchAndInitializeLevelp
+{
+ // 01ED:68E5
+ char *filename = "";
+ FILE *file = NULL;
+ char levelFileData[kLevelDataLength];
+
+ if (gIsPlayingDemo != 0 && (gSelectedOriginalDemoLevelNumber & 0xFF) == 0 && gIsSPDemoAvailableToRun == 0)
+ {
+ // Demos with the new format
+ Level *level = &gDemos.level[gDemoIndexOrDemoLevelNumber];
+
+ memcpy(&levelFileData, level, kLevelDataLength);
+
+ strcpy(gCurrentDemoLevelName, ".SP");
+
+ memcpy(&gCurrentDemoLevelName[4], level->name, sizeof(level->name));
+ }
+ else
+ {
+ if (gIsPlayingDemo == 0 || gIsSPDemoAvailableToRun != 0)
+ {
+ // loc_4D59F: ; CODE XREF: readLevels+5j
+ // ; readLevels+13j
+ filename = gLevelsDatFilename; // lea dx, aLevels_dat_0 ; "LEVELS.DAT"
+ }
+
+ if (gIsPlayingDemo != 0 && (gSelectedOriginalDemoLevelNumber & 0xFF) != 0) // cmp byte ptr gSelectedOriginalDemoLevelNumber, 0
+ {
+ // loc_4D599: ; CODE XREF: readLevels+Cj
+ filename = gLevelsDatFilename; // lea dx, aLevels_dat ; "LEVELS.DAT"
+ }
+ // loc_4D5A3: ; CODE XREF: readLevels+55j
+ else if (gIsSPDemoAvailableToRun != 0)
+ {
+ filename = demoFileName;
+ }
+ else if (gSelectedOriginalDemoFromCommandLineLevelNumber != 0)
+ {
+ filename = gLevelsDatFilename; // lea dx, aLevels_dat ; "LEVELS.DAT"
+ }
+
+ // loc_4D5BB: ; CODE XREF: readLevels+63j
+ file = openWritableFileWithReadonlyFallback(filename, "rb");
+ if (file == NULL)
+ {
+ exitWithError("Error opening %s\n", filename);
+ }
+
+ uint8_t levelIndex = 0;
+
+ // loc_4D5C2: ; CODE XREF: readLevels+75j
+ if (gIsPlayingDemo != 0)
+ {
+ levelIndex = gDemoIndexOrDemoLevelNumber;
+ }
+ else
+ {
+ // loc_4D5D1: ; CODE XREF: readLevels+82j
+ levelIndex = gCurrentSelectedLevelIndex;
+ }
+
+ // loc_4D5D4: ; CODE XREF: readLevels+87j
+ if (gIsSPDemoAvailableToRun != 0)
+ {
+ levelIndex = gSelectedOriginalDemoFromCommandLineLevelNumber;
+ if (levelIndex == 0)
+ {
+ levelIndex++;
+ }
+ }
+
+ // loc_4D5E3: ; CODE XREF: readLevels+91j
+ // ; readLevels+98j
+ levelIndex--; // Levels anywhere else are 1-index, we need them to start from 0 here
+ size_t fileOffset = levelIndex * kLevelDataLength;
+ // 01ED:699A
+ int result = fseek(file, fileOffset, SEEK_SET);
+ if (result != 0)
+ {
+ exitWithError("Error seeking %s\n", filename);
+ }
+
+ // loc_4D604: ; CODE XREF: readLevels+B7j
+ // 01ED:69AE
+ size_t bytes = fileReadBytes(levelFileData, kLevelDataLength, file);
+ if (bytes < kLevelDataLength)
+ {
+ exitWithError("Error reading %s\n", filename);
+ }
+
+ Level tmpLevel;
+ mapLevelFileData(levelFileData, &tmpLevel);
+ gIsGravityEnabled = tmpLevel.initialGravitation;
+ gAreZonksFrozen = tmpLevel.freezeZonks;
+ gNumberOfInfoTrons = tmpLevel.numberOfInfotrons;
+ gNumberOfSpecialPorts = tmpLevel.numberOfSpecialPorts;
+ gRandomSeed = tmpLevel.randomSeed;
+
+ // loc_4D618: ; CODE XREF: readLevels+CBj
+ if ((gSelectedOriginalDemoLevelNumber & 0xFF) != 0) // cmp byte ptr gSelectedOriginalDemoLevelNumber, 0
+ {
+ gSelectedOriginalDemoLevelNumber |= 0xFF00; // mov byte ptr gSelectedOriginalDemoLevelNumber+1, 0FFh
+
+ gDemoIndexOrDemoLevelNumber = gSelectedOriginalDemoIndex;
+
+ Level *level = &gDemos.level[gDemoIndexOrDemoLevelNumber];
+ mapLevelFileData(levelFileData, level);
+ }
+ }
+
+ char *levelName = NULL;
+
+ // loc_4D64B: ; CODE XREF: readLevels+4Ej
+ // ; readLevels+D5j
+ if (gIsPlayingDemo != 0)
+ {
+ gRandomGeneratorSeed = gRandomSeed;
+ levelName = gCurrentDemoLevelName;
+ }
+ else
+ {
+ // loc_4D65D: ; CODE XREF: readLevels+108j
+ levelName = gCurrentLevelName;
+ }
+
+ // loc_4D660: ; CODE XREF: readLevels+113j
+ if (gSelectedOriginalDemoLevelNumber != 0 || (gIsSPDemoAvailableToRun != 0 && gSelectedOriginalDemoFromCommandLineLevelNumber != 0))
+ {
+ // loc_4D679: ; CODE XREF: readLevels+121j
+ strcpy(gCurrentDemoLevelName, "BIN");
+ levelName += 4;
+ }
+ else if (gIsSPDemoAvailableToRun == 0)
+ {
+ // loc_4D68C: ; CODE XREF: readLevels+128j
+ levelName += 4; // Skips the number directly to the title (from pointing "005 ------- EASY DEAL -------" to pointing "------- EASY DEAL -------")
+ }
+ else if (gSelectedOriginalDemoFromCommandLineLevelNumber == 0)
+ {
+ // loc_4D682: ; CODE XREF: readLevels+12Fj
+ strcpy(gCurrentDemoLevelName, ".SP");
+ levelName += 4;
+ }
+
+ // loc_4D68F: ; CODE XREF: readLevels+142j
+ mapLevelFileData(levelFileData, &gCurrentLevel);
+ memcpy(levelName, gCurrentLevel.name, sizeof(gCurrentLevel.name));
+
+ // The reason this is 1536 (level file size) and not 1440 (actual gamefield size of 60 * 24 tiles) is because
+ // the game was written like this, and some levels rely on this behavior by removing the bottom border of the level
+ // and using some unused bytes from those 1536 to store tile data.
+ // An example of this is "HIDDEN TRACK" (07/07s062-1.sp)
+ //
+ for (int i = 0; i < kLevelDataLength; ++i)
+ {
+ // loc_4D6B8: ; CODE XREF: readLevels+172j
+ StatefulLevelTile *tile = &gCurrentLevelState[i];
+ tile->tile = levelFileData[i];
+ tile->state = 0;
+ }
+
+ memset(&gExplosionTimers, 0, sizeof(gExplosionTimers)); // rep stosb
+
+ if (gIsPlayingDemo == 0 || (gSelectedOriginalDemoLevelNumber & 0xFF) != 0 || gIsSPDemoAvailableToRun != 0)
+ {
+ // loc_4D6DC: ; CODE XREF: readLevels+184j
+ // ; readLevels+18Bj
+ if (fclose(file) != 0)
+ {
+ exitWithError("Error closing %s\n", filename);
+ }
+ }
+
+ // loc_4D6EA: ; CODE XREF: readLevels+192j
+ // ; readLevels+19Dj
+ gSelectedOriginalDemoLevelNumber &= 0xFF00; // mov byte ptr gSelectedOriginalDemoLevelNumber, 0
+}
+
+void soundShutdown() // proc near ; CODE XREF: start+48Ep
+// ; loadScreen2-7DAp
+{
+ stopMusicAndSounds();
+}
+
+void activateInternalStandardSound() // loadBeep proc near ; CODE XREF: readConfig:loc_4751Ap
+// ; readConfig:loc_47551p ...
+{
+ stopMusicAndSounds();
+ setSoundType(SoundTypeInternalStandard, SoundTypeInternalStandard);
+ playMusicIfNeeded();
+ gCurrentSoundPriority = 0;
+ gCurrentSoundDuration = 0;
+}
+
+void activateInternalSamplesSound() // loadBeep2 proc near ; CODE XREF: readConfig+4Cp handleOptionsSamplesClickp
+{
+ stopMusicAndSounds();
+ setSoundType(SoundTypeInternalStandard, SoundTypeInternalSamples);
+ playMusicIfNeeded();
+ gCurrentSoundPriority = 0;
+ gCurrentSoundDuration = 0;
+}
+
+void activateAdlibSound() // loadAdlib proc near ; CODE XREF: readConfig+56p handleOptionsAdlibClickp
+{
+ stopMusicAndSounds(); // 01ED:6D06
+ setSoundType(SoundTypeAdlib, SoundTypeAdlib);
+ playMusicIfNeeded();
+ gCurrentSoundPriority = 0;
+ gCurrentSoundDuration = 0;
+}
+
+void activateSoundBlasterSound() // loadBlaster proc near ; CODE XREF: readConfig+60p handleOptionsSoundBlasterClickp
+{
+ // 01ED:6D39
+ stopMusicAndSounds();
+ setSoundType(SoundTypeAdlib, SoundTypeSoundBlaster);
+ playMusicIfNeeded();
+ gCurrentSoundPriority = 0;
+ gCurrentSoundDuration = 0;
+}
+
+void activateRolandSound() // loadRoland proc near ; CODE XREF: readConfig+6Ap handleOptionsRolandClickp
+{
+ stopMusicAndSounds();
+ setSoundType(SoundTypeRoland, SoundTypeRoland);
+ playMusicIfNeeded();
+ gCurrentSoundPriority = 0;
+ gCurrentSoundDuration = 0;
+}
+
+void activateCombinedSound() // loadCombined proc near ; CODE XREF: readConfig+74p handleOptionsCombinedClickp
+{
+ stopMusicAndSounds();
+ setSoundType(SoundTypeRoland, SoundTypeSoundBlaster);
+ playMusicIfNeeded();
+ gCurrentSoundPriority = 0;
+ gCurrentSoundDuration = 0;
+}
+
+void stopMusicAndSounds() // sound1 proc near ; CODE XREF: soundShutdown?+5p
+ // ; code:6CC7p ...
+{
+ // 01ED:6E4E
+ setSoundType(SoundTypeNone, SoundTypeNone);
+}
+
+void playMusicIfNeeded() // sound2 proc near ; CODE XREF: start+39Bp start+410p ...
+{
+ // 01ED:6EA8
+ if (isMusicEnabled != 1)
+ {
+ return;
+ }
+
+ playMusic();
+}
+
+void playExplosionSound() // sound4 proc near ; CODE XREF: detonateBigExplosion+2EDp code:5ADEp ...
+{
+ if (isFXEnabled != 1)
+ {
+ return;
+ }
+
+ // loc_4DB7F: ; CODE XREF: playExplosionSound+5j
+ if (gCurrentSoundPriority >= 5)
+ {
+ return;
+ }
+
+ // loc_4DB87: ; CODE XREF: playExplosionSound+Dj
+ gCurrentSoundDuration = 0xF;
+ gCurrentSoundPriority = 5;
+
+ playSoundEffect(SoundEffectExplosion);
+}
+
+void playInfotronSound() // sound5 proc near ; CODE XREF: update?:loc_4E55Cp
+ // ; update?:loc_4E588p ...
+{
+ if (isFXEnabled == 0)
+ {
+ return;
+ }
+
+ // loc_4DBE8: ; CODE XREF: playInfotronSound+5j
+ if (gCurrentSoundPriority >= 5)
+ {
+ return;
+ }
+
+ // loc_4DBF0: ; CODE XREF: playInfotronSound+Dj
+ gCurrentSoundDuration = 0xF;
+ gCurrentSoundPriority = 4;
+
+ playSoundEffect(SoundEffectInfotron);
+}
+
+void playPushSound() // sound6 proc near ; CODE XREF: update?+B8Bp
+ // ; update?+136Cp
+{
+ if (isFXEnabled == 0)
+ {
+ return;
+ }
+
+ // loc_4DC51: ; CODE XREF: playPushSound+5j
+ if (gCurrentSoundPriority >= 2)
+ {
+ return;
+ }
+
+ // loc_4DC59: ; CODE XREF: playPushSound+Dj
+ gCurrentSoundDuration = 7;
+ gCurrentSoundPriority = 2;
+ playSoundEffect(SoundEffectPush);
+}
+
+void playFallSound() // sound7 proc near ; CODE XREF: movefun:loc_48125p
+// ; movefun2:loc_48573p
+{
+ if (isFXEnabled == 0)
+ {
+ return;
+ }
+
+ // loc_4DCBA: ; CODE XREF: playFallSound+5j
+ if (gCurrentSoundPriority >= 2)
+ {
+ return;
+ }
+
+ // loc_4DCC2: ; CODE XREF: playFallSound+Dj
+ gCurrentSoundDuration = 7;
+ gCurrentSoundPriority = 2;
+ playSoundEffect(SoundEffectFall);
+}
+
+void playBugSound() // sound8 proc near ; CODE XREF: movefun7:loc_4A0ABp
+{
+ if (isFXEnabled == 0)
+ {
+ return;
+ }
+
+ // loc_4DD23: ; CODE XREF: playBugSound+5j
+ if (gCurrentSoundPriority >= 3)
+ {
+ return;
+ }
+
+ // loc_4DD2B: ; CODE XREF: playBugSound+Dj
+ gCurrentSoundDuration = 3;
+ gCurrentSoundPriority = 3;
+
+ playSoundEffect(SoundEffectBug);
+}
+
+void playBaseSound() // sound9 proc near ; CODE XREF: runLevel+2F4p
+ // ; update?:loc_4E3E1p ...
+{
+ if (isFXEnabled == 0)
+ {
+ return;
+ }
+
+ // xxxxxxxxdcdc: ; CODE XREF: playBaseSound+5j
+ if (gCurrentSoundPriority >= 1)
+ {
+ return;
+ }
+
+ // loc_4DD94: ; CODE XREF: playBaseSound+Dj
+ gCurrentSoundDuration = 3;
+ gCurrentSoundPriority = 1;
+
+ playSoundEffect(SoundEffectBase);
+}
+
+void playExitSound() // sound10 proc near ; CODE XREF: update?+7EBp
+{
+ if (isFXEnabled == 0)
+ {
+ return;
+ }
+
+ // loc_4DDF5: ; CODE XREF: playExitSound+5j
+ gCurrentSoundDuration = 0xFA;
+ gCurrentSoundPriority = 0xA;
+ stopMusic();
+
+ playSoundEffect(SoundEffectExit);
+}
+
+int16_t updateMurphy(int16_t position) // update? proc near ; CODE XREF: updateMovingObjects+Ep
+{
+ // 01ED:722D
+
+ StatefulLevelTile *murphyTile = &gCurrentLevelState[position];
+ StatefulLevelTile *leftTile = &gCurrentLevelState[position - 1];
+ StatefulLevelTile *rightTile = &gCurrentLevelState[position + 1];
+ StatefulLevelTile *belowTile = &gCurrentLevelState[position + kLevelWidth];
+ StatefulLevelTile *aboveTile = &gCurrentLevelState[position - kLevelWidth];
+
+ if (murphyTile->tile != LevelTileTypeMurphy)
+ {
+ gIsMurphyUpdated = 0;
+ return position;
+ }
+
+ // hasValidMurphy: ; CODE XREF: update?+5j
+ gIsMurphyUpdated = 1;
+ gMurphyPreviousLocation = position;
+ if (murphyTile->state != 0 || murphyTile->tile != LevelTileTypeMurphy)
+ {
+ return updateMurphyAnimation(position);
+ }
+
+ // loc_4DEB4: ; CODE XREF: update?+1Fj
+ gScratchGravity = 0;
+
+ if (gIsGravityEnabled != 0 && aboveTile->tile != LevelTileTypePortUp && aboveTile->tile != LevelTileTypePortVertical && aboveTile->tile != LevelTileTypePort4Way && (belowTile->state == 0 && belowTile->tile == LevelTileTypeSpace))
+ {
+ gScratchGravity = 1;
+ }
+
+ // loc_4DEE1: ; CODE XREF: update?+2Ej update?+35j ...
+ UserInput userInput = gCurrentUserInput;
+
+ if (userInput == UserInputNone)
+ {
+ // loc_4DEED: ; CODE XREF: update?+58j
+ gPreviousUserInputWasNone = 1;
+ if (gScratchGravity != 0)
+ {
+ MurphyAnimationDescriptor unknownMurphyData;
+ // loc_4E38A: ; CODE XREF: update?+69j update?+2FFj
+ if (gIsMurphyLookingLeft != 0)
+ {
+ unknownMurphyData = kMurphyAnimationDescriptors[3]; // dx = 0x0E2E;
+ }
+ else
+ {
+ // loc_4E396: ; CODE XREF: update?+4FFj
+ unknownMurphyData = kMurphyAnimationDescriptors[4]; // dx = 0x0E3E;
+ }
+
+ // loc_4E399: ; CODE XREF: update?+504j
+ belowTile->state = 3;
+ belowTile->tile = LevelTileTypeMurphy;
+ murphyTile->state = 3;
+ murphyTile->tile = LevelTileTypeSpace;
+ gMurphyCounterToStartPushAnimation = 0;
+ gIsMurphyGoingThroughPortal = 0;
+ return updateMurphyAnimationInfo(position + kLevelWidth, unknownMurphyData);
+ }
+
+ // loc_4DEFC: ; CODE XREF: update?+67j
+ if ((gFrameCounter & 3) != 0)
+ {
+ return position;
+ }
+
+ // loc_4DF05: ; CODE XREF: update?+72j
+ gMurphyYawnAndSleepCounter++;
+ if (gMurphyYawnAndSleepCounter == 4)
+ {
+ // si = kMurphyStillSpriteCoordinates;
+ drawMovingFrame(304, 132, position);
+
+ return position;
+ }
+ // loc_4DF1E: ; CODE XREF: update?+7Ej
+ else if (gMurphyYawnAndSleepCounter <= 0x01F4)
+ {
+ return position;
+ }
+ // loc_4DF27: ; CODE XREF: update?+94j
+ else if (gMurphyYawnAndSleepCounter <= 0x020A)
+ {
+ // Yawning animation
+ uint16_t currentFrame = gMurphyYawnAndSleepCounter - 0x1F4;
+ currentFrame = currentFrame >> 1;
+
+ AnimationFrameCoordinates animationFrameCoordinates = kMurphyAnimationFrameCoordinates[34];
+ Point frameCoordinates = animationFrameCoordinates.coordinates[currentFrame];
+
+ drawMovingFrame(frameCoordinates.x, frameCoordinates.y, position);
+
+ return position;
+ }
+ // loc_4DF4A: ; CODE XREF: update?+9Dj
+ else if (gMurphyYawnAndSleepCounter <= 0x03E8)
+ {
+ return position;
+ }
+ // loc_4DF53: ; CODE XREF: update?+C0j
+ else if (gMurphyYawnAndSleepCounter <= 0x03FE)
+ {
+ // Yawning animation
+ uint16_t currentFrame = gMurphyYawnAndSleepCounter - 0x3E8;
+ currentFrame = currentFrame >> 1;
+
+ AnimationFrameCoordinates animationFrameCoordinates = kMurphyAnimationFrameCoordinates[34];
+ Point frameCoordinates = animationFrameCoordinates.coordinates[currentFrame];
+
+ drawMovingFrame(frameCoordinates.x, frameCoordinates.y, position);
+
+ return position;
+ }
+ // loc_4DF76: ; CODE XREF: update?+C9j
+ else if (gMurphyYawnAndSleepCounter <= 0x0640)
+ {
+ return position;
+ }
+ // loc_4DF7F: ; CODE XREF: update?+ECj
+ else if (gMurphyYawnAndSleepCounter <= 0x0656)
+ {
+ // Yawning animation
+ uint16_t currentFrame = gMurphyYawnAndSleepCounter - 0x640;
+ currentFrame = currentFrame >> 1;
+
+ AnimationFrameCoordinates animationFrameCoordinates = kMurphyAnimationFrameCoordinates[34];
+ Point frameCoordinates = animationFrameCoordinates.coordinates[currentFrame];
+
+ drawMovingFrame(frameCoordinates.x, frameCoordinates.y, position);
+
+ return position;
+ }
+ // loc_4DFA2: ; CODE XREF: update?+F5j
+ else if (gMurphyYawnAndSleepCounter > 0x0676)
+ {
+ return position;
+ }
+ else if (leftTile->state != 0 || leftTile->tile != LevelTileTypeSpace)
+ {
+ // loc_4DFBF: ; CODE XREF: update?+11Fj
+ // Sleep to left animation
+ uint16_t currentFrame = gMurphyYawnAndSleepCounter - 0x656;
+ currentFrame = currentFrame >> 4;
+
+ AnimationFrameCoordinates animationFrameCoordinates = kMurphyAnimationFrameCoordinates[35];
+ Point frameCoordinates = animationFrameCoordinates.coordinates[currentFrame];
+
+ drawMovingFrame(frameCoordinates.x, frameCoordinates.y, position);
+
+ return position;
+ }
+ else if (rightTile->state != 0 || rightTile->tile != LevelTileTypeSpace)
+ {
+ // loc_4DFE0: ; CODE XREF: update?+126j
+ // Sleep to right animation
+ uint16_t currentFrame = gMurphyYawnAndSleepCounter - 0x656;
+ currentFrame = currentFrame >> 4;
+
+ AnimationFrameCoordinates animationFrameCoordinates = kMurphyAnimationFrameCoordinates[36];
+ Point frameCoordinates = animationFrameCoordinates.coordinates[currentFrame];
+
+ drawMovingFrame(frameCoordinates.x, frameCoordinates.y, position);
+
+ return position;
+ }
+ else
+ {
+ gMurphyYawnAndSleepCounter = 0x24; // 36
+ return position;
+ }
+ }
+
+ // loc_4E001: ; CODE XREF: update?+5Aj
+ // 01ED:739E
+ if (gScratchGravity != 0 && (belowTile->state == 0 && belowTile->tile == LevelTileTypeSpace))
+ {
+ if (userInput != UserInputUp || (aboveTile->state != 0 || aboveTile->tile != LevelTileTypeBase))
+ {
+ // loc_4E01B: ; CODE XREF: update?+182j
+ if (userInput != UserInputLeft || (leftTile->state != 0 || leftTile->tile != LevelTileTypeBase))
+ {
+ // loc_4E027: ; CODE XREF: update?+18Ej
+ if (userInput != UserInputRight || (rightTile->state != 0 || rightTile->tile != LevelTileTypeBase))
+ {
+ // loc_4E033: ; CODE XREF: update?+19Aj
+ userInput = UserInputDown;
+ }
+ }
+ }
+ }
+
+ // loc_4E035: ; CODE XREF: update?+176j update?+17Dj ...
+ // 01ED:73D2
+ if (userInput == UserInputUp)
+ {
+ gPreviousUserInputWasNone = 0;
+ return handleMurphyDirectionUp(position);
+ }
+ // loc_4E041: ; CODE XREF: update?+1A8j
+ else if (userInput == UserInputLeft)
+ {
+ gPreviousUserInputWasNone = 0;
+ return handleMurphyDirectionLeft(position);
+ }
+ // loc_4E04E: ; CODE XREF: update?+1B4j
+ else if (userInput == UserInputDown)
+ {
+ gPreviousUserInputWasNone = 0;
+ return handleMurphyDirectionDown(position);
+ }
+ // loc_4E05B: ; CODE XREF: update?+1C1j
+ else if (userInput == UserInputRight)
+ {
+ gPreviousUserInputWasNone = 0;
+ return handleMurphyDirectionRight(position);
+ }
+ // loc_4E068: ; CODE XREF: update?+1CEj
+ else if (userInput == UserInputSpaceUp)
+ {
+ gPreviousUserInputWasNone = 0;
+ // loc_4E260: ; CODE XREF: update?+1E2j
+ // mov ax, leveldata[si-78h]
+ if (aboveTile->state == 0 && aboveTile->tile == LevelTileTypeBase)
+ {
+ // loc_4E4BD: ; CODE XREF: update?+3D9j
+ // push si
+ // mov di, [si+6155h]
+ // si = word_51840;
+ drawMovingFrame(160, 64, position);
+ // pop si
+ playBaseSound();
+ // dx = 0x0ECE;
+ murphyTile->state = 0x10;
+ gMurphyCounterToStartPushAnimation = 0;
+ gIsMurphyGoingThroughPortal = 0;
+ return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[13]);
+ }
+ // loc_4E26C: ; CODE XREF: update?+3D7j
+ else if (aboveTile->tile == LevelTileTypeBug)
+ {
+ // loc_4E4AC: ; CODE XREF: update?+3E0j
+ if (aboveTile->state < 0x80)
+ {
+ detonateBigExplosion(position);
+ return position;
+ }
+
+ // loc_4E4B7: ; CODE XREF: update?+621j
+ aboveTile->state = 0;
+ aboveTile->tile = LevelTileTypeBase;
+
+ // loc_4E4BD: ; CODE XREF: update?+3D9j
+ // push si
+ // mov di, [si+6155h]
+ // si = word_51840;
+ drawMovingFrame(160, 64, position);
+ // pop si
+ playBaseSound();
+ // dx = 0x0ECE;
+ murphyTile->state = 0x10;
+ gMurphyCounterToStartPushAnimation = 0;
+ gIsMurphyGoingThroughPortal = 0;
+ return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[13]);
+ }
+ // loc_4E273: ; CODE XREF: update?+3DEj
+ else if (aboveTile->state == 0 && aboveTile->tile == LevelTileTypeInfotron)
+ {
+ // loc_4E5F4: ; CODE XREF: update?+3E8j
+ // push si
+ // mov di, [si+6155h]
+ // si = word_51840;
+ drawMovingFrame(160, 64, position);
+ // pop si
+ playInfotronSound();
+ // dx = 0x0F6E;
+ murphyTile->state = 0x14;
+ aboveTile->state = 0xFF;
+ gMurphyCounterToStartPushAnimation = 0;
+ gIsMurphyGoingThroughPortal = 0;
+ return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[23]);
+ }
+ // loc_4E27B: ; CODE XREF: update?+3E6j
+ else if (aboveTile->tile == LevelTileTypeTerminal)
+ {
+ // loc_4E712: ; CODE XREF: update?+249j update?+3EFj
+ // push si
+ // mov di, [si+6155h]
+ // si = word_51840;
+ drawMovingFrame(160, 64, position);
+ // pop si
+ if (gAreYellowDisksDetonated != 0)
+ {
+ gMurphyYawnAndSleepCounter = 0xA;
+ return position;
+ }
+
+ // loc_4E72D: ; CODE XREF: update?+894j
+ // push si
+ // mov di, [si+60DDh]
+ // si = kTerminalOnSpriteCoordinates;
+ drawMovingFrame(256, 388, position - kLevelWidth);
+ // pop si
+ detonateYellowDisks();
+ return position;
+ }
+ // loc_4E282: ; CODE XREF: update?+3EDj
+ else if (aboveTile->tile == LevelTileTypeRedDisk)
+ {
+ // loc_4E8B6: ; CODE XREF: update?+3F6j
+ // dx = 0x106E;
+ murphyTile->state = 0x20;
+ aboveTile->state = 3;
+ gMurphyCounterToStartPushAnimation = 0;
+ gIsMurphyGoingThroughPortal = 0;
+ return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[39]);
+ }
+ else
+ {
+ return position;
+ }
+ }
+ // loc_4E075: ; CODE XREF: update?+1DBj
+ else if (userInput == UserInputSpaceLeft)
+ {
+ gPreviousUserInputWasNone = 0;
+ // loc_4E28A: ; CODE XREF: update?+1EFj
+ gIsMurphyLookingLeft = 1;
+ // mov ax, [si+1832h]
+ if (leftTile->state == 0 && leftTile->tile == LevelTileTypeBase)
+ {
+ // loc_4E4E9: ; CODE XREF: update?+409j
+ // push si
+ // mov di, [si+6155h]
+ // si = word_51842;
+ drawMovingFrame(208, 16, position);
+ // pop si
+ playBaseSound();
+ // dx = 0x0EDE;
+ murphyTile->state = 0x11;
+ gMurphyCounterToStartPushAnimation = 0;
+ gIsMurphyGoingThroughPortal = 0;
+ return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[14]);
+ }
+ // loc_4E29C: ; CODE XREF: update?+407j
+ else if (leftTile->tile == LevelTileTypeBug)
+ {
+ // loc_4E4D8: ; CODE XREF: update?+410j
+ if (leftTile->state < 0x80)
+ {
+ detonateBigExplosion(position);
+ return position;
+ }
+
+ // loc_4E4E3: ; CODE XREF: update?+64Dj
+ leftTile->state = 0;
+ leftTile->tile = LevelTileTypeBase;
+
+ // loc_4E4E9: ; CODE XREF: update?+409j
+ // push si
+ // mov di, [si+6155h]
+ // si = word_51842;
+ drawMovingFrame(208, 16, position);
+ // pop si
+ playBaseSound();
+ // dx = 0x0EDE;
+ murphyTile->state = 0x11;
+ gMurphyCounterToStartPushAnimation = 0;
+ gIsMurphyGoingThroughPortal = 0;
+ return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[14]);
+ }
+ // loc_4E2A3: ; CODE XREF: update?+40Ej
+ else if (leftTile->state == 0 && leftTile->tile == LevelTileTypeInfotron)
+ {
+ // loc_4E614: ; CODE XREF: update?+418j
+ // push si
+ // mov di, [si+6155h]
+ // si = word_51842;
+ drawMovingFrame(208, 16, position);
+ // pop si
+ playInfotronSound();
+ // dx = 0x0F7E;
+ murphyTile->state = 0x15;
+ leftTile->state = 0xFF;
+ gMurphyCounterToStartPushAnimation = 0;
+ gIsMurphyGoingThroughPortal = 0;
+ return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[24]);
+ }
+ // loc_4E2AB: ; CODE XREF: update?+416j
+ else if (leftTile->tile == LevelTileTypeTerminal)
+ {
+ // loc_4E73C: ; CODE XREF: update?+2B9j update?+41Fj
+ // push si
+ // mov di, [si+6155h]
+ // si = word_51842;
+ drawMovingFrame(208, 16, position);
+ // pop si
+ if (gAreYellowDisksDetonated != 0)
+ {
+ gMurphyYawnAndSleepCounter = 0xA;
+ return position;
+ }
+
+ // loc_4E757: ; CODE XREF: update?+8BEj
+ // push si
+ // mov di, [si+6153h]
+ // si = kTerminalOnSpriteCoordinates;
+ drawMovingFrame(256, 388, position - 1);
+ // pop si
+ detonateYellowDisks();
+ return position;
+ }
+ // loc_4E2B2: ; CODE XREF: update?+41Dj
+ else if (leftTile->tile == LevelTileTypeRedDisk)
+ {
+ // loc_4E8C5: ; CODE XREF: update?+426j
+ // dx = 0x107E;
+ murphyTile->state = 0x21;
+ leftTile->state = 3;
+ gMurphyCounterToStartPushAnimation = 0;
+ gIsMurphyGoingThroughPortal = 0;
+ return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[40]);
+ }
+ else
+ {
+ return position;
+ }
+ }
+ // loc_4E082: ; CODE XREF: update?+1E8j
+ else if (userInput == UserInputSpaceDown)
+ {
+ gPreviousUserInputWasNone = 0;
+ // loc_4E2BA: ; CODE XREF: update?+1FCj
+ // mov ax, [si+18ACh]
+ if (belowTile->state == 0 && belowTile->tile == LevelTileTypeBase)
+ {
+ // loc_4E515: ; CODE XREF: update?+433j
+ // push si
+ // mov di, [si+6155h]
+ // si = word_51844;
+ drawMovingFrame(176, 64, position);
+ // pop si
+ playBaseSound();
+ // dx = 0x0EEE;
+ murphyTile->state = 0x12;
+ gMurphyCounterToStartPushAnimation = 0;
+ gIsMurphyGoingThroughPortal = 0;
+ return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[15]);
+ }
+ // loc_4E2C6: ; CODE XREF: update?+431j
+ else if (belowTile->tile == LevelTileTypeBug)
+ {
+ // loc_4E504: ; CODE XREF: update?+43Aj
+ if (belowTile->state < 0x80)
+ {
+ detonateBigExplosion(position);
+ return position;
+ }
+
+ // loc_4E50F: ; CODE XREF: update?+679j
+ belowTile->state = 0;
+ belowTile->tile = LevelTileTypeBase;
+
+ // loc_4E515: ; CODE XREF: update?+433j
+ // push si
+ // mov di, [si+6155h]
+ // si = word_51844;
+ drawMovingFrame(176, 64, position);
+ // pop si
+ playBaseSound();
+ // dx = 0x0EEE;
+ murphyTile->state = 0x12;
+ gMurphyCounterToStartPushAnimation = 0;
+ gIsMurphyGoingThroughPortal = 0;
+ return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[15]);
+ }
+ // loc_4E2CD: ; CODE XREF: update?+438j
+ else if (belowTile->state == 0 && belowTile->tile == LevelTileTypeInfotron)
+ {
+ // loc_4E634: ; CODE XREF: update?+442j
+ // push si
+ // mov di, [si+6155h]
+ // si = word_51844;
+ drawMovingFrame(176, 64, position);
+ // pop si
+ playInfotronSound();
+ // dx = 0x0F8E;
+ murphyTile->state = 0x16;
+ belowTile->state = 0xFF;
+ gMurphyCounterToStartPushAnimation = 0;
+ gIsMurphyGoingThroughPortal = 0;
+ return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[25]);
+ }
+ // loc_4E2D5: ; CODE XREF: update?+440j
+ else if (belowTile->tile == LevelTileTypeTerminal)
+ {
+ // loc_4E766: ; CODE XREF: update?+325j update?+449j
+ // push si
+ // mov di, [si+6155h]
+ // si = word_51844;
+ drawMovingFrame(176, 64, position);
+ // pop si
+ if (gAreYellowDisksDetonated != 0)
+ {
+ gMurphyYawnAndSleepCounter = 0xA;
+ return position;
+ }
+
+ // loc_4E781: ; CODE XREF: update?+8E8j
+ // push si
+ // mov di, [si+61CDh]
+ // si = kTerminalOnSpriteCoordinates;
+ drawMovingFrame(256, 388, position + kLevelWidth);
+ // pop si
+ detonateYellowDisks();
+ return position;
+ }
+ // loc_4E2DC: ; CODE XREF: update?+447j
+ else if (belowTile->tile == LevelTileTypeRedDisk)
+ {
+ // loc_4E8D4: ; CODE XREF: update?+450j
+ // dx = 0x108E;
+ murphyTile->state = 0x22;
+ belowTile->state = 3;
+ gMurphyCounterToStartPushAnimation = 0;
+ gIsMurphyGoingThroughPortal = 0;
+ return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[41]);
+ }
+ else
+ {
+ return position;
+ }
+ }
+ // loc_4E08F: ; CODE XREF: update?+1F5j
+ else if (userInput == UserInputSpaceRight)
+ {
+ gPreviousUserInputWasNone = 0;
+ // loc_4E2E4: ; CODE XREF: update?+209j
+ gIsMurphyLookingLeft = 0;
+ // mov ax, [si+1836h]
+ if (rightTile->state == 0 && rightTile->tile == LevelTileTypeBase)
+ {
+ // loc_4E541: ; CODE XREF: update?+463j
+ // push si
+ // mov di, [si+6155h]
+ // si = word_51846;
+ drawMovingFrame(192, 16, position);
+ // pop si
+ playBaseSound();
+ // dx = 0x0EFE;
+ murphyTile->state = 0x13;
+ gMurphyCounterToStartPushAnimation = 0;
+ gIsMurphyGoingThroughPortal = 0;
+ return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[16]);
+ }
+ // loc_4E2F6: ; CODE XREF: update?+461j
+ else if (rightTile->tile == LevelTileTypeBug)
+ {
+ // loc_4E530: ; CODE XREF: update?+46Aj
+ if (rightTile->state < 0x80)
+ {
+ detonateBigExplosion(position);
+ return position;
+ }
+
+ // loc_4E53B: ; CODE XREF: update?+6A5j
+ rightTile->state = 0;
+ rightTile->tile = LevelTileTypeBase;
+
+ // loc_4E541: ; CODE XREF: update?+463j
+ // push si
+ // mov di, [si+6155h]
+ // si = word_51846;
+ drawMovingFrame(192, 16, position);
+ // pop si
+ playBaseSound();
+ // dx = 0x0EFE;
+ murphyTile->state = 0x13;
+ gMurphyCounterToStartPushAnimation = 0;
+ gIsMurphyGoingThroughPortal = 0;
+ return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[16]);
+ }
+ // loc_4E2FD: ; CODE XREF: update?+468j
+ else if (rightTile->state == 0 && rightTile->tile == LevelTileTypeInfotron)
+ {
+ // loc_4E654: ; CODE XREF: update?+472j
+ // push si
+ // mov di, [si+6155h]
+ // si = word_51846;
+ drawMovingFrame(192, 16, position);
+ // pop si
+ playInfotronSound();
+ // dx = 0x0F9E;
+ murphyTile->state = 0x17;
+ rightTile->state = 0xFF;
+ gMurphyCounterToStartPushAnimation = 0;
+ gIsMurphyGoingThroughPortal = 0;
+ return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[26]);
+ }
+ // loc_4E305: ; CODE XREF: update?+470j
+ else if (rightTile->tile != LevelTileTypeTerminal)
+ {
+ // loc_4E30C: ; CODE XREF: update?+477j
+ if (rightTile->tile != LevelTileTypeRedDisk)
+ {
+ return position;
+ }
+ // loc_4E8E3: ; CODE XREF: update?+480j
+ // dx = 0x109E;
+ murphyTile->state = 0x23;
+ rightTile->state = 3;
+
+ // loc_4E8F0: ; CODE XREF: update?+4DAj update?+4F7j ...
+ gMurphyCounterToStartPushAnimation = 0;
+ gIsMurphyGoingThroughPortal = 0;
+ return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[42]);
+ }
+ else
+ {
+ // loc_4E790: ; CODE XREF: update?+395j update?+479j
+ // push si
+ // mov di, [si+6155h]
+ // si = word_51846;
+ drawMovingFrame(192, 16, position);
+ // pop si
+ if (gAreYellowDisksDetonated != 0)
+ {
+ gMurphyYawnAndSleepCounter = 0xA;
+ return position;
+ }
+
+ // loc_4E7AB: ; CODE XREF: update?+912j
+ // push si
+ // mov di, [si+6157h]
+ // si = kTerminalOnSpriteCoordinates;
+ drawMovingFrame(256, 388, position + 1);
+ // pop si
+
+ detonateYellowDisks();
+ return position;
+ }
+ }
+ // loc_4E09C: ; CODE XREF: update?+202j
+ else if (userInput == UserInputSpaceOnly)
+ {
+ // loc_4E314: ; CODE XREF: update?+211j
+ if (gNumberOfRemainingRedDisks == 0 || gPlantedRedDiskCountdown != 0 || gPreviousUserInputWasNone != 1)
+ {
+ return position;
+ }
+ murphyTile->state = 0x2A;
+ gMurphyCounterToStartPushAnimation = 0x40; // 64
+ // dx = 0x110E;
+ gPlantedRedDiskCountdown = 1;
+ gPlantedRedDiskPosition = position;
+ return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[49]);
+ }
+ else
+ {
+ // loc_4E0A4: ; CODE XREF: update?+20Fj
+ gPreviousUserInputWasNone = 0;
+ return position;
+ }
+}
+
+int16_t handleMurphyDirectionUp(int16_t position)
+{
+ // 01ED:7447
+ StatefulLevelTile *murphyTile = &gCurrentLevelState[position];
+ StatefulLevelTile *aboveTile = &gCurrentLevelState[position - kLevelWidth];
+ StatefulLevelTile *aboveAboveTile = &gCurrentLevelState[position - kLevelWidth * 2];
+
+ // loc_4E0AA: ; CODE XREF: update?+1AFj update?+279j
+ if (aboveTile->state == 0 && aboveTile->tile == LevelTileTypeSpace)
+ {
+ MurphyAnimationDescriptor animationDescriptor;
+
+ // loc_4E344: ; CODE XREF: update?+223j
+ if (gIsMurphyLookingLeft != 0)
+ {
+ animationDescriptor = kMurphyAnimationDescriptors[0]; // dx = 0x0DFE;
+ }
+ else
+ {
+ // loc_4E350: ; CODE XREF: update?+4B9j
+ animationDescriptor = kMurphyAnimationDescriptors[1]; // dx = 0x0E0E;
+ }
+
+ // loc_4E353: ; CODE XREF: update?+4BEj
+ aboveTile->state = 1;
+ aboveTile->tile = LevelTileTypeMurphy;
+ murphyTile->state = 3;
+ murphyTile->tile = LevelTileTypeSpace;
+
+ gMurphyCounterToStartPushAnimation = 0;
+ gIsMurphyGoingThroughPortal = 0;
+ return updateMurphyAnimationInfo(position - kLevelWidth, animationDescriptor);
+ }
+ // loc_4E0B6: ; CODE XREF: update?+221j
+ else if (aboveTile->state == 0 && aboveTile->tile == LevelTileTypeBase)
+ {
+ MurphyAnimationDescriptor unknownMurphyData;
+
+ // loc_4E3E1: ; CODE XREF: update?+22Bj
+ playBaseSound();
+ if (gIsMurphyLookingLeft != 0)
+ {
+ unknownMurphyData = kMurphyAnimationDescriptors[7]; // dx = 0x0E6E;
+ }
+ else
+ {
+ // loc_4E3F0: ; CODE XREF: update?+559j
+ unknownMurphyData = kMurphyAnimationDescriptors[8]; // dx = 0x0E7E;
+ }
+
+ // loc_4E3F3: ; CODE XREF: update?+55Ej
+
+ aboveTile->state = 5;
+ aboveTile->tile = LevelTileTypeMurphy;
+ murphyTile->state = 3;
+ murphyTile->tile = LevelTileTypeSpace;
+ gMurphyCounterToStartPushAnimation = 0;
+ gIsMurphyGoingThroughPortal = 0;
+ return updateMurphyAnimationInfo(position - kLevelWidth, unknownMurphyData);
+ }
+ // loc_4E0BE: ; CODE XREF: update?+229j
+ else if (aboveTile->tile == LevelTileTypeBug)
+ {
+ // loc_4E3D0: ; CODE XREF: update?+232j
+ // cmp byte ptr [si+17BDh], 0
+ // jl short loc_4E3DB
+ if (aboveTile->state < 0x80)
+ {
+ detonateBigExplosion(position);
+ return position;
+ }
+
+ // loc_4E3DB: ; CODE XREF: update?+545j
+ aboveTile->state = 0;
+ aboveTile->tile = LevelTileTypeBase;
+
+ // loc_4E3E1: ; CODE XREF: update?+22Bj
+ MurphyAnimationDescriptor animationDescriptor;
+ playBaseSound();
+ if (gIsMurphyLookingLeft != 0)
+ {
+ animationDescriptor = kMurphyAnimationDescriptors[7]; // dx = 0x0E6E;
+ }
+ else
+ {
+ // loc_4E3F0: ; CODE XREF: update?+559j
+ animationDescriptor = kMurphyAnimationDescriptors[8]; // dx = 0x0E7E;
+ }
+
+ // loc_4E3F3: ; CODE XREF: update?+55Ej
+
+ aboveTile->state = 5;
+ aboveTile->tile = LevelTileTypeMurphy;
+ murphyTile->state = 3;
+ murphyTile->tile = LevelTileTypeSpace;
+ gMurphyCounterToStartPushAnimation = 0;
+ gIsMurphyGoingThroughPortal = 0;
+ return updateMurphyAnimationInfo(position - kLevelWidth, animationDescriptor);
+ }
+ // loc_4E0C5: ; CODE XREF: update?+230j
+ else if (aboveTile->state == 0 && aboveTile->tile == LevelTileTypeInfotron)
+ {
+ MurphyAnimationDescriptor unknownMurphyData;
+
+ // loc_4E55C: ; CODE XREF: update?+23Aj
+ playInfotronSound();
+ if (gIsMurphyLookingLeft != 0)
+ {
+ unknownMurphyData = kMurphyAnimationDescriptors[17]; // dx = 0x0F0E;
+ }
+ else
+ {
+ // loc_4E56B: ; CODE XREF: update?+6D4j
+ unknownMurphyData = kMurphyAnimationDescriptors[18]; // dx = 0x0F1E;
+ }
+
+ // loc_4E56E: ; CODE XREF: update?+6D9j
+ aboveTile->state = 9;
+ aboveTile->tile = LevelTileTypeMurphy;
+ murphyTile->state = 3;
+ murphyTile->tile = LevelTileTypeSpace;
+ gMurphyCounterToStartPushAnimation = 0;
+ gIsMurphyGoingThroughPortal = 0;
+ return updateMurphyAnimationInfo(position - kLevelWidth, unknownMurphyData);
+ }
+ // loc_4E0CD: ; CODE XREF: update?+238j
+ else if (aboveTile->state == 0 && aboveTile->tile == LevelTileTypeExit)
+ {
+ // loc_4E674: ; CODE XREF: update?+242j update?+2AAj ...
+ if (gNumberOfRemainingInfotrons != 0)
+ {
+ return position;
+ }
+ playExitSound();
+ byte_5A19B = 1;
+ gCurrentPlayerLevelState = PlayerLevelStateCompleted;
+ gLevelFailed = 0;
+ if (gHasUserCheated == 0 && gShouldUpdateTotalLevelTime != 0)
+ {
+ byte_5A323 = 1;
+ addCurrentGameTimeToPlayer();
+ }
+
+ // loc_4E6A4: ; CODE XREF: update?+803j update?+80Aj
+ changePlayerCurrentLevelState();
+ gQuitLevelCountdown = 0x40;
+ // pop si
+ // dx = 0x0E5E;
+ murphyTile->state = 0xD;
+ gMurphyCounterToStartPushAnimation = 0;
+ gIsMurphyGoingThroughPortal = 0;
+ return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[6]);
+ }
+ // loc_4E0D5: ; CODE XREF: update?+240j
+ else if (aboveTile->tile == LevelTileTypeTerminal)
+ {
+ // loc_4E712: ; CODE XREF: update?+249j update?+3EFj
+ // 01ED:7AAF
+ // push si
+ // mov di, [si+6155h]
+ // si = word_51840;
+ drawMovingFrame(160, 64, position);
+ // pop si
+ if (gAreYellowDisksDetonated != 0)
+ {
+ gMurphyYawnAndSleepCounter = 0xA;
+ return position;
+ }
+
+ // loc_4E72D: ; CODE XREF: update?+894j
+ // push si
+ // mov di, [si+60DDh]
+ // si = kTerminalOnSpriteCoordinates;
+ drawMovingFrame(256, 388, position - kLevelWidth);
+ // pop si
+ detonateYellowDisks();
+ return position;
+ }
+ // loc_4E0DC: ; CODE XREF: update?+247j
+ else if (aboveTile->tile == LevelTileTypePortUp || aboveTile->tile == LevelTileTypePortVertical || aboveTile->tile == LevelTileTypePort4Way)
+ {
+ // loc_4E7DE: ; CODE XREF: update?+250j update?+257j ...
+ if (aboveAboveTile->state != 0 || aboveAboveTile->tile != LevelTileTypeSpace)
+ {
+ return position;
+ }
+
+ // loc_4E7E6: ; CODE XREF: update?+953j
+ // dx = 0x0FCE;
+ murphyTile->state = 0x18;
+ aboveAboveTile->state = 3;
+ gMurphyCounterToStartPushAnimation = 0;
+ gIsMurphyGoingThroughPortal = 1;
+ return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[29]);
+ }
+ // loc_4E0F1: ; CODE XREF: update?+25Cj
+ else if (aboveTile->tile == LevelTileTypeRedDisk)
+ {
+ MurphyAnimationDescriptor unknownMurphyData;
+
+ // loc_4E847: ; CODE XREF: update?+265j
+ if (gIsMurphyLookingLeft != 0)
+ {
+ unknownMurphyData = kMurphyAnimationDescriptors[33]; // dx = 0x100E;
+ }
+ else
+ {
+ // loc_4E853: ; CODE XREF: update?+9BCj
+ unknownMurphyData = kMurphyAnimationDescriptors[34]; // dx = 0x101E;
+ }
+
+ // loc_4E856: ; CODE XREF: update?+9C1j
+ murphyTile->state = 0x1C;
+ aboveTile->state = 3;
+ gMurphyCounterToStartPushAnimation = 0;
+ gIsMurphyGoingThroughPortal = 0;
+ return updateMurphyAnimationInfo(position, unknownMurphyData);
+ }
+ // loc_4E0F8: ; CODE XREF: update?+263j
+ else if (aboveTile->tile == LevelTileTypeYellowDisk)
+ {
+ // loc_4E8F9: ; CODE XREF: update?+26Cj
+ if (aboveAboveTile->state != 0 || aboveAboveTile->tile != LevelTileTypeSpace)
+ {
+ return position;
+ }
+
+ // loc_4E903: ; CODE XREF: update?+A70j
+ aboveAboveTile->state = 0x12;
+ // push si
+ // mov di, [si+6155h]
+ // si = word_5157C;
+ drawMovingFrame(97, 132, position);
+ // pop si
+ // dx = 0x10AE;
+ murphyTile->state = 0x24;
+ gMurphyCounterToStartPushAnimation = 8;
+ gIsMurphyGoingThroughPortal = 0;
+ return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[43]);
+ }
+ // loc_4E0FF: ; CODE XREF: update?+26Aj
+ else if (checkMurphyMovementToPosition(position - kLevelWidth, UserInputUp) != 1)
+ {
+ return handleMurphyDirectionUp(position);
+ }
+ else
+ {
+ return position;
+ }
+}
+
+int16_t handleMurphyDirectionLeft(int16_t position)
+{
+ // 01ED:74A9
+ StatefulLevelTile *murphyTile = &gCurrentLevelState[position];
+ StatefulLevelTile *leftLeftTile = &gCurrentLevelState[position - 2];
+ StatefulLevelTile *leftTile = &gCurrentLevelState[position - 1];
+
+ // loc_4E10C: ; CODE XREF: update?+1BBj update?+2F3j
+ gIsMurphyLookingLeft = 1;
+ // mov ax, [si+1832h]
+ if (leftTile->state == 0 && leftTile->tile == LevelTileTypeSpace)
+ {
+ // loc_4E36D: ; CODE XREF: update?+28Bj
+ // dx = 0x0E1E;
+ leftTile->state = 2;
+ leftTile->tile = LevelTileTypeMurphy;
+ murphyTile->state = 3;
+ murphyTile->tile = LevelTileTypeSpace;
+ gMurphyCounterToStartPushAnimation = 0;
+ gIsMurphyGoingThroughPortal = 0;
+ return updateMurphyAnimationInfo(position - 1, kMurphyAnimationDescriptors[2]);
+ }
+ // loc_4E11E: ; CODE XREF: update?+289j
+ else if (leftTile->state == 0 && leftTile->tile == LevelTileTypeBase) // 01ED:7634
+ {
+ // loc_4E41E: ; CODE XREF: update?+293j
+ // 01ED:77BB
+ playBaseSound();
+ // dx = 0x0E8E;
+ leftTile->state = 2;
+ leftTile->tile = LevelTileTypeMurphy;
+ murphyTile->state = 3;
+ murphyTile->tile = LevelTileTypeSpace;
+ gMurphyCounterToStartPushAnimation = 0;
+ gIsMurphyGoingThroughPortal = 0;
+ return updateMurphyAnimationInfo(position - 1, kMurphyAnimationDescriptors[9]);
+ }
+ // loc_4E126: ; CODE XREF: update?+291j
+ else if (leftTile->tile == LevelTileTypeBug) // 01ED:763B
+ {
+ // loc_4E40D: ; CODE XREF: update?+29Aj
+ if (leftTile->state < 0x80)
+ {
+ detonateBigExplosion(position);
+ return position;
+ }
+
+ // loc_4E418: ; CODE XREF: update?+582j
+ // 01ED:77B5
+ leftTile->state = 0;
+ leftTile->tile = LevelTileTypeBase;
+
+ // loc_4E41E: ; CODE XREF: update?+293j
+ playBaseSound();
+ // dx = 0x0E8E;
+ leftTile->state = 2;
+ leftTile->tile = LevelTileTypeMurphy;
+ murphyTile->state = 3;
+ murphyTile->tile = LevelTileTypeSpace;
+ gMurphyCounterToStartPushAnimation = 0;
+ gIsMurphyGoingThroughPortal = 0;
+ return updateMurphyAnimationInfo(position - 1, kMurphyAnimationDescriptors[9]);
+ }
+ // loc_4E12D: ; CODE XREF: update?+298j
+ else if (leftTile->state == 0 && leftTile->tile == LevelTileTypeInfotron)
+ {
+ // loc_4E588: ; CODE XREF: update?+2A2j
+ playInfotronSound();
+ // dx = 0x0F2E;
+ leftTile->state = 10;
+ leftTile->tile = LevelTileTypeMurphy;
+ murphyTile->state = 3;
+ murphyTile->tile = LevelTileTypeSpace;
+ gMurphyCounterToStartPushAnimation = 0;
+ gIsMurphyGoingThroughPortal = 0;
+ return updateMurphyAnimationInfo(position - 1, kMurphyAnimationDescriptors[19]);
+ }
+ // loc_4E135: ; CODE XREF: update?+2A0j
+ else if (leftTile->state == 0 && leftTile->tile == LevelTileTypeExit)
+ {
+ // loc_4E674: ; CODE XREF: update?+242j update?+2AAj ...
+ if (gNumberOfRemainingInfotrons != 0)
+ {
+ return position;
+ }
+ playExitSound();
+ byte_5A19B = 1;
+ gCurrentPlayerLevelState = PlayerLevelStateCompleted;
+ gLevelFailed = 0;
+ if (gHasUserCheated == 0 && gShouldUpdateTotalLevelTime != 0)
+ {
+ byte_5A323 = 1;
+ addCurrentGameTimeToPlayer();
+ }
+
+ // loc_4E6A4: ; CODE XREF: update?+803j update?+80Aj
+ changePlayerCurrentLevelState();
+ gQuitLevelCountdown = 0x40;
+ // pop si
+ // dx = 0x0E5E;
+ murphyTile->state = 0xD;
+ gMurphyCounterToStartPushAnimation = 0;
+ gIsMurphyGoingThroughPortal = 0;
+ return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[6]);
+ }
+ // loc_4E13D: ; CODE XREF: update?+2A8j
+ else if (leftTile->state == 0 && leftTile->tile == LevelTileTypeZonk)
+ {
+ // loc_4E6BA: ; CODE XREF: update?+2B2j
+ // mov ax, [si+1830h]
+ if (leftLeftTile->state != 0 || leftLeftTile->tile != LevelTileTypeSpace)
+ {
+ return position;
+ }
+
+ // loc_4E6C4: ; CODE XREF: update?+831j
+ leftLeftTile->state = 1;
+ // push si
+ // mov di, [si+6155h]
+ // si = word_5157A;
+ drawMovingFrame(64, 132, position);
+ // pop si
+ // dx = 0x0FAE;
+ murphyTile->state = 0xE;
+ gMurphyCounterToStartPushAnimation = 8;
+ gIsMurphyGoingThroughPortal = 0;
+ return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[27]);
+ }
+ // loc_4E145: ; CODE XREF: update?+2B0j
+ else if (leftTile->tile == LevelTileTypeTerminal)
+ {
+ // loc_4E73C: ; CODE XREF: update?+2B9j update?+41Fj
+ // push si
+ // mov di, [si+6155h]
+ // si = word_51842;
+ drawMovingFrame(208, 16, position);
+ // pop si
+ if (gAreYellowDisksDetonated != 0)
+ {
+ gMurphyYawnAndSleepCounter = 0xA;
+ return position;
+ }
+
+ // loc_4E757: ; CODE XREF: update?+8BEj
+ // push si
+ // mov di, [si+6153h]
+ // si = kTerminalOnSpriteCoordinates;
+ drawMovingFrame(256, 388, position - 1);
+ // pop si
+ detonateYellowDisks();
+ return position;
+ }
+ // loc_4E14C: ; CODE XREF: update?+2B7j
+ else if (leftTile->tile == LevelTileTypePortLeft || leftTile->tile == LevelTileTypePortHorizontal || leftTile->tile == LevelTileTypePort4Way)
+ {
+ // loc_4E7F5: ; CODE XREF: update?+2C0j update?+2C7j ...
+ if (leftLeftTile->state != 0 || leftLeftTile->tile != LevelTileTypeSpace)
+ {
+ return position;
+ }
+
+ // loc_4E7FD: ; CODE XREF: update?+96Aj
+ // dx = 0x0FDE;
+ murphyTile->state = 0x19;
+ leftLeftTile->state = 3;
+ gMurphyCounterToStartPushAnimation = 0;
+ gIsMurphyGoingThroughPortal = 1;
+ return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[30]);
+ }
+ // loc_4E161: ; CODE XREF: update?+2CCj
+ else if (leftTile->state == 0 && leftTile->tile == LevelTileTypeRedDisk)
+ {
+ // loc_4E863: ; CODE XREF: update?+2D6j
+ // dx = 0x102E;
+ leftTile->state = 0x1D;
+ leftTile->tile = LevelTileTypeMurphy;
+ murphyTile->state = 3;
+ murphyTile->tile = LevelTileTypeSpace;
+ gMurphyCounterToStartPushAnimation = 0;
+ gIsMurphyGoingThroughPortal = 0;
+ return updateMurphyAnimationInfo(position - 1, kMurphyAnimationDescriptors[35]);
+ }
+ // loc_4E169: ; CODE XREF: update?+2D4j
+ else if (leftTile->state == 0 && leftTile->tile == LevelTileTypeYellowDisk)
+ {
+ // loc_4E920: ; CODE XREF: update?+2DEj
+ if (leftLeftTile->state != 0 || leftLeftTile->tile != LevelTileTypeSpace)
+ {
+ return position;
+ }
+
+ // loc_4E92A: ; CODE XREF: update?+A97j
+ leftLeftTile->state = 0x12;
+ // push si
+ // mov di, [si+6155h]
+ // si = word_5157A;
+ drawMovingFrame(64, 132, position);
+ // pop si
+ // dx = 0x10BE;
+ murphyTile->state = 0x25;
+ gMurphyCounterToStartPushAnimation = 8;
+ gIsMurphyGoingThroughPortal = 0;
+ return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[44]);
+ }
+ // loc_4E171: ; CODE XREF: update?+2DCj
+ else if (leftTile->state == 0 && leftTile->tile == LevelTileTypeOrangeDisk)
+ {
+ // loc_4E993: ; CODE XREF: update?+2E6j
+ if (leftLeftTile->state != 0 || leftLeftTile->tile != LevelTileTypeSpace)
+ {
+ return position;
+ }
+
+ // loc_4E99D: ; CODE XREF: update?+B0Aj
+ leftLeftTile->state = 8;
+ // push si
+ // mov di, [si+6155h]
+ // si = word_5157A;
+ drawMovingFrame(64, 132, position);
+ // pop si
+ // dx = 0x10EE;
+ murphyTile->state = 0x28;
+ gMurphyCounterToStartPushAnimation = 8;
+ gIsMurphyGoingThroughPortal = 0;
+ return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[47]);
+ }
+ // loc_4E179: ; CODE XREF: update?+2E4j
+ else if (checkMurphyMovementToPosition(position - 1, UserInputLeft) != 1)
+ {
+ return handleMurphyDirectionLeft(position);
+ }
+ else
+ {
+ return position;
+ }
+}
+
+int16_t handleMurphyDirectionDown(int16_t position)
+{
+ StatefulLevelTile *murphyTile = &gCurrentLevelState[position];
+ StatefulLevelTile *belowTile = &gCurrentLevelState[position + kLevelWidth];
+ StatefulLevelTile *belowBelowTile = &gCurrentLevelState[position + kLevelWidth * 2];
+
+ // loc_4E186: ; CODE XREF: update?+1C8j update?+355j
+ // mov ax, leveldata[si+78h]
+ if (belowTile->state == 0 && belowTile->tile == LevelTileTypeSpace)
+ {
+ MurphyAnimationDescriptor unknownMurphyData;
+
+ // loc_4E38A: ; CODE XREF: update?+69j update?+2FFj
+ if (gIsMurphyLookingLeft != 0)
+ {
+ unknownMurphyData = kMurphyAnimationDescriptors[3]; // dx = 0x0E2E;
+ }
+ else
+ {
+ // loc_4E396: ; CODE XREF: update?+4FFj
+ unknownMurphyData = kMurphyAnimationDescriptors[4]; // dx = 0x0E3E;
+ }
+
+ // loc_4E399: ; CODE XREF: update?+504j
+ belowTile->state = 3;
+ belowTile->tile = LevelTileTypeMurphy;
+ murphyTile->state = 3;
+ murphyTile->tile = LevelTileTypeSpace;
+ gMurphyCounterToStartPushAnimation = 0;
+ gIsMurphyGoingThroughPortal = 0;
+ return updateMurphyAnimationInfo(position + kLevelWidth, unknownMurphyData);
+ }
+ // loc_4E192: ; CODE XREF: update?+2FDj
+ else if (belowTile->state == 0 && belowTile->tile == LevelTileTypeBase)
+ {
+ MurphyAnimationDescriptor unknownMurphyData;
+
+ // loc_4E44F: ; CODE XREF: update?+307j
+ playBaseSound();
+ if (gIsMurphyLookingLeft != 0)
+ {
+ unknownMurphyData = kMurphyAnimationDescriptors[10]; // dx = 0x0E9E;
+ }
+ else
+ {
+ // loc_4E45E: ; CODE XREF: update?+5C7j
+ unknownMurphyData = kMurphyAnimationDescriptors[11]; // dx = 0x0EAE;
+ }
+
+ // loc_4E461: ; CODE XREF: update?+5CCj
+ belowTile->state = 7;
+ belowTile->tile = LevelTileTypeMurphy;
+ murphyTile->state = 3;
+ murphyTile->tile = LevelTileTypeSpace;
+ gMurphyCounterToStartPushAnimation = 0;
+ gIsMurphyGoingThroughPortal = 0;
+ return updateMurphyAnimationInfo(position + kLevelWidth, unknownMurphyData);
+ }
+ // loc_4E19A: ; CODE XREF: update?+305j
+ else if (belowTile->tile == LevelTileTypeBug)
+ {
+ // loc_4E43E: ; CODE XREF: update?+30Ej
+ if (belowTile->state < 0x80)
+ {
+ detonateBigExplosion(position);
+ return position;
+ }
+
+ // loc_4E449: ; CODE XREF: update?+5B3j
+ belowTile->state = 0;
+ belowTile->tile = LevelTileTypeBase;
+
+ MurphyAnimationDescriptor unknownMurphyData;
+
+ // loc_4E44F: ; CODE XREF: update?+307j
+ playBaseSound();
+ if (gIsMurphyLookingLeft != 0)
+ {
+ unknownMurphyData = kMurphyAnimationDescriptors[10]; // dx = 0x0E9E;
+ }
+ else
+ {
+ // loc_4E45E: ; CODE XREF: update?+5C7j
+ unknownMurphyData = kMurphyAnimationDescriptors[11]; // dx = 0x0EAE;
+ }
+
+ // loc_4E461: ; CODE XREF: update?+5CCj
+ belowTile->state = 7;
+ belowTile->tile = LevelTileTypeMurphy;
+ murphyTile->state = 3;
+ murphyTile->tile = LevelTileTypeSpace;
+ gMurphyCounterToStartPushAnimation = 0;
+ gIsMurphyGoingThroughPortal = 0;
+ return updateMurphyAnimationInfo(position + kLevelWidth, unknownMurphyData);
+ }
+ // loc_4E1A1: ; CODE XREF: update?+30Cj
+ else if (belowTile->tile == LevelTileTypeInfotron)
+ {
+ MurphyAnimationDescriptor unknownMurphyData;
+
+ // loc_4E5A8: ; CODE XREF: update?+316j
+ playInfotronSound();
+ if (gIsMurphyLookingLeft != 0)
+ {
+ unknownMurphyData = kMurphyAnimationDescriptors[20]; // dx = 0x0F3E;
+ }
+ else
+ {
+ // loc_4E5B7: ; CODE XREF: update?+720j
+ unknownMurphyData = kMurphyAnimationDescriptors[21]; // dx = 0x0F4E;
+ }
+
+ // loc_4E5BA: ; CODE XREF: update?+725j
+ belowTile->state = 11;
+ belowTile->tile = LevelTileTypeMurphy;
+ murphyTile->state = 3;
+ murphyTile->tile = LevelTileTypeBase;
+ gMurphyCounterToStartPushAnimation = 0;
+ gIsMurphyGoingThroughPortal = 0;
+ return updateMurphyAnimationInfo(position + kLevelWidth, unknownMurphyData);
+ }
+ // loc_4E1A9: ; CODE XREF: update?+314j
+ else if (belowTile->tile == LevelTileTypeExit)
+ {
+ // loc_4E674: ; CODE XREF: update?+242j update?+2AAj ...
+ if (gNumberOfRemainingInfotrons != 0)
+ {
+ return position;
+ }
+ playExitSound();
+ byte_5A19B = 1;
+ gCurrentPlayerLevelState = PlayerLevelStateCompleted;
+ gLevelFailed = 0;
+ if (gHasUserCheated == 0 && gShouldUpdateTotalLevelTime != 0)
+ {
+ byte_5A323 = 1;
+ addCurrentGameTimeToPlayer();
+ }
+
+ // loc_4E6A4: ; CODE XREF: update?+803j update?+80Aj
+ changePlayerCurrentLevelState();
+ gQuitLevelCountdown = 0x40;
+ // pop si
+ // dx = 0x0E5E;
+ murphyTile->state = 0xD;
+ gMurphyCounterToStartPushAnimation = 0;
+ gIsMurphyGoingThroughPortal = 0;
+ return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[6]);
+ }
+ // loc_4E1B1: ; CODE XREF: update?+31Cj
+ else if (belowTile->tile == LevelTileTypeTerminal)
+ {
+ // loc_4E766: ; CODE XREF: update?+325j update?+449j
+ // push si
+ // mov di, [si+6155h]
+ // si = word_51844;
+ drawMovingFrame(176, 64, position);
+ // pop si
+ if (gAreYellowDisksDetonated != 0)
+ {
+ gMurphyYawnAndSleepCounter = 0xA;
+ return position;
+ }
+
+ // loc_4E781: ; CODE XREF: update?+8E8j
+ // push si
+ // mov di, [si+61CDh]
+ // si = kTerminalOnSpriteCoordinates;
+ drawMovingFrame(256, 388, position + kLevelWidth);
+ // pop si
+ detonateYellowDisks();
+ return position;
+ }
+ // loc_4E1B8: ; CODE XREF: update?+323j
+ else if (belowTile->tile == LevelTileTypePortDown || belowTile->tile == LevelTileTypePortVertical || belowTile->tile == LevelTileTypePort4Way)
+ {
+ // loc_4E80C: ; CODE XREF: update?+32Cj update?+333j ...
+ if (belowBelowTile->state != 0 || belowBelowTile->tile != LevelTileTypeSpace)
+ {
+ return position;
+ }
+
+ // loc_4E814: ; CODE XREF: update?+981j
+ // dx = 0x0FEE;
+ murphyTile->state = 0x1A;
+ belowBelowTile->state = 3;
+ gMurphyCounterToStartPushAnimation = 0;
+ gIsMurphyGoingThroughPortal = 1;
+ return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[31]);
+ }
+ // loc_4E1CD: ; CODE XREF: update?+338j
+ else if (belowTile->tile == LevelTileTypeRedDisk)
+ {
+ MurphyAnimationDescriptor unknownMurphyData;
+
+ // loc_4E87F: ; CODE XREF: update?+341j
+ if (gIsMurphyLookingLeft != 0)
+ {
+ unknownMurphyData = kMurphyAnimationDescriptors[36]; // dx = 0x103E;
+ }
+ else
+ {
+ // loc_4E88B: ; CODE XREF: update?+9F4j
+ unknownMurphyData = kMurphyAnimationDescriptors[37]; // dx = 0x104E;
+ }
+
+ // loc_4E88E: ; CODE XREF: update?+9F9j
+ murphyTile->state = 0x1E;
+ belowTile->state = 3;
+ gMurphyCounterToStartPushAnimation = 0;
+ gIsMurphyGoingThroughPortal = 0;
+ return updateMurphyAnimationInfo(position, unknownMurphyData);
+ }
+ // loc_4E1D4: ; CODE XREF: update?+33Fj
+ else if (belowTile->tile == LevelTileTypeYellowDisk)
+ {
+ // loc_4E947: ; CODE XREF: update?+348j
+ if (belowBelowTile->state != 0 || belowBelowTile->tile != LevelTileTypeSpace)
+ {
+ return position;
+ }
+
+ // loc_4E951: ; CODE XREF: update?+ABEj
+ belowBelowTile->state = 0x12;
+ // push si
+ // mov di, [si+6155h]
+ // si = word_5157C;
+ drawMovingFrame(97, 132, position);
+ // pop si
+ // dx = 0x10CE;
+ murphyTile->state = 0x27;
+ gMurphyCounterToStartPushAnimation = 8;
+ gIsMurphyGoingThroughPortal = 0;
+ return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[45]);
+ }
+ // loc_4E1DB: ; CODE XREF: update?+346j
+ else if (checkMurphyMovementToPosition(position + kLevelWidth, UserInputDown) != 1)
+ {
+ return handleMurphyDirectionDown(position);
+ }
+ else
+ {
+ return position;
+ }
+}
+
+int16_t handleMurphyDirectionRight(int16_t position)
+{
+ StatefulLevelTile *murphyTile = &gCurrentLevelState[position];
+ StatefulLevelTile *rightTile = &gCurrentLevelState[position + 1];
+ StatefulLevelTile *rightRightTile = &gCurrentLevelState[position + 2];
+ StatefulLevelTile *belowRightTile = &gCurrentLevelState[position + kLevelWidth + 1];
+
+ // loc_4E1E8: ; CODE XREF: update?+1D5j update?+3CDj
+ gIsMurphyLookingLeft = 0;
+ // mov ax, leveldata[si+2]
+ if (rightTile->state == 0 && rightTile->tile == LevelTileTypeSpace)
+ {
+ // loc_4E3B3: ; CODE XREF: update?+367j
+ // dx = 0x0E4E;
+ rightTile->state = 4;
+ rightTile->tile = LevelTileTypeMurphy;
+ murphyTile->state = 3;
+ murphyTile->tile = LevelTileTypeSpace;
+ gMurphyCounterToStartPushAnimation = 0;
+ gIsMurphyGoingThroughPortal = 0;
+ return updateMurphyAnimationInfo(position + 1, kMurphyAnimationDescriptors[5]);
+ }
+ // loc_4E1FA: ; CODE XREF: update?+365j
+ else if (rightTile->state == 0 && rightTile->tile == LevelTileTypeBase)
+ {
+ // loc_4E48C: ; CODE XREF: update?+36Fj
+ playBaseSound();
+ // dx = 0x0EBE;
+ rightTile->state = 8;
+ rightTile->tile = LevelTileTypeMurphy;
+ murphyTile->state = 3;
+ murphyTile->tile = LevelTileTypeSpace;
+ gMurphyCounterToStartPushAnimation = 0;
+ gIsMurphyGoingThroughPortal = 0;
+ return updateMurphyAnimationInfo(position + 1, kMurphyAnimationDescriptors[12]);
+ }
+ // loc_4E202: ; CODE XREF: update?+36Dj
+ else if (rightTile->tile == LevelTileTypeBug)
+ {
+ // loc_4E47B: ; CODE XREF: update?+376j
+ if (rightTile->state < 0x80)
+ {
+ detonateBigExplosion(position);
+ return position;
+ }
+
+ // loc_4E486: ; CODE XREF: update?+5F0j
+ rightTile->state = 0;
+ rightTile->tile = LevelTileTypeBase;
+
+ // loc_4E48C: ; CODE XREF: update?+36Fj
+ playBaseSound();
+ // dx = 0x0EBE;
+ rightTile->state = 8;
+ rightTile->tile = LevelTileTypeMurphy;
+ murphyTile->state = 3;
+ murphyTile->tile = LevelTileTypeSpace;
+ gMurphyCounterToStartPushAnimation = 0;
+ gIsMurphyGoingThroughPortal = 0;
+ return updateMurphyAnimationInfo(position + 1, kMurphyAnimationDescriptors[12]);
+ }
+ // loc_4E209: ; CODE XREF: update?+374j
+ else if (rightTile->state == 0 && rightTile->tile == LevelTileTypeInfotron)
+ {
+ // loc_4E5D4: ; CODE XREF: update?+37Ej
+ playInfotronSound();
+ // dx = 0x0F5E;
+ rightTile->state = 12;
+ rightTile->tile = LevelTileTypeMurphy;
+ murphyTile->state = 3;
+ murphyTile->tile = LevelTileTypeSpace;
+ gMurphyCounterToStartPushAnimation = 0;
+ gIsMurphyGoingThroughPortal = 0;
+ return updateMurphyAnimationInfo(position + 1, kMurphyAnimationDescriptors[22]);
+ }
+ // loc_4E211: ; CODE XREF: update?+37Cj
+ else if (rightTile->state == 0 && rightTile->tile == LevelTileTypeExit)
+ {
+ // loc_4E674: ; CODE XREF: update?+242j update?+2AAj ...
+ if (gNumberOfRemainingInfotrons != 0)
+ {
+ return position;
+ }
+ playExitSound();
+ byte_5A19B = 1;
+ gCurrentPlayerLevelState = PlayerLevelStateCompleted;
+ gLevelFailed = 0;
+ if (gHasUserCheated == 0 && gShouldUpdateTotalLevelTime != 0)
+ {
+ byte_5A323 = 1;
+ addCurrentGameTimeToPlayer();
+ }
+
+ // loc_4E6A4: ; CODE XREF: update?+803j update?+80Aj
+ changePlayerCurrentLevelState();
+ gQuitLevelCountdown = 0x40;
+ murphyTile->state = 0xD;
+ gMurphyCounterToStartPushAnimation = 0;
+ gIsMurphyGoingThroughPortal = 0;
+ return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[6]);
+ }
+ // loc_4E219: ; CODE XREF: update?+384j
+ else if (rightTile->state == 0 && rightTile->tile == LevelTileTypeZonk)
+ {
+ // loc_4E6E1: ; CODE XREF: update?+38Ej
+ // mov ax, [si+1838h]
+ if (rightRightTile->state != 0 || rightRightTile->tile != LevelTileTypeSpace)
+ {
+ return position;
+ }
+
+ // loc_4E6EB: ; CODE XREF: update?+858j
+ // mov ax, [si+18AEh]
+ if (belowRightTile->state == 0 && belowRightTile->tile == LevelTileTypeSpace)
+ {
+ return position;
+ }
+
+ // loc_4E6F5: ; CODE XREF: update?+862j
+ rightRightTile->state = 1;
+ // push si
+ // mov di, [si+6155h]
+ // si = word_5157C;
+ drawMovingFrame(97, 132, position);
+ // pop si
+ // dx = 0x0FBE;
+ murphyTile->state = 0xF;
+ gMurphyCounterToStartPushAnimation = 8;
+ gIsMurphyGoingThroughPortal = 0;
+ return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[28]);
+ }
+ // loc_4E221: ; CODE XREF: update?+38Cj
+ else if (rightTile->tile == LevelTileTypeTerminal)
+ {
+ // loc_4E790: ; CODE XREF: update?+395j update?+479j
+ // push si
+ // mov di, [si+6155h]
+ // si = word_51846;
+ drawMovingFrame(192, 16, position);
+ // pop si
+ if (gAreYellowDisksDetonated != 0)
+ {
+ gMurphyYawnAndSleepCounter = 0xA;
+ return position;
+ }
+
+ // loc_4E7AB: ; CODE XREF: update?+912j
+ // push si
+ // mov di, [si+6157h]
+ // si = kTerminalOnSpriteCoordinates;
+ drawMovingFrame(256, 388, position + 1);
+ // pop si
+
+ detonateYellowDisks();
+ return position;
+ }
+ // loc_4E228: ; CODE XREF: update?+393j
+ else if (rightTile->tile == LevelTileTypePortRight || rightTile->tile == LevelTileTypePortHorizontal || rightTile->tile == LevelTileTypePort4Way)
+ {
+ // loc_4E823: ; CODE XREF: update?+39Cj update?+3A3j ...
+ if (rightRightTile->state != 0 || rightRightTile->tile != LevelTileTypeSpace)
+ {
+ return position;
+ }
+
+ // loc_4E82B: ; CODE XREF: update?+998j
+ // dx = 0x0FFE;
+ murphyTile->state = 0x1B;
+ rightRightTile->state = 3;
+ gMurphyCounterToStartPushAnimation = 0;
+ gIsMurphyGoingThroughPortal = 1;
+ return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[32]);
+ }
+ // loc_4E23D: ; CODE XREF: update?+3A8j
+ else if (rightTile->tile == LevelTileTypeRedDisk)
+ {
+ // loc_4E89A: ; CODE XREF: update?+3B1j
+ // dx = 0x105E;
+ rightTile->state = 0x1F;
+ rightTile->tile = LevelTileTypeMurphy;
+ murphyTile->state = 3;
+ murphyTile->tile = LevelTileTypeSpace;
+ gMurphyCounterToStartPushAnimation = 0;
+ gIsMurphyGoingThroughPortal = 0;
+ return updateMurphyAnimationInfo(position + 1, kMurphyAnimationDescriptors[38]);
+ }
+ // loc_4E244: ; CODE XREF: update?+3AFj
+ else if (rightTile->tile == LevelTileTypeYellowDisk)
+ {
+ // loc_4E96D: ; CODE XREF: update?+3B8j
+ if (rightRightTile->state != 0 || rightRightTile->tile != LevelTileTypeSpace)
+ {
+ return position;
+ }
+
+ // loc_4E977: ; CODE XREF: update?+AE4j
+ rightRightTile->state = 0x12;
+ // push si
+ // mov di, [si+6155h]
+ // si = word_5157C;
+ drawMovingFrame(97, 132, position);
+ // pop si
+ // dx = 0x10DE;
+ murphyTile->state = 0x26;
+ gMurphyCounterToStartPushAnimation = 8;
+ gIsMurphyGoingThroughPortal = 0;
+ return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[46]);
+ }
+ // loc_4E24B: ; CODE XREF: update?+3B6j
+ else if (rightTile->state == 0 && rightTile->tile == LevelTileTypeOrangeDisk)
+ {
+ // loc_4E9B9: ; CODE XREF: update?+3C0j
+ if (rightRightTile->state != 0 || rightRightTile->tile != LevelTileTypeSpace)
+ {
+ return position;
+ }
+
+ // loc_4E9C3: ; CODE XREF: update?+B30j
+ if (belowRightTile->state == 0 && belowRightTile->tile == LevelTileTypeSpace)
+ {
+ return position;
+ }
+
+ // loc_4E9CD: ; CODE XREF: update?+B3Aj
+ rightRightTile->state = 1;
+ // push si
+ // mov di, [si+6155h]
+ // si = word_5157C;
+ drawMovingFrame(97, 132, position);
+ // pop si
+ // dx = 0x10FE;
+ murphyTile->state = 0x29;
+
+ // loc_4E9E7: ; CODE XREF: update?+84Ej update?+87Fj ...
+ gMurphyCounterToStartPushAnimation = 8;
+
+ // loc_4E9ED: ; CODE XREF: update?+A66j
+ gIsMurphyGoingThroughPortal = 0;
+
+ return updateMurphyAnimationInfo(position, kMurphyAnimationDescriptors[48]);
+ }
+ // loc_4E253: ; CODE XREF: update?+3BEj
+ else if (checkMurphyMovementToPosition(position + 1, UserInputRight) != 1)
+ {
+ return handleMurphyDirectionRight(position);
+ }
+ else
+ {
+ return position;
+ }
+}
+
+int16_t updateMurphyAnimationInfo(int16_t position, MurphyAnimationDescriptor unknownMurphyData)
+{
+ // 01ED:7D9F
+
+ // loc_4E9F3: ; CODE XREF: update?+4B0j update?+9B4j
+ // di = 0x0DE0;
+ // memcpy(di, si, 7 * 2); // rep movsw
+
+ gCurrentMurphyAnimation = unknownMurphyData;
+
+ return updateMurphyAnimation(position);
+}
+
+int16_t updateMurphyAnimation(int16_t position)
+{
+ // 01ED:7DA4
+
+ StatefulLevelTile *murphyTile = &gCurrentLevelState[position];
+ StatefulLevelTile *leftLeftTile = &gCurrentLevelState[position - 2];
+ StatefulLevelTile *leftTile = &gCurrentLevelState[position - 1];
+ StatefulLevelTile *rightTile = &gCurrentLevelState[position + 1];
+ StatefulLevelTile *rightRightTile = &gCurrentLevelState[position + 2];
+ StatefulLevelTile *belowTile = &gCurrentLevelState[position + kLevelWidth];
+ StatefulLevelTile *belowBelowTile = &gCurrentLevelState[position + kLevelWidth * 2];
+ StatefulLevelTile *aboveTile = &gCurrentLevelState[position - kLevelWidth];
+ StatefulLevelTile *aboveAboveTile = &gCurrentLevelState[position - kLevelWidth * 2];
+ StatefulLevelTile *belowRightRightTile = &gCurrentLevelState[position + kLevelWidth + 2];
+
+ // loc_4EA07: ; CODE XREF: update?+21j
+ gMurphyYawnAndSleepCounter = 0;
+
+ if (gMurphyCounterToStartPushAnimation == 0)
+ {
+ // 01ED:7E08
+ uint8_t currentFrame = gCurrentMurphyAnimation.currentFrame;
+ AnimationFrameCoordinates animationFrameCoordinates = kMurphyAnimationFrameCoordinates[gCurrentMurphyAnimation.animationIndex];
+ Point frameCoordinates = animationFrameCoordinates.coordinates[currentFrame];
+
+ // loc_4EA6B: ; CODE XREF: update?+B83j
+ gMurphyPositionX += gCurrentMurphyAnimation.speedX;
+ gMurphyPositionY += gCurrentMurphyAnimation.speedY;
+ gCurrentMurphyAnimation.currentFrame++;
+
+ uint16_t dstX = (position % kLevelWidth) * kTileSize;
+ uint16_t dstY = (position / kLevelWidth) * kTileSize;
+
+ int16_t offsetX = (gCurrentMurphyAnimation.animationCoordinatesOffset % 122) * 8;
+ int16_t offsetY = (gCurrentMurphyAnimation.animationCoordinatesOffset / 122);
+
+ // loc_4EA9F: ; CODE XREF: update?+C28j
+ drawMovingSpriteFrameInLevel(frameCoordinates.x,
+ frameCoordinates.y,
+ gCurrentMurphyAnimation.width * 8,
+ gCurrentMurphyAnimation.height,
+ dstX + offsetX,
+ dstY + offsetY);
+
+ if (gIsMurphyGoingThroughPortal != 0)
+ {
+ // This +1 is because the "opposite" portal animation is always the next one
+ AnimationFrameCoordinates animationFrameCoordinates = kMurphyAnimationFrameCoordinates[gCurrentMurphyAnimation.animationIndex + 1];
+ Point frameCoordinates = animationFrameCoordinates.coordinates[currentFrame];
+
+ int16_t offsetX = (gCurrentMurphyAnimation.animationCoordinatesOffsetIncrement % 122) * 8;
+ int16_t offsetY = (gCurrentMurphyAnimation.animationCoordinatesOffsetIncrement / 122);
+
+ drawMovingSpriteFrameInLevel(frameCoordinates.x,
+ frameCoordinates.y,
+ gCurrentMurphyAnimation.width * 8,
+ gCurrentMurphyAnimation.height,
+ dstX + offsetX,
+ dstY + offsetY);
+ }
+ else
+ {
+ // loc_4EAFA: ; CODE XREF: update?+C32j
+ gCurrentMurphyAnimation.animationCoordinatesOffset += gCurrentMurphyAnimation.animationCoordinatesOffsetIncrement;
+ }
+
+ // loc_4EB04: ; CODE XREF: update?+C68j
+ if (gCurrentMurphyAnimation.currentFrame < animationFrameCoordinates.numberOfCoordinates)
+ {
+ return position;
+ }
+
+ // loc_4EB10: ; CODE XREF: update?+C7Bj
+ // 01ED:7EAD
+ gMurphyTileX += gCurrentMurphyAnimation.speedX / 2;
+ gMurphyTileY += gCurrentMurphyAnimation.speedY / 2;
+ uint8_t previousMurphyMovingObject = murphyTile->state;
+ murphyTile->state = 0;
+ if (previousMurphyMovingObject == 1)
+ {
+ // loc_4EC93: ; CODE XREF: update?+CA3j update?+CC3j
+ murphyTile->tile = LevelTileTypeMurphy;
+ handleMurphyCollisionAfterMovement(position + kLevelWidth);
+ return position;
+ }
+ // loc_4EB36: ; CODE XREF: update?+CA1j
+ else if (previousMurphyMovingObject == 2)
+ {
+ // loc_4ECB1: ; CODE XREF: update?+CABj update?+CCBj
+ murphyTile->tile = LevelTileTypeMurphy;
+ handleMurphyCollisionAfterMovement(position + 1);
+ return position;
+ }
+ // loc_4EB3E: ; CODE XREF: update?+CA9j
+ else if (previousMurphyMovingObject == 3)
+ {
+ // loc_4ECCF: ; CODE XREF: update?+CB3j update?+CD3j
+ if (aboveTile->tile != LevelTileTypeExplosion)
+ {
+ aboveTile->state = 0;
+ aboveTile->tile = LevelTileTypeSpace;
+ }
+
+ // loc_4ECDC: ; CODE XREF: update?+E44j
+ murphyTile->state = 0;
+ murphyTile->tile = LevelTileTypeMurphy;
+ return position;
+ }
+ // loc_4EB46: ; CODE XREF: update?+CB1j
+ else if (previousMurphyMovingObject == 4)
+ {
+ // loc_4EF53: ; CODE XREF: update?+CBBj update?+CDBj
+ handleMurphyCollisionAfterMovement(position - 1);
+ murphyTile->state = 0;
+ murphyTile->tile = LevelTileTypeMurphy;
+ return position;
+ }
+ // loc_4EB4E: ; CODE XREF: update?+CB9j
+ else if (previousMurphyMovingObject == 5)
+ {
+ // loc_4EC93: ; CODE XREF: update?+CA3j update?+CC3j
+ murphyTile->tile = LevelTileTypeMurphy;
+ handleMurphyCollisionAfterMovement(position + kLevelWidth);
+ return position;
+ }
+ // loc_4EB56: ; CODE XREF: update?+CC1j
+ else if (previousMurphyMovingObject == 6)
+ {
+ // loc_4ECB1: ; CODE XREF: update?+CABj update?+CCBj
+ murphyTile->tile = LevelTileTypeMurphy;
+ handleMurphyCollisionAfterMovement(position + 1);
+ return position;
+ }
+ // loc_4EB5E: ; CODE XREF: update?+CC9j
+ else if (previousMurphyMovingObject == 7)
+ {
+ // loc_4ECCF: ; CODE XREF: update?+CB3j update?+CD3j
+ if (aboveTile->tile != LevelTileTypeExplosion)
+ {
+ aboveTile->state = 0;
+ aboveTile->tile = LevelTileTypeSpace;
+ }
+
+ // loc_4ECDC: ; CODE XREF: update?+E44j
+ murphyTile->state = 0;
+ murphyTile->tile = LevelTileTypeMurphy;
+ return position;
+ }
+ // loc_4EB66: ; CODE XREF: update?+CD1j
+ else if (previousMurphyMovingObject == 8)
+ {
+ // loc_4EF53: ; CODE XREF: update?+CBBj update?+CDBj
+ // 01ED:82F0
+ handleMurphyCollisionAfterMovement(position - 1);
+ murphyTile->state = 0;
+ murphyTile->tile = LevelTileTypeMurphy;
+ return position;
+ }
+ // loc_4EB6E: ; CODE XREF: update?+CD9j
+ else if (previousMurphyMovingObject == 9)
+ {
+ // loc_4EC85: ; CODE XREF: update?+CE3j
+ if (gNumberOfRemainingInfotrons > 0)
+ {
+ gNumberOfRemainingInfotrons--;
+ }
+
+ // loc_4EC90: ; CODE XREF: update?+DFAj
+ drawNumberOfRemainingInfotrons();
+
+ // loc_4EC93: ; CODE XREF: update?+CA3j update?+CC3j
+ murphyTile->tile = LevelTileTypeMurphy;
+ handleMurphyCollisionAfterMovement(position + kLevelWidth);
+ return position;
+ }
+ // loc_4EB76: ; CODE XREF: update?+CE1j
+ else if (previousMurphyMovingObject == 10)
+ {
+ // loc_4ECA3: ; CODE XREF: update?+CEBj
+ if (gNumberOfRemainingInfotrons > 0)
+ {
+ gNumberOfRemainingInfotrons--;
+ }
+
+ // loc_4ECAE: ; CODE XREF: update?+E18j
+ drawNumberOfRemainingInfotrons();
+
+ // loc_4ECB1: ; CODE XREF: update?+CABj update?+CCBj
+ murphyTile->tile = LevelTileTypeMurphy;
+ handleMurphyCollisionAfterMovement(position + 1);
+ return position;
+ }
+ // loc_4EB7E: ; CODE XREF: update?+CE9j
+ else if (previousMurphyMovingObject == 11)
+ {
+ // loc_4ECC1: ; CODE XREF: update?+CF3j
+ if (gNumberOfRemainingInfotrons > 0)
+ {
+ gNumberOfRemainingInfotrons--;
+ }
+
+ // loc_4ECCC: ; CODE XREF: update?+E36j
+ drawNumberOfRemainingInfotrons();
+
+ // loc_4ECCF: ; CODE XREF: update?+CB3j update?+CD3j
+ if (aboveTile->tile != LevelTileTypeExplosion)
+ {
+ aboveTile->state = 0;
+ aboveTile->tile = LevelTileTypeSpace;
+ }
+
+ // loc_4ECDC: ; CODE XREF: update?+E44j
+ murphyTile->state = 0;
+ murphyTile->tile = LevelTileTypeMurphy;
+ return position;
+ }
+ // loc_4EB86: ; CODE XREF: update?+CF1j
+ else if (previousMurphyMovingObject == 12)
+ {
+ // loc_4EF45: ; CODE XREF: update?+CFBj
+ if (gNumberOfRemainingInfotrons > 0)
+ {
+ gNumberOfRemainingInfotrons--;
+ }
+
+ // loc_4EF50: ; CODE XREF: update?+10BAj
+ drawNumberOfRemainingInfotrons();
+
+ // loc_4EF53: ; CODE XREF: update?+CBBj update?+CDBj
+ handleMurphyCollisionAfterMovement(position - 1);
+ murphyTile->state = 0;
+ murphyTile->tile = LevelTileTypeMurphy;
+ return position;
+ }
+ // loc_4EB8E: ; CODE XREF: update?+CF9j
+ else if (previousMurphyMovingObject == 14)
+ {
+ // loc_4ECE3: ; CODE XREF: update?+D03j
+ if (murphyTile->tile != LevelTileTypeExplosion)
+ {
+ murphyTile->state = 0;
+ murphyTile->tile = LevelTileTypeSpace;
+ }
+
+ // loc_4ECF0: ; CODE XREF: update?+E58j
+ leftTile->state = 0;
+ leftTile->tile = LevelTileTypeMurphy;
+ leftLeftTile->state = 0;
+ leftLeftTile->tile = LevelTileTypeZonk;
+ handleZonkPushedByMurphy(position - 2);
+ return position - 1;
+ }
+ // loc_4EB96: ; CODE XREF: update?+D01j
+ else if (previousMurphyMovingObject == 15)
+ {
+ // loc_4ED06: ; CODE XREF: update?+D0Bj
+ if (murphyTile->tile != LevelTileTypeExplosion)
+ {
+ murphyTile->state = 0;
+ murphyTile->tile = LevelTileTypeSpace;
+ }
+
+ // loc_4ED13: ; CODE XREF: update?+E7Bj
+ rightTile->state = 0;
+ rightTile->tile = LevelTileTypeMurphy;
+ rightRightTile->state = 0;
+ rightRightTile->tile = LevelTileTypeZonk;
+ handleZonkPushedByMurphy(position + 2);
+ return position + 1;
+ }
+ // loc_4EB9E: ; CODE XREF: update?+D09j
+ else if (previousMurphyMovingObject == 16)
+ {
+ // loc_4EF71: ; CODE XREF: update?+D13j
+ if (aboveTile->tile != LevelTileTypeExplosion)
+ {
+ aboveTile->state = 0;
+ aboveTile->tile = LevelTileTypeSpace;
+ }
+
+ return position;
+ }
+ // loc_4EBA6: ; CODE XREF: update?+D11j
+ else if (previousMurphyMovingObject == 17)
+ {
+ // loc_4EF8D: ; CODE XREF: update?+D1Bj
+ if (leftTile->tile != LevelTileTypeExplosion)
+ {
+ leftTile->state = 0;
+ leftTile->tile = LevelTileTypeSpace;
+ }
+
+ return position;
+ }
+ // loc_4EBAE: ; CODE XREF: update?+D19j
+ else if (previousMurphyMovingObject == 19)
+ {
+ // loc_4EFC5: ; CODE XREF: update?+D23j
+ if (rightTile->tile != LevelTileTypeExplosion)
+ {
+ rightTile->state = 0;
+ rightTile->tile = LevelTileTypeSpace;
+ }
+
+ return position;
+ }
+ // loc_4EBB6: ; CODE XREF: update?+D21j
+ else if (previousMurphyMovingObject == 18)
+ {
+ // loc_4EFA9: ; CODE XREF: update?+D2Bj
+ if (belowTile->tile != LevelTileTypeExplosion)
+ {
+ belowTile->state = 0;
+ belowTile->tile = LevelTileTypeSpace;
+ }
+
+ return position;
+ }
+ // loc_4EBBE: ; CODE XREF: update?+D29j
+ else if (previousMurphyMovingObject == 20)
+ {
+ // loc_4EF63: ; CODE XREF: update?+D33j
+ if (gNumberOfRemainingInfotrons > 0)
+ {
+ gNumberOfRemainingInfotrons--;
+ }
+
+ // loc_4EF6E: ; CODE XREF: update?+10D8j
+ drawNumberOfRemainingInfotrons();
+
+ // loc_4EF71: ; CODE XREF: update?+D13j
+ if (aboveTile->tile != LevelTileTypeExplosion)
+ {
+ aboveTile->state = 0;
+ aboveTile->tile = LevelTileTypeSpace;
+ }
+
+ return position;
+ }
+ // loc_4EBC6: ; CODE XREF: update?+D31j
+ else if (previousMurphyMovingObject == 21)
+ {
+ // loc_4EF7F: ; CODE XREF: update?+D3Bj
+ if (gNumberOfRemainingInfotrons > 0)
+ {
+ gNumberOfRemainingInfotrons--;
+ }
+
+ // loc_4EF8A: ; CODE XREF: update?+10F4j
+ drawNumberOfRemainingInfotrons();
+
+ // loc_4EF8D: ; CODE XREF: update?+D1Bj
+ if (leftTile->tile != LevelTileTypeExplosion)
+ {
+ leftTile->state = 0;
+ leftTile->tile = LevelTileTypeSpace;
+ }
+
+ return position;
+ }
+ // loc_4EBCE: ; CODE XREF: update?+D39j
+ else if (previousMurphyMovingObject == 23)
+ {
+ // loc_4EFB7: ; CODE XREF: update?+D43j
+ if (gNumberOfRemainingInfotrons > 0)
+ {
+ gNumberOfRemainingInfotrons--;
+ }
+
+ // loc_4EFC2: ; CODE XREF: update?+112Cj
+ drawNumberOfRemainingInfotrons();
+
+ // loc_4EFC5: ; CODE XREF: update?+D23j
+ if (rightTile->tile != LevelTileTypeExplosion)
+ {
+ rightTile->state = 0;
+ rightTile->tile = LevelTileTypeSpace;
+ }
+
+ return position;
+ }
+ // loc_4EBD6: ; CODE XREF: update?+D41j
+ else if (previousMurphyMovingObject == 22)
+ {
+ // loc_4EF9B: ; CODE XREF: update?+D4Bj
+ if (gNumberOfRemainingInfotrons > 0)
+ {
+ gNumberOfRemainingInfotrons--;
+ }
+
+ // loc_4EFA6: ; CODE XREF: update?+1110j
+ drawNumberOfRemainingInfotrons();
+
+ // loc_4EFA9: ; CODE XREF: update?+D2Bj
+ if (belowTile->tile != LevelTileTypeExplosion)
+ {
+ belowTile->state = 0;
+ belowTile->tile = LevelTileTypeSpace;
+ }
+
+ return position;
+ }
+ // loc_4EBDE: ; CODE XREF: update?+D49j
+ else if (previousMurphyMovingObject == 13)
+ {
+ // loc_4ED42: ; CODE XREF: update?+D53j
+ gShouldExitLevel = 1;
+ return position;
+ }
+ // loc_4EBE6: ; CODE XREF: update?+D51j
+ else if (previousMurphyMovingObject == 24)
+ {
+ // loc_4EFD3: ; CODE XREF: update?+D5Bj
+ if (murphyTile->tile != LevelTileTypeExplosion)
+ {
+ murphyTile->state = 0;
+ murphyTile->tile = LevelTileTypeSpace;
+ }
+
+ // loc_4EFE0: ; CODE XREF: update?+1148j
+ aboveAboveTile->state = 0;
+ aboveAboveTile->tile = LevelTileTypeMurphy;
+ gIsMurphyGoingThroughPortal = 0;
+ position -= kLevelWidth * 2;
+ if (aboveTile->state == 1)
+ {
+ updateSpecialPort(position + kLevelWidth);
+ }
+
+ return position;
+ }
+ // loc_4EBEE: ; CODE XREF: update?+D59j
+ else if (previousMurphyMovingObject == 25)
+ {
+ // loc_4F001: ; CODE XREF: update?+D63j
+ if (murphyTile->tile != LevelTileTypeExplosion)
+ {
+ murphyTile->state = 0;
+ murphyTile->tile = LevelTileTypeSpace;
+ }
+
+ // loc_4F00E: ; CODE XREF: update?+1176j
+ leftLeftTile->state = 0;
+ leftLeftTile->tile = LevelTileTypeMurphy;
+ gIsMurphyGoingThroughPortal = 0;
+ position -= 2;
+ if (leftTile->state == 1)
+ {
+ updateSpecialPort(position + 1);
+ }
+
+ return position;
+ }
+ // loc_4EBF6: ; CODE XREF: update?+D61j
+ else if (previousMurphyMovingObject == 26)
+ {
+ // loc_4F02E: ; CODE XREF: update?+D6Bj
+ if (murphyTile->tile != LevelTileTypeExplosion)
+ {
+ murphyTile->state = 0;
+ murphyTile->tile = LevelTileTypeSpace;
+ }
+
+ // loc_4F03B: ; CODE XREF: update?+11A3j
+ belowBelowTile->state = 0;
+ belowBelowTile->tile = LevelTileTypeMurphy;
+ gIsMurphyGoingThroughPortal = 0;
+ position += kLevelWidth * 2;
+ if (belowTile->state == 1)
+ {
+ updateSpecialPort(position - kLevelWidth);
+ }
+
+ return position;
+ }
+ // loc_4EBFE: ; CODE XREF: update?+D69j
+ else if (previousMurphyMovingObject == 27)
+ {
+ // loc_4F05C: ; CODE XREF: update?+D73j
+ if (murphyTile->tile != LevelTileTypeExplosion)
+ {
+ murphyTile->state = 0;
+ murphyTile->tile = LevelTileTypeSpace;
+ }
+
+ // loc_4F069: ; CODE XREF: update?+11D1j
+ rightRightTile->state = 0;
+ rightRightTile->tile = LevelTileTypeMurphy;
+ gIsMurphyGoingThroughPortal = 0;
+ position += 2;
+ if (rightTile->state == 1)
+ {
+ updateSpecialPort(position - 1);
+ }
+
+ return position;
+ }
+ // loc_4EC06: ; CODE XREF: update?+D71j
+ else if (previousMurphyMovingObject == 28)
+ {
+ // loc_4F089: ; CODE XREF: update?+D7Bj
+ if (murphyTile->tile != LevelTileTypeExplosion)
+ {
+ murphyTile->state = 0;
+ murphyTile->tile = LevelTileTypeSpace;
+ }
+
+ // loc_4F096: ; CODE XREF: update?+11FEj
+ position -= kLevelWidth;
+
+ // loc_4FDAF: ; CODE XREF: update?+1209j
+ // ; update?:loc_4F0A9j ...
+ aboveTile->state = 0;
+ aboveTile->tile = LevelTileTypeMurphy;
+ decreaseRemainingRedDisksIfNeeded(position);
+ return position;
+ }
+ // loc_4EC0E: ; CODE XREF: update?+D79j
+ else if (previousMurphyMovingObject == 29)
+ {
+ // loc_4F09C: ; CODE XREF: update?+D83j
+ if (rightTile->tile != LevelTileTypeExplosion)
+ {
+ rightTile->state = 0;
+ rightTile->tile = LevelTileTypeSpace;
+ }
+
+ // loc_4FDAF: ; CODE XREF: update?+1209j
+ // ; update?:loc_4F0A9j ...
+ murphyTile->state = 0;
+ murphyTile->tile = LevelTileTypeMurphy;
+ decreaseRemainingRedDisksIfNeeded(position);
+ return position;
+ }
+ // loc_4EC16: ; CODE XREF: update?+D81j
+ else if (previousMurphyMovingObject == 30)
+ {
+ // loc_4F0AC: ; CODE XREF: update?+D8Bj
+ if (murphyTile->tile != LevelTileTypeExplosion)
+ {
+ murphyTile->state = 0;
+ murphyTile->tile = LevelTileTypeSpace;
+ }
+
+ // loc_4F0B9: ; CODE XREF: update?+1221j
+ position += kLevelWidth;
+
+ // loc_4FDAF: ; CODE XREF: update?+1209j
+ // ; update?:loc_4F0A9j ...
+ belowTile->state = 0;
+ belowTile->tile = LevelTileTypeMurphy;
+ decreaseRemainingRedDisksIfNeeded(position);
+ return position;
+ }
+ // loc_4EC1E: ; CODE XREF: update?+D89j
+ else if (previousMurphyMovingObject == 31)
+ {
+ // loc_4F0BF: ; CODE XREF: update?+D93j
+ if (leftTile->tile != LevelTileTypeExplosion)
+ {
+ leftTile->state = 0;
+ leftTile->tile = LevelTileTypeSpace;
+ }
+
+ // loc_4FDAF: ; CODE XREF: update?+1209j
+ // ; update?:loc_4F0A9j ...
+ murphyTile->state = 0;
+ murphyTile->tile = LevelTileTypeMurphy;
+ decreaseRemainingRedDisksIfNeeded(position);
+ return position;
+ }
+ // loc_4EC26: ; CODE XREF: update?+D91j
+ else if (previousMurphyMovingObject == 32)
+ {
+ // loc_4F0CF: ; CODE XREF: update?+D9Bj
+ if (aboveTile->tile != LevelTileTypeExplosion)
+ {
+ aboveTile->state = 0;
+ aboveTile->tile = LevelTileTypeSpace;
+ }
+
+ // loc_4F0DC: ; CODE XREF: update?+1244j
+ decreaseRemainingRedDisksIfNeeded(position - kLevelWidth);
+ return position;
+ }
+ // loc_4EC2E: ; CODE XREF: update?+D99j
+ else if (previousMurphyMovingObject == 33)
+ {
+ // loc_4F0E6: ; CODE XREF: update?+DA3j
+ if (leftTile->tile != LevelTileTypeExplosion)
+ {
+ leftTile->state = 0;
+ leftTile->tile = LevelTileTypeSpace;
+ }
+
+ // loc_4F0F3: ; CODE XREF: update?+125Bj
+ decreaseRemainingRedDisksIfNeeded(position - 1);
+ return position;
+ }
+ // loc_4EC36: ; CODE XREF: update?+DA1j
+ else if (previousMurphyMovingObject == 34)
+ {
+ // loc_4F0FD: ; CODE XREF: update?+DABj
+ if (belowTile->tile != LevelTileTypeExplosion)
+ {
+ belowTile->state = 0;
+ belowTile->tile = LevelTileTypeSpace;
+ }
+
+ // loc_4F10A: ; CODE XREF: update?+1272j
+ decreaseRemainingRedDisksIfNeeded(position + kLevelWidth);
+ return position;
+ }
+ // loc_4EC3E: ; CODE XREF: update?+DA9j
+ else if (previousMurphyMovingObject == 35)
+ {
+ // loc_4F114: ; CODE XREF: update?+DB3j
+ if (rightTile->tile != LevelTileTypeExplosion)
+ {
+ rightTile->state = 0;
+ rightTile->tile = LevelTileTypeSpace;
+ }
+
+ // loc_4F121: ; CODE XREF: update?+1289j
+ decreaseRemainingRedDisksIfNeeded(position + 1);
+ return position;
+ }
+ // loc_4EC46: ; CODE XREF: update?+DB1j
+ else if (previousMurphyMovingObject == 36)
+ {
+ // loc_4F12B: ; CODE XREF: update?+DBBj
+ if (murphyTile->tile != LevelTileTypeExplosion)
+ {
+ murphyTile->state = 0;
+ murphyTile->tile = LevelTileTypeSpace;
+ }
+
+ // loc_4F138: ; CODE XREF: update?+12A0j
+ aboveTile->state = 0;
+ aboveTile->tile = LevelTileTypeMurphy;
+ aboveAboveTile->state = 0;
+ aboveAboveTile->tile = LevelTileTypeYellowDisk;
+ return position - kLevelWidth;
+ }
+ // loc_4EC4E: ; CODE XREF: update?+DB9j
+ else if (previousMurphyMovingObject == 37)
+ {
+ // loc_4F148: ; CODE XREF: update?+DC3j
+ if (murphyTile->tile != LevelTileTypeExplosion)
+ {
+ murphyTile->state = 0;
+ murphyTile->tile = LevelTileTypeSpace;
+ }
+
+ // loc_4F155: ; CODE XREF: update?+12BDj
+ leftTile->state = 0;
+ leftTile->tile = LevelTileTypeMurphy;
+ leftLeftTile->state = 0;
+ leftLeftTile->tile = LevelTileTypeYellowDisk;
+ return position - 1;
+ }
+ // loc_4EC56: ; CODE XREF: update?+DC1j
+ else if (previousMurphyMovingObject == 39)
+ {
+ // loc_4F165: ; CODE XREF: update?+DCBj
+ if (murphyTile->tile != LevelTileTypeExplosion)
+ {
+ murphyTile->state = 0;
+ murphyTile->tile = LevelTileTypeSpace;
+ }
+
+ // loc_4F172: ; CODE XREF: update?+12DAj
+ belowTile->state = 0;
+ belowTile->tile = LevelTileTypeMurphy;
+ belowBelowTile->state = 0;
+ belowBelowTile->tile = LevelTileTypeYellowDisk;
+ return position + kLevelWidth;
+ }
+ // loc_4EC5E: ; CODE XREF: update?+DC9j
+ else if (previousMurphyMovingObject == 38)
+ {
+ // loc_4F182: ; CODE XREF: update?+DD3j
+ if (murphyTile->tile != LevelTileTypeExplosion)
+ {
+ murphyTile->state = 0;
+ murphyTile->tile = LevelTileTypeSpace;
+ }
+
+ // loc_4F18F: ; CODE XREF: update?+12F7j
+ rightTile->state = 0;
+ rightTile->tile = LevelTileTypeMurphy;
+ rightRightTile->state = 0;
+ rightRightTile->tile = LevelTileTypeYellowDisk;
+ return position + 1;
+ }
+ // loc_4EC66: ; CODE XREF: update?+DD1j
+ else if (previousMurphyMovingObject == 40)
+ {
+ // loc_4F19F: ; CODE XREF: update?+DDBj
+ if (murphyTile->tile != LevelTileTypeExplosion)
+ {
+ murphyTile->state = 0;
+ murphyTile->tile = LevelTileTypeSpace;
+ }
+
+ // loc_4F1AC: ; CODE XREF: update?+1314j
+ leftTile->state = 0;
+ leftTile->tile = LevelTileTypeMurphy;
+ leftLeftTile->state = 0;
+ leftLeftTile->tile = LevelTileTypeOrangeDisk;
+ return position - 1;
+ }
+ // loc_4EC6E: ; CODE XREF: update?+DD9j
+ else if (previousMurphyMovingObject == 41)
+ {
+ // loc_4F1BC: ; CODE XREF: update?+DE3j
+ if (murphyTile->tile != LevelTileTypeExplosion)
+ {
+ murphyTile->state = 0;
+ murphyTile->tile = LevelTileTypeSpace;
+ }
+
+ // loc_4F1C9: ; CODE XREF: update?+1331j
+ rightTile->state = 0;
+ rightTile->tile = LevelTileTypeMurphy;
+ rightRightTile->state = 0;
+ rightRightTile->tile = LevelTileTypeOrangeDisk;
+ if (belowRightRightTile->state == 0 && belowRightRightTile->tile == LevelTileTypeSpace)
+ {
+ rightRightTile->state = 0x20;
+ belowRightRightTile->state = 8;
+ }
+
+ // loc_4F1E6: ; CODE XREF: update?+134Aj
+ return position + 1;
+ }
+ // loc_4EC76: ; CODE XREF: update?+DE1j
+ else if (previousMurphyMovingObject == 42)
+ {
+ // loc_4F1EA: ; CODE XREF: update?+DEBj
+ murphyTile->state = 0;
+ murphyTile->tile = LevelTileTypeMurphy;
+ gPlantedRedDiskCountdown = 2;
+ gNumberOfRemainingRedDisks--;
+ drawNumberOfRemainingRedDisks();
+ playPushSound();
+ return position;
+ }
+ else
+ {
+ // loc_4EC7E: ; CODE XREF: update?+DE9j
+ gShouldExitLevel = 1;
+ return position;
+ }
+ }
+
+ gMurphyCounterToStartPushAnimation--;
+ if (gMurphyCounterToStartPushAnimation == 0)
+ {
+ playPushSound();
+ }
+
+ // loc_4EA1E: ; CODE XREF: update?+B89j
+ if (murphyTile->state == 0xE)
+ {
+ // loc_4ED49: ; CODE XREF: update?+B97j
+ if (gCurrentUserInput == UserInputLeft && (leftTile->state == 0 && leftTile->tile == LevelTileTypeZonk))
+ {
+ return position;
+ }
+
+ // loc_4ED5A: ; CODE XREF: update?+EC0j update?+EC7j
+ murphyTile->state = 0;
+ murphyTile->tile = LevelTileTypeMurphy;
+ leftTile->state = 0;
+ leftTile->tile = LevelTileTypeZonk;
+ if (leftLeftTile->tile != LevelTileTypeExplosion)
+ {
+ leftLeftTile->state = 0;
+ leftLeftTile->tile = LevelTileTypeSpace;
+ }
+
+ // loc_4ED73: ; CODE XREF: update?+EDBj
+ // si = kMurphyStillSpriteCoordinates;
+ drawMovingFrame(304, 132, position);
+
+ return position;
+ }
+ // loc_4EA2A: ; CODE XREF: update?+B95j
+ else if (murphyTile->state == 0xF)
+ {
+ // loc_4ED81: ; CODE XREF: update?+B9Fj
+ if (gCurrentUserInput == UserInputRight && (rightTile->state == 0 && rightTile->tile == LevelTileTypeZonk))
+ {
+ return position;
+ }
+
+ // loc_4ED92: ; CODE XREF: update?+EF8j update?+EFFj
+ murphyTile->state = 0;
+ murphyTile->tile = LevelTileTypeMurphy;
+ rightTile->state = 0;
+ rightTile->tile = LevelTileTypeZonk;
+ if (rightRightTile->tile != LevelTileTypeExplosion)
+ {
+ rightRightTile->state = 0;
+ rightRightTile->tile = LevelTileTypeSpace;
+ }
+
+ // loc_4EDAB: ; CODE XREF: update?+F13j
+ // si = kMurphyStillSpriteCoordinates;
+ drawMovingFrame(304, 132, position);
+
+ return position;
+ }
+ // loc_4EA32: ; CODE XREF: update?+B9Dj
+ else if (murphyTile->state == 0x28)
+ {
+ // loc_4EDB9: ; CODE XREF: update?+BA7j
+ if (gCurrentUserInput == UserInputLeft && (leftTile->state == 0 && leftTile->tile == LevelTileTypeOrangeDisk))
+ {
+ return position;
+ }
+
+ // loc_4EDCA: ; CODE XREF: update?+F30j update?+F37j
+ murphyTile->state = 0;
+ murphyTile->tile = LevelTileTypeMurphy;
+ leftTile->state = 0;
+ leftTile->tile = LevelTileTypeOrangeDisk;
+ if (leftLeftTile->tile != LevelTileTypeExplosion)
+ {
+ leftLeftTile->state = 0;
+ leftLeftTile->tile = LevelTileTypeSpace;
+ }
+
+ // loc_4EDE3: ; CODE XREF: update?+F4Bj
+ // si = kMurphyStillSpriteCoordinates;
+ drawMovingFrame(304, 132, position);
+
+ return position;
+ }
+ // loc_4EA3A: ; CODE XREF: update?+BA5j
+ else if (murphyTile->state == 0x29)
+ {
+ // loc_4EDF1: ; CODE XREF: update?+BAFj
+ if (gCurrentUserInput == UserInputRight && (rightTile->state == 0 && rightTile->tile == LevelTileTypeOrangeDisk))
+ {
+ return position;
+ }
+
+ // loc_4EE02: ; CODE XREF: update?+F68j update?+F6Fj
+ murphyTile->state = 0;
+ murphyTile->tile = LevelTileTypeMurphy;
+ rightTile->state = 0;
+ rightTile->tile = LevelTileTypeOrangeDisk;
+ if (rightRightTile->tile != LevelTileTypeExplosion)
+ {
+ rightRightTile->state = 0;
+ rightRightTile->tile = LevelTileTypeSpace;
+ }
+
+ // loc_4EE1B: ; CODE XREF: update?+F83j
+ // si = kMurphyStillSpriteCoordinates;
+ drawMovingFrame(304, 132, position);
+
+ return position;
+ }
+ // loc_4EA42: ; CODE XREF: update?+BADj
+ else if (murphyTile->state == 0x24)
+ {
+ // loc_4EE29: ; CODE XREF: update?+BB7j
+ if (gCurrentUserInput == UserInputUp && (aboveTile->state == 0 && aboveTile->tile == LevelTileTypeYellowDisk))
+ {
+ return position;
+ }
+
+ // loc_4EE3A: ; CODE XREF: update?+FA0j update?+FA7j
+ murphyTile->state = 0;
+ murphyTile->tile = LevelTileTypeMurphy;
+ aboveTile->state = 0;
+ aboveTile->tile = LevelTileTypeYellowDisk;
+ if (aboveAboveTile->tile != LevelTileTypeExplosion)
+ {
+ aboveAboveTile->state = 0;
+ aboveAboveTile->tile = LevelTileTypeSpace;
+ }
+
+ // loc_4EE53: ; CODE XREF: update?+FBBj
+ // si = kMurphyStillSpriteCoordinates;
+ drawMovingFrame(304, 132, position);
+
+ return position;
+ }
+ // loc_4EA4A: ; CODE XREF: update?+BB5j
+ else if (murphyTile->state == 0x25)
+ {
+ // loc_4EE61: ; CODE XREF: update?+BBFj
+ if (gCurrentUserInput == UserInputLeft && (leftTile->state == 0 && leftTile->tile == LevelTileTypeYellowDisk))
+ {
+ return position;
+ }
+
+ // loc_4EE72: ; CODE XREF: update?+FD8j update?+FDFj
+ murphyTile->state = 0;
+ murphyTile->tile = LevelTileTypeMurphy;
+ leftTile->state = 0;
+ leftTile->tile = LevelTileTypeYellowDisk;
+ if (leftLeftTile->tile != LevelTileTypeExplosion)
+ {
+ leftLeftTile->state = 0;
+ leftLeftTile->tile = LevelTileTypeSpace;
+ }
+
+ // loc_4EE8B: ; CODE XREF: update?+FF3j
+ // si = kMurphyStillSpriteCoordinates;
+ drawMovingFrame(304, 132, position);
+
+ return position;
+ }
+ // loc_4EA52: ; CODE XREF: update?+BBDj
+ else if (murphyTile->state == 0x27)
+ {
+ // loc_4EE99: ; CODE XREF: update?+BC7j
+ if (gCurrentUserInput == UserInputDown && (belowTile->state == 0 && belowTile->tile == LevelTileTypeYellowDisk))
+ {
+ return position;
+ }
+
+ // loc_4EEAA: ; CODE XREF: update?+1010j
+ // ; update?+1017j
+ murphyTile->state = 0;
+ murphyTile->tile = LevelTileTypeMurphy;
+ belowTile->state = 0;
+ belowTile->tile = LevelTileTypeYellowDisk;
+ if (belowBelowTile->tile != LevelTileTypeExplosion)
+ {
+ belowBelowTile->state = 0;
+ belowBelowTile->tile = LevelTileTypeSpace;
+ }
+
+ // loc_4EEC3: ; CODE XREF: update?+102Bj
+ // si = kMurphyStillSpriteCoordinates;
+ drawMovingFrame(304, 132, position);
+
+ return position;
+ }
+ // loc_4EA5A: ; CODE XREF: update?+BC5j
+ else if (murphyTile->state == 0x26)
+ {
+ // loc_4EED1: ; CODE XREF: update?+BCFj
+ if (gCurrentUserInput == UserInputRight && (rightTile->state == 0 && rightTile->tile == LevelTileTypeYellowDisk))
+ {
+ return position;
+ }
+
+ // loc_4EEE2: ; CODE XREF: update?+1048j
+ // ; update?+104Fj
+ murphyTile->state = 0;
+ murphyTile->tile = LevelTileTypeMurphy;
+ rightTile->state = 0;
+ rightTile->tile = LevelTileTypeYellowDisk;
+ if (rightRightTile->tile != LevelTileTypeExplosion)
+ {
+ rightRightTile->state = 0;
+ rightRightTile->tile = LevelTileTypeSpace;
+ }
+
+ // loc_4EEFB: ; CODE XREF: update?+1063j
+ // si = kMurphyStillSpriteCoordinates;
+ drawMovingFrame(304, 132, position);
+
+ return position;
+ }
+ // loc_4EA62: ; CODE XREF: update?+BCDj
+ else if (murphyTile->state == 0x2A)
+ {
+ // loc_4EF09: ; CODE XREF: update?+BD7j
+ if (gCurrentUserInput == UserInputSpaceOnly)
+ {
+ if (gMurphyCounterToStartPushAnimation > 0x20)
+ {
+ return position;
+ }
+
+ // si = word_51790;
+ drawMovingFrame(288, 132, position);
+ gPlantedRedDiskCountdown = 1;
+
+ return position;
+ }
+
+ // loc_4EF2C: ; CODE XREF: update?+1080j
+ murphyTile->state = 0;
+ murphyTile->tile = LevelTileTypeMurphy;
+
+ // si = kMurphyStillSpriteCoordinates;
+ drawMovingFrame(304, 132, position);
+ gPlantedRedDiskCountdown = 0;
+
+ return position;
+ }
+ else
+ {
+ return position;
+ }
+}
+
+void detonateYellowDisks()
+{
+ // loc_4E7B8: ; CODE XREF: update?+8AAj update?+8D4j ...
+ gTerminalMaxFramesToNextScroll = 7;
+ gAreYellowDisksDetonated = 1;
+
+ for (int i = 0; i < kLevelSize; ++i)
+ {
+ // loc_4E7C9: ; CODE XREF: update?+94Aj
+ StatefulLevelTile *tile = &gCurrentLevelState[i];
+ if (tile->state == 0 && tile->tile == LevelTileTypeYellowDisk)
+ {
+ detonateBigExplosion(i);
+ }
+ }
+}
+
+void handleZonkPushedByMurphy(int16_t position) // sub_4ED29 proc near ; CODE XREF: update?+E6Fp update?+E92p
+{
+ StatefulLevelTile *belowTile = &gCurrentLevelState[position + kLevelWidth];
+
+ if (belowTile->tile == LevelTileTypeSnikSnak || belowTile->tile == 0xBB)
+ {
+ // loc_4ED38: ; CODE XREF: handleZonkPushedByMurphy+5j handleZonkPushedByMurphy+Cj
+ detonateBigExplosion(position + kLevelWidth);
+ }
+}
+
+uint8_t checkMurphyMovementToPosition(int16_t position, UserInput userInput) // sub_4F21F proc near ; CODE XREF: update?+273p update?+2EDp ...
+{
+ // 01ED:85BC
+ // Parameters:
+ // - si: position
+ // - ax: value of that position (state + tile)
+ // - bl: user input to process
+ StatefulLevelTile *tile = &gCurrentLevelState[position];
+
+ if ((tile->state == 0xFF && tile->tile == 0xFF) || (tile->state == 0xAA && tile->tile == 0xAA) || (tile->state == 0))
+ {
+ // loc_4F296: ; CODE XREF: checkMurphyMovementToPosition+3j checkMurphyMovementToPosition+8j ...
+ return 1;
+ }
+ else if (tile->tile == LevelTileTypeZonk)
+ {
+ // loc_4F24F: ; CODE XREF: checkMurphyMovementToPosition+11j
+ if (userInput == UserInputLeft)
+ {
+ // loc_4F25E: ; CODE XREF: checkMurphyMovementToPosition+33j
+ uint8_t stateType = (tile->state & 0xF0);
+ if (stateType == 0x20 || stateType == 0x40 || stateType == 0x50 || stateType == 0x70)
+ {
+ // loc_4F278: ; CODE XREF: checkMurphyMovementToPosition+45j
+ // ; checkMurphyMovementToPosition+4Aj ...
+ return 1;
+ }
+ detonateBigExplosion(position);
+
+ // loc_4F278: ; CODE XREF: checkMurphyMovementToPosition+45j
+ // ; checkMurphyMovementToPosition+4Aj ...
+ return 1;
+ }
+ else if (userInput != UserInputRight)
+ {
+ detonateBigExplosion(position);
+ return 1;
+ }
+
+ // loc_4F27A: ; CODE XREF: checkMurphyMovementToPosition+38j
+ uint8_t stateType = (tile->state & 0xF0);
+ if (stateType == 0x30 || stateType == 0x40 || stateType == 0x60 || stateType == 0x70)
+ {
+ // loc_4F294: ; CODE XREF: checkMurphyMovementToPosition+61j
+ // ; checkMurphyMovementToPosition+66j ...
+ return 1;
+ }
+ detonateBigExplosion(position);
+
+ // loc_4F294: ; CODE XREF: checkMurphyMovementToPosition+61j
+ // ; checkMurphyMovementToPosition+66j ...
+ return 1;
+ }
+ else if (tile->tile == LevelTileTypeExplosion)
+ {
+ // loc_4F298: ; CODE XREF: checkMurphyMovementToPosition+15j
+ // 01ED:8635
+ if ((tile->state & 0x80) != 0 || tile->state < 4)
+ {
+ // loc_4F2A2: ; CODE XREF: checkMurphyMovementToPosition+7Cj
+ detonateBigExplosion(position);
+ return 1;
+ }
+ else
+ {
+ // loc_4F2A7: ; CODE XREF: checkMurphyMovementToPosition+81j
+ tile->state = 0;
+ tile->tile = LevelTileTypeSpace;
+ return 0;
+ }
+ }
+ else if (tile->tile == LevelTileTypeOrangeDisk || tile->tile == LevelTileTypePortRight || tile->tile == LevelTileTypePortDown || tile->tile == LevelTileTypePortLeft || tile->tile == LevelTileTypePortUp)
+ {
+ // loc_4F296: ; CODE XREF: checkMurphyMovementToPosition+3j checkMurphyMovementToPosition+8j ...
+ return 1;
+ }
+ else
+ {
+ detonateBigExplosion(position);
+ return 1;
+ }
+}
+
+void updateSpecialPort(int16_t position) // sub_4F2AF proc near ; CODE XREF: update?+116Ap
+ // ; update?+1197p ...
+{
+ // 01ED:864C
+ if (gNumberOfSpecialPorts == 0)
+ {
+ return;
+ }
+
+ uint8_t isPortInPosition = 0;
+ uint8_t portIndex = 0;
+
+ for (uint8_t i = 0; i < gNumberOfSpecialPorts; ++i)
+ {
+ // loc_4F2BD: ; CODE XREF: updateSpecialPort+19j
+ SpecialPortInfo portInfo = gCurrentLevel.specialPortsInfo[i];
+ // For _reasons_ the port position has its bytes inverted (first high, then low), so we must reverse them
+ uint16_t portPosition = swap16(portInfo.position);
+ portPosition /= 2; // We must divide by 2 because the level format works with words
+
+ if (portPosition == position)
+ {
+ isPortInPosition = 1;
+ portIndex = i;
+ break;
+ }
+ }
+
+ if (isPortInPosition == 0)
+ {
+ return;
+ }
+
+ // loc_4F2CB: ; CODE XREF: updateSpecialPort+14j
+ SpecialPortInfo portInfo = gCurrentLevel.specialPortsInfo[portIndex];
+ gIsGravityEnabled = portInfo.gravity;
+ gAreZonksFrozen = portInfo.freezeZonks;
+ gAreEnemiesFrozen = portInfo.freezeEnemies;
+ // I still don't know where word_510AC is read :fearful:
+ // I tried with a breakpoint on memory read and it was never accessed :shrug:
+ // I can probably forget about it...
+ // word_510AC = word_510AC ^ gRandomGeneratorSeed;
+}
+
+void updateSnikSnakTiles(int16_t position) // movefun4 proc near ; DATA XREF: data:162Co
+{
+ // 01ED:868D
+ if (gAreEnemiesFrozen == 1)
+ {
+ return;
+ }
+
+ StatefulLevelTile *currentTile = &gCurrentLevelState[position];
+
+ if (currentTile->tile != LevelTileTypeSnikSnak)
+ {
+ return;
+ }
+
+ uint8_t frame = currentTile->state;
+
+ FrameBasedMovingFunction function = kSnikSnakMovingFunctions[frame];
+
+ if (function != NULL)
+ {
+ // 01ED:86AC
+ function(position, frame);
+ }
+}
+
+void updateSnikSnakTurnLeft(int16_t position, uint8_t frame) // sub_4F312 proc near ; DATA XREF: data:movingFunctions3o
+{
+ // 01ED:86AF
+ StatefulLevelTile *currentTile = &gCurrentLevelState[position];
+ StatefulLevelTile *aboveTile = &gCurrentLevelState[position - kLevelWidth];
+ StatefulLevelTile *leftTile = &gCurrentLevelState[position - 1];
+ StatefulLevelTile *belowTile = &gCurrentLevelState[position + kLevelWidth];
+ StatefulLevelTile *rightTile = &gCurrentLevelState[position + 1];
+
+ uint16_t value = gFrameCounter & 3;
+
+ if (value == 0)
+ {
+ // loc_4F320: ; CODE XREF: updateSnikSnakTurnLeft+6j
+ Point frameCoordinates = kSnikSnakAnimationFrameCoordinates[frame];
+ drawMovingFrame(frameCoordinates.x, frameCoordinates.y, position);
+ frame++;
+ frame &= 7;
+ currentTile->state = frame;
+ return;
+ }
+ else if (value != 3)
+ {
+ return;
+ }
+
+ // loc_4F362: ; CODE XREF: updateSnikSnakTurnLeft+Bj
+ uint8_t state = currentTile->state;
+ uint8_t nextMovingObject = 0;
+
+ if (state == 0)
+ {
+ // loc_4F37B: ; CODE XREF: updateSnikSnakTurnLeft+57j
+ if (aboveTile->state == 0 && aboveTile->tile == LevelTileTypeSpace)
+ {
+ // loc_4F38E: ; CODE XREF: updateSnikSnakTurnLeft+6Ej
+ currentTile->state = 0x1;
+ currentTile->tile = 0xBB;
+ aboveTile->state = 0x10;
+ aboveTile->tile = LevelTileTypeSnikSnak;
+ return;
+ }
+ nextMovingObject = aboveTile->state;
+ if (aboveTile->tile != LevelTileTypeMurphy)
+ {
+ return;
+ }
+ }
+ else if (state == 2)
+ {
+ // loc_4F39E: ; CODE XREF: updateSnikSnakTurnLeft+5Cj
+ if (leftTile->state == 0 && leftTile->tile == LevelTileTypeSpace)
+ {
+ // loc_4F3B1: ; CODE XREF: updateSnikSnakTurnLeft+91j
+ currentTile->state = 0x2;
+ currentTile->tile = 0xBB;
+ leftTile->state = 0x18;
+ leftTile->tile = LevelTileTypeSnikSnak;
+ return;
+ }
+ nextMovingObject = leftTile->state;
+ if (leftTile->tile != LevelTileTypeMurphy)
+ {
+ return;
+ }
+ }
+ else if (state == 4)
+ {
+ // loc_4F3C1: ; CODE XREF: updateSnikSnakTurnLeft+61j
+ if (belowTile->state == 0 && belowTile->tile == LevelTileTypeSpace)
+ {
+ // loc_4F3D7: ; CODE XREF: updateSnikSnakTurnLeft+B4j
+ currentTile->state = 0x3;
+ currentTile->tile = 0xBB;
+ belowTile->state = 0x20;
+ belowTile->tile = LevelTileTypeSnikSnak;
+ return;
+ }
+ nextMovingObject = belowTile->state;
+ if (belowTile->tile != LevelTileTypeMurphy)
+ {
+ return;
+ }
+ }
+ else if (state == 6)
+ {
+ // loc_4F3E7: ; CODE XREF: updateSnikSnakTurnLeft+66j
+ if (rightTile->state == 0 && rightTile->tile == LevelTileTypeSpace)
+ {
+ // loc_4F3FD: ; CODE XREF: updateSnikSnakTurnLeft+DAj
+ currentTile->state = 0x4;
+ currentTile->tile = 0xBB;
+ rightTile->state = 0x28;
+ rightTile->tile = LevelTileTypeSnikSnak;
+ return;
+ }
+ nextMovingObject = rightTile->state;
+ if (rightTile->tile != LevelTileTypeMurphy)
+ {
+ return;
+ }
+ }
+ else
+ {
+ return;
+ }
+
+ // loc_4F34A: ; CODE XREF: updateSnikSnakTurnLeft+79j
+ // ; updateSnikSnakTurnLeft+9Cj ...
+ if (nextMovingObject != 0x1B && nextMovingObject != 0x19 && nextMovingObject != 0x18 && nextMovingObject != 0x1A)
+ {
+ detonateBigExplosion(position);
+ }
+}
+
+void updateSnikSnakTurnRight(int16_t position, uint8_t frame) // sub_4F40D proc near ; DATA XREF: data:155Ao
+{
+ // 01ED:87AA
+ StatefulLevelTile *currentTile = &gCurrentLevelState[position];
+ StatefulLevelTile *aboveTile = &gCurrentLevelState[position - kLevelWidth];
+ StatefulLevelTile *rightTile = &gCurrentLevelState[position + 1];
+ StatefulLevelTile *leftTile = &gCurrentLevelState[position - 1];
+ StatefulLevelTile *belowTile = &gCurrentLevelState[position + kLevelWidth];
+
+ uint16_t value = gFrameCounter & 3;
+
+ if (value == 0)
+ {
+ // loc_4F41B: ; CODE XREF: updateSnikSnakTurnRight+6j
+ Point frameCoordinates = kSnikSnakAnimationFrameCoordinates[frame];
+ drawMovingFrame(frameCoordinates.x, frameCoordinates.y, position);
+ frame++;
+ frame &= 7;
+ frame |= 8;
+ currentTile->state = frame;
+ return;
+ }
+ else if (value != 3)
+ {
+ return;
+ }
+
+ // loc_4F45F: ; CODE XREF: updateSnikSnakTurnRight+Bj
+ uint8_t state = currentTile->state;
+ uint8_t nextMovingObject = 0;
+ if (state == 8)
+ {
+ // loc_4F478: ; CODE XREF: updateSnikSnakTurnRight+59j
+ if (aboveTile->state == 0 && aboveTile->tile == LevelTileTypeSpace)
+ {
+ // loc_4F48B: ; CODE XREF: updateSnikSnakTurnRight+70j
+ currentTile->state = 0x1;
+ currentTile->tile = 0xBB;
+ aboveTile->state = 0x10;
+ aboveTile->tile = LevelTileTypeSnikSnak;
+ return;
+ }
+ nextMovingObject = aboveTile->state;
+ if (aboveTile->tile != LevelTileTypeMurphy)
+ {
+ return;
+ }
+ }
+ else if (state == 0xA)
+ {
+ // loc_4F4E4: ; CODE XREF: updateSnikSnakTurnRight+5Ej
+ if (rightTile->state == 0 && rightTile->tile == LevelTileTypeSpace)
+ {
+ // loc_4F4FA: ; CODE XREF: updateSnikSnakTurnLeft+DAj
+ currentTile->state = 0x4;
+ currentTile->tile = 0xBB;
+ rightTile->state = 0x28;
+ rightTile->tile = LevelTileTypeSnikSnak;
+ return;
+ }
+ nextMovingObject = rightTile->state;
+ if (rightTile->tile != LevelTileTypeMurphy)
+ {
+ return;
+ }
+ }
+ else if (state == 0xC)
+ {
+ // loc_4F4BE: ; CODE XREF: updateSnikSnakTurnRight+63j
+ if (belowTile->state == 0 && belowTile->tile == LevelTileTypeSpace)
+ {
+ // loc_4F4D4: ; CODE XREF: updateSnikSnakTurnRight+B6j
+ currentTile->state = 0x3;
+ currentTile->tile = 0xBB;
+ belowTile->state = 0x20;
+ belowTile->tile = LevelTileTypeSnikSnak;
+ return;
+ }
+ nextMovingObject = belowTile->state;
+ if (belowTile->tile != LevelTileTypeMurphy)
+ {
+ return;
+ }
+ }
+ else if (state == 0xE)
+ {
+ // loc_4F49B: ; CODE XREF: updateSnikSnakTurnRight+68j
+ if (leftTile->state == 0 && leftTile->tile == LevelTileTypeSpace)
+ {
+ // loc_4F4AE: ; CODE XREF: updateSnikSnakTurnRight+93j
+ currentTile->state = 0x2;
+ currentTile->tile = 0xBB;
+ leftTile->state = 0x18;
+ leftTile->tile = LevelTileTypeSnikSnak;
+ return;
+ }
+ nextMovingObject = leftTile->state;
+ if (leftTile->tile != LevelTileTypeMurphy)
+ {
+ return;
+ }
+ }
+ else
+ {
+ return;
+ }
+
+ // loc_4F447: ; CODE XREF: updateSnikSnakTurnRight+7Bj
+ // ; updateSnikSnakTurnRight+9Ej ...
+ if (nextMovingObject != 0x1B && nextMovingObject != 0x19 && nextMovingObject != 0x18 && nextMovingObject != 0x1A)
+ {
+ detonateBigExplosion(position);
+ }
+}
+
+void updateSnikSnakMovementUp(int16_t position, uint8_t frame) // sub_4F50A proc near ; DATA XREF: data:156Ao
+{
+ // 01ED:88A7
+ StatefulLevelTile *currentTile = &gCurrentLevelState[position];
+ StatefulLevelTile *belowTile = &gCurrentLevelState[position + kLevelWidth];
+ StatefulLevelTile *leftTile = &gCurrentLevelState[position - 1];
+ StatefulLevelTile *aboveTile = &gCurrentLevelState[position - kLevelWidth];
+ StatefulLevelTile *rightTile = &gCurrentLevelState[position + 1];
+
+ uint16_t finalPosition = position + kLevelWidth;
+ Point frameCoordinates = kSnikSnakAnimationFrameCoordinates[frame];
+ // sub bx, 1Eh
+ frame -= 15; // 0x1E / 2
+ uint16_t offset = kFallAnimationGravityOffsets[frame];
+
+ uint8_t tileX = (finalPosition % kLevelWidth);
+ uint8_t tileY = (finalPosition / kLevelWidth);
+
+ uint16_t dstX = tileX * kTileSize - (offset % 122) * 2;
+ uint16_t dstY = tileY * kTileSize - (offset / 122) * 2;
+
+ drawMovingSpriteFrameInLevel(frameCoordinates.x, frameCoordinates.y,
+ kTileSize,
+ kTileSize + 2,
+ dstX, dstY);
+ if (frame == 7)
+ {
+ if (belowTile->tile != LevelTileTypeExplosion)
+ {
+ belowTile->state = 0;
+ belowTile->tile = LevelTileTypeSpace;
+ }
+ }
+ // loc_4F546: ; CODE XREF: updateSnikSnakMovementUp+2Dj
+ // ; updateSnikSnakMovementUp+34j
+ if (frame < 8)
+ {
+ frame += 0x10;
+ currentTile->state = frame;
+ return;
+ }
+
+ // loc_4F553: ; CODE XREF: updateSnikSnakMovementUp+3Fj
+ currentTile->state = 0;
+ currentTile->tile = LevelTileTypeSnikSnak;
+
+ if (leftTile->state == 0 && leftTile->tile == LevelTileTypeSpace)
+ {
+ currentTile->state = 1;
+ return;
+ }
+
+ // loc_4F566: ; CODE XREF: updateSnikSnakMovementUp+54j
+ if (leftTile->tile == LevelTileTypeMurphy)
+ {
+ currentTile->state = 1;
+ return;
+ }
+
+ // loc_4F573: ; CODE XREF: updateSnikSnakMovementUp+61j
+ if (aboveTile->state == 0 && aboveTile->tile == LevelTileTypeSpace)
+ {
+ currentTile->state = 1;
+ currentTile->tile = 0xBB;
+ aboveTile->state = 0x10;
+ aboveTile->tile = LevelTileTypeSnikSnak;
+ return;
+ }
+
+ // loc_4F58A: ; CODE XREF: updateSnikSnakMovementUp+6Ej
+ if (aboveTile->tile == LevelTileTypeMurphy)
+ {
+ detonateBigExplosion(position);
+ return;
+ }
+
+ // loc_4F595: ; CODE XREF: updateSnikSnakMovementUp+85j
+ if (rightTile->state == 0 && rightTile->tile == LevelTileTypeSpace)
+ {
+ // 01ED:8939
+ currentTile->state = 9;
+ return;
+ }
+
+ // loc_4F5A2: ; CODE XREF: updateSnikSnakMovementUp+90j
+ if (rightTile->tile == LevelTileTypeMurphy)
+ {
+ currentTile->state = 9;
+ return;
+ }
+
+ // loc_4F5AF: ; CODE XREF: updateSnikSnakMovementUp+9Dj
+ currentTile->state = 1;
+}
+
+void updateSnikSnakMovementLeft(int16_t position, uint8_t frame) // sub_4F5B5 proc near ; DATA XREF: data:157Ao
+{
+ // 01ED:8952
+ StatefulLevelTile *currentTile = &gCurrentLevelState[position];
+ StatefulLevelTile *belowTile = &gCurrentLevelState[position + kLevelWidth];
+ StatefulLevelTile *leftTile = &gCurrentLevelState[position - 1];
+ StatefulLevelTile *aboveTile = &gCurrentLevelState[position - kLevelWidth];
+ StatefulLevelTile *rightTile = &gCurrentLevelState[position + 1];
+
+ Point frameCoordinates = kSnikSnakAnimationFrameCoordinates[frame];
+
+ uint8_t tileX = (position % kLevelWidth);
+ uint8_t tileY = (position / kLevelWidth);
+
+ uint16_t dstX = tileX * kTileSize;
+ uint16_t dstY = tileY * kTileSize;
+
+ drawMovingSpriteFrameInLevel(frameCoordinates.x, frameCoordinates.y,
+ kTileSize * 2,
+ kTileSize,
+ dstX, dstY);
+
+ frame &= 7;
+ frame++;
+ if (frame == 7)
+ {
+ if (rightTile->tile != LevelTileTypeExplosion)
+ {
+ rightTile->state = 0;
+ rightTile->tile = LevelTileTypeSpace;
+ }
+ }
+ // loc_4F5EC: ; CODE XREF: updateSnikSnakMovementLeft+28j
+ // ; updateSnikSnakMovementLeft+2Fj
+ if (frame < 8)
+ {
+ frame += 0x18;
+ currentTile->state = frame;
+ return;
+ }
+
+ // loc_4F5F9: ; CODE XREF: updateSnikSnakMovementLeft+3Aj
+ currentTile->state = 0;
+ currentTile->tile = LevelTileTypeSnikSnak;
+
+ if (belowTile->state == 0 && belowTile->tile == LevelTileTypeSpace)
+ {
+ currentTile->state = 3;
+ return;
+ }
+
+ // loc_4F60C: ; CODE XREF: updateSnikSnakMovementLeft+4Fj
+ if (belowTile->tile == LevelTileTypeMurphy)
+ {
+ currentTile->state = 3;
+ return;
+ }
+
+ // loc_4F619: ; CODE XREF: updateSnikSnakMovementLeft+5Cj
+ if (leftTile->state == 0 && leftTile->tile == LevelTileTypeSpace)
+ {
+ currentTile->state = 2;
+ currentTile->tile = 0xBB;
+ leftTile->state = 0x18;
+ leftTile->tile = LevelTileTypeSnikSnak;
+ return;
+ }
+
+ // loc_4F630: ; CODE XREF: updateSnikSnakMovementLeft+69j
+ if (leftTile->tile == LevelTileTypeMurphy)
+ {
+ detonateBigExplosion(position);
+ return;
+ }
+
+ // loc_4F63B: ; CODE XREF: updateSnikSnakMovementLeft+80j
+ if (aboveTile->state == 0 && aboveTile->tile == LevelTileTypeSpace)
+ {
+ currentTile->state = 0xF;
+ return;
+ }
+
+ // loc_4F648: ; CODE XREF: updateSnikSnakMovementLeft+8Bj
+ if (aboveTile->tile == LevelTileTypeMurphy)
+ {
+ currentTile->state = 0xF;
+ return;
+ }
+
+ // loc_4F655: ; CODE XREF: updateSnikSnakMovementLeft+98j
+ currentTile->state = 3;
+}
+
+void updateSnikSnakMovementDown(int16_t position, uint8_t frame) // sub_4F65B proc near ; DATA XREF: data:158Ao
+{
+ // 01ED:89F8
+ StatefulLevelTile *currentTile = &gCurrentLevelState[position];
+ StatefulLevelTile *belowTile = &gCurrentLevelState[position + kLevelWidth];
+ StatefulLevelTile *leftTile = &gCurrentLevelState[position - 1];
+ StatefulLevelTile *aboveTile = &gCurrentLevelState[position - kLevelWidth];
+ StatefulLevelTile *rightTile = &gCurrentLevelState[position + 1];
+
+ uint16_t finalPosition = position - kLevelWidth;
+ Point frameCoordinates = kSnikSnakAnimationFrameCoordinates[frame];
+ // sub bx, 40h ; '@'
+ frame -= 0x20; // 0x40 / 2
+
+ uint16_t offset = kFallAnimationGravityOffsets[frame];
+
+ uint8_t tileX = (finalPosition % kLevelWidth);
+ uint8_t tileY = (finalPosition / kLevelWidth);
+
+ uint16_t dstX = tileX * kTileSize + (offset % 122) * 2;
+ uint16_t dstY = tileY * kTileSize + (offset / 122) * 2;
+
+ drawMovingSpriteFrameInLevel(frameCoordinates.x, frameCoordinates.y,
+ kTileSize,
+ kTileSize + 2,
+ dstX, dstY);
+
+ frame++;
+ if (frame == 7)
+ {
+ if (aboveTile->tile != LevelTileTypeExplosion)
+ {
+ aboveTile->state = 0;
+ aboveTile->tile = LevelTileTypeSpace;
+ }
+ }
+ // loc_4F699: ; CODE XREF: sub_4F66B+1Fj
+ // ; sub_4F66B+26j
+ if (frame < 8)
+ {
+ frame += 0x20;
+ currentTile->state = frame;
+ return;
+ }
+
+ // loc_4F6A6: ; CODE XREF: sub_4F66B+31j
+ currentTile->state = 0;
+ currentTile->tile = LevelTileTypeSnikSnak;
+
+ if (rightTile->state == 0 && rightTile->tile == LevelTileTypeSpace)
+ {
+ currentTile->state = 5;
+ return;
+ }
+
+ // loc_4F6B9: ; CODE XREF: sub_4F66B+46j
+ if (rightTile->tile == LevelTileTypeMurphy)
+ {
+ currentTile->state = 5;
+ return;
+ }
+
+ // loc_4F6C6: ; CODE XREF: sub_4F66B+53j
+ if (belowTile->state == 0 && belowTile->tile == LevelTileTypeSpace)
+ {
+ currentTile->state = 3;
+ currentTile->tile = 0xBB;
+ belowTile->state = 0x20;
+ belowTile->tile = LevelTileTypeSnikSnak;
+ return;
+ }
+
+ // loc_4F6DD: ; CODE XREF: sub_4F66B+60j
+ if (belowTile->tile == LevelTileTypeMurphy)
+ {
+ detonateBigExplosion(position);
+ return;
+ }
+
+ // loc_4F6E8: ; CODE XREF: sub_4F66B+77j
+ if (leftTile->state == 0 && leftTile->tile == LevelTileTypeSpace)
+ {
+ // 01ED:8A8C
+ currentTile->state = 0xD;
+ return;
+ }
+
+ // loc_4F6F5: ; CODE XREF: sub_4F66B+82j
+ if (leftTile->tile == LevelTileTypeMurphy)
+ {
+ currentTile->state = 0xD;
+ return;
+ }
+
+ // loc_4F702: ; CODE XREF: sub_4F66B+8Fj
+ currentTile->state = 5;
+}
+
+void updateSnikSnakMovementRight(int16_t position, uint8_t frame) // sub_4F708 proc near ; DATA XREF: data:159Ao
+{
+ // 01ED:8AA5
+ StatefulLevelTile *currentTile = &gCurrentLevelState[position];
+ StatefulLevelTile *belowTile = &gCurrentLevelState[position + kLevelWidth];
+ StatefulLevelTile *leftTile = &gCurrentLevelState[position - 1];
+ StatefulLevelTile *aboveTile = &gCurrentLevelState[position - kLevelWidth];
+ StatefulLevelTile *rightTile = &gCurrentLevelState[position + 1];
+
+ Point frameCoordinates = kSnikSnakAnimationFrameCoordinates[frame];
+
+ uint16_t finalPosition = position - 1;
+
+ uint8_t tileX = (finalPosition % kLevelWidth);
+ uint8_t tileY = (finalPosition / kLevelWidth);
+
+ uint16_t dstX = tileX * kTileSize;
+ uint16_t dstY = tileY * kTileSize;
+
+ drawMovingSpriteFrameInLevel(frameCoordinates.x, frameCoordinates.y,
+ kTileSize * 2,
+ kTileSize,
+ dstX, dstY);
+ frame &= 7;
+ frame++;
+ if (frame == 7)
+ {
+ if (leftTile->tile != LevelTileTypeExplosion)
+ {
+ leftTile->state = 0;
+ leftTile->tile = LevelTileTypeSpace;
+ }
+ }
+ // loc_4F740: ; CODE XREF: updateSnikSnakMovementRight+29j
+ // ; updateSnikSnakMovementRight+30j
+ if (frame < 8)
+ {
+ frame += 0x28;
+ currentTile->state = frame;
+ return;
+ }
+
+ // loc_4F74D: ; CODE XREF: updateSnikSnakMovementRight+3Bj
+ currentTile->state = 0;
+ currentTile->tile = LevelTileTypeSnikSnak;
+
+ if (aboveTile->state == 0 && aboveTile->tile == LevelTileTypeSpace)
+ {
+ currentTile->state = 7;
+ return;
+ }
+
+ // loc_4F760: ; CODE XREF: updateSnikSnakMovementRight+50j
+ if (aboveTile->tile == LevelTileTypeMurphy)
+ {
+ currentTile->state = 7;
+ return;
+ }
+
+ // loc_4F76D: ; CODE XREF: updateSnikSnakMovementRight+5Dj
+ if (rightTile->state == 0 && rightTile->tile == LevelTileTypeSpace)
+ {
+ currentTile->state = 4;
+ currentTile->tile = 0xBB;
+ rightTile->state = 0x28;
+ rightTile->tile = LevelTileTypeSnikSnak;
+ return;
+ }
+
+ // loc_4F784: ; CODE XREF: updateSnikSnakMovementRight+6Aj
+ if (rightTile->tile == LevelTileTypeMurphy)
+ {
+ detonateBigExplosion(position);
+ return;
+ }
+
+ // loc_4F78F: ; CODE XREF: updateSnikSnakMovementRight+81j
+ if (belowTile->state == 0 && belowTile->tile == LevelTileTypeSpace)
+ {
+ currentTile->state = 0xB;
+ return;
+ }
+
+ // loc_4F79C: ; CODE XREF: updateSnikSnakMovementRight+8Cj
+ if (belowTile->tile == LevelTileTypeMurphy)
+ {
+ currentTile->state = 0xB;
+ return;
+ }
+
+ // loc_4F7A9: ; CODE XREF: updateSnikSnakMovementRight+99j
+ currentTile->state = 7;
+}
+
+void updateElectronTiles(int16_t position) // movefun6 proc near ; DATA XREF: data:163Ao
+{
+ // 01ED:8B4C
+ if (gAreEnemiesFrozen == 1)
+ {
+ return;
+ }
+ StatefulLevelTile *currentTile = &gCurrentLevelState[position];
+
+ if (currentTile->tile != LevelTileTypeElectron)
+ {
+ return;
+ }
+
+ uint8_t frame = currentTile->state;
+
+ FrameBasedMovingFunction function = kElectronMovingFunctions[frame];
+
+ if (function != NULL)
+ {
+ // 01ED:8B6B
+ function(position, frame);
+ }
+}
+
+void updateElectronTurnLeft(int16_t position, uint8_t frame) // sub_4F7D1 proc near ; DATA XREF: data:movingFunctions2o
+{
+ // 01ED:8B6E
+ StatefulLevelTile *currentTile = &gCurrentLevelState[position];
+ StatefulLevelTile *aboveTile = &gCurrentLevelState[position - kLevelWidth];
+ StatefulLevelTile *leftTile = &gCurrentLevelState[position - 1];
+ StatefulLevelTile *belowTile = &gCurrentLevelState[position + kLevelWidth];
+ StatefulLevelTile *rightTile = &gCurrentLevelState[position + 1];
+
+ uint16_t value = gFrameCounter & 3;
+
+ if (value == 0)
+ {
+ // loc_4F7DF: ; CODE XREF: updateElectronTurnLeft+6j
+ Point frameCoordinates = kElectronAnimationFrameCoordinates[frame];
+ drawMovingFrame(frameCoordinates.x, frameCoordinates.y, position);
+ frame++;
+ frame &= 7;
+ currentTile->state = frame;
+ return;
+ }
+ else if (value != 3)
+ {
+ return;
+ }
+
+ // loc_4F80D: ; CODE XREF: updateElectronTurnLeft+Bj
+ uint8_t state = currentTile->state;
+ if (state == 0)
+ {
+ // loc_4F826: ; CODE XREF: updateElectronTurnLeft+43j
+ if (aboveTile->state == 0 && aboveTile->tile == LevelTileTypeSpace)
+ {
+ // loc_4F835: ; CODE XREF: updateElectronTurnLeft+5Aj
+ currentTile->state = 0x1;
+ currentTile->tile = 0xBB;
+ aboveTile->state = 0x10;
+ aboveTile->tile = LevelTileTypeElectron;
+ return;
+ }
+ else if (aboveTile->tile != LevelTileTypeMurphy)
+ {
+ return;
+ }
+ }
+ else if (state == 2)
+ {
+ // loc_4F845: ; CODE XREF: updateElectronTurnLeft+48j
+ if (leftTile->state == 0 && leftTile->tile == LevelTileTypeSpace)
+ {
+ // loc_4F854: ; CODE XREF: updateElectronTurnLeft+79j
+ currentTile->state = 0x2;
+ currentTile->tile = 0xBB;
+ leftTile->state = 0x18;
+ leftTile->tile = LevelTileTypeElectron;
+ return;
+ }
+ else if (leftTile->tile != LevelTileTypeMurphy)
+ {
+ return;
+ }
+ }
+ else if (state == 4)
+ {
+ // loc_4F864: ; CODE XREF: updateElectronTurnLeft+4Dj
+ if (belowTile->state == 0 && belowTile->tile == LevelTileTypeSpace)
+ {
+ // loc_4F873: ; CODE XREF: updateElectronTurnLeft+98j
+ currentTile->state = 0x3;
+ currentTile->tile = 0xBB;
+ belowTile->state = 0x20;
+ belowTile->tile = LevelTileTypeElectron;
+ return;
+ }
+ else if (belowTile->tile != LevelTileTypeMurphy)
+ {
+ return;
+ }
+ }
+ else if (state == 6)
+ {
+ // loc_4F883: ; CODE XREF: updateElectronTurnLeft+52j
+ if (rightTile->state == 0 && rightTile->tile == LevelTileTypeSpace)
+ {
+ // loc_4F895: ; CODE XREF: updateElectronTurnLeft+B7j
+ currentTile->state = 0x4;
+ currentTile->tile = 0xBB;
+ rightTile->state = 0x28;
+ rightTile->tile = LevelTileTypeElectron;
+ return;
+ }
+ else if (rightTile->tile != LevelTileTypeMurphy)
+ {
+ return;
+ }
+ }
+ else
+ {
+ return;
+ }
+
+ // loc_4F809: ; CODE XREF: updateElectronTurnLeft+61j
+ // ; updateElectronTurnLeft+80j ...
+ detonateBigExplosion(position);
+}
+
+void updateElectronTurnRight(int16_t position, uint8_t frame) // sub_4F8A5 proc near ; DATA XREF: data:15BAo
+{
+ // 01ED:8C42
+ StatefulLevelTile *currentTile = &gCurrentLevelState[position];
+ StatefulLevelTile *aboveTile = &gCurrentLevelState[position - kLevelWidth];
+ StatefulLevelTile *leftTile = &gCurrentLevelState[position - 1];
+ StatefulLevelTile *belowTile = &gCurrentLevelState[position + kLevelWidth];
+ StatefulLevelTile *rightTile = &gCurrentLevelState[position + 1];
+
+ uint16_t value = gFrameCounter & 3;
+
+ if (value == 0)
+ {
+ // loc_4F8B3: ; CODE XREF: updateElectronTurnRight+6j
+ Point frameCoordinates = kElectronAnimationFrameCoordinates[frame];
+ drawMovingFrame(frameCoordinates.x, frameCoordinates.y, position);
+ frame++;
+ frame &= 7;
+ frame |= 8;
+ currentTile->state = frame;
+ return;
+ }
+ else if (value != 3)
+ {
+ return;
+ }
+
+ // loc_4F8E3: ; CODE XREF: updateElectronTurnRight+Bj
+ uint8_t state = currentTile->state;
+ if (state == 8)
+ {
+ // loc_4F8FC: ; CODE XREF: updateElectronTurnRight+45j
+ if (aboveTile->state == 0 && aboveTile->tile == LevelTileTypeSpace)
+ {
+ // loc_4F90B: ; CODE XREF: updateElectronTurnRight+5Cj
+ currentTile->state = 0x1;
+ currentTile->tile = 0xBB;
+ aboveTile->state = 0x10;
+ aboveTile->tile = LevelTileTypeElectron;
+ return;
+ }
+ else if (aboveTile->tile != LevelTileTypeMurphy)
+ {
+ return;
+ }
+ }
+ else if (state == 0xA)
+ {
+ // loc_4F959: ; CODE XREF: updateElectronTurnRight+4Aj
+ if (rightTile->state == 0 && rightTile->tile == LevelTileTypeSpace)
+ {
+ // loc_4F96B: ; CODE XREF: updateElectronTurnRight+B9j
+ currentTile->state = 0x4;
+ currentTile->tile = 0xBB;
+ rightTile->state = 0x28;
+ rightTile->tile = LevelTileTypeElectron;
+ return;
+ }
+ else if (rightTile->tile != LevelTileTypeMurphy)
+ {
+ return;
+ }
+ }
+ else if (state == 0xC)
+ {
+ // loc_4F93A: ; CODE XREF: updateElectronTurnRight+4Fj
+ if (belowTile->state == 0 && belowTile->tile == LevelTileTypeSpace)
+ {
+ // loc_4F949: ; CODE XREF: updateElectronTurnRight+9Aj
+ currentTile->state = 0x3;
+ currentTile->tile = 0xBB;
+ belowTile->state = 0x20;
+ belowTile->tile = LevelTileTypeElectron;
+ return;
+ }
+ else if (belowTile->tile != LevelTileTypeMurphy)
+ {
+ return;
+ }
+ }
+ else if (state == 0xE)
+ {
+ // loc_4F91B: ; CODE XREF: updateElectronTurnRight+54j
+ if (leftTile->state == 0 && leftTile->tile == LevelTileTypeSpace)
+ {
+ // loc_4F92A: ; CODE XREF: updateElectronTurnRight+7Bj
+ currentTile->state = 0x2;
+ currentTile->tile = 0xBB;
+ leftTile->state = 0x18;
+ leftTile->tile = LevelTileTypeElectron;
+ return;
+ }
+ else if (leftTile->tile != LevelTileTypeMurphy)
+ {
+ return;
+ }
+ }
+ else
+ {
+ return;
+ }
+
+ // loc_4F8DF: ; CODE XREF: updateElectronTurnRight+63j
+ // ; updateElectronTurnRight+82j ...
+ detonateBigExplosion(position);
+}
+
+void updateElectronMovementUp(int16_t position, uint8_t frame) // sub_4F97B proc near ; DATA XREF: data:15CAo
+{
+ // 01ED:8D18
+ StatefulLevelTile *currentTile = &gCurrentLevelState[position];
+ StatefulLevelTile *belowTile = &gCurrentLevelState[position + kLevelWidth];
+ StatefulLevelTile *leftTile = &gCurrentLevelState[position - 1];
+ StatefulLevelTile *aboveTile = &gCurrentLevelState[position - kLevelWidth];
+ StatefulLevelTile *rightTile = &gCurrentLevelState[position + 1];
+
+ uint16_t finalPosition = position + kLevelWidth;
+ Point frameCoordinates = kElectronAnimationFrameCoordinates[frame];
+ // sub bx, 1Eh
+ frame -= 15; // 0x1E / 2
+ uint16_t offset = kFallAnimationGravityOffsets[frame];
+
+ uint8_t tileX = (finalPosition % kLevelWidth);
+ uint8_t tileY = (finalPosition / kLevelWidth);
+
+ uint16_t dstX = tileX * kTileSize - (offset % 122) * 2;
+ uint16_t dstY = tileY * kTileSize - (offset / 122) * 2;
+
+ drawMovingSpriteFrameInLevel(frameCoordinates.x, frameCoordinates.y,
+ kTileSize,
+ kTileSize + 2,
+ dstX, dstY);
+
+ if (frame == 7)
+ {
+ if (belowTile->tile != LevelTileTypeExplosion)
+ {
+ belowTile->state = 0;
+ belowTile->tile = LevelTileTypeSpace;
+ }
+ }
+
+ // loc_4F9B7: ; CODE XREF: updateElectronMovementUp+2Dj
+ // ; updateElectronMovementUp+34j
+ if (frame < 8)
+ {
+ frame += 0x10;
+ currentTile->state = frame;
+ return;
+ }
+
+ // loc_4F9C4: ; CODE XREF: updateElectronMovementUp+3Fj
+ currentTile->state = 0;
+ currentTile->tile = LevelTileTypeElectron;
+
+ if (leftTile->state == 0 && leftTile->tile == LevelTileTypeSpace)
+ {
+ currentTile->state = 1;
+ return;
+ }
+
+ // loc_4F9D7: ; CODE XREF: updateElectronMovementUp+54j
+ if (leftTile->tile == LevelTileTypeMurphy)
+ {
+ currentTile->state = 1;
+ return;
+ }
+
+ // loc_4F9E4: ; CODE XREF: updateElectronMovementUp+61j
+ if (aboveTile->state == 0 && aboveTile->tile == LevelTileTypeSpace)
+ {
+ currentTile->state = 1;
+ currentTile->tile = 0xBB;
+ aboveTile->state = 0x10;
+ aboveTile->tile = LevelTileTypeElectron;
+ return;
+ }
+
+ // loc_4F9FB: ; CODE XREF: updateElectronMovementUp+6Ej
+ if (aboveTile->tile == LevelTileTypeMurphy)
+ {
+ detonateBigExplosion(position);
+ return;
+ }
+
+ // loc_4FA06: ; CODE XREF: updateElectronMovementUp+85j
+ if (rightTile->state == 0 && rightTile->tile == LevelTileTypeSpace)
+ {
+ currentTile->state = 9;
+ return;
+ }
+
+ // loc_4FA13: ; CODE XREF: updateElectronMovementUp+90j
+ if (rightTile->tile == LevelTileTypeMurphy)
+ {
+ currentTile->state = 9;
+ return;
+ }
+
+ // loc_4FA20: ; CODE XREF: updateElectronMovementUp+9Dj
+ currentTile->state = 1;
+}
+
+void updateElectronMovementDown(int16_t position, uint8_t frame) // sub_4FA26 proc near ; DATA XREF: data:15DAo
+{
+ // 01ED:8DC3
+ StatefulLevelTile *currentTile = &gCurrentLevelState[position];
+ StatefulLevelTile *belowTile = &gCurrentLevelState[position + kLevelWidth];
+ StatefulLevelTile *leftTile = &gCurrentLevelState[position - 1];
+ StatefulLevelTile *aboveTile = &gCurrentLevelState[position - kLevelWidth];
+ StatefulLevelTile *rightTile = &gCurrentLevelState[position + 1];
+
+ Point frameCoordinates = kElectronAnimationFrameCoordinates[frame];
+
+ uint8_t tileX = (position % kLevelWidth);
+ uint8_t tileY = (position / kLevelWidth);
+
+ uint16_t dstX = tileX * kTileSize;
+ uint16_t dstY = tileY * kTileSize;
+
+ drawMovingSpriteFrameInLevel(frameCoordinates.x, frameCoordinates.y,
+ kTileSize * 2,
+ kTileSize,
+ dstX, dstY);
+
+ frame &= 7;
+ frame++;
+ if (frame == 7)
+ {
+ if (rightTile->tile != LevelTileTypeExplosion)
+ {
+ rightTile->state = 0;
+ rightTile->tile = LevelTileTypeSpace;
+ }
+ }
+
+ // loc_4FA5D: ; CODE XREF: updateElectronMovementDown+28j
+ // ; updateElectronMovementDown+2Fj
+ if (frame < 8)
+ {
+ frame += 0x18;
+ currentTile->state = frame;
+ return;
+ }
+
+ // loc_4FA6A: ; CODE XREF: updateElectronMovementDown+3Aj
+ currentTile->state = 0;
+ currentTile->tile = LevelTileTypeElectron;
+
+ if (belowTile->state == 0 && belowTile->tile == LevelTileTypeSpace)
+ {
+ currentTile->state = 3;
+ return;
+ }
+
+ // loc_4FA7D: ; CODE XREF: updateElectronMovementDown+4Fj
+ if (belowTile->tile == LevelTileTypeMurphy)
+ {
+ currentTile->state = 3;
+ return;
+ }
+
+ // loc_4FA8A: ; CODE XREF: updateElectronMovementDown+5Cj
+ if (leftTile->state == 0 && leftTile->tile == LevelTileTypeSpace)
+ {
+ currentTile->state = 2;
+ currentTile->tile = 0xBB;
+ leftTile->state = 0x18;
+ leftTile->tile = LevelTileTypeElectron;
+ return;
+ }
+
+ // loc_4FAA1: ; CODE XREF: updateElectronMovementDown+69j
+ if (leftTile->tile == LevelTileTypeMurphy)
+ {
+ detonateBigExplosion(position);
+ return;
+ }
+
+ // loc_4FAAC: ; CODE XREF: updateElectronMovementDown+80j
+ if (aboveTile->state == 0 && aboveTile->tile == LevelTileTypeSpace)
+ {
+ currentTile->state = 0xF;
+ return;
+ }
+
+ // loc_4FAB9: ; CODE XREF: updateElectronMovementDown+8Bj
+ if (aboveTile->tile == LevelTileTypeMurphy)
+ {
+ currentTile->state = 0xF;
+ return;
+ }
+
+ // loc_4FAC6: ; CODE XREF: updateElectronMovementDown+98j
+ currentTile->state = 3;
+}
+
+void updateElectronMovementRight(int16_t position, uint8_t frame) // sub_4FACC proc near ; DATA XREF: data:15EAo
+{
+ // 01ED:8E69
+ StatefulLevelTile *currentTile = &gCurrentLevelState[position];
+ StatefulLevelTile *belowTile = &gCurrentLevelState[position + kLevelWidth];
+ StatefulLevelTile *leftTile = &gCurrentLevelState[position - 1];
+ StatefulLevelTile *aboveTile = &gCurrentLevelState[position - kLevelWidth];
+ StatefulLevelTile *rightTile = &gCurrentLevelState[position + 1];
+
+ uint16_t finalPosition = position - kLevelWidth;
+ Point frameCoordinates = kElectronAnimationFrameCoordinates[frame];
+ // sub bx, 40h ; '@'
+ frame -= 0x20; // 0x40 / 2
+
+ uint16_t offset = kFallAnimationGravityOffsets[frame];
+
+ uint8_t tileX = (finalPosition % kLevelWidth);
+ uint8_t tileY = (finalPosition / kLevelWidth);
+
+ uint16_t dstX = tileX * kTileSize + (offset % 122) * 2;
+ uint16_t dstY = tileY * kTileSize + (offset / 122) * 2;
+
+ drawMovingSpriteFrameInLevel(frameCoordinates.x, frameCoordinates.y,
+ kTileSize,
+ kTileSize + 2,
+ dstX, dstY);
+
+ frame++;
+ if (frame == 7)
+ {
+ if (aboveTile->tile != LevelTileTypeExplosion)
+ {
+ aboveTile->state = 0;
+ aboveTile->tile = LevelTileTypeSpace;
+ }
+ }
+
+ // loc_4FB0A: ; CODE XREF: updateElectronMovementRight+2Fj
+ // ; updateElectronMovementRight+36j
+ if (frame < 8)
+ {
+ frame += 0x20;
+ currentTile->state = frame;
+ return;
+ }
+
+ // loc_4FB17: ; CODE XREF: updateElectronMovementRight+41j
+ currentTile->state = 0;
+ currentTile->tile = LevelTileTypeElectron;
+
+ if (rightTile->state == 0 && rightTile->tile == LevelTileTypeSpace)
+ {
+ currentTile->state = 5;
+ return;
+ }
+
+ // loc_4FB2A: ; CODE XREF: updateElectronMovementRight+56j
+ if (rightTile->tile == LevelTileTypeMurphy)
+ {
+ currentTile->state = 5;
+ return;
+ }
+
+ // loc_4FB37: ; CODE XREF: updateElectronMovementRight+63j
+ if (belowTile->state == 0 && belowTile->tile == LevelTileTypeSpace)
+ {
+ currentTile->state = 3;
+ currentTile->tile = 0xBB;
+ belowTile->state = 0x20;
+ belowTile->tile = LevelTileTypeElectron;
+ return;
+ }
+
+ // loc_4FB4E: ; CODE XREF: updateElectronMovementRight+70j
+ if (belowTile->tile == LevelTileTypeMurphy)
+ {
+ detonateBigExplosion(position);
+ return;
+ }
+
+ // loc_4FB59: ; CODE XREF: updateElectronMovementRight+87j
+ if (leftTile->state == 0 && leftTile->tile == LevelTileTypeSpace)
+ {
+ // 01ED:8A8C
+ currentTile->state = 0xD;
+ return;
+ }
+
+ // loc_4FB66: ; CODE XREF: updateElectronMovementRight+92j
+ if (leftTile->tile == LevelTileTypeMurphy)
+ {
+ currentTile->state = 0xD;
+ return;
+ }
+
+ // loc_4FB73: ; CODE XREF: updateElectronMovementRight+9Fj
+ currentTile->state = 5;
+}
+
+void updateElectronMovementLeft(int16_t position, uint8_t frame) // sub_4FB79 proc near ; DATA XREF: data:15FAo
+{
+ // 01ED:8F16
+ StatefulLevelTile *currentTile = &gCurrentLevelState[position];
+ StatefulLevelTile *belowTile = &gCurrentLevelState[position + kLevelWidth];
+ StatefulLevelTile *leftTile = &gCurrentLevelState[position - 1];
+ StatefulLevelTile *aboveTile = &gCurrentLevelState[position - kLevelWidth];
+ StatefulLevelTile *rightTile = &gCurrentLevelState[position + 1];
+
+ Point frameCoordinates = kElectronAnimationFrameCoordinates[frame];
+
+ uint16_t finalPosition = position - 1;
+
+ uint8_t tileX = (finalPosition % kLevelWidth);
+ uint8_t tileY = (finalPosition / kLevelWidth);
+
+ uint16_t dstX = tileX * kTileSize;
+ uint16_t dstY = tileY * kTileSize;
+
+ drawMovingSpriteFrameInLevel(frameCoordinates.x, frameCoordinates.y,
+ kTileSize * 2,
+ kTileSize,
+ dstX, dstY);
+ frame &= 7;
+ frame++;
+ if (frame == 7)
+ {
+ if (leftTile->tile != LevelTileTypeExplosion)
+ {
+ leftTile->state = 0;
+ leftTile->tile = LevelTileTypeSpace;
+ }
+ }
+
+ // loc_4FBB1: ; CODE XREF: updateElectronMovementLeft+29j
+ // ; updateElectronMovementLeft+30j
+ if (frame < 8)
+ {
+ frame += 0x28;
+ currentTile->state = frame;
+ return;
+ }
+
+ // loc_4FBBE: ; CODE XREF: updateElectronMovementLeft+3Bj
+ currentTile->state = 0;
+ currentTile->tile = LevelTileTypeElectron;
+
+ if (aboveTile->state == 0 && aboveTile->tile == LevelTileTypeSpace)
+ {
+ currentTile->state = 7;
+ return;
+ }
+
+ // loc_4FBD1: ; CODE XREF: updateElectronMovementLeft+50j
+ if (aboveTile->tile == LevelTileTypeMurphy)
+ {
+ currentTile->state = 7;
+ return;
+ }
+
+ // loc_4FBDE: ; CODE XREF: updateElectronMovementLeft+5Dj
+ if (rightTile->state == 0 && rightTile->tile == LevelTileTypeSpace)
+ {
+ currentTile->state = 4;
+ currentTile->tile = 0xBB;
+ rightTile->state = 0x28;
+ rightTile->tile = LevelTileTypeElectron;
+ return;
+ }
+
+ // loc_4FBF5: ; CODE XREF: updateElectronMovementLeft+6Aj
+ if (rightTile->tile == LevelTileTypeMurphy)
+ {
+ detonateBigExplosion(position);
+ return;
+ }
+
+ // loc_4FC00: ; CODE XREF: updateElectronMovementLeft+81j
+ if (belowTile->state == 0 && belowTile->tile == LevelTileTypeSpace)
+ {
+ currentTile->state = 0xB;
+ return;
+ }
+
+ // loc_4FC0D: ; CODE XREF: updateElectronMovementLeft+8Cj
+ if (belowTile->tile == LevelTileTypeMurphy)
+ {
+ currentTile->state = 0xB;
+ return;
+ }
+
+ // loc_4FC1A: ; CODE XREF: updateElectronMovementLeft+99j
+ currentTile->state = 7;
+}
+
+void drawGamePanelText() // sub_4FC20 proc near ; CODE XREF: stopRecordingDemo:loc_4944Fp
+ // ; drawGamePanel+22p ...
+{
+ if (gFastMode == FastModeTypeUltra)
+ {
+ return;
+ }
+
+ // 01ED:8FBD
+ if (gIsRecordingDemo != 0) // Recording demo?
+ {
+ // mov si, 87D1h // " DEMO "
+ drawTextWithChars8FontToGamePanel(72, 3, 8, " DEMO ");
+ // mov si, 87DAh // "000" -> this address is the ".SP" text
+ drawTextWithChars8FontToGamePanel(16, 14, 8, gCurrentDemoLevelName);
+ // mov si, 87F6h // "--- RECORDING DEMO0 ---"
+ drawTextWithChars8FontToGamePanel(64, 14, 8, gRecordingDemoMessage);
+ }
+ // loc_4FC6F: ; CODE XREF: drawGamePanelText+5j
+ else if (gIsPlayingDemo != 0) // Playing demo?
+ {
+ drawTextWithChars8FontToGamePanel(72, 3, 8, " DEMO ");
+ // mov si, 87DAh // "000" -> this address is the ".SP" text
+ drawTextWithChars8FontToGamePanel(16, 14, 8, gCurrentDemoLevelName);
+ // mov si, 87DEh // "----- DEMO LEVEL! -----"
+ drawTextWithChars8FontToGamePanel(64, 14, 8, &gCurrentDemoLevelName[4]);
+ }
+ else
+ {
+ // loc_4FCD6: ; CODE XREF: drawGamePanelText+B1j
+ drawTextWithChars8FontToGamePanel(72, 3, 6, gPlayerName);
+ char levelNumber[4] = "000";
+ memcpy(levelNumber, gCurrentLevelName, 3);
+ drawTextWithChars8FontToGamePanel(16, 14, 8, levelNumber);
+ drawTextWithChars8FontToGamePanel(64, 14, 8, &gCurrentLevelName[4]);
+ }
+
+ // loc_4FD1A: ; CODE XREF: drawGamePanelText+4Cj
+ // ; drawGamePanelText+A0j ...
+ drawNumberOfRemainingInfotrons();
+ drawGameTime();
+}
+
+void drawNumberOfRemainingInfotrons() // sub_4FD21 proc near ; CODE XREF: resetNumberOfInfotrons+13p
+ // ; update?:loc_4EC90p ...
+{
+ if (gFastMode == FastModeTypeUltra)
+ {
+ return;
+ }
+
+ if (gNumberOfRemainingInfotrons < 1)
+ {
+ gNumberOfRemainingInfotrons = 0; // WTF? Can this be negative? In theory not...
+ }
+
+ // loc_4FD2E: ; CODE XREF: drawNumberOfRemainingInfotrons+6j
+ char number[4] = "000";
+ convertNumberTo3DigitStringWithPadding0(gNumberOfRemainingInfotrons, number);
+
+ // loc_4FD46: ; CODE XREF: drawNumberOfRemainingInfotrons+20j
+ uint8_t color = (gNumberOfRemainingInfotrons == 0
+ ? 6
+ : 8);
+
+ // loc_4FD56: ; CODE XREF: drawNumberOfRemainingInfotrons+31j
+ drawTextWithChars8FontToGamePanel(272, 14, color, number);
+}
+
+void clearAdditionalInfoInGamePanelIfNeeded() // sub_4FD65 proc near ; CODE XREF: runLevel+E9p
+{
+ // loc_4FD6D: ; CODE XREF: clearAdditionalInfoInGamePanelIfNeeded+5j
+ if (gAdditionalInfoInGamePanelFrameCounter == 0)
+ {
+ return;
+ }
+
+ // loc_4FD75: ; CODE XREF: clearAdditionalInfoInGamePanelIfNeeded+Dj
+ gAdditionalInfoInGamePanelFrameCounter--;
+ if (gAdditionalInfoInGamePanelFrameCounter != 0)
+ {
+ return;
+ }
+
+ clearAdditionalInfoInGamePanel();
+}
+
+void decreaseRemainingRedDisksIfNeeded(int16_t position) // sub_4FDB5 proc near ; CODE XREF: update?+124Fp
+ // ; update?+1266p ...
+{
+ if (word_59B73 == 0 && gPlantedRedDiskCountdown != 0 && gPlantedRedDiskPosition == position)
+ {
+ return;
+ }
+
+ // loc_4FDCA: ; CODE XREF: decreaseRemainingRedDisksIfNeeded+5j decreaseRemainingRedDisksIfNeeded+Cj ...
+ gNumberOfRemainingRedDisks++;
+ drawNumberOfRemainingRedDisks();
+}
+
+void drawNumberOfRemainingRedDisks() // sub_4FDCE proc near ; CODE XREF: handleGameUserInput+7F4p
+ // ; update?+1369p
+{
+ // loc_4FDD6: ; CODE XREF: drawNumberOfRemainingRedDisks+5j
+ char numberString[4] = "000";
+ convertNumberTo3DigitStringWithPadding0(gNumberOfRemainingRedDisks, numberString);
+ // mov di, 6D2h
+ // mov si, 87CAh
+ uint8_t color = 0;
+ if (gNumberOfRemainingRedDisks != 0)
+ {
+ // loc_4FDF1: ; CODE XREF: drawNumberOfRemainingRedDisks+1Dj
+ color = 6;
+ }
+ else
+ {
+ color = 8;
+ }
+
+ // loc_4FDF3: ; CODE XREF: drawNumberOfRemainingRedDisks+21j
+ drawTextWithChars8FontToGamePanel(304, 14, color, &numberString[1]);
+ gAdditionalInfoInGamePanelFrameCounter = 0x46; // 70
+}
+
+void drawGameTime() // sub_4FDFD proc near ; CODE XREF: runLevel+29p
+ // ; runLevel:noFlashing2p ...
+{
+ if (gFastMode == FastModeTypeUltra)
+ {
+ return;
+ }
+
+ // Only the 2 last digits will be printed, hence why it will be used with &number[1] everywhere
+ char number[4] = "000";
+
+ if ((gLastDrawnMinutesAndSeconds & 0xFF) != gGameSeconds) // byte
+ {
+ gLastDrawnMinutesAndSeconds = (gLastDrawnMinutesAndSeconds & 0xFF00) + gGameSeconds; // byte
+ convertNumberTo3DigitStringWithPadding0(gGameSeconds, number);
+ // loc_4FE2C: ; CODE XREF: drawGameTime+2Aj
+ drawTextWithChars8FontToGamePanel(208, 3, 6, &number[1]); // seconds
+ }
+
+ // loc_4FE36: ; CODE XREF: drawGameTime+12j
+ if ((gLastDrawnMinutesAndSeconds >> 8) != gGameMinutes) // byte
+ {
+ gLastDrawnMinutesAndSeconds = (gGameMinutes << 8) + (gLastDrawnMinutesAndSeconds & 0x00FF); // byte
+ convertNumberTo3DigitStringWithPadding0(gGameMinutes, number);
+ drawTextWithChars8FontToGamePanel(184, 3, 6, &number[1]); // minutes
+ }
+
+ // loc_4FE5F: ; CODE XREF: drawGameTime+40j
+ if (gLastDrawnHours != gGameHours)
+ {
+ gLastDrawnHours = gGameHours;
+ convertNumberTo3DigitStringWithPadding0(gGameHours, number);
+ drawTextWithChars8FontToGamePanel(160, 3, 6, &number[1]); // hours
+ }
+}
+
+void drawGamePanel() // sub_501C0 proc near ; CODE XREF: start+338p handleGameUserInput+678p ...
+{
+ clearGamePanel();
+ drawGamePanelText();
+}
+
+void drawSpeedFixTitleAndVersion() // proc near ; CODE XREF: start+2E6p
+{
+ drawTextWithChars6FontWithOpaqueBackgroundIfPossible(102, 11, 1, "SUPAPLEX VERSION " VERSION_STRING);
+}
+
+void drawSpeedFixCredits() // showNewCredits proc near ; CODE XREF: start+2ECp
+{
+ drawTextWithChars6FontWithOpaqueBackgroundIfPossible(60, 168, 0xE, "VERSIONS 1-4 + 6.X BY HERMAN PERK");
+ drawTextWithChars6FontWithOpaqueBackgroundIfPossible(60, 176, 0xE, "VERSIONS 5.X BY ELMER PRODUCTIONS");
+ drawTextWithChars6FontWithOpaqueBackgroundIfPossible(60, 184, 0xE, " VERSION 7.X BY SERGIO PADRINO ");
+
+ videoLoop();
+
+ do
+ {
+ // loc_502F1: // ; CODE XREF: drawSpeedFixCredits+28j
+ int9handler(1);
+
+ if (gIsScrollLockPressed == 1)
+ {
+ gIsDebugModeEnabled = 1;
+ }
+ // loc_50301: // ; CODE XREF: drawSpeedFixCredits+1Ej
+ } while (isAnyKeyPressed() == 0 && isAnyGameControllerButtonPressed() == 0);
+}