From eaaf57114cfb74c29f1b0c4e9786bed6d225225c Mon Sep 17 00:00:00 2001 From: Steph Enders Date: Wed, 15 Jun 2022 23:36:43 -0400 Subject: Initial setup commit This setups up the project for messing around with C++ and Lua bindings. So far this project just prints the defined dng map and lets you move the character around. What this fails to do is actually provide any reason to use Lua at the moment. So I need to figure out some way of enabling logic on the processing side of things. --- .clang-format | 178 +++++++++++++++++++++++++++++++++++++++++++++++ .gitignore | 2 + CMakeLists.txt | 21 ++++++ include/constants.lua | 10 +++ include/default_proc.lua | 42 +++++++++++ maps/lvl1/dng.map | 10 +++ maps/lvl1/proc.lua | 4 ++ src/Api.h | 71 +++++++++++++++++++ src/Level.cpp | 108 ++++++++++++++++++++++++++++ src/Level.h | 45 ++++++++++++ src/main.cpp | 83 ++++++++++++++++++++++ 11 files changed, 574 insertions(+) create mode 100644 .clang-format create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 include/constants.lua create mode 100644 include/default_proc.lua create mode 100644 maps/lvl1/dng.map create mode 100644 maps/lvl1/proc.lua create mode 100644 src/Api.h create mode 100644 src/Level.cpp create mode 100644 src/Level.h create mode 100644 src/main.cpp 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 +#include + +extern std::shared_ptr lvl; + +static int +c_update_player_pos(lua_State* L) +{ + // stack ordering + int dy = static_cast(lua_tonumber(L, -1)); + int dx = static_cast(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(lua_tonumber(L, -1)); + int dx = static_cast(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 +#include +#include + +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 +#include + +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> map; + Pos player; + std::vector enemyPositions; + std::vector 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 +#include +#include +#include +#include + +const char* DEFAULT_PROC = "include/default_proc.lua"; +std::shared_ptr lvl = std::make_shared(); + +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 -- cgit v1.2.3-54-g00ecf