├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── assets └── tiles.psd ├── doc └── Doc.md ├── escape.dsk ├── floppy ├── ESCAPE ├── LEVELS.ACTS ├── LEVELS.CONF └── STATES ├── scripts ├── add-to-disk.sh └── los.py └── src ├── actors ├── actors.asm ├── actors.inc └── reactions.asm ├── builder ├── actors.inc ├── builder.asm ├── builder.inc ├── maze.asm ├── maze.inc ├── rooms.asm ├── rooms.inc ├── unite.asm └── unite.inc ├── common.inc ├── debug.asm ├── display.asm ├── display.inc ├── display_map.asm ├── display_map.inc ├── escape.cfg ├── game_loop.asm ├── io ├── files.asm ├── files.inc ├── gr.asm ├── gr.inc ├── textio.asm ├── textio.inc └── title.asm ├── main.asm ├── math.asm ├── math.inc ├── memory.asm ├── memory.inc ├── monitor.inc ├── player.asm ├── random.asm ├── random.inc ├── tiles.asm ├── tiles.inc └── world ├── level.asm ├── level.inc ├── level_private.inc ├── world.asm └── world.inc /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | .DS_Store 3 | *.out 4 | *.a2 5 | *.o 6 | *.map 7 | *.bak 8 | *.s 9 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | APPLE2_CL := $(CC65_HOME)/bin/cl65 2 | APPLE2_SRC := src/main.asm src/math.asm src/memory.asm src/random.asm \ 3 | src/game_loop.asm src/display.asm src/tiles.asm src/player.asm \ 4 | src/world/world.asm src/world/level.asm \ 5 | src/builder/builder.asm src/builder/rooms.asm src/builder/maze.asm src/builder/unite.asm \ 6 | src/actors/reactions.asm src/actors/actors.asm \ 7 | src/debug.asm src/display_map.asm \ 8 | src/io/title.asm src/io/textio.asm src/io/gr.asm src/io/files.asm 9 | APPLE2_MAP := escape.map 10 | APPLE2_CFLAGS := -Oirs -v -t apple2 -vm --cpu 6502 11 | APPLE2_OUT := floppy/ESCAPE 12 | 13 | all: apple2 14 | 15 | apple2: $(APPLE2_SRC) 16 | $(APPLE2_CL) -m $(APPLE2_MAP) -o $(APPLE2_OUT) $? $(APPLE2_CFLAGS) -C src/escape.cfg 17 | 18 | clean: $(SRC) 19 | rm -f $(APPLE2_MAP) floppy/ESCAPE src/*.o src/builder/*.o src/io/*.o src/world/*.o src/actors/*.o gmon.out 20 | 21 | install: apple2 22 | ./scripts/add-to-disk.sh $(APPLE_COMMANDER) ./floppy escape.dsk 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## What is it? 2 | 3 | **Escape** (working title) is a homebrew *Rogue-Like** game developed for the Apple II computers. 4 | 5 | It is written in assembly and serves two purposes: 6 | 1. Be fun 7 | 2. Document the proccess of coding for the Apple II on [my blog](https://www.xtof.info): 8 | - [A tile engine for the Apple II](https://www.xtof.info/an-hires-tile-engine-for-the-apple-ii.html) 9 | - [Raycasting a Line of Sight](https://www.xtof.info/appleii-roguelike-line-of-sight.html) 10 | - [Random level generation on Apple II](https://www.xtof.info/random-level-generation-on-apple-ii.html) 11 | 12 | ## How to build 13 | 14 | ### Prerequisites 15 | 16 | * The build process relies on the assembler provided by the [CC65 compiler suite](https://github.com/cc65/cc65). 17 | * Set the environment variable **CC65_HOME** to the root folder of CC65 18 | * Builds are guaranteed to be successful using version 2.19 (commit 555282497c3ecf8). They should also work with any subsequent versions. 19 | * A makefile compatible with GNU Make is provided. 20 | * [AppleCommander](http://applecommander.sourceforge.net/) is used to produce a disk image that can be loaded in any emulator. Apple Commander requires a Java Runtime. 21 | * Export the variable **APPLE_COMMANDER** to the path of the jar file. 22 | 23 | ### How to build 24 | 25 | ```bash 26 | make 27 | ``` 28 | 29 | This will produce *bin/escape.a2* which is a binary executable for Apple's II PRODOS. 30 | 31 | ```bash 32 | make install 33 | ``` 34 | 35 | Will produce the executable binary and copy it along with all the required files into the floppy image *escape.dsk* 36 | 37 | ### How to play 38 | 39 | You can navigate the levels using the IJKL keys and display a map by pressing TAB. 40 | -------------------------------------------------------------------------------- /assets/tiles.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pixinn/rogue-like/261b7276b5839246001facf25e4f0323bd423d60/assets/tiles.psd -------------------------------------------------------------------------------- /doc/Doc.md: -------------------------------------------------------------------------------- 1 | # Documentation 2 | 3 | ## Actors 4 | 5 | Actors can be **static** or **dynamic**. 6 | Static actors are immutable while dynamic have a link to their status. Both react to the player's actions. For instance a floor or an opened door will let the player pass, while a table or a monster will block him. Dynamic actors can also have a behavior which can evolute by itself each turn, driven by a finite state machine. 7 | 8 | There can be 128 actors of 128 different kinds in a single level. Actor #0 is always the player. 9 | 10 | As there are immutable, many instances of a static actor can be represented by a single ID, while each instance of a dynamic actor require a unique ID. 11 | 12 | In memory, tiles contain the actor ID, which serves as an offset to render the tile, compute its behavior, and so on. 13 | 14 | ## Level generation 15 | 16 | Read [this page](https://www.xtof.info/random-level-generation-on-apple-ii.html) for a presentation of the general principle concerning the random level generation. 17 | 18 | The level configuration is given by the *level.conf* file. 19 | 20 | ### LEVELS.CONF 21 | 22 | Description of the levels for the random builder. 23 | All values are 8-bit integers. 24 | 25 | ```text 26 | [NB_LEVELS] 27 | # level conf * NB_LEVELS 28 | [NUMBER] 29 | [SIZE] 30 | # number of actors of each types * NB_ACTORS_MAX (128) 31 | [NB_ACTORS] 32 | . 33 | . 34 | . 35 | [NB_ACTORS] 36 | ``` 37 | 38 | ### STATES 39 | 40 | State of the levels 41 | 42 | ```text 43 | "LVLS" 44 | CURRENT_LEVEL 1 byte 45 | NB_LEVELS 1 byte 46 | # level state * NB_LEVELS 47 | "LVL" 48 | VISITED 1 byte 49 | PLAYER_TILE 1 byte 50 | LAYOUT 4096 bytes 51 | "ACTS" 52 | # actor state * NB_LEVELS 53 | "ACT" 54 | STATE sizeof(actor_t) 55 | ``` 56 | -------------------------------------------------------------------------------- /escape.dsk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pixinn/rogue-like/261b7276b5839246001facf25e4f0323bd423d60/escape.dsk -------------------------------------------------------------------------------- /floppy/ESCAPE: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pixinn/rogue-like/261b7276b5839246001facf25e4f0323bd423d60/floppy/ESCAPE -------------------------------------------------------------------------------- /floppy/LEVELS.ACTS: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pixinn/rogue-like/261b7276b5839246001facf25e4f0323bd423d60/floppy/LEVELS.ACTS -------------------------------------------------------------------------------- /floppy/LEVELS.CONF: -------------------------------------------------------------------------------- 1 |   -------------------------------------------------------------------------------- /floppy/STATES: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pixinn/rogue-like/261b7276b5839246001facf25e4f0323bd423d60/floppy/STATES -------------------------------------------------------------------------------- /scripts/add-to-disk.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Adds the required files to the provided disk.dsk 3 | # usage: add_to_disk PATH_TO_APPLECOMMANDER.jar PATH_TO_BINARY.a2 PATH_TO_DISK 4 | 5 | set -e 6 | 7 | if (( $# != 3 )); then 8 | echo "Bad number of arguments" 9 | echo "usage: add_to_disk.sh PATH_TO_APPLECOMMANDER.jar PATH_TO_FLOPPYDIR PATH_TO_DISK" 10 | exit 11 | fi 12 | 13 | # ### 14 | # NOTE: 15 | # The loader must have the same basename as the game loaded 16 | # ### 17 | echo " . removing previous instance of ESCAPE from the disk" 18 | java -jar ${1} -d ${3} ESCAPE 19 | java -jar ${1} -d ${3} ESCAPE.SYSTEM 20 | java -jar ${1} -d ${3} LEVELS.CONF 21 | java -jar ${1} -d ${3} LEVELS.ACTS 22 | java -jar ${1} -d ${3} STATES 23 | 24 | echo " .. adding files to the disk" 25 | java -jar ${1} -as ${3} ESCAPE BIN < ${2}/ESCAPE 26 | java -jar ${1} -p ${3} ESCAPE.SYSTEM SYS < ${CC65_HOME}/target/apple2/util/loader.system 27 | java -jar ${1} -p ${3} LEVELS.CONF BIN < ${2}/LEVELS.CONF 28 | java -jar ${1} -p ${3} LEVELS.ACTS BIN < ${2}/LEVELS.ACTS 29 | java -jar ${1} -p ${3} STATES BIN < ${2}/STATES 30 | 31 | echo "DONE." 32 | -------------------------------------------------------------------------------- /scripts/los.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2019 Christophe Meneboeuf 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see . 15 | # 16 | 17 | 18 | from bresenham import bresenham 19 | from PIL import Image, ImageDraw 20 | import os 21 | 22 | 23 | SIZE_GRID = 10 24 | SIZE_TILE = 64 25 | WIDTH_WORLD = 64 26 | x_player = int(SIZE_GRID/2) 27 | y_player = int(SIZE_GRID/2) 28 | 29 | Im = Image.new('RGB',(SIZE_GRID*SIZE_TILE,SIZE_GRID*SIZE_TILE),(255,255,255)) 30 | Draw = ImageDraw.Draw(Im) 31 | 32 | 33 | 34 | # fills a rectangle with the given color 35 | def fill_rect(x,y,color): 36 | Draw.rectangle([SIZE_TILE*x,SIZE_TILE*y, 37 | SIZE_TILE*x+SIZE_TILE-1,SIZE_TILE*y+SIZE_TILE-1], 38 | outline = (0,0,128), 39 | fill = color) 40 | 41 | # returns rays from (x0, y0) to (x1, y1) 42 | # output in rays 43 | def compute_rays(x0, y0, x1, y1, rays): 44 | ray = list(bresenham(x0, y0, x1, y1)) 45 | # Duplicate the ray so that x and y are not incremented at the same time 46 | duplicated = False 47 | ray_x = [] # x incremented before y 48 | ray_y = [] # y incremented before x 49 | x = ray[0][0] 50 | y = ray[0][1] 51 | for tile in ray[1:]: 52 | if tile[0] != x and tile[1] != y: 53 | duplicated = True 54 | ray_x.append((tile[0], y)) 55 | ray_x.append((tile[0], tile[1])) 56 | ray_y.append((x, tile[1])) 57 | ray_y.append((tile[0], tile[1])) 58 | else: 59 | ray_x.append((tile[0], tile[1])) 60 | ray_y.append((tile[0], tile[1])) 61 | x = tile[0] 62 | y = tile[1] 63 | 64 | rays.append(ray_x) 65 | 66 | if duplicated: 67 | rays.append(ray_y) 68 | 69 | return rays 70 | 71 | 72 | 73 | if __name__=="__main__": 74 | rays = [] 75 | y = 0 76 | for x in range(0,SIZE_GRID-1): 77 | rays = compute_rays(x_player,y_player,x,y, rays) 78 | x = SIZE_GRID-1 79 | for y in range(0,SIZE_GRID-1): 80 | rays = compute_rays(x_player,y_player,x,y, rays) 81 | y = SIZE_GRID-1 82 | for x in range(SIZE_GRID-1,0,-1): 83 | rays = compute_rays(x_player,y_player,x,y, rays) 84 | x = 0 85 | for y in range(SIZE_GRID-1,0,-1): 86 | rays = compute_rays(x_player,y_player,x,y, rays) 87 | 88 | 89 | # create the grid 90 | for x in range(0,SIZE_GRID): 91 | for y in range(0,SIZE_GRID): 92 | fill_rect(x,y,(255,255,255)) 93 | 94 | # fill the player 95 | fill_rect(x_player,y_player,(0,255,0)) 96 | 97 | # fill the rays 98 | nb_cells = 0 99 | rgb = 0 100 | for ray in rays: 101 | for tile in ray: 102 | fill_rect(tile[0], tile[1], (rgb,rgb,rgb)) 103 | nb_cells += 1 104 | rgb += int(200 / len(rays)) 105 | 106 | # print rays 107 | # [[len(ray), offset_view, offset_world]] 108 | # offset_world: offset in the world from the 1st tile viewed 109 | str_ray = "; Nb rays: {}\n".format(len(rays)) 110 | str_ray += "; A ray: length (nb_tiles), offset_from_view_in_world_low, offset_from_view_in_world_high, offset_view\nRays:\n" 111 | for ray in rays: 112 | str_ray += ".byte " + str(len(ray)) 113 | for tile in ray: 114 | offset_view = tile[0] + SIZE_GRID*tile[1] 115 | offset_world = (tile[0] + WIDTH_WORLD*tile[1]) 116 | offset_world_low = offset_world & 0xFF 117 | offset_world_high = (offset_world >> 8) & 0xFF 118 | str_ray += ", " + str(offset_world_low) + ", " + str(offset_world_high) + ", " + str(offset_view) 119 | str_ray += "\n" 120 | 121 | print(str_ray) 122 | Im.show() 123 | 124 | 125 | -------------------------------------------------------------------------------- /src/actors/actors.asm: -------------------------------------------------------------------------------- 1 | .include "../common.inc" 2 | .include "../random.inc" 3 | .include "../memory.inc" 4 | .include "../math.inc" 5 | .include "../world/world.inc" 6 | .include "actors.inc" 7 | 8 | .import Rooms 9 | .import World 10 | .import Compute_Maze_Addr 11 | 12 | .export ActorsInLevel 13 | .export ActorPositions 14 | .export ActorStates 15 | .export ActorTypes 16 | .export ActorTransparent 17 | 18 | 19 | .BSS 20 | 21 | ; struc actors_t { 22 | .align 256 23 | ActorsInLevel: 24 | ; aligned 256 25 | ActorPositions: .res 256 ; coords_t positions[NB_ACTORS_MAX]; 26 | ; aligned 256 27 | ActorStates: .res 256 ; actor_state_t* states[NB_ACTORS_MAX]; 28 | ; aligned 256 29 | ActorTypes: .res 128 ; uint8_t types[NB_ACTORS_MAX]; 30 | ; } 31 | ; NOTE: Modify SIZEOF_ACTORS_T if necessary!! 32 | 33 | .RODATA 34 | 35 | .align 256 36 | ActorTransparent: ; NB_ACTORS_MAX 37 | ; player 38 | .byte TRUE 39 | ; floors 40 | .byte TRUE, TRUE, TRUE, TRUE, TRUE, TRUE 41 | ; walls 42 | .byte FALSE, FALSE, FALSE, FALSE 43 | ; stair down 44 | .byte TRUE 45 | ; stair up 46 | .byte FALSE 47 | ; monsters 48 | .byte TRUE, TRUE, TRUE 49 | ; others 50 | .byte TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE 51 | .byte TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE 52 | .byte TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE 53 | .byte TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE 54 | .byte TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE 55 | .byte TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE 56 | .byte TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE 57 | 58 | .CODE 59 | 60 | ; code 61 | .export Place_Actors 62 | .export Actors_Init 63 | 64 | Actors_Init: 65 | 66 | ; positions 67 | ldx #(2*NB_ACTORS_MAX - 1) 68 | lda #UNDEF 69 | loop_actors_pos_init: 70 | sta ActorPositions, x 71 | dex 72 | bne loop_actors_pos_init 73 | 74 | ; types 75 | ldx #eACTORTYPES::LAST_STATIC 76 | loop_actors_types_init: 77 | txa 78 | sta ActorTypes, X 79 | dex 80 | bne loop_actors_types_init 81 | ldx #eACTORTYPES::LAST_MONSTER+1 82 | loop_actors_types_init_2: 83 | txa 84 | sta ActorTypes, X 85 | inx 86 | cpx #eACTORTYPES::NB_ACTORS 87 | bne loop_actors_types_init_2 88 | 89 | rts 90 | 91 | 92 | 93 | .define PTR_ROOM ZERO_2_1 ; 2 bytes 94 | .define PTR_TILE ZERO_2_1 ; 2 bytes 95 | ; the two following defines must be the same as in Build_Level 96 | .define ROOM_X ZERO_3 97 | .define ROOM_Y ZERO_2_4 98 | .define ROOM_W ZERO_2_5 99 | .define ROOM_H ZERO_2_6 100 | 101 | ; parameters: 102 | .define NB_ROOMS ZERO_9_9 103 | .define ACTOR_ID ZERO_9_1 104 | .define ACTOR_TYPE ZERO_9_2 105 | Place_Actors: 106 | 107 | loop_find_location: 108 | 109 | jsr Random8 110 | ldx NB_ROOMS 111 | jsr Modulus 112 | 113 | ; sizeof(room_t) == 4 114 | asl 115 | asl 116 | clc 117 | adc #Rooms 121 | sta PTR_ROOM+1 122 | 123 | ldy #0 124 | lda (PTR_ROOM), Y 125 | sta ROOM_H 126 | iny 127 | lda (PTR_ROOM), Y 128 | sta ROOM_W 129 | iny 130 | lda (PTR_ROOM), Y 131 | sta ROOM_X 132 | iny 133 | lda (PTR_ROOM), Y 134 | sta ROOM_Y 135 | 136 | ; x = room->x + rand() % (room->width - 2) + 1; 137 | sec 138 | lda ROOM_W 139 | sbc #2 140 | sta ROOM_W 141 | jsr Random8 142 | ldx ROOM_W 143 | jsr Modulus 144 | clc 145 | adc ROOM_X 146 | adc #1 147 | sta ROOM_X 148 | 149 | ; y = room->y + rand() % (room->height - 2) + 1; 150 | sec 151 | lda ROOM_H 152 | sbc #2 153 | sta ROOM_H 154 | jsr Random8 155 | ldx ROOM_H 156 | jsr Modulus 157 | clc 158 | adc ROOM_Y 159 | adc #1 160 | sta ROOM_Y 161 | tay 162 | 163 | ldx ROOM_X 164 | jsr Compute_Maze_Addr 165 | stx PTR_TILE 166 | sta PTR_TILE+1 167 | ldy #0 168 | lda (PTR_TILE), Y 169 | cmp #eACTORTYPES::FLOOR_2 170 | bne loop_find_location 171 | 172 | ; save position 173 | ldx ACTOR_ID 174 | lda ROOM_X 175 | sta ActorPositions, X 176 | lda ROOM_Y 177 | sta ActorPositions+1, X 178 | ; save type 179 | lda ACTOR_TYPE 180 | sta ActorTypes, X 181 | 182 | txa 183 | sta (PTR_TILE), Y 184 | 185 | rts -------------------------------------------------------------------------------- /src/actors/actors.inc: -------------------------------------------------------------------------------- 1 | 2 | 3 | .define NB_ACTORS_MAX 128 4 | .define SIZEOF_ACTORS_T 256+256+128 5 | 6 | .enum eACTORTYPES 7 | 8 | PLAYER = 0 ; PLAYER MUST ALWAYS BE 0 9 | ; STATIC ACTORS 10 | ; FLOOR 11 | FLOOR_1 = 1 12 | FLOOR_2 ; FLOOR BY DEFAULT 13 | FLOOR_3 14 | FLOOR_4 15 | FLOOR_5 16 | FLOOR_6 17 | LAST_FLOOR = FLOOR_6 18 | ; WALLS 19 | WALL_1 20 | WALL_2 21 | WALL_3 22 | WALL_4 23 | LAST_STATIC = WALL_4 24 | 25 | ; DYNAMIC ACTORS 26 | STAIR_DOWN ; 11 27 | FIRST_DYNAMIC = STAIR_DOWN 28 | STAIR_UP 29 | 30 | ; ITEMS 31 | MAP ; 13 32 | 33 | ; MONSTERS 34 | RAT ; 14 35 | FIRST_MONSTER = RAT 36 | TARENTULA 37 | SERPENT 38 | LAST_MONSTER = SERPENT ;16 39 | 40 | NB_ACTORS = 127 41 | UNKNOWN = NB_ACTORS 42 | 43 | .endenum 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /src/actors/reactions.asm: -------------------------------------------------------------------------------- 1 | ; Copyright (C) 2020 Christophe Meneboeuf 2 | ; 3 | ; This program is free software: you can redistribute it and/or modify 4 | ; it under the terms of the GNU General Public License as published by 5 | ; the Free Software Foundation, either version 3 of the License, or 6 | ; (at your option) any later version. 7 | ; 8 | ; This program is distributed in the hope that it will be useful, 9 | ; but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | ; GNU General Public License for more details. 12 | ; 13 | ; You should have received a copy of the GNU General Public License 14 | ; along with this program. If not, see . 15 | 16 | .include "actors.inc" 17 | .include "../world/level.inc" 18 | .include "../io/textio.inc" 19 | .include "../common.inc" 20 | 21 | .export Reactions_lsb 22 | .export Reactions_msb 23 | .export ReactionStairUp 24 | .export ReactionStairDown 25 | .export ReactionMap 26 | 27 | .import ActorPositions 28 | .import World_PickedObject 29 | 30 | .DATA 31 | 32 | STR_REACTION_WALL: ASCIIZ "YOU HIT A WALL" 33 | STR_REACTION_STAIR_UP: ASCIIZ "YOU GO UPSTAIRS TO TO THE NEXT LEVEL" 34 | STR_REACTION_STAIR_DOWN: ASCIIZ "YOU GO DOWNSTAIRS THE PREVIOUS LEVEL" 35 | STR_REACTION_MAP: ASCIIZ "YOU FOUND A MAP!" 36 | STR_REACTION_RAT: ASCIIZ "YOU ATTACK THE RAT" 37 | STR_REACTION_SPIDER: ASCIIZ "YOU ATTACK THE SPIDER" 38 | STR_REACTION_SERPENT: ASCIIZ "YOU ATTACK THE SERPENT" 39 | 40 | .align 256 41 | 42 | ; functions address seperated in LSB / MSB to use the same X/Y offset 43 | ; They must be in the very same order as the actor's types 44 | Reactions_lsb: 45 | ; player 46 | .byte 0 47 | ; floors 48 | .byte ReactionFloor, >ReactionFloor, >ReactionFloor, >ReactionFloor, >ReactionFloor, >ReactionFloor 94 | ; walls 95 | .byte >ReactionWall, >ReactionWall, >ReactionWall, >ReactionWall 96 | ; stairs 97 | .byte >ReactionStairDown, >ReactionStairUp 98 | ; items 99 | .byte >ReactionMap 100 | ; monsters 101 | .byte >ReactionRat, >ReactionSpider, >ReactionSerpent 102 | ; others 103 | .byte 0, 0, 0, 0, 0, 0, 0, 0 104 | .byte 0, 0, 0, 0, 0, 0, 0, 0 105 | .byte 0, 0, 0, 0, 0, 0, 0, 0 106 | .byte 0, 0, 0, 0, 0, 0, 0, 0 107 | .byte 0, 0, 0, 0, 0, 0, 0, 0 108 | .byte 0, 0, 0, 0, 0, 0, 0, 0 109 | .byte 0, 0, 0, 0, 0, 0, 0, 0 110 | .byte 0, 0, 0, 0, 0, 0, 0, 0 111 | .byte 0, 0, 0, 0, 0, 0, 0, 0 112 | .byte 0, 0, 0, 0, 0, 0, 0, 0 113 | .byte 0, 0, 0, 0, 0, 0, 0, 0 114 | .byte 0, 0, 0, 0, 0, 0, 0, 0 115 | .byte 0, 0, 0, 0, 0, 0, 0, 0 116 | .byte 0, 0, 0, 0, 0, 0, 0, 0 117 | .byte 0, 0, 0, 0, 0, 0, 0, 0 118 | .byte 0, 0, 0, 0, 0, 0, 0, 0 119 | .byte 0, 0, 0, 0, 0, 0, 0, 0 120 | .byte 0, 0, 0, 0, 0, 0, 0, 0 121 | .byte 0, 0, 0, 0, 0, 0, 0, 0 122 | .byte 0, 0, 0, 0, 0, 0, 0, 0 123 | .byte 0, 0, 0, 0, 0, 0, 0, 0 124 | .byte 0, 0, 0, 0, 0, 0, 0, 0 125 | .byte 0, 0, 0, 0, 0, 0, 0, 0 126 | .byte 0, 0, 0, 0, 0, 0, 0, 0 127 | .byte 0, 0, 0, 0, 0, 0, 0, 0 128 | .byte 0, 0, 0, 0, 0, 0, 0, 0 129 | .byte 0, 0, 0, 0, 0, 0, 0, 0 130 | .byte 0, 0, 0, 0, 0, 0, 0, 0 131 | .byte 0, 0, 0, 0, 0, 0, 0, 0 132 | .byte 0, 0, 0, 0, 0, 0, 0 133 | 134 | 135 | .CODE 136 | 137 | 138 | ReactionFloor: 139 | lda #TRUE 140 | rts 141 | 142 | ReactionWall: 143 | PRINT STR_REACTION_WALL 144 | lda #FALSE 145 | rts 146 | 147 | ReactionStairUp: 148 | PRINT STR_REACTION_STAIR_UP 149 | lda CurrentLevel 150 | sta NextLevel 151 | inc NextLevel 152 | lda #TRUE 153 | sta ExitLevel 154 | lda #FALSE 155 | rts 156 | 157 | ReactionStairDown: 158 | PRINT STR_REACTION_STAIR_DOWN 159 | lda CurrentLevel 160 | sta NextLevel 161 | dec NextLevel 162 | lda #TRUE 163 | sta ExitLevel 164 | lda #FALSE 165 | rts 166 | 167 | ; @param actor_id in X 168 | ReactionMap: 169 | 170 | ; index of &ActorPositions[actor_id] 171 | txa 172 | asl 173 | tax 174 | lda #UNDEF 175 | sta ActorPositions, X 176 | sta ActorPositions+1, X 177 | 178 | PRINT STR_REACTION_MAP 179 | 180 | lda #TRUE 181 | sta World_PickedObject 182 | 183 | rts 184 | 185 | ReactionRat: 186 | PRINT STR_REACTION_RAT 187 | lda #FALSE 188 | rts 189 | 190 | ReactionSpider: 191 | PRINT STR_REACTION_SPIDER 192 | lda #FALSE 193 | rts 194 | 195 | ReactionSerpent: 196 | PRINT STR_REACTION_SERPENT 197 | lda #FALSE 198 | rts 199 | -------------------------------------------------------------------------------- /src/builder/actors.inc: -------------------------------------------------------------------------------- 1 | .define NB_ACTORS_MAX 128 2 | 3 | 4 | 5 | .enum ACTORS 6 | 7 | PLAYER = 0 8 | ; FLOOR 9 | FLOOR_1 = 1 10 | FLOOR_2 ; FLOOR BY DEFAULT 11 | FLOOR_3 12 | FLOOR_4 13 | FLOOR_5 14 | FLOOR_6 15 | STAIR_DOWN 16 | ; OBJECT 17 | MAP 18 | ; WALLS 19 | STAIR_UP 20 | WALKABLE = STAIR_UP ; Player won't be allowed to go on anything > WALKABLE 21 | 22 | NOT_TRANSPARENT = STAIR_UP 23 | NOT_WALKABLE 24 | 25 | WALL_1 = NOT_WALKABLE 26 | WALL_2 27 | 28 | UNKNOWN 29 | 30 | NB_ACTORS 31 | 32 | .endenum -------------------------------------------------------------------------------- /src/builder/builder.asm: -------------------------------------------------------------------------------- 1 | 2 | ; Copyright (C) 2019 Christophe Meneboeuf 3 | ; 4 | ; This program is free software: you can redistribute it and/or modify 5 | ; it under the terms of the GNU General Public License as published by 6 | ; the Free Software Foundation, either version 3 of the License, or 7 | ; (at your option) any later version. 8 | ; 9 | ; This program is distributed in the hope that it will be useful, 10 | ; but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | ; GNU General Public License for more details. 13 | ; 14 | ; You should have received a copy of the GNU General Public License 15 | ; along with this program. If not, see . 16 | 17 | 18 | ; All the dungeon builder is based on this article: http://journal.stuffwithstuff.com/2014/12/21/rooms-and-mazes/ 19 | 20 | .include "rooms.inc" 21 | .include "maze.inc" 22 | .include "unite.inc" 23 | .include "../common.inc" 24 | .include "../actors/actors.inc" 25 | .include "../io/textio.inc" 26 | .include "../math.inc" 27 | .include "../monitor.inc" 28 | .include "../memory.inc" 29 | .include "../world/world.inc" 30 | .include "../world/level.inc" 31 | 32 | ; code 33 | .import Random8 34 | .import Grow_Maze ; to patch 35 | .import Compute_Maze_Addr 36 | .import Place_Actors 37 | ; data 38 | .import World 39 | .import Tile_player_standing_actor 40 | 41 | .export Init_Dimensions_Maze 42 | .export Build_Level 43 | 44 | .export Rooms 45 | .export WIDTH_MAZE 46 | .export HEIGHT_MAZE 47 | 48 | .BSS 49 | 50 | ; Describes a room to be built 51 | ; typedef struct { 52 | ; uint8_t height; 53 | ; uint8_t width; 54 | ; uint8_t x; 55 | ; uint8_t y; 56 | ; } room_t; 57 | .define SIZEOF_ROOM_T 4 58 | .align 256 59 | Rooms: .res SIZEOF_ROOM_T*MAX_NB_ROOMS ; MAX 1 page of data! 60 | 61 | WIDTH_MAZE: .res 1 62 | HEIGHT_MAZE: .res 1 63 | 64 | .DATA 65 | STR_SIZE_MAZE_1: ASCIIZ "PLEASE ENTER THE SIZE OF THE LEVEL." 66 | STR_SIZE_MAZE_2: ASCIIZ "A:TINY B:SMALL C:NORMAL D:BIG E:HUGE" 67 | STR_SIZE_MAZE_3: ASCIIZ "PLEASE ENTER A VALID CHOICE." 68 | 69 | STR_ROOMS: ASCIIZ "CARVING ROOMS..." 70 | STR_MAZE: ASCIIZ "GROWING THE MAZE..." 71 | STR_DOORS: ASCIIZ "OPENING DOORS..." 72 | STR_DEADENDS: ASCIIZ "FILLING DEAD ENDS..." 73 | STR_UNITE: ASCIIZ "UNITING THE ROOMS..." 74 | STR_ACTORS: ASCIIZ "PLACING ACTORS..." 75 | 76 | 77 | 78 | .CODE 79 | 80 | 81 | ; @brief Fills border walls 82 | ; @param type of the "wall" in A 83 | ; destroys ZERO_2_1, ZERO_2_2 84 | .define ADDR_WORLD ZERO_2_1 85 | .macro WORLD_NEXT_LINE 86 | clc 87 | lda ADDR_WORLD 88 | adc #WIDTH_WORLD 89 | sta ADDR_WORLD 90 | lda ADDR_WORLD+1 91 | adc #0 92 | sta ADDR_WORLD+1 93 | .endmacro 94 | ; DO NOT MESS WITH THIS FUNCTION: IT IS PATCHED!! 95 | .define PATCH_WIDTH_MAZE_1 0 96 | .define PATCH_HEIGHT_MAZE_2 0 97 | _build_fences: 98 | 99 | ldx #World 102 | stx ADDR_WORLD+1 103 | ldy #PATCH_WIDTH_MAZE_1 104 | 105 | loop_wall_top: 106 | sta (ADDR_WORLD), Y 107 | dey 108 | bne loop_wall_top 109 | sta (ADDR_WORLD), Y 110 | 111 | ldx #PATCH_HEIGHT_MAZE_2 112 | loop_wall_left_right: 113 | pha 114 | WORLD_NEXT_LINE 115 | pla 116 | ldy #PATCH_WIDTH_MAZE_1 117 | sta (ADDR_WORLD), Y 118 | ldy #0 119 | sta (ADDR_WORLD), Y 120 | dex 121 | bne loop_wall_left_right 122 | 123 | pha 124 | WORLD_NEXT_LINE 125 | pla 126 | ldy #PATCH_WIDTH_MAZE_1 127 | loop_wall_bottom: 128 | sta (ADDR_WORLD), Y 129 | dey 130 | bne loop_wall_bottom 131 | sta (ADDR_WORLD), Y 132 | 133 | rts 134 | .undefine ADDR_WORLD 135 | 136 | ; @brief Sets the Maze's dimentions 137 | ; @param width in X 138 | ; @param height in Y 139 | Init_Dimensions_Maze: 140 | 141 | stx WIDTH_MAZE 142 | ; patch WIDTH_MAZE usage NO MORE PATCH: comment to be removed 143 | dex 144 | stx _build_fences + $9 145 | stx _build_fences + $23 146 | stx _build_fences + $3D 147 | ; patch HEIGHT_MAZE usage NO MORE PATCH: comment to be removed 148 | sty HEIGHT_MAZE 149 | dey 150 | dey 151 | sty _build_fences + $12 152 | 153 | rts 154 | 155 | ; @brief Builds a whole level 156 | ; @param Uses NextLevel to get the conf 157 | ; @return player position in X and Y 158 | .define DST_WORLD World 159 | .define ADDR_TO_PATCH init_world_line + 3 160 | .define NB_ROOMS ZERO_9_9 ; use same location as Place_Actors 161 | Build_Level: 162 | 163 | lda #UNDEF 164 | sta Tile_player_standing_actor 165 | 166 | ; Filling World with eACTORTYPES::WALL_1 167 | ldy #HEIGHT_WORLD 168 | init_world: 169 | ldx #0 170 | init_world_line: 171 | lda #eACTORTYPES::WALL_1 172 | sta DST_WORLD, x 173 | inx 174 | cpx #WIDTH_WORLD 175 | bne init_world_line 176 | ; patching DST_WORLD 177 | lda ADDR_TO_PATCH 178 | clc 179 | adc #WIDTH_WORLD 180 | sta ADDR_TO_PATCH 181 | lda ADDR_TO_PATCH + 1 182 | adc #0 183 | sta ADDR_TO_PATCH + 1 184 | dey 185 | bne init_world 186 | ; patching back for a future execution 187 | lda #DST_WORLD 190 | sta ADDR_TO_PATCH+1 191 | 192 | PRINT STR_ROOMS 193 | 194 | lda #MAX_NB_ROOMS+1 195 | jsr Carve_Rooms 196 | sta NB_ROOMS 197 | 198 | lda #eACTORTYPES::FLOOR_1 199 | jsr _build_fences 200 | 201 | PRINT STR_MAZE 202 | jsr Grow_Maze 203 | 204 | lda #eACTORTYPES::WALL_1 205 | jsr _build_fences 206 | 207 | PRINT STR_DOORS 208 | .define PERCENT_7 #17 209 | ldx PERCENT_7 210 | lda NB_ROOMS 211 | jsr Connect_Rooms 212 | 213 | PRINT STR_DEADENDS 214 | jsr Remove_Dead_Ends 215 | 216 | PRINT STR_UNITE 217 | jsr Unite_Rooms 218 | 219 | ; the two following defines must be the same as in Place_Actors 220 | .define POS_X ZERO_3 ; ROOM_X in Place_Actors 221 | .define POS_Y ZERO_2_4 ; ROOM_Y in Place_Actors 222 | .define POS_STARDOWN_X ZERO_5_1 223 | .define POS_STARDOWN_Y ZERO_5_2 224 | .define ACTOR_NB ZERO_4_2 225 | .define ACTOR_ID ZERO_9_1 ; use same location as Place_Actors 226 | .define ACTOR_TYPE ZERO_9_2 ; use same location as Place_Actors 227 | .define CURR_ACTOR_OFFSET ZERO_4_3 228 | .define ADDR_LEVEL_CONF ZERO_5_3 ; 2 bytes 229 | .define ADDR_ACTOR ZERO_4_3 ; 2 bytes 230 | 231 | ; place actors 232 | PRINT STR_ACTORS 233 | ; offset to level conf 234 | 235 | lda NextLevel 236 | jsr level_get_config_offset 237 | pha 238 | clc 239 | txa 240 | adc #LevelConfigs 244 | sta ADDR_LEVEL_CONF+1 245 | lda #eACTORTYPES::LAST_STATIC + 1 ; 1st dynamic actor id 246 | sta ACTOR_ID 247 | sta ACTOR_TYPE 248 | tay 249 | iny 250 | iny ; offset to actors[ACTOR_ID] in level conf 251 | loop_actors: ; loop over actors from conf 252 | sty CURR_ACTOR_OFFSET 253 | lda (ADDR_LEVEL_CONF), Y 254 | sta ACTOR_NB 255 | loop_actor_id: 256 | beq end_loop_actor_id 257 | 258 | jsr Place_Actors 259 | 260 | ; save stair down position 261 | lda ACTOR_TYPE 262 | cmp #eACTORTYPES::STAIR_DOWN 263 | bne not_stair_down 264 | lda POS_X 265 | sta POS_STARDOWN_X 266 | lda POS_Y 267 | sta POS_STARDOWN_Y 268 | not_stair_down: 269 | 270 | inc ACTOR_ID 271 | dec ACTOR_NB ; next 272 | lda ACTOR_NB 273 | jmp loop_actor_id 274 | end_loop_actor_id: 275 | 276 | ldy CURR_ACTOR_OFFSET 277 | iny 278 | inc ACTOR_TYPE 279 | lda ACTOR_TYPE 280 | cmp #(NB_ACTORS_MAX-1) 281 | bne loop_actors 282 | 283 | ; Set the 1st position of the player in the level 284 | ; offset to level state 285 | lda NextLevel 286 | cmp #0 287 | bne not_first_level 288 | ; Special case: first level 289 | ; TODO avoid non empty floor... 290 | ldx Rooms+2 ; Rooms[0].x 291 | ldy Rooms+3 ; Rooms[0].y 292 | rts 293 | not_first_level: 294 | ldx POS_STARDOWN_X 295 | ldy POS_STARDOWN_Y 296 | jsr Compute_Maze_Addr 297 | ; addr offseted by - witdh_maze to access all tiles with offset 298 | sta ADDR_ACTOR+1 299 | txa 300 | sec 301 | sbc #WIDTH_WORLD 302 | sta ADDR_ACTOR 303 | lda ADDR_ACTOR+1 304 | sbc #0 305 | sta ADDR_ACTOR+1 306 | ldx POS_STARDOWN_X 307 | ; NOTE: There is at least one solution, the tile is not surrounded! 308 | ; if (World[pos_stair_down.y][pos_stair_down.x - 1] == FLOOR_2) 309 | ldy #(WIDTH_WORLD - 1) 310 | lda (ADDR_ACTOR), Y 311 | cmp #eACTORTYPES::FLOOR_2 312 | bne not_x_minus 313 | ldy POS_STARDOWN_Y 314 | dex 315 | rts 316 | not_x_minus: 317 | ; if (World[pos_stair_down.y - 1][pos_stair_down.x] == FLOOR_2) 318 | ldy #0 319 | lda (ADDR_ACTOR), Y 320 | cmp #eACTORTYPES::FLOOR_2 321 | bne not_y_minus 322 | ldy POS_STARDOWN_Y 323 | dey 324 | rts 325 | not_y_minus: 326 | ; if (World[pos_stair_down.y + 1][pos_stair_down.x] == FLOOR_2) 327 | ldy #(WIDTH_WORLD * 2) 328 | lda (ADDR_ACTOR), Y 329 | cmp #eACTORTYPES::FLOOR_2 330 | bne not_y_plus 331 | ldy POS_STARDOWN_Y 332 | iny 333 | rts 334 | not_y_plus: 335 | ldy POS_STARDOWN_Y 336 | inx 337 | 338 | rts 339 | 340 | -------------------------------------------------------------------------------- /src/builder/builder.inc: -------------------------------------------------------------------------------- 1 | ; Copyright (C) 2019 Christophe Meneboeuf 2 | ; 3 | ; This program is free software: you can redistribute it and/or modify 4 | ; it under the terms of the GNU General Public License as published by 5 | ; the Free Software Foundation, either version 3 of the License, or 6 | ; (at your option) any later version. 7 | ; 8 | ; This program is distributed in the hope that it will be useful, 9 | ; but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | ; GNU General Public License for more details. 12 | ; 13 | ; You should have received a copy of the GNU General Public License 14 | ; along with this program. If not, see . 15 | 16 | .import Init_Dimensions_Maze 17 | .import Build_Level 18 | .import Rooms -------------------------------------------------------------------------------- /src/builder/maze.asm: -------------------------------------------------------------------------------- 1 | ; Copyright (C) 2019 Christophe Meneboeuf 2 | ; 3 | ; This program is free software: you can redistribute it and/or modify 4 | ; it under the terms of the GNU General Public License as published by 5 | ; the Free Software Foundation, either version 3 of the License, or 6 | ; (at your option) any later version. 7 | ; 8 | ; This program is distributed in the hope that it will be usefuELEFT, 9 | ; but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | ; GNU General Public License for more details. 12 | ; 13 | ; You should have received a copy of the GNU General Public License 14 | ; along with this program. If not, see . 15 | 16 | .include "../memory.inc" 17 | .include "../random.inc" 18 | .include "../math.inc" 19 | .include "../common.inc" 20 | .include "../world/world.inc" 21 | .include "../actors/actors.inc" 22 | 23 | 24 | .export Grow_Maze 25 | .export Remove_Dead_Ends 26 | 27 | .import Compute_Maze_Addr 28 | .import World 29 | .import WIDTH_MAZE 30 | .import HEIGHT_MAZE 31 | 32 | ; using HGR2 space as the stack 33 | ; Stack contains pointers to tiles (2 byte long) 34 | .define STACK_ADDR $3FFE ; Will be 2 byte incremented before 1st push 35 | 36 | 37 | .define Y_TILE ZERO_2_4 38 | .define X_TILE ZERO_2_5 39 | .define PTR_STACK ZERO_4_1 ; 2 bytes 40 | .define PTR_NEW_TILE ZERO_4_3 ; 2 bytes 41 | 42 | 43 | ; increments the stack pointer then pushes address A:X (little endian) 44 | ; *ptr_stack = x 45 | ; *(ptr_stack+1) = a 46 | ; ptr_stack += 2 47 | .macro PUSHAX 48 | pha 49 | ; increment stack pointer 50 | clc 51 | lda PTR_STACK 52 | adc #2 53 | sta PTR_STACK 54 | lda PTR_STACK+1 55 | adc #0 56 | sta PTR_STACK+1 57 | ; push A:X to the stack 58 | pla 59 | ldy #1 60 | sta (PTR_STACK),Y 61 | dey 62 | txa 63 | sta (PTR_STACK),Y 64 | .endmacro 65 | 66 | ; ptr_newtile is offseted by -WIDTH_WORLD so we can access all its neighbors 67 | ; with positive offsets using Y indirect addressing 68 | .macro PTR_UP_TILE 69 | sec 70 | ldy #0 71 | lda (PTR_STACK),Y 72 | sbc #(2*WIDTH_WORLD) 73 | sta PTR_NEW_TILE 74 | iny 75 | lda (PTR_STACK),Y 76 | sbc #0 77 | sta PTR_NEW_TILE+1 78 | .endmacro 79 | .macro PTR_LEFT_TILE 80 | sec 81 | ldy #0 82 | lda (PTR_STACK),Y 83 | sbc #(WIDTH_WORLD+1) 84 | sta PTR_NEW_TILE 85 | iny 86 | lda (PTR_STACK),Y 87 | sbc #0 88 | sta PTR_NEW_TILE+1 89 | .endmacro 90 | .macro PTR_RIGHT_TILE 91 | sec 92 | ldy #0 93 | lda (PTR_STACK),Y 94 | sbc #(WIDTH_WORLD-1) 95 | sta PTR_NEW_TILE 96 | iny 97 | lda (PTR_STACK),Y 98 | sbc #0 99 | sta PTR_NEW_TILE+1 100 | .endmacro 101 | .macro PTR_DOWN_TILE 102 | ldy #0 103 | lda (PTR_STACK),Y 104 | sta PTR_NEW_TILE 105 | iny 106 | lda (PTR_STACK),Y 107 | sta PTR_NEW_TILE+1 108 | .endmacro 109 | .macro PTR_CURR_TILE 110 | sec 111 | ldy #0 112 | lda (PTR_STACK),Y 113 | sbc #(WIDTH_WORLD) 114 | sta PTR_NEW_TILE 115 | iny 116 | lda (PTR_STACK),Y 117 | sbc #0 118 | sta PTR_NEW_TILE+1 119 | .endmacro 120 | 121 | ; test if the tile offseted from PTR_NEW_TILE is walkable 122 | .macro ISWALKABLE offset 123 | ldy offset 124 | lda (PTR_NEW_TILE),Y 125 | cmp #eACTORTYPES::LAST_FLOOR+1 126 | bcc cannot_carve 127 | .endmacro 128 | 129 | 130 | 131 | .define OFFSET_NIL #WIDTH_WORLD 132 | .define OFFSET_UP #0 133 | .define OFFSET_RIGHT #(WIDTH_WORLD+1) 134 | .define OFFSET_DOWN #(2*WIDTH_WORLD) 135 | .define OFFSET_LEFT #(WIDTH_WORLD-1) 136 | 137 | 138 | .macro IS_TILE_WALLED 139 | PTR_CURR_TILE 140 | 141 | ldy OFFSET_NIL 142 | lda (PTR_NEW_TILE),Y 143 | cmp #eACTORTYPES::LAST_FLOOR 144 | bcc end_loop_stack 145 | 146 | ldy OFFSET_UP 147 | lda (PTR_NEW_TILE),Y 148 | cmp #eACTORTYPES::LAST_FLOOR 149 | bcc end_loop_stack 150 | 151 | ldy OFFSET_RIGHT 152 | lda (PTR_NEW_TILE),Y 153 | cmp #eACTORTYPES::LAST_FLOOR 154 | bcc end_loop_stack 155 | 156 | ldy OFFSET_DOWN 157 | lda (PTR_NEW_TILE),Y 158 | cmp #eACTORTYPES::LAST_FLOOR 159 | bcc end_loop_stack 160 | 161 | ldy OFFSET_LEFT 162 | lda (PTR_NEW_TILE),Y 163 | cmp #eACTORTYPES::LAST_FLOOR 164 | bcc end_loop_stack 165 | 166 | .endmacro 167 | 168 | 169 | ; @brief Fills the empty space with a perfect maze 170 | .define PATCH_WIDTH_MAZE_4 0 171 | .define PATCH_HEIGHT_MAZE_4 0 172 | Grow_Maze: 173 | 174 | ; Groth start location 175 | ldx #2 176 | stx X_TILE 177 | stx Y_TILE 178 | 179 | loop_grow_maze: 180 | 181 | ; init the stack 182 | lda #STACK_ADDR 185 | sta PTR_STACK+1 186 | 187 | ; Test if the tile is suitable 188 | ldy Y_TILE 189 | ldx X_TILE 190 | jsr Compute_Maze_Addr ; result addr in A:X 191 | 192 | ; test if the tile is walled 193 | PUSHAX 194 | IS_TILE_WALLED 195 | 196 | ; carve 197 | ldy #WIDTH_WORLD 198 | lda #eACTORTYPES::FLOOR_1 199 | sta (PTR_NEW_TILE),Y 200 | 201 | 202 | .define IDX ZERO_2_6 203 | ; while the stack is not empty: carve 204 | loop_stack: 205 | 206 | jsr _random_directions 207 | 208 | tax 209 | stx IDX 210 | loop_find_dir: ; find a direction suitable to carvingm 211 | jsr _can_carve 212 | cmp #1 ; cannot carve -> test other directions 213 | beq carve_the_tile 214 | lda IDX 215 | and #3 ; 4th direction? 216 | cmp #3 217 | beq test_stack 218 | inc IDX 219 | ldx IDX 220 | jmp loop_find_dir 221 | test_stack: 222 | lda PTR_STACK+1 223 | cmp #>(STACK_ADDR+2) 224 | bne unstack 225 | lda PTR_STACK 226 | cmp #<(STACK_ADDR+2) 227 | beq end_loop_stack ; stack is empty -> break 228 | unstack: 229 | sec 230 | lda PTR_STACK 231 | sbc #2 232 | sta PTR_STACK 233 | lda PTR_STACK+1 234 | sbc #0 235 | sta PTR_STACK+1 236 | jmp loop_stack 237 | carve_the_tile: 238 | ; carve the tile 239 | ldy #0 240 | lda #eACTORTYPES::FLOOR_1 241 | sta (PTR_NEW_TILE),Y 242 | jmp loop_stack 243 | end_loop_stack: 244 | 245 | inc X_TILE 246 | ldx WIDTH_MAZE 247 | dex 248 | dex 249 | cpx X_TILE 250 | beq incr_y_tile 251 | jmp loop_grow_maze 252 | incr_y_tile: 253 | ldx #2 254 | stx X_TILE 255 | inc Y_TILE 256 | ldy HEIGHT_MAZE 257 | dey 258 | cpy Y_TILE 259 | beq end_loop_grow_maze 260 | jmp loop_grow_maze 261 | 262 | end_loop_grow_maze: 263 | rts 264 | 265 | 266 | 267 | .enum 268 | EUP = 0 269 | ERIGHT 270 | EDOWN 271 | ELEFT 272 | .endenum 273 | 274 | ; 24 direction quartets 275 | RandomDirection: 276 | .byte EUP,ERIGHT,EDOWN,ELEFT,EUP,ERIGHT,ELEFT,EDOWN,EUP,EDOWN,ERIGHT,ELEFT,EUP,EDOWN,ELEFT,ERIGHT,EUP,ELEFT,ERIGHT,EDOWN,EUP,ELEFT,EDOWN,ERIGHT 277 | .byte ERIGHT,EUP,EDOWN,ELEFT,ERIGHT,EUP,ELEFT,EDOWN,ERIGHT,EDOWN,EUP,ELEFT,ERIGHT,EDOWN,ELEFT,EUP,ERIGHT,ELEFT,EUP,EDOWN,ERIGHT,ELEFT,EDOWN,EUP 278 | .byte EDOWN,ERIGHT,EUP,ELEFT,EDOWN,ERIGHT,ELEFT,EUP,EDOWN,ELEFT,EUP,ERIGHT,EDOWN,ELEFT,ERIGHT,EUP,EDOWN,EUP,ELEFT,ERIGHT,EDOWN,EUP,ERIGHT,ELEFT 279 | .byte ELEFT,ERIGHT,EDOWN,EUP,ELEFT,ERIGHT,EDOWN,EUP,ELEFT,EDOWN,ERIGHT,EUP,ELEFT,EDOWN,EUP,ERIGHT,ELEFT,EUP,ERIGHT,EDOWN,ELEFT,EUP,EDOWN,ERIGHT 280 | 281 | ; Uses a precomputed table to quickly return an offset to one of the direction quartets 282 | _random_directions: 283 | 284 | jsr Random8 285 | and #31 286 | ldx #24 287 | jsr Modulus 288 | asl 289 | asl ; offset to a direction quartet 290 | rts 291 | 292 | ; @brief Returns A=1 if the tile can be carved, A=0 otherwise. 293 | ; some difficulties for the branches to be in range to end_can_carve. 294 | ; thus the jsr and jmp and the breakdown of the routine 295 | _can_carve: 296 | 297 | lda RandomDirection,X 298 | can_carve_up: 299 | cmp #EUP 300 | bne can_carve_right 301 | jmp _can_carve_up 302 | 303 | can_carve_right: 304 | cmp #ERIGHT 305 | bne can_carve_down 306 | jmp _can_carve_right 307 | 308 | can_carve_down: 309 | cmp #EDOWN 310 | bne can_carve_left 311 | PTR_DOWN_TILE ; ptr_newtile = down - width_world 312 | ISWALKABLE OFFSET_NIL ; the new tile 313 | ISWALKABLE OFFSET_RIGHT ; new tile's right neighbor 314 | ISWALKABLE OFFSET_DOWN ; new tile's bottom neighbor 315 | ISWALKABLE OFFSET_LEFT ; new tile's left neighbor 316 | jmp _save_ptr_newtile 317 | 318 | can_carve_left: 319 | cmp #ELEFT 320 | bne end_can_carve 321 | PTR_LEFT_TILE ; ptr_newtile = left - width_world 322 | ISWALKABLE OFFSET_NIL ; the new tile 323 | ISWALKABLE OFFSET_UP ; new tile's upper neighbor 324 | ISWALKABLE OFFSET_DOWN ; new tile's bottom neighbor 325 | ISWALKABLE OFFSET_LEFT ; new tile's left neighbor 326 | jmp _save_ptr_newtile 327 | 328 | cannot_carve: 329 | lda #0 330 | end_can_carve: 331 | rts 332 | 333 | _can_carve_up: 334 | PTR_UP_TILE ; ptr_newtile = up - width_world 335 | ISWALKABLE OFFSET_NIL ; the new tile 336 | ISWALKABLE OFFSET_UP ; new tile's upper neighbor 337 | ISWALKABLE OFFSET_RIGHT ; new tile's right neighbor 338 | ISWALKABLE OFFSET_LEFT ; new tile's left neighbor 339 | jmp _save_ptr_newtile 340 | 341 | _can_carve_right: 342 | PTR_RIGHT_TILE ; ptr_newtile = rigth - width_world 343 | ISWALKABLE OFFSET_NIL ; the new tile 344 | ISWALKABLE OFFSET_RIGHT ; new tile's right neighbor 345 | ISWALKABLE OFFSET_DOWN ; new tile's bottom neighbor 346 | ISWALKABLE OFFSET_UP ; new tile's upper neighbor 347 | ; jmp _save_ptr_newtile 348 | 349 | 350 | ; save new tile on the stack 351 | _save_ptr_newtile: 352 | clc 353 | lda PTR_NEW_TILE 354 | adc #WIDTH_WORLD 355 | sta PTR_NEW_TILE 356 | tax 357 | lda PTR_NEW_TILE+1 358 | adc #0 359 | sta PTR_NEW_TILE+1 360 | PUSHAX 361 | 362 | ;CREUSER??? 363 | 364 | lda #1 365 | jmp end_can_carve 366 | 367 | .undefine Y_TILE 368 | .undefine X_TILE 369 | .undefine PTR_STACK 370 | .undefine PTR_NEW_TILE 371 | 372 | 373 | .define PTR_TILE ZERO_2_1 ; 2 bytes 374 | .define PTR_NEXT_TILE ZERO_2_3 ; 2 bytes 375 | .define NB_WALLS ZERO_2_5 376 | .define ADDR_END ZERO_4_1 ; 2 bytes 377 | .define HEIGHTxWIDTH WIDTH_WORLD*HEIGHT_WORLD 378 | ; @brief Removes all the dead ends 379 | Remove_Dead_Ends: 380 | 381 | ; Compute addr_end as the preprocessor cannot handle 16bit multiplications (???) 382 | lda #WIDTH_WORLD 383 | sta FAC1 384 | lda #HEIGHT_WORLD 385 | sta FAC2 386 | jsr mul8 387 | sta ADDR_END+1 388 | stx ADDR_END 389 | lda #World 394 | adc ADDR_END+1 395 | sta ADDR_END+1 396 | 397 | ; starting tile: offsetted by - width_world 398 | lda #<(World + 1) ; &World[1][1] - width_world 399 | sta PTR_TILE 400 | lda #>(World + 1) 401 | sta PTR_TILE+1 402 | 403 | loop_tiles: 404 | jsr _is_tile_dead_end 405 | lda NB_WALLS 406 | cmp #3 407 | bcc next_tile 408 | 409 | jsr _follow_dead_end 410 | 411 | next_tile: 412 | clc 413 | lda PTR_TILE 414 | adc #1 415 | sta PTR_TILE 416 | lda PTR_TILE+1 417 | adc #0 418 | sta PTR_TILE+1 419 | 420 | ; end? 421 | lda PTR_TILE+1 422 | cmp ADDR_END+1 423 | bne loop_tiles 424 | lda PTR_TILE 425 | cmp ADDR_END 426 | bne loop_tiles 427 | end_loop_tiles: 428 | 429 | rts 430 | .undefine ADDR_END 431 | 432 | _follow_dead_end: 433 | 434 | ; saving ptr_tile 435 | lda PTR_TILE 436 | pha 437 | lda PTR_TILE+1 438 | pha 439 | 440 | loop_follow: 441 | ldy #WIDTH_WORLD 442 | lda #eACTORTYPES::WALL_1 443 | sta (PTR_TILE), Y 444 | 445 | lda PTR_NEXT_TILE 446 | sta PTR_TILE 447 | lda PTR_NEXT_TILE+1 448 | sta PTR_TILE+1 449 | 450 | jsr _is_tile_dead_end 451 | lda NB_WALLS 452 | cmp #3 453 | bcs loop_follow 454 | 455 | end_loop_follow: 456 | 457 | ; restoring ptr_tile 458 | pla 459 | sta PTR_TILE+1 460 | pla 461 | sta PTR_TILE 462 | 463 | rts 464 | 465 | 466 | .define ADD_FACTOR ZERO_4_3 467 | ; REM: PTR_TILE is already offsetted by -WIDTH_WORLD 468 | ; for easy access to adjacent tiles by indirect indexing 469 | ; Returns : NB_WALLS >= 3 if it is a dead end 470 | _is_tile_dead_end: 471 | 472 | lda #0 473 | sta NB_WALLS 474 | ldy #WIDTH_WORLD 475 | sty ADD_FACTOR 476 | 477 | ; Returns if the tile is a wall 478 | lda #eACTORTYPES::LAST_FLOOR 479 | cmp (PTR_TILE), Y 480 | bcc end_tst_up_tile 481 | 482 | tst_up_tile: 483 | ldy #0 484 | cmp (PTR_TILE), Y 485 | bcc up_non_walkable 486 | sty ADD_FACTOR 487 | bcs tst_right_tile 488 | up_non_walkable: 489 | inc NB_WALLS 490 | tst_right_tile: 491 | ldy #(WIDTH_WORLD + 1) 492 | cmp (PTR_TILE), Y 493 | bcc right_non_walkable 494 | sty ADD_FACTOR 495 | bcs tst_down_tile 496 | right_non_walkable: 497 | inc NB_WALLS 498 | tst_down_tile: 499 | ldy #(2*WIDTH_WORLD) 500 | cmp (PTR_TILE), Y 501 | bcc down_non_walkable 502 | sty ADD_FACTOR 503 | bcs tst_left_tile 504 | down_non_walkable: 505 | inc NB_WALLS 506 | tst_left_tile: 507 | ldy #(WIDTH_WORLD - 1) 508 | cmp (PTR_TILE), Y 509 | bcc left_non_walkable 510 | sty ADD_FACTOR 511 | bcs end_tests 512 | left_non_walkable: 513 | inc NB_WALLS 514 | 515 | end_tests: 516 | ; computing ptr_next_tile 517 | clc 518 | lda PTR_TILE 519 | adc ADD_FACTOR 520 | sta PTR_NEXT_TILE 521 | lda PTR_TILE+1 522 | adc #0 523 | sta PTR_NEXT_TILE+1 524 | ; offseting ptr_next_tile 525 | sec 526 | lda PTR_NEXT_TILE 527 | sbc #WIDTH_WORLD 528 | sta PTR_NEXT_TILE 529 | lda PTR_NEXT_TILE+1 530 | sbc #0 531 | sta PTR_NEXT_TILE+1 532 | 533 | end_tst_up_tile: 534 | rts 535 | -------------------------------------------------------------------------------- /src/builder/maze.inc: -------------------------------------------------------------------------------- 1 | .import Grow_Maze 2 | .import Remove_Dead_Ends 3 | -------------------------------------------------------------------------------- /src/builder/rooms.asm: -------------------------------------------------------------------------------- 1 | 2 | ; Copyright (C) 2019 Christophe Meneboeuf 3 | ; 4 | ; This program is free software: you can redistribute it and/or modify 5 | ; it under the terms of the GNU General Public License as published by 6 | ; the Free Software Foundation, either version 3 of the License, or 7 | ; (at your option) any later version. 8 | ; 9 | ; This program is distributed in the hope that it will be useful, 10 | ; but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | ; GNU General Public License for more details. 13 | ; 14 | ; You should have received a copy of the GNU General Public License 15 | ; along with this program. If not, see . 16 | 17 | .include "../memory.inc" 18 | .include "../random.inc" 19 | .include "../math.inc" 20 | .include "../common.inc" 21 | .include "../world/world.inc" 22 | .include "../actors/actors.inc" 23 | 24 | .export Carve_Rooms 25 | .export Connect_Rooms 26 | 27 | .import Rooms 28 | .import Compute_Maze_Addr 29 | .import World 30 | .import WIDTH_MAZE 31 | .import HEIGHT_MAZE 32 | 33 | 34 | .BSS 35 | 36 | ; Configration to build rooms 37 | ; FIXME??? CA65 will locate this struct at address 0 !!! 38 | .struct Config_Room 39 | width_min .byte 40 | width_max .byte 41 | height_min .byte 42 | height_max .byte 43 | .endstruct 44 | 45 | .CODE 46 | 47 | .define NB_ATTEMPTS ZERO_2_1 48 | .define NB_ROOMS_OK ZERO_2_2 49 | .define NB_ROOMS_TO_DRAW NB_ATTEMPTS 50 | .define IDX_ROOMS ZERO_3 51 | 52 | ; @params A nb_attempts 53 | ; @returns NB_ROOMS_OK in A 54 | Carve_Rooms: 55 | 56 | sta NB_ATTEMPTS 57 | 58 | ; height_max + width_max shall be < 64 59 | lda #9 60 | sta Config_Room::height_max 61 | lda #3 62 | sta Config_Room::height_min 63 | lda #11 64 | sta Config_Room::width_max 65 | lda #3 66 | sta Config_Room::width_min 67 | 68 | ldx #0 69 | stx NB_ROOMS_OK 70 | 71 | loop_rooms: 72 | 73 | dec NB_ATTEMPTS 74 | beq end_loop_rooms 75 | lda NB_ROOMS_OK ; NB_ROOMS_OK*sizeof(room_t) -> X 76 | asl 77 | asl 78 | tax 79 | 80 | jsr _Build_Room 81 | 82 | lda NB_ROOMS_OK 83 | jsr _Is_intersecting 84 | cmp #TRUE ; not intersecting with another room? 85 | beq loop_rooms 86 | 87 | inc NB_ROOMS_OK 88 | jmp loop_rooms 89 | 90 | end_loop_rooms: 91 | 92 | lda NB_ROOMS_OK 93 | sta NB_ROOMS_TO_DRAW 94 | 95 | ldx #0 96 | loop_draw_rooms: 97 | 98 | jsr _Draw_Room 99 | 100 | inx 101 | inx 102 | inx 103 | inx 104 | 105 | dec NB_ROOMS_TO_DRAW 106 | bne loop_draw_rooms 107 | 108 | end_loop_draw_rooms: 109 | 110 | lda NB_ROOMS_OK 111 | 112 | rts 113 | .undefine NB_ATTEMPTS 114 | .undefine NB_ROOMS_OK 115 | .undefine NB_ROOMS_TO_DRAW 116 | .undefine IDX_ROOMS 117 | 118 | 119 | .define ADDR_WORLD ZERO_2_3 ; 2 bytes 120 | .define NB_LINE ZERO_2_5 121 | .define LINE_LENGTH ZERO_2_6 122 | .define IDX_ROOMS ZERO_3 123 | _Draw_Room: 124 | 125 | stx IDX_ROOMS 126 | 127 | ldy Rooms+3, X ; room->y 128 | lda Rooms+2, X ; rooms->x 129 | tax 130 | PUSH ZERO_2_1 131 | PUSH ZERO_2_2 132 | jsr Compute_Maze_Addr 133 | stx ADDR_WORLD 134 | sta ADDR_WORLD+1 135 | POP ZERO_2_2 136 | POP ZERO_2_1 137 | 138 | 139 | ldx IDX_ROOMS 140 | ldy Rooms, X ; room->height 141 | sty NB_LINE 142 | lda Rooms+1, X ; room->width 143 | sta LINE_LENGTH 144 | loop_draw_line: 145 | lda #eACTORTYPES::FLOOR_1 146 | ldy #0 147 | loop_draw_tile: 148 | sta (ADDR_WORLD), Y 149 | iny 150 | cpy LINE_LENGTH 151 | bne loop_draw_tile 152 | end_loop_draw_tile: 153 | clc 154 | lda ADDR_WORLD 155 | adc #WIDTH_WORLD 156 | sta ADDR_WORLD 157 | lda ADDR_WORLD+1 158 | adc #0 159 | sta ADDR_WORLD+1 160 | dec NB_LINE 161 | bne loop_draw_line 162 | end_loop_draw_line: 163 | 164 | ldx IDX_ROOMS 165 | rts 166 | .undefine ADDR_WORLD 167 | .undefine NB_LINE 168 | .undefine LINE_LENGTH 169 | .undefine IDX_ROOMS 170 | 171 | 172 | .define MODULUS ZERO_2_3 173 | .define WIDTH_ROOM ZERO_2_5 174 | .define HEIGHT_ROOM ZERO_2_6 175 | .define OFFSET_ROOMS ZERO_5_1 176 | ; @param X offset to the room to be built from Rooms 177 | ; @return offset room in X 178 | _Build_Room: 179 | 180 | stx OFFSET_ROOMS 181 | 182 | ; room.height = config->height_min + Random8() % (config->height_max - config->height_min - 1); 183 | sec 184 | lda Config_Room::height_max 185 | sbc Config_Room::height_min 186 | sbc #1 187 | sta MODULUS 188 | jsr Random8 189 | and #7 190 | ldx MODULUS 191 | jsr Modulus 192 | ldx OFFSET_ROOMS 193 | clc 194 | adc Config_Room::height_min 195 | ora #1 196 | sta Rooms, x ; height 197 | sta HEIGHT_ROOM 198 | inx 199 | stx OFFSET_ROOMS 200 | 201 | ; room.width = config->width_min + Random8() % (config->width_max - config->width_min - 1); 202 | sec 203 | lda Config_Room::width_max 204 | sbc Config_Room::width_min 205 | sbc #1 206 | sta MODULUS 207 | jsr Random8 208 | and #$F ; room's height shall be < 16 209 | ldx MODULUS 210 | jsr Modulus 211 | ldx OFFSET_ROOMS 212 | clc 213 | adc Config_Room::width_min 214 | ora #1 215 | sta Rooms, x ; width 216 | sta WIDTH_ROOM 217 | inx 218 | stx OFFSET_ROOMS 219 | 220 | 221 | ; room.x = 3 + Random8() % (WIDTH_MAZE - room.width - 5); 222 | sec 223 | lda WIDTH_MAZE 224 | sbc WIDTH_ROOM 225 | sbc #5 226 | sta MODULUS 227 | jsr Random8 228 | and #$7F 229 | ldx MODULUS 230 | jsr Modulus 231 | ldx OFFSET_ROOMS 232 | clc 233 | adc #3 234 | ora #1 235 | sta Rooms, x ; x 236 | inx 237 | stx OFFSET_ROOMS 238 | 239 | ; room.y = 3 + Random8() % (HEIGHT_MAZE - room.height - 5); 240 | sec 241 | lda HEIGHT_MAZE 242 | sbc HEIGHT_ROOM 243 | sbc #5 244 | sta MODULUS 245 | jsr Random8 246 | and #$7F 247 | ldx MODULUS 248 | jsr Modulus 249 | ldx OFFSET_ROOMS 250 | clc 251 | adc #3 252 | ora #1 253 | sta Rooms, x ; y 254 | inx 255 | stx OFFSET_ROOMS 256 | 257 | rts 258 | .undefine MODULUS 259 | .undefine WIDTH_ROOM 260 | .undefine HEIGHT_ROOM 261 | .undefine OFFSET_ROOMS 262 | 263 | 264 | ; @brief test ifthe room is intersecting with other rooms 265 | ; @param A : nb rooms already carved 266 | ; @return 0 if no intersection, 1 otherwise 267 | .define NB_ROOMS ZERO_3 268 | .define OFFSET_Y ZERO_2_4 269 | .define OFFSET_X ZERO_2_5 270 | _Is_intersecting: 271 | 272 | cmp #0 273 | bne compare 274 | ; first room 275 | lda #FALSE 276 | clc 277 | bcc end_intersecting ; branch always 278 | 279 | ; previous rooms were carved 280 | compare: 281 | sta NB_ROOMS 282 | asl ; * sizeof(room_t) to get offset to the last room randomized 283 | asl 284 | tax ; offset to new room in X 285 | stx OFFSET_X 286 | ldy #0 ; offset to carved rooms in Y 287 | 288 | loop_intersecting: ; each test must be true 289 | sty OFFSET_Y 290 | clc 291 | lda Rooms+2, Y ; room->x 292 | adc Rooms+1, Y ; room->width 293 | cmp Rooms+2, X 294 | bcc false ; branch if room->x + room->width < new_room->x 295 | clc 296 | lda Rooms+2, X ; new_room->x 297 | adc Rooms+1, X ; new_room->width 298 | cmp Rooms+2, Y ; room->x 299 | bcc false ; branch if new_room->x + new_room->width < room->x 300 | clc 301 | lda Rooms+3, Y ; room->y 302 | adc Rooms, Y ; room->height 303 | cmp Rooms+3, X ; new_room->y 304 | bcc false ; branch if room->y + room->height < new_room->y 305 | clc 306 | lda Rooms+3, X ; new_room->y 307 | adc Rooms, X ; new_room->height 308 | cmp Rooms+3, Y ; room->y 309 | bcc false ; branch if new_room->y + new_room->height < room->y 310 | ; all test are true: rooms are intersecting 311 | lda #TRUE ; return value 312 | clc 313 | bcc end_intersecting 314 | 315 | false: 316 | ldx OFFSET_X 317 | lda OFFSET_Y 318 | adc #4 319 | tay 320 | dec NB_ROOMS 321 | bne loop_intersecting 322 | 323 | lda #FALSE ; no room intersects 324 | 325 | end_intersecting: 326 | rts 327 | 328 | 329 | ; using HGR2 space as the stack 330 | ; Stack contains pointers to the tiles encompassing a room (except the corners) 331 | .define STACK_ADDR $4000 332 | .define PTR_TILE_TOP ZERO_2_3 ; 2 bytes 333 | .define PTR_TILE_BOT ZERO_4_4 ; 2 bytes 334 | .define PTR_TILE_LEFT ZERO_2_3 ; 2 bytes 335 | .define PTR_TILE_RIGHT ZERO_4_4 ; 2 bytes 336 | .define PTR_TILE ZERO_2_1 337 | .define PTR_STACK ZERO_4_1 ; 2 bytes 338 | .define PROBABILITY_OPENING ZERO_4_3 339 | .define NB_DOORS ZERO_5_1 340 | .define NB_WALKABLE ZERO_5_2 341 | .define SIZE_STACK ZERO_5_3 ; 2 bytes 342 | .define WIDTH_ROOM ZERO_2_5 343 | .define HEIGHT_ROOM ZERO_2_6 344 | .define OFFSET_ROOMS ZERO_5_5 345 | 346 | ; @brief Connects the rooms to the maze's galleries 347 | ; @param A number of rooms 348 | ; @param X probability to make an opening in a wall tile 349 | ; @detail One opening is made, then all remaning encompassing wall tiles 350 | ; can be opened. Depending on the provided probability 351 | Connect_Rooms: 352 | 353 | sta NB_ROOMS 354 | stx PROBABILITY_OPENING 355 | 356 | lda #0 357 | sta OFFSET_ROOMS 358 | ; for each room 359 | loop_connect_rooms: 360 | 361 | ; # Build a stack of encompassing tiles. Except corners 362 | lda #STACK_ADDR 365 | sta PTR_STACK+1 366 | 367 | ; ## stacking horizontal walls 368 | ; ### init ptr_top 369 | ldx OFFSET_ROOMS 370 | inx 371 | inx 372 | lda Rooms, X ; room->x 373 | sta PTR_TILE_TOP 374 | clc 375 | lda #World 379 | adc #0 380 | sta PTR_TILE_TOP+1 381 | inx 382 | lda Rooms, X ; room->y 383 | tax 384 | dex 385 | stx FAC1 386 | lda #WIDTH_WORLD 387 | sta FAC2 388 | jsr mul8 389 | tay 390 | txa 391 | clc 392 | adc PTR_TILE_TOP 393 | sta PTR_TILE_TOP 394 | tya 395 | adc PTR_TILE_TOP+1 396 | sta PTR_TILE_TOP+1 397 | 398 | ; ### init ptr_bottom 399 | ldx OFFSET_ROOMS 400 | lda Rooms, X ; room->height 401 | sta HEIGHT_ROOM 402 | tax 403 | inx 404 | stx FAC1 405 | lda #WIDTH_WORLD 406 | sta FAC2 407 | jsr mul8 408 | tay 409 | txa 410 | clc 411 | adc PTR_TILE_TOP 412 | sta PTR_TILE_BOT 413 | tya 414 | adc PTR_TILE_TOP+1 415 | sta PTR_TILE_BOT+1 416 | 417 | ; ## stacking 418 | ldx OFFSET_ROOMS 419 | inx 420 | lda Rooms, X ; room->width 421 | sta WIDTH_ROOM 422 | ; for x = 0; x < room->width; x++ 423 | ldx #0 424 | loop_stack_horiz: 425 | ldy #0 426 | clc 427 | txa 428 | adc PTR_TILE_TOP 429 | sta (PTR_STACK), Y 430 | iny 431 | lda #0 432 | adc PTR_TILE_TOP+1 433 | sta (PTR_STACK), Y 434 | iny 435 | txa 436 | adc PTR_TILE_BOT 437 | sta (PTR_STACK), Y 438 | iny 439 | lda #0 440 | adc PTR_TILE_BOT+1 441 | sta (PTR_STACK), Y 442 | iny 443 | ; incr ptr_stack 444 | tya 445 | clc 446 | adc PTR_STACK 447 | sta PTR_STACK 448 | lda #0 449 | adc PTR_STACK+1 450 | sta PTR_STACK+1 451 | ; next x 452 | inx 453 | cpx WIDTH_ROOM 454 | bne loop_stack_horiz 455 | 456 | ; ## stacking vertical walls 457 | ; ### init ptr_left 458 | clc 459 | lda #(WIDTH_WORLD-1) 460 | adc PTR_TILE_TOP 461 | sta PTR_TILE_LEFT 462 | lda #0 463 | adc PTR_TILE_TOP+1 464 | sta PTR_TILE_LEFT+1 465 | ; ### init ptr_right 466 | clc 467 | lda WIDTH_ROOM 468 | adc #1 469 | adc PTR_TILE_LEFT 470 | sta PTR_TILE_RIGHT 471 | lda #0 472 | adc PTR_TILE_LEFT+1 473 | sta PTR_TILE_RIGHT+1 474 | ; ### stacking 475 | ; for y = 0; y < room->height; y++ 476 | ldx #0 477 | loop_stack_vertical: 478 | ldy #1 479 | lda PTR_TILE_LEFT+1 480 | sta (PTR_STACK), Y 481 | dey 482 | lda PTR_TILE_LEFT 483 | sta (PTR_STACK), Y 484 | clc 485 | adc #WIDTH_WORLD 486 | sta PTR_TILE_LEFT 487 | lda PTR_TILE_LEFT+1 488 | adc #0 489 | sta PTR_TILE_LEFT+1 490 | iny 491 | iny 492 | iny 493 | lda PTR_TILE_RIGHT+1 494 | sta (PTR_STACK), Y 495 | dey 496 | lda PTR_TILE_RIGHT 497 | sta (PTR_STACK), Y 498 | clc 499 | adc #WIDTH_WORLD 500 | sta PTR_TILE_RIGHT 501 | lda PTR_TILE_RIGHT+1 502 | adc #0 503 | sta PTR_TILE_RIGHT+1 504 | ; incr ptr_stack 505 | clc 506 | lda #4 507 | adc PTR_STACK 508 | sta PTR_STACK 509 | lda #0 510 | adc PTR_STACK+1 511 | sta PTR_STACK+1 512 | ; next y 513 | inx 514 | cpx HEIGHT_ROOM 515 | bne loop_stack_vertical 516 | 517 | ; ## Compute stack's size 518 | ; UTILISER DIRECTEMENT L ADRESSE DE FIN ET BREAKER QUAND ON L ATTEINT 519 | sec 520 | lda PTR_STACK 521 | sbc #STACK_ADDR 525 | lsr 526 | sta SIZE_STACK+1 527 | pla 528 | ror 529 | sta SIZE_STACK 530 | 531 | ; # Opening the first door 532 | lda #0 533 | sta NB_DOORS 534 | lda #= 2 if it can be carved 620 | ; @detailed PTR_TILE is offsetted by -WIDTH_WORLD 621 | _nb_walkable: 622 | lda #0 623 | sta NB_WALKABLE 624 | lda #eACTORTYPES::FLOOR_1 625 | tst_up: 626 | ldy #0 627 | cmp (PTR_TILE), Y 628 | bne tst_left 629 | inc NB_WALKABLE 630 | tst_left: 631 | ldy #(WIDTH_WORLD-1) 632 | cmp (PTR_TILE), Y 633 | bne tst_right 634 | inc NB_WALKABLE 635 | tst_right: 636 | ldy #(WIDTH_WORLD+1) 637 | cmp (PTR_TILE), Y 638 | bne tst_down 639 | inc NB_WALKABLE 640 | tst_down: 641 | ldy #(2*WIDTH_WORLD) 642 | cmp (PTR_TILE), Y 643 | bne end_is_door_possible 644 | inc NB_WALKABLE 645 | end_is_door_possible: 646 | rts 647 | 648 | .undefine STACK_ADDR 649 | .undefine PTR_TILE_TOP 650 | .undefine PTR_TILE_BOT 651 | .undefine PTR_TILE_LEFT 652 | .undefine PTR_TILE_RIGHT 653 | .undefine PTR_TILE 654 | .undefine PTR_STACK 655 | .undefine PROBABILITY_OPENING 656 | .undefine NB_DOORS 657 | .undefine NB_WALKABLE 658 | .undefine SIZE_STACK 659 | .undefine WIDTH_ROOM 660 | .undefine HEIGHT_ROOM 661 | .undefine OFFSET_ROOMS 662 | 663 | -------------------------------------------------------------------------------- /src/builder/rooms.inc: -------------------------------------------------------------------------------- 1 | 2 | ; Copyright (C) 2019 Christophe Meneboeuf 3 | ; 4 | ; This program is free software: you can redistribute it and/or modify 5 | ; it under the terms of the GNU General Public License as published by 6 | ; the Free Software Foundation, either version 3 of the License, or 7 | ; (at your option) any later version. 8 | ; 9 | ; This program is distributed in the hope that it will be useful, 10 | ; but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | ; GNU General Public License for more details. 13 | ; 14 | ; You should have received a copy of the GNU General Public License 15 | ; along with this program. If not, see . 16 | 17 | .import Carve_Rooms 18 | .import Connect_Rooms 19 | 20 | .define MAX_NB_ROOMS 64 ; MAX_NB_ROOMS *MUST BE* <= 64 and a power of 2 21 | -------------------------------------------------------------------------------- /src/builder/unite.asm: -------------------------------------------------------------------------------- 1 | 2 | ; Copyright (C) 2020 Christophe Meneboeuf 3 | ; 4 | ; This program is free software: you can redistribute it and/or modify 5 | ; it under the terms of the GNU General Public License as published by 6 | ; the Free Software Foundation, either version 3 of the License, or 7 | ; (at your option) any later version. 8 | ; 9 | ; This program is distributed in the hope that it will be useful, 10 | ; but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | ; GNU General Public License for more details. 13 | ; 14 | ; You should have received a copy of the GNU General Public License 15 | ; along with this program. If not, see . 16 | 17 | 18 | 19 | .include "rooms.inc" 20 | .include "maze.inc" 21 | .include "../io/textio.inc" 22 | .include "../memory.inc" 23 | .include "../math.inc" 24 | .include "../common.inc" 25 | .include "../world/world.inc" 26 | .include "../actors/actors.inc" 27 | 28 | .import World 29 | .import Rooms 30 | .import WIDTH_MAZE 31 | .import HEIGHT_MAZE 32 | 33 | .import Compute_Maze_Addr 34 | 35 | .export Unite_Rooms 36 | 37 | 38 | 39 | .CODE 40 | 41 | 42 | .define PTR_TILE ZERO_2_1 ; 2 bytes 43 | .define ZONE_NR ZERO_2_4 44 | .define CPT_X ZERO_2_5 45 | .define CPT_Y ZERO_2_6 46 | 47 | 48 | .define QUEUE_ADDR $4000 49 | 50 | 51 | .define REPLACED_NR ZERO_3 52 | .define PTR_QUEUE ZERO_4_1 ; 2 bytes 53 | .define PTR_TILE_LOCAL ZERO_4_3 ; 2 bytes 54 | .define FILL_NR ZERO_4_5 55 | ; @param X fill color 56 | ; @param A replaced 57 | ; @return TRUE in A some tiles were filled, FALSE otherwise 58 | _Flood_Fill : 59 | 60 | stx FILL_NR 61 | ldy #0 62 | cmp (PTR_TILE), Y ; if (*ptr_tile != replaced) return; 63 | beq fill_1 64 | lda #FALSE 65 | rts 66 | 67 | fill_1: 68 | sta REPLACED_NR 69 | txa 70 | sta (PTR_TILE), Y 71 | lda #QUEUE_ADDR 74 | sta PTR_QUEUE+1 75 | ; offset ptr_tile by -width_world to be quickly accessed with an Y offset 76 | lda PTR_TILE 77 | sec 78 | sbc #WIDTH_WORLD 79 | sta QUEUE_ADDR 80 | lda PTR_TILE+1 81 | sbc #0 82 | sta QUEUE_ADDR+1 83 | 84 | ldx #0 85 | loop_fill: 86 | 87 | ; while ptr_queue >= QUEUE_ADDR 88 | lda PTR_QUEUE 89 | cmp #<(QUEUE_ADDR-2) 90 | bne continue_fill 91 | lda PTR_QUEUE+1 92 | cmp #>(QUEUE_ADDR-2) 93 | bne continue_fill 94 | jmp end_fill 95 | 96 | continue_fill: 97 | ; ptr_tile = *(ptr_queue--); 98 | ldy #0 99 | lda (PTR_QUEUE), Y 100 | sta PTR_TILE_LOCAL 101 | iny 102 | lda (PTR_QUEUE), Y 103 | sta PTR_TILE_LOCAL+1 104 | DEC16 PTR_QUEUE, #2 105 | 106 | tile_west: 107 | ; if (*tile_west == replaced) 108 | ldy #(WIDTH_WORLD+1) 109 | lda REPLACED_NR 110 | cmp (PTR_TILE_LOCAL),Y 111 | bne tile_east 112 | ; *tile_west = fill; 113 | lda FILL_NR 114 | sta (PTR_TILE_LOCAL),Y 115 | ; *(++ptr_queue) = tile_w 116 | ADD16 PTR_QUEUE, #2, #0 117 | clc 118 | lda #1 119 | adc PTR_TILE_LOCAL 120 | ldy #0 121 | sta (PTR_QUEUE),Y 122 | lda #0 123 | adc PTR_TILE_LOCAL+1 124 | iny 125 | sta (PTR_QUEUE),Y 126 | 127 | 128 | tile_east: 129 | ; if (*tile_east == replaced) 130 | ldy #(WIDTH_WORLD-1) 131 | lda REPLACED_NR 132 | cmp (PTR_TILE_LOCAL),Y 133 | bne tile_north 134 | ; *tile_east = fill; 135 | lda FILL_NR 136 | sta (PTR_TILE_LOCAL),Y 137 | ; *(++ptr_queue) = tile_tile_east 138 | ADD16 PTR_QUEUE, #2, #0 139 | sec 140 | lda PTR_TILE_LOCAL 141 | sbc #1 142 | ldy #0 143 | sta (PTR_QUEUE),Y 144 | lda PTR_TILE_LOCAL+1 145 | sbc #0 146 | iny 147 | sta (PTR_QUEUE),Y 148 | 149 | tile_north: 150 | ; if (*tile_north == replaced) 151 | ldy #0 152 | lda REPLACED_NR 153 | cmp (PTR_TILE_LOCAL),Y 154 | bne tile_south 155 | ; *tile_north = fill; 156 | lda FILL_NR 157 | sta (PTR_TILE_LOCAL),Y 158 | ; *(++ptr_queue) = tile_tile_north 159 | ADD16 PTR_QUEUE, #2, #0 160 | sec 161 | lda PTR_TILE_LOCAL 162 | sbc #WIDTH_WORLD 163 | ldy #0 164 | sta (PTR_QUEUE),Y 165 | lda PTR_TILE_LOCAL+1 166 | sbc #0 167 | iny 168 | sta (PTR_QUEUE),Y 169 | 170 | tile_south: 171 | ; if (*tile_south == replaced) 172 | ldy #(2*WIDTH_WORLD) 173 | lda REPLACED_NR 174 | cmp (PTR_TILE_LOCAL),Y 175 | bne end_tiles 176 | ; *tile_south = fill; 177 | lda FILL_NR 178 | sta (PTR_TILE_LOCAL),Y 179 | ; *(++ptr_queue) = tile_tile_south 180 | ADD16 PTR_QUEUE, #2, #0 181 | clc 182 | lda #WIDTH_WORLD 183 | adc PTR_TILE_LOCAL 184 | ldy #0 185 | sta (PTR_QUEUE),Y 186 | lda #0 187 | adc PTR_TILE_LOCAL+1 188 | iny 189 | sta (PTR_QUEUE),Y 190 | 191 | end_tiles: 192 | jmp loop_fill 193 | 194 | 195 | end_fill: 196 | lda #TRUE 197 | rts 198 | 199 | 200 | 201 | .define ROOM_NR ZERO_3 202 | .define SAVE_X ZERO_4_1 203 | .define PTR_ROOM ZERO_4_2 ; 2 bytes 204 | .define ZONE_0 eACTORTYPES::FLOOR_1 ; 1st useful zone: ZONE_1 205 | Unite_Rooms: 206 | 207 | ; *** flood fill room to identify separated zones *** 208 | ; &Wordl[1][1] -> ptr_tile 209 | clc 210 | lda #World 214 | adc #0 215 | sta PTR_TILE+1 216 | lda #1 217 | sta CPT_X 218 | sta CPT_Y 219 | 220 | lda #ZONE_0 221 | sta ZONE_NR 222 | inc ZONE_NR 223 | 224 | loop_flood: 225 | ldx ZONE_NR 226 | lda #ZONE_0 227 | jsr _Flood_Fill 228 | cmp #TRUE 229 | bne loop_flood_next 230 | inc ZONE_NR 231 | 232 | loop_flood_next: 233 | ;next tile 234 | ADD16 PTR_TILE, #1, #0 235 | ; end line? 236 | inc CPT_X 237 | ldx CPT_X 238 | cpx #WIDTH_WORLD-1 239 | bne loop_flood 240 | ldx #0 241 | stx CPT_X 242 | ; next 243 | ADD16 PTR_TILE, #1, #0 244 | ; the end? 245 | inc CPT_Y 246 | ldy CPT_Y 247 | cpy #HEIGHT_WORLD-1 248 | bne loop_flood 249 | 250 | ; *** 251 | ; reunite rooms that are not in the first zone 252 | ; find the location of a room in zone_1 253 | ; Its origin will be targeted by th other zones to join 254 | ; *** 255 | .define SIZEOF_ROOM_T 4 256 | .define ROOM_ZONE_1 ZERO_5_4 257 | lda #0 ; FIXEME : useless? 258 | sta ROOM_NR ; FIXEME : useless? 259 | ldx #3 260 | lda #WIDTH_WORLD 261 | sta FAC2 262 | loop_find_room: 263 | lda Rooms, X 264 | tay 265 | stx SAVE_X 266 | dex 267 | lda Rooms, X 268 | tax 269 | jsr Compute_Maze_Addr 270 | sta PTR_TILE+1 271 | stx PTR_TILE 272 | ldy #0 273 | lda (PTR_TILE), Y 274 | cmp #(ZONE_0 + 1) 275 | beq room_found 276 | ; next room 277 | lda SAVE_X 278 | clc 279 | adc #(SIZEOF_ROOM_T) 280 | tax 281 | jmp loop_find_room 282 | 283 | room_found: 284 | ldx SAVE_X 285 | stx ROOM_ZONE_1 286 | 287 | ; *** 288 | ; Connect one room of each zone to the location found 289 | .define END_LOOP_ZONE ZERO_5_1 290 | lda ZONE_NR 291 | sta END_LOOP_ZONE 292 | lda #(ZONE_0 + 2) ; zone_2 293 | cmp END_LOOP_ZONE 294 | beq end_loop_zone ; only one zone 295 | sta ZONE_NR 296 | 297 | ; loop over the zones 298 | loop_zones: 299 | 300 | ldx #3 301 | loop_rooms: ; find a room of the zone 302 | lda Rooms, X 303 | tay 304 | stx SAVE_X 305 | dex 306 | lda Rooms, X 307 | tax 308 | jsr Compute_Maze_Addr 309 | stx ZERO_5_2 310 | sta ZERO_5_3 311 | ldy #0 312 | lda (ZERO_5_2), Y 313 | cmp ZONE_NR 314 | beq zone_found 315 | ; next room 316 | lda SAVE_X 317 | clc 318 | adc #(SIZEOF_ROOM_T) 319 | tax 320 | jmp loop_rooms 321 | 322 | zone_found: 323 | jsr _Connect_Room 324 | ; next zone 325 | inc ZONE_NR 326 | ; end loop? 327 | lda END_LOOP_ZONE 328 | cmp ZONE_NR 329 | beq end_loop_zone 330 | ; loop 331 | jmp loop_zones 332 | 333 | end_loop_zone: 334 | 335 | 336 | 337 | rts 338 | 339 | ; to compute ptr_romm += ix / iy or ptr_romm += ix / iy, the code is patched. 340 | .macro PATCH_POS address, address2 341 | lda #$18 ; clc 342 | sta address 343 | sta address2 344 | lda #$69 ; adc imm 345 | sta address+3 ; adc #1 / #WIDTH_WORLD 346 | sta address+9 ; adc #0 347 | sta address2+3 ; adc #1 / #WIDTH_WORLD 348 | sta address2+9 ; adc #0 349 | .endmacro 350 | .macro PATCH_NEG address, address2 351 | lda #$38 ; sec 352 | sta address 353 | sta address2 354 | lda #$E9 ; sbc imm 355 | sta address+3 ; sbc #1 / #WIDTH_WORLD 356 | sta address+9 ; sbc #0 357 | sta address2+3 ; sbc #1 / #WIDTH_WORLD 358 | sta address2+9 ; sbc #0 359 | .endmacro 360 | 361 | .define DELTA_X ZERO_9_1 362 | .define DELTA_Y ZERO_9_2 363 | .define DELTA_X_2 ZERO_9_3 364 | .define DELTA_Y_2 ZERO_9_4 365 | .define ROOM_Y ZERO_9_5 366 | .define ROOM_X ZERO_9_6 367 | .define D ZERO_9_7 368 | .define ROOM_FOUND SAVE_X 369 | _Connect_Room: 370 | 371 | ; d = 0 372 | lda #0 373 | sta D 374 | 375 | ; delta_y = zone1_y - room->y 376 | ldx ROOM_FOUND 377 | lda Rooms, X 378 | sta ROOM_Y ; room->y 379 | dex 380 | lda Rooms, X 381 | sta ROOM_X ; room->x 382 | ldx ROOM_ZONE_1 383 | lda Rooms, X ; zone1_y 384 | sec 385 | sbc ROOM_Y 386 | sta DELTA_Y 387 | ; delta_x = zone1_x - room->x 388 | dex 389 | lda Rooms, X ; zone1_x 390 | sec 391 | sbc ROOM_X 392 | sta DELTA_X 393 | 394 | ; delta_x = delta_x > 0 ? delta_x : -delta_x 395 | lda #0 396 | cmp DELTA_X 397 | bmi end_abs_x 398 | sec 399 | sbc DELTA_X 400 | sta DELTA_X 401 | end_abs_x: 402 | ; int dx2 = 2 * dx 403 | lda DELTA_X 404 | asl 405 | sta DELTA_X_2 406 | 407 | ; delta_y = delta_y > 0 ? delta_y : -delta_y 408 | abs_delta_y: 409 | lda #0 410 | cmp DELTA_Y 411 | bmi end_abs_y 412 | sec 413 | sbc DELTA_Y 414 | sta DELTA_Y 415 | end_abs_y: 416 | ; int dy2 = 2 * dy 417 | lda DELTA_Y 418 | asl 419 | sta DELTA_Y_2 420 | 421 | ; uint8_t* ptr_room = &World[room->y][room->x] 422 | ldx ROOM_X 423 | ldy ROOM_Y 424 | jsr Compute_Maze_Addr 425 | stx PTR_ROOM 426 | stx PTR_TILE 427 | sta PTR_ROOM+1 428 | sta PTR_TILE+1 429 | 430 | ldx ROOM_ZONE_1 431 | dex 432 | ; int ix = room->x < zone1_x ? 1 : -1 433 | ix: 434 | lda ROOM_X 435 | cmp Rooms, X 436 | bcc ix_positive 437 | PATCH_NEG patch_ix1, patch_ix2 438 | jmp iy 439 | ix_positive: 440 | PATCH_POS patch_ix1, patch_ix2 441 | ; int iy = room->y < zone1_y ? WIDTH_WORLD : -WIDTH_WORLD; 442 | iy: 443 | inx 444 | lda ROOM_Y 445 | cmp Rooms, X 446 | bcc iy_positive 447 | PATCH_NEG patch_iy1, patch_iy2 448 | jmp iterate 449 | iy_positive: 450 | PATCH_POS patch_iy1, patch_iy2 451 | 452 | iterate: 453 | ldy #0 454 | lda DELTA_X 455 | cmp DELTA_Y 456 | bcc dy_sup 457 | ; if (dx >= dy) 458 | dx_sup: 459 | while_1: 460 | ; ptr_room += ix 461 | patch_ix1: 462 | ADD16 PTR_ROOM, #1, #0 463 | ; d += dy2 464 | clc 465 | lda DELTA_Y_2 466 | adc D 467 | sta D 468 | cmp DELTA_X 469 | bcc d_infequal_dx 470 | beq d_infequal_dx 471 | ; if (d > dx) 472 | ; if (*ptr_room != zone_nr && *ptr_room <= WALKABLE) break; 473 | lda (PTR_ROOM), Y ; Y = 0 474 | cmp ZONE_NR 475 | beq continue_1a 476 | cmp #eACTORTYPES::LAST_FLOOR 477 | beq end 478 | bpl continue_1a 479 | jmp end 480 | continue_1a: 481 | ; *ptr_room = zone_nr 482 | lda ZONE_NR 483 | sta (PTR_ROOM), Y ; Y = 0 484 | ; ptr_room += iy 485 | patch_iy1: 486 | ADD16 PTR_ROOM, #WIDTH_WORLD, #0 487 | ; d -= dx2 488 | sec 489 | lda D 490 | sbc DELTA_X_2 491 | sta D 492 | d_infequal_dx: 493 | ; if (*ptr_room != zone_nr && *ptr_room <= WALKABLE) break; 494 | lda (PTR_ROOM), Y ; Y = 0 495 | cmp ZONE_NR 496 | beq continue_1b 497 | cmp #eACTORTYPES::LAST_FLOOR 498 | beq end 499 | bpl continue_1b 500 | jmp end 501 | continue_1b: 502 | lda ZONE_NR 503 | sta (PTR_ROOM), Y ; Y = 0 504 | jmp while_1 505 | 506 | ; end label in the middle to be reachable by the branches 507 | end: 508 | 509 | ; flood fills works on ptr_tile 510 | ldx #(ZONE_0 + 1) 511 | lda ZONE_NR 512 | jsr _Flood_Fill 513 | rts 514 | 515 | dy_sup: 516 | while_2: 517 | ; ptr_room += iy 518 | patch_iy2: 519 | ADD16 PTR_ROOM, #WIDTH_WORLD, #0 520 | ; d += dx2 521 | clc 522 | lda DELTA_X_2 523 | adc D 524 | sta D 525 | cmp DELTA_Y 526 | bcc d_infequal_dy 527 | beq d_infequal_dy 528 | ; if (d > dy) { 529 | ; if (*ptr_room != zone_nr && *ptr_room <= WALKABLE) break; 530 | lda (PTR_ROOM), Y ; Y = 0 531 | cmp ZONE_NR 532 | beq continue_2a 533 | cmp #eACTORTYPES::LAST_FLOOR 534 | beq end 535 | bpl continue_2a 536 | jmp end 537 | continue_2a: 538 | ; *ptr_room = zone_nr 539 | lda ZONE_NR 540 | sta (PTR_ROOM), Y ; Y = 0 541 | ; ptr_room += ix; 542 | patch_ix2: 543 | ADD16 PTR_ROOM, #1, #0 544 | ; d -= dy2 545 | sec 546 | lda D 547 | sbc DELTA_Y_2 548 | sta D 549 | d_infequal_dy: 550 | ; (*ptr_room != zone_nr && *ptr_room <= WALKABLE) 551 | lda (PTR_ROOM), Y ; Y = 0 552 | cmp ZONE_NR 553 | beq continue_2b 554 | cmp #eACTORTYPES::LAST_FLOOR 555 | beq end 556 | bpl continue_2b 557 | jmp end 558 | continue_2b: 559 | lda ZONE_NR 560 | sta (PTR_ROOM), Y ; Y = 0 561 | jmp while_2 -------------------------------------------------------------------------------- /src/builder/unite.inc: -------------------------------------------------------------------------------- 1 | ; Copyright (C) 2020 Christophe Meneboeuf 2 | ; 3 | ; This program is free software: you can redistribute it and/or modify 4 | ; it under the terms of the GNU General Public License as published by 5 | ; the Free Software Foundation, either version 3 of the License, or 6 | ; (at your option) any later version. 7 | ; 8 | ; This program is distributed in the hope that it will be useful, 9 | ; but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | ; GNU General Public License for more details. 12 | ; 13 | ; You should have received a copy of the GNU General Public License 14 | ; along with this program. If not, see . 15 | 16 | .import Unite_Rooms -------------------------------------------------------------------------------- /src/common.inc: -------------------------------------------------------------------------------- 1 | ; Copyright (C) 2020 Christophe Meneboeuf 2 | ; 3 | ; This program is free software: you can redistribute it and/or modify 4 | ; it under the terms of the GNU General Public License as published by 5 | ; the Free Software Foundation, either version 3 of the License, or 6 | ; (at your option) any later version. 7 | ; 8 | ; This program is distributed in the hope that it will be useful, 9 | ; but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | ; GNU General Public License for more details. 12 | ; 13 | ; You should have received a copy of the GNU General Public License 14 | ; along with this program. If not, see . 15 | 16 | .define TRUE 1 17 | .define FALSE 0 18 | .define UNDEF $FF -------------------------------------------------------------------------------- /src/debug.asm: -------------------------------------------------------------------------------- 1 | ; Copyright (C) 2020 Christophe Meneboeuf 2 | ; 3 | ; This program is free software: you can redistribute it and/or modify 4 | ; it under the terms of the GNU General Public License as published by 5 | ; the Free Software Foundation, either version 3 of the License, or 6 | ; (at your option) any later version. 7 | ; 8 | ; This program is distributed in the hope that it will be useful, 9 | ; but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | ; GNU General Public License for more details. 12 | ; 13 | ; You should have received a copy of the GNU General Public License 14 | ; along with this program. If not, see . 15 | 16 | 17 | 18 | .include "memory.inc" 19 | 20 | ; nb of bytes to be displayed in DBG_TRACES[0] 21 | .export DBG_TRACE 22 | ; bytes to be displayed 23 | .export DBG_TRACES 24 | 25 | 26 | .CODE 27 | .define STROUT $DB3A ; Applesoft: OUTPUTS AY-POINTED NULL TERMINATED STRING 28 | .define LINPTR $ED24 ; Applesoft: Displays the number A(high)X(low) in decimal 29 | 30 | ; Traces the number of TRACES requested 31 | DBG_TRACE: 32 | ldy #>str_trace 33 | lda #str_space 48 | lda # 3 | ; 4 | ; This program is free software: you can redistribute it and/or modify 5 | ; it under the terms of the GNU General Public License as published by 6 | ; the Free Software Foundation, either version 3 of the License, or 7 | ; (at your option) any later version. 8 | ; 9 | ; This program is distributed in the hope that it will be useful, 10 | ; but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | ; GNU General Public License for more details. 13 | ; 14 | ; You should have received a copy of the GNU General Public License 15 | ; along with this program. If not, see . 16 | 17 | .include "world/world.inc" 18 | .include "actors/actors.inc" 19 | .include "display.inc" 20 | .include "tiles.inc" 21 | .include "math.inc" 22 | .include "memory.inc" 23 | .include "monitor.inc" 24 | .include "common.inc" 25 | 26 | 27 | ; Init the view. To be called before anything else! 28 | .export view_init 29 | 30 | ; Refreshes the view to reflect the world 31 | ; destroyed: ZERO_2_1 & ZERO_2_2 32 | .export view_refresh 33 | 34 | 35 | ; Places the upper left corner of the *view* on the given coordinates 36 | ; X: in x (values [0:255]) 37 | ; Y: in y (values [0:255]) 38 | ; destroyed: ZERO_2_1 39 | .export set_view_coords ; routine to place the view over the world 40 | 41 | ; Shows a tile at the provided coordinates 42 | ; Tile number to be displayed : TILE_NR 43 | ; X: TILE_COORD_X 44 | ; Y: TILE_COORD_Y 45 | 46 | .import Compute_Maze_Addr 47 | .import World 48 | .import Player_XY 49 | .import ActorTransparent 50 | .import ActorTypes 51 | 52 | .import DBG_TRACE 53 | .import DBG_TRACES 54 | 55 | .CODE 56 | 57 | 58 | ; ********* PRIVATE CONSTANTS ******** 59 | ; shall be even 60 | .define GRID_WIDTH $A 61 | ; shall be even 62 | .define GRID_HEIGHT $A 63 | 64 | ; location of th eplayer in the view 65 | .define PLAYER_X GRID_WIDTH/2 66 | .define PLAYER_Y GRID_HEIGHT/2 67 | 68 | ; sizeof the structure describing a "free tile": 69 | ; struct { int8_t* p_in_world; int8_t offset_from_player } 70 | .define SIZEOF_TILETRANSP_T 3 71 | 72 | ; ********* USEFUL VARIABLES & LOCATIONS ******* 73 | ; x location 74 | .define TILE_COORD_X ZERO_5_3 75 | ; y location 76 | .define TILE_COORD_Y ZERO_5_4 77 | ; Address of the first tile in the world to be viewed 78 | .define VIEW_WORLD ZERO_4_1 79 | 80 | 81 | 82 | view_init: 83 | 84 | ; inits the current view to an impossible value 85 | ; for the first screen refresh 86 | ldx #(GRID_HEIGHT*GRID_WIDTH - 1) 87 | lda #$FF 88 | sta View_Current 89 | loop_view_init: 90 | sta View_Current, x 91 | dex 92 | bne loop_view_init 93 | 94 | ; sets the text portion of the mixed mode 95 | lda #$14 96 | sta WNDTOP ; sets the beginning of the text 97 | jsr HOME ; clear the text screen and positions the "cursor" 98 | 99 | jsr HGR ; HIRES mixed mode, page 1 100 | 101 | rts 102 | 103 | 104 | ; this routine will create the view and populate View_Future 105 | ; destroys ZERO_4_3, ZERO_4_4, ZERO_5_1, ZERO_5_2, ZERO_5_3 106 | ; ZERO_5_4, ZERO_5_5, ZERO_5_6, ZERO_7_1, ZERO_7_2 107 | _build_view: 108 | 109 | ; 1 - Init the view 110 | lda #0 111 | ldx #(GRID_WIDTH * GRID_HEIGHT - 1) 112 | loop_init_view: 113 | lda #eACTORTYPES::UNKNOWN 114 | sta View_Future,X 115 | dex 116 | bne loop_init_view 117 | sta View_Future,X 118 | 119 | ; 2 - Player 120 | .define OFFSET_PLAYER_IN_VIEW PLAYER_X+PLAYER_Y*GRID_WIDTH 121 | ldx #(OFFSET_PLAYER_IN_VIEW) 122 | lda #eACTORTYPES::PLAYER 123 | sta View_Future,X 124 | 125 | ; 3 - Casting rays 126 | .define NB_RAYS 68 127 | .define NB_ITER ZERO_4_3 128 | .define NB_TILES_IN_RAY ZERO_4_4 129 | .define NB_TILES_IN_RAY_LEFT ZERO_5_1 130 | .define SRC_TILE_IN_WORLD ZERO_5_2 ; 2 bytes 131 | .define PTR_RAY ZERO_5_4 ; 2 bytes 132 | .define TMP ZERO_5_6 133 | .define TMP2 ZERO_7_1 134 | 135 | 136 | ; loading ptr_rays - 1 as it will be incremented 137 | ; at the 1st iteration of the loop 138 | lda #Rays 143 | sbc #0 144 | sta PTR_RAY+1 145 | 146 | ldx #0 147 | stx NB_TILES_IN_RAY 148 | 149 | loop_rays: 150 | 151 | stx NB_ITER 152 | 153 | ; computing the pointer to the ray to be casted 154 | ; ptr_ray += sizeof(ray_elem)*nb elem 155 | lda NB_TILES_IN_RAY 156 | sta FAC1 157 | lda #3 ; sizeof(ray_elem) = 1 byte (offset_view) + 2 bytes (offset_world) 158 | sta FAC2 159 | jsr mul8 ; result is alway 8 bit 160 | inx ; result += 1 161 | txa 162 | clc 163 | adc PTR_RAY ; incrementing the ptr to the current ray 164 | sta PTR_RAY 165 | lda #0 166 | adc PTR_RAY+1 167 | sta PTR_RAY+1 168 | 169 | 170 | ; loading nb tiles in ray 171 | ldy #0 172 | lda (PTR_RAY),Y 173 | tax 174 | stx NB_TILES_IN_RAY 175 | iny 176 | 177 | loop_ray: 178 | stx NB_TILES_IN_RAY_LEFT 179 | 180 | lda VIEW_WORLD 181 | clc 182 | adc (PTR_RAY),Y ; offset_tile_world_low 183 | sta SRC_TILE_IN_WORLD 184 | lda VIEW_WORLD+1 185 | iny 186 | adc (PTR_RAY),Y ; offset_tile_world_high 187 | sta SRC_TILE_IN_WORLD+1 188 | iny 189 | sty TMP 190 | lda (PTR_RAY),Y ; offset_view 191 | sta TMP2 192 | ldy #0 193 | lda (SRC_TILE_IN_WORLD), Y 194 | ; sta View_Future, X ; ptr tile in view future 195 | ldy TMP 196 | iny 197 | ; break if non-transparent 198 | tax 199 | lda ActorTypes, X 200 | ldx TMP2 201 | sta View_Future, X 202 | tax 203 | lda ActorTransparent, X 204 | cmp #TRUE 205 | bne end_loop_ray 206 | ; loop if tiles are left in the ray 207 | 208 | ldx NB_TILES_IN_RAY_LEFT 209 | dex 210 | bne loop_ray 211 | 212 | end_loop_ray: 213 | 214 | ldx NB_ITER 215 | inx 216 | cpx #NB_RAYS 217 | bne loop_rays 218 | 219 | end_loop_rays: 220 | 221 | rts 222 | 223 | .undef OFFSET_PLAYER_IN_VIEW 224 | .undef NB_RAYS 225 | .undef NB_ITER 226 | .undef NB_TILES_IN_RAY 227 | .undef NB_TILES_IN_RAY_LEFT 228 | .undef SRC_TILE_IN_WORLD 229 | .undef PTR_RAY 230 | .undef TMP 231 | 232 | 233 | 234 | 235 | ;.align 256 236 | ; This routine refreshes (updates) the screen. 237 | ; It won't update tiles that are not modified 238 | ; destroys ZERO_2_1, ZERO_2_2, 239 | ; ZERO_4_3, ZERO_4_4, ZERO_5_1, ZERO_5_2, ZERO_5_3, 240 | ; ZERO_5_4, ZERO_5_5, ZERO_5_6, ZERO_7_1, ZERO_7_2 241 | view_refresh: 242 | 243 | ; 1 - computing the start address of the view 244 | ; to VIEW_WORLD. It places the top-left corner at the given offset 245 | lda #World 250 | adc View_Offset+1 251 | sta VIEW_WORLD+1 252 | ; 2 - shifting the view to place the center at the given offset 253 | sec 254 | lda VIEW_WORLD 255 | sbc SHIFT_VIEW 256 | sta VIEW_WORLD 257 | lda VIEW_WORLD+1 258 | sbc SHIFT_VIEW+1 259 | sta VIEW_WORLD+1 260 | 261 | ; 3 - build the view to be displayed 262 | jsr _build_view 263 | ;jsr _dbg_build_view 264 | 265 | ; 4 - display the tiles viewed 266 | .define SAVE_X ZERO_3 267 | lda #0 268 | tax 269 | sta TILE_COORD_Y 270 | loop_display_tiles_y: 271 | ldy #0 272 | sty TILE_COORD_X 273 | loop_display_tiles_x: 274 | lda View_Future, X 275 | cmp View_Current, X 276 | beq no_display ; do not display an unchanged tile 277 | sta View_Current, X ; update the list of diplayed tiles 278 | sta TILE_NR 279 | stx SAVE_X 280 | jsr _set_tile ; this routines does not alter its parameters 281 | ldx SAVE_X 282 | no_display: 283 | inx 284 | inc TILE_COORD_X 285 | ldy TILE_COORD_X 286 | tya 287 | cmp #GRID_WIDTH 288 | bne loop_display_tiles_x 289 | ; next line 290 | inc TILE_COORD_Y 291 | lda TILE_COORD_Y 292 | cmp #GRID_HEIGHT 293 | bne loop_display_tiles_y 294 | 295 | rts 296 | .undef VIEW_WORLD 297 | SHIFT_VIEW: .word WIDTH_WORLD*PLAYER_Y + PLAYER_X ; shift to center the view on the player 298 | 299 | .CODE 300 | set_view_coords: 301 | 302 | ; 1. Compute offset from the starting address of the maze 303 | stx ZERO_2_1 304 | sty FAC1 305 | lda #WIDTH_WORLD 306 | sta FAC2 307 | jsr mul8 308 | tay ; high part 309 | txa ; low part 310 | clc 311 | adc ZERO_2_1 312 | sta View_Offset ; little endian 313 | tya 314 | adc #0 315 | sta View_Offset+1 ; little endian 316 | 317 | rts 318 | 319 | 320 | ;.align 256 321 | ; displays tile #TILE_NR at [TILE_COORD_X, TILE_COORD_Y] 322 | ; destroys ZERO_2_1, ZERO_2_2 323 | _set_tile: 324 | .define ADDR_TO_PATCH $666 ; 2 byte address to be patched by tile's address 325 | ; A tile being 16 line tall, it will vertically spawn on two 8 line "blocks" 326 | .define ADDR_DST_BLOCK_1 ZERO_2_1 ; first block 327 | .define ADDR_DST_BLOCK_2 ZERO_2_3 ; second bloc 328 | 329 | ; 1 - patching the code with the adress 330 | ; of the tile to be displayed 331 | lda TILE_NR 332 | asl 333 | tax 334 | 335 | lda TILES, X 336 | sta PATCH_1+1 337 | sta PATCH_2+1 338 | sta PATCH_3+1 339 | sta PATCH_4+1 340 | sta PATCH_5+1 341 | sta PATCH_6+1 342 | sta PATCH_7+1 343 | sta PATCH_8+1 344 | 345 | lda TILES+1, X 346 | sta PATCH_1+2 347 | sta PATCH_2+2 348 | sta PATCH_3+2 349 | sta PATCH_4+2 350 | sta PATCH_5+2 351 | sta PATCH_6+2 352 | sta PATCH_7+2 353 | sta PATCH_8+2 354 | 355 | ; destination address (HGR) 356 | ; 2 - get the offset from HGR_GRID (view) 357 | lda #GRID_WIDTH 358 | sta FAC1 359 | lda TILE_COORD_Y 360 | sta FAC2 361 | jsr mul8 ; X = GRID_WITH * Y (always < 0xFF) 362 | txa 363 | clc 364 | adc TILE_COORD_X ; Won't set the carry 365 | asl ; 16 bit elements: doubling the offset. Won't work if grid > 127 tiles (ie 20x10) 366 | tay ; Y: offset to get the address 367 | ; 3 - retrieve the destination address 368 | lda HGR_GRID, Y 369 | sta ADDR_DST_BLOCK_1 370 | adc #$80 371 | sta ADDR_DST_BLOCK_2 372 | lda HGR_GRID+1, Y 373 | sta ADDR_DST_BLOCK_1+1 374 | sta ADDR_DST_BLOCK_2+1 375 | 376 | ; 4 - Draw 377 | ldx #0 ; loop counter & index source 378 | .define NB_ITER_1 #$20 379 | ; First loop: draw lines 1 to 8 380 | loop_lines_1to8: 381 | ldy #0 ; index destination 382 | ; copy lines (4 blocks) 383 | PATCH_1: 384 | lda ADDR_TO_PATCH, X 385 | sta (ADDR_DST_BLOCK_1), Y 386 | iny 387 | inx 388 | PATCH_2: 389 | lda ADDR_TO_PATCH, X 390 | sta (ADDR_DST_BLOCK_1), Y 391 | iny 392 | inx 393 | PATCH_3: 394 | lda ADDR_TO_PATCH, X 395 | sta (ADDR_DST_BLOCK_1), Y 396 | iny 397 | inx 398 | PATCH_4: 399 | lda ADDR_TO_PATCH, X 400 | sta (ADDR_DST_BLOCK_1), Y 401 | iny 402 | inx 403 | ; next line 404 | lda ADDR_DST_BLOCK_1+1 405 | ADC #$4 ; addr += 0x400 406 | sta ADDR_DST_BLOCK_1+1 407 | cpx NB_ITER_1 408 | bne loop_lines_1to8 409 | 410 | clc ; cpx affects carry 411 | 412 | .define NB_ITER_2 #$40 413 | ; Second loop: draw lines 9 to 16 414 | loop_lines_9to16: 415 | ldy #0 ; index destination 416 | ; copy lines (4 blocks) 417 | _DISP_TILE_2: 418 | PATCH_5: 419 | lda ADDR_TO_PATCH, X 420 | sta (ADDR_DST_BLOCK_2), Y 421 | iny 422 | inx 423 | PATCH_6: 424 | lda ADDR_TO_PATCH, X 425 | sta (ADDR_DST_BLOCK_2), Y 426 | iny 427 | inx 428 | PATCH_7: 429 | lda ADDR_TO_PATCH, X 430 | sta (ADDR_DST_BLOCK_2), Y 431 | iny 432 | inx 433 | PATCH_8: 434 | lda ADDR_TO_PATCH, X 435 | sta (ADDR_DST_BLOCK_2), Y 436 | iny 437 | inx 438 | ; next line 439 | lda ADDR_DST_BLOCK_2+1 440 | ADC #$4 ; addr += 0x400 441 | sta ADDR_DST_BLOCK_2+1 442 | cpx NB_ITER_2 443 | bne loop_lines_9to16 444 | 445 | 446 | rts 447 | 448 | 449 | 450 | 451 | 452 | .RODATA 453 | 454 | ; Adress of the tiles in the HIRES screen 455 | ; T0 T1 T2 456 | ; T3 T4 T5 457 | HGR_GRID: 458 | .word $2000, $2004, $2008, $200C, $2010, $2014, $2018, $201C, $2020, $2024 459 | .word $2100, $2104, $2108, $210C, $2110, $2114, $2118, $211C, $2120, $2124 460 | .word $2200, $2204, $2208, $220C, $2210, $2214, $2218, $221C, $2220, $2224 461 | .word $2300, $2304, $2308, $230C, $2310, $2314, $2318, $231C, $2320, $2324 462 | .word $2028, $202C, $2030, $2034, $2038, $203C, $2040, $2044, $2048, $204C 463 | .word $2128, $212C, $2130, $2134, $2138, $213C, $2140, $2144, $2148, $214C 464 | .word $2228, $222C, $2230, $2234, $2238, $223C, $2240, $2244, $2248, $224C 465 | .word $2328, $232C, $2330, $2334, $2338, $233C, $2340, $2344, $2348, $234C 466 | .word $2050, $2054, $2058, $205C, $2060, $2064, $2068, $206C, $2070, $2074 467 | .word $2150, $2154, $2158, $215C, $2160, $2164, $2168, $216C, $2170, $2174 468 | 469 | ; Nb rays: 68 470 | ; A ray: length (nb_tiles), offset_from_view_in_world_low, offset_from_view_in_world_high, offset_view 471 | Rays: 472 | .byte 10, 68, 1, 54, 4, 1, 44, 3, 1, 43, 195, 0, 33, 194, 0, 32, 130, 0, 22, 129, 0, 21, 65, 0, 11, 64, 0, 10, 0, 0, 0 473 | .byte 10, 5, 1, 45, 4, 1, 44, 196, 0, 34, 195, 0, 33, 131, 0, 23, 130, 0, 22, 66, 0, 12, 65, 0, 11, 1, 0, 1, 0, 0, 0 474 | .byte 9, 68, 1, 54, 4, 1, 44, 3, 1, 43, 195, 0, 33, 131, 0, 23, 130, 0, 22, 66, 0, 12, 65, 0, 11, 1, 0, 1 475 | .byte 9, 5, 1, 45, 4, 1, 44, 196, 0, 34, 195, 0, 33, 131, 0, 23, 67, 0, 13, 66, 0, 12, 2, 0, 2, 1, 0, 1 476 | .byte 8, 68, 1, 54, 4, 1, 44, 196, 0, 34, 195, 0, 33, 131, 0, 23, 67, 0, 13, 66, 0, 12, 2, 0, 2 477 | .byte 8, 5, 1, 45, 4, 1, 44, 196, 0, 34, 132, 0, 24, 131, 0, 23, 67, 0, 13, 3, 0, 3, 2, 0, 2 478 | .byte 7, 5, 1, 45, 4, 1, 44, 196, 0, 34, 132, 0, 24, 131, 0, 23, 67, 0, 13, 3, 0, 3 479 | .byte 7, 5, 1, 45, 197, 0, 35, 196, 0, 34, 132, 0, 24, 68, 0, 14, 67, 0, 13, 3, 0, 3 480 | .byte 6, 5, 1, 45, 197, 0, 35, 196, 0, 34, 132, 0, 24, 68, 0, 14, 4, 0, 4 481 | .byte 6, 5, 1, 45, 197, 0, 35, 133, 0, 25, 132, 0, 24, 68, 0, 14, 4, 0, 4 482 | .byte 5, 5, 1, 45, 197, 0, 35, 133, 0, 25, 69, 0, 15, 5, 0, 5 483 | .byte 6, 5, 1, 45, 197, 0, 35, 198, 0, 36, 134, 0, 26, 70, 0, 16, 6, 0, 6 484 | .byte 6, 5, 1, 45, 197, 0, 35, 133, 0, 25, 134, 0, 26, 70, 0, 16, 6, 0, 6 485 | .byte 7, 5, 1, 45, 6, 1, 46, 198, 0, 36, 134, 0, 26, 135, 0, 27, 71, 0, 17, 7, 0, 7 486 | .byte 7, 5, 1, 45, 197, 0, 35, 198, 0, 36, 134, 0, 26, 70, 0, 16, 71, 0, 17, 7, 0, 7 487 | .byte 8, 70, 1, 56, 6, 1, 46, 198, 0, 36, 199, 0, 37, 135, 0, 27, 71, 0, 17, 72, 0, 18, 8, 0, 8 488 | .byte 8, 5, 1, 45, 6, 1, 46, 198, 0, 36, 134, 0, 26, 135, 0, 27, 71, 0, 17, 7, 0, 7, 8, 0, 8 489 | .byte 9, 70, 1, 56, 6, 1, 46, 7, 1, 47, 199, 0, 37, 135, 0, 27, 136, 0, 28, 72, 0, 18, 73, 0, 19, 9, 0, 9 490 | .byte 9, 5, 1, 45, 6, 1, 46, 198, 0, 36, 199, 0, 37, 135, 0, 27, 71, 0, 17, 72, 0, 18, 8, 0, 8, 9, 0, 9 491 | .byte 8, 70, 1, 56, 6, 1, 46, 7, 1, 47, 199, 0, 37, 200, 0, 38, 136, 0, 28, 137, 0, 29, 73, 0, 19 492 | .byte 8, 5, 1, 45, 6, 1, 46, 198, 0, 36, 199, 0, 37, 135, 0, 27, 136, 0, 28, 72, 0, 18, 73, 0, 19 493 | .byte 7, 70, 1, 56, 6, 1, 46, 7, 1, 47, 199, 0, 37, 200, 0, 38, 201, 0, 39, 137, 0, 29 494 | .byte 7, 5, 1, 45, 6, 1, 46, 198, 0, 36, 199, 0, 37, 200, 0, 38, 136, 0, 28, 137, 0, 29 495 | .byte 6, 70, 1, 56, 6, 1, 46, 7, 1, 47, 8, 1, 48, 200, 0, 38, 201, 0, 39 496 | .byte 6, 5, 1, 45, 6, 1, 46, 7, 1, 47, 199, 0, 37, 200, 0, 38, 201, 0, 39 497 | .byte 5, 70, 1, 56, 71, 1, 57, 7, 1, 47, 8, 1, 48, 9, 1, 49 498 | .byte 5, 70, 1, 56, 6, 1, 46, 7, 1, 47, 8, 1, 48, 9, 1, 49 499 | .byte 4, 70, 1, 56, 71, 1, 57, 72, 1, 58, 73, 1, 59 500 | .byte 5, 70, 1, 56, 71, 1, 57, 135, 1, 67, 136, 1, 68, 137, 1, 69 501 | .byte 5, 70, 1, 56, 134, 1, 66, 135, 1, 67, 136, 1, 68, 137, 1, 69 502 | .byte 6, 70, 1, 56, 134, 1, 66, 135, 1, 67, 136, 1, 68, 200, 1, 78, 201, 1, 79 503 | .byte 6, 133, 1, 65, 134, 1, 66, 135, 1, 67, 199, 1, 77, 200, 1, 78, 201, 1, 79 504 | .byte 7, 70, 1, 56, 134, 1, 66, 135, 1, 67, 199, 1, 77, 200, 1, 78, 201, 1, 79, 9, 2, 89 505 | .byte 7, 133, 1, 65, 134, 1, 66, 198, 1, 76, 199, 1, 77, 200, 1, 78, 8, 2, 88, 9, 2, 89 506 | .byte 8, 70, 1, 56, 134, 1, 66, 135, 1, 67, 199, 1, 77, 200, 1, 78, 8, 2, 88, 9, 2, 89, 73, 2, 99 507 | .byte 8, 133, 1, 65, 134, 1, 66, 198, 1, 76, 199, 1, 77, 7, 2, 87, 8, 2, 88, 72, 2, 98, 73, 2, 99 508 | .byte 7, 70, 1, 56, 134, 1, 66, 135, 1, 67, 199, 1, 77, 7, 2, 87, 8, 2, 88, 72, 2, 98 509 | .byte 7, 133, 1, 65, 134, 1, 66, 198, 1, 76, 199, 1, 77, 7, 2, 87, 71, 2, 97, 72, 2, 98 510 | .byte 6, 70, 1, 56, 134, 1, 66, 198, 1, 76, 199, 1, 77, 7, 2, 87, 71, 2, 97 511 | .byte 6, 133, 1, 65, 134, 1, 66, 198, 1, 76, 6, 2, 86, 7, 2, 87, 71, 2, 97 512 | .byte 5, 133, 1, 65, 134, 1, 66, 198, 1, 76, 6, 2, 86, 70, 2, 96 513 | .byte 5, 133, 1, 65, 197, 1, 75, 198, 1, 76, 6, 2, 86, 70, 2, 96 514 | .byte 4, 133, 1, 65, 197, 1, 75, 5, 2, 85, 69, 2, 95 515 | .byte 5, 133, 1, 65, 132, 1, 64, 196, 1, 74, 4, 2, 84, 68, 2, 94 516 | .byte 5, 133, 1, 65, 197, 1, 75, 196, 1, 74, 4, 2, 84, 68, 2, 94 517 | .byte 6, 68, 1, 54, 132, 1, 64, 196, 1, 74, 195, 1, 73, 3, 2, 83, 67, 2, 93 518 | .byte 6, 133, 1, 65, 132, 1, 64, 196, 1, 74, 4, 2, 84, 3, 2, 83, 67, 2, 93 519 | .byte 7, 68, 1, 54, 132, 1, 64, 131, 1, 63, 195, 1, 73, 3, 2, 83, 2, 2, 82, 66, 2, 92 520 | .byte 7, 133, 1, 65, 132, 1, 64, 196, 1, 74, 195, 1, 73, 3, 2, 83, 67, 2, 93, 66, 2, 92 521 | .byte 8, 68, 1, 54, 132, 1, 64, 131, 1, 63, 195, 1, 73, 194, 1, 72, 2, 2, 82, 1, 2, 81, 65, 2, 91 522 | .byte 8, 133, 1, 65, 132, 1, 64, 196, 1, 74, 195, 1, 73, 3, 2, 83, 2, 2, 82, 66, 2, 92, 65, 2, 91 523 | .byte 9, 68, 1, 54, 132, 1, 64, 131, 1, 63, 195, 1, 73, 194, 1, 72, 193, 1, 71, 1, 2, 81, 0, 2, 80, 64, 2, 90 524 | .byte 9, 133, 1, 65, 132, 1, 64, 196, 1, 74, 195, 1, 73, 194, 1, 72, 2, 2, 82, 1, 2, 81, 65, 2, 91, 64, 2, 90 525 | .byte 8, 68, 1, 54, 132, 1, 64, 131, 1, 63, 130, 1, 62, 194, 1, 72, 193, 1, 71, 192, 1, 70, 0, 2, 80 526 | .byte 8, 133, 1, 65, 132, 1, 64, 131, 1, 63, 195, 1, 73, 194, 1, 72, 193, 1, 71, 1, 2, 81, 0, 2, 80 527 | .byte 7, 68, 1, 54, 67, 1, 53, 131, 1, 63, 130, 1, 62, 129, 1, 61, 193, 1, 71, 192, 1, 70 528 | .byte 7, 68, 1, 54, 132, 1, 64, 131, 1, 63, 130, 1, 62, 194, 1, 72, 193, 1, 71, 192, 1, 70 529 | .byte 6, 68, 1, 54, 67, 1, 53, 66, 1, 52, 130, 1, 62, 129, 1, 61, 128, 1, 60 530 | .byte 6, 68, 1, 54, 67, 1, 53, 131, 1, 63, 130, 1, 62, 129, 1, 61, 128, 1, 60 531 | .byte 5, 68, 1, 54, 67, 1, 53, 66, 1, 52, 65, 1, 51, 64, 1, 50 532 | .byte 6, 68, 1, 54, 67, 1, 53, 66, 1, 52, 2, 1, 42, 1, 1, 41, 0, 1, 40 533 | .byte 6, 68, 1, 54, 67, 1, 53, 3, 1, 43, 2, 1, 42, 1, 1, 41, 0, 1, 40 534 | .byte 7, 68, 1, 54, 67, 1, 53, 3, 1, 43, 2, 1, 42, 1, 1, 41, 193, 0, 31, 192, 0, 30 535 | .byte 7, 68, 1, 54, 4, 1, 44, 3, 1, 43, 2, 1, 42, 194, 0, 32, 193, 0, 31, 192, 0, 30 536 | .byte 8, 68, 1, 54, 4, 1, 44, 3, 1, 43, 2, 1, 42, 194, 0, 32, 193, 0, 31, 192, 0, 30, 128, 0, 20 537 | .byte 8, 5, 1, 45, 4, 1, 44, 3, 1, 43, 195, 0, 33, 194, 0, 32, 193, 0, 31, 129, 0, 21, 128, 0, 20 538 | .byte 9, 68, 1, 54, 4, 1, 44, 3, 1, 43, 195, 0, 33, 194, 0, 32, 193, 0, 31, 129, 0, 21, 128, 0, 20, 64, 0, 10 539 | .byte 9, 5, 1, 45, 4, 1, 44, 196, 0, 34, 195, 0, 33, 194, 0, 32, 130, 0, 22, 129, 0, 21, 65, 0, 11, 64, 0, 10 540 | 541 | .BSS 542 | 543 | .align 256 544 | View_Current: .res GRID_HEIGHT*GRID_WIDTH ; current displayed view 545 | 546 | View_Offset: .res 2 ; offset of the corner from HGR_GRID (x,y) 547 | 548 | Tiles_Transparent: .res GRID_WIDTH*SIZEOF_TILETRANSP_T ; Tiles on the same line as the player that don't block the view 549 | ; struct { int8_t* p_in_world; int8_t offset_from_player} 550 | 551 | DBG_NB_REDRAW: .res 1 552 | 553 | ; This alignement is **MANDATORY** for the raycasting to work: 554 | ; only 8-bit additions are used to compute pointers in this view 555 | .align 256 556 | View_Future: .res GRID_HEIGHT*GRID_WIDTH ; next displayed view 557 | -------------------------------------------------------------------------------- /src/display.inc: -------------------------------------------------------------------------------- 1 | 2 | ; Copyright (C) 2018 Christophe Meneboeuf 3 | ; 4 | ; This program is free software: you can redistribute it and/or modify 5 | ; it under the terms of the GNU General Public License as published by 6 | ; the Free Software Foundation, either version 3 of the License, or 7 | ; (at your option) any later version. 8 | ; 9 | ; This program is distributed in the hope that it will be useful, 10 | ; but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | ; GNU General Public License for more details. 13 | ; 14 | ; You should have received a copy of the GNU General Public License 15 | ; along with this program. If not, see . 16 | 17 | 18 | .define TILE_NR ZERO_5_1 ; tile number to be displayed 19 | 20 | -------------------------------------------------------------------------------- /src/display_map.asm: -------------------------------------------------------------------------------- 1 | 2 | ; Copyright (C) 2019 Christophe Meneboeuf 3 | ; 4 | ; This program is free software: you can redistribute it and/or modify 5 | ; it under the terms of the GNU General Public License as published by 6 | ; the Free Software Foundation, either version 3 of the License, or 7 | ; (at your option) any later version. 8 | ; 9 | ; This program is distributed in the hope that it will be useful, 10 | ; but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | ; GNU General Public License for more details. 13 | ; 14 | ; You should have received a copy of the GNU General Public License 15 | ; along with this program. If not, see . 16 | 17 | 18 | .include "world/world.inc" 19 | .include "actors/actors.inc" 20 | .include "io/gr.inc" 21 | .include "memory.inc" 22 | .include "math.inc" 23 | .include "monitor.inc" 24 | 25 | ; inits display of map 26 | .export Display_Map_Init 27 | ; displays the map and reacts to keyboard input 28 | .export Map_Loop 29 | ; displays the map 30 | .export Display_Map_Show 31 | 32 | 33 | 34 | .define DBG_DISP_WIDTH 40 35 | .define DBG_DISP_HEIGHT 48 36 | 37 | .BSS 38 | Map_x: .byte 1 39 | Map_y: .byte 1 40 | 41 | .define MAX_Y HEIGHT_WORLD - DBG_DISP_HEIGHT 42 | .define MAX_X WIDTH_WORLD - DBG_DISP_WIDTH 43 | 44 | .CODE 45 | 46 | .define KEY_UP $C9 47 | .define KEY_LEFT $CA 48 | .define KEY_DOWN $CB 49 | .define KEY_RIGHT $CC 50 | .define TAB $89 51 | 52 | 53 | ; ##################### MAP ################## 54 | 55 | ; Buffer to save the 4 bottom line of gr1 56 | SAVED_GR_L36: .res 40 57 | SAVED_GR_L37: .res 40 58 | SAVED_GR_L38: .res 40 59 | SAVED_GR_L39: .res 40 60 | ; The 4 bottom lines of GR1 61 | .define SRC_GR1_L36 $650 62 | .define SRC_GR1_L37 $6D0 63 | .define SRC_GR1_L38 $750 64 | .define SRC_GR1_L39 $7D0 65 | 66 | ; @brief Displays the map and wait for action: move the view or exit 67 | Map_Loop: 68 | 69 | ; save the 4 last lines of GR1 70 | ldx #39 71 | loop_save_gr: 72 | lda SRC_GR1_L36, X 73 | sta SAVED_GR_L36, X 74 | lda SRC_GR1_L37, X 75 | sta SAVED_GR_L37, X 76 | lda SRC_GR1_L38, X 77 | sta SAVED_GR_L38, X 78 | lda SRC_GR1_L39, X 79 | sta SAVED_GR_L39, X 80 | dex 81 | bne loop_save_gr 82 | lda SRC_GR1_L36, X 83 | sta SAVED_GR_L36, X 84 | lda SRC_GR1_L37, X 85 | sta SAVED_GR_L37, X 86 | lda SRC_GR1_L38, X 87 | sta SAVED_GR_L38, X 88 | lda SRC_GR1_L39, X 89 | sta SAVED_GR_L39, X 90 | 91 | disp: 92 | ; display map 93 | ;sta $C051 ; full screen 94 | sta $C054 ; page 1 95 | sta $C056 ; lores 96 | sta $C050 ; gfx 97 | sta $C052 ; mixed mode off 98 | 99 | ldx Map_x 100 | ldy Map_y 101 | ; capping X and Y 102 | min_x: 103 | cpx #$FF 104 | bne max_x 105 | ldx #0 106 | bvc min_y 107 | max_x: 108 | cpx #MAX_X 109 | bcc min_y 110 | ldx #MAX_X 111 | min_y: 112 | cpy #$FF 113 | bne max_y 114 | ldy #0 115 | bvc disp_map 116 | max_y: 117 | cpy #MAX_Y 118 | bcc disp_map 119 | ldy #MAX_Y 120 | disp_map: 121 | stx Map_x 122 | sty Map_y 123 | jsr Display_Map_Show 124 | 125 | ; waiting for a key to be pressed 126 | kbd_loop: 127 | lda KEYBD 128 | bpl kbd_loop ; bit #8 is set when a character is present (thus A < 0) 129 | sta KEYBD_STROBE 130 | 131 | cmp #KEY_UP 132 | beq move_up 133 | cmp #KEY_RIGHT 134 | beq move_right 135 | cmp #KEY_DOWN 136 | beq move_down 137 | cmp #KEY_LEFT 138 | beq move_left 139 | cmp #TAB 140 | beq quit 141 | jmp kbd_loop 142 | 143 | move_up: 144 | dec Map_y 145 | jmp disp 146 | move_right: 147 | inc Map_x 148 | jmp disp 149 | move_down: 150 | inc Map_y 151 | jmp disp 152 | move_left: 153 | dec Map_x 154 | jmp disp 155 | quit: 156 | ; restore player's view 157 | ;sta $C054 ; page 1 158 | sta $C057 ; hires 159 | sta $C053 ; mixed mode on 160 | 161 | ; restore the 4 last lines of GR1 162 | ldx #39 163 | loop_restore_gr: 164 | lda SAVED_GR_L36, X 165 | sta SRC_GR1_L36, X 166 | lda SAVED_GR_L37, X 167 | sta SRC_GR1_L37, X 168 | lda SAVED_GR_L38, X 169 | sta SRC_GR1_L38, X 170 | lda SAVED_GR_L39, X 171 | sta SRC_GR1_L39, X 172 | dex 173 | bne loop_restore_gr 174 | lda SAVED_GR_L36, X 175 | sta SRC_GR1_L36, X 176 | lda SAVED_GR_L37, X 177 | sta SRC_GR1_L37, X 178 | lda SAVED_GR_L38, X 179 | sta SRC_GR1_L38, X 180 | lda SAVED_GR_L39, X 181 | sta SRC_GR1_L39, X 182 | 183 | rts 184 | 185 | 186 | 187 | Display_Map_Init: 188 | ; Init coords to view map 189 | lda #0 190 | sta Map_x 191 | sta Map_y 192 | 193 | jsr Clear_Gr1 194 | 195 | ; display GR1 196 | sta $C051 ; full screen 197 | sta $C054 ; page 1 198 | sta $C056 ; lores 199 | sta $C050 ; gfx 200 | sta $C052 ; mixed mode off 201 | 202 | rts 203 | 204 | .import World 205 | .define SRC_LINE_UP ZERO_7_1 206 | .define SRC_LINE_DOWN ZERO_8_1 207 | .define SRC_OFFSET ZERO_5_5 208 | .define DST_GR1 ZERO_5_5 209 | .define CPT_FILL ZERO_5_4 210 | .define TMP ZERO_9_10 211 | 212 | Addr_GR1: ; 3 address blocks to fill GR1 (address -1), stored in reversed endianess. 213 | .word $4F04, $2704, $FF03 214 | 215 | 216 | ; @brief Displays the map in GR1 screen 217 | ; @param X top-left x coordinate 218 | ; @param Y top-left y coordinate 219 | ; X, Y capped by WITH_WOLRD - GR_WIDTH, HEIGHT_WORLD - GR_HEIGHT 220 | ; The code in "copy_8x2_lines" copies 16 lines from World 221 | ; It is called 3 times to fill the entire screen. Each line 222 | ; is two tile heigh. 223 | Display_Map_Show: 224 | 225 | ; display GR1 226 | sta $C051 ; full screen 227 | sta $C054 ; page 1 228 | sta $C056 ; lores 229 | sta $C050 ; gfx 230 | sta $C052 ; mixed mode off 231 | 232 | ld_src: 233 | ; compute offset 234 | stx TMP 235 | sty FAC1 236 | lda #WIDTH_WORLD 237 | sta FAC2 238 | jsr mul8 239 | sta SRC_OFFSET+1 240 | txa 241 | clc 242 | adc TMP 243 | sta SRC_OFFSET 244 | lda SRC_OFFSET+1 245 | adc #0 246 | sta SRC_OFFSET+1 247 | 248 | ; source address in World 249 | lda #<(World-1) 250 | clc 251 | adc SRC_OFFSET 252 | sta SRC_LINE_UP 253 | lda #>(World-1) 254 | adc SRC_OFFSET+1 255 | sta SRC_LINE_UP+1 256 | lda SRC_LINE_UP 257 | clc 258 | adc #WIDTH_WORLD 259 | sta SRC_LINE_DOWN 260 | lda SRC_LINE_UP+1 261 | adc #0 262 | sta SRC_LINE_DOWN+1 263 | 264 | ldx #5 265 | stx CPT_FILL 266 | 267 | ; destination adress in GR1. 268 | lda Addr_GR1,X 269 | sta DST_GR1 270 | dex 271 | lda Addr_GR1,X 272 | sta DST_GR1+1 273 | dex 274 | stx CPT_FILL 275 | 276 | fill: 277 | ldx #8 278 | copy_8x2_lines: ; call 3 times to fill the screen 279 | ldy #DBG_DISP_WIDTH 280 | copy_2_lines: 281 | lda (SRC_LINE_DOWN),Y 282 | cmp #eACTORTYPES::FIRST_DYNAMIC 283 | bcc keep_color_1 284 | lda #$F ; white 285 | keep_color_1: 286 | asl 287 | asl 288 | asl 289 | asl 290 | sta TMP 291 | lda (SRC_LINE_UP),Y 292 | cmp #eACTORTYPES::FIRST_DYNAMIC 293 | bcc keep_color_2 294 | lda #$F ; white 295 | keep_color_2: 296 | and #$F 297 | ora TMP 298 | sta (DST_GR1), Y 299 | dey 300 | bne copy_2_lines 301 | 302 | ; update source 303 | lda SRC_LINE_UP 304 | clc 305 | adc #(2*WIDTH_WORLD) 306 | sta SRC_LINE_UP 307 | lda SRC_LINE_UP+1 308 | adc #0 309 | sta SRC_LINE_UP+1 310 | lda SRC_LINE_DOWN 311 | clc 312 | adc #(2*WIDTH_WORLD) 313 | sta SRC_LINE_DOWN 314 | lda SRC_LINE_DOWN+1 315 | adc #0 316 | sta SRC_LINE_DOWN+1 317 | ; update destination 318 | clc 319 | lda DST_GR1 320 | adc #$80 321 | sta DST_GR1 322 | lda DST_GR1+1 323 | adc #0 324 | sta DST_GR1+1 325 | dex 326 | bne copy_8x2_lines 327 | 328 | ; destination adress in GR1. 329 | ldx CPT_FILL 330 | lda Addr_GR1,X 331 | sta DST_GR1 332 | dex 333 | lda Addr_GR1,X 334 | sta DST_GR1+1 335 | dex 336 | stx CPT_FILL 337 | 338 | cpx #$FD 339 | bne fill 340 | 341 | rts 342 | -------------------------------------------------------------------------------- /src/display_map.inc: -------------------------------------------------------------------------------- 1 | ; inits display of map 2 | .import Display_Map_Init 3 | 4 | ; @brief Displays the map in GR1 screen 5 | .import Map_Loop 6 | -------------------------------------------------------------------------------- /src/escape.cfg: -------------------------------------------------------------------------------- 1 | # Configuration: 2 | # APPLESOFT required 3 | # HGR1 & HGR2 memory reserved 4 | # CODE2, RODATA and DATA segment are loaded contiguously in the main memory 5 | # but will be run from MAIN2 memory. The program has to do the relocation. 6 | FEATURES { 7 | STARTADDRESS: default = $0803; 8 | } 9 | SYMBOLS { 10 | __EXEHDR__: type = import; 11 | __FILETYPE__: type = weak, value = $0006; # ProDOS file type 12 | } 13 | MEMORY { 14 | ZP: file = "", start = $0000, size = $00FF; 15 | HEADER: file = %O, start = %S - $3A, size = $003A; 16 | HGR: file = "", define = yes, start = $2000, size = $4000; 17 | MAIN2: file = "", define = yes, start = $6000, size = $3000; 18 | BSSMEM: file = "", define = yes, start = __MAIN2_LAST__, size = $BF00 - __BSSMEM_START__; 19 | MAIN: file = %O, define = yes, start = %S, size = __BSSMEM_START__ - %S - __HGR_SIZE__; 20 | } 21 | SEGMENTS { 22 | ZEROPAGE: load = ZP, type = zp, optional = no; 23 | EXEHDR: load = HEADER, type = ro; 24 | CODE: load = MAIN, type = rw, define = yes; 25 | CODE2: load = MAIN, run = MAIN2, type = rw, define = yes; 26 | RODATA: load = MAIN, run = MAIN2, type = ro, define = yes, align = $100; 27 | DATA: load = MAIN, run = MAIN2, type = rw, define = yes, align = $100; 28 | BSS: load = BSSMEM, type = bss, define = yes, align = $100; 29 | } 30 | -------------------------------------------------------------------------------- /src/game_loop.asm: -------------------------------------------------------------------------------- 1 | ; Copyright (C) 2018 Christophe Meneboeuf 2 | ; 3 | ; This program is free software: you can redistribute it and/or modify 4 | ; it under the terms of the GNU General Public License as published by 5 | ; the Free Software Foundation, either version 3 of the License, or 6 | ; (at your option) any later version. 7 | ; 8 | ; This program is distributed in the hope that it will be useful, 9 | ; but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | ; GNU General Public License for more details. 12 | ; 13 | ; You should have received a copy of the GNU General Public License 14 | ; along with this program. If not, see . 15 | 16 | .include "actors/actors.inc" 17 | .include "world/level.inc" 18 | .include "world/world.inc" 19 | .include "display.inc" 20 | .include "display_map.inc" 21 | .include "memory.inc" 22 | .include "monitor.inc" 23 | .include "common.inc" 24 | 25 | 26 | .export game_loop 27 | 28 | ; display / view 29 | .import set_view_coords 30 | .import view_refresh 31 | ; player 32 | .import player_move 33 | .import player_move_inx 34 | .import player_move_iny 35 | .import player_move_dex 36 | .import player_move_dey 37 | .import ActorPositions 38 | ; world 39 | .import world_set_player 40 | ; actors 41 | .import ActorPositions 42 | 43 | ; ************ 44 | .include "builder/builder.inc" 45 | .import world_init 46 | .import player_init 47 | .import view_init 48 | .import Actors_Init 49 | ; ************ 50 | 51 | 52 | .define KEY_UP_UPP $C9 ; 'I' 53 | .define KEY_UP_LOW $E9 ; 'i' 54 | .define KEY_LEFT_UPP $CA ; 'J' 55 | .define KEY_LEFT_LOW $EA ; 'j' 56 | .define KEY_DOWN_UPP $CB ; 'K' 57 | .define KEY_DOWN_LOW $EB ; 'k' 58 | .define KEY_RIGHT_UPP $CC ; 'L' 59 | .define KEY_RIGHT_LOW $EC ; 'l' 60 | .define TAB $89 ; '\t' 61 | 62 | .define Player_XY ActorPositions + eACTORTYPES::PLAYER 63 | 64 | 65 | .CODE 66 | 67 | nop ; Main can jump to a wrong game_loop's addr without this nop :/ 68 | 69 | ; ########### GAME ########## 70 | 71 | ; @brief Main game loop 72 | game_loop: 73 | 74 | jsr levels_init 75 | jsr Actors_Init 76 | 77 | level_loop: 78 | jsr level_enter ; Uses NextLevel as level number 79 | 80 | ; ***************** 81 | jsr Display_Map_Init 82 | 83 | ldx Player_XY 84 | ldy Player_XY + 1 85 | jsr world_init 86 | 87 | jsr view_init 88 | ; ***************** 89 | 90 | ldx Player_XY 91 | ldy Player_XY+1 92 | 93 | jsr world_set_player 94 | jsr set_view_coords 95 | jsr view_refresh 96 | 97 | ; waiting for a key to be pressed 98 | kbd_loop: 99 | lda KEYBD 100 | bpl kbd_loop ; bit #8 is set when a character is present (thus A < 0) 101 | sta KEYBD_STROBE 102 | 103 | jsr key_action 104 | 105 | lda ExitLevel 106 | cmp #TRUE 107 | bne kbd_loop 108 | 109 | jsr level_exit 110 | 111 | jmp level_loop 112 | 113 | rts 114 | 115 | ; action on key pressed 116 | key_action: 117 | cmp #KEY_UP_UPP 118 | beq move_up 119 | cmp #KEY_UP_LOW 120 | beq move_up 121 | 122 | cmp #KEY_RIGHT_UPP 123 | beq move_right 124 | cmp #KEY_RIGHT_LOW 125 | beq move_right 126 | 127 | cmp #KEY_DOWN_UPP 128 | beq move_down 129 | cmp #KEY_DOWN_LOW 130 | beq move_down 131 | 132 | cmp #KEY_LEFT_UPP 133 | beq move_left 134 | cmp #KEY_LEFT_LOW 135 | beq move_left 136 | 137 | cmp #TAB 138 | beq display_map 139 | 140 | rts 141 | 142 | move_up: 143 | ldx Player_XY 144 | ldy Player_XY+1 145 | dey 146 | jsr player_move 147 | jmp end_action_move 148 | move_right: 149 | ldx Player_XY 150 | ldy Player_XY+1 151 | inx 152 | jsr player_move 153 | jmp end_action_move 154 | move_down: 155 | ldx Player_XY 156 | ldy Player_XY+1 157 | iny 158 | jsr player_move 159 | jmp end_action_move 160 | move_left: 161 | ldx Player_XY 162 | ldy Player_XY+1 163 | dex 164 | jsr player_move 165 | jmp end_action_move 166 | 167 | end_action_move: ; update player/view coordinates and refresh the display 168 | jsr world_set_player 169 | jsr set_view_coords ; coords of the player in XY after player_move_* 170 | jsr view_refresh 171 | rts 172 | 173 | 174 | 175 | display_map: 176 | jsr Map_Loop 177 | rts 178 | 179 | -------------------------------------------------------------------------------- /src/io/files.asm: -------------------------------------------------------------------------------- 1 | ; Copyright (C) 2021 Christophe Meneboeuf 2 | ; 3 | ; This program is free software: you can redistribute it and/or modify 4 | ; it under the terms of the GNU General Public License as published by 5 | ; the Free Software Foundation, either version 3 of the License, or 6 | ; (at your option) any later version. 7 | ; 8 | ; This program is distributed in the hope that it will be useful, 9 | ; but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | ; GNU General Public License for more details. 12 | ; 13 | ; You should have received a copy of the GNU General Public License 14 | ; along with this program. If not, see . 15 | 16 | 17 | ; TODO a lot of code space can be spared by factorizing ReadState and Write state. Almos the same code 18 | 19 | 20 | .include "../actors/actors.inc" 21 | .include "../world/level.inc" 22 | .include "../world/world.inc" 23 | .include "../common.inc" 24 | .include "../memory.inc" 25 | .include "../math.inc" 26 | 27 | .import World 28 | .import ActorsInLevel 29 | .import LevelIsBuilt 30 | .import PlayerTile 31 | .import Tile_player_standing_actor 32 | 33 | ; data 34 | ; TODO : should remain private!! 35 | .export Str_FileLevelConfs 36 | .export Str_FileLevelsActors 37 | .export Param_FileOpen 38 | .export Param_FileOffset 39 | .export Param_FilesReadWrite 40 | .export File_Buffer 41 | 42 | 43 | ; functions 44 | .export ReadFile 45 | .export LoadState 46 | .export SaveState 47 | .export ResetIsBuilt 48 | .export LoadCurrentLevel 49 | 50 | .define TO_PATCH 0 51 | 52 | ; reserve 1024 bytes for MLI file operations in the upper part of HGR2 53 | File_Buffer := $5C00 54 | 55 | 56 | .RODATA 57 | 58 | Str_FileLevelConfs: 59 | .byte $19, "/PRODOS.2.4.2/LEVELS.CONF" ; Pascal string 60 | Str_FileLevelsActors: 61 | .byte $19, "/PRODOS.2.4.2/LEVELS.ACTS" ; Pascal string 62 | Str_FileStates: 63 | .byte $14, "/PRODOS.2.4.2/STATES" ; Pascal string 64 | 65 | .BSS 66 | 67 | Lvl_Nr: .res 1 68 | 69 | .define MIN_READ_SIZE 256 70 | ReadWriteBuffer: .res MIN_READ_SIZE 71 | 72 | .DATA 73 | 74 | Param_FileOpen: 75 | .byte $3 ; in - nb params 76 | .addr TO_PATCH ; in - char* filepath 77 | .addr File_Buffer ; in - char* workbuffer 78 | Handle_File: 79 | .byte $0 ; out - handle on the file 80 | 81 | Param_FileOffset: 82 | .byte $2 ; in - nb params 83 | .byte TO_PATCH ; in - handle on the file 84 | .byte TO_PATCH, TO_PATCH, 0 ; in - Offset 85 | 86 | Param_FilesReadWrite: 87 | .byte $4 ; in - nb params 88 | .byte TO_PATCH ; in - handle on the file 89 | .addr TO_PATCH ; in - out buffer 90 | .word TO_PATCH ; in - max nb bytes to WRITE/READ 91 | .word $0000 ; out - nb bytes write/read 92 | 93 | Param_FileClose: 94 | .byte $1 ; in - nb params 95 | .byte TO_PATCH ; in - handle on the file 96 | 97 | 98 | 99 | .CODE 100 | 101 | ; TODO handle errors 102 | ReadFile: 103 | 104 | ; Open the file 105 | jsr $BF00 ; call MLI 106 | .byte $C8 ; Open 107 | .addr Param_FileOpen 108 | 109 | ; Set read position 110 | lda Handle_File 111 | sta Param_FileOffset+1 112 | jsr $BF00 ; call MLI 113 | .byte $CE ; Set Mark 114 | .addr Param_FileOffset 115 | 116 | ; Read the file 117 | lda Handle_File 118 | sta Param_FilesReadWrite+1 119 | jsr $BF00 ; call MLI 120 | .byte $CA ; read 121 | .addr Param_FilesReadWrite 122 | 123 | ; Close the file 124 | lda Handle_File 125 | sta Param_FileClose+1 126 | jsr $BF00 ; call MLI 127 | .byte $CC ; Close 128 | .addr Param_FileClose 129 | rts 130 | 131 | 132 | 133 | .define READ_DST ZERO_2_4 ; 2 bytes 134 | 135 | ; @brief Offset in A:X (little endian) 136 | ; HandleFile must have been set by opening the file 137 | _SetOffset: 138 | 139 | sta Param_FileOffset+2 140 | stx Param_FileOffset+3 141 | lda Handle_File 142 | sta Param_FileOffset+1 143 | jsr $BF00 ; call MLI 144 | .byte $CE ; Set Mark 145 | .addr Param_FileOffset 146 | 147 | rts 148 | 149 | ; @brief Size in in A:X (little endian) 150 | ; Destination ptr in READ_DST: cannot be locate in ZERO PAGE!!! 151 | ; HandleFile must have been set by opening the file 152 | _Read: 153 | 154 | ; Read the file 155 | sta Param_FilesReadWrite+4 156 | stx Param_FilesReadWrite+5 157 | lda Handle_File 158 | sta Param_FilesReadWrite+1 159 | lda READ_DST 160 | sta Param_FilesReadWrite+2 161 | lda READ_DST+1 162 | sta Param_FilesReadWrite+3 163 | jsr $BF00 ; call MLI 164 | .byte $CA ; read 165 | .addr Param_FilesReadWrite 166 | 167 | rts 168 | 169 | ; @param LevelNr in A 170 | ; @return isBuilt in A 171 | ; modifies ZERO_2_3, ZERO_2_4, ZERO_2_5 172 | .define LVLS_HEADER_SIZE 4 173 | .define LVL_HEADER_SIZE 3 174 | .define OFFSET Param_FileOffset+2 175 | ; compute offset and sets file position 176 | _FindLevelLayout: 177 | 178 | ; Compute the level state offset in file 179 | ldy #(LVLS_HEADER_SIZE + 2 + LVL_HEADER_SIZE) ; +2: current lvl + nb lvls 180 | sty OFFSET 181 | lda #0 182 | sta OFFSET+1 183 | lda Lvl_Nr 184 | tax 185 | cpx #0 186 | beq end_acc_offset_lvl 187 | acc_offset_lvl: 188 | ADD16 OFFSET, #<(HEIGHT_WORLD * WIDTH_WORLD), #>(HEIGHT_WORLD * WIDTH_WORLD) 189 | ADD16 OFFSET, #(LVL_HEADER_SIZE + 2), #0 ; +2: visited + tile 190 | dex 191 | bne acc_offset_lvl 192 | end_acc_offset_lvl: 193 | 194 | ; Set read position 195 | lda OFFSET 196 | ldx OFFSET+1 197 | jsr _SetOffset 198 | 199 | rts 200 | 201 | 202 | .define ACTSS_HEADER_SIZE 4; 203 | .define ACT_HEADER_SIZE 3; 204 | _FindLevelActors: 205 | 206 | ; Compute offset 207 | lda #(LVLS_HEADER_SIZE + 2) 208 | sta OFFSET 209 | lda #0 210 | sta OFFSET+1 211 | ldx NbLevels 212 | acc_offset_lvls: 213 | ADD16 OFFSET, #<(HEIGHT_WORLD * WIDTH_WORLD), #>(HEIGHT_WORLD * WIDTH_WORLD) 214 | ADD16 OFFSET, #(LVL_HEADER_SIZE + 2), #0 ; +2: visited + tile 215 | dex 216 | bne acc_offset_lvls 217 | ADD16 OFFSET, #(ACTSS_HEADER_SIZE + ACT_HEADER_SIZE), #0 218 | 219 | ldx Lvl_Nr 220 | cpx #0 221 | beq end_acc_offset_actors 222 | acc_offset_actors: 223 | ADD16 OFFSET, #<(ACT_HEADER_SIZE + SIZEOF_ACTORS_T), #>(ACT_HEADER_SIZE + SIZEOF_ACTORS_T) 224 | dex 225 | bne acc_offset_actors 226 | end_acc_offset_actors: 227 | 228 | ; Set read position 229 | lda OFFSET 230 | ldx OFFSET+1 231 | jsr _SetOffset 232 | 233 | rts 234 | 235 | ; @param Level NR in A 236 | LoadState: 237 | 238 | sta Lvl_Nr 239 | 240 | ; Open the file 241 | lda #Str_FileStates 244 | sta Param_FileOpen+2 245 | jsr $BF00 ; call MLI 246 | .byte $C8 ; Open 247 | .addr Param_FileOpen 248 | 249 | ; save the new current level 250 | lda #(LVLS_HEADER_SIZE) 251 | ldx #0 252 | jsr _SetOffset ; current lvl 253 | lda #Lvl_Nr 256 | sta READ_DST+1 257 | lda #1 258 | ldx #0 259 | jsr _Write 260 | 261 | lda Lvl_Nr 262 | jsr _FindLevelLayout ; compute offset and sets file read position 263 | 264 | lda #ReadWriteBuffer 267 | sta READ_DST+1 268 | lda #MIN_READ_SIZE 270 | jsr _Read 271 | lda ReadWriteBuffer 272 | sta LevelIsBuilt 273 | lda ReadWriteBuffer+1 274 | sta Tile_player_standing_actor 275 | 276 | lda LevelIsBuilt 277 | cmp #FALSE 278 | beq LoadState_end 279 | 280 | ; Read the level's layout 281 | ; Set read position 282 | ADD16 OFFSET, #2, #0 ; offset past "visited" & "tile" 283 | lda OFFSET 284 | ldx OFFSET+1 285 | jsr _SetOffset 286 | ; Read the file 287 | lda #World 290 | sta READ_DST+1 291 | lda #<(HEIGHT_WORLD * WIDTH_WORLD) 292 | ldx #>(HEIGHT_WORLD * WIDTH_WORLD) 293 | jsr _Read 294 | 295 | ; Read level actors state 296 | jsr _FindLevelActors ; compute offset and sets file read position 297 | ; Read the file 298 | lda #ActorsInLevel 301 | sta READ_DST+1 302 | lda #<(SIZEOF_ACTORS_T) 303 | ldx #>(SIZEOF_ACTORS_T) 304 | jsr _Read 305 | 306 | LoadState_end: 307 | 308 | ; Close the file 309 | lda Handle_File 310 | sta Param_FileClose+1 311 | jsr $BF00 ; call MLI 312 | .byte $CC ; Close 313 | .addr Param_FileClose 314 | 315 | rts 316 | 317 | 318 | 319 | .define WRITE_DST ZERO_2_4 ; 2 bytes 320 | ; @brief Size in in A:X (little endian) 321 | ; Destination ptr in WRITE_DST: cannot be locate in ZERO PAGE!!! 322 | ; HandleFile must have been set by opening the file 323 | _Write: 324 | 325 | ; Wr the file 326 | sta Param_FilesReadWrite+4 327 | stx Param_FilesReadWrite+5 328 | lda Handle_File 329 | sta Param_FilesReadWrite+1 330 | lda WRITE_DST 331 | sta Param_FilesReadWrite+2 332 | lda WRITE_DST+1 333 | sta Param_FilesReadWrite+3 334 | jsr $BF00 ; call MLI 335 | .byte $CB ; write 336 | .addr Param_FilesReadWrite 337 | 338 | rts 339 | 340 | 341 | ; @param Level NR in A 342 | SaveState: 343 | 344 | sta Lvl_Nr 345 | 346 | ; Open the file 347 | lda #Str_FileStates 350 | sta Param_FileOpen+2 351 | jsr $BF00 ; call MLI 352 | .byte $C8 ; Open 353 | .addr Param_FileOpen 354 | 355 | lda Lvl_Nr 356 | jsr _FindLevelLayout ; compute offset and sets file write position 357 | 358 | ; Write the file 359 | lda #TRUE 360 | sta ReadWriteBuffer 361 | lda Tile_player_standing_actor 362 | sta ReadWriteBuffer+1 363 | lda #ReadWriteBuffer 366 | sta WRITE_DST+1 367 | lda #MIN_READ_SIZE 369 | jsr _Write 370 | 371 | ; Write the level's layout 372 | ; Set write position 373 | ADD16 OFFSET, #2, #0 ; offset past "visited" & "tile" 374 | lda OFFSET 375 | ldx OFFSET+1 376 | jsr _SetOffset 377 | ; Write the file 378 | lda #World 381 | sta WRITE_DST+1 382 | lda #<(HEIGHT_WORLD * WIDTH_WORLD) 383 | ldx #>(HEIGHT_WORLD * WIDTH_WORLD) 384 | jsr _Write 385 | 386 | ; Write level actors state 387 | jsr _FindLevelActors ; compute offset and sets file write position 388 | ; Write the file 389 | lda #ActorsInLevel 392 | sta WRITE_DST+1 393 | lda #<(SIZEOF_ACTORS_T) 394 | ldx #>(SIZEOF_ACTORS_T) 395 | jsr _Write 396 | 397 | ; Close the file 398 | lda Handle_File 399 | sta Param_FileClose+1 400 | jsr $BF00 ; call MLI 401 | .byte $CC ; Close 402 | .addr Param_FileClose 403 | 404 | rts 405 | 406 | ; @param LevelNr in A 407 | ResetIsBuilt: 408 | 409 | sta Lvl_Nr 410 | 411 | ; Open the file 412 | lda #Str_FileStates 415 | sta Param_FileOpen+2 416 | jsr $BF00 ; call MLI 417 | .byte $C8 ; Open 418 | .addr Param_FileOpen 419 | 420 | for_each_lvl: 421 | dec Lvl_Nr 422 | jsr _FindLevelLayout ; compute offset and sets file write position 423 | inc Lvl_Nr 424 | 425 | lda #FALSE 426 | sta ReadWriteBuffer 427 | lda #ReadWriteBuffer 430 | sta WRITE_DST+1 431 | lda #MIN_READ_SIZE 433 | jsr _Write 434 | 435 | dec Lvl_Nr 436 | bne for_each_lvl 437 | 438 | ; Close the file 439 | lda Handle_File 440 | sta Param_FileClose+1 441 | jsr $BF00 ; call MLI 442 | .byte $CC ; Close 443 | .addr Param_FileClose 444 | 445 | rts 446 | 447 | 448 | 449 | LoadCurrentLevel: 450 | 451 | ; Open the file 452 | lda #Str_FileStates 455 | sta Param_FileOpen+2 456 | jsr $BF00 ; call MLI 457 | .byte $C8 ; Open 458 | .addr Param_FileOpen 459 | 460 | ; load the current level 461 | lda #(LVLS_HEADER_SIZE) 462 | ldx #0 463 | jsr _SetOffset ; current lvl 464 | lda #ReadWriteBuffer 467 | sta READ_DST+1 468 | lda #MIN_READ_SIZE 470 | jsr _Read 471 | lda ReadWriteBuffer 472 | sta NextLevel 473 | 474 | ; Close the file 475 | lda Handle_File 476 | sta Param_FileClose+1 477 | jsr $BF00 ; call MLI 478 | .byte $CC ; Close 479 | .addr Param_FileClose 480 | 481 | rts -------------------------------------------------------------------------------- /src/io/files.inc: -------------------------------------------------------------------------------- 1 | ; Copyright (C) 2021 Christophe Meneboeuf 2 | ; 3 | ; This program is free software: you can redistribute it and/or modify 4 | ; it under the terms of the GNU General Public License as published by 5 | ; the Free Software Foundation, either version 3 of the License, or 6 | ; (at your option) any later version. 7 | ; 8 | ; This program is distributed in the hope that it will be useful, 9 | ; but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | ; GNU General Public License for more details. 12 | ; 13 | ; You should have received a copy of the GNU General Public License 14 | ; along with this program. If not, see . 15 | 16 | 17 | ; data 18 | ; TODO : should remain private!! 19 | .import Str_FileLevelConfs 20 | .import Str_FileLevelsActors 21 | .import Param_FileOpen 22 | .import Param_FileOffset 23 | .import Param_FilesReadWrite 24 | 25 | ; functions 26 | .import ReadFile 27 | .import LoadState 28 | .import SaveState 29 | .import ResetIsBuilt 30 | .import LoadCurrentLevel 31 | -------------------------------------------------------------------------------- /src/io/gr.asm: -------------------------------------------------------------------------------- 1 | ; Copyright (C) 2019 Christophe Meneboeuf 2 | ; 3 | ; This program is free software: you can redistribute it and/or modify 4 | ; it under the terms of the GNU General Public License as published by 5 | ; the Free Software Foundation, either version 3 of the License, or 6 | ; (at your option) any later version. 7 | ; 8 | ; This program is distributed in the hope that it will be useful, 9 | ; but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | ; GNU General Public License for more details. 12 | ; 13 | ; You should have received a copy of the GNU General Public License 14 | ; along with this program. If not, see . 15 | 16 | 17 | .export Clear_Gr1 18 | 19 | 20 | .CODE 21 | 22 | ;@brief Clears GR1 screen 23 | ; BEWARE : It will fill the "holes" with 0s 24 | Clear_Gr1: 25 | 26 | lda #0 ; black 27 | ldx #0 28 | clear_gr1_1: 29 | sta $400, X 30 | inx 31 | bne clear_gr1_1 32 | ldx #0 33 | clear_gr1_2: 34 | sta $500, X 35 | inx 36 | bne clear_gr1_2 37 | ldx #0 38 | clear_gr1_3: 39 | sta $600, X 40 | inx 41 | bne clear_gr1_3 42 | ldx #0 43 | clear_gr1_4: 44 | sta $700, X 45 | inx 46 | bne clear_gr1_4 47 | 48 | rts -------------------------------------------------------------------------------- /src/io/gr.inc: -------------------------------------------------------------------------------- 1 | .import Clear_Gr1 -------------------------------------------------------------------------------- /src/io/textio.asm: -------------------------------------------------------------------------------- 1 | ; Copyright (C) 2019 Christophe Meneboeuf 2 | ; 3 | ; This program is free software: you can redistribute it and/or modify 4 | ; it under the terms of the GNU General Public License as published by 5 | ; the Free Software Foundation, either version 3 of the License, or 6 | ; (at your option) any later version. 7 | ; 8 | ; This program is distributed in the hope that it will be useful, 9 | ; but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | ; GNU General Public License for more details. 12 | ; 13 | ; You should have received a copy of the GNU General Public License 14 | ; along with this program. If not, see . 15 | 16 | 17 | .include "../memory.inc" 18 | 19 | .export CIN_STR 20 | .export Cin_Str 21 | .export Cin_Char 22 | .export Print 23 | .export ClearTxt 24 | 25 | 26 | .define SPACE $A0 27 | .define CURSOR $5F 28 | .define RETURN $8D 29 | .define DELETE $FF 30 | .define LEFT $88 31 | 32 | 33 | .DATA 34 | 35 | CIN_STR: 36 | .byte 0 37 | .res 40 ; input string buffer char[40] 38 | 39 | 40 | .CODE 41 | 42 | ; brief Clears the 4 line in text zone (mixed mode) 43 | ClearTxt: 44 | ldx #39 45 | lda #SPACE 46 | loop_clear_lines: 47 | sta TXT1_LINE20, X 48 | sta TXT1_LINE21, X 49 | sta TXT1_LINE22, X 50 | sta TXT1_LINE23, X 51 | dex 52 | bpl loop_clear_lines 53 | rts 54 | 55 | 56 | ; brief Scrolls the lines in text zone (mixed mode) 57 | _ScrollTxt: 58 | ldx #39 59 | loop_scroll_lines: 60 | lda TXT1_LINE21, X 61 | sta TXT1_LINE20, X 62 | lda TXT1_LINE22, X 63 | sta TXT1_LINE21, X 64 | lda TXT1_LINE23, X 65 | sta TXT1_LINE22, X 66 | lda #SPACE 67 | sta TXT1_LINE23, X 68 | dex 69 | bpl loop_scroll_lines 70 | rts 71 | 72 | 73 | .define ADDR_STR ZERO_7_1 ; 2 bytes same as FAC1 and FAC2 74 | ; @brief Print the null terminated str 75 | ; @param A,X Adress of the string (hi, low) 76 | Print: 77 | stx ADDR_STR 78 | sta ADDR_STR+1 79 | 80 | jsr _ScrollTxt 81 | 82 | ldy #0 83 | loop_print_str: 84 | lda (ADDR_STR), Y 85 | cmp #0 86 | beq break_print_str 87 | sta TXT1_LINE23, Y 88 | iny 89 | cpy #40 90 | bne loop_print_str 91 | break_print_str: 92 | 93 | rts 94 | 95 | .macro INC_SEED 96 | clc 97 | lda SEED0 98 | adc #1 99 | sta SEED0 100 | lda SEED1 101 | adc #0 102 | sta SEED1 103 | lda SEED2 104 | adc #0 105 | sta SEED2 106 | lda SEED3 107 | adc #0 108 | sta SEED3 109 | .endmacro 110 | 111 | 112 | ; @brief Gets an input string into the CIN_STR buffer 113 | ; @details It will also init the seed 114 | Cin_Str: 115 | 116 | jsr _ScrollTxt 117 | 118 | ; position the cursor 119 | ldx #0 120 | lda #CURSOR 121 | sta TXT1_LINE23, X 122 | 123 | ; wait for keyboard 124 | cin_str_kbd_loop: 125 | INC_SEED 126 | lda KEYBD 127 | bpl cin_str_kbd_loop ; bit #8 is set when a character is present (thus A < 0) 128 | sta KEYBD_STROBE 129 | 130 | cmp #RETURN 131 | beq break_cin ; break if "Return" 132 | 133 | cmp #DELETE 134 | beq delete 135 | 136 | cmp #LEFT 137 | beq delete 138 | 139 | ; print key and avance the cursor 140 | sta TXT1_LINE23, X 141 | sta CIN_STR, X 142 | inx 143 | cpx #40 ; eol -> ends anyway 144 | beq break_cin 145 | lda #CURSOR 146 | sta TXT1_LINE23, X 147 | jmp cin_str_kbd_loop 148 | 149 | break_cin: 150 | lda #SPACE 151 | sta TXT1_LINE23, X ; erase the cursor 152 | lda #0 153 | sta CIN_STR, X 154 | 155 | rts 156 | 157 | delete: 158 | cpx #0 ; cannot delete when at pos 0 159 | beq cin_str_kbd_loop 160 | lda #SPACE 161 | sta TXT1_LINE23, X 162 | dex 163 | lda #0 164 | sta CIN_STR, X 165 | lda #CURSOR 166 | sta TXT1_LINE23, X 167 | jmp cin_str_kbd_loop 168 | 169 | 170 | 171 | ; @brief Gets an input char 172 | ; @detals Entered char in A 173 | Cin_Char: 174 | 175 | jsr _ScrollTxt 176 | 177 | lda #CURSOR 178 | sta TXT1_LINE23 179 | 180 | ; wait for keyboard 181 | cin_char_kbd_loop: 182 | lda KEYBD 183 | bpl cin_char_kbd_loop ; bit #8 is set when a character is present (thus A < 0) 184 | sta KEYBD_STROBE 185 | sta TXT1_LINE23 186 | 187 | rts 188 | -------------------------------------------------------------------------------- /src/io/textio.inc: -------------------------------------------------------------------------------- 1 | .import Print 2 | .import ClearTxt 3 | .import Cin_Str 4 | .import Cin_Char 5 | 6 | .import CIN_STR 7 | 8 | 9 | ; --- string with high-bit set, null-terminated 10 | ; Apple II char < 128 are inverted 11 | .macro ASCIIZ str 12 | .repeat .strlen (str), c 13 | .byte .strat (str, c) | $80 14 | .endrep 15 | .byte 0 16 | .endmacro 17 | 18 | 19 | .macro PRINT string 20 | lda #>string 21 | ldx # 2 | ; 3 | ; This program is free software: you can redistribute it and/or modify 4 | ; it under the terms of the GNU General Public License as published by 5 | ; the Free Software Foundation, either version 3 of the License, or 6 | ; (at your option) any later version. 7 | ; 8 | ; This program is distributed in the hope that it will be useful, 9 | ; but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | ; GNU General Public License for more details. 12 | ; 13 | ; You should have received a copy of the GNU General Public License 14 | ; along with this program. If not, see . 15 | 16 | .include "../monitor.inc" 17 | .include "../memory.inc" 18 | .include "../io/textio.inc" 19 | .include "gr.inc" 20 | 21 | .export Title 22 | 23 | 24 | .DATA 25 | GR_TITLE_00 : .byte $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0 26 | GR_TITLE_01 : .byte $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0 27 | GR_TITLE_02 : .byte $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0 28 | GR_TITLE_03 : .byte $0,$0,$99,$99,$99,$99,$99,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$99,$99,$99,$99,$99,$0 29 | GR_TITLE_04 : .byte $0,$0,$99,$99,$99,$99,$99,$0,$0,$88,$88,$88,$88,$0,$0,$88,$88,$88,$88,$0,$0,$0,$88,$88,$0,$0,$0,$88,$88,$88,$88,$88,$0,$0,$99,$99,$99,$99,$99,$0 30 | GR_TITLE_05 : .byte $0,$0,$99,$0,$0,$0,$0,$0,$88,$0,$0,$0,$0,$0,$88,$0,$0,$0,$0,$0,$0,$88,$0,$0,$88,$0,$0,$88,$0,$0,$0,$0,$88,$0,$99,$0,$0,$0,$0,$0 31 | GR_TITLE_06 : .byte $0,$0,$99,$0,$0,$0,$0,$0,$88,$0,$0,$0,$0,$0,$88,$0,$0,$0,$0,$0,$88,$0,$0,$0,$0,$88,$0,$88,$0,$0,$0,$0,$88,$0,$99,$0,$0,$0,$0,$0 32 | GR_TITLE_07 : .byte $0,$0,$99,$99,$99,$0,$0,$0,$88,$88,$88,$88,$0,$0,$88,$0,$0,$0,$0,$0,$88,$0,$0,$0,$0,$88,$0,$88,$88,$88,$88,$88,$0,$0,$99,$99,$99,$0,$0,$0 33 | GR_TITLE_08 : .byte $0,$0,$99,$0,$0,$0,$0,$0,$0,$0,$0,$0,$88,$0,$88,$0,$0,$0,$0,$0,$88,$88,$88,$88,$88,$88,$0,$88,$0,$0,$0,$0,$0,$0,$99,$0,$0,$0,$0,$0 34 | GR_TITLE_09 : .byte $0,$0,$99,$0,$0,$0,$0,$0,$0,$0,$0,$0,$88,$0,$88,$0,$0,$0,$0,$0,$88,$0,$0,$0,$0,$88,$0,$88,$0,$0,$0,$0,$0,$0,$99,$0,$0,$0,$0,$0 35 | GR_TITLE_10 : .byte $0,$0,$99,$99,$99,$99,$99,$0,$88,$0,$0,$0,$88,$0,$88,$0,$0,$0,$0,$0,$88,$0,$0,$0,$0,$88,$0,$88,$0,$0,$0,$0,$0,$0,$99,$99,$99,$99,$99,$0 36 | GR_TITLE_11 : .byte $0,$0,$99,$99,$99,$99,$99,$0,$88,$88,$88,$88,$0,$0,$0,$88,$88,$88,$88,$0,$88,$0,$0,$0,$0,$88,$0,$88,$0,$0,$0,$0,$0,$0,$99,$99,$99,$99,$99,$0 37 | GR_TITLE_12 : .byte $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0 38 | GR_TITLE_13 : .byte $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0 39 | GR_TITLE_14 : .byte $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$55,$55,$55,$55,$0,$0,$55,$55,$55,$55,$55,$55,$0,$55,$55,$55,$55,$55,$55,$0,$55,$55,$55,$55,$55,$0,$0,$0 40 | GR_TITLE_15 : .byte $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$55,$0,$0,$0,$0,$55,$0,$0,$0,$0,$0,$0,$55,$0,$55,$0,$0,$0,$0,$0,$0,$55,$0,$0,$0,$0,$55,$0,$0 41 | GR_TITLE_16 : .byte $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$55,$55,$55,$55,$55,$55,$0,$55,$55,$55,$55,$55,$55,$0,$55,$55,$55,$55,$0,$0,$0,$55,$55,$55,$55,$55,$0,$0,$0 42 | GR_TITLE_17 : .byte $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$55,$0,$0,$0,$0,$55,$0,$55,$0,$0,$0,$0,$0,$0,$55,$0,$0,$0,$0,$0,$0,$55,$0,$0,$0,$0,$0,$0,$0 43 | GR_TITLE_18 : .byte $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$55,$0,$0,$0,$0,$55,$0,$55,$55,$55,$55,$55,$55,$0,$55,$0,$0,$0,$0,$0,$0,$55,$0,$0,$0,$0,$0,$0,$0 44 | GR_TITLE_19 : .byte $0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0,$0 45 | 46 | 47 | .CODE 48 | 49 | 50 | Scroll: 51 | 52 | ldx #39 53 | loop_scroll: 54 | lda TXT1_LINE1, X 55 | sta TXT1_LINE0, X 56 | lda TXT1_LINE2, X 57 | sta TXT1_LINE1, X 58 | lda TXT1_LINE3, X 59 | sta TXT1_LINE2, X 60 | lda TXT1_LINE4, X 61 | sta TXT1_LINE3, X 62 | lda TXT1_LINE5, X 63 | sta TXT1_LINE4, X 64 | lda TXT1_LINE6, X 65 | sta TXT1_LINE5, X 66 | lda TXT1_LINE7, X 67 | sta TXT1_LINE6, X 68 | lda TXT1_LINE8, X 69 | sta TXT1_LINE7, X 70 | lda TXT1_LINE9, X 71 | sta TXT1_LINE8, X 72 | lda TXT1_LINE10, X 73 | sta TXT1_LINE9, X 74 | lda TXT1_LINE11, X 75 | sta TXT1_LINE10, X 76 | lda TXT1_LINE12, X 77 | sta TXT1_LINE11, X 78 | lda TXT1_LINE13, X 79 | sta TXT1_LINE12, X 80 | lda TXT1_LINE14, X 81 | sta TXT1_LINE13, X 82 | lda TXT1_LINE15, X 83 | sta TXT1_LINE14, X 84 | lda TXT1_LINE16, X 85 | sta TXT1_LINE15, X 86 | lda TXT1_LINE17, X 87 | sta TXT1_LINE16, X 88 | lda TXT1_LINE18, X 89 | sta TXT1_LINE17, X 90 | lda TXT1_LINE19, X 91 | sta TXT1_LINE18, X 92 | dex 93 | bpl loop_scroll 94 | 95 | rts 96 | 97 | .DATA 98 | Title_Scr_Addr: 99 | .word GR_TITLE_01, GR_TITLE_02, GR_TITLE_03, GR_TITLE_04, GR_TITLE_05, GR_TITLE_06, GR_TITLE_07, GR_TITLE_08, GR_TITLE_09, GR_TITLE_10 100 | .word GR_TITLE_11, GR_TITLE_12, GR_TITLE_13, GR_TITLE_14, GR_TITLE_15, GR_TITLE_16, GR_TITLE_17, GR_TITLE_18, GR_TITLE_19 101 | 102 | .CODE 103 | ; @brief Displays's the title screen and main game menu 104 | Title: 105 | 106 | ; Title Screen 107 | jsr Clear_Gr1 108 | jsr ClearTxt 109 | 110 | sta $C054 ; page 1 111 | sta $C056 ; lores 112 | sta $C050 ; gfx 113 | sta $C053 ; mixed mode on 114 | 115 | ldy #$FE 116 | loop_scrolling: 117 | lda #$A2 ; wait for 66ms 118 | jsr WAIT 119 | ldx #39 120 | loop_lines: 121 | lda GR_TITLE_00, X 122 | sta TXT1_LINE19, X 123 | dex 124 | bpl loop_lines 125 | jsr Scroll 126 | iny 127 | iny 128 | lda Title_Scr_Addr, Y 129 | sta loop_lines+1 130 | lda Title_Scr_Addr+1, Y 131 | sta loop_lines+2 132 | cpy #$26 133 | bne loop_scrolling 134 | 135 | rts -------------------------------------------------------------------------------- /src/main.asm: -------------------------------------------------------------------------------- 1 | 2 | ; Copyright (C) 2018 Christophe Meneboeuf 3 | ; 4 | ; This program is free software: you can redistribute it and/or modify 5 | ; it under the terms of the GNU General Public License as published by 6 | ; the Free Software Foundation, either version 3 of the License, or 7 | ; (at your option) any later version. 8 | ; 9 | ; This program is distributed in the hope that it will be useful, 10 | ; but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | ; GNU General Public License for more details. 13 | ; 14 | ; You should have received a copy of the GNU General Public License 15 | ; along with this program. If not, see . 16 | 17 | .include "random.inc" 18 | .include "memory.inc" 19 | .include "monitor.inc" 20 | .include "display_map.inc" 21 | .include "builder/builder.inc" 22 | .include "io/textio.inc" 23 | .include "io/files.inc" 24 | 25 | .export _main 26 | 27 | ; functions 28 | .import world_init 29 | .import player_init 30 | .import view_init 31 | .import game_loop 32 | .import Title 33 | .import level_reset_states 34 | 35 | .import memcpy 36 | .import memset 37 | .import meminit 38 | 39 | ; data 40 | .import __CODE2_LOAD__ 41 | .import __CODE2_RUN__ 42 | .import __CODE2_SIZE__ 43 | .import __RODATA_LOAD__ 44 | .import __RODATA_RUN__ 45 | .import __RODATA_SIZE__ 46 | .import __DATA_LOAD__ 47 | .import __DATA_RUN__ 48 | .import __DATA_SIZE__ 49 | 50 | .DATA 51 | 52 | STR_EMPTY: ASCIIZ " " 53 | STR_NEWGAME: ASCIIZ "(N)EW GAME" 54 | STR_JOURNEY: ASCIIZ "(J)OURNEY ONWARD" 55 | STR_NAME: ASCIIZ "WHAT'S YOUR NAME, ADVENTURER?" 56 | 57 | .CODE 58 | 59 | _main: 60 | 61 | ; jsr meminit // Uncomment to zero memory, for debug 62 | 63 | ; Relocations after the HGR "hole" 64 | ; relocating code 65 | lda #<__CODE2_LOAD__ 66 | sta FROM 67 | lda #>__CODE2_LOAD__ 68 | sta FROM+1 69 | lda #<__CODE2_RUN__ 70 | sta TO 71 | lda #>__CODE2_RUN__ 72 | sta TO+1 73 | lda #<__CODE2_SIZE__ 74 | sta SIZEL 75 | lda #>__CODE2_SIZE__ 76 | sta SIZEH 77 | jsr memcpy 78 | ; relocating RODATA 79 | lda #<__RODATA_LOAD__ 80 | sta FROM 81 | lda #>__RODATA_LOAD__ 82 | sta FROM+1 83 | lda #<__RODATA_RUN__ 84 | sta TO 85 | lda #>__RODATA_RUN__ 86 | sta TO+1 87 | lda #<__RODATA_SIZE__ 88 | sta SIZEL 89 | lda #>__RODATA_SIZE__ 90 | sta SIZEH 91 | jsr memcpy 92 | ; relocating DATA 93 | lda #<__DATA_LOAD__ 94 | sta FROM 95 | lda #>__DATA_LOAD__ 96 | sta FROM+1 97 | lda #<__DATA_RUN__ 98 | sta TO 99 | lda #>__DATA_RUN__ 100 | sta TO+1 101 | lda #<__DATA_SIZE__ 102 | sta SIZEL 103 | lda #>__DATA_SIZE__ 104 | sta SIZEH 105 | jsr memcpy 106 | 107 | jsr _StartMenu ; will init the seed 108 | 109 | ; overwrite the seed to debug 110 | ; lda #$0 111 | ; sta SEED0 112 | ; lda #$0 113 | ; sta SEED1 114 | ; lda #$0 115 | ; sta SEED2 116 | ; lda #$0 117 | ; sta SEED3 118 | 119 | jsr Random8_Init 120 | 121 | ; Run 122 | jsr game_loop 123 | 124 | rts 125 | 126 | 127 | ; @brief Starting game menu 128 | _StartMenu: 129 | 130 | ; Scrolling Title 131 | jsr Title 132 | 133 | ; New game or continue 134 | lda #>STR_NEWGAME 135 | ldx #STR_JOURNEY 138 | ldx #STR_NAME 165 | ldx # 2 | ; 3 | ; This program is free software: you can redistribute it and/or modify 4 | ; it under the terms of the GNU General Public License as published by 5 | ; the Free Software Foundation, either version 3 of the License, or 6 | ; (at your option) any later version. 7 | ; 8 | ; This program is distributed in the hope that it will be useful, 9 | ; but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | ; GNU General Public License for more details. 12 | ; 13 | ; You should have received a copy of the GNU General Public License 14 | ; along with this program. If not, see . 15 | 16 | 17 | .define FAC1 ZERO_7_1 18 | .define FAC2 ZERO_7_2 19 | 20 | .import mul8 21 | .import Modulus 22 | .import div8 23 | 24 | ; Inverts a positive number to its 2's complement counterpart 25 | ; The bits are all reversed then one is added 26 | ; param : number to be inverted in in A 27 | ; result : in A 28 | .macro NEG 29 | eor #$FF ; %11111111 to reverse bits 30 | clc 31 | adc #$01 32 | .endmacro 33 | 34 | 35 | .macro ADD16 addr, csteL, csteH 36 | clc 37 | lda addr 38 | adc csteL 39 | sta addr 40 | lda addr+1 41 | adc csteH 42 | sta addr+1 43 | .endmacro 44 | 45 | 46 | .macro DEC16 addr, cste 47 | sec 48 | lda addr 49 | sbc cste 50 | sta addr 51 | lda addr+1 52 | sbc #0 53 | sta addr+1 54 | .endmacro 55 | 56 | -------------------------------------------------------------------------------- /src/memory.asm: -------------------------------------------------------------------------------- 1 | 2 | ; Copyright (C) 2019 Christophe Meneboeuf 3 | ; 4 | ; This program is free software: you can redistribute it and/or modify 5 | ; it under the terms of the GNU General Public License as published by 6 | ; the Free Software Foundation, either version 3 of the License, or 7 | ; (at your option) any later version. 8 | ; 9 | ; This program is distributed in the hope that it will be useful, 10 | ; but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | ; GNU General Public License for more details. 13 | ; 14 | ; You should have received a copy of the GNU General Public License 15 | ; along with this program. If not, see . 16 | 17 | .include "memory.inc" 18 | 19 | .export memcpy 20 | .export memset 21 | .export meminit 22 | 23 | 24 | .CODE 25 | 26 | ; http://www.6502.org/source/general/memory_move.html 27 | ; Move memory up 28 | ; 29 | ; FROM = source start address 30 | ; TO = destination start address 31 | ; SIZE = number of bytes to move 32 | ; 33 | memcpy: LDX SIZEH ; the last byte must be moved first 34 | CLC ; start at the final pages of FROM and TO 35 | TXA 36 | ADC FROM+1 37 | STA FROM+1 38 | CLC 39 | TXA 40 | ADC TO+1 41 | STA TO+1 42 | INX ; allows the use of BNE after the DEX below 43 | LDY SIZEL 44 | BEQ MU3 45 | DEY ; move bytes on the last page first 46 | BEQ MU2 47 | MU1: LDA (FROM),Y 48 | STA (TO),Y 49 | DEY 50 | BNE MU1 51 | MU2: LDA (FROM),Y ; handle Y = 0 separately 52 | STA (TO),Y 53 | MU3: DEY 54 | DEC FROM+1 ; move the next page (if any) 55 | DEC TO+1 56 | DEX 57 | BNE MU1 58 | RTS 59 | 60 | ; Sets a block of memory to the provided value 61 | ; 62 | ; A = value to set 63 | ; TO = memory to be set starting address 64 | ; SIZE = number of bytes to set. Max value: $FEFF 65 | ; 66 | ; !!! TO and SIZE are overwritten !! 67 | memset: 68 | cmp SIZEH 69 | beq memset_remain 70 | memset_loop_hi: 71 | ldy #$FF 72 | memset_loop_low: 73 | sta (TO),Y 74 | dey 75 | bne memset_loop_low 76 | sta (TO),Y 77 | inc TO+1 ; next 256 byte block 78 | dec SIZEH 79 | bne memset_loop_hi 80 | memset_remain: 81 | ldy SIZEL 82 | cpy #0 83 | beq memset_end 84 | memset_loop_remain: 85 | sta (TO),Y 86 | dey 87 | bne memset_loop_remain 88 | memset_end: 89 | rts 90 | 91 | ; DEBUG: zeros the useful memory locations 92 | meminit: 93 | 94 | lda #0 95 | sta TO 96 | ldx #$60 97 | stx TO+1 98 | ldx #$FF 99 | stx SIZEL 100 | ldx #$05 101 | stx SIZEH 102 | jsr memset 103 | 104 | lda #0 105 | sta ZERO_2_1 106 | sta ZERO_2_2 107 | sta ZERO_2_3 108 | sta ZERO_2_4 109 | sta ZERO_2_5 110 | sta ZERO_2_6 111 | sta ZERO_3 112 | sta ZERO_4_1 113 | sta ZERO_4_2 114 | sta ZERO_4_3 115 | sta ZERO_4_4 116 | sta ZERO_4_5 117 | sta ZERO_5_1 118 | sta ZERO_5_2 119 | sta ZERO_5_3 120 | sta ZERO_5_4 121 | sta ZERO_5_5 122 | sta ZERO_5_6 123 | sta ZERO_7_1 124 | sta ZERO_7_2 125 | sta ZERO_8_1 126 | sta ZERO_8_2 127 | sta ZERO_9_1 128 | sta ZERO_9_2 129 | sta ZERO_9_3 130 | sta ZERO_9_4 131 | sta ZERO_9_5 132 | sta ZERO_9_6 133 | sta ZERO_9_7 134 | sta ZERO_9_8 135 | sta ZERO_9_9 136 | sta ZERO_9_10 137 | 138 | rts -------------------------------------------------------------------------------- /src/memory.inc: -------------------------------------------------------------------------------- 1 | 2 | ; Copyright (C) 2019 Christophe Meneboeuf 3 | ; 4 | ; This program is free software: you can redistribute it and/or modify 5 | ; it under the terms of the GNU General Public License as published by 6 | ; the Free Software Foundation, either version 3 of the License, or 7 | ; (at your option) any later version. 8 | ; 9 | ; This program is distributed in the hope that it will be useful, 10 | ; but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | ; GNU General Public License for more details. 13 | ; 14 | ; You should have received a copy of the GNU General Public License 15 | ; along with this program. If not, see . 16 | 17 | 18 | ; ********* ZERO PAGE ********* 19 | ; Free locations in Zero Page: not used by either 20 | ; the Monitor, Applesoft, Integer Basic, DOS3.3 or PRODOS 21 | 22 | ; reserved locations 23 | .define RESERVED01 $52 24 | 25 | ; locations for the random generator 26 | ; little endian 27 | .define SEED0 $6 28 | .define SEED1 $7 29 | .define SEED2 $8 30 | .define SEED3 $9 31 | 32 | .define ZERO_2_1 $19 33 | .define ZERO_2_2 $1A 34 | .define ZERO_2_3 $1B 35 | .define ZERO_2_4 $1C 36 | .define ZERO_2_5 $1D 37 | .define ZERO_2_6 $1E 38 | 39 | .define ZERO_3 $E3 40 | 41 | .define ZERO_4_1 $EB 42 | .define ZERO_4_2 $EC 43 | .define ZERO_4_3 $ED 44 | .define ZERO_4_4 $EE 45 | .define ZERO_4_5 $EF 46 | 47 | .define ZERO_5_1 $FA 48 | .define ZERO_5_2 $FB 49 | .define ZERO_5_3 $FC 50 | .define ZERO_5_4 $FD 51 | .define ZERO_5_5 $FE 52 | .define ZERO_5_6 $FF 53 | 54 | ; Used by Integer Basic 55 | 56 | .define ZERO_7_1 $CE 57 | .define ZERO_7_2 $CF 58 | 59 | .define ZERO_8_1 $D6 60 | .define ZERO_8_2 $D7 61 | 62 | .define ZERO_9_1 $F0 63 | .define ZERO_9_2 $F1 64 | .define ZERO_9_3 $F2 65 | .define ZERO_9_4 $F3 66 | .define ZERO_9_5 $F4 67 | .define ZERO_9_6 $F5 68 | .define ZERO_9_7 $F6 69 | .define ZERO_9_8 $F7 70 | .define ZERO_9_9 $F8 71 | .define ZERO_9_10 $F9 72 | 73 | ; ************ I/O ************ 74 | .define KEYBD $C000 75 | .define KEYBD_STROBE $C010 76 | 77 | ; *********** HIRES ************ 78 | .define ADDR_HGR1 $2000 79 | .define ADDR_HGR2 $4000 80 | 81 | ; ********** TEXT ************* 82 | .define TXT1_LINE0 $400 83 | .define TXT1_LINE1 $480 84 | .define TXT1_LINE2 $500 85 | .define TXT1_LINE3 $580 86 | .define TXT1_LINE4 $600 87 | .define TXT1_LINE5 $680 88 | .define TXT1_LINE6 $700 89 | .define TXT1_LINE7 $780 90 | .define TXT1_LINE8 $428 91 | .define TXT1_LINE9 $4A8 92 | .define TXT1_LINE10 $528 93 | .define TXT1_LINE11 $5A8 94 | .define TXT1_LINE12 $628 95 | .define TXT1_LINE13 $6A8 96 | .define TXT1_LINE14 $728 97 | .define TXT1_LINE15 $7A8 98 | .define TXT1_LINE16 $450 99 | .define TXT1_LINE17 $4D0 100 | .define TXT1_LINE18 $550 101 | .define TXT1_LINE19 $5D0 102 | .define TXT1_LINE20 $650 103 | .define TXT1_LINE21 $6D0 104 | .define TXT1_LINE22 $750 105 | .define TXT1_LINE23 $7D0 106 | 107 | .import TXT1_LINES 108 | 109 | 110 | ; *********** CUSTOM ROUTINES ********* 111 | .define FROM ZERO_2_1 ; 2 bytes 112 | .define TO ZERO_2_3 ; 2 bytes 113 | .define SIZEH ZERO_8_2 114 | .define SIZEL ZERO_8_1 115 | 116 | 117 | ; ************ MACROS *********** 118 | 119 | ; pushes addr to the stack 120 | ; A is detroyed 121 | .macro PUSH addr 122 | lda addr 123 | pha 124 | .endmacro 125 | 126 | 127 | ; pops addr from the stack 128 | ; A is detroyed 129 | .macro POP addr 130 | pla 131 | sta addr 132 | .endmacro -------------------------------------------------------------------------------- /src/monitor.inc: -------------------------------------------------------------------------------- 1 | ; Copyright (C) 2018 Christophe Meneboeuf 2 | ; 3 | ; This program is free software: you can redistribute it and/or modify 4 | ; it under the terms of the GNU General Public License as published by 5 | ; the Free Software Foundation, either version 3 of the License, or 6 | ; (at your option) any later version. 7 | ; 8 | ; This program is distributed in the hope that it will be useful, 9 | ; but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | ; GNU General Public License for more details. 12 | ; 13 | ; You should have received a copy of the GNU General Public License 14 | ; along with this program. If not, see . 15 | 16 | 17 | ; ****************************************************** 18 | ; This file contains aliases for Montitor's and Applesoft's routines 19 | ; ****************************************************** 20 | 21 | ; ++++ ZERO PAGE ++++ 22 | 23 | .define WNDLFT $20 ; $20 Left edge of text window 24 | .define WNDWDTH WNDLFT+1 ; $21 Width of text window 25 | .define WNDTOP WNDWDTH+1 ; $22 Top of text window 26 | .define WNDBTM WNDTOP+1 ; $23 Bottom+1 of text window 27 | .define CH WNDBTM+1 ; $24 Cursor horizontal position 28 | .define CV CH+1 ; $25 Cursor vertical position 29 | .define BASL $28 ; $28 Text base address 30 | .define BASH BASL+1 ; $29 31 | .define INVFLG $32 ; $32 Normal/Inverse/Flash 32 | .define PROMPT INVFLG+1 ; $33 Prompt character 33 | .define CSWL $36 ; $36 Character output hook 34 | .define CSWH CSWL+1 ; $37 35 | .define KSWL CSWH+1 ; $38 Character input hook 36 | .define KSWH KSWL+1 ; $39 37 | 38 | 39 | ; ++++ Monitor ++++ 40 | 41 | .define INIT $fb2f ; Initialize text screen 42 | .define SETTXT $fb39 ; Set text mode 43 | .define SETWND $fb4b ; Set text window size 44 | .define SETWND2 $fb51 ; Set text window width and bottom size 45 | .define TABV $fb5b ; Vertical tab 46 | .define BASCALC $fbc1 ; Text base-address calculator 47 | .define STORADV $fbf0 ; Place a printable character on the screen 48 | .define ADVANCE $fbf4 ; Increment the cursor position 49 | .define VIDOUT $fbfd ; Place a character on the screen 50 | .define VTAB $fc22 ; Vertical tab 51 | .define VTABZ $fc24 ; Vertical tab (alternate entry) 52 | .define HOME $fc58 ; Home cursor and clear to end of page 53 | .define WAIT $fca8 ; wait : read "Assembly Cookbook for the Apple II/IIe" 54 | .define RDKEY $fd0c ; Get an input character 55 | .define CROUT $fd8e ; Issue a carriage return 56 | .define PRBYTE $fdda ; Print a hexadecimal byte 57 | .define COUT $fded ; Output a character 58 | .define COUT1 $fdf0 ; Output a character to the screen 59 | .define SETINV $fe80 ; Set inverse text mode 60 | .define SETNORM $fe84 ; Set normal text mode 61 | .define SETKBD $fe89 ; Reset input to keyboard 62 | .define SETVID $fe93 ; Reset output to screen 63 | 64 | 65 | ; ++++ HIRES GFX ++++ 66 | 67 | ; Colors 68 | ; SET #1 69 | .define BLACK1 $0 70 | .define GREEN $1 71 | .define VIOLET $2 72 | .define WHITE1 $3 73 | ; SET #2 74 | .define BLACK2 $4 75 | .define ORANGE $5 76 | .define BLUE $6 77 | .define WHITE2 $7 78 | 79 | .define HGR $F3E2 80 | ; Initializes to hi-res page 1, clears screen. 81 | 82 | .define HGR2 $F3D8 83 | ; Initializes to hi-res page 2, clears screen. 84 | 85 | .define HCLR $F3F2 86 | ; Clears current screen to black1. 87 | 88 | .define BKGND $F3F6 89 | ; Clears current screen to last plotted HCOLOR. 90 | 91 | .define HCOLOR $F6F0 92 | ; Sets HCOLOR to contents of X-Register (0−7). 93 | 94 | .define HPOSN $F411 95 | ; Positions hi-res “cursor” without plotting. Enter with 96 | ; X, Y (low, high) = horizontal position, Accumulator = 97 | ; vertical position. 98 | 99 | .define HPLOT $F457 100 | ; Identical to HPOSN, but plots current HCOLOR at coordinates 101 | ; given. 102 | 103 | .define HFIND $F5CB 104 | ; Returns current “cursor” position. Useful after aDRAW 105 | ; to find where you’ve been left. Coordinates returned in: 106 | ; $E0, $E1 = horizontal (low,high), $E2 = vertical. 107 | 108 | .define HLIN $F53A 109 | ; Draws a line from last plot to point given. Accumulator, 110 | ; X (low, high) = horizontal, Y = vertical position. 111 | 112 | .define SHNUM $F730 113 | ; Puts address of shape number indicated by X-Register 114 | ; into$1A,$1B; returns with X, Y (low, high) also set to 115 | ; address of that shape-table entry. 116 | 117 | .define DRAW $F601 118 | ; Draw shape pointed to by X, Y (low, high) in current 119 | ; HCOLOR. Note: X, Y point to specific entry,not the 120 | ; beginning of the table. Call SHNUM first 121 | 122 | .define XDRAW $F65D 123 | ; Erases shape just drawn (if there) by doing anexclusive 124 | ; OR with the screen data. Load X, Y (low, high) with 125 | ; address of shape toXDRAW or callSHNUM first with XRegister 126 | ; = shape numbe 127 | 128 | 129 | ; *********** STRINGS ************* 130 | 131 | .define GETSPACE $E452 132 | ; Reduces the start-of-strings 133 | ; pointer, FRETOP ($6F), by the 134 | ; number specified in the A-register 135 | ; (the string length) and sets up 136 | ; FRESPC ($71) so that it equals 137 | ; FRETOP. After this has been done, 138 | ; A remains unaffected and Y (high) 139 | ; and X (low) point to the beginning 140 | ; of the space. The string can 141 | ; then be moved into place in upper 142 | ; memory by using MOVESTR. 143 | 144 | 145 | .define GARBAGE $E484 146 | ; Clears out old string definitions 147 | ; that are no longer being used and 148 | ; adjusts FRETOP {$6F) accordingly. 149 | ; (Each time that a string is 150 | ; redefined, its old definition is kept 151 | ; in memory but is not used.) This 152 | ; process is called "garbage collection" 153 | ; and is performed automatically 154 | ; whenever the start-ofstrings 155 | ; address, FRETOP, comes 156 | ; close to the end-of-variables address, 157 | ; STREND {$6D). 158 | 159 | 160 | .define MOVESTR $E5E2 161 | ; Copies the string that is pointed 162 | ; to by Y (high) and X (low) and 163 | ; that has a length of A to the location 164 | ; pointed to by FRESPC 165 | ; ($71). 166 | 167 | 168 | .define FOUT $ED34 169 | ; Converts the FAC into an ASCII 170 | ; character string that represents 171 | ; the number in decimal form (like 172 | ; Applesoft's STR$ function). The 173 | ; string is followed by a $00 byte 174 | ; and is pointed to by Y (high) and 175 | ; A (low) so that STROUT can be 176 | ; used to print the string. 177 | 178 | 179 | .define STROUT $DB3A 180 | ; Prints the string pointed to by Y 181 | ; (high) and A (low). The string must 182 | ; be followed immediately by a $00 183 | ; or a $22 byte. All of these conditions 184 | ; are set up by FOUT. 185 | 186 | 187 | .define STRPRT $DB3D 188 | ; Prints the string whose 3-byte descriptor 189 | ; is pointed to by $A0/$A1. 190 | ; FRMEVL sets up such a pointer 191 | ; when calculating string formulas. 192 | -------------------------------------------------------------------------------- /src/player.asm: -------------------------------------------------------------------------------- 1 | 2 | ; Copyright (C) 2018 Christophe Meneboeuf 3 | ; 4 | ; This program is free software: you can redistribute it and/or modify 5 | ; it under the terms of the GNU General Public License as published by 6 | ; the Free Software Foundation, either version 3 of the License, or 7 | ; (at your option) any later version. 8 | ; 9 | ; This program is distributed in the hope that it will be useful, 10 | ; but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | ; GNU General Public License for more details. 13 | ; 14 | ; You should have received a copy of the GNU General Public License 15 | ; along with this program. If not, see . 16 | 17 | 18 | .include "common.inc" 19 | .include "memory.inc" 20 | .include "monitor.inc" 21 | .include "io/textio.inc" 22 | .include "world/world.inc" 23 | .include "actors/actors.inc" 24 | 25 | 26 | ; init the player's structures 27 | .export player_init 28 | 29 | ; exectutes the tile's reaction and updates the player's position if possible 30 | ; Destroys ZEROS_2_1 -> 2_5 31 | .export player_move 32 | 33 | ; All the following functions returns the new player position: 34 | ; x in X and y in Y 35 | ; They may be unmodified ;) 36 | ; DESTROY A, X, Y, ZERO_2_1, ZERO_2_2 37 | 38 | 39 | .import Compute_Maze_Addr 40 | .import Reactions_lsb 41 | .import Reactions_msb 42 | .import ActorTypes 43 | .import ActorPositions 44 | 45 | 46 | .define TO_BE_PATCHED 0 47 | .define Player_XY ActorPositions + eACTORTYPES::PLAYER 48 | 49 | .DATA 50 | 51 | STR_HIT_WALL: ASCIIZ "YOU HIT A WALL" 52 | 53 | .CODE 54 | 55 | ; @brief Player initial coords 56 | ; @param X player's x 57 | ; @param Y player's y 58 | player_init: 59 | stx Player_XY 60 | sty Player_XY+1 61 | rts 62 | 63 | ; @param X target tile's x 64 | ; @param Y target tile's y 65 | ; @return TRUE in A if the player can move to the tile, FALE otherwise 66 | .define ADDR_IN_MAZE ZERO_2_1 ; 2 bytes 67 | .define NEW_PLAYER_XY ZERO_2_4 ; 2 bytes 68 | player_move: 69 | 70 | stx NEW_PLAYER_XY 71 | sty NEW_PLAYER_XY+1 72 | 73 | jsr Compute_Maze_Addr 74 | 75 | ; get the actor id 76 | stx ADDR_IN_MAZE 77 | sta ADDR_IN_MAZE+1 78 | ldy #0 79 | lda (ADDR_IN_MAZE), Y 80 | tax 81 | 82 | ; get the actor's type 83 | lda ActorTypes, X 84 | tay 85 | 86 | ; get the reaction address 87 | lda Reactions_lsb, Y 88 | sta FUNC_REACTION + 1 89 | lda Reactions_msb, Y 90 | sta FUNC_REACTION+2 91 | 92 | FUNC_REACTION : jsr TO_BE_PATCHED ; actord id in Y 93 | 94 | cmp #TRUE 95 | bne end_player_move 96 | ldx NEW_PLAYER_XY 97 | stx Player_XY 98 | ldy NEW_PLAYER_XY+1 99 | sty Player_XY+1 100 | 101 | end_player_move: 102 | ldx Player_XY 103 | ldy Player_XY+1 104 | 105 | rts 106 | 107 | 108 | ; Common code to return from the moves. 109 | ; Moves BRANCH to here 110 | return_from_player_move: 111 | 112 | ; return player's coordinates 113 | ldx Player_XY 114 | ldy Player_XY+1 115 | rts 116 | 117 | hit_wall: 118 | PRINT STR_HIT_WALL 119 | jmp return_from_player_move 120 | 121 | -------------------------------------------------------------------------------- /src/random.asm: -------------------------------------------------------------------------------- 1 | ; code copied form: http://6502.org/source/integers/random/random.html 2 | ; copyright Bruce Clark 2004 3 | 4 | .include "memory.inc" 5 | 6 | .export Random8 7 | .export Random8_Init 8 | .export Random8_RestoreRandomness 9 | .export Random8_SaveRandomness 10 | .export DBG_SEED 11 | 12 | .define TMP RESERVED01 13 | 14 | .BSS 15 | 16 | .align 256 17 | T0: .res 256 18 | T1: .res 256 19 | T2: .res 256 20 | T3: .res 256 21 | T0COPY: .res 256 22 | 23 | .CODE 24 | 25 | DBG_SEED: .byte 0,0,0,0 ; MUST NOT BE RELOCATED! 26 | 27 | ; Linear congruential pseudo-random number generator 28 | ; 29 | ; Calculate SEED = 1664525 * SEED + 1 30 | ; 31 | ; Enter with: 32 | ; 33 | ; SEED0 = byte 0 of seed 34 | ; SEED1 = byte 1 of seed 35 | ; SEED2 = byte 2 of seed 36 | ; SEED3 = byte 3 of seed 37 | ; 38 | ; Returns: 39 | ; 40 | ; SEED0 = byte 0 of seed 41 | ; SEED1 = byte 1 of seed 42 | ; SEED2 = byte 2 of seed 43 | ; SEED3 = byte 3 of seed 44 | ; 45 | ; TMP is overwritten 46 | ; 47 | ; For maximum speed, locate each table on a page boundary 48 | ; 49 | ; Assuming that (a) SEED0 to SEED3 and TMP are located on page zero, and (b) 50 | ; all four tables start on a page boundary: 51 | ; 52 | ; Space: 58 bytes for the routine 53 | ; 1024 bytes for the tables 54 | ; Speed: JSR RAND takes 94 cycles 55 | ; 56 | Random8: 57 | CLC ; compute lower 32 bits of: 58 | LDX SEED0 ; 1664525 * ($100 * SEED1 + SEED0) + 1 59 | LDY SEED1 60 | LDA T0,X 61 | ADC #1 62 | STA SEED0 63 | LDA T1,X 64 | ADC T0,Y 65 | STA SEED1 66 | LDA T2,X 67 | ADC T1,Y 68 | STA TMP 69 | LDA T3,X 70 | ADC T2,Y 71 | TAY ; keep byte 3 in Y for now (for speed) 72 | CLC ; add lower 32 bits of: 73 | LDX SEED2 ; 1664525 * ($10000 * SEED2) 74 | LDA TMP 75 | ADC T0,X 76 | STA SEED2 77 | TYA 78 | ADC T1,X 79 | CLC 80 | LDX SEED3 ; add lower 32 bits of: 81 | ADC T0,X ; 1664525 * ($1000000 * SEED3) 82 | STA SEED3 83 | RTS 84 | ; 85 | ; Generate T0, T1, T2 and T3 tables 86 | ; 87 | ; A different multiplier can be used by simply replacing the four bytes 88 | ; that are commented below 89 | ; 90 | ; To speed this routine up (which will make the routine one byte longer): 91 | ; 1. Delete the first INX instruction 92 | ; 2. Replace LDA Tn-1,X with LDA Tn,X (n = 0 to 3) 93 | ; 3. Replace STA Tn,X with STA Tn+1,X (n = 0 to 3) 94 | ; 4. Insert CPX #$FF between the INX and BNE GT1 95 | ; 96 | Random8_Init: 97 | ; Xtof's dbg Saving the seed 98 | lda SEED0 99 | sta DBG_SEED 100 | lda SEED1 101 | sta DBG_SEED+1 102 | lda SEED2 103 | sta DBG_SEED+2 104 | lda SEED3 105 | sta DBG_SEED+3 106 | ; Xtof's dbg 107 | LDX #0 ; 1664525 * 0 = 0 108 | STX T0 109 | STX T1 110 | STX T2 111 | STX T3 112 | INX 113 | CLC 114 | GT1: LDA T0-1,X ; add 1664525 to previous entry to get next entry 115 | ADC #$0D ; byte 0 of multiplier 116 | STA T0,X 117 | LDA T1-1,X 118 | ADC #$66 ; byte 1 of multiplier 119 | STA T1,X 120 | LDA T2-1,X 121 | ADC #$19 ; byte 2 of multiplier 122 | STA T2,X 123 | LDA T3-1,X 124 | ADC #$00 ; byte 3 of multiplier 125 | STA T3,X 126 | INX ; note: carry will be clear here 127 | BNE GT1 128 | RTS 129 | 130 | 131 | 132 | Random8_RestoreRandomness: 133 | 134 | ldx #$FF 135 | restore_loop: 136 | lda T0COPY, X 137 | sta T0, X 138 | dex 139 | bne restore_loop 140 | lda T0COPY, X 141 | sta T0, X 142 | 143 | rts 144 | 145 | 146 | Random8_SaveRandomness: 147 | 148 | ldx #$FF 149 | save_loop: 150 | lda T0, X 151 | sta T0COPY, X 152 | dex 153 | bne save_loop 154 | lda T0, X 155 | sta T0COPY, X 156 | 157 | rts 158 | -------------------------------------------------------------------------------- /src/random.inc: -------------------------------------------------------------------------------- 1 | 2 | 3 | ; A = random number 4 | .import Random8 5 | 6 | .import Random8_Init 7 | 8 | .import Random8_RestoreRandomness 9 | 10 | .import Random8_SaveRandomness 11 | -------------------------------------------------------------------------------- /src/tiles.asm: -------------------------------------------------------------------------------- 1 | 2 | ; Copyright (C) 2018 Christophe Meneboeuf 3 | ; 4 | ; This program is free software: you can redistribute it and/or modify 5 | ; it under the terms of the GNU General Public License as published by 6 | ; the Free Software Foundation, either version 3 of the License, or 7 | ; (at your option) any later version. 8 | ; 9 | ; This program is distributed in the hope that it will be useful, 10 | ; but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | ; GNU General Public License for more details. 13 | ; 14 | ; You should have received a copy of the GNU General Public License 15 | ; along with this program. If not, see . 16 | 17 | .export TILES 18 | 19 | .SEGMENT "RODATA" 20 | .ALIGN 256 21 | 22 | PLAYER: 23 | .byte 85, 42, 85, 42 24 | .byte 1, 120, 3, 0 25 | .byte 1, 126, 15, 0 26 | .byte 1, 102, 12, 0 27 | .byte 1, 126, 15, 0 28 | .byte 1, 30, 15, 24 29 | .byte 1, 120, 3, 6 30 | .byte 113, 103, 76, 1 31 | .byte 49, 70, 48, 0 32 | .byte 53, 94, 95, 43 33 | .byte 112, 71, 64, 0 34 | .byte 80, 65, 64, 0 35 | .byte 16, 16, 66, 0 36 | .byte 16, 16, 66, 0 37 | .byte 16, 16, 66, 0 38 | .byte 16, 20, 74, 0 39 | FLOOR_1: 40 | .byte $00, $00, $00, $00 41 | .byte $00, $00, $00, $00 42 | .byte $00, $00, $00, $00 43 | .byte $00, $00, $00, $00 44 | .byte $00, $00, $00, $00 45 | .byte $00, $00, $00, $00 46 | .byte $00, $00, $00, $00 47 | .byte $00, $00, $00, $00 48 | .byte $00, $00, $00, $00 49 | .byte $00, $00, $00, $00 50 | .byte $00, $00, $00, $00 51 | .byte $00, $00, $00, $00 52 | .byte $00, $00, $00, $00 53 | .byte $00, $00, $00, $00 54 | .byte $00, $00, $00, $00 55 | .byte $00, $00, $00, $00 56 | FLOOR_2: 57 | .byte 85, 42, 85, 42 58 | .byte 1, 32, 0, 0 59 | .byte 1, 32, 0, 0 60 | .byte 1, 32, 0, 0 61 | .byte 1, 32, 0, 0 62 | .byte 1, 32, 0, 0 63 | .byte 1, 32, 0, 0 64 | .byte 1, 32, 0, 0 65 | .byte 1, 32, 0, 0 66 | .byte 85, 42, 85, 42 67 | .byte 16, 0, 64, 0 68 | .byte 16, 0, 64, 0 69 | .byte 16, 0, 64, 0 70 | .byte 16, 0, 64, 0 71 | .byte 16, 0, 64, 0 72 | .byte 16, 0, 64, 0 73 | FLOOR_3: 74 | .byte $55, $2A, $55, $2A 75 | .byte $55, $2A, $55, $2A 76 | .byte $55, $2A, $55, $2A 77 | .byte $55, $2A, $55, $2A 78 | .byte $55, $2A, $55, $2A 79 | .byte $55, $2A, $55, $2A 80 | .byte $55, $2A, $55, $2A 81 | .byte $55, $2A, $55, $2A 82 | .byte $55, $2A, $55, $2A 83 | .byte $55, $2A, $55, $2A 84 | .byte $55, $2A, $55, $2A 85 | .byte $55, $2A, $55, $2A 86 | .byte $55, $2A, $55, $2A 87 | .byte $55, $2A, $55, $2A 88 | .byte $55, $2A, $55, $2A 89 | .byte $55, $2A, $55, $2A 90 | FLOOR_4: 91 | .byte $D5, $AA, $D5, $AA 92 | .byte $D5, $AA, $D5, $AA 93 | .byte $D5, $AA, $D5, $AA 94 | .byte $D5, $AA, $D5, $AA 95 | .byte $D5, $AA, $D5, $AA 96 | .byte $D5, $AA, $D5, $AA 97 | .byte $D5, $AA, $D5, $AA 98 | .byte $D5, $AA, $D5, $AA 99 | .byte $D5, $AA, $D5, $AA 100 | .byte $D5, $AA, $D5, $AA 101 | .byte $D5, $AA, $D5, $AA 102 | .byte $D5, $AA, $D5, $AA 103 | .byte $D5, $AA, $D5, $AA 104 | .byte $D5, $AA, $D5, $AA 105 | .byte $D5, $AA, $D5, $AA 106 | .byte $D5, $AA, $D5, $AA 107 | STAIR_DOWN: 108 | .byte $55, $2A, $55, $2A 109 | .byte $11, $00, $40, $00 110 | .byte $71, $7F, $7F, $1F 111 | .byte $71, $01, $C5, $82 112 | .byte $71, $79, $80, $8A 113 | .byte $71, $79, $3C, $00 114 | .byte $71, $79, $3C, $1E 115 | .byte $71, $79, $3C, $1E 116 | .byte $71, $79, $3C, $1E 117 | .byte $71, $79, $3C, $1E 118 | .byte $71, $79, $3C, $1E 119 | .byte $71, $79, $3C, $1E 120 | .byte $71, $79, $3C, $1E 121 | .byte $71, $79, $3C, $1E 122 | .byte $71, $7F, $7F, $1F 123 | .byte $01, $8A, $95, $A8 124 | STAIR_UP: 125 | .byte $55, $2A, $55, $2A 126 | .byte $01, $00, $90, $F8 127 | .byte $01, $AA, $70, $79 128 | .byte $01, $E2, $73, $79 129 | .byte $01, $60, $73, $79 130 | .byte $41, $67, $73, $79 131 | .byte $41, $67, $73, $79 132 | .byte $41, $67, $73, $79 133 | .byte $41, $67, $73, $79 134 | .byte $41, $67, $73, $79 135 | .byte $40, $67, $73, $79 136 | .byte $40, $67, $73, $79 137 | .byte $40, $67, $73, $79 138 | .byte $40, $67, $F3, $A1 139 | .byte $40, $67, $D3, $A8 140 | .byte $C0, $A7, $91, $A8 141 | WALL_1: 142 | .byte 197, 138, 213, 168 143 | .byte 197, 138, 213, 168 144 | .byte 197, 138, 213, 168 145 | .byte 0, 0, 0, 0 146 | .byte 209, 162, 149, 170 147 | .byte 209, 162, 149, 170 148 | .byte 209, 162, 149, 170 149 | .byte 209, 162, 149, 170 150 | .byte 0, 0, 0, 0 151 | .byte 213, 168, 197, 138 152 | .byte 213, 168, 197, 138 153 | .byte 213, 168, 197, 138 154 | .byte 0, 0, 0, 0 155 | .byte 209, 130, 213, 168 156 | .byte 209, 138, 212, 168 157 | .byte 0, 0, 0, 0 158 | WALL_2: 159 | .byte $7F, $7F, $7F, $7F 160 | .byte $7F, $7F, $7F, $7F 161 | .byte $7F, $7F, $7F, $7F 162 | .byte $7F, $7F, $7F, $7F 163 | .byte $7F, $7F, $7F, $7F 164 | .byte $7F, $7F, $7F, $7F 165 | .byte $7F, $7F, $7F, $7F 166 | .byte $7F, $7F, $7F, $7F 167 | .byte $7F, $7F, $7F, $7F 168 | .byte $7F, $7F, $7F, $7F 169 | .byte $7F, $7F, $7F, $7F 170 | .byte $7F, $7F, $7F, $7F 171 | .byte $7F, $7F, $7F, $7F 172 | .byte $7F, $7F, $7F, $7F 173 | .byte $7F, $7F, $7F, $7F 174 | .byte $7F, $7F, $7F, $7F 175 | COFFER: 176 | .byte $55, $2A, $55, $2A 177 | .byte $01, $20, $00, $00 178 | .byte $01, $7E, $0F, $00 179 | .byte $41, $67, $3C, $00 180 | .byte $31, $66, $4C, $01 181 | .byte $31, $66, $4C, $01 182 | .byte $31, $66, $4C, $01 183 | .byte $71, $7F, $7F, $01 184 | .byte $31, $C0, $40, $01 185 | .byte $35, $C0, $40, $2B 186 | .byte $B0, $C0, $40, $01 187 | .byte $30, $00, $40, $01 188 | .byte $30, $00, $40, $01 189 | .byte $70, $7F, $7F, $01 190 | .byte $70, $7F, $7F, $01 191 | .byte $10, $00, $40, $00 192 | RAT: 193 | .byte $55, $2A, $55, $2A 194 | .byte $01, $20, $03, $00 195 | .byte $01, $20, $EF, $81 196 | .byte $01, $20, $7C, $1F 197 | .byte $01, $78, $7F, $1F 198 | .byte $01, $7E, $BF, $85 199 | .byte $41, $7F, $0F, $00 200 | .byte $71, $7F, $03, $00 201 | .byte $7D, $7F, $03, $00 202 | .byte $5D, $7F, $57, $2A 203 | .byte $5C, $1F, $43, $00 204 | .byte $5C, $01, $43, $00 205 | .byte $50, $01, $4F, $00 206 | .byte $50, $1F, $40, $00 207 | .byte $10, $00, $40, $00 208 | .byte $10, $00, $40, $00 209 | SERPENT: 210 | .byte $55, $2A, $55, $2A 211 | .byte $01, $20, $03, $00 212 | .byte $01, $60, $0C, $00 213 | .byte $01, $60, $3F, $00 214 | .byte $01, $60, $8B, $80 215 | .byte $01, $60, $A3, $81 216 | .byte $01, $60, $00, $00 217 | .byte $01, $78, $00, $00 218 | .byte $01, $7E, $00, $00 219 | .byte $55, $2F, $55, $2A 220 | .byte $70, $01, $40, $00 221 | .byte $30, $18, $40, $00 222 | .byte $30, $78, $40, $00 223 | .byte $30, $60, $40, $00 224 | .byte $70, $79, $40, $00 225 | .byte $40, $1F, $40, $00 226 | SPIDER: 227 | .byte $55, $2A, $55, $2A 228 | .byte $01, $18, $03, $00 229 | .byte $01, $60, $00, $00 230 | .byte $01, $78, $03, $00 231 | .byte $01, $78, $03, $00 232 | .byte $01, $60, $00, $00 233 | .byte $01, $78, $03, $00 234 | .byte $71, $5F, $7F, $01 235 | .byte $01, $5E, $0F, $00 236 | .byte $75, $5F, $7F, $2B 237 | .byte $10, $56, $0E, $00 238 | .byte $70, $5F, $7F, $01 239 | .byte $10, $58, $43, $00 240 | .byte $50, $7F, $7F, $00 241 | .byte $10, $60, $40, $00 242 | .byte $10, $00, $40, $00 243 | UNKNOWN: 244 | .byte $80, $80, $80, $80 245 | .byte $80, $80, $80, $80 246 | .byte $80, $80, $80, $80 247 | .byte $80, $80, $80, $80 248 | .byte $80, $80, $80, $80 249 | .byte $80, $80, $80, $80 250 | .byte $80, $80, $80, $80 251 | .byte $80, $80, $80, $80 252 | .byte $80, $80, $80, $80 253 | .byte $80, $80, $80, $80 254 | .byte $80, $80, $80, $80 255 | .byte $80, $80, $80, $80 256 | .byte $80, $80, $80, $80 257 | .byte $80, $80, $80, $80 258 | .byte $80, $80, $80, $80 259 | .byte $80, $80, $80, $80 260 | 261 | 262 | .ALIGN 256 263 | 264 | ; Tiles used by ACTORS 265 | ; 128 addresses 266 | TILES: 267 | .word PLAYER 268 | ; floors 269 | .word FLOOR_1, FLOOR_2, FLOOR_3, FLOOR_4, FLOOR_4, FLOOR_4 270 | ; walls 271 | .word WALL_1, WALL_2, WALL_2, WALL_2 272 | ; stairs 273 | .word STAIR_DOWN, STAIR_UP 274 | ; items 275 | .word COFFER 276 | ; monsters 277 | .word RAT, SPIDER, SERPENT 278 | ; other 279 | ; COMPLETE TO GET THE 128 TILES!!! 280 | .word UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN 281 | .word UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN 282 | .word UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN 283 | .word UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN 284 | .word UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN 285 | .word UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN 286 | .word UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN 287 | .word UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN 288 | .word UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN 289 | .word UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN 290 | .word UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN 291 | .word UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN 292 | .word UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN 293 | .word UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN 294 | -------------------------------------------------------------------------------- /src/tiles.inc: -------------------------------------------------------------------------------- 1 | ; Copyright (C) 2018 Christophe Meneboeuf 2 | ; 3 | ; This program is free software: you can redistribute it and/or modify 4 | ; it under the terms of the GNU General Public License as published by 5 | ; the Free Software Foundation, either version 3 of the License, or 6 | ; (at your option) any later version. 7 | ; 8 | ; This program is distributed in the hope that it will be useful, 9 | ; but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | ; GNU General Public License for more details. 12 | ; 13 | ; You should have received a copy of the GNU General Public License 14 | ; along with this program. If not, see . 15 | 16 | 17 | 18 | 19 | .import TILES 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/world/level.asm: -------------------------------------------------------------------------------- 1 | 2 | ; Copyright (C) 2021 Christophe Meneboeuf 3 | ; 4 | ; This program is free software: you can redistribute it and/or modify 5 | ; it under the terms of the GNU General Public License as published by 6 | ; the Free Software Foundation, either version 3 of the License, or 7 | ; (at your option) any later version. 8 | ; 9 | ; This program is distributed in the hope that it will be useful, 10 | ; but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | ; GNU General Public License for more details. 13 | ; 14 | ; You should have received a copy of the GNU General Public License 15 | ; along with this program. If not, see . 16 | 17 | .include "../common.inc" 18 | .include "../memory.inc" 19 | .include "../random.inc" 20 | .include "../math.inc" 21 | .include "../actors/actors.inc" 22 | .include "../builder/builder.inc" 23 | .include "../io/files.inc" 24 | .include "level_private.inc" 25 | 26 | ; code 27 | .export levels_init 28 | .export level_enter 29 | .export level_exit 30 | .export level_get_config_offset 31 | .export level_reset_states 32 | ; data 33 | .export LevelConfigs 34 | .export NbLevels 35 | .export CurrentLevel 36 | .export NextLevel 37 | .export ExitLevel 38 | .export LevelIsBuilt 39 | 40 | .import player_init 41 | .import ActorsInLevel 42 | .import ActorPositions 43 | .import World 44 | 45 | .BSS 46 | 47 | CurrentLevel: .res 1 48 | NbLevels: .res 1 49 | NextLevel: .res 1 50 | ExitLevel: .res 1 51 | LevelIsBuilt: .res 1 52 | 53 | 54 | LevelConfigs: .res 1 + NB_LEVELS * SIZEOF_CONF_LEVEL 55 | 56 | 57 | .segment "CODE2" 58 | 59 | 60 | .define ACCUMULATOR ZERO_9_4 ; 2 bytes 61 | 62 | ; @param LevelNr in X 63 | _Set_Params_LoadSaveLevelActors: 64 | 65 | ; compute offset in file 66 | lda #0 67 | sta ACCUMULATOR 68 | sta ACCUMULATOR+1 69 | beq end_loop_offset 70 | loop_offset: ; accumulating offsets 71 | clc 72 | lda ACCUMULATOR 73 | adc #<(SIZEOF_ACTORS_T) 74 | sta ACCUMULATOR 75 | lda ACCUMULATOR+1 76 | adc #>(SIZEOF_ACTORS_T) 77 | sta ACCUMULATOR+1 78 | dex 79 | bne loop_offset 80 | end_loop_offset: 81 | ; set function parameters 82 | sta Param_FileOffset+3 83 | lda ACCUMULATOR 84 | sta Param_FileOffset+2 85 | lda #Str_FileLevelsActors 88 | sta Param_FileOpen+2 89 | lda #ActorsInLevel 92 | sta Param_FilesReadWrite+3 93 | 94 | rts 95 | 96 | 97 | .define NR_LEVELS ZERO_4_1 98 | levels_init: 99 | 100 | ; file path 101 | lda #Str_FileLevelConfs 104 | sta Param_FileOpen+2 105 | ; read buffer 106 | lda #0 107 | sta Param_FileOffset+2 108 | sta Param_FileOffset+3 109 | sta Param_FileOffset+4 110 | lda #LevelConfigs 113 | sta Param_FilesReadWrite+3 114 | lda #<(1 + SIZEOF_CONF_LEVEL * NB_LEVELS) 115 | sta Param_FilesReadWrite+4 116 | lda #>(1 + SIZEOF_CONF_LEVEL * NB_LEVELS) 117 | sta Param_FilesReadWrite+5 118 | 119 | ; load 120 | jsr ReadFile 121 | 122 | ; exploit 123 | lda LevelConfigs 124 | sta NbLevels 125 | sta NR_LEVELS 126 | inc NR_LEVELS 127 | 128 | ; global vars 129 | lda #0 130 | sta CurrentLevel 131 | sta NextLevel 132 | lda #FALSE 133 | sta ExitLevel 134 | 135 | rts 136 | 137 | 138 | 139 | ; @param: Uses NextLevel as level number 140 | .define LEVEL_STATE_OFFSET ZERO_9_1 141 | .define ADDR_LEVEL_CONF ZERO_9_2 ; 2 bytes 142 | level_enter: 143 | 144 | lda NextLevel 145 | jsr LoadState 146 | 147 | lda LevelIsBuilt 148 | cmp #TRUE 149 | beq level_was_built 150 | 151 | level_generation: 152 | 153 | ; compute offset to level config 154 | lda NextLevel 155 | jsr level_get_config_offset 156 | pha 157 | clc 158 | txa 159 | adc #LevelConfigs 163 | sta ADDR_LEVEL_CONF+1 164 | 165 | ; init maze size 166 | ldy #1 167 | lda (ADDR_LEVEL_CONF), Y ; size 168 | tax 169 | tay 170 | jsr Init_Dimensions_Maze 171 | 172 | ; player position returned in X and Y 173 | jsr Build_Level 174 | jsr player_init ; param: player pos in X and Y 175 | 176 | level_was_built: 177 | 178 | ldx ActorPositions + eACTORTYPES::PLAYER 179 | ldy ActorPositions + eACTORTYPES::PLAYER + 1 180 | jsr player_init 181 | 182 | level_enter_end: 183 | 184 | lda #FALSE 185 | sta ExitLevel 186 | 187 | lda NextLevel 188 | sta CurrentLevel 189 | 190 | rts 191 | 192 | 193 | .define Player_XY ActorPositions + eACTORTYPES::PLAYER 194 | level_exit: 195 | 196 | lda CurrentLevel 197 | jsr SaveState 198 | 199 | rts 200 | 201 | ; @param Level_Nr in A 202 | ; @return Config offset for the level in X 203 | level_get_config_offset: 204 | 205 | sta FAC1 206 | ldx #SIZEOF_CONF_LEVEL 207 | stx FAC2 208 | jsr mul8 209 | ; first byte of conf is the number of levels 210 | pha 211 | clc 212 | txa 213 | adc #1 214 | tax 215 | pla 216 | adc #0 217 | 218 | rts 219 | 220 | ; @brief reset the states of all levels 221 | level_reset_states: 222 | 223 | lda #0 224 | sta CurrentLevel 225 | lda #NB_LEVELS 226 | jsr ResetIsBuilt 227 | 228 | rts 229 | -------------------------------------------------------------------------------- /src/world/level.inc: -------------------------------------------------------------------------------- 1 | ; Copyright (C) 2021 Christophe Meneboeuf 2 | ; 3 | ; This program is free software: you can redistribute it and/or modify 4 | ; it under the terms of the GNU General Public License as published by 5 | ; the Free Software Foundation, either version 3 of the License, or 6 | ; (at your option) any later version. 7 | ; 8 | ; This program is distributed in the hope that it will be useful, 9 | ; but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | ; GNU General Public License for more details. 12 | ; 13 | ; You should have received a copy of the GNU General Public License 14 | ; along with this program. If not, see . 15 | 16 | 17 | 18 | .import levels_init 19 | .import level_enter ; param: A = level_nr 20 | .import level_exit 21 | .import level_get_config_offset 22 | 23 | .import CurrentLevel 24 | .import NextLevel 25 | .import ExitLevel 26 | .import LevelConfigs 27 | .import NbLevels 28 | 29 | .enum LEVELSIZE 30 | TINY = 20 31 | SMALL = 24 32 | NORMAL = 32 33 | BIG = 48 34 | HUGE = 64 35 | .endenum 36 | 37 | 38 | ; struct Level 39 | ; { 40 | ; uint8_t is_built; 41 | ; uint8_t seed[4]; //< seed used to build the level 42 | ; coords_t pos_player_enter; //< position of player when entering the level (x,y) 43 | ; } 44 | ; 45 | 46 | ; nb_levels >>>> ALWAYS OFFSET + 1!!! <<<< 47 | ; array [ 48 | ; struct LevelsConf 49 | ; { 50 | ; uint8_t level_nr; 51 | ; uint8_t size; //< eLEVELSIZE 52 | ; uint8_t actors[NB_ACTORS_MAX]; //< number of actors of each kind added in the level after building the maze 53 | ; } 54 | ; ] 55 | 56 | -------------------------------------------------------------------------------- /src/world/level_private.inc: -------------------------------------------------------------------------------- 1 | ; Copyright (C) 2021 Christophe Meneboeuf 2 | ; 3 | ; This program is free software: you can redistribute it and/or modify 4 | ; it under the terms of the GNU General Public License as published by 5 | ; the Free Software Foundation, either version 3 of the License, or 6 | ; (at your option) any later version. 7 | ; 8 | ; This program is distributed in the hope that it will be useful, 9 | ; but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | ; GNU General Public License for more details. 12 | ; 13 | ; You should have received a copy of the GNU General Public License 14 | ; along with this program. If not, see . 15 | 16 | 17 | 18 | ; BEWARE: NB_LEVELS * SIZEOF_STATE_LEVEL shall not be > 256 19 | .define SIZEOF_STATE_LEVEL 7 20 | 21 | .define SIZEOF_CONF_LEVEL 130 ;( NB_ACTORS_MAX + 2 ) 22 | .define NB_LEVELS 3 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/world/world.asm: -------------------------------------------------------------------------------- 1 | 2 | ; Copyright (C) 2018 Christophe Meneboeuf 3 | ; 4 | ; This program is free software: you can redistribute it and/or modify 5 | ; it under the terms of the GNU General Public License as published by 6 | ; the Free Software Foundation, either version 3 of the License, or 7 | ; (at your option) any later version. 8 | ; 9 | ; This program is distributed in the hope that it will be useful, 10 | ; but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | ; GNU General Public License for more details. 13 | ; 14 | ; You should have received a copy of the GNU General Public License 15 | ; along with this program. If not, see . 16 | 17 | 18 | .include "world.inc" 19 | .include "../common.inc" 20 | .include "../actors/actors.inc" 21 | .include "../tiles.inc" 22 | .include "../random.inc" 23 | .include "../math.inc" 24 | .include "../memory.inc" 25 | 26 | 27 | .export World 28 | 29 | ; initializes the world 30 | ; DESTROYS A,X,Y, ZERO_2_1, ZERO_2_2, ZERO_2_3 31 | .export world_init 32 | 33 | ; sets the player's position onto the Maze 34 | ; in: X = x coord in the maze 35 | ; in: Y = y coord in the maze 36 | ; out : X and Y as they where given 37 | ; DESTROYS A,X,Y, ZERO_2_1, ZERO_2_2, ZERO_2_4, ZERO_2_5 38 | .export world_set_player 39 | 40 | ; Computes the adress corresponding to the coordinates in the maze 41 | ; in: X = x coord in the maze 42 | ; in: Y = y coord in the maze 43 | ; out: AX = Address corresponding to (x,y) in the World array 44 | ; DESTROYS A,X,Y, ZERO_2_1, ZERO_2_2, ZERO_2_3 45 | .export Compute_Maze_Addr 46 | 47 | ; #TRUE if an object has been picked by the player 48 | .export World_PickedObject 49 | 50 | .export Tile_player_standing_actor 51 | 52 | .define COORD_X ZERO_2_1 53 | .define COORD_Y ZERO_2_2 54 | .define OFFSET ZERO_2_2 55 | 56 | 57 | .BSS 58 | 59 | ; The tile where the player stands 60 | ; The two memory locations must be adjacent 61 | Tile_player_standing_addr: .res 2 62 | Tile_player_standing_actor: .res 1 63 | 64 | .DATA 65 | 66 | World_PickedObject: .byte 0 67 | 68 | .CODE 69 | 70 | ; @param X Player's x 71 | ; @param Y Player's y 72 | world_init: 73 | ; Saving the first tile on which the player stands 74 | ; FIXME player could be standing anywhere on any type of floor 75 | jsr Compute_Maze_Addr 76 | stx Tile_player_standing_addr 77 | sta Tile_player_standing_addr+1 78 | 79 | lda Tile_player_standing_actor 80 | cmp #UNDEF 81 | bne world_init_end 82 | lda #eACTORTYPES::FLOOR_2 83 | sta Tile_player_standing_actor 84 | 85 | world_init_end: 86 | rts 87 | 88 | 89 | ; sets the player's position onto the World 90 | .define PLAYER_XY ZERO_2_1; 2 bytes 91 | .define NEXT_TILE_XY ZERO_2_4 ; 2 bytes 92 | world_set_player: 93 | 94 | stx NEXT_TILE_XY 95 | sty NEXT_TILE_XY+1 96 | 97 | ; restore the previous tile 98 | ldx Tile_player_standing_addr 99 | lda Tile_player_standing_addr+1 100 | stx PLAYER_XY 101 | sta PLAYER_XY+1 102 | ldy #0 103 | lda Tile_player_standing_actor 104 | sta (PLAYER_XY), Y 105 | 106 | ; save the next tile 107 | ldx NEXT_TILE_XY 108 | ldy NEXT_TILE_XY+1 109 | jsr Compute_Maze_Addr ; get's player's position address in memory 110 | stx Tile_player_standing_addr 111 | sta Tile_player_standing_addr+1 112 | stx PLAYER_XY 113 | sta PLAYER_XY+1 114 | ldy #0 115 | lda (PLAYER_XY), y 116 | sta Tile_player_standing_actor 117 | ; if an object was picked 118 | ; override to force save a floor tile 119 | lda World_PickedObject 120 | cmp #TRUE 121 | bne no_object_picked 122 | lda #eACTORTYPES::FLOOR_2 123 | sta Tile_player_standing_actor 124 | lda #FALSE 125 | sta World_PickedObject 126 | no_object_picked: 127 | 128 | ; sets the player on the tile 129 | lda #eACTORTYPES::PLAYER 130 | sta (PLAYER_XY), y 131 | 132 | ; restore the given locations 133 | ldx NEXT_TILE_XY 134 | ldy NEXT_TILE_XY+1 135 | 136 | rts 137 | 138 | ; Destroys ZERO_2_1, ZERO_2_2, ZERO_2_3 ZERO_7_1 and ZERO_7_2 139 | Compute_Maze_Addr: 140 | 141 | stx COORD_X 142 | 143 | ; offset due to Y coord 144 | sty FAC1 145 | lda #WIDTH_WORLD 146 | sta FAC2 147 | jsr mul8 148 | tay ; high part of the mul 149 | txa ; low part of the mul 150 | 151 | ; adding offset due to X 152 | clc 153 | adc COORD_X 154 | sta OFFSET 155 | tya 156 | adc #0 157 | sta OFFSET+1 158 | 159 | ; adding the offset to the address 160 | lda #World 165 | adc OFFSET+1 ; high part of address to be returned in A 166 | 167 | rts 168 | 169 | 170 | 171 | .BSS 172 | 173 | .align 256 174 | 175 | World: .res (WIDTH_WORLD) * (HEIGHT_WORLD) 176 | -------------------------------------------------------------------------------- /src/world/world.inc: -------------------------------------------------------------------------------- 1 | ; Copyright (C) 2018-2021 Christophe Meneboeuf 2 | ; 3 | ; This program is free software: you can redistribute it and/or modify 4 | ; it under the terms of the GNU General Public License as published by 5 | ; the Free Software Foundation, either version 3 of the License, or 6 | ; (at your option) any later version. 7 | ; 8 | ; This program is distributed in the hope that it will be useful, 9 | ; but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | ; GNU General Public License for more details. 12 | ; 13 | ; You should have received a copy of the GNU General Public License 14 | ; along with this program. If not, see . 15 | 16 | 17 | 18 | 19 | ; The world contains a Maze, filled with Actors 20 | ; Actors can be static, such a a floor or a wall, 21 | ; dynamic such as a door 22 | ; or alive, such as a monster 23 | 24 | 25 | .define MAXIMUM_WIDTH_MAZE 64 ; must be a power of 2 26 | .define MAXIMUM_HEIGHT_MAZE 64 ; must be a power of 2 27 | .define WIDTH_WORLD 64 28 | .define NEG_WIDTH_WORLD $C0 29 | .define HEIGHT_WORLD 64 30 | .define NEG_HEIGHT_WORLD $C0 31 | 32 | --------------------------------------------------------------------------------