summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.clang-format178
-rw-r--r--.copyright_headers/C_HEADER.txt25
-rw-r--r--.copyright_headers/LUA_HEADER.txt25
-rw-r--r--.gitignore8
-rw-r--r--.gitmodules8
-rw-r--r--CMakeLists.txt96
-rw-r--r--CONTRIBUTING.md3
-rw-r--r--LICENSE17
-rw-r--r--README.md134
-rw-r--r--dnglib/algs.lua151
-rw-r--r--dnglib/constants.lua90
-rw-r--r--dnglib/defaults.lua178
-rw-r--r--dnglib/queue.lua55
-rw-r--r--maps/lvl1/dng.map10
-rw-r--r--maps/lvl1/proc.lua7
-rw-r--r--maps/lvl2/dng.map18
-rw-r--r--maps/lvl2/proc.lua0
-rw-r--r--maps/lvl3/dng.map40
-rw-r--r--res/LiberationMono-Regular.ttfbin0 -> 319508 bytes
-rw-r--r--res/PressStart2P-vaV7.ttfbin0 -> 82480 bytes
-rw-r--r--src/CApi.h235
-rw-r--r--src/Level.cpp161
-rw-r--r--src/Level.h84
-rw-r--r--src/LuaApi.h120
-rw-r--r--src/MessageBox.h98
-rw-r--r--src/Scene.h31
-rw-r--r--src/SfmlUtils.h110
-rw-r--r--src/linux/res.h77
-rw-r--r--src/lua.hpp8
-rw-r--r--src/main.cpp278
-rw-r--r--thirdparty/.clang-format4
m---------thirdparty/SFML0
m---------thirdparty/lua0
-rw-r--r--thirdparty/thirdparty-licenses.txt250
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.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..8cf10c3
--- /dev/null
+++ b/LICENSE
@@ -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.ttf
new file mode 100644
index 0000000..a1d70ed
--- /dev/null
+++ b/res/LiberationMono-Regular.ttf
Binary files differ
diff --git a/res/PressStart2P-vaV7.ttf b/res/PressStart2P-vaV7.ttf
new file mode 100644
index 0000000..98044e9
--- /dev/null
+++ b/res/PressStart2P-vaV7.ttf
Binary files differ
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.
+-------------------------------------------------------------------------------
+-------------------------------------------------------------------------------
+