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 |