diff options
| -rw-r--r-- | .clang-format | 178 | ||||
| -rw-r--r-- | .gitignore | 2 | ||||
| -rw-r--r-- | CMakeLists.txt | 21 | ||||
| -rw-r--r-- | include/constants.lua | 10 | ||||
| -rw-r--r-- | include/default_proc.lua | 42 | ||||
| -rw-r--r-- | maps/lvl1/dng.map | 10 | ||||
| -rw-r--r-- | maps/lvl1/proc.lua | 4 | ||||
| -rw-r--r-- | src/Api.h | 71 | ||||
| -rw-r--r-- | src/Level.cpp | 108 | ||||
| -rw-r--r-- | src/Level.h | 45 | ||||
| -rw-r--r-- | src/main.cpp | 83 | 
11 files changed, 574 insertions, 0 deletions
diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..c0d69d8 --- /dev/null +++ b/.clang-format @@ -0,0 +1,178 @@ +--- +Language:        Cpp +# BasedOnStyle:  Mozilla +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignArrayOfStructures: None +AlignConsecutiveMacros: None +AlignConsecutiveAssignments: None +AlignConsecutiveBitFields: None +AlignConsecutiveDeclarations: None +AlignEscapedNewlines: Right +AlignOperands:   Align +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: true +AllowAllConstructorInitializersOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortEnumsOnASingleLine: true +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Inline +AllowShortLambdasOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: TopLevel +AlwaysBreakAfterReturnType: TopLevel +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: Yes +AttributeMacros: +  - __capability +BinPackArguments: false +BinPackParameters: false +BraceWrapping: +  AfterCaseLabel:  false +  AfterClass:      true +  AfterControlStatement: Never +  AfterEnum:       true +  AfterFunction:   true +  AfterNamespace:  false +  AfterObjCDeclaration: false +  AfterStruct:     true +  AfterUnion:      true +  AfterExternBlock: true +  BeforeCatch:     false +  BeforeElse:      false +  BeforeLambdaBody: false +  BeforeWhile:     false +  IndentBraces:    false +  SplitEmptyFunction: true +  SplitEmptyRecord: false +  SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeConceptDeclarations: true +BreakBeforeBraces: Mozilla +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeComma +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeComma +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit:     80 +CommentPragmas:  '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 2 +ContinuationIndentWidth: 2 +Cpp11BracedListStyle: false +DeriveLineEnding: true +DerivePointerAlignment: false +DisableFormat:   false +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: LogicalBlock +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: false +ForEachMacros: +  - foreach +  - Q_FOREACH +  - BOOST_FOREACH +IfMacros: +  - KJ_IF_MAYBE +IncludeBlocks:   Preserve +IncludeCategories: +  - Regex:           '^"(llvm|llvm-c|clang|clang-c)/' +    Priority:        2 +    SortPriority:    0 +    CaseSensitive:   false +  - Regex:           '^(<|"(gtest|gmock|isl|json)/)' +    Priority:        3 +    SortPriority:    0 +    CaseSensitive:   false +  - Regex:           '.*' +    Priority:        1 +    SortPriority:    0 +    CaseSensitive:   false +IncludeIsMainRegex: '(Test)?$' +IncludeIsMainSourceRegex: '' +IndentAccessModifiers: false +IndentCaseLabels: true +IndentCaseBlocks: false +IndentGotoLabels: true +IndentPPDirectives: None +IndentExternBlock: AfterExternBlock +IndentRequires:  false +IndentWidth:     2 +IndentWrappedFunctionNames: false +InsertTrailingCommas: None +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: true +LambdaBodyIndentation: Signature +MacroBlockBegin: '' +MacroBlockEnd:   '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 2 +ObjCBreakBeforeNestedBlockParam: true +ObjCSpaceAfterProperty: true +ObjCSpaceBeforeProtocolList: false +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 200 +PenaltyIndentedWhitespace: 0 +PointerAlignment: Left +PPIndentWidth:   -1 +ReferenceAlignment: Pointer +ReflowComments:  true +ShortNamespaceLines: 1 +SortIncludes:    CaseSensitive +SortJavaStaticImport: Before +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceAroundPointerQualifiers: Default +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles:  Never +SpacesInConditionalStatement: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInLineCommentPrefix: +  Minimum:         1 +  Maximum:         -1 +SpacesInParentheses: false +SpacesInSquareBrackets: false +SpaceBeforeSquareBrackets: false +BitFieldColonSpacing: Both +Standard:        Latest +StatementAttributeLikeMacros: +  - Q_EMIT +StatementMacros: +  - Q_UNUSED +  - QT_REQUIRE_VERSION +TabWidth:        8 +UseCRLF:         false +UseTab:          Never +WhitespaceSensitiveMacros: +  - STRINGIZE +  - PP_STRINGIZE +  - BOOST_PP_STRINGIZE +  - NS_SWIFT_NAME +  - CF_SWIFT_NAME +... + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8e24b65 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea +cmake-build-debug diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..3dcce6a --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,21 @@ +cmake_minimum_required(VERSION 3.23) +project(dng) + +set(CMAKE_CXX_STANDARD 23) + +find_package(Lua REQUIRED) + +# check what features I use and assert minimum +if (LUA_VERSION_STRING VERSION_LESS "5.4") +    message(FATAL_ERROR "Invalid Lau version: ${LUA_VERSION_STRING} - must be >= 5.4") +endif() + +set(SFML_LIBRARIES sfml-system sfml-window sfml-graphics) +find_package(SFML 2.5 REQUIRED COMPONENTS system window graphics) + +#file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/res DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) +#file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/maps DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) +#file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/include DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) + +add_executable(${PROJECT_NAME} src/main.cpp src/Level.cpp src/Level.h src/Api.h) +target_link_libraries(${PROJECT_NAME} ${LUA_LIBRARIES} ${SFML_LIBRARIES}) diff --git a/include/constants.lua b/include/constants.lua new file mode 100644 index 0000000..5d8e860 --- /dev/null +++ b/include/constants.lua @@ -0,0 +1,10 @@ +KEY_W = 119 +KEY_A = 97 +KEY_S = 115 +KEY_D = 100 +KEY_SPACE = ' ' + + +TILE_WALL = 'w' +TILE_SPACE = ' ' +TILE_ENEMY = 'e'
\ No newline at end of file diff --git a/include/default_proc.lua b/include/default_proc.lua new file mode 100644 index 0000000..13ef15d --- /dev/null +++ b/include/default_proc.lua @@ -0,0 +1,42 @@ +--[[ +These are the default implementations of the override actions. +If you want to add custom logic into your game you can define a "proc.lua" in your map dir. + +The following functions are also available via our C library: + +void c_update_player_pos (dx, dy) +boolean c_player_can_move (dx, dy) +boolean c_enemy_can_move (id, dx, dy) +c_spawn_enemy (x, y) +c_destroy_enemy (id) +c_trigger_win() +c_trigger_loss(msg) +c_fatal(msg) + +--]] + +require "include.constants"; + +---@param pressedKey number +function onKeyPress(pressedKey) + +    dx = 0 +    dy = 0 +    if (pressedKey == KEY_W) then +        dy = -1 +    elseif pressedKey == KEY_A then +        dx = -1 +    elseif pressedKey == KEY_S then +        dy = 1 +    elseif pressedKey == KEY_D then +        dx = 1 +    end + +    if c_player_can_move(dx, dy) then +        c_update_player_pos(dx, dy) +    end +end + +function onUpdate() + +end
\ No newline at end of file diff --git a/maps/lvl1/dng.map b/maps/lvl1/dng.map new file mode 100644 index 0000000..fb9ceb1 --- /dev/null +++ b/maps/lvl1/dng.map @@ -0,0 +1,10 @@ +w w w w w w w w w w +w 0 0 0 0 0 0 0 0 w +w 0 w w 0 w w 0 0 w +w p 0 0 0 e 0 w 0 w +w 0 w w w 0 w w 0 w +w 0 w w w 0 w w w w +w 0 w t w 0 0 0 0 w +w 0 w 0 w w w w 0 w +w 0 0 0 w 0 0 0 0 w +w w w w w w w w w w diff --git a/maps/lvl1/proc.lua b/maps/lvl1/proc.lua new file mode 100644 index 0000000..fec9ed5 --- /dev/null +++ b/maps/lvl1/proc.lua @@ -0,0 +1,4 @@ +require "include.constants"; + +function onKeyPress(key) +end
\ No newline at end of file diff --git a/src/Api.h b/src/Api.h new file mode 100644 index 0000000..da763c2 --- /dev/null +++ b/src/Api.h @@ -0,0 +1,71 @@ +#ifndef DNG_API_H +#define DNG_API_H + +#include "Level.h" +#include <lua.hpp> +#include <memory> + +extern std::shared_ptr<Level> lvl; + +static int +c_update_player_pos(lua_State* L) +{ +  // stack ordering +  int dy = static_cast<int>(lua_tonumber(L, -1)); +  int dx = static_cast<int>(lua_tonumber(L, -2)); + +  bool res = false; + +  if (lvl->canStep(dx, dy)) { +    lvl->player.x += dx; +    lvl->player.y += dy; +    res = true; +  } + +  lua_pushboolean(L, res); + +  return 1; +} + +static int +c_player_can_move(lua_State* L) +{ +  // stack ordering +  int dy = static_cast<int>(lua_tonumber(L, -1)); +  int dx = static_cast<int>(lua_tonumber(L, -2)); + +  bool res = lvl->canStep(dx, dy); +  lua_pushboolean(L, res); + +  return 1; +} + +static int +c_enemy_can_move(lua_State* L) +{ +  return 1; +} + +static int +c_spawn_enemy(lua_State* L) +{ +  return 1; +} + +static int +c_destroy_enemy(lua_State* L) +{ +  return 1; +} + +static void +init_c_api(lua_State* L) +{ +  lua_register(L, "c_update_player_pos", c_update_player_pos); +  lua_register(L, "c_player_can_move", c_player_can_move); +  lua_register(L, "c_enemy_can_move", c_enemy_can_move); +  lua_register(L, "c_spawn_enemy", c_spawn_enemy); +  lua_register(L, "c_destroy_enemy", c_destroy_enemy); +} + +#endif // DNG_API_H
\ No newline at end of file diff --git a/src/Level.cpp b/src/Level.cpp new file mode 100644 index 0000000..013c2a1 --- /dev/null +++ b/src/Level.cpp @@ -0,0 +1,108 @@ +#include "Level.h" +#include <fstream> +#include <iostream> +#include <string> + +void +Level::loadLevelFromFile(const char* filePath) +{ +  std::ifstream mapFile(filePath); +  if (mapFile.is_open()) { + +    // each element in the map has a unique ID +    // some magic but player is always 0 +    const int playerId = 0; +    // from 1 -> N each enemy and treasure has its  own unique ID +    // IDs are unique entirely, not just per enemy or treasure + + +    std::string line; +    int y = 0; +    while (std::getline(mapFile, line)) { +      this->map.emplace_back(); +      int x = 0; +      for (char c : line) { +        if (c == WALL_TKN) { +          this->map[y].push_back(WALL_SPACE); +        } else if (c == EMPTY_TKN) { +          this->map[y].push_back(BLANK_SPACE); +        } else if (c == ENEMY_TKN) { +          this->enemyPositions.push_back( +            { .id = this->nextId(), .x = x, .y = y }); +          this->map[y].push_back(BLANK_SPACE); +        } else if (c == PLAYER_TKN) { +          this->player = { .id = playerId, .x = x, .y = y }; +          this->map[y].push_back(BLANK_SPACE); +        } else if (c == TREASURE_TKN) { +          this->treasurePositions.push_back( +            { .id = this->nextId(), .x = x, .y = y }); +          this->map[y].push_back(BLANK_SPACE); +        } else { +          continue; +        } +        ++x; +      } +      ++y; +    } +  } +  mapFile.close(); +} + +bool +Level::isEmpty(int x, int y) +{ +  return map[y][x] == BLANK_SPACE; +} + +bool +Level::canStep(int dx, int dy) +{ +  bool res = map[player.y + dy][player.x + dx] != WALL_SPACE; +  return res; +} + +void +Level::print() +{ +  int x = 0; +  int y = 0; +  for (auto& row : map) { +    for (auto& tile : row) { +      bool printed = false; +      if (player.x == x && player.y == y) { +        std::cout << "p"; +        printed = true; +      } +      for (auto pos : enemyPositions) { +        if (pos.x == x && pos.y == y) { +          std::cout << "e"; +          printed = true; +        } +      } +      for (auto pos : treasurePositions) { +        if (pos.x == x && pos.y == y) { +          std::cout << "t"; +          printed = true; +        } +      } +      if (tile == WALL_SPACE) { +        std::cout << tile; +        printed = true; +      } +      if (!printed) { +        std::cout << " "; +      } +      std::cout << " "; +      ++x; +    } +    std::cout << "\n"; +    ++y; +    x = 0; +  } +} + +int +Level::nextId() +{ +  return idCounter++; +}
\ No newline at end of file diff --git a/src/Level.h b/src/Level.h new file mode 100644 index 0000000..1607f0c --- /dev/null +++ b/src/Level.h @@ -0,0 +1,45 @@ +#ifndef DNG_LEVEL_H +#define DNG_LEVEL_H + +static const char PLAYER_TKN = 'p'; +static const char WALL_TKN = 'w'; +static const char EMPTY_TKN = '0'; +static const char TREASURE_TKN = 't'; +static const char ENEMY_TKN = 'e'; +static const char BLANK_SPACE = '\0'; +static const char WALL_SPACE = '#'; + +#include <memory> +#include <vector> + +struct Pos +{ +  int id; +  int x; +  int y; +} typedef Coord; + +class Level +{ + +public: +  void loadLevelFromFile(const char* filePath); + +  bool isEmpty(int x, int y); + +  bool canStep(int dx, int dy); + +  void print(); + +  int nextId(); + +  std::vector<std::vector<char>> map; +  Pos player; +  std::vector<Pos> enemyPositions; +  std::vector<Pos> treasurePositions; + +private: +  int idCounter = 1; // defaults at 1 (player always 0) +}; + +#endif // DNG_LEVEL_H diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..fb61369 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,83 @@ +#include "Api.h" +#include "Level.h" +#include <SFML/Graphics.hpp> +#include <filesystem> +#include <iostream> +#include <lua.hpp> +#include <memory> + +const char* DEFAULT_PROC = "include/default_proc.lua"; +std::shared_ptr<Level> lvl = std::make_shared<Level>(); + +bool +call_onkeypress(lua_State* L, char pressedKey); + +int +main(int argc, char** argv) +{ + +  if (argc <= 1) { +    return -1; +  } + +  std::string lvl_pfx = argv[1]; + +  std::filesystem::path mapFile{ lvl_pfx + "/dng.map" }; +  std::filesystem::path luaFile{ lvl_pfx + "/proc.lua" }; + +  lvl->loadLevelFromFile(mapFile.c_str()); + +  lua_State* L_lvl = luaL_newstate(); +  luaL_openlibs(L_lvl); +  init_c_api(L_lvl); + +  lua_State* L_default = luaL_newstate(); +  luaL_openlibs(L_default); +  init_c_api(L_default); + +  if (std::filesystem::exists(luaFile) && +      luaL_dofile(L_default, DEFAULT_PROC) != LUA_OK) { +    std::cout << "Failed to load default proc" << std::endl; +    luaL_error(L_default, "Error: %s", lua_tostring(L_default, -1)); +    return EXIT_FAILURE; +  } + +  if (std::filesystem::exists(luaFile) && +      luaL_dofile(L_lvl, luaFile.c_str()) != LUA_OK) { +    std::cout << "[C] No Good" << std::endl; +    luaL_error(L_lvl, "Error: %s\n", lua_tostring(L_lvl, -1)); +    return EXIT_FAILURE; +  } + +  bool quit = false; + +  char in; + +  do { +    lvl->print(); +    std::cin >> in; +    if (!call_onkeypress(L_default, in)) { +      quit = true; +    } +    if (!quit && in == 'q') { +      quit = true; +    } +  } while (!quit); + +  std::cout << "[C] Quit" << std::endl; + +  return EXIT_SUCCESS; +} + +bool +call_onkeypress(lua_State* L, char pressedKey) +{ +  lua_getglobal(L, "onKeyPress"); +  if (!lua_isfunction(L, -1)) { +    std::cout << "[C] Error onKeyPress not function | not found" << std::endl; +    return false; +  } +  lua_pushinteger(L, pressedKey); +  lua_pcall(L, 1, 1, 0); +  return true; +}
\ No newline at end of file  |