From c9e9021b710b18ebc0cad98bb581765e710eb9e9 Mon Sep 17 00:00:00 2001 From: JJFlash Date: Tue, 1 Nov 2022 08:37:14 +0100 Subject: [PATCH] Uploading final version --- SuperChaseProj.charsetproject | Bin 8902 -> 8902 bytes inc_monster.bas | 202 +++++++++ inc_player.bas | 97 +++++ superchase.bas | 792 ++++++++++++++-------------------- superchase.prg | Bin 10840 -> 14310 bytes 5 files changed, 633 insertions(+), 458 deletions(-) create mode 100644 inc_monster.bas create mode 100644 inc_player.bas diff --git a/SuperChaseProj.charsetproject b/SuperChaseProj.charsetproject index 6e196decb91eeecd9fde1813c0bb34285ecdf891..faab7d606e2b6b2d73b30c34cb5ffe23605e91fa 100644 GIT binary patch delta 520 zcmX@+ddzi$8Q0_otSpmxxTPjf;HsFsfYWAj5RV*KtcFE$@<|}OpQ{omE<5=h)s5s7i9*jnS4-QmerAgmmzTS zZh334$VE9x^&$o?xJe-0I82)iR14GNUdX_Sa1YQWi3|)t%m+3HWFE*2n3@6x9=O_K z1`fDgAQxSf6J-pXd|Dw`6lN<-H;5lN*pa~$mOMq@GVPFS~ zf%r&ffb2Ji*$fj$4g+L4WPbo%Ihj#WRvab|qoI01=CXqQ5A=aFD@ZnQa=ubH0Eco_ AyZ`_I delta 520 zcmX@+ddzi$8Q0_qToscSa7Imjz{)bYhDCDneO6X5-Op7C6t|xIojYnWJGVTLEju}o z$7ZrKx7_CEJnllg5e$qBa6H*sURoQ(OJHCFV`MfKvB{3|qRc=wlMl+vvN|&GGDJ+? zEpH7LxhN;8Uc|r!HwmO0hiQ|6YGHcZ3mG^O?g6?ak%0k-`M~CY%mbMLQ&Yge16Nzj zzyY@l<^$TCo?L_io@h#G*mChTvo9Efj*FC1<6KC&Q}Tt0FmWZ AN&o-= diff --git a/inc_monster.bas b/inc_monster.bas new file mode 100644 index 0000000..1ecfddc --- /dev/null +++ b/inc_monster.bas @@ -0,0 +1,202 @@ +Const MASK_SUBGROUP = 8 '0000 1000 +Const MASK_WALK_LOWHALF = 24 '0001 1000 + +Dim SHARED bMonster_Col as BYTE +Dim SHARED bMonster_Row as BYTE +Dim bMonster_Direction as BYTE +Dim bMonster_Lag as BYTE +Dim bMonster_PreviousTile as BYTE +Dim SHARED bMonster_SpeedUpMode as BYTE +Dim bMonster_MarkingMode as BYTE +Dim bMonster_DelayFrame as BYTE +Dim bMonster_Distance_TurnONSpeedUp as BYTE +Dim bMonster_Distance_TurnOFFSpeedUp as BYTE + +Dim bManhattanDistance as BYTE + +declare sub monsterMovement() STATIC + +sub initMonster() SHARED STATIC + bMonsterIsOn = FALSE + if bSkillLevel < 8 then + bTreasuresToActivateMonster = 8 - bSkillLevel + end if + + bMonster_Col = 1 + bMonster_Row = 1 + bMonster_Direction = EAST + bMonster_Lag = 10 + bMonster_PreviousTile = SPACE + bMonster_SpeedUpMode = FALSE + bMonster_MarkingMode = FALSE + + if bSkillLevel < 16 then + bMonster_Distance_TurnONSpeedUp = 21 - bSkillLevel 'minimum: 6 + bMonster_Distance_TurnOFFSpeedUp = 12 - shr(bSkillLevel, 1) 'minimum: 5 + end if + + bMonster_DelayFrame = 9 '= Lag - 1 + + VOICE 2 OFF TONE 256 WAVE NOISE ADSR 0, 0, VOI2_S, VOI2_R 'monster sound + + '~ textat 33, 3, " " + '~ textat 33, 4, " " + '~ textat 33, 21, " " +end sub + +sub handleMonster() SHARED STATIC + Dim bMoveFrame as BYTE + + bManhattanDistance = myByteABS(bPlayer_Row - bMonster_Row) + myByteABS(bPlayer_Col - bMonster_Col) + '~ textat 33, 21, str$(bManhattanDistance) + " ", 11 'dark gray + if bManhattanDistance > bMonster_Distance_TurnONSpeedUp then + bMonster_SpeedUpMode = TRUE + else + if bManhattanDistance < bMonster_Distance_TurnOFFSpeedUp then + if bMonster_SpeedUpMode then + if bMonster_Lag > 1 then + bMonster_Lag = bMonster_Lag - 1 + textat 33, 20, 11 - bMonster_Lag, 2 'red + end if + bMonster_SpeedUpMode = FALSE + VOICE 2 TONE 256 ADSR 0, 0, VOI2_S, VOI2_R + end if + end if + end if + if bMonster_SpeedUpMode then + VOICE 2 TONE shl(cword(bManhattanDistance), 8) ADSR 0, 0, 2, VOI2_R + call monsterMovement() + exit sub + end if + + if bMonster_DelayFrame then + bMonster_DelayFrame = bMonster_DelayFrame - 1 + if bMonster_DelayFrame = 0 then bMoveFrame = bSkillLevel + else + call monsterMovement() + if bMoveFrame then bMoveFrame = bMoveFrame - 1 + if bMoveFrame = 0 then bMonster_DelayFrame = bMonster_Lag - 1 + end if + + '~ textat 33, 3, str$(bMonster_DelayFrame), 10 'light red + '~ textat 33, 4, str$(bMoveFrame) + " ", 13 'light green +end sub + +sub monsterMovement() STATIC + Const MINUS_ONE = 255 + Dim wPeekingLocation as WORD + Dim bMonster_PreviousColour as BYTE + + Dim bTravelingDirection as BYTE + + Dim bThisTileDistance as BYTE + Dim bClosestDistance as BYTE + Dim bThisTileRow as BYTE + Dim bThisTileCol as BYTE + Dim bClosestTileDirection as BYTE + Dim bPeekedDirection as BYTE FAST + + Dim bWalkableDirections(4) as BYTE '0...3 + Dim bWalkableDirections_Count as BYTE FAST + Dim bTrailDirections(4) as BYTE '0...3 + Dim bTrailDirections_Count as BYTE FAST + '------------------------------------------------ + wPeekingLocation = scrAddrCache(bMonster_Row) + bMonster_Col + + bTravelingDirection = bMonster_Direction + + bWalkableDirections_Count = MINUS_ONE + bTrailDirections_Count = MINUS_ONE + + bClosestDistance = 255 + + bMonster_Direction = (bMonster_Direction - 1) AND 3 'starting from the Monster's right (going clockwise) + For bPeekedDirection = 1 to 4 + bPeekedTileContent = peek(wPeekingLocation + iDirections(bMonster_Direction)) + + if (bPeekedTileContent AND MASK_ALL) = GROUP_CREATURES then bExitEvent = EVENT_PLAYER_CAUGHT : exit for 'Gotcha, Player!! + + if (bPeekedTileContent AND MASK_WALK_LOWHALF) = GROUP_WALKABLE then + if bPeekedTileContent = TRAIL then + bWalkableDirections_Count = bWalkableDirections_Count + 1 + bTrailDirections_Count = bTrailDirections_Count + 1 + bTrailDirections(bTrailDirections_Count) = bMonster_Direction + else + if bPeekedDirection < 4 then 'ignoring the opposite travelled direction! + bWalkableDirections_Count = bWalkableDirections_Count + 1 + bWalkableDirections(bWalkableDirections_Count) = bMonster_Direction + 'ALSO, find the closest tile to the player... + bThisTileRow = bMonster_Row : bThisTileCol = bMonster_Col + if (bMonster_Direction AND 1) then 'odd number, vertical direction + bThisTileRow = bMonster_Row + cbyte(SGN(iDirections(bMonster_Direction))) + else 'even number, horizontal direction + bThisTileCol = bMonster_Col + cbyte(iDirections(bMonster_Direction)) + end if + bThisTileDistance = myByteABS(bPlayer_Row - bThisTileRow) + myByteABS(bPlayer_Col - bThisTileCol) + if bThisTileDistance < bClosestDistance then + bClosestDistance = bThisTileDistance + bClosestTileDirection = bMonster_Direction + end if + end if + end if + end if + + bMonster_Direction = (bMonster_Direction + 1) AND 3 'now going counter-clockwise + next bPeekedDirection + + if bExitEvent = EVENT_NONE then + if bTrailDirections_Count <> MINUS_ONE then + bMonster_Direction = bTrailDirections(myRandom(bTrailDirections_Count, 3)) + else + if bWalkableDirections_Count = MINUS_ONE then + bMonster_Direction = (bTravelingDirection + 2) AND 3 'go to the opposite direction and start marking tiles + bMonster_MarkingMode = TRUE + else + if bWalkableDirections_Count = 2 then 'if there are *three* walkable tiles... + bWalkableDirections_Count = bWalkableDirections_Count + 1 + bWalkableDirections(bWalkableDirections_Count) = bClosestTileDirection + end if + bMonster_Direction = bWalkableDirections(myRandom(bWalkableDirections_Count, 3)) + end if + end if + if bWalkableDirections_Count AND (bWalkableDirections_Count <> MINUS_ONE) then bMonster_MarkingMode = FALSE 'if there are at least *two* walkable tiles... + end if + +'------------------------DRAW------------------------------------------- + if (bMonster_PreviousTile AND MASK_ALL) <> GROUP_TREASURE then + bMonster_PreviousTile = SPACE + end if + if bMonster_MarkingMode then + bMonster_PreviousTile = bMonster_PreviousTile OR MASK_SUBGROUP + end if + + charat bMonster_Col, bMonster_Row, bMonster_PreviousTile, bMonster_PreviousColour + + bMonster_PreviousTile = peek(wPeekingLocation + iDirections(bMonster_Direction)) + bMonster_PreviousColour = peek(VIC_COLOR_OFFSET + wPeekingLocation + iDirections(bMonster_Direction)) + + if (bMonster_Direction AND 1) then 'odd number, vertical direction + bMonster_Row = bMonster_Row + cbyte(SGN(iDirections(bMonster_Direction))) + else 'even number, horizontal direction + bMonster_Col = bMonster_Col + cbyte(iDirections(bMonster_Direction)) + end if + charat bMonster_Col, bMonster_Row, MONSTER, 2 'red + VOICE 2 ON + + +'debug code right after -DRAW- + '~ for bMonsterDebug as BYTE = 0 to 3 + '~ textat 33, 2 + bMonsterDebug, " " + '~ next bMonsterDebug + + '~ if bWalkableDirections_Count <> MINUS_ONE then + '~ for bMonsterDebug as BYTE = 0 to bWalkableDirections_Count + '~ if bWalkableDirections(bMonsterDebug) = bMonster_Direction then + '~ textat 33, 2 + bMonsterDebug, str$(bWalkableDirections(bMonsterDebug)), 1 'white + '~ else + '~ textat 33, 2 + bMonsterDebug, str$(bWalkableDirections(bMonsterDebug)), 11 'gray + '~ end if + '~ next bMonsterDebug + '~ end if +end sub + diff --git a/inc_player.bas b/inc_player.bas new file mode 100644 index 0000000..4abc96a --- /dev/null +++ b/inc_player.bas @@ -0,0 +1,97 @@ +SHARED Const PLAYER = 64 +SHARED Const PLAYER_ALT = 65 + Const PLAYER_LEFT = 66 + +Const MASK_TREASURE_GOLD = 247 '1111 0111 +Const MASK_TILE = 7 '0000 0111 + +SHARED Const EAST = 0 + Const NORTH = 1 + Const WEST = 2 + Const SOUTH = 3 + +Dim SHARED bPlayer_Col as BYTE +Dim SHARED bPlayer_Row as BYTE +Dim SHARED bPlayer_FacingCharacter as BYTE + +Dim bPlayerDirection as BYTE + +declare function playerMoved as BYTE () STATIC + +sub initPlayer() SHARED STATIC + bPlayer_Col = 1 + bPlayer_Row = 1 + bPlayer_FacingCharacter = PLAYER + + VOICE 1 TONE 256 PULSE 1536 WAVE PULSE ADSR 0, 0, VOI1_S, VOI1_R OFF 'player sound +end sub + +sub playerMovement() SHARED STATIC + bJoystick2 = peek( $DC00) XOR 127 + + if (bJoystick2 AND 1) then + bPlayerDirection = NORTH + if playerMoved() then exit sub + else + if (bJoystick2 AND 2) then + bPlayerDirection = SOUTH + if playerMoved() then exit sub + end if + end if + + if (bJoystick2 AND 4) then + bPlayerDirection = WEST + bPlayer_FacingCharacter = PLAYER_LEFT + if playerMoved() then exit sub + else + if (bJoystick2 AND 8) then + bPlayerDirection = EAST + bPlayer_FacingCharacter = PLAYER + if playerMoved() then exit sub + end if + end if +end sub + +function playerMoved as BYTE () STATIC + Dim wScoreTable(5) as WORD @loc_wScoreTable +loc_wScoreTable: + DATA AS WORD 10, 20, 30, 50, 500 + + bPeekedTileContent = peek(scrAddrCache(bPlayer_Row) + bPlayer_Col + iDirections(bPlayerDirection)) + + if (bPeekedTileContent AND MASK_ALL) = GROUP_CREATURES then 'Player bumped into Monster! + charat bPlayer_Col, bPlayer_Row, SPACE + bExitEvent = EVENT_PLAYER_CAUGHT + return TRUE + end if + + if (bPeekedTileContent AND MASK_WALKABLE) = GROUP_WALKABLE then + charat bPlayer_Col, bPlayer_Row, TRAIL, 11 'dark grey + VOICE 1 ON + + if (bPlayerDirection AND 1) then 'odd number, vertical direction + bPlayer_Row = bPlayer_Row + cbyte(SGN(iDirections(bPlayerDirection))) + else 'even number, horizontal direction + bPlayer_Col = bPlayer_Col + cbyte(iDirections(bPlayerDirection)) + end if + charat bPlayer_Col, bPlayer_Row, bPlayer_FacingCharacter, 13 'light green + + if (bPeekedTileContent AND MASK_ALL) = GROUP_TREASURE Then + bTreasuresCollected = bTreasuresCollected + 1 + if bTreasuresCollected = bTreasuresToActivateMonster then bMonsterIsOn = TRUE + if (bPeekedTileContent AND MASK_TREASURE_GOLD) = TREASURE_GOLD then 'both non-marked and marked gold! + bGoldNotCollected = FALSE : bSoundTimer_GoldTaken = 28 + else + bSoundTimer_TreasureTaken = 7 + end if + wScore = wScore + wScoreTable( (bPeekedTileContent AND MASK_TILE) ) + textat 33, 15, wScore, 10 'light red + if bTreasuresCollected = bTreasuresToOpenDoor then call openDoorAnimation() + else + if bPeekedTileContent = DOOR_OPEN then bExitEvent = EVENT_PLAYER_EXITED + end if + + return TRUE + end if + return FALSE +end function diff --git a/superchase.bas b/superchase.bas index 7ce87a2..2980eb2 100644 --- a/superchase.bas +++ b/superchase.bas @@ -1,5 +1,5 @@ '*********************************************************************** -'** SUPERCHASE ** +'** SUPERCHASE ** '** REMIX! ** '** ** '** 3 different versions of the same game into one program! ** @@ -8,15 +8,15 @@ '** - Atari version by someone @ Compute! Gazette ** '** - TI-99 4/A version by Cheryl Regena ** '** ** -'** Written in XC-BASIC 3.0.8 by @JJFlash@mastodon.social - Sep 2022 ** +'** Written in XC-BASIC 3.1.0 by @JJFlash@mastodon.social - Oct 2022 ** '** XC-BASIC created by Csaba Fekete! - https://xc-basic.net/ ** '** ** '** WORK IN PROGRESS!! ** '*********************************************************************** -'--------------INITIAL-SETUP-------------------------------------------- -Dim scrAddrCache(25) as WORD @loc_scrAddrCache ' 0 -> 24 +'------------------------INITIAL-SETUP---------------------------------- +Dim SHARED scrAddrCache(25) as WORD @loc_scrAddrCache ' 0 -> 24 loc_scrAddrCache: DATA AS WORD 1024, 1064, 1104, 1144, 1184, 1224, 1264, 1304, 1344, 1384 DATA AS WORD 1424, 1464, 1504, 1544, 1584, 1624, 1664, 1704, 1744, 1784 @@ -24,129 +24,130 @@ DATA AS WORD 1824, 1864, 1904, 1944, 1984 poke 53280, 0 : poke 53281, 0 -memcpy @LOC_charset_addr, $3800, 1024 -poke $D018, 31 'final character location: $3800 +'~ memcpy @LOC_charset_addr, $3800, 1000 +poke $D018, %00011111 'screen $0400, char location: $3800 poke 657, 128 'disable upper-lower case change -Dim wStack(128) as WORD @LOC_charset_addr 'recycling the charset data already copied to the final location! + +VOLUME 15 '----------------------------------------------------------------------- -'-------------CONSTANTS------------------------------------------------- -Const TRUE = 255 -Const FALSE = 0 -Const INIT = 255 -Const GO_ON = 0 +'-------------------------CONSTANTS------------------------------------- +SHARED Const TRUE = 255 +SHARED Const FALSE = 0 -Const SPACE = 88 -Const MARKED_SPACE = 89 -Const WALL = 96 -Const EX_WALL = 98 -Const DOOR_CLOSED = 97 -Const DOOR_CLOSED_REVERSED = 101 -Const DOOR_OPEN = 99 -Const DOOR_OPEN_REVERSED = 103 +SHARED Const SPACE = 81 + Const WALL = 96 + Const EX_WALL = 98 + Const DOOR_CLOSED = 97 + Const DOOR_CLOSED_REVERSED = 99 +SHARED Const DOOR_OPEN = 88 -Const PLAYER = 64 -Const PLAYER_LEFT = 66 -Const MONSTER = 68 -Const MONSTER_ALT = 69 -Const TRAIL = 80 -Const TREASURES = 112 -Const TREASURE_GOLD = 116 +SHARED Const MONSTER = 68 + Const MONSTER_ALT = 69 +SHARED Const TRAIL = 80 + Const TREASURES = 112 +SHARED Const TREASURE_GOLD = 116 -Const EAST = 0 -Const NORTH = 1 -Const WEST = 2 -Const SOUTH = 3 +SHARED Const MASK_WALKABLE = 16 '0001 0000 +SHARED Const MASK_ALL = 48 '0011 0000 -Const MASK_WALKABLE = 16 '0001 0000 -Const MASK_ALL = 48 '0011 0000 -Const MASK_SUBGROUP = 8 '0000 1000 -Const MASK_ALL_SUBGROUP = 56 '0011 1000 -Const MASK_TILE = 7 '0000 0111 +SHARED Const GROUP_CREATURES = 0 '0000 0000 +SHARED Const GROUP_WALKABLE = 16 '0001 0000 +SHARED Const GROUP_TREASURE = 48 '0011 0000 -Const GROUP_CREATURES = 0 '0000 0000 -Const GROUP_WALKABLE = 16 '0001 0000 -'~ Const GROUP_WALLS = 32 '0010 0000 -Const GROUP_TREASURE = 48 '0011 0000 -Const SUBGROUP_TREASURE = 56 '0011 1000 +SHARED Const EVENT_NONE = 0 +SHARED Const EVENT_PLAYER_CAUGHT = 1 +SHARED Const EVENT_PLAYER_EXITED = 2 -Const VIC_COLOR_OFFSET = $D400 +SHARED Const VIC_COLOR_OFFSET = $D400 + +SHARED Const VOI1_S = 3 +SHARED Const VOI1_R = 3 +SHARED Const VOI2_S = 8 +SHARED Const VOI2_R = 5 + Const VOI3_S = 7 + Const VOI3_R = 8 '----------------------------------------------------------------------- -'--------------GAME-STATE-&-GLOBAL-STUFF-------------------------------- -Dim bPeekedTileContent as BYTE +'-----------------GAME-STATE-&-GLOBAL-STUFF----------------------------- +Dim SHARED bPeekedTileContent as BYTE Dim bFrameCounter as BYTE Dim bAnimFrameCounter as BYTE -Dim iDirections(4) as INT @loc_iDirections +Dim SHARED iDirections(4) as INT @loc_iDirections loc_iDirections: DATA as INT 1, -40, -1, 40 'east, north, west, south -Dim bPlayer_Col as BYTE -Dim bPlayer_Row as BYTE -Dim bPlayer_FacingCharacter as BYTE - -Dim bMonsterIsOn as BYTE -Dim bMonster_Col as BYTE -Dim bMonster_Row as BYTE -Dim bMonster_Direction as BYTE -Dim bMonster_Lag as BYTE -Dim bMonster_PreviousTile as BYTE -Dim bMonster_SpeedUpMode as BYTE -Dim bMonster_DelayFrame as BYTE -Dim bMonster_Distance_TurnONSpeedUp as BYTE -Dim bMonster_Distance_TurnOFFSpeedUp as BYTE - -Dim bTreasuresToActivateMonster as BYTE - -Dim bManhattanDistance as BYTE - Dim wDoorAddress as WORD +Dim wDoorColorAddress as WORD Dim wGoldColorAddress as WORD -Dim bSkillLevel as BYTE : bSkillLevel = 1 -Dim wScore as WORD -Dim wLastExitScore as WORD : wLastExitScore = 0 -Dim bTreasures_Quantity as BYTE -Dim bTreasuresCollected as BYTE -Dim bTreasuresToOpenDoor as BYTE -Dim bGoldNotCollected as BYTE -Dim bPlayerExited as BYTE -Dim bPlayerCaught as BYTE +Dim SHARED bSkillLevel as BYTE : bSkillLevel = 1 +Dim SHARED wScore as WORD +Dim wLastExitScore as WORD : wLastExitScore = 0 +Dim bTreasures_Quantity as BYTE FAST +Dim SHARED bTreasuresCollected as BYTE +Dim SHARED bTreasuresToActivateMonster as BYTE : bTreasuresToActivateMonster = 1 +Dim SHARED bTreasuresToOpenDoor as BYTE + +Dim SHARED bMonsterIsOn as BYTE +Dim SHARED bGoldNotCollected as BYTE +Dim SHARED bExitEvent as BYTE + +Dim wNoteTable(4) as WORD @loc_wNoteTable +loc_wNoteTable: +DATA as WORD $4495, $5669, $6602, $892B 'C6, E6, G6, C7 + +Dim bSoundIndex as BYTE +Dim SHARED bSoundTimer_GoldTaken as BYTE +Dim SHARED bSoundTimer_TreasureTaken as BYTE +Dim wEventSound as WORD + +Dim N as BYTE FAST +Dim SHARED bJoystick2 as BYTE declare sub generateMaze () STATIC declare sub placeDoor() STATIC -declare sub playerMovement() STATIC -declare sub initMonster() STATIC -declare sub handleMonster() STATIC -declare sub monsterMovement() STATIC declare sub drawInfoBox() STATIC declare sub shakeScreen() STATIC declare sub mazeShiftAway() STATIC -declare sub openDoorAnimation() STATIC -declare sub goldFlashAnimation(bInitAnim as BYTE) STATIC +declare sub openDoorAnimation() SHARED STATIC +declare sub timedSounds() STATIC +declare sub colouredFrame() STATIC declare sub titleScreen() STATIC +declare sub playerAppears() STATIC + +declare sub placeCharset() STATIC '----------------------------------------------------------------------- '------------------HELPER FUNCTIONS------------------------------------- -function myRandom as BYTE (bMax as BYTE, bMask as BYTE) STATIC +function myRandom as BYTE (bMax as BYTE, bMask as BYTE) SHARED STATIC do myRandom = RNDB() AND bMask loop while myRandom > bMax end function -function myByteABS as BYTE (bNumber as BYTE) STATIC +function myByteABS as BYTE (bNumber as BYTE) SHARED STATIC if (bNumber AND 128) = 128 then bNumber = (NOT bNumber) + 1 return bNumber end function +'----------------------------------------------------------------------- + +'------------------------INCLUDES--------------------------------------- +include "inc_player.bas" +include "inc_monster.bas" +'----------------------------------------------------------------------- + + '------------------TITLE SCREEN----------------------------------------- sys $E544 FAST ' clear screen +call placeCharset() call titleScreen() -'------------------LEVEL-START------------------------------------------ +'-------------------GAME-START------------------------------------------ '~ Dim lRandomSeed as LONG : lRandomSeed = clong(248) ' <<<<<<<<<<<<<<<------------- RANDOM!!! @@ -158,9 +159,12 @@ textat 33, 8, "level", 5 'green textat 32, 13, "score", 3 'cyan textat 32, 18, "speed", 7 'yellow -Randomize (TI() * (clong(peek( $D012)) + clong(1))) +'~ Randomize (TI() * (clong(peek( $D012)) + clong(1))) +Randomize TI() +For N = 1 to 10 : bPeekedTileContent = RNDB() : Next N ' bPeekedTileContent used here as a dummy variable! -redraw: +'------------------LEVEL-START------------------------------------------ +restartLevel: For N as BYTE = 0 to 24 memset scrAddrCache(N), 31, WALL memset VIC_COLOR_OFFSET + scrAddrCache(N), 31, 6 'blue @@ -169,29 +173,27 @@ Next N '~ lRandomSeed = lRandomSeed + clong(1) '~ textat 33, 23, str$(lRandomSeed), 14 'light blue '~ Randomize (lRandomSeed) - -For N as BYTE = 1 to 10 : bPeekedTileContent = RNDB() : Next N ' bPeekedTileContent used here as a dummy variable! +'~ For N = 1 to 10 : bPeekedTileContent = RNDB() : Next N ' bPeekedTileContent used here as a dummy variable! call generateMaze() '~ textat 33, 24, str$(bTreasures_Quantity), 7 'yellow call placeDoor() +'********LEVEL INIT********** bFrameCounter = 0 -bPlayer_Col = 1 -bPlayer_Row = 1 -bPlayer_FacingCharacter = PLAYER - -call initMonster() +bSoundIndex = 255 +bSoundTimer_TreasureTaken = 0 +bSoundTimer_GoldTaken = 0 +VOICE 3 WAVE TRI ADSR 0, 0, VOI3_S, VOI3_R OFF wScore = wLastExitScore bTreasuresCollected = 0 -bPlayerExited = FALSE -bPlayerCaught = FALSE +bExitEvent = EVENT_NONE -textat 33, 10, str$(bSkillLevel), 1 'white +textat 33, 10, bSkillLevel, 1 'white memset 1657, 5, 32 'erase previous printed score -textat 33, 15, str$(wScore), 10 'light red +textat 33, 15, wScore, 10 'light red textat 33, 20, "1 ", 2 'red bTreasuresToOpenDoor = shr(bTreasures_Quantity, 1) @@ -202,75 +204,81 @@ else end if '~ textat 36, 24, str$(bTreasuresToOpenDoor), 12 'gray +call playerAppears() +call initPlayer() +call initMonster() '-------------------------MAIN LOOP!------------------------------------ do - on bFrameCounter goto actorMovement, endFrame, animation, endFrame + on bFrameCounter goto actorMovement, endFrame, mainAnimation, endFrame actorMovement: '-------PLAYER MOVEMENT----------------- call playerMovement() - if (bPlayerCaught OR bPlayerExited) then exit do + if bExitEvent then exit do '~ textat 33, 22, str$(bTreasuresCollected) + " ", 15 ' light gray '-------MONSTER MOVEMENT----------------- if bMonsterIsOn then call handleMonster() - if bPlayerCaught then exit do - else - if bTreasuresCollected = bTreasuresToActivateMonster then bMonsterIsOn = TRUE + if bExitEvent then exit do end if - - if NOT bGoldNotCollected then call goldFlashAnimation(GO_ON) + goto endFrame '------------------------------------------------------------------- -animation: - if bGoldNotCollected then - poke wGoldColorAddress, peek(wGoldColorAddress) XOR 6 'alternates white, yellow, white, yellow, ... - else - call goldFlashAnimation(GO_ON) - end if +mainAnimation: + if bGoldNotCollected then poke wGoldColorAddress, peek(wGoldColorAddress) XOR 6 'alternates white, yellow, white, yellow, ... + charat bPlayer_Col, bPlayer_Row, bPlayer_FacingCharacter XOR 1, 13 'light green + VOICE 1 OFF + if bMonsterIsOn then if bMonster_SpeedUpMode then call handleMonster() + if bExitEvent then exit do else charat bMonster_Col, bMonster_Row, MONSTER_ALT, 2 'red + VOICE 2 OFF end if end if - if bTreasuresCollected = bTreasuresToOpenDoor then call openDoorAnimation() '------------------------------------------------------------------- endFrame: - '~ if (peek ( $DC00) AND 16) = 0 then goto redraw + '~ if (peek( $DC00) AND 16) = 0 then goto restartGame + '~ if (peek( $DC00) AND 16) = 0 then poke wDoorAddress, DOOR_CLOSED : call openDoorAnimation() + '~ if (peek( $DC00) AND 16) = 0 then bAnimTimer_GoldTaken = 16 + '~ if (peek( $DC00) AND 16) = 0 then call colouredFrame() : wait $DC00, 16 + '~ if (peek( $DC00) AND 16) = 0 then call playerAppears() + + call timedSounds() wait $d011, 128, 128 : wait $d011, 128 : wait $d011, 128, 128 : wait $d011, 128 bFrameCounter = (bFrameCounter + 1) AND 3 loop -if bPlayerCaught then +if bExitEvent = EVENT_PLAYER_CAUGHT then call shakeScreen() call drawInfoBox() textat 8, 12, "a tasty morsel", 8 'orange textat 12, 13, "indeed!", 8 'orange - wScore = wLastExitScore + wait $DC00, 16, 16 'wait for fire to be pressed else call drawInfoBox() textat 8, 12, "congratulations!", 7 'yellow textat 6, 13, "onto the next level!", 7 'yellow wLastExitScore = wScore if bSkillLevel < 255 then bSkillLevel = bSkillLevel + 1 + call colouredFrame() + call mazeShiftAway() end if -wait $DC00, 16, 16 'wait for fire to be pressed -if bPlayerExited then call mazeShiftAway() -goto redraw +goto restartLevel '------------GAME-SUBROUTINES------------------------------------------- sub generateMaze () STATIC - Dim bRow as BYTE - Dim bCol as BYTE - Dim bRowEnd as BYTE - Dim bColEnd as BYTE + Dim bRow as BYTE FAST + Dim bCol as BYTE FAST + Dim bRowEnd as BYTE FAST + Dim bColEnd as BYTE FAST memset 1065, 5, EX_WALL poke 1105, EX_WALL @@ -278,14 +286,14 @@ sub generateMaze () STATIC poke 1185, EX_WALL poke 1225, EX_WALL - For N as BYTE = 1 to 65 + For N = 1 to 65 bCol = shl(myRandom(13, 15), 1) + 1 bRow = shl(myRandom(11, 15), 1) + 1 bColEnd = bCol + 6 : if bColEnd > 29 then bColEnd = 29 memset scrAddrCache(bRow) + bCol, bColEnd - bCol + 1, EX_WALL Next N - For N as BYTE = 1 to 60 + For N = 1 to 60 bCol = shl(myRandom(14, 15), 1) + 1 bRow = shl(myRandom(10, 15), 1) + 1 bRowEnd = bRow + 4 : if bRowEnd > 23 then bRowEnd = 23 @@ -294,10 +302,11 @@ sub generateMaze () STATIC Next K Next N - Dim wCalcScreenAddress as WORD - Dim bStackPointer as BYTE - Dim wFilledCells as WORD + Dim wCalcScreenAddress as WORD FAST + Dim bStackPointer as BYTE FAST + Dim wFilledCells as WORD FAST Dim wPatchScrAddress as WORD + Dim wStack(128) as WORD bStackPointer = 1 : wFilledCells = 0 wStack(0) = 1065 'player starting position: Row 1, Column 1 @@ -339,12 +348,12 @@ loc_bTreasures_Sequence: if wFilledCells > 5 then If myRandom(5, 7) = 5 then bTileType = TREASURES + bTreasures_Sequence(bTreasures_Index) + if bTileType = TREASURE_GOLD then wGoldColorAddress = VIC_COLOR_OFFSET + wCalcScreenAddress : bGoldNotCollected = TRUE bTileColor = bTreasures_Color(bTileType AND 7) if bTreasures_Index < 50 then bTreasures_Index = bTreasures_Index + 1 bTreasures_Quantity = bTreasures_Quantity + 1 end if end if - if bTileType = TREASURE_GOLD then wGoldColorAddress = VIC_COLOR_OFFSET + wCalcScreenAddress : bGoldNotCollected = TRUE poke wCalcScreenAddress, bTileType : poke VIC_COLOR_OFFSET + wCalcScreenAddress, bTileColor wFilledCells = wFilledCells + 1 @@ -379,271 +388,31 @@ loc_bTreasures_Sequence: end sub sub placeDoor() STATIC - wDoorAddress = $0586 'Col 30, Row 9 + Dim wStartingDoorAddress as WORD FAST + wStartingDoorAddress = scrAddrCache(myRandom(22, 31) + 1) + 29 'Col 29, Row random + Dim wMaxDoorAddress as WORD FAST : wMaxDoorAddress = $07DD 'Col 29, Row 24 + Dim wMinDoorAddress as WORD FAST : wMinDoorAddress = $0445 'Col 29, Row 1 + + wDoorAddress = wStartingDoorAddress do - For bRowIncrement AS BYTE = 0 to 22 'row 9 -> 23 + 1 -> 8 - if ((peek(wDoorAddress - cword(1)) AND MASK_WALKABLE) = GROUP_WALKABLE) then - poke wDoorAddress, DOOR_CLOSED - poke VIC_COLOR_OFFSET + wDoorAddress, 14 'light blue - exit sub - else - if bRowIncrement = 14 then - wDoorAddress = wDoorAddress - 880 'up 22 rows - else - wDoorAddress = wDoorAddress + cword(40) 'next row - end if - end if - Next bRowIncrement - - 'back to row 9? Try the previous column... - wDoorAddress = wDoorAddress - cword(1) + if (peek(wDoorAddress) AND MASK_WALKABLE) = GROUP_WALKABLE then + wDoorAddress = wDoorAddress + 1 + poke wDoorAddress, DOOR_CLOSED + wDoorColorAddress = VIC_COLOR_OFFSET + wDoorAddress + poke wDoorColorAddress, 14 'light blue + exit sub + end if + wDoorAddress = wDoorAddress + 40 + if wDoorAddress = wMaxDoorAddress then wDoorAddress = wMinDoorAddress + if wDoorAddress = wStartingDoorAddress then + wStartingDoorAddress = wStartingDoorAddress - 1 + wDoorAddress = wStartingDoorAddress + wMaxDoorAddress = wMaxDoorAddress - 1 + wMinDoorAddress = wMinDoorAddress - 1 + end if loop end sub -sub playerMovement() STATIC - Dim wScoreTable(5) as WORD @loc_wScoreTable -loc_wScoreTable: - DATA AS WORD 10, 20, 30, 50, 500 - - Dim bJoystick2 as BYTE - bJoystick2 = peek ( $DC00) XOR 127 - - if (bJoystick2 AND 1) then - bJoystick2 = NORTH - else - if (bJoystick2 AND 2) then - bJoystick2 = SOUTH - else - if (bJoystick2 AND 4) then - bJoystick2 = WEST - bPlayer_FacingCharacter = PLAYER_LEFT - else - if (bJoystick2 AND 8) then - bJoystick2 = EAST - bPlayer_FacingCharacter = PLAYER - else - exit sub - end if - end if - end if - end if - - bPeekedTileContent = peek(scrAddrCache(bPlayer_Row) + bPlayer_Col + iDirections(bJoystick2)) - - if (bPeekedTileContent AND MASK_ALL) = GROUP_CREATURES then 'Player bumped into Monster! - charat bPlayer_Col, bPlayer_Row, SPACE, 11 'dark grey - bPlayerCaught = TRUE : exit sub - end if - - if ((bPeekedTileContent AND MASK_WALKABLE) = GROUP_WALKABLE) OR (bPeekedTileContent = DOOR_OPEN) then - charat bPlayer_Col, bPlayer_Row, TRAIL, 11 'dark grey - - if (bJoystick2 AND 1) then 'odd number, vertical direction - bPlayer_Row = bPlayer_Row + cbyte(SGN(iDirections(bJoystick2))) - else 'even number, horizontal direction - bPlayer_Col = bPlayer_Col + cbyte(iDirections(bJoystick2)) - end if - charat bPlayer_Col, bPlayer_Row, bPlayer_FacingCharacter, 13 'light green - - if (bPeekedTileContent AND MASK_ALL) = GROUP_TREASURE Then - '~ poke 53280, (bPeekedTileContent AND MASK_TILE) + 1 - '~ wait $d011, 128, 128 : wait $d011, 128 : wait $d011, 128, 128 : wait $d011, 128 - '~ poke 53280, 0 - bTreasuresCollected = bTreasuresCollected + 1 - if (bPeekedTileContent AND 247) = TREASURE_GOLD then call goldFlashAnimation(INIT) 'both non-marked and marked! - wScore = wScore + wScoreTable ( (bPeekedTileContent AND MASK_TILE) ) - textat 33, 15, str$(wScore), 10 'light red - else - if bPeekedTileContent = DOOR_OPEN then bPlayerExited = TRUE - end if - - end if -end sub - -sub initMonster() STATIC - bMonsterIsOn = FALSE - if bSkillLevel < 8 then - bTreasuresToActivateMonster = 8 - bSkillLevel - else - bTreasuresToActivateMonster = 1 - end if - - bMonster_Col = 1 - bMonster_Row = 1 - bMonster_Direction = EAST - bMonster_Lag = 10 - bMonster_PreviousTile = SPACE - bMonster_SpeedUpMode = FALSE - if bSkillLevel < 14 then - bMonster_Distance_TurnONSpeedUp = 20 - bSkillLevel - bMonster_Distance_TurnOFFSpeedUp = 12 - shr(bSkillLevel, 1) - else - bMonster_Distance_TurnONSpeedUp = 7 - bMonster_Distance_TurnOFFSpeedUp = 5 - end if - - bMonster_DelayFrame = 9 '= Lag - 1 - - '~ textat 33, 3, " " - '~ textat 33, 4, " " - '~ textat 33, 21, " " -end sub - -sub handleMonster() STATIC - Dim bMoveFrame as BYTE - - bManhattanDistance = myByteABS(bPlayer_Row - bMonster_Row) + myByteABS(bPlayer_Col - bMonster_Col) - '~ textat 33, 21, str$(bManhattanDistance) + " ", 11 'dark gray - if bManhattanDistance > bMonster_Distance_TurnONSpeedUp then - bMonster_SpeedUpMode = TRUE - else - if bManhattanDistance < bMonster_Distance_TurnOFFSpeedUp then - if bMonster_SpeedUpMode then - if bMonster_Lag > 1 then - bMonster_Lag = bMonster_Lag - 1 - textat 33, 20, str$(11 - bMonster_Lag), 2 'red - end if - bMonster_SpeedUpMode = FALSE - end if - end if - end if - if bMonster_SpeedUpMode then call monsterMovement() : exit sub - - if bMonster_DelayFrame = 0 then - call monsterMovement() - if bMoveFrame > 0 then bMoveFrame = bMoveFrame - 1 - if bMoveFrame = 0 then bMonster_DelayFrame = bMonster_Lag - 1 - else - bMonster_DelayFrame = bMonster_DelayFrame - 1 - if bMonster_DelayFrame = 0 then bMoveFrame = bSkillLevel - end if - - '~ textat 33, 3, str$(bMonster_DelayFrame), 10 'light red - '~ textat 33, 4, str$(bMoveFrame) + " ", 13 'light green -end sub - -sub monsterMovement() STATIC - Const MINUS_ONE = 255 - Dim wPeekingLocation as WORD - Dim bMonster_PreviousColour as BYTE - - Dim bTravelingDirection as BYTE - - Dim bThisTileDistance as BYTE - Dim bClosestDistance as BYTE - Dim bThisTileRow as BYTE - Dim bThisTileCol as BYTE - Dim bClosestTileDirection as BYTE - Dim bPeekedDirection as BYTE - - Dim bWalkableDirections(4) as BYTE '0...3 - Dim bWalkableDirections_Count as BYTE - Dim bTrailDirections(4) as BYTE '0...3 - Dim bTrailDirections_Count as BYTE - '------------------------------------------------ - wPeekingLocation = scrAddrCache(bMonster_Row) + bMonster_Col - - bTravelingDirection = bMonster_Direction - - bWalkableDirections_Count = MINUS_ONE - bTrailDirections_Count = MINUS_ONE - - bClosestDistance = 255 - - bMonster_Direction = (bMonster_Direction - 1) AND 3 'starting from the Monster's right (going clockwise) - For bPeekedDirection = 1 to 4 - bPeekedTileContent = peek(wPeekingLocation + iDirections(bMonster_Direction)) - - if (bPeekedTileContent AND MASK_ALL) = GROUP_CREATURES then bPlayerCaught = TRUE : exit for 'Gotcha, Player!! - - if (bPeekedTileContent AND MASK_WALKABLE) = GROUP_WALKABLE then - '~ if (bPeekedTileContent AND MASK_ALL_SUBGROUP) = GROUP_TRAIL then - if bPeekedTileContent = TRAIL then - bWalkableDirections_Count = bWalkableDirections_Count + 1 - '~ bWalkableDirections(bWalkableDirections_Count) = bMonster_Direction - bTrailDirections_Count = bTrailDirections_Count + 1 - bTrailDirections(bTrailDirections_Count) = bMonster_Direction - else - if ((bPeekedTileContent AND MASK_ALL_SUBGROUP) <> SUBGROUP_TREASURE) AND (bPeekedTileContent <> MARKED_SPACE) and bPeekedDirection < 4 then 'ignoring the opposite direction travelled! - bWalkableDirections_Count = bWalkableDirections_Count + 1 - bWalkableDirections(bWalkableDirections_Count) = bMonster_Direction - 'ALSO, find the closest tile to the player... - bThisTileRow = bMonster_Row : bThisTileCol = bMonster_Col - if (bMonster_Direction AND 1) then 'odd number, vertical direction - bThisTileRow = bMonster_Row + cbyte(SGN(iDirections(bMonster_Direction))) - else 'even number, horizontal direction - bThisTileCol = bMonster_Col + cbyte(iDirections(bMonster_Direction)) - end if - bThisTileDistance = myByteABS(bPlayer_Row - bThisTileRow) + myByteABS(bPlayer_Col - bThisTileCol) - if bThisTileDistance < bClosestDistance then - bClosestDistance = bThisTileDistance - bClosestTileDirection = bMonster_Direction - end if - end if - end if - end if - - bMonster_Direction = (bMonster_Direction + 1) AND 3 'now going counter-clockwise - next bPeekedDirection - - if bPlayerCaught = FALSE then - if bTrailDirections_Count <> MINUS_ONE then - bMonster_Direction = bTrailDirections(myRandom(bTrailDirections_Count, 3)) - else - if bWalkableDirections_Count = MINUS_ONE then - bMonster_Direction = (bTravelingDirection + 2) AND 3 'opposite direction - else - bWalkableDirections_Count = bWalkableDirections_Count + 1 - bWalkableDirections(bWalkableDirections_Count) = bClosestTileDirection - bMonster_Direction = bWalkableDirections(myRandom(bWalkableDirections_Count, 3)) - end if - end if - end if - -'------------------------DRAW------------------------------------------- - - '~ for bMonsterDebug as BYTE = 0 to 3 - '~ textat 33, 2 + bMonsterDebug, " " - '~ next bMonsterDebug - - '~ if bWalkableDirections_Count <> MINUS_ONE then - '~ for bMonsterDebug as BYTE = 0 to bWalkableDirections_Count - '~ if bWalkableDirections(bMonsterDebug) = bMonster_Direction then - '~ textat 33, 2 + bMonsterDebug, str$(bWalkableDirections(bMonsterDebug)), 1 'white - '~ else - '~ textat 33, 2 + bMonsterDebug, str$(bWalkableDirections(bMonsterDebug)), 11 'gray - '~ end if - '~ next bMonsterDebug - '~ end if - - if (bMonster_PreviousTile AND MASK_ALL) = GROUP_TREASURE then - if bWalkableDirections_Count = MINUS_ONE then - bMonster_PreviousTile = bMonster_PreviousTile OR MASK_SUBGROUP - end if - else - if bWalkableDirections_Count = MINUS_ONE then - bMonster_PreviousTile = MARKED_SPACE - else - bMonster_PreviousTile = SPACE - end if - end if - - if (bMonster_PreviousColour AND 15) = 0 then bMonster_PreviousColour = 14 - charat bMonster_Col, bMonster_Row, bMonster_PreviousTile, bMonster_PreviousColour - - bMonster_PreviousTile = peek(wPeekingLocation + iDirections(bMonster_Direction)) - bMonster_PreviousColour = peek(VIC_COLOR_OFFSET + wPeekingLocation + iDirections(bMonster_Direction)) - - if (bMonster_Direction AND 1) = 1 then 'odd number, vertical direction - bMonster_Row = bMonster_Row + cbyte(SGN(iDirections(bMonster_Direction))) - else 'even number, horizontal direction - bMonster_Col = bMonster_Col + cbyte(iDirections(bMonster_Direction)) - end if - charat bMonster_Col, bMonster_Row, MONSTER, 2 'red - - if bWalkableDirections_Count = MINUS_ONE then bMonster_Direction = bTravelingDirection -end sub - sub drawInfoBox() STATIC '~ textat 4, 11, "{168}{171}{171}{171}{171}{171}{171}{171}{171}{171}{171}{171}{171}{171}{171}{171}{171}{171}{171}{171}{171}{171}{173}", 6 'blu '~ textat 4, 12, "{169} {174}", 6 'blu @@ -661,90 +430,173 @@ sub drawInfoBox() STATIC memset 1549, 21, 32 'col 5-25, row 13 poke 1570, 110 'col 26, row 13 - poke 1588, 106 'col 4, row 13 - memset 1589, 21, 108 'col 5-25, row 13 - poke 1610, 111 'col 26, row 13 + poke 1588, 106 'col 4, row 14 + memset 1589, 21, 108 'col 5-25, row 14 + poke 1610, 111 'col 26, row 14 - memset VIC_COLOR_OFFSET + 1468, 23, 6 'blu - memset VIC_COLOR_OFFSET + 1508, 23, 6 'blu - memset VIC_COLOR_OFFSET + 1548, 23, 6 'blu - memset VIC_COLOR_OFFSET + 1588, 23, 6 'blu + memset 55740, 23, 6 'blu + memset 55780, 23, 6 'blu + memset 55820, 23, 6 'blu + memset 55860, 23, 6 'blu end sub sub shakeScreen() STATIC - Dim bVariance as BYTE : bVariance = 7 + Dim bVariance as BYTE + + VOICE 1 OFF + VOICE 2 ON + VOICE 3 OFF WAVE SAW + + bVariance = 7 bAnimFrameCounter = 0 - + wEventSound = 1382 + do poke $D011, %10011000 OR ((3 + myRandom(bVariance, 7)) AND 7) poke $D016, %00001000 OR myRandom(bVariance, 7) + VOICE 3 TONE wEventSound ON wait $d011, 128, 128 : wait $d011, 128 bAnimFrameCounter = (bAnimFrameCounter + 1) AND 3 if bAnimFrameCounter = 0 then bVariance = bVariance - 1 + wEventSound = wEventSound - 40 loop until bVariance = 255 + + VOICE 2 OFF + VOICE 3 OFF end sub sub mazeShiftAway() STATIC - dim wLineAddress as WORD : dim wColorLineAddress as WORD - wait $d011, 128, 128 : wait $d011, 128 + dim wLineAddress as WORD FAST : dim wColorLineAddress as WORD FAST + Dim nShiftLine as BYTE FAST - for nShiftColumn as BYTE = 0 to 30 - for nShiftLine as BYTE = 0 to 24 + for nShiftLine = 0 to 24 + wLineAddress = scrAddrCache(nShiftLine) + wColorLineAddress = VIC_COLOR_OFFSET + wLineAddress + memcpy wLineAddress + 1, wLineAddress, 31 + memcpy wColorLineAddress + 1, wColorLineAddress, 31 + next nShiftLine + + for N = 0 to 15 '31 columns, 2 cells moved per frame, so 16 times + for nShiftLine = 0 to 24 wLineAddress = scrAddrCache(nShiftLine) wColorLineAddress = VIC_COLOR_OFFSET + wLineAddress - memcpy wLineAddress + 1, wLineAddress, 31 - memcpy wColorLineAddress + 1, wColorLineAddress, 31 + memcpy wLineAddress + 2, wLineAddress, 30 + memcpy wColorLineAddress + 2, wColorLineAddress, 30 next nShiftLine - wait $d011, 128, 128 : wait $d011, 128 - next nShiftColumn + wait $d011, 128 + next N end sub -sub openDoorAnimation() STATIC - For bAnimFrameCounter = 1 to 5 - poke wDoorAddress, DOOR_CLOSED_REVERSED - poke VIC_COLOR_OFFSET + wDoorAddress, 13 'light green - wait $d011, 128, 128 : wait $d011, 128 : wait $d011, 128, 128 : wait $d011, 128 - poke wDoorAddress, DOOR_CLOSED - poke VIC_COLOR_OFFSET + wDoorAddress, 14 'light blue - wait $d011, 128, 128 : wait $d011, 128 : wait $d011, 128, 128 : wait $d011, 128 - Next bAnimFrameCounter - For bAnimFrameCounter = 1 to 5 - poke wDoorAddress, DOOR_OPEN_REVERSED - poke VIC_COLOR_OFFSET + wDoorAddress, 7 'yellow - wait $d011, 128, 128 : wait $d011, 128 : wait $d011, 128, 128 : wait $d011, 128 - poke wDoorAddress, DOOR_OPEN - poke VIC_COLOR_OFFSET + wDoorAddress, 14 'light blue - wait $d011, 128, 128 : wait $d011, 128 : wait $d011, 128, 128 : wait $d011, 128 - Next bAnimFrameCounter +sub openDoorAnimation() SHARED STATIC + Dim bDoorColor as BYTE : bDoorColor = 14 'light blue + Dim bDoorChar as BYTE : bDoorChar = DOOR_CLOSED_REVERSED + wEventSound = $7F00 - bTreasuresToOpenDoor = 0 + VOICE 1 OFF + VOICE 2 OFF + VOICE 3 OFF WAVE PULSE ADSR 0, 0, 12, 1 + + For bAnimFrameCounter = 1 to 20 + bDoorChar = bDoorChar XOR 2 'alternates normal door, reversed door, normal door, ... (or viceversa) + poke wDoorAddress, bDoorChar + bDoorColor = bDoorColor XOR 9 'alternates yellow, light blue, yellow, ... + poke wDoorColorAddress, bDoorColor + VOICE 3 TONE wEventSound PULSE 63 ON + wEventSound = wEventSound + 780 + wait $d011, 128, 128 : wait $d011, 128 : wait $d011, 128, 128 : wait $d011, 128 + if bAnimFrameCounter = 10 then bDoorChar = DOOR_OPEN 'changes from reversed closed door to NORMAL open door + Next bAnimFrameCounter + VOICE 3 OFF TONE 0 WAVE TRI ADSR 0, 0, VOI3_S, VOI3_R + bSoundTimer_TreasureTaken = 0 end sub -sub goldFlashAnimation (bInitAnim as BYTE) STATIC - Dim bFlashTimer as BYTE +sub timedSounds() STATIC + dim bSoundPreviousIndex as BYTE - if bInitAnim then - bGoldNotCollected = FALSE - bFlashTimer = 8 - else - if bFlashTimer = 0 then exit sub + if bSoundTimer_GoldTaken then + VOICE 3 TONE shr(wNoteTable(bSoundIndex), 2) ON + bSoundIndex = (bSoundIndex + 1) AND 3 + + bSoundTimer_GoldTaken = bSoundTimer_GoldTaken - 1 + if bSoundTimer_GoldTaken = 0 then VOICE 3 OFF : bSoundTimer_TreasureTaken = 0 + exit sub end if - bFlashTimer = bFlashTimer - 1 - if (bFlashTimer AND 1) then - poke 53281, 8 'orange - else - poke 53281, 0 'black + if bSoundTimer_TreasureTaken then + bSoundPreviousIndex = bSoundIndex + do + bSoundIndex = myRandom(3, 3) + loop while bSoundIndex = bSoundPreviousIndex + + VOICE 3 TONE wNoteTable(bSoundIndex) ON + + bSoundTimer_TreasureTaken = bSoundTimer_TreasureTaken - 1 + if bSoundTimer_TreasureTaken = 0 then VOICE 3 OFF end if end sub +sub colouredFrame() STATIC + Dim iFrameColorAddress as INT FAST + Dim bStripIndex as BYTE FAST + Dim bStartingStripIndex as BYTE FAST + Dim bStripColors(8) as BYTE @loc_bStripColors +loc_bStripColors: + DATA as BYTE 1, 1, 7, 7, 3, 3, 2, 2 'white, yellow, cyan, red + + VOICE 1 OFF WAVE TRI TONE shr(wNoteTable(0), 4) ADSR 0, 11, 1, 10 ON + VOICE 2 OFF WAVE TRI TONE shr(wNoteTable(1), 1) ADSR 0, 11, 1, 10 ON + VOICE 3 OFF WAVE TRI TONE wNoteTable(2) ADSR 0, 11, 1, 10 ON + + bStartingStripIndex = 0 + + do + bStripIndex = bStartingStripIndex + + for iFrameColorAddress = $D800 to $D81E + poke iFrameColorAddress, bStripColors(bStripIndex) + bStripIndex = (bStripIndex + 1) AND 7 + next iFrameColorAddress + for iFrameColorAddress = $D846 to $DBDE STEP 40 + poke iFrameColorAddress, bStripColors(bStripIndex) + bStripIndex = (bStripIndex + 1) AND 7 + next iFrameColorAddress + poke wDoorColorAddress, 13 'light green (Player is here) + for iFrameColorAddress = $DBDD to $DBC0 STEP -1 + poke iFrameColorAddress, bStripColors(bStripIndex) + bStripIndex = (bStripIndex + 1) AND 7 + next iFrameColorAddress + for iFrameColorAddress = $DB98 to $D828 STEP -40 + poke iFrameColorAddress, bStripColors(bStripIndex) + bStripIndex = (bStripIndex + 1) AND 7 + next iFrameColorAddress + + for bAnimFrameCounter = 1 to 5 + wait $D011, 128, 128 : wait $D011, 128 + if peek( $DC00) = %01101111 then exit do 'if fire button is pressed, exit + next bAnimFrameCounter + + bStartingStripIndex = (bStartingStripIndex - 2) AND 7 + loop + + VOICE 1 TONE shr(wNoteTable(0), 3) OFF + VOICE 2 TONE shr(wNoteTable(0), 1) OFF + VOICE 3 TONE shl(wNoteTable(0), 1) OFF +end sub + sub titleScreen() STATIC Const PLAYER_LOCATION = 1354 Const MONSTER_LOCATION = 1434 - Dim bJoystick2 as BYTE + Dim sCredits(5) as STRING * 40 @loc_sCredits +loc_sCredits: +DATA as STRING * 40 " written by jjflash@itch.io " +DATA as STRING * 40 " original vic-20 game: anthony godshall " +DATA as STRING * 40 " atari graphics: compute! gazette " +DATA as STRING * 40 " ti99-4/a version: cheryl regena " +DATA as STRING * 40 " xc=basic 3: csaba fekete - xc-basic.net" + Dim bCreditIndex as BYTE 'treasure frame for title memset 1036, 16, TREASURE_GOLD @@ -765,43 +617,67 @@ sub titleScreen() STATIC textat 13, 10, "monster of dungeons!", 7 'yellow textat 12, 15, "choose skill level", 5 'green + textat 13, 19, "then press fire", 5 'green + charat 18, 17, 60, 10 'left arrow, light red charat 22, 17, 62, 10 'right arrow, light red poke PLAYER_LOCATION, PLAYER : poke 55626, 13 'light green poke MONSTER_LOCATION, MONSTER : poke 55706, 2 'red + bCreditIndex = 0 do - charat 20, 17, 48 + bSkillLevel, 1 'white + textat 0, 24, sCredits(bCreditIndex), 15 'light gray - poke 55994, 10 'color for left arrow, light red - poke 55998, 10 'color for right arrow, light red - - bJoystick2 = peek( $DC00) XOR 127 - - if (bJoystick2 AND 4) then 'color for left arrow, left - poke 55994, 1 'white - if bSkillLevel > 1 then bSkillLevel = bSkillLevel - 1 - else - if (bJoystick2 AND 8) then 'right - poke 55998, 1 'color for right arrow, white - if bSkillLevel < 9 then bSkillLevel = bSkillLevel + 1 + for bFrameCounter = 1 to 36 + charat 20, 17, 48 + bSkillLevel, 1 'white + + poke 55994, 10 'color for left arrow, light red + poke 55998, 10 'color for right arrow, light red + + bJoystick2 = peek( $DC00) XOR 127 + + if (bJoystick2 AND 4) then 'left + poke 55994, 1 'color for left arrow, white + if bSkillLevel > 1 then bSkillLevel = bSkillLevel - 1 else - if (bJoystick2 AND 16) then 'fire - exit do + if (bJoystick2 AND 8) then 'right + poke 55998, 1 'color for right arrow, white + if bSkillLevel < 9 then bSkillLevel = bSkillLevel + 1 + else + if (bJoystick2 AND 16) then 'fire + exit sub + end if end if end if - end if - - poke PLAYER_LOCATION, peek(PLAYER_LOCATION) XOR 1 - poke MONSTER_LOCATION, peek(MONSTER_LOCATION) XOR 1 - - wait $D011, 128, 128 : wait $D011, 128 : wait $D011, 128, 128 : wait $D011, 128 - wait $D011, 128, 128 : wait $D011, 128 : wait $D011, 128, 128 : wait $D011, 128 - wait $D011, 128, 128 : wait $D011, 128 : wait $D011, 128, 128 : wait $D011, 128 + + poke PLAYER_LOCATION, peek(PLAYER_LOCATION) XOR 1 + poke MONSTER_LOCATION, peek(MONSTER_LOCATION) XOR 1 + + for bAnimFrameCounter = 1 to 6 + wait $D011, 128, 128 : wait $D011, 128 + next bAnimFrameCounter + + next bFrameCounter + bCreditIndex = bCreditIndex + 1 : if bCreditIndex = 5 then bCreditIndex = 0 loop + +end sub + +sub playerAppears() STATIC + charat 1, 1, PLAYER_ALT, 0 'black + VOICE 1 WAVE PULSE TONE 10000 PULSE 2048 ADSR 0, 0, 2, 9 ON OFF + For bAnimFrameCounter = 1 to 21 + poke $D829, peek( $D829) XOR 13 'row 1, col 1, light green + wait $d011, 128, 128 : wait $d011, 128 + wait $d011, 128, 128 : wait $d011, 128 + next bAnimFrameCounter end sub '----------------------------------------------------------------------- -LOC_charset_addr: +sub placeCharset() STATIC + exit sub +origin $3800 incbin "charset_superchase.chr" +end sub + diff --git a/superchase.prg b/superchase.prg index 99e3e56efd8bc8be19c563e2706464b169b09b26..18978139848ec6d557b4730b640962527c1c4f6f 100644 GIT binary patch literal 14310 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�Q$XtdbIz5Me*4{Osi`5BG=c-WSrSp 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 I?LDFY0T{weT>t<8 literal 10840 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