diff --git a/build.cmd b/build.cmd index 45bed37..1034163 100644 --- a/build.cmd +++ b/build.cmd @@ -1 +1 @@ -tjc source/main.vx source/kernel32.vx source/sdl.vx source/sdlimage.vx source/tile.vx source/entity.vx source/window.vx source/utils.vx bin/SDL2.dll bin/SDL2_image.dll C:\Windows\System32\kernel32.dll --of=bin/main.exe --print-time --print-mem \ No newline at end of file +tjc source/death_functions.vx source/main.vx source/kernel32.vx source/sdl.vx source/sdlimage.vx source/tile.vx source/entity.vx source/window.vx source/utils.vx bin/SDL2.dll bin/SDL2_image.dll C:\Windows\System32\kernel32.dll --of=bin/main.exe --print-time --print-mem \ No newline at end of file diff --git a/readme.md b/readme.md index e8996e1..928377d 100644 --- a/readme.md +++ b/readme.md @@ -8,7 +8,7 @@ Compile with `build.cmd` script. -Produces `main.exe` for win64. +Produces `bin/main.exe` for win64. Or compile and run with `run.cmd` diff --git a/run.cmd b/run.cmd index fdf6f0e..3d9e450 100644 --- a/run.cmd +++ b/run.cmd @@ -1 +1 @@ -build.cmd && cd bin && main.exe \ No newline at end of file +build.cmd && cd bin && start main.exe \ No newline at end of file diff --git a/source/death_functions.vx b/source/death_functions.vx new file mode 100644 index 0000000..4d12f06 --- /dev/null +++ b/source/death_functions.vx @@ -0,0 +1,38 @@ +import main; +import entity; +import utils; + +void kill_entity(GameMap* map, EntityId eid) +{ + if (eid == 0) + kill_player(map, eid); + else + kill_monster(map, eid); +} + +void kill_player(GameMap* map, EntityId eid) +{ + Entity* player = &map.entities[eid]; + player.char = '%'; + player.r = 150; + player.g = 0; + player.b = 0; + player.render_order = RenderOrder.CORPSE; + map.game_state = GameState.PLAYER_DEAD; + println("You died!"); +} + +void kill_monster(GameMap* map, EntityId eid) +{ + Entity* monster = &map.entities[eid]; + monster.char = '%'; + monster.r = 150; + monster.g = 0; + monster.b = 0; + monster.render_order = RenderOrder.CORPSE; + monster.ai = AiId.max; + monster.fighter = FighterId.max; + monster.flags &= ~EntityFlags.blocks; + print(monster.name); + println(" is dead!"); +} \ No newline at end of file diff --git a/source/entity.vx b/source/entity.vx index 2cd781d..20facc0 100644 --- a/source/entity.vx +++ b/source/entity.vx @@ -1,37 +1,95 @@ +import main; +import tile; import utils; +import death_functions; enum EntityFlags : u8 { blocks = 1 << 0, } +enum RenderOrder : u8 { + CORPSE = 0, + ITEM = 1, + ACTOR = 2, +} + struct Entity { i32 x; i32 y; u8 flags; u8 char; + u8 render_order; u8 r; u8 g; u8 b; u8[] name; - - void move(i32 dx, i32 dy) { - print("Move "); - printInt(x); - print(" "); - printInt(y); - x += dx; - y += dy; - print(" -> "); - printInt(x); - print(" "); - printInt(y); - print(" "); - print(name); - println(null); - } + FighterId fighter; + AiId ai; bool blocks() { return flags & EntityFlags.blocks; } } -alias EntityId = u16; \ No newline at end of file +alias EntityId = u16; + +struct Fighter +{ + EntityId owner; + u16 max_hp; + u16 hp; + u16 defense; + u16 power; +} +alias FighterId = u16; + +struct Ai +{ + EntityId owner; +} +alias AiId = u16; + +void do_ai(GameMap* map, EntityId monsterId, EntityId targetId) +{ + Entity* monster = &map.entities[monsterId]; + Entity* target = &map.entities[targetId]; + Tile tile = map.tiles[monster.y][monster.x]; + if (tile.visible) + { + Fighter* target_fighter = &map.fighters[target.fighter]; + if (map.distance_between(monsterId, targetId) > 1) + { + map.move_towards(monsterId, target.x, target.y); + } + else if (target_fighter.hp > 0) + { + attack(map, monsterId, targetId); + } + } +} + +void attack(GameMap* map, EntityId attackerId, EntityId targetId) +{ + Entity* attacker = &map.entities[attackerId]; + Entity* target = &map.entities[targetId]; + Fighter* attacker_fighter = &map.fighters[attacker.fighter]; + Fighter* target_fighter = &map.fighters[target.fighter]; + i32 damage = attacker_fighter.power - target_fighter.defense; + if (damage > 0) { + print(attacker.name); print(" "); printInt(attackerId); + print(" attacks "); print(target.name); print(" for "); printInt(damage); print(" hit points. "); + if (target_fighter.hp <= damage) { + target_fighter.hp = 0; + printInt(0); + println(" hp left."); + kill_entity(map, targetId); + } + else { + target_fighter.hp -= cast(u16)damage; + printInt(target_fighter.hp); + println(" hp left."); + } + + } else { + print(attacker.name); print(" attacks "); print(target.name); println(" but does no damage."); + } +} diff --git a/source/main.vx b/source/main.vx index 249955c..dbf595d 100644 --- a/source/main.vx +++ b/source/main.vx @@ -28,8 +28,10 @@ struct GameMap enum max_monsters_per_room = 8; enum i32 room_min_size = 3; enum i32 room_max_size = 20; + enum EntityId playerId = 0; - u64[2]* rand_state; + u8 game_state; + u64[2] rand_state; i32 view_offset_x; i32 view_offset_y; i32 viewport_w; @@ -37,13 +39,42 @@ struct GameMap i32 view_radius; Tile[map_width][map_height] tiles; Entity[max_entities] entities; - u16 num_entities; + EntityId num_entities; + Fighter[max_entities] fighters; + FighterId num_fighters; + Ai[max_entities] ais; + AiId num_ais; + + void init(i32 window_w, i32 window_h) + { + init_rand_state(&rand_state); + view_offset_x = 0; + view_offset_y = 0; + viewport_w = window_w / (TILE_W * scale); + viewport_h = window_h / (TILE_H * scale); + view_radius = 7; + num_entities = 0; + num_fighters = 0; + num_ais = 0; + + // blocking_entity will be erased by initialize_tiles() and written by make_map() + EntityId eid = add_entity("Player", map_width/2, map_height/2, '@', RenderOrder.ACTOR, 255, 255, 255, true); + add_fighter(eid, 30, 30, 2, 5); + + initialize_tiles(); + make_map(&entities[0]); + + game_state = GameState.PLAYER_TURN; + } - void add_entity(u8[] name, i32 x, i32 y, u8 c, u8 r, u8 g, u8 b, bool blocks) + EntityId add_entity(u8[] name, i32 x, i32 y, u8 c, u8 render_order, u8 r, u8 g, u8 b, bool blocks) { - Entity* entity = &entities[num_entities]; - *entity = Entity(x, y, 0, c, r, g, b, name); + EntityId eid = num_entities++; + Entity* entity = &entities[eid]; + *entity = Entity(x, y, 0, c, render_order, r, g, b, name); print("Add "); + printInt(eid); + print(" "); print(name); print(" at "); printInt(x); @@ -52,20 +83,40 @@ struct GameMap println(";"); if (blocks) { entity.flags |= EntityFlags.blocks; - tiles[y][x].blocking_entity = num_entities; + tiles[y][x].blocking_entity = eid; } - ++num_entities; + return eid; + } + + void add_fighter(EntityId owner, u16 max_hp, u16 hp, u16 defense, u16 power) + { + FighterId fid = num_fighters++; + entities[owner].fighter = fid; + fighters[fid] = Fighter(owner, max_hp, hp, defense, power); + print("new fighter "); + printInt(owner); print(" id "); + printInt(fighters[fid].max_hp); print(" MAXHP "); + printInt(fighters[fid].hp); print(" HP "); + printInt(fighters[fid].defense); print(" DEF "); + printInt(fighters[fid].power); println(" STR"); + } + + void add_ai(EntityId owner) + { + AiId aid = num_ais++; + entities[owner].ai = aid; + ais[aid] = Ai(owner); } - Entity* get_blocking_entities_at_location(i32 x, i32 y) + EntityId get_blocking_entities_at_location(i32 x, i32 y) { - for (u32 i = 0; i < num_entities; ++i) + for (EntityId i = 0; i < num_entities; ++i) { Entity* e = &entities[i]; if (e.blocks && e.x == x && e.y == y) - return e; + return i; } - return null; + return EntityId.max; } bool valid_pos(i32 x, i32 y) @@ -101,8 +152,7 @@ struct GameMap { for (i32 x = 0; x < map_width; ++x) { - tiles[y][x].flags = TileFlags.blocks_walk | TileFlags.blocks_sight; - tiles[y][x].blocking_entity = EntityId.max; + tiles[y][x] = Tile(TileFlags.blocks_walk | TileFlags.blocks_sight, 0, EntityId.max); } } } @@ -148,12 +198,12 @@ struct GameMap for (u32 roomIndex = 0; roomIndex < max_rooms; ++roomIndex) { // random width and height - i32 w = cast(i32)uniform(room_min_size, room_max_size, rand_state); - i32 h = cast(i32)uniform(room_min_size, room_max_size, rand_state); + i32 w = cast(i32)uniform(room_min_size, room_max_size, &rand_state); + i32 h = cast(i32)uniform(room_min_size, room_max_size, &rand_state); // random position without going out of the boundaries of the map - i32 x = cast(i32)uniform(0, map_width - w - 1, rand_state); - i32 y = cast(i32)uniform(0, map_height - h - 1, rand_state); + i32 x = cast(i32)uniform(0, map_width - w - 1, &rand_state); + i32 y = cast(i32)uniform(0, map_height - h - 1, &rand_state); rooms[roomIndex] = Rect(x, y, x+w, y+h); @@ -184,7 +234,7 @@ struct GameMap Point prev; rooms[roomIndex - 1].center(&prev); - if (uniform(0, 1, rand_state) == 1) + if (uniform(0, 1, &rand_state) == 1) { // first move horizontally, then vertically create_h_tunnel(prev.x, new_x, prev.y); @@ -206,23 +256,91 @@ struct GameMap void place_entities(Rect room) { // Get a random number of monsters - i32 number_of_monsters = cast(i32)uniform(0, max_monsters_per_room, rand_state); + i32 number_of_monsters = cast(i32)uniform(0, max_monsters_per_room, &rand_state); for (i32 i = 0; i < number_of_monsters; ++i) { // Choose a random location in the room - i32 x = cast(i32)uniform(room.x1 + 1, room.x2 - 1, rand_state); - i32 y = cast(i32)uniform(room.y1 + 1, room.y2 - 1, rand_state); + i32 x = cast(i32)uniform(room.x1 + 1, room.x2 - 1, &rand_state); + i32 y = cast(i32)uniform(room.y1 + 1, room.y2 - 1, &rand_state); if (!tiles[y][x].has_blocking_entity) { - if (uniform(0, 100, rand_state) < 80) - add_entity("Orc", x, y, 142, 0, 250, 0, true); + if (uniform(0, 100, &rand_state) < 80) + { + EntityId eid = add_entity("Orc", x, y, 'o', RenderOrder.ACTOR, 0, 250, 0, true); + add_fighter(eid, 10, 10, 0, 3); + add_ai(eid); + } else - add_entity("Troll", x, y, 115, 0, 200, 0, true); + { + EntityId eid = add_entity("Troll", x, y, 'T', RenderOrder.ACTOR, 0, 200, 0, true); + add_fighter(eid, 16, 16, 1, 4); + add_ai(eid); + } } } } + + void move_to(EntityId eid, i32 dest_x, i32 dest_y) + { + Entity* e = &entities[eid]; + + print("Move "); + printInt(e.x); + print(" "); + printInt(e.y); + print(" -> "); + printInt(dest_x); + print(" "); + printInt(dest_y); + print(" "); + print(e.name); + println(null); + + tiles[e.y][e.x].blocking_entity = EntityId.max; + e.x = dest_x; + e.y = dest_y; + tiles[dest_y][dest_x].blocking_entity = eid; + } + + i32 distance_between(EntityId eid_a, EntityId eid_b) + { + Entity* a = &entities[eid_a]; + Entity* b = &entities[eid_b]; + i32 dx = abs(b.x - a.x); + i32 dy = abs(b.y - a.y); + return max(dx, dy); + } + + void move_towards(EntityId eid, i32 target_x, i32 target_y) + { + MoveData data = MoveData(this, eid, 0); + Entity* e = &entities[eid]; + tran_thong(e.x, e.y, target_x, target_y, &move_visitor, cast(void*)&data, LineVisitFlags.SKIP_START); + } +} + +struct MoveData +{ + GameMap* map; + EntityId eid; +} + +bool move_visitor(void* _data, i32 x, i32 y) +{ + MoveData data = *cast(MoveData*)_data; + + if (!data.map.blocks_walk(x, y)) + { + EntityId targetId = data.map.get_blocking_entities_at_location(x, y); + if (targetId == EntityId.max) + { + Entity* e = &data.map.entities[data.eid]; + data.map.move_to(data.eid, x, y); + } + } + return true; } enum VIEW_RADIUS = 7; @@ -268,23 +386,49 @@ void update_fov(GameMap* map, Entity* player) i32 view_radius = map.view_radius; for (i32 i = -view_radius; i <= view_radius; ++i) { - tran_thong(0, 0, -view_radius, i, &visibility_check, cast(void*)&data); - tran_thong(0, 0, view_radius, i, &visibility_check, cast(void*)&data); - tran_thong(0, 0, i, view_radius, &visibility_check, cast(void*)&data); - tran_thong(0, 0, i, -view_radius, &visibility_check, cast(void*)&data); + tran_thong(0, 0, -view_radius, i, &visibility_check, cast(void*)&data, 0); + tran_thong(0, 0, view_radius, i, &visibility_check, cast(void*)&data, 0); + tran_thong(0, 0, i, view_radius, &visibility_check, cast(void*)&data, 0); + tran_thong(0, 0, i, -view_radius, &visibility_check, cast(void*)&data, 0); } } -void render_all(Window* win, GameMap* map) +void render_int(Window* win, i64 i, i32 x, i32 y) +{ + u8[21] buf; + u8[] res = formatInt(i, &buf, 1); + render_string(win, res, x, y); +} + +void render_string(Window* win, u8[] str, i32 x, i32 y) { - SDL_Rect from; - SDL_Rect to; + y *= scale * TILE_H; + for (u64 i = 0; i < str.length; ++i) + { + i32 char_id = cast(i32)str[i] - 32; + i32 from_x = (char_id % 32) * TILE_W; + i32 from_y = (char_id / 32) * TILE_H; + SDL_Rect from = SDL_Rect(from_x, from_y, TILE_W, TILE_H); + SDL_Rect to = SDL_Rect( + (x + cast(i32)i) * scale * TILE_W, + y, + TILE_W * scale, TILE_H * scale); + + SDL_RenderCopy(win.sdl_renderer, win.font, &from, &to); + } +} +void render_all(Window* win, GameMap* map) +{ + // Render tiles for (i32 y = 0; y < map.map_height; ++y) { for (i32 x = 0; x < map.map_width; ++x) { - Tile tile = map.tiles[y][x]; + Tile* tile_ptr = &map.tiles[y][x]; + tile_ptr.max_render_order = 0; // reset ordering + Tile tile = *tile_ptr; + if (tile.visible) { if (tile.block_sight) { @@ -311,7 +455,7 @@ void render_all(Window* win, GameMap* map) //SDL_SetRenderDrawColor(win.sdl_renderer, color[0], color[1], color[2]); - to = SDL_Rect( + SDL_Rect to = SDL_Rect( x * scale * TILE_W + map.view_offset_x, y * scale * TILE_H + map.view_offset_y, TILE_W * scale, @@ -320,33 +464,54 @@ void render_all(Window* win, GameMap* map) } } - for (u32 i = 0; i < map.num_entities; ++i) + // Render visible entities + for (EntityId i = 0; i < map.num_entities; ++i) { - Tile tile = map.tiles[map.entities[i].y][map.entities[i].x]; + Entity* e = &map.entities[i]; + Tile* tile = &map.tiles[e.y][e.x]; + + // only show visible entities if (!tile.visible) continue; - from = SDL_Rect(map.entities[i].char * TILE_W, 0, TILE_W, TILE_H); // TODO: use letter coords from font - to = SDL_Rect( - map.entities[i].x * scale * TILE_W + map.view_offset_x, - map.entities[i].y * scale * TILE_H + map.view_offset_y, - TILE_W * scale, TILE_H * scale); // TODO: use letter coords from font - SDL_SetTextureColorMod(win.font, map.entities[i].r, map.entities[i].g, map.entities[i].b); + // only show if no entity with higher order was drawn to this tile + if (tile.max_render_order > e.render_order) continue; + tile.max_render_order = e.render_order; + + i32 char_id = cast(i32)e.char - 32; + i32 from_x = (char_id % 32) * TILE_W; + i32 from_y = (char_id / 32) * TILE_H; + SDL_Rect from = SDL_Rect(from_x, from_y, TILE_W, TILE_H); + SDL_Rect to = SDL_Rect( + e.x * scale * TILE_W + map.view_offset_x, + e.y * scale * TILE_H + map.view_offset_y, + TILE_W * scale, TILE_H * scale); + + //SDL_SetRenderDrawColor(win.sdl_renderer, e.r, e.g, e.b, 255); + //SDL_RenderFillRect(win.sdl_renderer, &to); + SDL_SetTextureColorMod(win.font, e.r, e.g, e.b); SDL_RenderCopy(win.sdl_renderer, win.font, &from, &to); } + + // Show HP + SDL_SetTextureColorMod(win.font, 255, 255, 255); + Entity* player = &map.entities[map.playerId]; + Fighter* player_fighter = &map.fighters[player.fighter]; + render_string(win, "HP:", 0, 0); + render_int(win, player_fighter.hp, 3, 0); + render_string(win, "/", 6, 0); + render_int(win, player_fighter.max_hp, 7, 0); } GameMap global_map; enum GameState : u8 { - player_turn = 0, - enemy_turn = 1, + PLAYER_TURN = 0, + ENEMY_TURN = 1, + PLAYER_DEAD = 2, } i32 main(void* hInstance, void* hPrevInstance, u8* lpCmdLine, i32 nShowCmd) { - u64[2] rand_state; - init_rand_state(&rand_state); - Window win; i32 SCREEN_WIDTH = 800; i32 SCREEN_HEIGHT = 600; @@ -357,29 +522,15 @@ i32 main(void* hInstance, void* hPrevInstance, u8* lpCmdLine, i32 nShowCmd) SDL_Event e; GameMap* map = &global_map; - map.rand_state = &rand_state; - map.view_offset_x = 0; - map.view_offset_y = 0; - map.view_radius = 7; - map.viewport_w = SCREEN_WIDTH / (TILE_W * scale); - map.viewport_h = SCREEN_HEIGHT / (TILE_H * scale); - map.num_entities = 0; - - map.add_entity("Player", map.map_width/2, map.map_height/2, 32, 0, 255, 0, true); - Entity* player = &map.entities[0]; - - map.initialize_tiles(); - map.make_map(player); + map.init(SCREEN_WIDTH, SCREEN_HEIGHT); + Entity* player = &map.entities[map.playerId]; bool recalc_fov = true; - i32 mouse_x; - i32 mouse_y; - - u8 game_state = GameState.player_turn; - while (run) { + i32 mouse_x; + i32 mouse_y; u32 buttons_pressed = SDL_GetMouseState(&mouse_x, &mouse_y); i32 cursor_map_x = (mouse_x - map.view_offset_x) / (TILE_W * scale); i32 cursor_map_y = (mouse_y - map.view_offset_y) / (TILE_H * scale); @@ -410,68 +561,82 @@ i32 main(void* hInstance, void* hPrevInstance, u8* lpCmdLine, i32 nShowCmd) else if (e.type == SDL_EventType.SDL_KEYDOWN) { SDL_KeyboardEvent* key = cast(SDL_KeyboardEvent*)&e; - if (key.keysym.scancode == SDL_Scancode.SDL_SCANCODE_ESCAPE) { + u32 scancode = key.keysym.scancode; + if (scancode == SDL_Scancode.SDL_SCANCODE_ESCAPE) { run = false; } - if (key.keysym.scancode == SDL_Scancode.SDL_SCANCODE_D) { + if (scancode >= SDL_Scancode.SDL_SCANCODE_KP_1 && scancode <= SDL_Scancode.SDL_SCANCODE_KP_9) { doMove = true; - ++dx; - } else if (key.keysym.scancode == SDL_Scancode.SDL_SCANCODE_A) { - doMove = true; - --dx; - } else if (key.keysym.scancode == SDL_Scancode.SDL_SCANCODE_W) { - doMove = true; - --dy; - } else if (key.keysym.scancode == SDL_Scancode.SDL_SCANCODE_S) { - doMove = true; - ++dy; - } else if (key.keysym.scancode == SDL_Scancode.SDL_SCANCODE_LEFTBRACKET) { + } + + if (scancode == SDL_Scancode.SDL_SCANCODE_KP_1) { + dx = -1; dy = 1; + } else if (scancode == SDL_Scancode.SDL_SCANCODE_KP_2) { + dy = -1; dy = 1; + } else if (scancode == SDL_Scancode.SDL_SCANCODE_KP_3) { + dx = 1; dy = 1; + } else if (scancode == SDL_Scancode.SDL_SCANCODE_KP_4) { + dx = -1; + } else if (scancode == SDL_Scancode.SDL_SCANCODE_KP_5) { + // wait + if (map.game_state == GameState.PLAYER_TURN) + { + map.game_state = GameState.ENEMY_TURN; + continue; + } + } else if (scancode == SDL_Scancode.SDL_SCANCODE_KP_6) { + dx = 1; + } else if (scancode == SDL_Scancode.SDL_SCANCODE_KP_7) { + dx = -1; dy = -1; + } else if (scancode == SDL_Scancode.SDL_SCANCODE_KP_8) { + dy = -1; dy = -1; + } else if (scancode == SDL_Scancode.SDL_SCANCODE_KP_9) { + dx = 1; dy = -1; + } else if (scancode == SDL_Scancode.SDL_SCANCODE_LEFTBRACKET) { if (map.view_radius > 0) { --map.view_radius; recalc_fov = true; } - } else if (key.keysym.scancode == SDL_Scancode.SDL_SCANCODE_RIGHTBRACKET) { + } else if (scancode == SDL_Scancode.SDL_SCANCODE_RIGHTBRACKET) { ++map.view_radius; recalc_fov = true; } } - if (doMove && game_state == GameState.player_turn) + if (doMove && map.game_state == GameState.PLAYER_TURN) { i32 destx = player.x + dx; i32 desty = player.y + dy; if (!map.blocks_walk(destx, desty)) { - Entity* target = map.get_blocking_entities_at_location(destx, desty); + EntityId targetId = map.get_blocking_entities_at_location(destx, desty); - if (target != null) + if (targetId != EntityId.max) { - print("You kick the "); - print(target.name); - println(" in the shins, much to its annoyance!"); + attack(map, map.playerId, targetId); } else { recalc_fov = true; - player.move(dx, dy); + map.move_to(map.playerId, destx, desty); } - game_state = GameState.enemy_turn; + map.game_state = GameState.ENEMY_TURN; } } } - if (game_state == GameState.enemy_turn) + if (map.game_state == GameState.ENEMY_TURN) { - for (u32 i = 1; i < map.num_entities; ++i) + map.game_state = GameState.PLAYER_TURN; + for (EntityId i = 1; i < map.num_entities; ++i) { Entity* e = &map.entities[i]; - print("The "); - print(e.name); - println(" ponders the meaning of its existence."); + if (e.ai == AiId.max) continue; + do_ai(map, i, map.playerId); + if (map.game_state == GameState.PLAYER_DEAD) break; } - game_state = GameState.player_turn; } SDL_SetRenderDrawColor(win.sdl_renderer, 0, 0, 0, 0); @@ -484,6 +649,7 @@ i32 main(void* hInstance, void* hPrevInstance, u8* lpCmdLine, i32 nShowCmd) } render_all(&win, map); SDL_RenderPresent(win.sdl_renderer); + SDL_Delay(40); } window_destroy(&win); diff --git a/source/sdl.vx b/source/sdl.vx index 330d534..f1f6123 100644 --- a/source/sdl.vx +++ b/source/sdl.vx @@ -21,6 +21,7 @@ i32 SDL_SetRenderDrawBlendMode(SDL_Renderer* renderer, u32 blendMode); i32 SDL_SetTextureColorMod(SDL_Texture* texture, u8 r, u8 g, u8 b); i32 SDL_RenderDrawRect(SDL_Renderer* renderer, SDL_Rect* rect); i32 SDL_RenderFillRect(SDL_Renderer* renderer, SDL_Rect* rect); +void SDL_Delay(u32 ms); enum : u8 { SDL_BUTTON_LEFT = 1, @@ -73,13 +74,266 @@ struct SDL_Keysym { } enum SDL_Scancode : u32 { + SDL_SCANCODE_UNKNOWN = 0, + SDL_SCANCODE_A = 4, + SDL_SCANCODE_B = 5, + SDL_SCANCODE_C = 6, SDL_SCANCODE_D = 7, + SDL_SCANCODE_E = 8, + SDL_SCANCODE_F = 9, + SDL_SCANCODE_G = 10, + SDL_SCANCODE_H = 11, + SDL_SCANCODE_I = 12, + SDL_SCANCODE_J = 13, + SDL_SCANCODE_K = 14, + SDL_SCANCODE_L = 15, + SDL_SCANCODE_M = 16, + SDL_SCANCODE_N = 17, + SDL_SCANCODE_O = 18, + SDL_SCANCODE_P = 19, + SDL_SCANCODE_Q = 20, + SDL_SCANCODE_R = 21, SDL_SCANCODE_S = 22, + SDL_SCANCODE_T = 23, + SDL_SCANCODE_U = 24, + SDL_SCANCODE_V = 25, SDL_SCANCODE_W = 26, + SDL_SCANCODE_X = 27, + SDL_SCANCODE_Y = 28, + SDL_SCANCODE_Z = 29, + + SDL_SCANCODE_1 = 30, + SDL_SCANCODE_2 = 31, + SDL_SCANCODE_3 = 32, + SDL_SCANCODE_4 = 33, + SDL_SCANCODE_5 = 34, + SDL_SCANCODE_6 = 35, + SDL_SCANCODE_7 = 36, + SDL_SCANCODE_8 = 37, + SDL_SCANCODE_9 = 38, + SDL_SCANCODE_0 = 39, + + SDL_SCANCODE_RETURN = 40, SDL_SCANCODE_ESCAPE = 41, + SDL_SCANCODE_BACKSPACE = 42, + SDL_SCANCODE_TAB = 43, + SDL_SCANCODE_SPACE = 44, + + SDL_SCANCODE_MINUS = 45, + SDL_SCANCODE_EQUALS = 46, SDL_SCANCODE_LEFTBRACKET = 47, SDL_SCANCODE_RIGHTBRACKET = 48, + SDL_SCANCODE_BACKSLASH = 49, + SDL_SCANCODE_NONUSHASH = 50, + SDL_SCANCODE_SEMICOLON = 51, + SDL_SCANCODE_APOSTROPHE = 52, + SDL_SCANCODE_GRAVE = 53, + SDL_SCANCODE_COMMA = 54, + SDL_SCANCODE_PERIOD = 55, + SDL_SCANCODE_SLASH = 56, + + SDL_SCANCODE_CAPSLOCK = 57, + + SDL_SCANCODE_F1 = 58, + SDL_SCANCODE_F2 = 59, + SDL_SCANCODE_F3 = 60, + SDL_SCANCODE_F4 = 61, + SDL_SCANCODE_F5 = 62, + SDL_SCANCODE_F6 = 63, + SDL_SCANCODE_F7 = 64, + SDL_SCANCODE_F8 = 65, + SDL_SCANCODE_F9 = 66, + SDL_SCANCODE_F10 = 67, + SDL_SCANCODE_F11 = 68, + SDL_SCANCODE_F12 = 69, + + SDL_SCANCODE_PRINTSCREEN = 70, + SDL_SCANCODE_SCROLLLOCK = 71, + SDL_SCANCODE_PAUSE = 72, + SDL_SCANCODE_INSERT = 73, + SDL_SCANCODE_HOME = 74, + SDL_SCANCODE_PAGEUP = 75, + SDL_SCANCODE_DELETE = 76, + SDL_SCANCODE_END = 77, + SDL_SCANCODE_PAGEDOWN = 78, + SDL_SCANCODE_RIGHT = 79, + SDL_SCANCODE_LEFT = 80, + SDL_SCANCODE_DOWN = 81, + SDL_SCANCODE_UP = 82, + + SDL_SCANCODE_NUMLOCKCLEAR = 83, + SDL_SCANCODE_KP_DIVIDE = 84, + SDL_SCANCODE_KP_MULTIPLY = 85, + SDL_SCANCODE_KP_MINUS = 86, + SDL_SCANCODE_KP_PLUS = 87, + SDL_SCANCODE_KP_ENTER = 88, + SDL_SCANCODE_KP_1 = 89, + SDL_SCANCODE_KP_2 = 90, + SDL_SCANCODE_KP_3 = 91, + SDL_SCANCODE_KP_4 = 92, + SDL_SCANCODE_KP_5 = 93, + SDL_SCANCODE_KP_6 = 94, + SDL_SCANCODE_KP_7 = 95, + SDL_SCANCODE_KP_8 = 96, + SDL_SCANCODE_KP_9 = 97, + SDL_SCANCODE_KP_0 = 98, + SDL_SCANCODE_KP_PERIOD = 99, + + SDL_SCANCODE_NONUSBACKSLASH = 100, + SDL_SCANCODE_APPLICATION = 101, + SDL_SCANCODE_POWER = 102, + SDL_SCANCODE_KP_EQUALS = 103, + SDL_SCANCODE_F13 = 104, + SDL_SCANCODE_F14 = 105, + SDL_SCANCODE_F15 = 106, + SDL_SCANCODE_F16 = 107, + SDL_SCANCODE_F17 = 108, + SDL_SCANCODE_F18 = 109, + SDL_SCANCODE_F19 = 110, + SDL_SCANCODE_F20 = 111, + SDL_SCANCODE_F21 = 112, + SDL_SCANCODE_F22 = 113, + SDL_SCANCODE_F23 = 114, + SDL_SCANCODE_F24 = 115, + SDL_SCANCODE_EXECUTE = 116, + SDL_SCANCODE_HELP = 117, + SDL_SCANCODE_MENU = 118, + SDL_SCANCODE_SELECT = 119, + SDL_SCANCODE_STOP = 120, + SDL_SCANCODE_AGAIN = 121, + SDL_SCANCODE_UNDO = 122, + SDL_SCANCODE_CUT = 123, + SDL_SCANCODE_COPY = 124, + SDL_SCANCODE_PASTE = 125, + SDL_SCANCODE_FIND = 126, + SDL_SCANCODE_MUTE = 127, + SDL_SCANCODE_VOLUMEUP = 128, + SDL_SCANCODE_VOLUMEDOWN = 129, + SDL_SCANCODE_KP_COMMA = 133, + SDL_SCANCODE_KP_EQUALSAS400 = 134, + + SDL_SCANCODE_INTERNATIONAL1 = 135, + SDL_SCANCODE_INTERNATIONAL2 = 136, + SDL_SCANCODE_INTERNATIONAL3 = 137, + SDL_SCANCODE_INTERNATIONAL4 = 138, + SDL_SCANCODE_INTERNATIONAL5 = 139, + SDL_SCANCODE_INTERNATIONAL6 = 140, + SDL_SCANCODE_INTERNATIONAL7 = 141, + SDL_SCANCODE_INTERNATIONAL8 = 142, + SDL_SCANCODE_INTERNATIONAL9 = 143, + SDL_SCANCODE_LANG1 = 144, + SDL_SCANCODE_LANG2 = 145, + SDL_SCANCODE_LANG3 = 146, + SDL_SCANCODE_LANG4 = 147, + SDL_SCANCODE_LANG5 = 148, + SDL_SCANCODE_LANG6 = 149, + SDL_SCANCODE_LANG7 = 150, + SDL_SCANCODE_LANG8 = 151, + SDL_SCANCODE_LANG9 = 152, + + SDL_SCANCODE_ALTERASE = 153, + SDL_SCANCODE_SYSREQ = 154, + SDL_SCANCODE_CANCEL = 155, + SDL_SCANCODE_CLEAR = 156, + SDL_SCANCODE_PRIOR = 157, + SDL_SCANCODE_RETURN2 = 158, + SDL_SCANCODE_SEPARATOR = 159, + SDL_SCANCODE_OUT = 160, + SDL_SCANCODE_OPER = 161, + SDL_SCANCODE_CLEARAGAIN = 162, + SDL_SCANCODE_CRSEL = 163, + SDL_SCANCODE_EXSEL = 164, + + SDL_SCANCODE_KP_00 = 176, + SDL_SCANCODE_KP_000 = 177, + SDL_SCANCODE_THOUSANDSSEPARATOR = 178, + SDL_SCANCODE_DECIMALSEPARATOR = 179, + SDL_SCANCODE_CURRENCYUNIT = 180, + SDL_SCANCODE_CURRENCYSUBUNIT = 181, + SDL_SCANCODE_KP_LEFTPAREN = 182, + SDL_SCANCODE_KP_RIGHTPAREN = 183, + SDL_SCANCODE_KP_LEFTBRACE = 184, + SDL_SCANCODE_KP_RIGHTBRACE = 185, + SDL_SCANCODE_KP_TAB = 186, + SDL_SCANCODE_KP_BACKSPACE = 187, + SDL_SCANCODE_KP_A = 188, + SDL_SCANCODE_KP_B = 189, + SDL_SCANCODE_KP_C = 190, + SDL_SCANCODE_KP_D = 191, + SDL_SCANCODE_KP_E = 192, + SDL_SCANCODE_KP_F = 193, + SDL_SCANCODE_KP_XOR = 194, + SDL_SCANCODE_KP_POWER = 195, + SDL_SCANCODE_KP_PERCENT = 196, + SDL_SCANCODE_KP_LESS = 197, + SDL_SCANCODE_KP_GREATER = 198, + SDL_SCANCODE_KP_AMPERSAND = 199, + SDL_SCANCODE_KP_DBLAMPERSAND = 200, + SDL_SCANCODE_KP_VERTICALBAR = 201, + SDL_SCANCODE_KP_DBLVERTICALBAR = 202, + SDL_SCANCODE_KP_COLON = 203, + SDL_SCANCODE_KP_HASH = 204, + SDL_SCANCODE_KP_SPACE = 205, + SDL_SCANCODE_KP_AT = 206, + SDL_SCANCODE_KP_EXCLAM = 207, + SDL_SCANCODE_KP_MEMSTORE = 208, + SDL_SCANCODE_KP_MEMRECALL = 209, + SDL_SCANCODE_KP_MEMCLEAR = 210, + SDL_SCANCODE_KP_MEMADD = 211, + SDL_SCANCODE_KP_MEMSUBTRACT = 212, + SDL_SCANCODE_KP_MEMMULTIPLY = 213, + SDL_SCANCODE_KP_MEMDIVIDE = 214, + SDL_SCANCODE_KP_PLUSMINUS = 215, + SDL_SCANCODE_KP_CLEAR = 216, + SDL_SCANCODE_KP_CLEARENTRY = 217, + SDL_SCANCODE_KP_BINARY = 218, + SDL_SCANCODE_KP_OCTAL = 219, + SDL_SCANCODE_KP_DECIMAL = 220, + SDL_SCANCODE_KP_HEXADECIMAL = 221, + + SDL_SCANCODE_LCTRL = 224, + SDL_SCANCODE_LSHIFT = 225, + SDL_SCANCODE_LALT = 226, + SDL_SCANCODE_LGUI = 227, + SDL_SCANCODE_RCTRL = 228, + SDL_SCANCODE_RSHIFT = 229, + SDL_SCANCODE_RALT = 230, + SDL_SCANCODE_RGUI = 231, + + SDL_SCANCODE_MODE = 257, + + SDL_SCANCODE_AUDIONEXT = 258, + SDL_SCANCODE_AUDIOPREV = 259, + SDL_SCANCODE_AUDIOSTOP = 260, + SDL_SCANCODE_AUDIOPLAY = 261, + SDL_SCANCODE_AUDIOMUTE = 262, + SDL_SCANCODE_MEDIASELECT = 263, + SDL_SCANCODE_WWW = 264, + SDL_SCANCODE_MAIL = 265, + SDL_SCANCODE_CALCULATOR = 266, + SDL_SCANCODE_COMPUTER = 267, + SDL_SCANCODE_AC_SEARCH = 268, + SDL_SCANCODE_AC_HOME = 269, + SDL_SCANCODE_AC_BACK = 270, + SDL_SCANCODE_AC_FORWARD = 271, + SDL_SCANCODE_AC_STOP = 272, + SDL_SCANCODE_AC_REFRESH = 273, + SDL_SCANCODE_AC_BOOKMARKS = 274, + + SDL_SCANCODE_BRIGHTNESSDOWN = 275, + SDL_SCANCODE_BRIGHTNESSUP = 276, + SDL_SCANCODE_DISPLAYSWITCH = 277, + SDL_SCANCODE_KBDILLUMTOGGLE = 278, + SDL_SCANCODE_KBDILLUMDOWN = 279, + SDL_SCANCODE_KBDILLUMUP = 280, + SDL_SCANCODE_EJECT = 281, + SDL_SCANCODE_SLEEP = 282, + + SDL_SCANCODE_APP1 = 283, + SDL_SCANCODE_APP2 = 284, + + SDL_NUM_SCANCODES = 512 } enum u32 SDL_INIT_VIDEO = 0x00000020; diff --git a/source/tile.vx b/source/tile.vx index 972730a..0f45c98 100644 --- a/source/tile.vx +++ b/source/tile.vx @@ -12,7 +12,8 @@ enum TileFlags : u8 { struct Tile { u8 flags; - EntityId blocking_entity; + u8 max_render_order; + EntityId blocking_entity = EntityId.max; bool blocked() { return (flags & TileFlags.blocks_walk) != 0; } bool block_sight() { return (flags & TileFlags.blocks_sight) != 0; } bool visible() { return (flags & TileFlags.is_visible) != 0; } diff --git a/source/utils.vx b/source/utils.vx index c6901f8..0fb64ff 100644 --- a/source/utils.vx +++ b/source/utils.vx @@ -10,6 +10,11 @@ i32 max(i32 a, i32 b) { return b; } +i32 abs(i32 a) { + if (a < 0) return -a; + return a; +} + void print(u8[] str) { void* handle = GetStdHandle(STD_OUTPUT_HANDLE); u32 numWritten; @@ -21,29 +26,27 @@ void print(u8[] str) { null); } -void setRangeu8(u8* slice, i64 from, i64 to, u8 value) { - while(from < to) { - slice[from] = value; - ++from; +void setRangeu8(u8[] slice, u8 value) { + for (u64 i = 0; i < slice.length; ++i) { + slice[i] = value; } } void printInt(i64 i) { u8[21] buf; - u8[] res; - formatInt(i, &buf, 1, &res); + u8[] res = formatInt(i, &buf, 1); print(res); } -void formatInt(i64 i, u8[21]* output, u32 minSize, u8[]* result) +u8[] formatInt(i64 i, u8[21]* output, u32 minSize) { u32 numDigits = 0; if (i == 0) { if (minSize == 0) minSize = 1; - setRangeu8((*output).ptr, 21 - minSize, 21, '0'); + setRangeu8((*output)[21 - minSize..21], ' '); (*output)[20] = '0'; numDigits = minSize; } @@ -65,7 +68,7 @@ void formatInt(i64 i, u8[21]* output, u32 minSize, u8[]* result) } while (numDigits < minSize) { - (*output)[21 - ++numDigits] = '0'; + (*output)[21 - ++numDigits] = ' '; } if (neg) { @@ -75,22 +78,13 @@ void formatInt(i64 i, u8[21]* output, u32 minSize, u8[]* result) ++(*output)[20]; } } - //u8[] result; - (*result).ptr = (*output).ptr + (21 - numDigits); - (*result).length = numDigits; - //return result; - //result = (*output)[$-numDigits..$] + return (*output)[21 - numDigits..21]; } void println(u8[] str) { print(str); - u8[2] endl; - endl[0] = '\n'; - endl[1] = '\0'; - u8[] slice; - slice.ptr = cast(u8*)&endl; - slice.length = 1; - print(endl); + u8 endl = '\n'; + print((&endl)[0..1]); } i64 cstrlen(u8* str) { @@ -165,15 +159,25 @@ bool intersect(Rect a, Rect b) a.y1 <= b.y2 && a.y2 >= b.y1; } +enum LineVisitFlags : u32 { + SKIP_START = 1, + SKIP_END = 2, +} + // Line visiting algorithm // Calls callback for all cells between starting and ending position (inclusive) // Passes userData as first arg of callback -void tran_thong(i32 xstart, i32 ystart, i32 xend, i32 yend, bool function(void*, i32, i32) callback, void* userData) +// Stops traversal if callback returns true +// flags is set of LineVisitFlags +// - SKIP_START disables visit on first tile +// - SKIP_END disables visit on last tile +void tran_thong(i32 xstart, i32 ystart, i32 xend, i32 yend, bool function(void*, i32, i32) callback, void* userData, u32 flags) { i32 x = xstart; i32 y = ystart; - if (callback(userData, x, y)) return; + if (!(flags & LineVisitFlags.SKIP_START)) + if (callback(userData, x, y)) return; i32 deltax; i32 signdx; @@ -224,7 +228,9 @@ void tran_thong(i32 xstart, i32 ystart, i32 xend, i32 yend, bool function(void*, if (callback(userData, x, y)) return; } } - if (callback(userData, xend, yend)) return; + + if (!(flags & LineVisitFlags.SKIP_END)) + if (callback(userData, xend, yend)) return; } // Bresenham’s circle drawing algorithm