diff options
34 files changed, 2499 insertions, 0 deletions
| diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..b806607 --- /dev/null +++ b/.clang-format @@ -0,0 +1,178 @@ +--- +Language:        Cpp +# BasedOnStyle:  LLVM +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: true +AllowShortEnumsOnASingleLine: true +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: All +AllowShortLambdasOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: MultiLine +AttributeMacros: +  - __capability +BinPackArguments: true +BinPackParameters: true +BraceWrapping: +  AfterCaseLabel:  false +  AfterClass:      false +  AfterControlStatement: Never +  AfterEnum:       false +  AfterFunction:   false +  AfterNamespace:  false +  AfterObjCDeclaration: false +  AfterStruct:     false +  AfterUnion:      false +  AfterExternBlock: false +  BeforeCatch:     false +  BeforeElse:      false +  BeforeLambdaBody: false +  BeforeWhile:     false +  IndentBraces:    false +  SplitEmptyFunction: true +  SplitEmptyRecord: true +  SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeConceptDeclarations: true +BreakBeforeBraces: Attach +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeColon +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit:     80 +CommentPragmas:  '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DeriveLineEnding: true +DerivePointerAlignment: false +DisableFormat:   false +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: LogicalBlock +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +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: false +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: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 60 +PenaltyIndentedWhitespace: 0 +PointerAlignment: Right +PPIndentWidth:   -1 +ReferenceAlignment: Pointer +ReflowComments:  true +ShortNamespaceLines: 1 +SortIncludes:    CaseSensitive +SortJavaStaticImport: Before +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +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/.copyright_headers/C_HEADER.txt b/.copyright_headers/C_HEADER.txt new file mode 100644 index 0000000..f51ad28 --- /dev/null +++ b/.copyright_headers/C_HEADER.txt @@ -0,0 +1,25 @@ +//======================================================================== +// dng +//------------------------------------------------------------------------ +// Copyright (c) 2022 Steph Enders <steph@senders.io> +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +//    claim that you wrote the original software. If you use this software +//    in a product, an acknowledgment in the product documentation would +//    be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +//    be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +//    distribution. +// +//======================================================================== diff --git a/.copyright_headers/LUA_HEADER.txt b/.copyright_headers/LUA_HEADER.txt new file mode 100644 index 0000000..4204e33 --- /dev/null +++ b/.copyright_headers/LUA_HEADER.txt @@ -0,0 +1,25 @@ +-- ----------------------------------------------------------------------- +-- dng +-- ------------------------------------------------------------------------ +-- Copyright (c) 2022 Steph Enders <steph@senders.io> +-- +-- This software is provided 'as-is', without any express or implied +-- warranty. In no event will the authors be held liable for any damages +-- arising from the use of this software. +-- +-- Permission is granted to anyone to use this software for any purpose, +-- including commercial applications, and to alter it and redistribute it +-- freely, subject to the following restrictions: +-- +-- 1. The origin of this software must not be misrepresented; you must not +--    claim that you wrote the original software. If you use this software +--    in a product, an acknowledgment in the product documentation would +--    be appreciated but is not required. +-- +-- 2. Altered source versions must be plainly marked as such, and must not +--    be misrepresented as being the original software. +-- +-- 3. This notice may not be removed or altered from any source +--    distribution. +-- +-- ----------------------------------------------------------------------- diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..810508e --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +.idea +cmake-build* +build +Makefile +.cmake +CMakeFiles +cmake_install.cmake +CMakeCache.txt diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..93b742b --- /dev/null +++ b/.gitmodules @@ -0,0 +1,8 @@ +[submodule "thirdparty/SFML"] +	path = thirdparty/SFML +	url = https://github.com/SFML/SFML +	branch = 2.5.x +[submodule "thirdparty/lua"] +	path = thirdparty/lua +	url = https://github.com/lua/lua +	branch = v5.4.0-patch diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..69cd9b2 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,96 @@ +cmake_minimum_required(VERSION 3.16) +project(dng +        VERSION 0.1.0) +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_INSTALL_PREFIX ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-${PROJECT_VERSION}) + +# Setup Lua Libraries +# To determine which files look at the lua/makefile to see how the targets are built +set(LUA_SRC ${PROJECT_SOURCE_DIR}/thirdparty/lua) +add_library(lua STATIC +        ${LUA_SRC}/lapi.c +        ${LUA_SRC}/lcode.c +        ${LUA_SRC}/lctype.c +        ${LUA_SRC}/ldebug.c +        ${LUA_SRC}/ldo.c +        ${LUA_SRC}/ldump.c +        ${LUA_SRC}/lfunc.c +        ${LUA_SRC}/lgc.c +        ${LUA_SRC}/llex.c +        ${LUA_SRC}/lmem.c +        ${LUA_SRC}/lobject.c +        ${LUA_SRC}/lopcodes.c +        ${LUA_SRC}/lparser.c +        ${LUA_SRC}/lstate.c +        ${LUA_SRC}/lstring.c +        ${LUA_SRC}/ltable.c +        ${LUA_SRC}/ltm.c +        ${LUA_SRC}/lundump.c +        ${LUA_SRC}/lvm.c +        ${LUA_SRC}/lzio.c +        ${LUA_SRC}/ltests.c) +add_library(lauxlib STATIC ${LUA_SRC}/lauxlib.c) +add_library(lualib STATIC +        ${LUA_SRC}/lbaselib.c +        ${LUA_SRC}/lcorolib.c +        ${LUA_SRC}/ldblib.c +        ${LUA_SRC}/linit.c +        ${LUA_SRC}/liolib.c +        ${LUA_SRC}/lmathlib.c +        ${LUA_SRC}/loadlib.c +        ${LUA_SRC}/loslib.c +        ${LUA_SRC}/lstrlib.c +        ${LUA_SRC}/ltablib.c +        ${LUA_SRC}/lutf8lib.c) +target_compile_definitions(lualib PRIVATE -DLUA_USE_POSIX=1) # to properly use mkstemp +target_include_directories(lua PUBLIC lua) +target_include_directories(lauxlib PUBLIC lua) +target_include_directories(lualib PUBLIC lua) +target_link_libraries(lauxlib lua) +set(LUA_LIBRARIES lua lualib lauxlib) +include_directories(${PROJECT_SOURCE_DIR}/thirdparty/lua) + +# System specific includes +if (UNIX) +    set(RES_LIB src/linux/res.h) +endif () + +add_executable(${PROJECT_NAME} src/main.cpp src/Level.cpp src/Level.h src/CApi.h src/Scene.h src/LuaApi.h src/MessageBox.h ${RES_LIB}) +target_link_libraries(${PROJECT_NAME} PUBLIC ${LUA_LIBRARIES}) + +# configure and link SFML +set(BUILD_SHARED_LIBS FALSE) +set(SFML_LIBRARIES sfml-system sfml-window sfml-graphics) +if (WIN32) +    set(SFML_USE_STATIC_STD_LIBS TRUE) +endif () +set(SFML_USE_SYSTEM_DEPS ON) +set(CMAKE_SUPPRESS_DEVELOPER_WARNINGS ON) +add_subdirectory(${PROJECT_SOURCE_DIR}/thirdparty/SFML EXCLUDE_FROM_ALL) +target_link_libraries(${PROJECT_NAME} PUBLIC ${SFML_LIBRARIES} ${LUA_LIBRARIES}) + + +# Install Directives + +if (UNIX) +    set(SFML_INSTALL_PKGCONFIG_FILES FALSE) +endif () +install(TARGETS ${PROJECT_NAME} DESTINATION .) +install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/res DESTINATION .) +install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/maps DESTINATION .) +install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/dnglib DESTINATION .) +install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/LICENSE DESTINATION .) +install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty/thirdparty-licenses.txt DESTINATION .) + + +# Package Directives +set(CPACK_GEN TGZ) +if (WIN32) +    set(CPACK_GEN ZIP) +endif () + +set(CPACK_GENERATOR ${CPACK_GEN}) +set(CPACK_SOURCE_GENERATOR ${CPACK_GEN}) +set(CPACK_RESOURCE_FILE_README ${CMAKE_CURRENT_SOURCE_DIR}/README.md) +set(CPACK_RESOURCE_FILE_LICENSE ${CMAKE_CURRENT_SOURCE_DIR}/LICENSE) +include(CPack)
\ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..fbfcd48 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,3 @@ +# Contributing + +Pull requests welcome, but I do not monitor the repositories often. @@ -0,0 +1,17 @@ +Copyright (c) 2022 Steph Enders <steph@senders.io> + +This software is provided 'as-is', without any express or implied +warranty.  In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not +   claim that you wrote the original software. If you use this software +   in a product, an acknowledgment in the product documentation would be +   appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be +   misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. diff --git a/README.md b/README.md new file mode 100644 index 0000000..72c7c9f --- /dev/null +++ b/README.md @@ -0,0 +1,134 @@ +# dng + +dng is a maze/puzzle game + engine! Design and work your way through your own maze avoiding enemies and finding +treasure! + +## How to run + +You can run the game by `dng path/to/map/lvl` + +To run the first level: + +`dng maps/lvl1` + +## Create your own level! + +Levels use the following directory structure: + +```shell +level-name/ +  dng.map # required -the map file +  proc.lua # optional - custom lua overrides +``` + +### Map Format + +The map format is just a text file with the following key tokens: + +| Token | Description              | +|:------|:-------------------------| +| w     | Wall                     | +| p     | The player (must have 1) | +| e     | Enemies (0 or more)      | +| t     | Treasure (0 or more)     | +| 0     | Empty space              | + +#### Tips + +Space your map out using whitespace between every token (`w w w w` instead of `wwww`) for better readability. +Use a monospace font to help things align + +### Custom Lua Code + +Create a `proc.lua` in your level folder. +You can override anything found in `dnglib` using standard lua scoping rules. +If you wish to call the original defaults you can include `dnglib.defaults` in your file: +`local defaults = require('dnglib.defaults')` and call the functions: + +```lua +package.path = '[' +function onKeyPress(key) +    -- Set your own overrides +    defaults.onKeyPress(key) +end +``` + +## Develop + +dng uses git-submodules: + +```shell +git clone https://github.com/s3nd3r5/dng +git submodules init +git submodules update +``` + +### Dependencies + +Current build process is setup for a *nix like environment. + +| Dependency | Version | +|:-----------|:--------| +| c++        | 20+     | +| g++        | 8+      | +| CMake      | 3.16+   | +| Lua        | 5.4.*   | +| SFML       | 2.5.*   | + +_Note: with some CMake modifications we could probably leverage lower versions_ + +### Build + +dng uses CMake + +```shell +# use a build dir to ensure we ignore build props +cmake -B cmake-build +``` + +_Developed with CLion using CMake and g++ on Linux_ + +### Enforcing local lua files + +To ensure you're always loading the local files make sure you include: + +`package.path = "./?.lua;" .. package.path` at the top of your `proc.lua` + +If you're experiencing issues loading the proper files you can always set in your local development environment: + +`export LUA_PATH=/path/to/dng/?.lua;` (replacing `/path/to/dng` with your actual local development path) + +this will allow you to run `dng` from anywhere + +### Licensing in files + +You can find a copy of the notice in `.copyright_headers`. + +Note `dnglib/constants.lua` and `src/lua.hpp` which have custom licensing information. + +## Run + +Once you build the project you can execute it by: + +```shell +# use your build dir and select a map! +./cmake-build/dng ./path/to/map/lvl +``` + +To launch level 1 for example: + +```shell +./cmake-build/dng ./maps/lvl1 +``` + +Note: You need to use the working directory containing the `include` folder! + +## External Libraries and Resources used by dng + +* [SFML](https://github.com/SFML/SFML) licensed +  under [zlib/png license](https://www.sfml-dev.org/license.php) +* [Lua](https://github.com/lua/luat) licensed under [MIT license](https://www.lua.org/license.html) +* [Press Start 2P Font](https://fonts.google.com/specimen/Press+Start+2P#glyphs) licensed +  under [SIL Open Font License](https://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL) +* [Liberation Fonts](https://github.com/liberationfonts/liberation-fonts) licensed +  under [SIL Open Font License](https://github.com/liberationfonts/liberation-fonts/blob/main/LICENSE) diff --git a/dnglib/algs.lua b/dnglib/algs.lua new file mode 100644 index 0000000..9bb2542 --- /dev/null +++ b/dnglib/algs.lua @@ -0,0 +1,151 @@ +-- ----------------------------------------------------------------------- +-- dng +-- ------------------------------------------------------------------------ +-- Copyright (c) 2022 Steph Enders <steph@senders.io> +-- +-- This software is provided 'as-is', without any express or implied +-- warranty. In no event will the authors be held liable for any damages +-- arising from the use of this software. +-- +-- Permission is granted to anyone to use this software for any purpose, +-- including commercial applications, and to alter it and redistribute it +-- freely, subject to the following restrictions: +-- +-- 1. The origin of this software must not be misrepresented; you must not +--    claim that you wrote the original software. If you use this software +--    in a product, an acknowledgment in the product documentation would +--    be appreciated but is not required. +-- +-- 2. Altered source versions must be plainly marked as such, and must not +--    be misrepresented as being the original software. +-- +-- 3. This notice may not be removed or altered from any source +--    distribution. +-- +-- ----------------------------------------------------------------------- + +package.path="./?.lua;" .. package.path +require "dnglib.constants" +local Queue = require "dnglib.queue" + +---@param map table +local function print_map(map) +    for i = 1, #map do +        row = map[i] +        line = "" +        for j = 1, #row do +            if row[j] == MAP_WALL then +                line = line .. "# " +            elseif row[j] == MAP_VISITED then +                line = line .. "x " +            else +                line = line .. "  " +            end +        end +        print(line) +    end +end + +local function can_move(x, y, map) +    return map[y][x] == MAP_SPACE +end + +---@param x number +---@param y number +---@param origin table +---@param map table +---@param queue table +---@return boolean found goal +local function push_moves(x, y, origin, map, queue, target_pos) +    map[y][x] = MAP_VISITED -- should be but just in case +    -- UP +    if can_move(x, y - 1, map) then +        pos = { +            x = x, +            y = y - 1, +            origin = origin +        } +        map[pos.y][pos.x] = MAP_VISITED +        if (pos.x == target_pos.x and pos.y == target_pos.y) then +            return true +        end +        queue:push(pos) +    end +    -- DOWN +    if can_move(x, y + 1, map) then +        pos = { +            x = x, +            y = y + 1, +            origin = origin +        } +        map[pos.y][pos.x] = MAP_VISITED +        if (pos.x == target_pos.x and pos.y == target_pos.y) then +            return true +        end +        queue:push(pos) +    end +    -- LEFT +    if can_move(x - 1, y, map) then +        pos = { +            x = x - 1, +            y = y, +            origin = origin +        } +        map[pos.y][pos.x] = MAP_VISITED +        if (pos.x == target_pos.x and pos.y == target_pos.y) then +            return true +        end +        queue:push(pos) +    end +    -- RIGHT +    if can_move(x + 1, y, map) then +        pos = { +            x = x + 1, +            y = y, +            origin = origin +        } +        map[pos.y][pos.x] = MAP_VISITED +        if (pos.x == target_pos.x and pos.y == target_pos.y) then +            return true +        end +        queue:push(pos) +    end +    return false +end + +--- +---@param start_pos table [x, y] +---@param target_pos table [x, y] +---@param map table 2D map array +---@return table best move to target [x, y] +--- +local function pathfind(start_pos, target_pos, map) +    local queue = Queue:new() +    local visit_map = {} +    for k, v in ipairs(map) do +        row = {} +        for ik, iv in ipairs(v) do +            row[ik] = iv +        end +        visit_map[k] = row +    end + +    if (push_moves(start_pos.x, start_pos.y, nil, visit_map, queue, target_pos)) then +        return { dx = target_pos.x - start_pos.x, dy = target_pos.y - start_pos.y } +    end + +    while queue:empty() ~= true do +        local pos = queue:pop() +        origin = pos.origin or { x = pos.x, y = pos.y } +        hit_target = push_moves(pos.x, pos.y, origin, visit_map, queue, target_pos) +        if hit_target then +            return { dx = origin.x - start_pos.x, dy = origin.y - start_pos.y } +        end +    end +    return { dx = 0, dy = 0 } +end + +return { +    pathfind = pathfind, +    print_map = print_map, +} diff --git a/dnglib/constants.lua b/dnglib/constants.lua new file mode 100644 index 0000000..df0c37a --- /dev/null +++ b/dnglib/constants.lua @@ -0,0 +1,90 @@ +-- ----------------------------------------------------------------------- +-- dng +-- ------------------------------------------------------------------------ +-- Copyright (c) 2022 Steph Enders <steph@senders.io> +-- +-- This software is provided 'as-is', without any express or implied +-- warranty. In no event will the authors be held liable for any damages +-- arising from the use of this software. +-- +-- Permission is granted to anyone to use this software for any purpose, +-- including commercial applications, and to alter it and redistribute it +-- freely, subject to the following restrictions: +-- +-- 1. The origin of this software must not be misrepresented; you must not +--    claim that you wrote the original software. If you use this software +--    in a product, an acknowledgment in the product documentation would +--    be appreciated but is not required. +-- +-- 2. Altered source versions must be plainly marked as such, and must not +--    be misrepresented as being the original software. +-- +-- 3. This notice may not be removed or altered from any source +--    distribution. +-- +-- ----------------------------------------------------------------------- +-- NOTICE +-- Keyboard enumeration values derived from: +-- https://github.com/SFML/SFML/blob/master/include/SFML/Window/Keyboard.hpp#L48 +-- Licensed under zlib/png license Copyright (C) 2007-2022 Laurent Gomila (laurent@sfml-dev.org) +-- ------------------------------------------------------------------------ +KEY_A = 0 +KEY_B = 1 +KEY_C = 2 +KEY_D = 3 +KEY_E = 4 +KEY_F = 5 +KEY_G = 6 +KEY_H = 7 +KEY_I = 8 +KEY_J = 9 +KEY_L = 10 +KEY_M = 11 +KEY_N = 12 +KEY_M = 13 +KEY_O = 14 +KEY_P = 15 +KEY_Q = 16 +KEY_R = 17 +KEY_S = 18 +KEY_T = 19 +KEY_U = 20 +KEY_V = 21 +KEY_W = 22 +KEY_X = 23 +KEY_Y = 24 +KEY_Z = 25 + +-- control keys +KEY_ESCAPE = 36 +KEY_LCONTROL = 37 +KEY_LSHIFT = 38 +KEY_LALT = 39 +KEY_LSYSTEM = 40 +KEY_RCONTROL = 41 +KEY_RSHIFT = 42 +KEY_RALT = 43 +KEY_RSYSTEM = 44 +KEY_MENU = 45 + +KEY_SPACE = 57 +KEY_ENTER = 58 + +-- directional keys +KEY_LEFT = 71 +KEY_RIGHT = 72 +KEY_UP = 73 +KEY_DOWN = 74 + +-- map values +MAP_WALL = 1 +MAP_SPACE = 0 +MAP_VISITED = -1 + +-- scene values +SCENE_INTRO = 0 +SCENE_LEVEL = 1 +SCENE_WIN = 2 +SCENE_LOSS = 3 + +MOV_TIME = 0.5
\ No newline at end of file diff --git a/dnglib/defaults.lua b/dnglib/defaults.lua new file mode 100644 index 0000000..77f688c --- /dev/null +++ b/dnglib/defaults.lua @@ -0,0 +1,178 @@ +-- ----------------------------------------------------------------------- +-- dng +-- ------------------------------------------------------------------------ +-- Copyright (c) 2022 Steph Enders <steph@senders.io> +-- +-- This software is provided 'as-is', without any express or implied +-- warranty. In no event will the authors be held liable for any damages +-- arising from the use of this software. +-- +-- Permission is granted to anyone to use this software for any purpose, +-- including commercial applications, and to alter it and redistribute it +-- freely, subject to the following restrictions: +-- +-- 1. The origin of this software must not be misrepresented; you must not +--    claim that you wrote the original software. If you use this software +--    in a product, an acknowledgment in the product documentation would +--    be appreciated but is not required. +-- +-- 2. Altered source versions must be plainly marked as such, and must not +--    be misrepresented as being the original software. +-- +-- 3. This notice may not be removed or altered from any source +--    distribution. +-- +-- ----------------------------------------------------------------------- + +--[[ +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) + +--]] +package.path = "./?.lua;" .. package.path +require "dnglib.constants"; +local algs = require "dnglib.algs"; +local hasLost = false; +local hasWon = false; +local hasIntro = false; + +keys = { +    up = KEY_UP, +    down = KEY_DOWN, +    left = KEY_LEFT, +    right = KEY_RIGHT, +    quit = KEY_ESCAPE, +    start = KEY_SPACE, +    restart = KEY_SPACE, +} + +--- setup random +--math.randomseed(os.time()) + +---@param pressedKey number +function onKeyPress(pressedKey) +    scene = c_get_scene() +    assert(type(scene) == "number", "scene is not a number") +    if scene == SCENE_INTRO then +        if pressedKey == keys.start then +            c_trigger_level_start(); +        end +    elseif scene == SCENE_LEVEL then +        dx = 0 +        dy = 0 +        if pressedKey == keys.up then +            dy = -1 +        elseif pressedKey == keys.left then +            dx = -1 +        elseif pressedKey == keys.down then +            dy = 1 +        elseif pressedKey == keys.right then +            dx = 1 +        end + +        c_move_player(dx, dy) +    elseif scene == SCENE_WIN or scene == SCENE_LOSS then +        if pressedKey == keys.restart then +            c_trigger_restart() +        end +    end +end + +local diff_time = 0 +---@param dt number +function onUpdate(dt) +    diff_time = diff_time + dt +    enemies = c_get_enemies() +    assert(type(enemies) == "table", "Enemies not a table") + +    player = c_get_player_position() +    assert(type(player) == "table", "Player is not a table") + +    map = c_get_map(); +    assert(type(map) == "table", "map is not a table") + +    for _, v in ipairs(enemies) do +        local next; +        if diff_time >= MOV_TIME then +            next = algs.pathfind(v, player, map) +        else +            next = { dx = 0, dy = 0 } +        end +        new_pos = c_move_enemy(v.id, next.dx, next.dy) +        assert(type(new_pos) == "table", "new_pos is not a table") +        if new_pos.x == player.x and new_pos.y == player.y then +            c_trigger_loss() +        end +    end +    treasures = c_get_treasures() +    assert(type(treasures) == "table", "treasures is not a table") + +    for _, t in ipairs(treasures) do +        if t.x == player.x and t.y == player.y then +            c_score_treasure(t.id) +            if #treasures == 1 then +                c_trigger_win() +            end +        end +    end +    if diff_time > MOV_TIME then +        diff_time = 0 +    end +end + +function onWin() +    if hasWon == false then +        hasWon = true +        print("You WIN!!!!!!!!!") +    end +end + +function onLoss() +    if hasLost == false then +        hasLost = true +        print("Oh no! You lost!") +    end +end + +function onIntro() +    if hasIntro == false then +        hasIntro = true +        print([[ +................... +..      dng      .. +................... +..    controls   .. +................... +.. start -- space.. +.. move  -- wasd .. +.. quit  -- q    .. +................... +..      goal     .. +................... +.. Find treasure .. +.. Avoid enemies .. +................... +        ]]) +    end +end + +--- allow for requiring in other files for usage +return { +    onKeyPress = onKeyPress, +    onUpdate = onUpdate, +    onWin = onWin, +    onLoss = onLoss, +    onIntro = onIntro, +    keys = keys, +} diff --git a/dnglib/queue.lua b/dnglib/queue.lua new file mode 100644 index 0000000..a3470e5 --- /dev/null +++ b/dnglib/queue.lua @@ -0,0 +1,55 @@ +-- ----------------------------------------------------------------------- +-- dng +-- ------------------------------------------------------------------------ +-- Copyright (c) 2022 Steph Enders <steph@senders.io> +-- +-- This software is provided 'as-is', without any express or implied +-- warranty. In no event will the authors be held liable for any damages +-- arising from the use of this software. +-- +-- Permission is granted to anyone to use this software for any purpose, +-- including commercial applications, and to alter it and redistribute it +-- freely, subject to the following restrictions: +-- +-- 1. The origin of this software must not be misrepresented; you must not +--    claim that you wrote the original software. If you use this software +--    in a product, an acknowledgment in the product documentation would +--    be appreciated but is not required. +-- +-- 2. Altered source versions must be plainly marked as such, and must not +--    be misrepresented as being the original software. +-- +-- 3. This notice may not be removed or altered from any source +--    distribution. +-- +-- ----------------------------------------------------------------------- + +Queue = {} + +function Queue:new() +    o = { first = 1, top = 0, data = {} } +    self.__index = self +    return setmetatable(o, self) +end + +function Queue:push(val) +    local top = self.top + 1 +    self.top = top +    self.data[top] = val +end + +function Queue:pop() +    if self:empty() then +        return nil +    end +    local val = self.data[self.first] +    self.data[self.first] = nil +    self.first = self.first + 1 +    return val +end + +function Queue:empty() +    return self.top < self.first +end + +return Queue 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..29d4e46 --- /dev/null +++ b/maps/lvl1/proc.lua @@ -0,0 +1,7 @@ +package.path = "./?.lua;" .. package.path +require "dnglib.constants"; +local default = require "dnglib.defaults" + +function onKeyPress(key) +    default.onKeyPress(key) +end diff --git a/maps/lvl2/dng.map b/maps/lvl2/dng.map new file mode 100644 index 0000000..26467e0 --- /dev/null +++ b/maps/lvl2/dng.map @@ -0,0 +1,18 @@ +w w w w w w w w w w w w w w w w w +w 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 w +w e 0 0 0 t 0 0 0 0 0 0 w 0 0 0 w +w 0 0 0 0 0 0 0 0 0 0 0 w t 0 0 w +w 0 w w w w w w w w 0 w w w w 0 w +w 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 w +w 0 w w w w w w w w 0 w w 0 0 0 w +w e 0 0 0 0 0 0 0 0 0 w 0 0 0 0 w +w w 0 w w w w w w w 0 w w w w w w +w t 0 w 0 0 w 0 0 0 0 w w w w 0 w +w 0 w w 0 0 0 0 0 0 0 w e w w 0 w +w 0 0 0 0 w w w w 0 w w w w w 0 w +w t w w w 0 0 0 0 0 0 0 0 0 0 0 w +w w w w w 0 w 0 0 0 0 0 w 0 w w w +w 0 0 0 0 0 0 0 0 0 0 0 w 0 0 0 w +w p w w w w w w w w w w w w w 0 w +w 0 0 0 0 0 0 0 t 0 0 0 0 0 0 0 w +w w w w w w w w w w w w w w w w w
\ No newline at end of file diff --git a/maps/lvl2/proc.lua b/maps/lvl2/proc.lua new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/maps/lvl2/proc.lua diff --git a/maps/lvl3/dng.map b/maps/lvl3/dng.map new file mode 100644 index 0000000..b13ad83 --- /dev/null +++ b/maps/lvl3/dng.map @@ -0,0 +1,40 @@ +w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w +w p t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 w +w 0 t 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 t 0 w +w 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 w +w 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 w +w 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 w +w 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 w +w 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 w +w 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 w +w 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 w +w 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 w +w 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 w +w 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 w +w 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 w +w 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 e t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 w +w 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 w +w 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 w +w 0 t 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 t 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 w +w 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 w +w 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 w +w 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 w +w 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 w +w 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 w +w 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 w +w 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 w +w 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 w +w 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 w +w 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 w +w 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 w +w 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 w +w 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 w +w 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 w +w 0 w w w 0 w 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 w 0 w w w 0 w +w 0 t 0 w 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 0 w 0 t 0 w +w 0 t 0 w w w 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 w w w 0 t 0 w +w 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 w +w 0 w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w 0 w +w 0 t 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 t 0 w +w 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 t 0 0 w +w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w w
\ No newline at end of file diff --git a/res/LiberationMono-Regular.ttf b/res/LiberationMono-Regular.ttfBinary files differ new file mode 100644 index 0000000..a1d70ed --- /dev/null +++ b/res/LiberationMono-Regular.ttf diff --git a/res/PressStart2P-vaV7.ttf b/res/PressStart2P-vaV7.ttfBinary files differ new file mode 100644 index 0000000..98044e9 --- /dev/null +++ b/res/PressStart2P-vaV7.ttf diff --git a/src/CApi.h b/src/CApi.h new file mode 100644 index 0000000..21ad9eb --- /dev/null +++ b/src/CApi.h @@ -0,0 +1,235 @@ +//======================================================================== +// dng +//------------------------------------------------------------------------ +// Copyright (c) 2022 Steph Enders <steph@senders.io> +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +//    claim that you wrote the original software. If you use this software +//    in a product, an acknowledgment in the product documentation would +//    be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +//    be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +//    distribution. +// +//======================================================================== + +#ifndef DNG_CAPI_H +#define DNG_CAPI_H + +#include "Level.h" +#include "Scene.h" +#include "lua.hpp" +#include <memory> + +extern std::shared_ptr<Level> lvl; +extern Scene scene; + +/* + * c_get_player_position(int x, int y) + */ +static int c_get_player_position(lua_State *L) { +  lua_createtable(L, 0, 2); +  lua_pushnumber(L, lvl->player.y + 1); +  lua_setfield(L, -2, "y"); +  lua_pushnumber(L, lvl->player.x + 1); +  lua_setfield(L, -2, "x"); +  return 1; +} + +/* + * c_move_player(int dx, int dy) + */ +static int c_move_player(lua_State *L) { +  // stack ordering +  int dy = static_cast<int>(lua_tonumber(L, -1)); +  int dx = static_cast<int>(lua_tonumber(L, -2)); + +  if (lvl->playerCanStep(dx, dy)) { +    lvl->player.x += dx; +    lvl->player.y += dy; +  } + +  lua_createtable(L, 0, 2); +  lua_pushnumber(L, lvl->player.y + 1); +  lua_setfield(L, -2, "y"); +  lua_pushnumber(L, lvl->player.x + 1); +  lua_setfield(L, -2, "x"); + +  return 1; +} + +/* + * c_move_enemy(int id, int dx, int dy) + */ +static int c_move_enemy(lua_State *L) { +  // stack ordering +  int dy = static_cast<int>(lua_tonumber(L, -1)); +  int dx = static_cast<int>(lua_tonumber(L, -2)); +  int id = static_cast<int>(lua_tonumber(L, -3)); + +  int i = lvl->getEnemyIndex(id); +  // guard against enemy not found +  if (i == -1) { +    return 1; +  } + +  if (lvl->enemyCanStep(lvl->enemyPositions[i], dx, dy)) { +    lvl->enemyPositions[i].x += dx; +    lvl->enemyPositions[i].y += dy; +  } +  lua_createtable(L, 0, 3); +  lua_pushnumber(L, lvl->enemyPositions[i].y + 1); +  lua_setfield(L, -2, "y"); +  lua_pushnumber(L, lvl->enemyPositions[i].x + 1); +  lua_setfield(L, -2, "x"); +  lua_pushnumber(L, lvl->enemyPositions[i].id); +  lua_setfield(L, -2, "id"); + +  return 1; +} + +/* + * 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); +  } + +  return 1; +} + +/* + * c_spawn_enemy(int x, int y) + */ +static int c_spawn_enemy(lua_State *L) { return 1; } + +/* + * c_destroy_enemy(int id) + */ +static int c_destroy_enemy(lua_State *L) { return 1; } + +/* + * c_get_map() + */ +static int c_get_map(lua_State *L) { +  lua_createtable(L, int(lvl->map.size()), 0); +  int idx = 0; +  for (auto &vec : lvl->map) { +    lua_pushnumber(L, ++idx); +    lua_createtable(L, int(vec.size()), 0); +    int inner_idx = 0; +    for (auto &c : vec) { +      lua_pushnumber(L, ++inner_idx); +      lua_pushnumber(L, c == WALL_SPACE ? 1 : 0); +      lua_rawset(L, -3); +    } +    lua_rawset(L, -3); +  } +  return 1; +} + +/* + * c_trigger_level_start() + */ +static int c_trigger_level_start(lua_State *L) { +  scene = Scene::LEVEL; +  lua_pushboolean(L, true); +  return 1; +} +/* + * c_trigger_win() + */ +static int c_trigger_win(lua_State *L) { +  scene = Scene::WIN; +  lua_pushboolean(L, true); +  return 1; +} + +/* + * c_trigger_loss() + */ +static int c_trigger_loss(lua_State *L) { +  scene = Scene::LOSS; +  lua_pushboolean(L, true); +  return 1; +} + +static int c_get_scene(lua_State *L) { +  lua_pushnumber(L, scene); +  return 1; +} + +static int c_get_treasures(lua_State *L) { +  lua_createtable(L, static_cast<int>(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); +  } +  return 1; +} + +static int c_score_treasure(lua_State *L) { +  int id = static_cast<int>(lua_tonumber(L, -1)); + +  erase_if(lvl->treasurePositions, [id](Pos t) { return t.id == id; }); + +  return 1; +} + +static int c_trigger_restart(lua_State *L) { +  lvl->reset(); +  scene = Scene::INTRO; +  return 1; +} + +// not for lua use +void init_c_api(lua_State *L) { +  lua_register(L, "c_move_player", c_move_player); +  lua_register(L, "c_move_enemy", c_move_enemy); +  lua_register(L, "c_spawn_enemy", c_spawn_enemy); +  lua_register(L, "c_destroy_enemy", c_destroy_enemy); +  lua_register(L, "c_get_enemies", c_get_enemies); +  lua_register(L, "c_get_player_position", c_get_player_position); +  lua_register(L, "c_get_map", c_get_map); +  lua_register(L, "c_trigger_level_start", c_trigger_level_start); +  lua_register(L, "c_trigger_win", c_trigger_win); +  lua_register(L, "c_trigger_loss", c_trigger_loss); +  lua_register(L, "c_get_scene", c_get_scene); +  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); +} + +#endif // DNG_CAPI_H diff --git a/src/Level.cpp b/src/Level.cpp new file mode 100644 index 0000000..6db4b4e --- /dev/null +++ b/src/Level.cpp @@ -0,0 +1,161 @@ +//======================================================================== +// dng +//------------------------------------------------------------------------ +// Copyright (c) 2022 Steph Enders <steph@senders.io> +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +//    claim that you wrote the original software. If you use this software +//    in a product, an acknowledgment in the product documentation would +//    be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +//    be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +//    distribution. +// +//======================================================================== + +#include "Level.h" +#include "SfmlUtils.h" +#include <fstream> +#include <iostream> +#include <string> + +bool canStep(Pos pos, int dx, int dy, std::vector<std::vector<char>> map) { +  return map[pos.y + dy][pos.x + dx] != WALL_SPACE; +} + +void Level::loadLevelFromFile(const char *filePath) { +  std::ifstream mapFile(filePath); +  if (mapFile.is_open()) { +    this->file = filePath; +    // 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); +          auto w = create_wall(x, y); +          this->displayMap.push_back(w); +        } else if (c == EMPTY_TKN) { +          this->map[y].push_back(BLANK_SPACE); +          // no display info +        } else if (c == ENEMY_TKN) { +          auto e = create_enemy(x, y); +          this->enemyPositions.push_back( +              {.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->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}); +          this->map[y].push_back(BLANK_SPACE); +        } else { +          continue; +        } +        ++x; +      } +      ++y; +    } + +    unsigned long max_width = 0; +    for (auto &rows : this->map) { +      if (max_width < rows.size()) { +        max_width = rows.size(); +      } +    } +    this->width = static_cast<int>(max_width); +    this->height = static_cast<int>(this->map.size()); +  } +  mapFile.close(); +} + +void Level::reset() { +  this->map.clear(); +  this->treasurePositions.clear(); +  this->displayMap.clear(); +  this->enemyPositions.clear(); +  this->loadLevelFromFile(this->file); +} + +bool Level::isEmpty(int x, int y) { return map[y][x] == BLANK_SPACE; } + +bool Level::playerCanStep(int dx, int dy) { +  return canStep(player, dx, dy, map); +} + +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++; } + +int Level::getEnemyIndex(int id) { +  for (int i = 0; i < enemyPositions.size(); i++) { +    if (enemyPositions[i].id == id) { +      return i; +    } +  } + +  return -1; +} +bool Level::enemyCanStep(Pos pos, int dx, int dy) { +  return canStep(pos, dx, dy, map); +} +int Level::getWidth() { return this->width; } +int Level::getHeight() { return this->height; } diff --git a/src/Level.h b/src/Level.h new file mode 100644 index 0000000..f0cec68 --- /dev/null +++ b/src/Level.h @@ -0,0 +1,84 @@ +//======================================================================== +// dng +//------------------------------------------------------------------------ +// Copyright (c) 2022 Steph Enders <steph@senders.io> +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +//    claim that you wrote the original software. If you use this software +//    in a product, an acknowledgment in the product documentation would +//    be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +//    be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +//    distribution. +// +//======================================================================== + +#ifndef DNG_LEVEL_H +#define DNG_LEVEL_H + +#include "SFML/Graphics/RectangleShape.hpp" +#include <memory> +#include <vector> + +// tokens from map file +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 = '#'; + +struct Pos { +  int id; +  int x; +  int y; +  sf::RectangleShape sprite; +} typedef Pos; + +class Level { + +public: +  void loadLevelFromFile(const char *filePath); + +  bool isEmpty(int x, int y); + +  bool playerCanStep(int dx, int dy); + +  int getEnemyIndex(int id); + +  bool enemyCanStep(Pos pos, int dx, int dy); + +  void reset(); +  /* deprecate*/ +  void print(); + +  int nextId(); +  int getWidth(); +  int getHeight(); + +  std::vector<std::vector<char>> map; // source copy of map +  std::vector<sf::RectangleShape> displayMap; +  Pos player; +  std::vector<Pos> enemyPositions; +  std::vector<Pos> treasurePositions; + +private: +  int idCounter = 1; // defaults at 1 (player always 0) +  int width; +  int height; +  const char *file; +}; + +#endif // DNG_LEVEL_H diff --git a/src/LuaApi.h b/src/LuaApi.h new file mode 100644 index 0000000..43bd50f --- /dev/null +++ b/src/LuaApi.h @@ -0,0 +1,120 @@ +//======================================================================== +// dng +//------------------------------------------------------------------------ +// Copyright (c) 2022 Steph Enders <steph@senders.io> +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +//    claim that you wrote the original software. If you use this software +//    in a product, an acknowledgment in the product documentation would +//    be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +//    be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +//    distribution. +// +//======================================================================== + +#ifndef DNG_LUA_API_H +#define DNG_LUA_API_H + +#include "lua.hpp" +#include <iostream> + +struct LState { +  lua_State *onkeypress; +  lua_State *onupdate; +  lua_State *onintro; +  lua_State *onwin; +  lua_State *onloss; +} typedef LState; + +const char *ON_KEYPRESS = "onKeyPress"; +const char *ON_UPDATE = "onUpdate"; +const char *ON_INTRO = "onIntro"; +const char *ON_WIN = "onWin"; +const char *ON_LOSS = "onLoss"; + +LState *init_default(lua_State *L) { +  auto *state = static_cast<LState *>(malloc(sizeof(LState))); + +  state->onkeypress = L; +  state->onupdate = L; +  state->onintro = L; +  state->onwin = L; +  state->onloss = L; + +  return state; +} + +bool check_fn(lua_State *L, const char *fn) { +  lua_getglobal(L, fn); +  return lua_isfunction(L, -1); +} + +void override_file_fns(lua_State *L, LState *state) { +  if (check_fn(L, ON_KEYPRESS)) { +    state->onkeypress = L; +  } +  if (check_fn(L, ON_UPDATE)) { +    state->onupdate = L; +  } +  if (check_fn(L, ON_INTRO)) { +    state->onintro = L; +  } +  if (check_fn(L, ON_WIN)) { +    state->onwin = L; +  } +  if (check_fn(L, ON_LOSS)) { +    state->onloss = L; +  } +} + +bool lua_dofn(lua_State *L, const char *fn) { +  lua_getglobal(L, fn); +  if (!lua_isfunction(L, -1)) { +    std::cout << "[C] Error " << fn << " not function | not found" << std::endl; +    return false; +  } +  lua_pcall(L, 0, 1, 0); +  return true; +} + +bool lua_dofn_with_number(lua_State *L, const char *fn, lua_Number num) { +  lua_getglobal(L, fn); +  if (!lua_isfunction(L, -1)) { +    std::cout << "[C] Error " << fn << " not function | not found" << std::endl; +    return false; +  } +  lua_pushnumber(L, num); +  lua_pcall(L, 1, 1, 0); +  return true; +} + +bool lua_onkeypress(lua_State *L, int pressedKey) { +  if (pressedKey == -1) { +    return true; +  } +  return lua_dofn_with_number(L, "onKeyPress", pressedKey); +} + +bool lua_onupdate(lua_State *L, float dt) { +  return lua_dofn_with_number(L, "onUpdate", dt); +} + +bool lua_onintro(lua_State *L) { return lua_dofn(L, "onIntro"); } + +bool lua_onwin(lua_State *L) { return lua_dofn(L, "onWin"); } + +bool lua_onloss(lua_State *L) { return lua_dofn(L, "onLoss"); } + +#endif // DNG_LUA_API_H diff --git a/src/MessageBox.h b/src/MessageBox.h new file mode 100644 index 0000000..0e1fa8f --- /dev/null +++ b/src/MessageBox.h @@ -0,0 +1,98 @@ +//======================================================================== +// dng +//------------------------------------------------------------------------ +// Copyright (c) 2022 Steph Enders <steph@senders.io> +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +//    claim that you wrote the original software. If you use this software +//    in a product, an acknowledgment in the product documentation would +//    be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +//    be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +//    distribution. +// +//======================================================================== + +#ifndef DNG_MESSAGE_BOX_H +#define DNG_MESSAGE_BOX_H + +#include "SFML/Graphics/RectangleShape.hpp" +#include "SFML/Graphics/Text.hpp" +#include "SfmlUtils.h" +#include <vector> + +const int LARGE_TEXT = 54; +const int MEDIUM_TEXT = 36; +const int SMALL_TEXT = 24; +const float PADDING = 16.f; +const float LINE_HEIGHT = 1.2f; +struct MessageBox { + +  std::vector<sf::Text> msgs; +  sf::RectangleShape box; + +} typedef MessageBox; + +struct DisplayText { +  std::string msg; +  int textSize; +} typedef DisplayText; + +inline MessageBox initializeMessageBox(const std::vector<DisplayText> &strs, +                                       const sf::Font &font, +                                       sf::Vector2u windowSize) { + +  std::vector<sf::Text> messages; +  sf::RectangleShape box; +  float width = 0.f; +  float height = 0.f; +  float left = MAXFLOAT; +  sf::Text prev; +  for (const auto &str : strs) { +    sf::Text text = write_text(str.msg.c_str(), str.textSize, LINE_HEIGHT, font, +                               windowSize); +    // move below previous +    if (!prev.getString().isEmpty()) { +      text.move(0.f, prev.getLocalBounds().height * prev.getLineSpacing()); +    } +    // add to vector +    messages.push_back(text); + +    // find widest +    if (text.getGlobalBounds().width > width) { +      width = text.getGlobalBounds().width; +    } +    // find leftmost +    if (text.getGlobalBounds().left < left) { +      left = text.getGlobalBounds().left; +    } +    // heights added together + line height +    height += (text.getLocalBounds().height * text.getLineSpacing()); +    prev = text; +  } +  box = sf::RectangleShape(sf::Vector2f(width + PADDING, height + PADDING)); +  box.setOutlineColor(sf::Color::Black); +  box.setOutlineThickness(4.f); +  box.setFillColor(sf::Color(128, 128, 128, 128)); +  box.setPosition(left - (PADDING / 2.f), +                  // Top will always be the first message +                  messages[0].getGlobalBounds().top - (PADDING / 2.f)); + +  return { +      .msgs = messages, +      .box = box, +  }; +} + +#endif // DNG_MESSAGE_BOX_H
\ No newline at end of file diff --git a/src/Scene.h b/src/Scene.h new file mode 100644 index 0000000..0db80cd --- /dev/null +++ b/src/Scene.h @@ -0,0 +1,31 @@ +//======================================================================== +// dng +//------------------------------------------------------------------------ +// Copyright (c) 2022 Steph Enders <steph@senders.io> +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +//    claim that you wrote the original software. If you use this software +//    in a product, an acknowledgment in the product documentation would +//    be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +//    be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +//    distribution. +// +//======================================================================== + +#ifndef DNG_SCENE_H +#define DNG_SCENE_H +/* sync with constants.lua */ +enum Scene { INTRO, LEVEL, WIN, LOSS }; +#endif // DNG_SCENE_H diff --git a/src/SfmlUtils.h b/src/SfmlUtils.h new file mode 100644 index 0000000..dd63cd6 --- /dev/null +++ b/src/SfmlUtils.h @@ -0,0 +1,110 @@ +//======================================================================== +// dng +//------------------------------------------------------------------------ +// Copyright (c) 2022 Steph Enders <steph@senders.io> +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +//    claim that you wrote the original software. If you use this software +//    in a product, an acknowledgment in the product documentation would +//    be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +//    be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +//    distribution. +// +//======================================================================== + +#ifndef DNG_SFML_UTILS_H +#define DNG_SFML_UTILS_H + +#include "SFML/Graphics/RectangleShape.hpp" +#include "SFML/Graphics/Text.hpp" +#include "SFML/Window/Event.hpp" +#include "SFML/Window/Keyboard.hpp" +#include <cmath> +#include <iostream> + +const float SPRITE_SIZE = 16.f; // squares +const sf::Color WALL_COLOR = sf::Color(150, 150, 150, 255); +const sf::Color BLANK_COLOR = sf::Color(216, 216, 216, 255); + +inline sf::Keyboard::Key get_key(sf::Event event) { +  if (event.type == sf::Event::KeyPressed || +      sf::Keyboard::isKeyPressed(event.key.code)) { +    return event.key.code; +  } +  return sf::Keyboard::Unknown; +} + +inline sf::Vector2f to_position_xy(int x, int y) { +  return {static_cast<float>(x) * SPRITE_SIZE, +          static_cast<float>(y) * SPRITE_SIZE}; +} +inline sf::Vector2f to_position(const Pos &pos) { +  return to_position_xy(pos.x, pos.y); +} + +inline sf::RectangleShape create_square(sf::Color color, int x, int y) { +  sf::RectangleShape rect({SPRITE_SIZE - 1.f, SPRITE_SIZE - 1.f}); +  rect.setFillColor(color); +  rect.setOutlineColor(sf::Color::Black); +  rect.setOutlineThickness(1.f); +  rect.setPosition(to_position_xy(x, y)); +  return rect; +} + +inline sf::RectangleShape create_wall(int x, int y) { +  return create_square(WALL_COLOR, x, y); +} + +inline sf::RectangleShape create_enemy(int x, int y) { +  return create_square(sf::Color::Magenta, x, y); +} + +inline sf::RectangleShape create_player(int x, int y) { +  return create_square(sf::Color::Cyan, x, y); +} + +inline sf::RectangleShape create_treasure(int x, int y) { +  return create_square(sf::Color::Yellow, x, y); +} + +inline sf::Vector2f round(const sf::Vector2f vector) { +  return sf::Vector2f{std::round(vector.x), std::round(vector.y)}; +} + +inline sf::Text write_text(const char *msg, unsigned int fontSize, +                           float lineSpacing, const sf::Font &font, +                           const sf::Vector2u windowSize) { +  sf::Text text(msg, font, fontSize); +  text.setOutlineThickness(4.f); +  text.setOrigin(round(sf::Vector2f{text.getLocalBounds().width / 2, +                                    text.getLocalBounds().height / 2})); +  text.setPosition(sf::Vector2f(windowSize / 2u)); +  text.setLineSpacing(lineSpacing); +  while (text.getGlobalBounds().top < 0 || text.getGlobalBounds().left < 0 || +         static_cast<unsigned int>( +             (text.getGlobalBounds().width + text.getGlobalBounds().left) * +             text.getLineSpacing()) > windowSize.x || +         static_cast<unsigned int>( +             (text.getGlobalBounds().height + text.getGlobalBounds().top) * +             text.getLineSpacing()) > windowSize.y) { +    text.setCharacterSize(text.getCharacterSize() - 1); +    text.setOrigin(round(sf::Vector2f{text.getLocalBounds().width / 2, +                                      text.getLocalBounds().height / 2})); +    text.setPosition(sf::Vector2f(windowSize / 2u)); +  } +  return text; +} + +#endif // DNG_SFML_UTILS_H
\ No newline at end of file diff --git a/src/linux/res.h b/src/linux/res.h new file mode 100644 index 0000000..9e3b8c5 --- /dev/null +++ b/src/linux/res.h @@ -0,0 +1,77 @@ +//======================================================================== +// dng +//------------------------------------------------------------------------ +// Copyright (c) 2022 Steph Enders <steph@senders.io> +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +//    claim that you wrote the original software. If you use this software +//    in a product, an acknowledgment in the product documentation would +//    be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +//    be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +//    distribution. +// +//======================================================================== + +#ifndef DNG_RES_H +#define DNG_RES_H + +#include <filesystem> + +const std::filesystem::path DEFAULT_PROC{"dnglib/defaults.lua"}; +const std::filesystem::path DEFAULT_FONT{"res/PressStart2P-vaV7.ttf"}; +// TODO setup to allow switching to monospace instead of game font +// const std::filesystem::path DEFAULT_MONOSPACE_FONT{ +//    "res/LiberationMono-Regular.ttf"}; + +struct Res { + +  std::filesystem::path defaultsFile; +  std::filesystem::path fontFile; + +} typedef Res; + +inline Res get_resources() { +  using namespace std::filesystem; + +  auto current_dir = current_path(); +  auto exe_dir = canonical("/proc/self/exe").remove_filename(); +  auto install_dir = path{"/usr/local/share/dng/"}; + +  Res res; +  if (exists(current_dir / DEFAULT_PROC)) { +    res.defaultsFile = current_dir / DEFAULT_PROC; +  } else if (exists(exe_dir / DEFAULT_PROC)) { +    res.defaultsFile = exe_dir / DEFAULT_PROC; +  } else if (exists(install_dir / DEFAULT_PROC)) { +    res.defaultsFile = install_dir / DEFAULT_PROC; +  } else { +    res.defaultsFile = DEFAULT_PROC; // just return w/ no path info +  } +  // TODO make configurable +  path fontp = DEFAULT_FONT; +  if (exists(current_dir / fontp)) { +    res.fontFile = current_dir / fontp; +  } else if (exists(exe_dir / fontp)) { +    res.fontFile = exe_dir / fontp; +  } else if (exists(install_dir / fontp)) { +    res.fontFile = install_dir / fontp; +  } else { +    res.fontFile = fontp; // just return w/ no path info +  } + +  return res; +} + +#endif // DNG_RES_H
\ No newline at end of file diff --git a/src/lua.hpp b/src/lua.hpp new file mode 100644 index 0000000..4cf79d2 --- /dev/null +++ b/src/lua.hpp @@ -0,0 +1,8 @@ +// Included locally +// Building from sources doesn't include this by default +// See Copyright Notice in thirdparty/lua/lua.h +extern "C" { +#include "lauxlib.h" +#include "lua.h" +#include "lualib.h" +} diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..99f8603 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,278 @@ +//======================================================================== +// dng +//------------------------------------------------------------------------ +// Copyright (c) 2022 Steph Enders <steph@senders.io> +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +//    claim that you wrote the original software. If you use this software +//    in a product, an acknowledgment in the product documentation would +//    be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +//    be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +//    distribution. +// +//======================================================================== + +#include "CApi.h" +#include "Level.h" +#include "LuaApi.h" +#include "MessageBox.h" +#include "SfmlUtils.h" +#ifdef __linux__ +#include "linux/res.h" +#endif +#include "lua.hpp" +#include <SFML/Graphics.hpp> +#include <cmath> +#include <filesystem> +#include <iostream> + +std::shared_ptr<Level> lvl; + +Scene scene; +const int MAX_WIDTH = static_cast<int>(SPRITE_SIZE) * 20 * 4; +const int MAX_HEIGHT = static_cast<int>(SPRITE_SIZE) * 20 * 3; + +int main(int argc, char **argv) { + +  if (argc <= 1) { +    std::cerr << "Must pass in path to level directory" << std::endl; +    return -1; +  } + +  auto res = get_resources(); + +  std::string lvl_pfx = argv[1]; + +  std::filesystem::path mapFile{lvl_pfx + "/dng.map"}; +  std::filesystem::path luaFile{lvl_pfx + "/proc.lua"}; + +  lvl = std::make_shared<Level>(); +  scene = Scene::INTRO; + +  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(res.defaultsFile) && +      luaL_dofile(L_default, res.defaultsFile.c_str()) != 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; +  } + +  // Initialize to default +  LState *l_state = init_default(L_default); + +  if (std::filesystem::exists(luaFile) && +      luaL_dofile(L_lvl, luaFile.c_str()) == LUA_OK) { +    override_file_fns(L_lvl, l_state); +  } else if (std::filesystem::exists(luaFile)) { +    std::cout << "[C] No Good" << std::endl; +    luaL_error(L_lvl, "Error: %s\n", lua_tostring(L_lvl, -1)); +    return EXIT_FAILURE; +  } + +  float ZOOM = 0.5f; +  sf::Vector2f mapBounds = {static_cast<float>(lvl->getWidth()) * SPRITE_SIZE, +                            static_cast<float>(lvl->getHeight()) * SPRITE_SIZE}; +  int width = static_cast<int>(static_cast<float>(lvl->getWidth()) / ZOOM * +                               SPRITE_SIZE / ZOOM); +  int height = static_cast<int>(static_cast<float>(lvl->getHeight()) / ZOOM * +                                SPRITE_SIZE / ZOOM); +  bool useViewport = false; +  if (width > MAX_WIDTH) { +    width = MAX_WIDTH; +    useViewport = true; +  } +  if (height > MAX_HEIGHT) { +    height = MAX_HEIGHT; +    useViewport = true; +  } + +  sf::RenderWindow window(sf::VideoMode(width, height), "dng"); +  window.setMouseCursorVisible(false); +  window.setFramerateLimit(30); +  sf::View view(window.getDefaultView()); +  view.zoom(ZOOM); +  view.setSize(view.getSize() * ZOOM); +  view.setViewport(sf::FloatRect(0.f, 0.f, 1.f, 1.f)); +  if (useViewport) { +    view.setCenter(to_position(lvl->player) + +                   sf::Vector2f(SPRITE_SIZE / 2.f, SPRITE_SIZE / 2.f)); +  } else { +    view.setCenter(view.getSize() / 2.f); +  } + +  window.setView(view); +  sf::Clock deltaClock; +  sf::Font font; +  font.loadFromFile(res.fontFile); + +  MessageBox intro; +  MessageBox win; +  MessageBox loss; + +  do { +    sf::Event event{}; +    float dt = deltaClock.restart().asSeconds(); + +    while (window.pollEvent(event)) { +      if (event.type == sf::Event::Closed || +          sf::Keyboard::isKeyPressed(sf::Keyboard::Q)) { +        window.close(); +      } +      if (!lua_onkeypress(l_state->onkeypress, get_key(event))) { +        window.close(); +      } +    } + +    if (scene == Scene::INTRO) { +      if (!lua_onintro(l_state->onintro)) { +        window.close(); +      } +    } else if (scene == Scene::LEVEL) { +      if (!lua_onupdate(l_state->onupdate, dt)) { +        window.close(); +      } +    } else if (scene == Scene::WIN) { +      lua_onwin(l_state->onwin); +      // window.close(); +    } else if (scene == Scene::LOSS) { +      lua_onloss(l_state->onloss); +    } + +    // Render +    if (useViewport) { +      // Reinitialize center view +      if (scene == INTRO) { +        if (useViewport) { +          view.setCenter(to_position(lvl->player) + +                         sf::Vector2f(SPRITE_SIZE / 2.f, SPRITE_SIZE / 2.f)); +        } else { +          view.setCenter(view.getSize() / 2.f); +        } +      } +      sf::Vector2f newPos = to_position(lvl->player) + // center +                            sf::Vector2f(SPRITE_SIZE / 2.f, SPRITE_SIZE / 2.f); +      sf::Vector2f diff = +          newPos - (lvl->player.sprite.getPosition() + +                    sf::Vector2f(SPRITE_SIZE / 2.f, SPRITE_SIZE / 2.f)); + +      if (diff.x > 0.f && newPos.x > view.getSize().x / 2.f) { +        view.move({diff.x, 0.f}); +      } +      if (diff.y > 0.f && newPos.y > view.getSize().y / 2.f) { +        view.move({0.f, diff.y}); +      } +      if (diff.x < 0.f && newPos.x < view.getCenter().x) { +        view.move({diff.x, 0.f}); +      } +      if (diff.y < 0.f && newPos.y < view.getCenter().y) { +        view.move({0.f, diff.y}); +      } +      // readjust for OB +      if (view.getCenter().x - view.getSize().x / 2.f < 0.f) { +        view.move({(view.getCenter().x - view.getSize().x / 2.f) * -1.f, 0.f}); +      } +      if (view.getCenter().y - view.getSize().y / 2.f < 0.f) { +        view.move({0.f, (view.getCenter().y - view.getSize().y / 2.f) * -1.f}); +      } +      if (view.getCenter().x + view.getSize().x / 2.f > mapBounds.x) { +        view.move( +            {-1 * ((view.getCenter().x + view.getSize().x / 2.f) - mapBounds.x), +             0.f}); +      } +      if (view.getCenter().y + view.getSize().y / 2.f > mapBounds.y) { +        view.move({0.f, -1 * ((view.getCenter().y + view.getSize().y / 2.f) - +                              mapBounds.y)}); +      } +    } + +    window.clear(BLANK_COLOR); +    window.setView(view); +    for (auto &rect : lvl->displayMap) { +      window.draw(rect); +    } +    if (scene == Scene::LEVEL) { +      for (auto &enemy : lvl->enemyPositions) { +        enemy.sprite.setPosition(to_position(enemy)); +        window.draw(enemy.sprite); +      } +      for (auto &treasure : lvl->treasurePositions) { +        treasure.sprite.setPosition(to_position(treasure)); +        window.draw(treasure.sprite); +      } +    } + +    if (scene != Scene::LOSS) { +      lvl->player.sprite.setPosition(to_position(lvl->player)); +      window.draw(lvl->player.sprite); +    } +    if (scene == Scene::WIN) { +      window.setView(window.getDefaultView()); +      if (win.msgs.empty()) { +        win = initializeMessageBox({{"You Win!", LARGE_TEXT}, +                                    {"press [space] to restart", SMALL_TEXT}}, +                                   font, window.getSize()); +      } + +      window.draw(win.box); +      for (auto &msg : win.msgs) { +        window.draw(msg); +      } +      window.setView(view); +    } +    if (scene == Scene::LOSS) { +      window.setView(window.getDefaultView()); +      if (loss.msgs.empty()) { +        loss = initializeMessageBox({{"You Lose!", LARGE_TEXT}, +                                     {"press [space] to restart", SMALL_TEXT}}, +                                    font, window.getSize()); +      } + +      window.draw(loss.box); +      for (auto &msg : loss.msgs) { +        window.draw(msg); +      } +      window.setView(view); +    } +    if (scene == INTRO) { +      window.setView(window.getDefaultView()); +      if (intro.msgs.empty()) { +        intro = initializeMessageBox( +            {{"Start!", LARGE_TEXT}, {"press [space]", SMALL_TEXT}}, font, +            window.getSize()); +      } + +      window.draw(intro.box); +      for (auto &msg : intro.msgs) { +        window.draw(msg); +      } +      window.setView(view); +    } +    window.setView(view); +    window.display(); +  } while (window.isOpen()); + +  std::cout << "[C] Quit" << std::endl; + +  return EXIT_SUCCESS; +} diff --git a/thirdparty/.clang-format b/thirdparty/.clang-format new file mode 100644 index 0000000..bdc824e --- /dev/null +++ b/thirdparty/.clang-format @@ -0,0 +1,4 @@ +{ +  "DisableFormat": true, +  "SortIncludes": "Never" +} diff --git a/thirdparty/SFML b/thirdparty/SFML new file mode 160000 +Subproject 2f11710abc5aa478503a7ff3f9e654bd2078eba diff --git a/thirdparty/lua b/thirdparty/lua new file mode 160000 +Subproject e37b96db04b373f79661357e18a061186ceb8e5 diff --git a/thirdparty/thirdparty-licenses.txt b/thirdparty/thirdparty-licenses.txt new file mode 100644 index 0000000..5e1adac --- /dev/null +++ b/thirdparty/thirdparty-licenses.txt @@ -0,0 +1,250 @@ +------------------------------------------------------------------------------- +dng uses SFML - licensed under the zlib/png license +------------------------------------------------------------------------------- +SFML - Copyright (C) 2007-2018 Laurent Gomila - laurent@sfml-dev.org + +This software is provided 'as-is', without any express or implied +warranty.  In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not +   claim that you wrote the original software. If you use this software +   in a product, an acknowledgment in the product documentation would be +   appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be +   misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. + +------------------------------------------------------------------------------- +------------------------------------------------------------------------------- +dng uses LUA - licensed under the MIT license +------------------------------------------------------------------------------- +Copyright (C) 1994-2020 Lua.org, PUC-Rio. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------- +------------------------------------------------------------------------------- +dng uses PressStart2P-vaV7 font - licensed under SIL Open Font license +------------------------------------------------------------------------------- +Copyright 2012 The Press Start 2P Project Authors (cody@zone38.net), with Reserved Font Name "Press Start 2P". + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. +------------------------------------------------------------------------------- +------------------------------------------------------------------------------- +dng uses Liberation Fonts - licensed under SIL Open Font license +------------------------------------------------------------------------------- +Digitized data copyright (c) 2010 Google Corporation +	with Reserved Font Arimo, Tinos and Cousine. +Copyright (c) 2012 Red Hat, Inc. +	with Reserved Font Name Liberation. + +This Font Software is licensed under the SIL Open Font License, +Version 1.1. + +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 + +PREAMBLE The goals of the Open Font License (OFL) are to stimulate +worldwide development of collaborative font projects, to support the font +creation efforts of academic and linguistic communities, and to provide +a free and open framework in which fonts may be shared and improved in +partnership with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. +The fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works.  The fonts and derivatives, +however, cannot be released under any other type of license.  The +requirement for fonts to remain under this license does not apply to +any document created using the fonts or their derivatives. + + + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. +This may include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components +as distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting ? in part or in whole ? +any of the components of the Original Version, by changing formats or +by porting the Font Software to a new environment. + +"Author" refers to any designer, engineer, programmer, technical writer +or other person who contributed to the Font Software. + + +PERMISSION & CONDITIONS + +Permission is hereby granted, free of charge, to any person obtaining a +copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components,in +   Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +   redistributed and/or sold with any software, provided that each copy +   contains the above copyright notice and this license. These can be +   included either as stand-alone text files, human-readable headers or +   in the appropriate machine-readable metadata fields within text or +   binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +   Name(s) unless explicit written permission is granted by the +   corresponding Copyright Holder. This restriction only applies to the +   primary font name as presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +   Software shall not be used to promote, endorse or advertise any +   Modified Version, except to acknowledge the contribution(s) of the +   Copyright Holder(s) and the Author(s) or with their explicit written +   permission. + +5) The Font Software, modified or unmodified, in part or in whole, must +   be distributed entirely under this license, and must not be distributed +   under any other license. The requirement for fonts to remain under +   this license does not apply to any document created using the Font +   Software. + + + +TERMINATION +This license becomes null and void if any of the above conditions are not met. + + + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT.  IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER +DEALINGS IN THE FONT SOFTWARE. +------------------------------------------------------------------------------- +------------------------------------------------------------------------------- + |