From 8e101f8bfd995321ac08a16fea9a171b549a0ae4 Mon Sep 17 00:00:00 2001 From: Steph Enders Date: Sat, 17 Dec 2022 11:04:20 -0500 Subject: Support doors with keys Add initial support for doors and keys via pre-defined mappings: k || d ------ 1 -> a 2 -> b 3 -> c 4 -> d Any key can open any door of its mapping, but is spent once used. May require additional testing --- src/CApi.h | 104 ++++++++++++++++++++++++++++++++++++++++---------------- src/Level.cpp | 53 ++++++++++++++++++++++++++--- src/Level.h | 11 ++++++ src/SfmlUtils.h | 38 ++++++++++++++++++++- src/main.cpp | 7 +++- 5 files changed, 178 insertions(+), 35 deletions(-) (limited to 'src') diff --git a/src/CApi.h b/src/CApi.h index 4d35939..c6ffea3 100644 --- a/src/CApi.h +++ b/src/CApi.h @@ -35,6 +35,25 @@ extern std::shared_ptr lvl; extern Scene scene; +void push_position_table(lua_State *L, std::vector positions) { + lua_createtable(L, int(positions.size()), 0); + int idx = 0; + + for (auto &pos : positions) { + lua_pushnumber(L, ++idx); + lua_createtable(L, 0, 3); + lua_pushnumber(L, pos.token); + lua_setfield(L, -2, "token"); + lua_pushnumber(L, pos.id); + lua_setfield(L, -2, "id"); + lua_pushnumber(L, pos.x + 1); + lua_setfield(L, -2, "x"); + lua_pushnumber(L, pos.y + 1); + lua_setfield(L, -2, "y"); + lua_settable(L, -3); + } +} + /* * c_get_player_position(int x, int y) */ @@ -103,22 +122,7 @@ static int c_move_enemy(lua_State *L) { * c_get_enemies() */ static int c_get_enemies(lua_State *L) { - lua_createtable(L, int(lvl->enemyPositions.size()), 0); - - int idx = 0; - - for (auto &pos : lvl->enemyPositions) { - lua_pushnumber(L, ++idx); - lua_createtable(L, 0, 3); - lua_pushnumber(L, pos.id); - lua_setfield(L, -2, "id"); - lua_pushnumber(L, pos.x + 1); - lua_setfield(L, -2, "x"); - lua_pushnumber(L, pos.y + 1); - lua_setfield(L, -2, "y"); - lua_settable(L, -3); - } - + push_position_table(L, lvl->enemyPositions); return 1; } @@ -184,19 +188,7 @@ static int c_get_scene(lua_State *L) { } static int c_get_treasures(lua_State *L) { - lua_createtable(L, static_cast(lvl->treasurePositions.size()), 0); - int idx = 0; - for (auto &t : lvl->treasurePositions) { - lua_pushnumber(L, ++idx); - lua_createtable(L, 0, 3); - lua_pushnumber(L, t.y + 1); - lua_setfield(L, -2, "y"); - lua_pushnumber(L, t.x + 1); - lua_setfield(L, -2, "x"); - lua_pushnumber(L, t.id); - lua_setfield(L, -2, "id"); - lua_settable(L, -3); - } + push_position_table(L, lvl->treasurePositions); return 1; } @@ -223,6 +215,56 @@ static int c_trigger_restart(lua_State *L) { return 1; } +static int c_get_doors(lua_State *L) { + push_position_table(L, lvl->doorPositions); + return 1; +} + +/** + * c_open_door(id) + * if you have a key it will open the door and use the key + */ +static int c_open_door(lua_State *L) { + int id = static_cast(lua_tonumber(L, -1)); + bool can_open = false; + for (int i = 0; i < lvl->doorPositions.size(); i++) { + if (lvl->doorPositions[i].id == id) { + char c = lvl->doorPositions[i].token; + for (int k = lvl->heldKeys.size() - 1; k >= 0; k--) { + char mapped_door = KEY_DOOR_MAPPING[lvl->heldKeys[k] - KEY_TKN_START]; + if (mapped_door == c) { + can_open = true; + // erase key + lvl->heldKeys.erase(lvl->heldKeys.begin() + k); + lvl->doorPositions.erase(lvl->doorPositions.begin() + i); + lvl->map[lvl->doorPositions[i].y][lvl->doorPositions[i].x] = + BLANK_SPACE; + break; + } + } + } + } + + return 1; +} + +static int c_get_keys(lua_State *L) { + push_position_table(L, lvl->keyPositions); + return 1; +} + +static int c_take_key(lua_State *L) { + int id = static_cast(lua_tonumber(L, -1)); + for (int i = 0; i < lvl->keyPositions.size(); i++) { + if (lvl->keyPositions[i].id == id) { + lvl->heldKeys.push_back(lvl->keyPositions[i].token); + lvl->keyPositions.erase(lvl->keyPositions.begin() + i); + break; + } + } + return 1; +} + // not for lua use void init_c_api(lua_State *L) { lua_register(L, "c_move_player", c_move_player); @@ -239,6 +281,10 @@ void init_c_api(lua_State *L) { lua_register(L, "c_score_treasure", c_score_treasure); lua_register(L, "c_get_treasures", c_get_treasures); lua_register(L, "c_trigger_restart", c_trigger_restart); + lua_register(L, "c_get_doors", c_get_doors); + lua_register(L, "c_open_door", c_open_door); + lua_register(L, "c_get_keys", c_get_keys); + lua_register(L, "c_take_key", c_take_key); } #endif // DNG_CAPI_H diff --git a/src/Level.cpp b/src/Level.cpp index 5c8fbe0..3a013b7 100644 --- a/src/Level.cpp +++ b/src/Level.cpp @@ -63,17 +63,28 @@ void Level::load() { } else if (c == ENEMY_TKN) { auto e = create_enemy(x, y); this->enemyPositions.push_back( - {.id = this->nextId(), .x = x, .y = y, .sprite = e}); + {.token = c, .id = this->nextId(), .x = x, .y = y, .sprite = e}); this->map[y].push_back(BLANK_SPACE); } else if (c == PLAYER_TKN) { auto p = create_player(x, y); - this->player = {.id = playerId, .x = x, .y = y, .sprite = p}; + this->player = { + .token = c, .id = playerId, .x = x, .y = y, .sprite = p}; this->map[y].push_back(BLANK_SPACE); } else if (c == TREASURE_TKN) { auto t = create_treasure(x, y); this->treasurePositions.push_back( - {.id = this->nextId(), .x = x, .y = y, .sprite = t}); + {.token = c, .id = this->nextId(), .x = x, .y = y, .sprite = t}); this->map[y].push_back(BLANK_SPACE); + } else if (c >= KEY_TKN_START && c <= KEY_TKN_END) { + auto k = create_key(c, x, y); + this->keyPositions.push_back( + {.token = c, .id = this->nextId(), .x = x, .y = y, .sprite = k}); + this->map[y].push_back(BLANK_SPACE); + } else if (c >= DOOR_TKN_START && c <= DOOR_TKN_END) { + auto d = create_door(c, x, y); + this->doorPositions.push_back( + {.token = c, .id = this->nextId(), .x = x, .y = y, .sprite = d}); + this->map[y].push_back(WALL_SPACE); } else { continue; } @@ -99,11 +110,45 @@ void Level::reset() { this->treasurePositions.clear(); this->displayMap.clear(); this->enemyPositions.clear(); + this->keyPositions.clear(); + this->doorPositions.clear(); this->load(); } bool Level::playerCanStep(int dx, int dy) const { - return canStep(player, dx, dy, map); + bool check_wall = canStep(player, dx, dy, map); + + auto new_pos_x = player.x + dx; + auto new_pos_y = player.y + dy; + return check_wall || + (isDoor(new_pos_x, new_pos_y) && tryDoor(new_pos_x, new_pos_y)); +} + +bool Level::isDoor(int x, int y) const { + for (auto &d : doorPositions) { + if (d.x == x && d.y == y) { + return true; + } + } + return false; +} + +bool Level::tryDoor(int x, int y) const { + + for (auto &d : doorPositions) { + if (d.x == x && d.y == y) { + char door_token = d.token; + for (auto &k : heldKeys) { + if (KEY_DOOR_MAPPING[k - KEY_TKN_START] == door_token) { + return true; + } + } + // matched door pos but not openable + return false; + } + } + // not a door? + return false; } int Level::nextId() { return idCounter++; } diff --git a/src/Level.h b/src/Level.h index 7b25d0b..7c2c74b 100644 --- a/src/Level.h +++ b/src/Level.h @@ -39,8 +39,14 @@ static const char TREASURE_TKN = 't'; static const char ENEMY_TKN = 'e'; static const char BLANK_SPACE = '\0'; static const char WALL_SPACE = '#'; +static const char KEY_TKN_START = '1'; // inclusive (1, 2, 3, 4) +static const char KEY_TKN_END = '4'; // inclusive (1, 2, 3, 4) +static const char DOOR_TKN_START = 'a'; // inclusive (a, b, c, d) +static const char DOOR_TKN_END = 'd'; // inclusive (a, b, c, d) +static const char KEY_DOOR_MAPPING[4] = {'a', 'b', 'c', 'd'}; struct Pos { + char token; int id; int x; int y; @@ -54,6 +60,8 @@ public: ~Level() = default; void load(); bool playerCanStep(int dx, int dy) const; + bool tryDoor(int x, int y) const; + bool isDoor(int x, int y) const; int getEnemyIndex(int id); bool enemyCanStep(const Pos &pos, int dx, int dy) const; void reset(); @@ -64,8 +72,11 @@ public: std::vector> map; // source copy of map std::vector displayMap; Pos player; + std::vector heldKeys; std::vector enemyPositions; std::vector treasurePositions; + std::vector doorPositions; + std::vector keyPositions; private: int idCounter = 1; // defaults at 1 (player always 0) diff --git a/src/SfmlUtils.h b/src/SfmlUtils.h index dd63cd6..86d5f62 100644 --- a/src/SfmlUtils.h +++ b/src/SfmlUtils.h @@ -63,6 +63,14 @@ inline sf::RectangleShape create_square(sf::Color color, int x, int y) { return rect; } +inline sf::RectangleShape create_small_square(sf::Color color, int x, int y) { + sf::RectangleShape rect({SPRITE_SIZE - 4.f, SPRITE_SIZE - 4.f}); + rect.setFillColor(color); + auto pos = to_position_xy(x, y); + rect.setPosition({pos.x + 2.f, pos.y + 2.f}); + return rect; +} + inline sf::RectangleShape create_wall(int x, int y) { return create_square(WALL_COLOR, x, y); } @@ -79,6 +87,34 @@ inline sf::RectangleShape create_treasure(int x, int y) { return create_square(sf::Color::Yellow, x, y); } +inline sf::RectangleShape create_key(char t, int x, int y) { + switch (t) { + case '1': + return create_small_square(sf::Color::Blue, x, y); + case '2': + return create_small_square(sf::Color::Green, x, y); + case '3': + return create_small_square(sf::Color::Black, x, y); + case '4': + default: + return create_small_square(sf::Color::White, x, y); + } +} + +inline sf::RectangleShape create_door(char t, int x, int y) { + switch (t) { + case 'a': + return create_square(sf::Color::Blue, x, y); + case 'b': + return create_square(sf::Color::Green, x, y); + case 'c': + return create_square(sf::Color::Black, x, y); + case 'd': + default: + return create_square(sf::Color::White, x, y); + } +} + inline sf::Vector2f round(const sf::Vector2f vector) { return sf::Vector2f{std::round(vector.x), std::round(vector.y)}; } @@ -107,4 +143,4 @@ inline sf::Text write_text(const char *msg, unsigned int fontSize, return text; } -#endif // DNG_SFML_UTILS_H \ No newline at end of file +#endif // DNG_SFML_UTILS_H diff --git a/src/main.cpp b/src/main.cpp index 38208ec..796c3f9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -238,9 +238,14 @@ int main(int argc, char **argv) { window.draw(enemy.sprite); } for (auto &treasure : lvl->treasurePositions) { - treasure.sprite.setPosition(to_position(treasure)); window.draw(treasure.sprite); } + for (auto &key : lvl->keyPositions) { + window.draw(key.sprite); + } + for (auto &door : lvl->doorPositions) { + window.draw(door.sprite); + } } if (scene != Scene::LOSS) { -- cgit v1.2.3-54-g00ecf