Compare commits
52 Commits
4f502b707f
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| b16dbaceec | |||
| f39b2060a8 | |||
| aed202ebf9 | |||
| a495179e5f | |||
| 4e1b404820 | |||
| 8c74ee31e0 | |||
| 77d3c54ebb | |||
| b5de39926b | |||
| 3a8dafbb91 | |||
| 6b22f547fe | |||
| de78be3e25 | |||
| 9f507be7bc | |||
| 9aaf271996 | |||
| b01c0d37b0 | |||
| 538079880d | |||
| fe0529d021 | |||
| d068f0f2c3 | |||
| f9a64b8d54 | |||
| 01cbfaae95 | |||
| f9006a90d5 | |||
| 7daeaee6b5 | |||
| 03218ce20f | |||
| 6f33522c1c | |||
| 3697cc3eef | |||
| 51a1077fda | |||
| 8740c2b165 | |||
| 6ed2bdd4c5 | |||
| c32df89490 | |||
| bd5a67676b | |||
| 903dab49e3 | |||
| 1668c4b0d2 | |||
| 2179a27bf5 | |||
| 6e7a0cba76 | |||
| 69b37b30bc | |||
| ae941a0fdb | |||
| 1b741a81e5 | |||
| edf321515b | |||
| c874e6c197 | |||
| 9a59c22288 | |||
| 750e8840f0 | |||
| cf59989167 | |||
| 7c194ab4b4 | |||
| be422d0a1e | |||
| 68b63d3007 | |||
| 8525138594 | |||
| 7b9f8b190e | |||
| 67f62daa9f | |||
| 0ec701f30b | |||
| c53439066e | |||
| 7278bd0c6f | |||
| b842e5821a | |||
| f7d4cce485 |
@@ -17,7 +17,7 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
apt-get update
|
||||
apt-get install -y build-essential cmake python3 python3-pip python3-polib python3-pil libsdl2-dev libgl1-mesa-dev libzip-dev
|
||||
apt-get install -y build-essential cmake python3 python3-pip python3-polib python3-pil libsdl2-dev libgl1-mesa-dev libzip-dev python3-dotenv python3-pyqt5 python3-opengl liblua5.3-dev
|
||||
- name: Configure CMake
|
||||
run: cmake -S . -B build -DDUSK_TARGET_SYSTEM=linux
|
||||
- name: Build
|
||||
@@ -39,7 +39,7 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
apt-get update
|
||||
apt-get install -y build-essential cmake python3 python3-pip python3-polib python3-pil libsdl2-dev libgl1-mesa-dev libzip-dev
|
||||
apt-get install -y build-essential cmake python3 python3-pip python3-polib python3-pil libsdl2-dev libgl1-mesa-dev libzip-dev python3-dotenv python3-pyqt5 python3-opengl liblua5.3-dev
|
||||
- name: Configure CMake
|
||||
run: cmake -S . -B build -DDUSK_TARGET_SYSTEM=psp
|
||||
- name: Build
|
||||
@@ -56,29 +56,3 @@ jobs:
|
||||
name: dusk-psp
|
||||
path: build/gitea/
|
||||
if-no-files-found: error
|
||||
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- build-linux
|
||||
- build-psp
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
- name: Download Linux binary
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: dusk-linux
|
||||
path: ./release-assets/linux
|
||||
- name: Download PSP binary
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: dusk-psp
|
||||
path: ./release-assets/psp
|
||||
- name: Create Gitea Release
|
||||
uses: akkuman/gitea-release-action@v1
|
||||
env:
|
||||
NODE_OPTIONS: '--experimental-fetch' # if nodejs < 18
|
||||
files: |
|
||||
./release-assets/linux/Dusk
|
||||
./release-assets/psp/Dusk/EBOOT.PBP
|
||||
7
.gitignore
vendored
7
.gitignore
vendored
@@ -95,3 +95,10 @@ assets/borrowed
|
||||
# /archive
|
||||
|
||||
__pycache__
|
||||
|
||||
package-lock.json
|
||||
yarn-error.log
|
||||
yarn.lock
|
||||
|
||||
.editor
|
||||
.venv
|
||||
@@ -92,9 +92,8 @@ add_subdirectory(src)
|
||||
# Build assets
|
||||
add_custom_target(DUSK_ASSETS_BUILT ALL
|
||||
COMMAND
|
||||
${Python3_EXECUTABLE} ${DUSK_TOOLS_DIR}/assetstool/main.py
|
||||
${Python3_EXECUTABLE} ${DUSK_TOOLS_DIR}/assettool.py
|
||||
--assets ${DUSK_GAME_ASSETS_DIR}
|
||||
--build-type wad
|
||||
--output-assets ${DUSK_BUILT_ASSETS_DIR}
|
||||
--output-file ${DUSK_BUILD_DIR}/dusk.dsk
|
||||
--headers-dir ${DUSK_GENERATED_HEADERS_DIR}
|
||||
|
||||
23
README.md
Normal file
23
README.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# Dusk
|
||||
RPG Game Project, small and able to run on a PSP.
|
||||
|
||||
# Building
|
||||
Each build target has different requirements. You can take a look at the git
|
||||
workflow to see how the builds are done for each target. In addition, for
|
||||
accessing the editor and building the game on your host system, install the
|
||||
following packages, depending on your system;
|
||||
|
||||
Fedora;
|
||||
```
|
||||
sudo dnf install git make gcc python python-polib python3-pillow python3-dotenv python3-numpy python-qt5 python3-pyopengl
|
||||
```
|
||||
|
||||
Ubuntu;
|
||||
```
|
||||
sudo apt-get install git build-essential gcc python python-polib python3-pillow python3-dotenv python3-numpy python3-pyqt5 python3-opengl
|
||||
```
|
||||
|
||||
Arch Linux;
|
||||
```
|
||||
sudo pacman -S git base-devel gcc python python-polib python-pillow python-dotenv python-numpy python-pyqt5 python-opengl
|
||||
```
|
||||
@@ -12,5 +12,6 @@ add_subdirectory(palette)
|
||||
add_subdirectory(locale)
|
||||
|
||||
add_subdirectory(entity)
|
||||
add_subdirectory(script)
|
||||
add_subdirectory(map)
|
||||
add_subdirectory(ui)
|
||||
@@ -1,9 +1,8 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Language: en_US\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "ui.test"
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
add_asset(MAP map)
|
||||
add_asset(MAP map.json)
|
||||
3
assets/map/map.json
Normal file
3
assets/map/map.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"mapName": "Test"
|
||||
}
|
||||
1
assets/map/map/-1_-1_0.json
Normal file
1
assets/map/map/-1_-1_0.json
Normal file
@@ -0,0 +1 @@
|
||||
{"shapes": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "entities": []}
|
||||
1
assets/map/map/-1_-1_1.json
Normal file
1
assets/map/map/-1_-1_1.json
Normal file
@@ -0,0 +1 @@
|
||||
{"shapes": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "entities": []}
|
||||
1
assets/map/map/-1_0_0.json
Normal file
1
assets/map/map/-1_0_0.json
Normal file
@@ -0,0 +1 @@
|
||||
{"shapes": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "entities": []}
|
||||
1
assets/map/map/-2_1_0.json
Normal file
1
assets/map/map/-2_1_0.json
Normal file
@@ -0,0 +1 @@
|
||||
{"shapes": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "entities": []}
|
||||
1
assets/map/map/0_-1_0.json
Normal file
1
assets/map/map/0_-1_0.json
Normal file
@@ -0,0 +1 @@
|
||||
{"shapes": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "entities": []}
|
||||
1
assets/map/map/0_-1_1.json
Normal file
1
assets/map/map/0_-1_1.json
Normal file
@@ -0,0 +1 @@
|
||||
{"shapes": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "entities": []}
|
||||
@@ -1,37 +1 @@
|
||||
{
|
||||
"tiles": [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 5, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
|
||||
1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
]
|
||||
}
|
||||
{"shapes": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 5, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 1, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 1, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 2, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "entities": []}
|
||||
@@ -1,37 +1 @@
|
||||
{
|
||||
"tiles": [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
]
|
||||
}
|
||||
{"shapes": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "entities": []}
|
||||
1
assets/map/map/2_0_0.json
Normal file
1
assets/map/map/2_0_0.json
Normal file
@@ -0,0 +1 @@
|
||||
{"shapes": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "entities": []}
|
||||
1
assets/map/map/3_0_0.json
Normal file
1
assets/map/map/3_0_0.json
Normal file
@@ -0,0 +1 @@
|
||||
{"shapes": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "entities": []}
|
||||
1
assets/map/map/4_0_0.json
Normal file
1
assets/map/map/4_0_0.json
Normal file
@@ -0,0 +1 @@
|
||||
{"shapes": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "entities": []}
|
||||
1
assets/map/map/5_0_0.json
Normal file
1
assets/map/map/5_0_0.json
Normal file
@@ -0,0 +1 @@
|
||||
{"shapes": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]}
|
||||
6
assets/script/CMakeLists.txt
Normal file
6
assets/script/CMakeLists.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
# Copyright (c) 2025 Dominic Masters
|
||||
#
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
add_asset(SCRIPT init.lua)
|
||||
40
assets/script/init.lua
Normal file
40
assets/script/init.lua
Normal file
@@ -0,0 +1,40 @@
|
||||
module('platform')
|
||||
module('input')
|
||||
module('scene')
|
||||
|
||||
-- Default Input bindings.
|
||||
if PLATFORM == "psp" then
|
||||
inputBind("up", "up")
|
||||
inputBind("down", "down")
|
||||
inputBind("left", "left")
|
||||
inputBind("right", "right")
|
||||
inputBind("circle", "cancel")
|
||||
inputBind("cross", "accept")
|
||||
inputBind("select", "ragequit")
|
||||
inputBind("lstick_up", "up")
|
||||
inputBind("lstick_down", "down")
|
||||
inputBind("lstick_left", "left")
|
||||
inputBind("lstick_right", "right")
|
||||
else
|
||||
if INPUT_KEYBOARD then
|
||||
inputBind("w", "up")
|
||||
inputBind("s", "down")
|
||||
inputBind("a", "left")
|
||||
inputBind("d", "right")
|
||||
|
||||
inputBind("left", "left")
|
||||
inputBind("right", "right")
|
||||
inputBind("up", "up")
|
||||
inputBind("down", "down")
|
||||
|
||||
inputBind("enter", "accept")
|
||||
inputBind("e", "accept" )
|
||||
|
||||
inputBind("escape", "cancel")
|
||||
inputBind("q", "cancel")
|
||||
|
||||
inputBind("z", "ragequit")
|
||||
end
|
||||
end
|
||||
|
||||
sceneSet('map')
|
||||
64
cmake/modules/envtoh.cmake
Normal file
64
cmake/modules/envtoh.cmake
Normal file
@@ -0,0 +1,64 @@
|
||||
if(NOT DEFINED ENV_FILE)
|
||||
message(FATAL_ERROR "ENV_FILE is not set")
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED OUT_HEADER)
|
||||
message(FATAL_ERROR "OUT_HEADER is not set")
|
||||
endif()
|
||||
|
||||
if(NOT EXISTS "${ENV_FILE}")
|
||||
message(FATAL_ERROR ".env file not found: ${ENV_FILE}")
|
||||
endif()
|
||||
|
||||
file(STRINGS "${ENV_FILE}" ENV_LINES)
|
||||
|
||||
set(HEADER_CONTENT "#pragma once\n#include \"dusk.h\"\n\n")
|
||||
|
||||
foreach(line IN LISTS ENV_LINES)
|
||||
# Skip comments and empty lines (allow whitespace before # or ;)
|
||||
if(line STREQUAL "" OR line MATCHES "^[ \t]*[#;]")
|
||||
continue()
|
||||
endif()
|
||||
|
||||
# Expect KEY=VALUE (allow spaces around '=')
|
||||
if(NOT line MATCHES "^[A-Za-z_][A-Za-z0-9_]*[ \t]*=[ \t]*")
|
||||
message(WARNING "Skipping invalid line in ${ENV_FILE}: '${line}'")
|
||||
continue()
|
||||
endif()
|
||||
|
||||
# Extract key
|
||||
string(REGEX MATCH "^[A-Za-z_][A-Za-z0-9_]*" KEY "${line}")
|
||||
string(LENGTH "${KEY}" key_len)
|
||||
|
||||
# Extract value (allow spaces around '=')
|
||||
string(REGEX REPLACE "^[A-Za-z_][A-Za-z0-9_]*[ \t]*=[ \t]*" "" RAW_VALUE "${line}")
|
||||
|
||||
# Lowercase copy for boolean detection
|
||||
string(TOLOWER "${RAW_VALUE}" VALUE_LC)
|
||||
set(VALUE "${RAW_VALUE}")
|
||||
|
||||
# Determine type and format accordingly
|
||||
if(VALUE_LC STREQUAL "true")
|
||||
set(HEADER_CONTENT "${HEADER_CONTENT}#define ${KEY} true\n")
|
||||
|
||||
elseif(VALUE_LC STREQUAL "false")
|
||||
set(HEADER_CONTENT "${HEADER_CONTENT}#define ${KEY} false\n")
|
||||
|
||||
elseif(VALUE MATCHES "^[+-]?[0-9]+$")
|
||||
# Integer
|
||||
set(HEADER_CONTENT "${HEADER_CONTENT}#define ${KEY} ${VALUE}\n")
|
||||
|
||||
elseif(VALUE MATCHES "^[+-]?[0-9]*\\.[0-9]+$")
|
||||
# Float → append "f"
|
||||
set(HEADER_CONTENT "${HEADER_CONTENT}#define ${KEY} ${VALUE}f\n")
|
||||
|
||||
else()
|
||||
# String → escape for C literal
|
||||
string(REPLACE "\\" "\\\\" VALUE_ESC "${VALUE}")
|
||||
string(REPLACE "\"" "\\\"" VALUE_ESC "${VALUE_ESC}")
|
||||
set(HEADER_CONTENT "${HEADER_CONTENT}#define ${KEY} \"${VALUE_ESC}\"\n")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
file(WRITE "${OUT_HEADER}" "${HEADER_CONTENT}")
|
||||
message(STATUS "Generated header: ${OUT_HEADER}")
|
||||
@@ -5,6 +5,17 @@
|
||||
|
||||
find_package(cglm REQUIRED)
|
||||
find_package(libzip REQUIRED)
|
||||
find_package(Lua REQUIRED)
|
||||
|
||||
if(Lua_FOUND AND NOT TARGET Lua::Lua)
|
||||
add_library(Lua::Lua INTERFACE IMPORTED)
|
||||
set_target_properties(
|
||||
Lua::Lua
|
||||
PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${LUA_INCLUDE_DIR}"
|
||||
INTERFACE_LINK_LIBRARIES "${LUA_LIBRARIES}"
|
||||
)
|
||||
endif()
|
||||
|
||||
# Libs
|
||||
target_link_libraries(${DUSK_TARGET_NAME}
|
||||
@@ -13,6 +24,7 @@ target_link_libraries(${DUSK_TARGET_NAME}
|
||||
cglm
|
||||
zip
|
||||
pthread
|
||||
Lua::Lua
|
||||
)
|
||||
|
||||
# Includes
|
||||
@@ -27,6 +39,13 @@ target_sources(${DUSK_TARGET_NAME}
|
||||
main.c
|
||||
)
|
||||
|
||||
# Defs
|
||||
add_defs(duskdefs.env duskdefs.h)
|
||||
target_compile_definitions(${DUSK_TARGET_NAME}
|
||||
PRIVATE
|
||||
DUSK_TARGET_SYSTEM="${DUSK_TARGET_SYSTEM}"
|
||||
)
|
||||
|
||||
# Subdirs
|
||||
add_subdirectory(assert)
|
||||
add_subdirectory(asset)
|
||||
@@ -38,6 +57,7 @@ add_subdirectory(input)
|
||||
add_subdirectory(locale)
|
||||
add_subdirectory(rpg)
|
||||
add_subdirectory(scene)
|
||||
add_subdirectory(script)
|
||||
add_subdirectory(thread)
|
||||
add_subdirectory(time)
|
||||
add_subdirectory(ui)
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "type/assetlanguage.h"
|
||||
#include "type/assetmap.h"
|
||||
#include "type/assetchunk.h"
|
||||
#include "type/assetscript.h"
|
||||
#include <zip.h>
|
||||
|
||||
typedef enum {
|
||||
@@ -21,6 +22,7 @@ typedef enum {
|
||||
ASSET_TYPE_LANGUAGE,
|
||||
ASSET_TYPE_MAP,
|
||||
ASSET_TYPE_CHUNK,
|
||||
ASSET_TYPE_SCRIPT,
|
||||
|
||||
ASSET_TYPE_COUNT,
|
||||
} assettype_t;
|
||||
@@ -81,5 +83,11 @@ static const assettypedef_t ASSET_TYPE_DEFINITIONS[ASSET_TYPE_COUNT] = {
|
||||
.header = "DCF",
|
||||
.loadStrategy = ASSET_LOAD_STRAT_CUSTOM,
|
||||
.custom = assetChunkLoad
|
||||
},
|
||||
|
||||
[ASSET_TYPE_SCRIPT] = {
|
||||
.header = "DSF",
|
||||
.loadStrategy = ASSET_LOAD_STRAT_CUSTOM,
|
||||
.custom = assetScriptHandler
|
||||
}
|
||||
};
|
||||
@@ -11,4 +11,5 @@ target_sources(${DUSK_TARGET_NAME}
|
||||
assetlanguage.c
|
||||
assetmap.c
|
||||
assetchunk.c
|
||||
assetscript.c
|
||||
)
|
||||
@@ -7,11 +7,13 @@
|
||||
|
||||
#include "asset/asset.h"
|
||||
#include "assert/assert.h"
|
||||
#include "rpg/entity/entity.h"
|
||||
|
||||
#pragma pack(push, 1)
|
||||
typedef struct {
|
||||
uint32_t tileCount;
|
||||
uint8_t modelCount;
|
||||
uint8_t entityCount;
|
||||
} assetchunkheader_t;
|
||||
#pragma pack(pop)
|
||||
|
||||
@@ -27,17 +29,21 @@ typedef struct {
|
||||
} assetchunkmodelheader_t;
|
||||
#pragma pack(pop)
|
||||
|
||||
#pragma pack(push, 1)
|
||||
typedef struct {
|
||||
entitytype_t entityType;
|
||||
uint8_t localX;
|
||||
uint8_t localY;
|
||||
uint8_t localZ;
|
||||
} assetchunkentityheader_t;
|
||||
#pragma pack(pop)
|
||||
|
||||
errorret_t assetChunkLoad(assetcustom_t custom) {
|
||||
assertNotNull(custom.output, "Output pointer cannot be NULL");
|
||||
assertNotNull(custom.zipFile, "Zip file pointer cannot be NULL");
|
||||
|
||||
chunk_t *chunk = (chunk_t *)custom.output;
|
||||
printf(
|
||||
"Loading chunk asset at position (%d, %d, %d)...\n",
|
||||
chunk->position.x,
|
||||
chunk->position.y,
|
||||
chunk->position.z
|
||||
);
|
||||
assertTrue(chunk->meshCount == 0, "Chunk is not in a good state");
|
||||
|
||||
// Read header
|
||||
assetchunkheader_t header;
|
||||
@@ -67,6 +73,15 @@ errorret_t assetChunkLoad(assetcustom_t custom) {
|
||||
);
|
||||
}
|
||||
|
||||
if(header.entityCount > CHUNK_ENTITY_COUNT_MAX) {
|
||||
zip_fclose(custom.zipFile);
|
||||
errorThrow(
|
||||
"Chunk asset has too many entities: %d (max %d).",
|
||||
header.entityCount,
|
||||
CHUNK_ENTITY_COUNT_MAX
|
||||
);
|
||||
}
|
||||
|
||||
chunk->meshCount = header.modelCount;
|
||||
|
||||
// Read tile data
|
||||
@@ -112,6 +127,7 @@ errorret_t assetChunkLoad(assetcustom_t custom) {
|
||||
}
|
||||
|
||||
// Init the mesh
|
||||
if(modelHeader.vertexCount > 0) {
|
||||
mesh_t *mesh = &chunk->meshes[i];
|
||||
meshInit(
|
||||
mesh,
|
||||
@@ -121,6 +137,41 @@ errorret_t assetChunkLoad(assetcustom_t custom) {
|
||||
);
|
||||
|
||||
vertexIndex += modelHeader.vertexCount;
|
||||
} else {
|
||||
chunk->meshes[i].vertexCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Read entity data
|
||||
for(uint8_t i = 0; i < header.entityCount; i++) {
|
||||
assetchunkentityheader_t entityHeader;
|
||||
bytesRead = zip_fread(
|
||||
custom.zipFile, &entityHeader, sizeof(assetchunkentityheader_t)
|
||||
);
|
||||
if(bytesRead != sizeof(assetchunkentityheader_t)) {
|
||||
zip_fclose(custom.zipFile);
|
||||
errorThrow("Failed to read chunk entity header.");
|
||||
}
|
||||
|
||||
uint8_t entityIndex = entityGetAvailable();
|
||||
if(entityIndex == 0xFF) {
|
||||
zip_fclose(custom.zipFile);
|
||||
errorThrow("No available entity slots.");
|
||||
}
|
||||
|
||||
entity_t *entity = &ENTITIES[entityIndex];
|
||||
entityInit(entity, (entitytype_t)entityHeader.entityType);
|
||||
entity->position.x = (
|
||||
(chunk->position.x * CHUNK_WIDTH) + entityHeader.localX
|
||||
);
|
||||
entity->position.y = (
|
||||
(chunk->position.y * CHUNK_HEIGHT) + entityHeader.localY
|
||||
);
|
||||
entity->position.z = (
|
||||
(chunk->position.z * CHUNK_DEPTH) + entityHeader.localZ
|
||||
);
|
||||
|
||||
chunk->entities[i] = entityIndex;
|
||||
}
|
||||
|
||||
errorOk();
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
#pragma once
|
||||
#include "locale/language/keys.h"
|
||||
#include "error/error.h"
|
||||
#include "duskdefs.h"
|
||||
#include <zip.h>
|
||||
|
||||
#define ASSET_LANG_CHUNK_CHAR_COUNT 6 * 1024 // 6 KB per chunk
|
||||
#define ASSET_LANG_CHUNK_CACHE 4 // Number of chunks to cache in memory
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
58
src/asset/type/assetscript.c
Normal file
58
src/asset/type/assetscript.c
Normal file
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "asset/asset.h"
|
||||
#include "assert/assert.h"
|
||||
|
||||
errorret_t assetScriptHandler(assetcustom_t custom) {
|
||||
assertNotNull(custom.zipFile, "Custom asset zip file cannot be NULL");
|
||||
assertNotNull(custom.output, "Custom asset output cannot be NULL");
|
||||
|
||||
assetscript_t *script = (assetscript_t *)custom.output;
|
||||
errorChain(assetScriptInit(script, custom.zipFile));
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t assetScriptInit(
|
||||
assetscript_t *script,
|
||||
zip_file_t *zipFile
|
||||
) {
|
||||
assertNotNull(script, "Script asset cannot be NULL");
|
||||
assertNotNull(zipFile, "Zip file cannot be NULL");
|
||||
|
||||
// We now own the zip file handle.
|
||||
script->zip = zipFile;
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
const char_t * assetScriptReader(lua_State* lState, void* data, size_t* size) {
|
||||
assetscript_t *script = (assetscript_t *)data;
|
||||
zip_int64_t bytesRead = zip_fread(
|
||||
script->zip, script->buffer, sizeof(script->buffer)
|
||||
);
|
||||
|
||||
if(bytesRead < 0) {
|
||||
*size = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*size = (size_t)bytesRead;
|
||||
return script->buffer;
|
||||
}
|
||||
|
||||
errorret_t assetScriptDispose(assetscript_t *script) {
|
||||
assertNotNull(script, "Script asset cannot be NULL");
|
||||
|
||||
if(script->zip != NULL) {
|
||||
zip_fclose(script->zip);
|
||||
script->zip = NULL;
|
||||
}
|
||||
|
||||
errorOk();
|
||||
}
|
||||
57
src/asset/type/assetscript.h
Normal file
57
src/asset/type/assetscript.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "error/error.h"
|
||||
#include "duskdefs.h"
|
||||
#include <zip.h>
|
||||
#include <lua.h>
|
||||
|
||||
#define ASSET_SCRIPT_BUFFER_SIZE 1024
|
||||
|
||||
typedef struct assetscript_s {
|
||||
zip_file_t *zip;
|
||||
char_t buffer[ASSET_SCRIPT_BUFFER_SIZE];
|
||||
} assetscript_t;
|
||||
|
||||
typedef struct assetcustom_s assetcustom_t;
|
||||
|
||||
/**
|
||||
* Receiving function from the asset manager to handle script assets.
|
||||
*
|
||||
* @param custom Custom asset loading data.
|
||||
* @return Error code.
|
||||
*/
|
||||
errorret_t assetScriptHandler(assetcustom_t custom);
|
||||
|
||||
/**
|
||||
* Initializes a script asset.
|
||||
*
|
||||
* @param script Script asset to initialize.
|
||||
* @param zipFile Zip file handle for the script asset.
|
||||
* @return Error code.
|
||||
*/
|
||||
errorret_t assetScriptInit(assetscript_t *script, zip_file_t *zipFile);
|
||||
|
||||
/**
|
||||
* Reader function for Lua to read script data from the asset.
|
||||
*
|
||||
* @param L Lua state.
|
||||
* @param data Pointer to the assetscript_t structure.
|
||||
* @param size Pointer to store the size of the read data.
|
||||
* @return Pointer to the read data buffer.
|
||||
*/
|
||||
const char_t * assetScriptReader(lua_State* L, void* data, size_t* size);
|
||||
|
||||
/**
|
||||
* Disposes of a script asset, freeing any allocated resources.
|
||||
*
|
||||
* @param script Script asset to dispose of.
|
||||
* @return Error code.
|
||||
*/
|
||||
errorret_t assetScriptDispose(assetscript_t *script);
|
||||
|
||||
@@ -12,9 +12,7 @@ void debugPrint(const char_t *message, ...) {
|
||||
va_start(args, message);
|
||||
vprintf(message, args);
|
||||
va_end(args);
|
||||
|
||||
// For the time being just use standard printing functions.
|
||||
printf(message, args);
|
||||
fflush(stdout);
|
||||
|
||||
#if PSP
|
||||
FILE *file = fopen("ms0:/PSP/GAME/Dusk/debug.log", "a");
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "display/display.h"
|
||||
#include "assert/assert.h"
|
||||
#include "display/framebuffer.h"
|
||||
#include "display/screen.h"
|
||||
|
||||
void cameraInit(camera_t *camera) {
|
||||
cameraInitPerspective(camera);
|
||||
@@ -112,9 +113,10 @@ void cameraPushMatrix(camera_t *camera) {
|
||||
"Pixel perfect camera view requires perspective projection"
|
||||
);
|
||||
|
||||
const float_t viewportHeight = (
|
||||
(float_t)frameBufferGetHeight(FRAMEBUFFER_BOUND)
|
||||
);
|
||||
// const float_t viewportHeight = (
|
||||
// (float_t)frameBufferGetHeight(FRAMEBUFFER_BOUND)
|
||||
// );
|
||||
const float_t viewportHeight = (float_t)SCREEN.height;
|
||||
const float_t z = (viewportHeight / 2.0f) / (
|
||||
camera->lookatPixelPerfect.pixelsPerUnit *
|
||||
tanf(camera->perspective.fov / 2.0f)
|
||||
|
||||
@@ -51,10 +51,15 @@ errorret_t displayInit(void) {
|
||||
}
|
||||
|
||||
SDL_GL_SetSwapInterval(1);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
|
||||
glDisable(GL_CULL_FACE);
|
||||
glDisable(GL_LIGHTING);// PSP defaults this on?
|
||||
glShadeModel(GL_SMOOTH); // Fixes color on PSP?
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glDepthFunc(GL_LEQUAL);
|
||||
glClearDepth(1.0f);
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
||||
|
||||
@@ -17,7 +17,7 @@ void screenInit() {
|
||||
|
||||
|
||||
#if DISPLAY_SIZE_DYNAMIC == 1
|
||||
SCREEN.mode = SCREEN_MODE_FIXED_HEIGHT;
|
||||
SCREEN.mode = SCREEN_MODE_FIXED_VIEWPORT_HEIGHT;
|
||||
SCREEN.fixedHeight.height = DISPLAY_SCREEN_HEIGHT_DEFAULT;
|
||||
|
||||
cameraInitOrthographic(&SCREEN.framebufferCamera);
|
||||
@@ -242,6 +242,17 @@ void screenBind() {
|
||||
frameBufferBind(&SCREEN.framebuffer);
|
||||
break;
|
||||
}
|
||||
|
||||
case SCREEN_MODE_FIXED_VIEWPORT_HEIGHT: {
|
||||
SCREEN.height = SCREEN.fixedViewportHeight.height;
|
||||
float_t fbWidth = (float_t)frameBufferGetWidth(FRAMEBUFFER_BOUND);
|
||||
float_t fbHeight = (float_t)frameBufferGetHeight(FRAMEBUFFER_BOUND);
|
||||
float_t fbAspect = fbWidth / fbHeight;
|
||||
SCREEN.width = (int32_t)floorf(SCREEN.height * fbAspect);
|
||||
SCREEN.aspect = (float_t)SCREEN.width / (float_t)SCREEN.height;
|
||||
break;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
default: {
|
||||
@@ -253,10 +264,9 @@ void screenBind() {
|
||||
|
||||
void screenUnbind() {
|
||||
switch(SCREEN.mode) {
|
||||
case SCREEN_MODE_BACKBUFFER: {
|
||||
// Nothing to do here.
|
||||
case SCREEN_MODE_BACKBUFFER:
|
||||
break;
|
||||
}
|
||||
|
||||
#if DISPLAY_SIZE_DYNAMIC == 1
|
||||
case SCREEN_MODE_ASPECT_RATIO:
|
||||
@@ -265,14 +275,16 @@ void screenUnbind() {
|
||||
case SCREEN_MODE_FIXED_WIDTH:
|
||||
if(SCREEN.framebufferReady) frameBufferBind(NULL);
|
||||
break;
|
||||
|
||||
case SCREEN_MODE_FIXED_VIEWPORT_HEIGHT:
|
||||
break;
|
||||
#endif
|
||||
|
||||
default: {
|
||||
default:
|
||||
assertUnreachable("Invalid screen mode.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void screenRender() {
|
||||
if(SCREEN.mode == SCREEN_MODE_BACKBUFFER) {
|
||||
@@ -280,6 +292,11 @@ void screenRender() {
|
||||
}
|
||||
|
||||
#if DISPLAY_SIZE_DYNAMIC == 1
|
||||
if(SCREEN.mode == SCREEN_MODE_FIXED_VIEWPORT_HEIGHT) {
|
||||
glViewport(0, 0, SCREEN.width, SCREEN.height);
|
||||
return;
|
||||
}
|
||||
|
||||
if(
|
||||
SCREEN.mode == SCREEN_MODE_ASPECT_RATIO ||
|
||||
SCREEN.mode == SCREEN_MODE_FIXED_HEIGHT ||
|
||||
|
||||
@@ -25,6 +25,8 @@ typedef enum {
|
||||
SCREEN_MODE_ASPECT_RATIO,// Maintains aspect at all cost
|
||||
SCREEN_MODE_FIXED_HEIGHT, // Fixed height, width expands/contracts as needed
|
||||
SCREEN_MODE_FIXED_WIDTH, // Fixed width, height expands/contracts as needed
|
||||
// Fixed viewport height. Fixed height but higher resolution.
|
||||
SCREEN_MODE_FIXED_VIEWPORT_HEIGHT,
|
||||
#endif
|
||||
} screenmode_t;
|
||||
|
||||
@@ -68,6 +70,10 @@ typedef struct {
|
||||
struct {
|
||||
int32_t width;
|
||||
} fixedWidth;
|
||||
|
||||
struct {
|
||||
int32_t height;
|
||||
} fixedViewportHeight;
|
||||
};
|
||||
} screen_t;
|
||||
|
||||
|
||||
45
src/duskdefs.env
Normal file
45
src/duskdefs.env
Normal file
@@ -0,0 +1,45 @@
|
||||
# Copyright (c) 2025 Dominic Masters
|
||||
#
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
ENTITY_DIR_SOUTH = 0
|
||||
ENTITY_DIR_WEST = 1
|
||||
ENTITY_DIR_EAST = 2
|
||||
ENTITY_DIR_NORTH = 3
|
||||
|
||||
ENTITY_COUNT = 128
|
||||
|
||||
ENTITY_TYPE_NULL = 0
|
||||
ENTITY_TYPE_PLAYER = 1
|
||||
ENTITY_TYPE_NPC = 2
|
||||
ENTITY_TYPE_COUNT = 3
|
||||
|
||||
CHUNK_WIDTH = 16
|
||||
CHUNK_HEIGHT = 16
|
||||
CHUNK_DEPTH = 4
|
||||
# CHUNK_VERTEX_COUNT_MAX = QUAD_VERTEXES * CHUNK_WIDTH * CHUNK_HEIGHT * 4
|
||||
CHUNK_VERTEX_COUNT_MAX=6144
|
||||
CHUNK_MESH_COUNT_MAX = 14
|
||||
CHUNK_ENTITY_COUNT_MAX = 8
|
||||
|
||||
TILE_WIDTH = 16.0
|
||||
TILE_HEIGHT = 16.0
|
||||
TILE_DEPTH = 16.0
|
||||
|
||||
TILE_SHAPE_NULL = 0
|
||||
TILE_SHAPE_FLOOR = 1
|
||||
TILE_SHAPE_RAMP_SOUTH = 2
|
||||
TILE_SHAPE_RAMP_WEST = 3
|
||||
TILE_SHAPE_RAMP_EAST = 4
|
||||
TILE_SHAPE_RAMP_NORTH = 5
|
||||
TILE_SHAPE_RAMP_SOUTHWEST = 6
|
||||
TILE_SHAPE_RAMP_SOUTHEAST = 7
|
||||
TILE_SHAPE_RAMP_NORTHWEST = 8
|
||||
TILE_SHAPE_RAMP_NORTHEAST = 9
|
||||
|
||||
RPG_CAMERA_FOV = 70
|
||||
RPG_CAMERA_PIXELS_PER_UNIT = 1.0
|
||||
RPG_CAMERA_Z_OFFSET = 24.0
|
||||
|
||||
ASSET_LANG_CHUNK_CHAR_COUNT = 6144
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "asset/asset.h"
|
||||
#include "ui/ui.h"
|
||||
#include "rpg/rpg.h"
|
||||
#include "script/scriptmanager.h"
|
||||
#include "debug/debug.h"
|
||||
|
||||
engine_t ENGINE;
|
||||
@@ -31,11 +32,18 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) {
|
||||
inputInit();
|
||||
errorChain(assetInit());
|
||||
errorChain(localeManagerInit());
|
||||
errorChain(scriptManagerInit());
|
||||
errorChain(displayInit());
|
||||
errorChain(uiInit());
|
||||
errorChain(rpgInit());
|
||||
errorChain(sceneManagerInit());
|
||||
|
||||
// Run the initial script.
|
||||
scriptcontext_t ctx;
|
||||
errorChain(scriptContextInit(&ctx));
|
||||
errorChain(scriptContextExecFile(&ctx, "script/init.dsf"));
|
||||
scriptContextDispose(&ctx);
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
@@ -48,7 +56,6 @@ errorret_t engineUpdate(void) {
|
||||
sceneManagerUpdate();
|
||||
errorChain(displayUpdate());
|
||||
|
||||
|
||||
if(inputPressed(INPUT_ACTION_RAGEQUIT)) ENGINE.running = false;
|
||||
|
||||
errorOk();
|
||||
|
||||
@@ -26,42 +26,42 @@ void inputInit(void) {
|
||||
INPUT.deadzone = 0.2f;
|
||||
|
||||
// Setup Default Binds
|
||||
#if INPUT_SDL2 == 1
|
||||
#if INPUT_KEYBOARD == 1
|
||||
inputBind(inputButtonGetByName("up"), INPUT_ACTION_UP);
|
||||
inputBind(inputButtonGetByName("down"), INPUT_ACTION_DOWN);
|
||||
inputBind(inputButtonGetByName("left"), INPUT_ACTION_LEFT);
|
||||
inputBind(inputButtonGetByName("right"), INPUT_ACTION_RIGHT);
|
||||
inputBind(inputButtonGetByName("w"), INPUT_ACTION_UP);
|
||||
inputBind(inputButtonGetByName("s"), INPUT_ACTION_DOWN);
|
||||
inputBind(inputButtonGetByName("a"), INPUT_ACTION_LEFT);
|
||||
inputBind(inputButtonGetByName("d"), INPUT_ACTION_RIGHT);
|
||||
inputBind(inputButtonGetByName("enter"), INPUT_ACTION_ACCEPT);
|
||||
inputBind(inputButtonGetByName("escape"), INPUT_ACTION_RAGEQUIT);
|
||||
inputBind(inputButtonGetByName("space"), INPUT_ACTION_ACCEPT);
|
||||
inputBind(inputButtonGetByName("backspace"), INPUT_ACTION_CANCEL);
|
||||
inputBind(inputButtonGetByName("e"), INPUT_ACTION_ACCEPT);
|
||||
inputBind(inputButtonGetByName("q"), INPUT_ACTION_CANCEL);
|
||||
#endif
|
||||
// #if INPUT_SDL2 == 1
|
||||
// #if INPUT_KEYBOARD == 1
|
||||
// inputBind(inputButtonGetByName("up"), INPUT_ACTION_UP);
|
||||
// inputBind(inputButtonGetByName("down"), INPUT_ACTION_DOWN);
|
||||
// inputBind(inputButtonGetByName("left"), INPUT_ACTION_LEFT);
|
||||
// inputBind(inputButtonGetByName("right"), INPUT_ACTION_RIGHT);
|
||||
// inputBind(inputButtonGetByName("w"), INPUT_ACTION_UP);
|
||||
// inputBind(inputButtonGetByName("s"), INPUT_ACTION_DOWN);
|
||||
// inputBind(inputButtonGetByName("a"), INPUT_ACTION_LEFT);
|
||||
// inputBind(inputButtonGetByName("d"), INPUT_ACTION_RIGHT);
|
||||
// inputBind(inputButtonGetByName("enter"), INPUT_ACTION_ACCEPT);
|
||||
// inputBind(inputButtonGetByName("escape"), INPUT_ACTION_RAGEQUIT);
|
||||
// inputBind(inputButtonGetByName("space"), INPUT_ACTION_ACCEPT);
|
||||
// inputBind(inputButtonGetByName("backspace"), INPUT_ACTION_CANCEL);
|
||||
// inputBind(inputButtonGetByName("e"), INPUT_ACTION_ACCEPT);
|
||||
// inputBind(inputButtonGetByName("q"), INPUT_ACTION_CANCEL);
|
||||
// #endif
|
||||
|
||||
#if INPUT_GAMEPAD == 1
|
||||
#if PSP
|
||||
INPUT.deadzone = 0.2890625f;// Taken from the PSP firmware
|
||||
// #if INPUT_GAMEPAD == 1
|
||||
// #if PSP
|
||||
// INPUT.deadzone = 0.2890625f;// Taken from the PSP firmware
|
||||
|
||||
inputBind(inputButtonGetByName("up"), INPUT_ACTION_UP);
|
||||
inputBind(inputButtonGetByName("down"), INPUT_ACTION_DOWN);
|
||||
inputBind(inputButtonGetByName("left"), INPUT_ACTION_LEFT);
|
||||
inputBind(inputButtonGetByName("right"), INPUT_ACTION_RIGHT);
|
||||
inputBind(inputButtonGetByName("circle"), INPUT_ACTION_CANCEL);
|
||||
inputBind(inputButtonGetByName("cross"), INPUT_ACTION_ACCEPT);
|
||||
inputBind(inputButtonGetByName("lstick_negative_y"), INPUT_ACTION_UP);
|
||||
inputBind(inputButtonGetByName("lstick_positive_y"), INPUT_ACTION_DOWN);
|
||||
inputBind(inputButtonGetByName("lstick_negative_x"), INPUT_ACTION_LEFT);
|
||||
inputBind(inputButtonGetByName("lstick_positive_x"), INPUT_ACTION_RIGHT);
|
||||
inputBind(inputButtonGetByName("select"), INPUT_ACTION_RAGEQUIT);
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
// inputBind(inputButtonGetByName("up"), INPUT_ACTION_UP);
|
||||
// inputBind(inputButtonGetByName("down"), INPUT_ACTION_DOWN);
|
||||
// inputBind(inputButtonGetByName("left"), INPUT_ACTION_LEFT);
|
||||
// inputBind(inputButtonGetByName("right"), INPUT_ACTION_RIGHT);
|
||||
// inputBind(inputButtonGetByName("circle"), INPUT_ACTION_CANCEL);
|
||||
// inputBind(inputButtonGetByName("cross"), INPUT_ACTION_ACCEPT);
|
||||
// inputBind(inputButtonGetByName("lstick_negative_y"), INPUT_ACTION_UP);
|
||||
// inputBind(inputButtonGetByName("lstick_positive_y"), INPUT_ACTION_DOWN);
|
||||
// inputBind(inputButtonGetByName("lstick_negative_x"), INPUT_ACTION_LEFT);
|
||||
// inputBind(inputButtonGetByName("lstick_positive_x"), INPUT_ACTION_RIGHT);
|
||||
// inputBind(inputButtonGetByName("select"), INPUT_ACTION_RAGEQUIT);
|
||||
// #endif
|
||||
// #endif
|
||||
// #endif
|
||||
}
|
||||
|
||||
void inputUpdate(void) {
|
||||
|
||||
@@ -9,13 +9,14 @@
|
||||
#include "assert/assert.h"
|
||||
#include "util/string.h"
|
||||
|
||||
// inputaction_t inputActionGetByName(const char_t *name) {
|
||||
// assertNotNull(name, "name must not be NULL");
|
||||
inputaction_t inputActionGetByName(const char_t *name) {
|
||||
assertNotNull(name, "name must not be NULL");
|
||||
|
||||
// for(inputaction_t i = 0; i < INPUT_ACTION_COUNT; i++) {
|
||||
// if(stringCompareInsensitive(INPUT_ACTION_NAMES[i], name) != 0) continue;
|
||||
// return i;
|
||||
// }
|
||||
for(inputaction_t i = 0; i < INPUT_ACTION_COUNT; i++) {
|
||||
if(INPUT_ACTION_NAMES[i] == NULL) continue;
|
||||
if(stringCompareInsensitive(INPUT_ACTION_NAMES[i], name) != 0) continue;
|
||||
return i;
|
||||
}
|
||||
|
||||
// return INPUT_ACTION_COUNT;
|
||||
// }
|
||||
return INPUT_ACTION_COUNT;
|
||||
}
|
||||
@@ -32,15 +32,15 @@ typedef struct {
|
||||
#endif
|
||||
} inputactiondata_t;
|
||||
|
||||
// static const char_t* INPUT_ACTION_NAMES[INPUT_ACTION_COUNT] = {
|
||||
// [INPUT_ACTION_UP] = "UP",
|
||||
// [INPUT_ACTION_DOWN] = "DOWN",
|
||||
// [INPUT_ACTION_LEFT] = "LEFT",
|
||||
// [INPUT_ACTION_RIGHT] = "RIGHT",
|
||||
// [INPUT_ACTION_ACCEPT] = "ACCEPT",
|
||||
// [INPUT_ACTION_CANCEL] = "CANCEL",
|
||||
// [INPUT_ACTION_RAGEQUIT] = "RAGEQUIT",
|
||||
// };
|
||||
static const char_t* INPUT_ACTION_NAMES[INPUT_ACTION_COUNT] = {
|
||||
[INPUT_ACTION_UP] = "UP",
|
||||
[INPUT_ACTION_DOWN] = "DOWN",
|
||||
[INPUT_ACTION_LEFT] = "LEFT",
|
||||
[INPUT_ACTION_RIGHT] = "RIGHT",
|
||||
[INPUT_ACTION_ACCEPT] = "ACCEPT",
|
||||
[INPUT_ACTION_CANCEL] = "CANCEL",
|
||||
[INPUT_ACTION_RAGEQUIT] = "RAGEQUIT",
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets an input action by its name.
|
||||
@@ -48,4 +48,4 @@ typedef struct {
|
||||
* @param name The name of the input action.
|
||||
* @return The input action, or INPUT_ACTION_COUNT if not found.
|
||||
*/
|
||||
// inputaction_t inputActionGetByName(const char_t *name);
|
||||
inputaction_t inputActionGetByName(const char_t *name);
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "input/input.h"
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
// Main applet
|
||||
errorret_t ret;
|
||||
|
||||
// Init engine
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
#include "cutscenewait.h"
|
||||
#include "cutscenecallback.h"
|
||||
#include "cutscenetext.h"
|
||||
#include "cutscenecutscene.h"
|
||||
|
||||
typedef struct cutscene_s cutscene_t;
|
||||
|
||||
typedef enum {
|
||||
CUTSCENE_ITEM_TYPE_NULL,
|
||||
@@ -27,7 +28,7 @@ typedef struct cutsceneitem_s {
|
||||
cutscenetext_t text;
|
||||
cutscenecallback_t callback;
|
||||
cutscenewait_t wait;
|
||||
const cutscenecutscene_t cutscene;
|
||||
const cutscene_t *cutscene;
|
||||
};
|
||||
} cutsceneitem_t;
|
||||
|
||||
|
||||
@@ -81,30 +81,74 @@ void entityWalk(entity_t *entity, const entitydir_t direction) {
|
||||
bool_t fall = false;
|
||||
bool_t raise = false;
|
||||
|
||||
// Are we walking up stairs?
|
||||
// Are we walking up a ramp?
|
||||
if(
|
||||
tileIsStairs(tileCurrent) &&
|
||||
(direction+TILE_STAIRS_SOUTH) == tileCurrent &&
|
||||
newPos.z < (MAP_CHUNK_DEPTH - 1)
|
||||
tileIsRamp(tileCurrent) &&
|
||||
(
|
||||
// Can only walk UP the direction the ramp faces.
|
||||
(direction+TILE_SHAPE_RAMP_SOUTH) == tileCurrent ||
|
||||
// If diagonal ramp, can go up one of two ways only.
|
||||
(
|
||||
(
|
||||
tileCurrent == TILE_SHAPE_RAMP_SOUTHEAST &&
|
||||
(direction == ENTITY_DIR_SOUTH || direction == ENTITY_DIR_EAST)
|
||||
) ||
|
||||
(
|
||||
tileCurrent == TILE_SHAPE_RAMP_SOUTHWEST &&
|
||||
(direction == ENTITY_DIR_SOUTH || direction == ENTITY_DIR_WEST)
|
||||
) ||
|
||||
(
|
||||
tileCurrent == TILE_SHAPE_RAMP_NORTHEAST &&
|
||||
(direction == ENTITY_DIR_NORTH || direction == ENTITY_DIR_EAST)
|
||||
) ||
|
||||
(
|
||||
tileCurrent == TILE_SHAPE_RAMP_NORTHWEST &&
|
||||
(direction == ENTITY_DIR_NORTH || direction == ENTITY_DIR_WEST)
|
||||
)
|
||||
)
|
||||
// Must be able to walk up.
|
||||
)
|
||||
) {
|
||||
tileNew = TILE_NULL;// Force check for stairs above.
|
||||
tileNew = TILE_SHAPE_NULL;// Force check for ramp above.
|
||||
worldpos_t abovePos = newPos;
|
||||
abovePos.z += 1;
|
||||
tile_t tileAbove = mapGetTile(abovePos);
|
||||
|
||||
if(tileAbove != TILE_NULL && tileIsWalkable(tileAbove)) {
|
||||
// We can go up the stairs.
|
||||
if(tileAbove != TILE_SHAPE_NULL && tileIsWalkable(tileAbove)) {
|
||||
// We can go up the ramp.
|
||||
raise = true;
|
||||
}
|
||||
} else if(tileNew == TILE_NULL && newPos.z > 0) {
|
||||
} else if(tileNew == TILE_SHAPE_NULL && newPos.z > 0) {
|
||||
// Falling down?
|
||||
worldpos_t belowPos = newPos;
|
||||
belowPos.z -= 1;
|
||||
tile_t tileBelow = mapGetTile(belowPos);
|
||||
if(
|
||||
tileBelow != TILE_NULL &&
|
||||
tileIsStairs(tileBelow) &&
|
||||
(entityDirGetOpposite(direction)+TILE_STAIRS_SOUTH) == tileBelow
|
||||
tileBelow != TILE_SHAPE_NULL &&
|
||||
tileIsRamp(tileBelow) &&
|
||||
(
|
||||
// This handles regular cardinal ramps
|
||||
(entityDirGetOpposite(direction)+TILE_SHAPE_RAMP_SOUTH) == tileBelow ||
|
||||
// This handles diagonal ramps
|
||||
(
|
||||
(
|
||||
tileBelow == TILE_SHAPE_RAMP_SOUTHEAST &&
|
||||
(direction == ENTITY_DIR_NORTH || direction == ENTITY_DIR_WEST)
|
||||
) ||
|
||||
(
|
||||
tileBelow == TILE_SHAPE_RAMP_SOUTHWEST &&
|
||||
(direction == ENTITY_DIR_NORTH || direction == ENTITY_DIR_EAST)
|
||||
) ||
|
||||
(
|
||||
tileBelow == TILE_SHAPE_RAMP_NORTHEAST &&
|
||||
(direction == ENTITY_DIR_SOUTH || direction == ENTITY_DIR_WEST)
|
||||
) ||
|
||||
(
|
||||
tileBelow == TILE_SHAPE_RAMP_NORTHWEST &&
|
||||
(direction == ENTITY_DIR_SOUTH || direction == ENTITY_DIR_EAST)
|
||||
)
|
||||
)
|
||||
)
|
||||
) {
|
||||
// We will fall to this tile.
|
||||
fall = true;
|
||||
@@ -145,3 +189,12 @@ entity_t * entityGetAt(const worldpos_t position) {
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint8_t entityGetAvailable() {
|
||||
entity_t *ent = ENTITIES;
|
||||
do {
|
||||
if(ent->type == ENTITY_TYPE_NULL) return ent - ENTITIES;
|
||||
} while(++ent, ent < &ENTITIES[ENTITY_COUNT]);
|
||||
|
||||
return 0xFF;
|
||||
}
|
||||
@@ -11,14 +11,12 @@
|
||||
#include "entitytype.h"
|
||||
#include "npc.h"
|
||||
|
||||
#define ENTITY_COUNT 256
|
||||
|
||||
typedef struct map_s map_t;
|
||||
|
||||
typedef struct entity_s {
|
||||
uint8_t id;
|
||||
entitytype_t type;
|
||||
entitytypedata_t;
|
||||
entitytypedata_t data;
|
||||
|
||||
// Movement
|
||||
entitydir_t direction;
|
||||
@@ -70,3 +68,10 @@ void entityWalk(entity_t *entity, const entitydir_t direction);
|
||||
* @return Pointer to the entity at the position, or NULL if none.
|
||||
*/
|
||||
entity_t *entityGetAt(const worldpos_t pos);
|
||||
|
||||
/**
|
||||
* Gets an available entity index.
|
||||
*
|
||||
* @return The index of an available entity, or 0xFF if none are available.
|
||||
*/
|
||||
uint8_t entityGetAvailable();
|
||||
@@ -9,11 +9,6 @@
|
||||
#include "rpg/world/worldpos.h"
|
||||
|
||||
typedef enum {
|
||||
ENTITY_DIR_SOUTH = 0,
|
||||
ENTITY_DIR_EAST = 1,
|
||||
ENTITY_DIR_WEST = 2,
|
||||
ENTITY_DIR_NORTH = 3,
|
||||
|
||||
ENTITY_DIR_UP = ENTITY_DIR_NORTH,
|
||||
ENTITY_DIR_DOWN = ENTITY_DIR_SOUTH,
|
||||
ENTITY_DIR_LEFT = ENTITY_DIR_WEST,
|
||||
|
||||
@@ -6,17 +6,11 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "duskdefs.h"
|
||||
#include "rpg/entity/player.h"
|
||||
#include "npc.h"
|
||||
|
||||
typedef enum {
|
||||
ENTITY_TYPE_NULL = 0,
|
||||
|
||||
ENTITY_TYPE_PLAYER,
|
||||
ENTITY_TYPE_NPC,
|
||||
|
||||
ENTITY_TYPE_COUNT
|
||||
} entitytype_t;
|
||||
typedef uint8_t entitytype_t;
|
||||
|
||||
typedef union {
|
||||
player_t player;
|
||||
|
||||
@@ -8,6 +8,6 @@
|
||||
#pragma once
|
||||
#include "dusk.h"
|
||||
|
||||
typedef struct cutscene_s cutscene_t;
|
||||
|
||||
typedef cutscene_t* cutscenecutscene_t;
|
||||
typedef struct {
|
||||
void *nothing;
|
||||
} inventory_t;
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "rpgcamera.h"
|
||||
#include "rpgtextbox.h"
|
||||
#include "util/memory.h"
|
||||
#include "assert/assert.h"
|
||||
|
||||
errorret_t rpgInit(void) {
|
||||
memoryZero(ENTITIES, sizeof(ENTITIES));
|
||||
@@ -26,16 +27,13 @@ errorret_t rpgInit(void) {
|
||||
rpgTextboxInit();
|
||||
|
||||
// TEST: Create some entities.
|
||||
entity_t *ent;
|
||||
ent = &ENTITIES[0];
|
||||
entityInit(ent, ENTITY_TYPE_PLAYER);
|
||||
RPG_CAMERA.mode = RPG_CAMERA_MODE_FOLLOW_ENTITY;
|
||||
RPG_CAMERA.followEntity.followEntityId = ent->id;
|
||||
ent->position.x = 2, ent->position.y = 2;
|
||||
|
||||
ent = &ENTITIES[1];
|
||||
entityInit(ent, ENTITY_TYPE_NPC);
|
||||
ent->position.x = 4, ent->position.y = 0, ent->position.z = 1;
|
||||
// uint8_t entIndex = entityGetAvailable();
|
||||
// assertTrue(entIndex != 0xFF, "No available entity slots!.");
|
||||
// entity_t *ent = &ENTITIES[entIndex];
|
||||
// entityInit(ent, ENTITY_TYPE_PLAYER);
|
||||
// RPG_CAMERA.mode = RPG_CAMERA_MODE_FOLLOW_ENTITY;
|
||||
// RPG_CAMERA.followEntity.followEntityId = ent->id;
|
||||
// ent->position.x = 2, ent->position.y = 2;
|
||||
|
||||
// All Good!
|
||||
errorOk();
|
||||
|
||||
@@ -10,11 +10,6 @@
|
||||
#include "worldpos.h"
|
||||
#include "display/mesh/quad.h"
|
||||
|
||||
#define CHUNK_VERTEX_COUNT_MAX ( \
|
||||
QUAD_VERTEX_COUNT * CHUNK_WIDTH * CHUNK_HEIGHT * 4 \
|
||||
)
|
||||
#define CHUNK_MESH_COUNT_MAX 14
|
||||
|
||||
typedef struct chunk_s {
|
||||
chunkpos_t position;
|
||||
tile_t tiles[CHUNK_TILE_COUNT];
|
||||
@@ -22,6 +17,7 @@ typedef struct chunk_s {
|
||||
uint8_t meshCount;
|
||||
meshvertex_t vertices[CHUNK_VERTEX_COUNT_MAX];
|
||||
mesh_t meshes[CHUNK_MESH_COUNT_MAX];
|
||||
uint8_t entities[CHUNK_ENTITY_COUNT_MAX];
|
||||
} chunk_t;
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,14 +9,14 @@
|
||||
#include "util/memory.h"
|
||||
#include "assert/assert.h"
|
||||
#include "asset/asset.h"
|
||||
#include "rpg/entity/entity.h"
|
||||
|
||||
map_t MAP;
|
||||
|
||||
errorret_t mapInit() {
|
||||
memoryZero(&MAP, sizeof(map_t));
|
||||
|
||||
// Init the default chunks. In future I'll probably make this based on where
|
||||
// the player spawns in to save an initial mapSet.
|
||||
// Init the first chunks.
|
||||
chunkindex_t index = 0;
|
||||
for(chunkunit_t z = 0; z < MAP_CHUNK_DEPTH; z++) {
|
||||
for(chunkunit_t y = 0; y < MAP_CHUNK_HEIGHT; y++) {
|
||||
@@ -126,7 +126,13 @@ void mapDispose() {
|
||||
}
|
||||
|
||||
void mapChunkUnload(chunk_t* chunk) {
|
||||
for(uint8_t i = 0; i < CHUNK_ENTITY_COUNT_MAX; i++) {
|
||||
if(chunk->entities[i] == 0xFF) break;
|
||||
entity_t *entity = &ENTITIES[chunk->entities[i]];
|
||||
entity->type = ENTITY_TYPE_NULL;
|
||||
}
|
||||
for(uint8_t i = 0; i < chunk->meshCount; i++) {
|
||||
if(chunk->meshes[i].vertexCount == 0) continue;
|
||||
meshDispose(&chunk->meshes[i]);
|
||||
}
|
||||
}
|
||||
@@ -135,6 +141,8 @@ errorret_t mapChunkLoad(chunk_t* chunk) {
|
||||
char_t buffer[64];
|
||||
|
||||
chunk->meshCount = 0;
|
||||
memoryZero(chunk->meshes, sizeof(chunk->meshes));
|
||||
memorySet(chunk->entities, 0xFF, sizeof(chunk->entities));
|
||||
|
||||
snprintf(buffer, sizeof(buffer), "map/map/%d_%d_%d.dcf",
|
||||
chunk->position.x,
|
||||
@@ -179,10 +187,10 @@ tile_t mapGetTile(const worldpos_t position) {
|
||||
chunkpos_t chunkPos;
|
||||
worldPosToChunkPos(&position, &chunkPos);
|
||||
chunkindex_t chunkIndex = mapGetChunkIndexAt(chunkPos);
|
||||
if(chunkIndex == -1) return TILE_NULL;
|
||||
if(chunkIndex == -1) return TILE_SHAPE_NULL;
|
||||
|
||||
chunk_t *chunk = mapGetChunk(chunkIndex);
|
||||
assertNotNull(chunk, "Chunk pointer cannot be NULL");
|
||||
chunktileindex_t tileIndex = woprldPosToChunkTileIndex(&position);
|
||||
chunktileindex_t tileIndex = worldPosToChunkTileIndex(&position);
|
||||
return chunk->tiles[tileIndex];
|
||||
}
|
||||
@@ -9,24 +9,24 @@
|
||||
|
||||
bool_t tileIsWalkable(const tile_t tile) {
|
||||
switch(tile) {
|
||||
case TILE_WALKABLE:
|
||||
case TILE_STAIRS_NORTH:
|
||||
case TILE_STAIRS_SOUTH:
|
||||
case TILE_STAIRS_EAST:
|
||||
case TILE_STAIRS_WEST:
|
||||
return true;
|
||||
case TILE_SHAPE_NULL:
|
||||
return false;
|
||||
|
||||
default:
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool_t tileIsStairs(const tile_t tile) {
|
||||
bool_t tileIsRamp(const tile_t tile) {
|
||||
switch(tile) {
|
||||
case TILE_STAIRS_NORTH:
|
||||
case TILE_STAIRS_SOUTH:
|
||||
case TILE_STAIRS_EAST:
|
||||
case TILE_STAIRS_WEST:
|
||||
case TILE_SHAPE_RAMP_NORTH:
|
||||
case TILE_SHAPE_RAMP_SOUTH:
|
||||
case TILE_SHAPE_RAMP_EAST:
|
||||
case TILE_SHAPE_RAMP_WEST:
|
||||
case TILE_SHAPE_RAMP_NORTHEAST:
|
||||
case TILE_SHAPE_RAMP_NORTHWEST:
|
||||
case TILE_SHAPE_RAMP_SOUTHEAST:
|
||||
case TILE_SHAPE_RAMP_SOUTHWEST:
|
||||
return true;
|
||||
|
||||
default:
|
||||
|
||||
@@ -10,15 +10,6 @@
|
||||
|
||||
typedef uint8_t tile_t;
|
||||
|
||||
#define TILE_NULL 0
|
||||
#define TILE_WALKABLE 1
|
||||
#define TILE_STAIRS_SOUTH (2 + ENTITY_DIR_SOUTH)
|
||||
#define TILE_STAIRS_EAST (2 + ENTITY_DIR_EAST)
|
||||
#define TILE_STAIRS_WEST (2 + ENTITY_DIR_WEST)
|
||||
#define TILE_STAIRS_NORTH (2 + ENTITY_DIR_NORTH)
|
||||
#define TILE_TEST 6
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether or not the given tile is walkable.
|
||||
*
|
||||
@@ -28,9 +19,9 @@ typedef uint8_t tile_t;
|
||||
bool_t tileIsWalkable(const tile_t tile);
|
||||
|
||||
/**
|
||||
* Returns whether or not the given tile is a stairs tile.
|
||||
* Returns whether or not the given tile is a ramp tile.
|
||||
*
|
||||
* @param tile The tile to check.
|
||||
* @return bool_t True if stairs, false if not.
|
||||
* @return bool_t True if ramp, false if not.
|
||||
*/
|
||||
bool_t tileIsStairs(const tile_t tile);
|
||||
bool_t tileIsRamp(const tile_t tile);
|
||||
@@ -6,39 +6,92 @@
|
||||
*/
|
||||
|
||||
#include "worldpos.h"
|
||||
#include "assert/assert.h"
|
||||
|
||||
bool_t worldPosIsEqual(const worldpos_t a, const worldpos_t b) {
|
||||
return a.x == b.x && a.y == b.y && a.z == b.z;
|
||||
}
|
||||
|
||||
void chunkPosToWorldPos(const chunkpos_t* chunkPos, worldpos_t* out) {
|
||||
assertNotNull(chunkPos, "Chunk position pointer cannot be NULL");
|
||||
assertNotNull(out, "Output world position pointer cannot be NULL");
|
||||
|
||||
out->x = (worldunit_t)(chunkPos->x * CHUNK_WIDTH);
|
||||
out->y = (worldunit_t)(chunkPos->y * CHUNK_HEIGHT);
|
||||
out->z = (worldunit_t)(chunkPos->z * CHUNK_DEPTH);
|
||||
}
|
||||
|
||||
void worldPosToChunkPos(const worldpos_t* worldPos, chunkpos_t* out) {
|
||||
assertNotNull(worldPos, "World position pointer cannot be NULL");
|
||||
assertNotNull(out, "Output chunk position pointer cannot be NULL");
|
||||
|
||||
if(worldPos->x < 0) {
|
||||
out->x = (chunkunit_t)((worldPos->x - (CHUNK_WIDTH - 1)) / CHUNK_WIDTH);
|
||||
} else {
|
||||
out->x = (chunkunit_t)(worldPos->x / CHUNK_WIDTH);
|
||||
out->y = (chunkunit_t)(worldPos->y / CHUNK_HEIGHT);
|
||||
out->z = (chunkunit_t)(worldPos->z / CHUNK_DEPTH);
|
||||
}
|
||||
|
||||
chunktileindex_t woprldPosToChunkTileIndex(const worldpos_t* worldPos) {
|
||||
uint8_t localX = (uint8_t)(worldPos->x % CHUNK_WIDTH);
|
||||
uint8_t localY = (uint8_t)(worldPos->y % CHUNK_HEIGHT);
|
||||
uint8_t localZ = (uint8_t)(worldPos->z % CHUNK_DEPTH);
|
||||
if(worldPos->y < 0) {
|
||||
out->y = (chunkunit_t)((worldPos->y - (CHUNK_HEIGHT - 1)) / CHUNK_HEIGHT);
|
||||
} else {
|
||||
out->y = (chunkunit_t)(worldPos->y / CHUNK_HEIGHT);
|
||||
}
|
||||
|
||||
return (chunktileindex_t)(
|
||||
if(worldPos->z < 0) {
|
||||
out->z = (chunkunit_t)((worldPos->z - (CHUNK_DEPTH - 1)) / CHUNK_DEPTH);
|
||||
} else {
|
||||
out->z = (chunkunit_t)(worldPos->z / CHUNK_DEPTH);
|
||||
}
|
||||
}
|
||||
|
||||
chunktileindex_t worldPosToChunkTileIndex(const worldpos_t* worldPos) {
|
||||
assertNotNull(worldPos, "World position pointer cannot be NULL");
|
||||
|
||||
uint8_t localX, localY, localZ;
|
||||
if(worldPos->x < 0) {
|
||||
localX = (uint8_t)(
|
||||
(CHUNK_WIDTH - 1) - ((-worldPos->x - 1) % CHUNK_WIDTH)
|
||||
);
|
||||
} else {
|
||||
localX = (uint8_t)(worldPos->x % CHUNK_WIDTH);
|
||||
}
|
||||
|
||||
if(worldPos->y < 0) {
|
||||
localY = (uint8_t)(
|
||||
(CHUNK_HEIGHT - 1) - ((-worldPos->y - 1) % CHUNK_HEIGHT)
|
||||
);
|
||||
} else {
|
||||
localY = (uint8_t)(worldPos->y % CHUNK_HEIGHT);
|
||||
}
|
||||
|
||||
if(worldPos->z < 0) {
|
||||
localZ = (uint8_t)(
|
||||
(CHUNK_DEPTH - 1) - ((-worldPos->z - 1) % CHUNK_DEPTH)
|
||||
);
|
||||
} else {
|
||||
localZ = (uint8_t)(worldPos->z % CHUNK_DEPTH);
|
||||
}
|
||||
|
||||
chunktileindex_t chunkTileIndex = (chunktileindex_t)(
|
||||
(localZ * CHUNK_WIDTH * CHUNK_HEIGHT) +
|
||||
(localY * CHUNK_WIDTH) +
|
||||
localX
|
||||
);
|
||||
assertTrue(
|
||||
chunkTileIndex < CHUNK_TILE_COUNT,
|
||||
"Calculated chunk tile index is out of bounds"
|
||||
);
|
||||
return chunkTileIndex;
|
||||
}
|
||||
|
||||
chunkindex_t chunkPosToIndex(const chunkpos_t* pos) {
|
||||
return (chunkindex_t)(
|
||||
assertNotNull(pos, "Chunk position pointer cannot be NULL");
|
||||
|
||||
chunkindex_t chunkIndex = (chunkindex_t)(
|
||||
(pos->z * MAP_CHUNK_WIDTH * MAP_CHUNK_HEIGHT) +
|
||||
(pos->y * MAP_CHUNK_WIDTH) +
|
||||
pos->x
|
||||
);
|
||||
|
||||
return chunkIndex;
|
||||
}
|
||||
@@ -7,10 +7,8 @@
|
||||
|
||||
#pragma once
|
||||
#include "dusk.h"
|
||||
#include "duskdefs.h"
|
||||
|
||||
#define CHUNK_WIDTH 16
|
||||
#define CHUNK_HEIGHT 16
|
||||
#define CHUNK_DEPTH 4
|
||||
#define CHUNK_TILE_COUNT (CHUNK_WIDTH * CHUNK_HEIGHT * CHUNK_DEPTH)
|
||||
|
||||
#define MAP_CHUNK_WIDTH 3
|
||||
@@ -66,7 +64,7 @@ void worldPosToChunkPos(const worldpos_t* worldPos, chunkpos_t* out);
|
||||
* @param worldPos The world position.
|
||||
* @return The tile index within the chunk.
|
||||
*/
|
||||
chunktileindex_t woprldPosToChunkTileIndex(const worldpos_t* worldPos);
|
||||
chunktileindex_t worldPosToChunkTileIndex(const worldpos_t* worldPos);
|
||||
|
||||
/**
|
||||
* Converts a chunk position to a world position.
|
||||
|
||||
@@ -15,10 +15,7 @@
|
||||
#include "display/screen.h"
|
||||
#include "rpg/rpgcamera.h"
|
||||
#include "util/memory.h"
|
||||
|
||||
#define TILE_WIDTH 16.0f
|
||||
#define TILE_HEIGHT 16.0f
|
||||
#define TILE_DEPTH 11.36f
|
||||
#include "duskdefs.h"
|
||||
|
||||
errorret_t sceneMapInit(scenedata_t *data) {
|
||||
// Init the camera.
|
||||
@@ -26,7 +23,7 @@ errorret_t sceneMapInit(scenedata_t *data) {
|
||||
data->sceneMap.camera.projType = CAMERA_PROJECTION_TYPE_PERSPECTIVE_FLIPPED;
|
||||
data->sceneMap.camera.viewType = CAMERA_VIEW_TYPE_LOOKAT_PIXEL_PERFECT;
|
||||
glm_vec3_zero(data->sceneMap.camera.lookatPixelPerfect.offset);
|
||||
data->sceneMap.camera.lookatPixelPerfect.offset[1] = TILE_HEIGHT;
|
||||
data->sceneMap.camera.lookatPixelPerfect.offset[1] = RPG_CAMERA_Z_OFFSET;
|
||||
glm_vec3_copy(
|
||||
(vec3){ 0.0f, 0.0f, 0.0f },
|
||||
data->sceneMap.camera.lookatPixelPerfect.target
|
||||
@@ -35,8 +32,10 @@ errorret_t sceneMapInit(scenedata_t *data) {
|
||||
(vec3){ 0.0f, 1.0f, 0.0f },
|
||||
data->sceneMap.camera.lookatPixelPerfect.up
|
||||
);
|
||||
data->sceneMap.camera.lookatPixelPerfect.pixelsPerUnit = 1.0f;
|
||||
data->sceneMap.camera.perspective.fov = glm_rad(90.0f);
|
||||
data->sceneMap.camera.lookatPixelPerfect.pixelsPerUnit = (
|
||||
RPG_CAMERA_PIXELS_PER_UNIT
|
||||
);
|
||||
data->sceneMap.camera.perspective.fov = glm_rad(RPG_CAMERA_FOV);
|
||||
|
||||
errorOk();
|
||||
}
|
||||
@@ -54,7 +53,7 @@ void sceneMapGetWorldPosition(const worldpos_t pos, vec3 outPosition) {
|
||||
|
||||
// Handle stair tiles.
|
||||
tile_t tile = mapGetTile(pos);
|
||||
if(tileIsStairs(tile)) {
|
||||
if(tileIsRamp(tile)) {
|
||||
outPosition[2] += TILE_DEPTH / 2.0f;
|
||||
}
|
||||
}
|
||||
@@ -66,6 +65,9 @@ void sceneMapEntityGetPosition(const entity_t *entity, vec3 outPosition) {
|
||||
// Get position
|
||||
sceneMapGetWorldPosition(entity->position, outPosition);
|
||||
|
||||
// Add a small offset so we render above the tile
|
||||
outPosition[2] += 0.1f;
|
||||
|
||||
// Add animation offset(s)
|
||||
switch(entity->animation) {
|
||||
case ENTITY_ANIM_WALK: {
|
||||
@@ -170,6 +172,7 @@ void sceneMapRenderMap() {
|
||||
|
||||
for(uint8_t j = 0; j < chunk->meshCount; j++) {
|
||||
mesh_t *mesh = &chunk->meshes[j];
|
||||
if(mesh->vertexCount == 0) continue;
|
||||
textureBind(NULL);
|
||||
meshDraw(mesh, -1, -1);
|
||||
}
|
||||
|
||||
@@ -19,12 +19,6 @@ errorret_t sceneManagerInit(void) {
|
||||
sceneManagerRegisterScene(&SCENE_TEST);
|
||||
sceneManagerRegisterScene(&SCENE_MAP);
|
||||
|
||||
// Initial scene
|
||||
scene_t *initial = sceneManagerGetSceneByName("map");
|
||||
sceneManagerSetScene(initial);
|
||||
if(initial->init) errorChain(initial->init(&SCENE_MANAGER.sceneData));
|
||||
initial->flags |= SCENE_FLAG_INITIALIZED;
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
@@ -53,14 +47,11 @@ void sceneManagerRegisterScene(scene_t *scene) {
|
||||
SCENE_MANAGER.scenes[SCENE_MANAGER.sceneCount++] = scene;
|
||||
}
|
||||
|
||||
void sceneManagerSetScene(scene_t *scene) {
|
||||
if(SCENE_MANAGER.current) {
|
||||
// TODO: Should dispose?
|
||||
assertTrue(
|
||||
SCENE_MANAGER.current->flags & SCENE_FLAG_INITIALIZED,
|
||||
"Current scene not initialized"
|
||||
);
|
||||
|
||||
errorret_t sceneManagerSetScene(scene_t *scene) {
|
||||
if(
|
||||
SCENE_MANAGER.current &&
|
||||
(SCENE_MANAGER.current->flags & SCENE_FLAG_INITIALIZED) != 0
|
||||
) {
|
||||
SCENE_MANAGER.current->flags &= ~SCENE_FLAG_INITIALIZED;
|
||||
if(SCENE_MANAGER.current->dispose) {
|
||||
SCENE_MANAGER.current->dispose(&SCENE_MANAGER.sceneData);
|
||||
@@ -69,12 +60,12 @@ void sceneManagerSetScene(scene_t *scene) {
|
||||
|
||||
SCENE_MANAGER.current = scene;
|
||||
|
||||
if(scene) {
|
||||
assertTrue(
|
||||
(scene->flags & SCENE_FLAG_INITIALIZED) == 0,
|
||||
"Scene should not yet be initialized"
|
||||
);
|
||||
if(scene && scene->init) {
|
||||
scene->flags |= SCENE_FLAG_INITIALIZED;
|
||||
errorChain(scene->init(&SCENE_MANAGER.sceneData));
|
||||
}
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
void sceneManagerUpdate(void) {
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include "scene.h"
|
||||
#include "scenedata.h"
|
||||
|
||||
#define SCENE_MANAGER_SCENE_COUNT_MAX 32
|
||||
#define SCENE_MANAGER_SCENE_COUNT_MAX 16
|
||||
|
||||
typedef struct {
|
||||
scene_t *current;
|
||||
@@ -44,8 +44,9 @@ void sceneManagerRegisterScene(scene_t *scene);
|
||||
* Sets the current active scene.
|
||||
*
|
||||
* @param scene The scene to set as current.
|
||||
* @return An error code indicating success or failure.
|
||||
*/
|
||||
void sceneManagerSetScene(scene_t *scene);
|
||||
errorret_t sceneManagerSetScene(scene_t *scene);
|
||||
|
||||
/**
|
||||
* Updates all active scenes.
|
||||
|
||||
12
src/script/CMakeLists.txt
Normal file
12
src/script/CMakeLists.txt
Normal file
@@ -0,0 +1,12 @@
|
||||
# Copyright (c) 2025 Dominic Masters
|
||||
#
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
# Sources
|
||||
target_sources(${DUSK_TARGET_NAME}
|
||||
PRIVATE
|
||||
scriptmanager.c
|
||||
scriptcontext.c
|
||||
scriptmodule.c
|
||||
)
|
||||
65
src/script/module/moduleinput.h
Normal file
65
src/script/module/moduleinput.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "script/scriptcontext.h"
|
||||
#include "input/input.h"
|
||||
|
||||
int32_t moduleInputBind(lua_State *L) {
|
||||
assertNotNull(L, "Lua state cannot be NULL");
|
||||
|
||||
// Requires action and button.
|
||||
if(!lua_isstring(L, 1)) {
|
||||
luaL_error(L, "inputBind: Expected button name as first argument");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(!lua_isstring(L, 2)) {
|
||||
luaL_error(L, "inputBind: Expected action name as second argument");
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char_t *strBtn = lua_tostring(L, 1);
|
||||
const char_t *strAct = lua_tostring(L, 2);
|
||||
|
||||
// Get button by name
|
||||
inputbutton_t btn = inputButtonGetByName(strBtn);
|
||||
if(btn.type == INPUT_BUTTON_TYPE_NONE) {
|
||||
printf("inputBind: Unknown button name '%s'\n", strBtn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Get action by name
|
||||
inputaction_t act = inputActionGetByName(strAct);
|
||||
if(act == INPUT_ACTION_COUNT) {
|
||||
printf("inputBind: Unknown action name '%s'\n", strAct);
|
||||
return 0;
|
||||
}
|
||||
|
||||
inputBind(btn, act);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void moduleInput(scriptcontext_t *context) {
|
||||
assertNotNull(context, "Script context cannot be NULL");
|
||||
|
||||
// Input values.
|
||||
scriptContextExec(context,
|
||||
#if INPUT_KEYBOARD == 1
|
||||
"INPUT_KEYBOARD = true\n"
|
||||
#endif
|
||||
#if INPUT_GAMEPAD == 1
|
||||
"INPUT_GAMEPAD = true\n"
|
||||
#endif
|
||||
#if INPUT_SDL2 == 1
|
||||
"INPUT_SDL2 = true\n"
|
||||
#endif
|
||||
);
|
||||
|
||||
// Bind methods
|
||||
scriptContextRegFunc(context, "inputBind", moduleInputBind);
|
||||
}
|
||||
21
src/script/module/moduleplatform.h
Normal file
21
src/script/module/moduleplatform.h
Normal file
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "script/scriptcontext.h"
|
||||
|
||||
#ifndef DUSK_TARGET_SYSTEM
|
||||
#error "DUSK_TARGET_SYSTEM must be defined"
|
||||
#endif
|
||||
|
||||
#define PLATFORM_VALUE "PLATFORM = '" DUSK_TARGET_SYSTEM "'"
|
||||
|
||||
void modulePlatform(scriptcontext_t *ctx) {
|
||||
assertNotNull(ctx, "Script context cannot be NULL");
|
||||
|
||||
scriptContextExec(ctx, PLATFORM_VALUE);
|
||||
}
|
||||
42
src/script/module/modulescene.h
Normal file
42
src/script/module/modulescene.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "script/scriptcontext.h"
|
||||
#include "scene/scenemanager.h"
|
||||
|
||||
int32_t moduleSceneSetScene(lua_State *L) {
|
||||
assertNotNull(L, "Lua state cannot be NULL");
|
||||
|
||||
assertTrue(lua_isstring(L, 1), "Expected string scene name");
|
||||
|
||||
const char *sceneName = luaL_checkstring(L, 1);
|
||||
if(sceneName == NULL || sceneName[0] == '\0') {
|
||||
luaL_error(L, "Scene name cannot be NULL");
|
||||
return 0;
|
||||
}
|
||||
|
||||
scene_t *scene = sceneManagerGetSceneByName(sceneName);
|
||||
if(scene == NULL) {
|
||||
luaL_error(L, "Scene '%s' not found", sceneName);
|
||||
return 0;
|
||||
}
|
||||
|
||||
errorret_t err = sceneManagerSetScene(scene);
|
||||
if(err.code != ERROR_OK) {
|
||||
|
||||
luaL_error(L, "Failed to set scene '%s'", sceneName);
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void moduleScene(scriptcontext_t *ctx) {
|
||||
assertNotNull(ctx, "Script context cannot be NULL");
|
||||
|
||||
scriptContextRegFunc(ctx, "sceneSet", moduleSceneSetScene);
|
||||
}
|
||||
122
src/script/module/modulesystem.h
Normal file
122
src/script/module/modulesystem.h
Normal file
@@ -0,0 +1,122 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "script/scriptcontext.h"
|
||||
#include "debug/debug.h"
|
||||
#include "assert/assert.h"
|
||||
#include "util/string.h"
|
||||
#include "script/scriptmodule.h"
|
||||
|
||||
int32_t moduleSysPrint(lua_State *L) {
|
||||
assertNotNull(L, "Lua state cannot be NULL");
|
||||
|
||||
int n = lua_gettop(L);
|
||||
luaL_Buffer b;
|
||||
luaL_buffinit(L, &b);
|
||||
|
||||
for (int i = 1; i <= n; ++i) {
|
||||
size_t len;
|
||||
const char *s = luaL_tolstring(L, i, &len); // converts any value to string
|
||||
luaL_addlstring(&b, s, len);
|
||||
lua_pop(L, 1); // pop result of luaL_tolstring
|
||||
if (i < n) luaL_addlstring(&b, "\t", 1);
|
||||
}
|
||||
|
||||
luaL_pushresult(&b);
|
||||
const char *msg = lua_tostring(L, -1);
|
||||
debugPrint("%s\n", msg);
|
||||
return 0; // no values returned to Lua
|
||||
}
|
||||
|
||||
int32_t moduleSysInclude(lua_State *L) {
|
||||
assertNotNull(L, "Lua state cannot be NULL");
|
||||
|
||||
if(!lua_isstring(L, 1)) {
|
||||
luaL_error(L, "Expected string filename");
|
||||
return 0;
|
||||
}
|
||||
|
||||
scriptcontext_t* ctx = *(scriptcontext_t**)lua_getextraspace(L);
|
||||
if(ctx == NULL) {
|
||||
luaL_error(L, "Script context is NULL");
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char_t *filename = luaL_checkstring(L, 1);
|
||||
if(filename == NULL || filename[0] == '\0') {
|
||||
luaL_error(L, "Filename cannot be NULL");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Copy out filename to mutable buffer
|
||||
char_t buffer[1024];
|
||||
stringCopy(buffer, filename, 1024);
|
||||
|
||||
// Ensure it has .dsf extension
|
||||
size_t len = strlen(buffer);
|
||||
if(len < 4 || strcmp(&buffer[len - 4], ".dsf") != 0) {
|
||||
// Append .dsf
|
||||
if(len + 4 >= 1024) {
|
||||
luaL_error(L, "Filename too long to append .dsf");
|
||||
return 0;
|
||||
}
|
||||
stringCopy(&buffer[len], ".dsf", 5);
|
||||
}
|
||||
|
||||
// Execute the script file
|
||||
errorret_t err = scriptContextExecFile(
|
||||
ctx,
|
||||
buffer
|
||||
);
|
||||
if(err.code != ERROR_OK) {
|
||||
luaL_error(L, "Failed to include script file");
|
||||
errorCatch(errorPrint(err));
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t moduleSysModule(lua_State *L) {
|
||||
assertNotNull(L, "Lua state cannot be NULL");
|
||||
|
||||
if(!lua_isstring(L, 1)) {
|
||||
luaL_error(L, "Expected string module name");
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char_t *moduleName = luaL_checkstring(L, 1);
|
||||
if(moduleName == NULL) {
|
||||
luaL_error(L, "Module name cannot be NULL");
|
||||
return 0;
|
||||
}
|
||||
|
||||
const scriptmodule_t *module = scriptModuleGetByName(moduleName);
|
||||
if(module == NULL) {
|
||||
luaL_error(L, "Module '%s' not found", moduleName);
|
||||
return 0;
|
||||
}
|
||||
|
||||
scriptcontext_t* ctx = *(scriptcontext_t**)lua_getextraspace(L);
|
||||
if(ctx == NULL) {
|
||||
luaL_error(L, "Script context is NULL");
|
||||
return 0;
|
||||
}
|
||||
|
||||
module->callback(ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void moduleSystem(scriptcontext_t *context) {
|
||||
assertNotNull(context, "Script context cannot be NULL");
|
||||
|
||||
scriptContextRegFunc(context, "print", moduleSysPrint);
|
||||
scriptContextRegFunc(context, "include", moduleSysInclude);
|
||||
scriptContextRegFunc(context, "module", moduleSysModule);
|
||||
}
|
||||
135
src/script/module/scriptfuncentity.h
Normal file
135
src/script/module/scriptfuncentity.h
Normal file
@@ -0,0 +1,135 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "script/scriptcontext.h"
|
||||
#include "rpg/entity/entity.h"
|
||||
#include "assert/assert.h"
|
||||
|
||||
int32_t scriptFuncEntityAdd(lua_State *L) {
|
||||
assertNotNull(L, "Lua state cannot be NULL");
|
||||
|
||||
assertTrue(lua_isinteger(L, 1), "Expected integer entity type");
|
||||
|
||||
lua_Integer entityType = luaL_checkinteger(L, 1);
|
||||
assertTrue(
|
||||
entityType >= ENTITY_TYPE_NULL && entityType < ENTITY_TYPE_COUNT,
|
||||
"Invalid entity type passed to scriptFuncEntityAdd"
|
||||
);
|
||||
|
||||
// Pop entity
|
||||
uint8_t available = entityGetAvailable();
|
||||
if(available == 0xFF) {
|
||||
lua_pushnil(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
entity_t *ent = &ENTITIES[available];
|
||||
entityInit(ent, (entitytype_t)entityType);
|
||||
|
||||
// May include X, Y and/or Z
|
||||
if(lua_isinteger(L, 2)) {
|
||||
lua_Integer xPos = luaL_checkinteger(L, 2);
|
||||
ent->position.x = (int32_t)xPos;
|
||||
}
|
||||
if(lua_isinteger(L, 3)) {
|
||||
lua_Integer yPos = luaL_checkinteger(L, 3);
|
||||
ent->position.y = (int32_t)yPos;
|
||||
}
|
||||
if(lua_isinteger(L, 4)) {
|
||||
lua_Integer zPos = luaL_checkinteger(L, 4);
|
||||
ent->position.z = (int32_t)zPos;
|
||||
}
|
||||
|
||||
// Send entity id.
|
||||
lua_pushinteger(L, ent->id);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int32_t scriptFuncEntitySetX(lua_State *L) {
|
||||
assertNotNull(L, "Lua state cannot be NULL");
|
||||
|
||||
assertTrue(lua_isinteger(L, 1), "Expected integer entity id");
|
||||
assertTrue(lua_isinteger(L, 2), "Expected integer x position");
|
||||
|
||||
lua_Integer entityId = luaL_checkinteger(L, 1);
|
||||
lua_Integer xPos = luaL_checkinteger(L, 2);
|
||||
|
||||
assertTrue(
|
||||
entityId >= 0 && entityId < ENTITY_COUNT,
|
||||
"Invalid entity id passed to scriptFuncEntitySetX"
|
||||
);
|
||||
|
||||
entity_t *ent = &ENTITIES[entityId];
|
||||
assertTrue(
|
||||
ent->type != ENTITY_TYPE_NULL,
|
||||
"Cannot set position of NULL entity in scriptFuncEntitySetX"
|
||||
);
|
||||
|
||||
ent->position.x = (int32_t)xPos;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t scriptFuncEntitySetY(lua_State *L) {
|
||||
assertNotNull(L, "Lua state cannot be NULL");
|
||||
|
||||
assertTrue(lua_isinteger(L, 1), "Expected integer entity id");
|
||||
assertTrue(lua_isinteger(L, 2), "Expected integer y position");
|
||||
|
||||
lua_Integer entityId = luaL_checkinteger(L, 1);
|
||||
lua_Integer yPos = luaL_checkinteger(L, 2);
|
||||
|
||||
assertTrue(
|
||||
entityId >= 0 && entityId < ENTITY_COUNT,
|
||||
"Invalid entity id passed to scriptFuncEntitySetY"
|
||||
);
|
||||
|
||||
entity_t *ent = &ENTITIES[entityId];
|
||||
assertTrue(
|
||||
ent->type != ENTITY_TYPE_NULL,
|
||||
"Cannot set position of NULL entity in scriptFuncEntitySetY"
|
||||
);
|
||||
|
||||
ent->position.y = (int32_t)yPos;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t scriptFuncEntitySetZ(lua_State *L) {
|
||||
assertNotNull(L, "Lua state cannot be NULL");
|
||||
|
||||
assertTrue(lua_isinteger(L, 1), "Expected integer entity id");
|
||||
assertTrue(lua_isinteger(L, 2), "Expected integer z position");
|
||||
|
||||
lua_Integer entityId = luaL_checkinteger(L, 1);
|
||||
lua_Integer zPos = luaL_checkinteger(L, 2);
|
||||
|
||||
assertTrue(
|
||||
entityId >= 0 && entityId < ENTITY_COUNT,
|
||||
"Invalid entity id passed to scriptFuncEntitySetZ"
|
||||
);
|
||||
|
||||
entity_t *ent = &ENTITIES[entityId];
|
||||
assertTrue(
|
||||
ent->type != ENTITY_TYPE_NULL,
|
||||
"Cannot set position of NULL entity in scriptFuncEntitySetZ"
|
||||
);
|
||||
|
||||
ent->position.z = (int32_t)zPos;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void scriptFuncEntity(scriptcontext_t *context) {
|
||||
assertNotNull(context, "Script context cannot be NULL");
|
||||
|
||||
scriptContextRegFunc(context, "entityAdd", scriptFuncEntityAdd);
|
||||
scriptContextRegFunc(context, "entitySetX", scriptFuncEntitySetX);
|
||||
scriptContextRegFunc(context, "entitySetY", scriptFuncEntitySetY);
|
||||
scriptContextRegFunc(context, "entitySetZ", scriptFuncEntitySetZ);
|
||||
}
|
||||
190
src/script/scriptcontext.c
Normal file
190
src/script/scriptcontext.c
Normal file
@@ -0,0 +1,190 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "scriptcontext.h"
|
||||
#include "assert/assert.h"
|
||||
#include "asset/asset.h"
|
||||
#include "util/memory.h"
|
||||
#include "debug/debug.h"
|
||||
#include "script/scriptmodule.h"
|
||||
|
||||
errorret_t scriptContextInit(scriptcontext_t *context) {
|
||||
assertNotNull(context, "Script context cannot be NULL");
|
||||
|
||||
memoryZero(context, sizeof(scriptcontext_t));
|
||||
|
||||
// Create a new Lua state for this context.
|
||||
context->luaState = luaL_newstate();
|
||||
if(context->luaState == NULL) {
|
||||
errorThrow("Failed to init Lua state");
|
||||
}
|
||||
luaL_openlibs(context->luaState);
|
||||
|
||||
// Store context in Lua extraspace
|
||||
*(scriptcontext_t**)lua_getextraspace(context->luaState) = context;
|
||||
|
||||
// All scripts get the system module
|
||||
const scriptmodule_t *sysModule = scriptModuleGetByName("system");
|
||||
if(sysModule == NULL) {
|
||||
errorThrow("Failed to find system script module");
|
||||
}
|
||||
sysModule->callback(context);
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
void scriptContextRegFunc(
|
||||
scriptcontext_t *context,
|
||||
const char_t *fnName,
|
||||
lua_CFunction function
|
||||
) {
|
||||
assertNotNull(context, "Script context cannot be NULL");
|
||||
assertNotNull(fnName, "Function name cannot be NULL");
|
||||
assertNotNull(function, "Function cannot be NULL");
|
||||
|
||||
lua_register(context->luaState, fnName, function);
|
||||
}
|
||||
|
||||
errorret_t scriptContextCallFunc(
|
||||
scriptcontext_t *context,
|
||||
const char_t *fnName,
|
||||
const scriptvalue_t *args,
|
||||
const int32_t argCount,
|
||||
scriptvalue_t *retValue
|
||||
) {
|
||||
assertNotNull(context, "Script context cannot be NULL");
|
||||
assertNotNull(fnName, "Function name cannot be NULL");
|
||||
assertTrue(args == NULL || argCount >= 0, "Invalid arg count");
|
||||
|
||||
// Get func
|
||||
lua_getglobal(context->luaState, fnName);
|
||||
if(!lua_isfunction(context->luaState, -1)) {
|
||||
errorThrow("Function '%s' not found in script context", fnName);
|
||||
}
|
||||
|
||||
// Push args
|
||||
for(int32_t i = 0; i < argCount; i++) {
|
||||
const scriptvalue_t *arg = &args[i];
|
||||
switch(arg->type) {
|
||||
case SCRIPT_VALUE_TYPE_INT:
|
||||
lua_pushinteger(context->luaState, arg->value.intValue);
|
||||
break;
|
||||
|
||||
case SCRIPT_VALUE_TYPE_FLOAT:
|
||||
lua_pushnumber(context->luaState, arg->value.floatValue);
|
||||
break;
|
||||
|
||||
case SCRIPT_VALUE_TYPE_STRING:
|
||||
lua_pushstring(context->luaState, arg->value.strValue);
|
||||
break;
|
||||
|
||||
default:
|
||||
errorThrow("Unsupported argument type %d", arg->type);
|
||||
}
|
||||
}
|
||||
|
||||
// Call func
|
||||
if(lua_pcall(
|
||||
context->luaState,
|
||||
args ? argCount : 0,
|
||||
retValue ? 1 : 0,
|
||||
0
|
||||
) != LUA_OK) {
|
||||
const char_t *strErr = lua_tostring(context->luaState, -1);
|
||||
lua_pop(context->luaState, 1);
|
||||
errorThrow("Failed to call function '%s': %s", fnName, strErr);
|
||||
}
|
||||
|
||||
// Was there a ret value?
|
||||
if(retValue == NULL) {
|
||||
errorOk();
|
||||
}
|
||||
|
||||
// Get ret value
|
||||
switch(retValue->type) {
|
||||
case SCRIPT_VALUE_TYPE_INT:
|
||||
if(!lua_isinteger(context->luaState, -1)) {
|
||||
errorThrow("Expected integer return value from '%s'", fnName);
|
||||
}
|
||||
retValue->value.intValue = (int32_t)lua_tointeger(context->luaState, -1);
|
||||
break;
|
||||
|
||||
case SCRIPT_VALUE_TYPE_FLOAT:
|
||||
if(!lua_isnumber(context->luaState, -1)) {
|
||||
errorThrow("Expected float return value from '%s'", fnName);
|
||||
}
|
||||
retValue->value.floatValue = (float)lua_tonumber(context->luaState, -1);
|
||||
break;
|
||||
|
||||
case SCRIPT_VALUE_TYPE_BOOL:
|
||||
if(!lua_isboolean(context->luaState, -1)) {
|
||||
errorThrow("Expected boolean return value from '%s'", fnName);
|
||||
}
|
||||
retValue->value.boolValue = lua_toboolean(context->luaState, -1);
|
||||
break;
|
||||
|
||||
// case SCRIPT_VALUE_TYPE_STRING:
|
||||
// if(!lua_isstring(context->luaState, -1)) {
|
||||
// errorThrow("Expected string return value from '%s'", fnName);
|
||||
// }
|
||||
// retValue->value.strValue = lua_tostring(context->luaState, -1);
|
||||
// break;
|
||||
|
||||
default:
|
||||
errorThrow("Unsupported return value type %d", retValue->type);
|
||||
}
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t scriptContextExec(scriptcontext_t *context, const char_t *script) {
|
||||
assertNotNull(context, "Script context cannot be NULL");
|
||||
assertNotNull(script, "Script cannot be NULL");
|
||||
|
||||
if(luaL_dostring(context->luaState, script) != LUA_OK) {
|
||||
const char_t *strErr = lua_tostring(context->luaState, -1);
|
||||
lua_pop(context->luaState, 1);
|
||||
errorThrow("Failed to execute Lua: ", strErr);
|
||||
}
|
||||
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t scriptContextExecFile(scriptcontext_t *ctx, const char_t *fname) {
|
||||
assertNotNull(ctx, "Script context cannot be NULL");
|
||||
assertNotNull(fname, "Filename cannot be NULL");
|
||||
|
||||
assetscript_t script;
|
||||
errorChain(assetLoad(fname, &script));
|
||||
|
||||
if(lua_load(
|
||||
ctx->luaState, assetScriptReader, &script, fname, NULL
|
||||
) != LUA_OK) {
|
||||
const char_t *strErr = lua_tostring(ctx->luaState, -1);
|
||||
lua_pop(ctx->luaState, 1);
|
||||
errorThrow("Failed to load Lua script: %s", strErr);
|
||||
}
|
||||
|
||||
if(lua_pcall(ctx->luaState, 0, LUA_MULTRET, 0) != LUA_OK) {
|
||||
const char_t *strErr = lua_tostring(ctx->luaState, -1);
|
||||
lua_pop(ctx->luaState, 1);
|
||||
errorThrow("Failed to execute Lua script: %s", strErr);
|
||||
}
|
||||
|
||||
errorChain(assetScriptDispose(&script));
|
||||
errorOk();
|
||||
}
|
||||
|
||||
void scriptContextDispose(scriptcontext_t *context) {
|
||||
assertNotNull(context, "Script context cannot be NULL");
|
||||
assertNotNull(context->luaState, "Lua state is not initialized");
|
||||
|
||||
if(context->luaState != NULL) {
|
||||
lua_close(context->luaState);
|
||||
context->luaState = NULL;
|
||||
}
|
||||
}
|
||||
82
src/script/scriptcontext.h
Normal file
82
src/script/scriptcontext.h
Normal file
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "error/error.h"
|
||||
#include "scriptvalue.h"
|
||||
#include <lua.h>
|
||||
#include <lauxlib.h>
|
||||
#include <lualib.h>
|
||||
|
||||
typedef struct scriptcontext_s {
|
||||
lua_State *luaState;
|
||||
} scriptcontext_t;
|
||||
|
||||
/**
|
||||
* Initialize a script context.
|
||||
*
|
||||
* @param context The script context to initialize.
|
||||
* @return The error return value.
|
||||
*/
|
||||
errorret_t scriptContextInit(scriptcontext_t *context);
|
||||
|
||||
/**
|
||||
* Register a C function within a script context.
|
||||
*
|
||||
* @param context The script context to use.
|
||||
* @param fnName The name of the function in Lua.
|
||||
* @param function The C function to register.
|
||||
*/
|
||||
void scriptContextRegFunc(
|
||||
scriptcontext_t *context,
|
||||
const char_t *fnName,
|
||||
lua_CFunction function
|
||||
);
|
||||
|
||||
/**
|
||||
* Call a Lua function within a script context.
|
||||
*
|
||||
* @param context The script context to use.
|
||||
* @param fnName The name of the Lua function to call.
|
||||
* @param args Array of args to pass to the function (or NULL for no args)
|
||||
* @param argCount The number of arguments in the args array (omitable).
|
||||
* @param retValue Output to store returned value (or NULL for no return value).
|
||||
* @return The error return value.
|
||||
*/
|
||||
|
||||
errorret_t scriptContextCallFunc(
|
||||
scriptcontext_t *context,
|
||||
const char_t *fnName,
|
||||
const scriptvalue_t *args,
|
||||
const int32_t argCount,
|
||||
scriptvalue_t *retValue
|
||||
);
|
||||
|
||||
/**
|
||||
* Execute a script within a script context.
|
||||
*
|
||||
* @param context The script context to use.
|
||||
* @param script The script to execute.
|
||||
* @return The error return value.
|
||||
*/
|
||||
errorret_t scriptContextExec(scriptcontext_t *context, const char_t *script);
|
||||
|
||||
/**
|
||||
* Execute a script from a file within a script context.
|
||||
*
|
||||
* @param ctx The script context to use.
|
||||
* @param fname The filename of the script to execute.
|
||||
* @return The error return value.
|
||||
*/
|
||||
errorret_t scriptContextExecFile(scriptcontext_t *ctx, const char_t *fname);
|
||||
|
||||
/**
|
||||
* Dispose of a script context.
|
||||
*
|
||||
* @param context The script context to dispose of.
|
||||
*/
|
||||
void scriptContextDispose(scriptcontext_t *context);
|
||||
23
src/script/scriptmanager.c
Normal file
23
src/script/scriptmanager.c
Normal file
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "scriptmanager.h"
|
||||
#include "util/memory.h"
|
||||
#include "assert/assert.h"
|
||||
#include "debug/debug.h"
|
||||
#include "asset/asset.h"
|
||||
|
||||
scriptmanager_t SCRIPT_MANAGER;
|
||||
|
||||
errorret_t scriptManagerInit() {
|
||||
memoryZero(&SCRIPT_MANAGER, sizeof(scriptmanager_t));
|
||||
errorOk();
|
||||
}
|
||||
|
||||
errorret_t scriptManagerDispose() {
|
||||
errorOk();
|
||||
}
|
||||
30
src/script/scriptmanager.h
Normal file
30
src/script/scriptmanager.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "error/error.h"
|
||||
#include "scriptcontext.h"
|
||||
|
||||
typedef struct scriptmanager_s {
|
||||
scriptcontext_t mainContext;
|
||||
} scriptmanager_t;
|
||||
|
||||
extern scriptmanager_t SCRIPT_MANAGER;
|
||||
|
||||
/**
|
||||
* Initialize the script manager.
|
||||
*
|
||||
* @return The error return value.
|
||||
*/
|
||||
errorret_t scriptManagerInit();
|
||||
|
||||
/**
|
||||
* Dispose of the script manager.
|
||||
*
|
||||
* @return The error return value.
|
||||
*/
|
||||
errorret_t scriptManagerDispose();
|
||||
32
src/script/scriptmodule.c
Normal file
32
src/script/scriptmodule.c
Normal file
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "scriptmodule.h"
|
||||
#include "script/module/modulesystem.h"
|
||||
#include "script/module/moduleinput.h"
|
||||
#include "script/module/moduleplatform.h"
|
||||
#include "script/module/modulescene.h"
|
||||
|
||||
const scriptmodule_t SCRIPT_MODULE_LIST[] = {
|
||||
{ .name = "system", .callback = moduleSystem },
|
||||
{ .name = "input", .callback = moduleInput },
|
||||
{ .name = "platform", .callback = modulePlatform },
|
||||
{ .name = "scene", .callback = moduleScene },
|
||||
};
|
||||
|
||||
#define SCRIPT_MODULE_COUNT ( \
|
||||
sizeof(SCRIPT_MODULE_LIST) / sizeof(scriptmodule_t) \
|
||||
)
|
||||
|
||||
const scriptmodule_t * scriptModuleGetByName(const char_t *name) {
|
||||
for(uint_fast8_t i = 0; i < SCRIPT_MODULE_COUNT; i++) {
|
||||
if(stringCompare(SCRIPT_MODULE_LIST[i].name, name) != 0) continue;
|
||||
return &SCRIPT_MODULE_LIST[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
24
src/script/scriptmodule.h
Normal file
24
src/script/scriptmodule.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "scriptcontext.h"
|
||||
|
||||
typedef struct scriptmodule_s {
|
||||
const char_t *name;
|
||||
void (*callback)(scriptcontext_t *ctx);
|
||||
} scriptmodule_t;
|
||||
|
||||
extern const scriptmodule_t SCRIPT_MODULE_LIST[];
|
||||
|
||||
/**
|
||||
* Retrieves a script module by its name.
|
||||
*
|
||||
* @param name The name of the script module.
|
||||
* @return Pointer to the script module, or NULL if not found.
|
||||
*/
|
||||
const scriptmodule_t * scriptModuleGetByName(const char_t *name);
|
||||
26
src/script/scriptvalue.h
Normal file
26
src/script/scriptvalue.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "dusk.h"
|
||||
|
||||
#define SCRIPT_VALUE_TYPE_NIL 0
|
||||
#define SCRIPT_VALUE_TYPE_INT 1
|
||||
#define SCRIPT_VALUE_TYPE_FLOAT 2
|
||||
#define SCRIPT_VALUE_TYPE_STRING 3
|
||||
#define SCRIPT_VALUE_TYPE_BOOL 4
|
||||
|
||||
typedef struct scriptvalue_s {
|
||||
uint8_t type;
|
||||
|
||||
union {
|
||||
int32_t intValue;
|
||||
float floatValue;
|
||||
const char_t *strValue;
|
||||
bool boolValue;
|
||||
} value;
|
||||
} scriptvalue_t;
|
||||
@@ -12,3 +12,6 @@ target_sources(${DUSK_TARGET_NAME}
|
||||
uiframe.c
|
||||
uitextbox.c
|
||||
)
|
||||
|
||||
# Subdirs
|
||||
add_subdirectory(element)
|
||||
9
src/ui/element/CMakeLists.txt
Normal file
9
src/ui/element/CMakeLists.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
# Copyright (c) 2025 Dominic Masters
|
||||
#
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
# Sources
|
||||
target_sources(${DUSK_TARGET_NAME}
|
||||
PRIVATE
|
||||
)
|
||||
16
src/ui/element/uielementtype.h
Normal file
16
src/ui/element/uielementtype.h
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Dominic Masters
|
||||
*
|
||||
* This software is released under the MIT License.
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
typedef enum {
|
||||
UI_ELEMENT_TYPE_NULL,
|
||||
|
||||
UI_ELEMENT_TYPE_TEXT,
|
||||
|
||||
UI_ELEMENT_TYPE_COUNT,
|
||||
} uielementtype_t;
|
||||
@@ -12,3 +12,20 @@ function(add_asset ASSET_TYPE ASSET_PATH)
|
||||
)
|
||||
set(DUSK_ASSETS ${DUSK_ASSETS} CACHE INTERNAL ${DUSK_CACHE_TARGET})
|
||||
endfunction()
|
||||
|
||||
function(add_defs INPUT_PATH OUTPUT_NAME_RELATIVE)
|
||||
set(INPUT_FULL_PATH "${CMAKE_CURRENT_LIST_DIR}/${INPUT_PATH}")
|
||||
set(OUTPUT_FULL_PATH "${DUSK_GENERATED_HEADERS_DIR}/${OUTPUT_NAME_RELATIVE}")
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${OUTPUT_FULL_PATH}
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
-DENV_FILE=${INPUT_FULL_PATH}
|
||||
-DOUT_HEADER=${OUTPUT_FULL_PATH}
|
||||
-P ${CMAKE_SOURCE_DIR}/cmake/modules/envtoh.cmake
|
||||
DEPENDS ${INPUT_FULL_PATH} ${CMAKE_SOURCE_DIR}/cmake/modules/envtoh.cmake
|
||||
COMMENT "Generating ${OUTPUT_NAME_RELATIVE}"
|
||||
)
|
||||
add_custom_target(${OUTPUT_NAME_RELATIVE}_header DEPENDS ${OUTPUT_FULL_PATH})
|
||||
add_dependencies(${DUSK_TARGET_NAME} ${OUTPUT_NAME_RELATIVE}_header)
|
||||
endfunction()
|
||||
@@ -1,50 +1,12 @@
|
||||
import os
|
||||
import sys, os
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
# Check if the script is run with the correct arguments
|
||||
parser = argparse.ArgumentParser(description="Generate chunk header files")
|
||||
parser.add_argument('--assets', required=True, help='Dir to output built assets')
|
||||
parser.add_argument('--build-type', choices=['wad', 'header'], default='raw', help='Type of build to perform')
|
||||
parser.add_argument('--output-file', required=True, help='Output file for built assets (required for wad build)')
|
||||
parser.add_argument('--headers-dir', required=True, help='Directory to output individual asset headers (required for header build)')
|
||||
parser.add_argument('--output-headers', help='Output header file for built assets (required for header build)')
|
||||
parser.add_argument('--output-assets', required=True, help='Output directory for built assets')
|
||||
parser.add_argument('--output-file', required=True, help='Output file for built assets (required for wad build)')
|
||||
parser.add_argument('--input', required=True, help='Input assets to process', nargs='+')
|
||||
args = parser.parse_args()
|
||||
|
||||
inputAssets = []
|
||||
for inputArg in args.input:
|
||||
files = inputArg.split('$')
|
||||
for file in files:
|
||||
if str(file).strip() == '':
|
||||
continue
|
||||
|
||||
pieces = file.split('#')
|
||||
|
||||
if len(pieces) < 2:
|
||||
print(f"Error: Invalid input asset format '{file}'. Expected format: type#path[#option1%option2...]")
|
||||
sys.exit(1)
|
||||
|
||||
options = {}
|
||||
if len(pieces) > 2:
|
||||
optionParts = pieces[2].split('%')
|
||||
for part in optionParts:
|
||||
partSplit = part.split('=')
|
||||
|
||||
if len(partSplit) < 1:
|
||||
continue
|
||||
if len(partSplit) == 2:
|
||||
options[partSplit[0]] = partSplit[1]
|
||||
else:
|
||||
options[partSplit[0]] = True
|
||||
|
||||
inputAssets.append({
|
||||
'type': pieces[0],
|
||||
'path': pieces[1],
|
||||
'options': options
|
||||
})
|
||||
|
||||
if not inputAssets:
|
||||
print("Error: No input assets provided.")
|
||||
sys.exit(1)
|
||||
@@ -1,5 +1,5 @@
|
||||
import os
|
||||
from args import args
|
||||
from assetstool.args import args
|
||||
|
||||
def getAssetRelativePath(fullPath):
|
||||
# Get the relative path to the asset
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
ASSET_FILE_NAME_MAX_LENGTH = 256
|
||||
@@ -1,49 +0,0 @@
|
||||
import sys, os
|
||||
from args import inputAssets, args
|
||||
from processasset import processAsset
|
||||
from processpalette import processPaletteList
|
||||
from processtileset import processTilesetList
|
||||
from processlanguage import processLanguageList
|
||||
from assethelpers import getBuiltAssetsRelativePath
|
||||
import zipfile
|
||||
|
||||
# Setup headers directory.
|
||||
# setOutputDir(args.output)
|
||||
# outputHeaders = []
|
||||
|
||||
# # Create output directory if it doesn't exist
|
||||
# if not os.path.exists(args.output):
|
||||
# os.makedirs(args.output)
|
||||
|
||||
files = []
|
||||
|
||||
for asset in inputAssets:
|
||||
asset = processAsset(asset)
|
||||
files.extend(asset['files'])
|
||||
|
||||
files.extend(processLanguageList()['files'])
|
||||
|
||||
# Take assets and add to a zip archive.
|
||||
outputFileName = args.output_file
|
||||
print(f"Creating output file: {outputFileName}")
|
||||
with zipfile.ZipFile(outputFileName, 'w') as zipf:
|
||||
for file in files:
|
||||
relativeOutputPath = getBuiltAssetsRelativePath(file)
|
||||
zipf.write(file, arcname=relativeOutputPath)
|
||||
|
||||
# Generate additional headers.
|
||||
processPaletteList()
|
||||
processTilesetList()
|
||||
|
||||
# Finalize build
|
||||
if args.build_type == 'header':
|
||||
print("Error: Header build not implemented yet.")
|
||||
sys.exit(1)
|
||||
|
||||
elif args.build_type == 'wad':
|
||||
# Nothing to do, already created above!
|
||||
pass
|
||||
|
||||
else:
|
||||
print("Error: Unknown build type.")
|
||||
sys.exit(1)
|
||||
@@ -1,10 +1,11 @@
|
||||
import sys
|
||||
# from processtileset import processTileset
|
||||
from processimage import processImage
|
||||
from processpalette import processPalette
|
||||
from processtileset import processTileset
|
||||
from processmap import processMap
|
||||
from processlanguage import processLanguage
|
||||
from assetstool.processimage import processImage
|
||||
from assetstool.processpalette import processPalette
|
||||
from assetstool.processtileset import processTileset
|
||||
from assetstool.processmap import processMap
|
||||
from assetstool.processlanguage import processLanguage
|
||||
from assetstool.processscript import processScript
|
||||
|
||||
processedAssets = []
|
||||
|
||||
@@ -25,6 +26,8 @@ def processAsset(asset):
|
||||
return processMap(asset)
|
||||
elif t == 'language':
|
||||
return processLanguage(asset)
|
||||
elif t == 'script':
|
||||
return processScript(asset)
|
||||
else:
|
||||
print(f"Error: Unknown asset type '{asset['type']}' for path '{asset['path']}'")
|
||||
sys.exit(1)
|
||||
@@ -1,10 +1,10 @@
|
||||
import os
|
||||
import sys
|
||||
from PIL import Image
|
||||
from processpalette import extractPaletteFromImage, palettes
|
||||
from args import args
|
||||
from assethelpers import getAssetRelativePath
|
||||
from assetcache import assetGetCache, assetCache
|
||||
from assetstool.processpalette import extractPaletteFromImage, palettes
|
||||
from assetstool.args import args
|
||||
from assetstool.assethelpers import getAssetRelativePath
|
||||
from assetstool.assetcache import assetGetCache, assetCache
|
||||
|
||||
images = []
|
||||
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import sys
|
||||
import os
|
||||
from args import args
|
||||
from assetcache import assetCache, assetGetCache
|
||||
from assethelpers import getAssetRelativePath
|
||||
from assetstool.args import args
|
||||
from assetstool.assetcache import assetCache, assetGetCache
|
||||
from assetstool.assethelpers import getAssetRelativePath
|
||||
from dusk.defs import defs
|
||||
import polib
|
||||
import re
|
||||
|
||||
LANGUAGE_CHUNK_CHAR_COUNT = 6 * 1024 # 6 KB per chunk
|
||||
LANGUAGE_CHUNK_CHAR_COUNT = int(defs.get('ASSET_LANG_CHUNK_CHAR_COUNT'))
|
||||
|
||||
LANGUAGE_DATA = {}
|
||||
LANGUAGE_KEYS = []
|
||||
|
||||
@@ -2,159 +2,96 @@ import struct
|
||||
import sys
|
||||
import os
|
||||
import json
|
||||
from args import args
|
||||
from assetcache import assetCache, assetGetCache
|
||||
from assethelpers import getAssetRelativePath
|
||||
from assetstool.args import args
|
||||
from assetstool.assetcache import assetCache, assetGetCache
|
||||
from assetstool.assethelpers import getAssetRelativePath
|
||||
from dusk.defs import TILE_WIDTH, TILE_HEIGHT, TILE_DEPTH, CHUNK_WIDTH, CHUNK_HEIGHT, CHUNK_DEPTH, CHUNK_TILE_COUNT
|
||||
from dusk.map import Map
|
||||
from dusk.chunk import Chunk
|
||||
|
||||
CHUNK_WIDTH = 16
|
||||
CHUNK_HEIGHT = 16
|
||||
CHUNK_DEPTH = 4
|
||||
CHUNK_TILE_COUNT = CHUNK_WIDTH * CHUNK_HEIGHT * CHUNK_DEPTH
|
||||
TILE_WIDTH = 16.0
|
||||
TILE_HEIGHT = 16.0
|
||||
TILE_DEPTH = 11.36
|
||||
|
||||
def processTile(tileIndex, x=0, y=0, z=0, chunkX=0, chunkY=0, chunkZ=0):
|
||||
vertices = []
|
||||
indices = []
|
||||
tileType = tileIndex
|
||||
|
||||
# Placement X, Y, Z
|
||||
px = (x * TILE_WIDTH) + (chunkX * CHUNK_WIDTH * TILE_WIDTH)
|
||||
py = (y * TILE_HEIGHT) + (chunkY * CHUNK_HEIGHT * TILE_HEIGHT)
|
||||
pz = (z * TILE_DEPTH) + (chunkZ * CHUNK_DEPTH * TILE_DEPTH)
|
||||
|
||||
if tileIndex == 0:
|
||||
# Tile 0, nothing
|
||||
return None
|
||||
|
||||
elif tileIndex == 5:
|
||||
# Tile 2, ramp up
|
||||
color = (255,0,0)
|
||||
vertices = [
|
||||
{'position': (px, py, pz + TILE_DEPTH), 'color': color, 'uv': (0,0)}, # 0,0
|
||||
{'position': (px + TILE_WIDTH, py, pz + TILE_DEPTH), 'color': color, 'uv': (1,0)}, # 1,0
|
||||
{'position': (px + TILE_WIDTH, py + TILE_HEIGHT, pz), 'color': color, 'uv': (1,1)}, # 1,1
|
||||
{'position': (px, py, pz + TILE_DEPTH), 'color': color, 'uv': (0,0)}, # 0,0 (repeat)
|
||||
{'position': (px + TILE_WIDTH, py + TILE_HEIGHT, pz), 'color': color, 'uv': (1,1)}, # 1,1 (repeat)
|
||||
{'position': (px, py + TILE_HEIGHT, pz), 'color': color, 'uv': (0,1)} # 0,1
|
||||
]
|
||||
indices = [0, 1, 2, 3, 4, 5]
|
||||
|
||||
else:
|
||||
# Determine color for checkerboard pattern
|
||||
if tileIndex == 1:
|
||||
color = (255, 255, 255)
|
||||
else:
|
||||
color = (0, 0, 255)
|
||||
|
||||
vertices = [
|
||||
{'position': (px, py, pz), 'color': color, 'uv': (0,0)}, # 0,0
|
||||
{'position': (px + TILE_WIDTH, py, pz), 'color': color, 'uv': (1,0)}, # 1,0
|
||||
{'position': (px + TILE_WIDTH, py + TILE_HEIGHT, pz), 'color': color, 'uv': (1,1)}, # 1,1
|
||||
{'position': (px, py, pz), 'color': color, 'uv': (0,0)}, # 0,0 (repeat)
|
||||
{'position': (px + TILE_WIDTH, py + TILE_HEIGHT, pz), 'color': color, 'uv': (1,1)}, # 1,1 (repeat)
|
||||
{'position': (px, py + TILE_HEIGHT, pz), 'color': color, 'uv': (0,1)} # 0,1
|
||||
]
|
||||
indices = [0, 1, 2, 3, 4, 5]
|
||||
def convertModelData(modelData):
|
||||
# TLDR; Model data stores things efficiently with indices, but we buffer it
|
||||
# out to 6 vertex quads for simplicity.
|
||||
outVertices = []
|
||||
outUVs = []
|
||||
outColors = []
|
||||
for indice in modelData['indices']:
|
||||
vertex = modelData['vertices'][indice]
|
||||
uv = modelData['uvs'][indice]
|
||||
color = modelData['colors'][indice]
|
||||
outVertices.append(vertex)
|
||||
outUVs.append(uv)
|
||||
outColors.append(color)
|
||||
|
||||
return {
|
||||
'vertices': vertices,
|
||||
'indices': indices,
|
||||
'tileType': tileType
|
||||
'vertices': outVertices,
|
||||
'uvs': outUVs,
|
||||
'colors': outColors
|
||||
}
|
||||
|
||||
def processChunk(path):
|
||||
cache = assetGetCache(path)
|
||||
def processChunk(chunk):
|
||||
cache = assetGetCache(chunk.getFilename())
|
||||
if cache:
|
||||
return cache
|
||||
|
||||
# Read input file as JSON
|
||||
with open(path, 'r') as f:
|
||||
inData = json.load(f)
|
||||
|
||||
# Filename must contain chunk coordinates as X_Y_Z
|
||||
fileName = os.path.basename(path)
|
||||
nameParts = os.path.splitext(fileName)[0].split('_')
|
||||
if len(nameParts) != 3:
|
||||
print(f"Error: Chunk filename {fileName} does not contain valid chunk coordinates.")
|
||||
sys.exit(1)
|
||||
|
||||
chunk = {
|
||||
'chunkX': int(nameParts[0]),
|
||||
'chunkY': int(nameParts[1]),
|
||||
'chunkZ': int(nameParts[2]),
|
||||
'tiles': [0] * CHUNK_TILE_COUNT,
|
||||
'models': []
|
||||
}
|
||||
|
||||
baseModel = {
|
||||
'vertices': [],
|
||||
'indices': [],
|
||||
'vertexCount': 0,
|
||||
'indexCount': 0
|
||||
'colors': [],
|
||||
'uvs': []
|
||||
}
|
||||
models = [ baseModel ]
|
||||
|
||||
# Append the model to chunk.models
|
||||
chunk['models'].append(baseModel)
|
||||
for tileIndex, tile in chunk.tiles.items():
|
||||
tileBase = tile.getBaseTileModel()
|
||||
|
||||
for i, tile in enumerate(inData['tiles']):
|
||||
# Set to chunk
|
||||
|
||||
# Calculate x, y, z from i
|
||||
x = i % CHUNK_WIDTH
|
||||
y = (i // CHUNK_WIDTH) % CHUNK_HEIGHT
|
||||
z = i // (CHUNK_WIDTH * CHUNK_HEIGHT)
|
||||
|
||||
# Add tile 3D model
|
||||
result = processTile(tile, x, y, z, chunk['chunkX'], chunk['chunkY'], chunk['chunkZ'])
|
||||
if result is not None and len(result['vertices']) > 0:
|
||||
base = len(baseModel['vertices'])
|
||||
quad_indices = [base + idx for idx in result['indices']]
|
||||
baseModel['vertices'].extend(result['vertices'])
|
||||
baseModel['indices'].extend(quad_indices)
|
||||
baseModel['vertexCount'] = len(baseModel['vertices'])
|
||||
baseModel['indexCount'] = len(baseModel['indices'])
|
||||
chunk['tiles'][i] = result['tileType']
|
||||
convertedBase = convertModelData(tileBase)
|
||||
baseModel['vertices'].extend(convertedBase['vertices'])
|
||||
baseModel['colors'].extend(convertedBase['colors'])
|
||||
baseModel['uvs'].extend(convertedBase['uvs'])
|
||||
|
||||
# Generate binary buffer for efficient output
|
||||
buffer = bytearray()
|
||||
buffer.extend(b'DCF')# Header
|
||||
buffer.extend(len(chunk['tiles']).to_bytes(4, 'little')) # Number of tiles
|
||||
buffer.extend(len(chunk['models']).to_bytes(1, 'little')) # Number of models
|
||||
buffer.extend(len(chunk.tiles).to_bytes(4, 'little')) # Number of tiles
|
||||
buffer.extend(len(models).to_bytes(1, 'little')) # Number of models
|
||||
buffer.extend(len(chunk.entities).to_bytes(1, 'little')) # Number of entities
|
||||
|
||||
# Buffer tile data as array of uint8_t
|
||||
for tileIndex in chunk['tiles']:
|
||||
buffer.append(tileIndex.to_bytes(1, 'little')[0])
|
||||
for tileIndex, tile in chunk.tiles.items():
|
||||
buffer.extend(tile.shape.to_bytes(1, 'little'))
|
||||
|
||||
# For each model
|
||||
for model in chunk['models']:
|
||||
# Write vertex count and index count
|
||||
buffer.extend(model['vertexCount'].to_bytes(4, 'little'))
|
||||
# # For each model
|
||||
for model in models:
|
||||
vertexCount = len(model['vertices'])
|
||||
buffer.extend(vertexCount.to_bytes(4, 'little'))
|
||||
for i in range(vertexCount):
|
||||
vertex = model['vertices'][i]
|
||||
uv = model['uvs'][i]
|
||||
color = model['colors'][i]
|
||||
|
||||
# For each vertex
|
||||
for vertex in model['vertices']:
|
||||
# This is not tightly packed in memory.
|
||||
# R G B A U V X Y Z
|
||||
# Color is 4 bytes (RGBA)
|
||||
# Rest is floats
|
||||
r, g, b = vertex['color']
|
||||
a = 255
|
||||
buffer.extend(r.to_bytes(1, 'little'))
|
||||
buffer.extend(g.to_bytes(1, 'little'))
|
||||
buffer.extend(b.to_bytes(1, 'little'))
|
||||
buffer.extend(a.to_bytes(1, 'little'))
|
||||
u, v = vertex['uv']
|
||||
buffer.extend(bytearray(struct.pack('<f', u)))
|
||||
buffer.extend(bytearray(struct.pack('<f', v)))
|
||||
x, y, z = vertex['position']
|
||||
buffer.extend(bytearray(struct.pack('<f', x)))
|
||||
buffer.extend(bytearray(struct.pack('<f', y)))
|
||||
buffer.extend(bytearray(struct.pack('<f', z)))
|
||||
buffer.extend(color[0].to_bytes(1, 'little'))
|
||||
buffer.extend(color[1].to_bytes(1, 'little'))
|
||||
buffer.extend(color[2].to_bytes(1, 'little'))
|
||||
buffer.extend(color[3].to_bytes(1, 'little'))
|
||||
|
||||
buffer.extend(bytearray(struct.pack('<f', uv[0])))
|
||||
buffer.extend(bytearray(struct.pack('<f', uv[1])))
|
||||
|
||||
buffer.extend(bytearray(struct.pack('<f', vertex[0])))
|
||||
buffer.extend(bytearray(struct.pack('<f', vertex[1])))
|
||||
buffer.extend(bytearray(struct.pack('<f', vertex[2])))
|
||||
|
||||
# For each entity
|
||||
for entity in chunk.entities.values():
|
||||
buffer.extend(entity.type.to_bytes(1, 'little'))
|
||||
buffer.extend(entity.localX.to_bytes(1, 'little'))
|
||||
buffer.extend(entity.localY.to_bytes(1, 'little'))
|
||||
buffer.extend(entity.localZ.to_bytes(1, 'little'))
|
||||
pass
|
||||
|
||||
# Write out map file
|
||||
relative = getAssetRelativePath(path)
|
||||
fileNameWithoutExt = os.path.splitext(os.path.basename(path))[0]
|
||||
relative = getAssetRelativePath(chunk.getFilename())
|
||||
fileNameWithoutExt = os.path.splitext(os.path.basename(relative))[0]
|
||||
outputFileRelative = os.path.join(os.path.dirname(relative), f"{fileNameWithoutExt}.dcf")
|
||||
outputFilePath = os.path.join(args.output_assets, outputFileRelative)
|
||||
os.makedirs(os.path.dirname(outputFilePath), exist_ok=True)
|
||||
@@ -165,20 +102,42 @@ def processChunk(path):
|
||||
'files': [ outputFilePath ],
|
||||
'chunk': chunk
|
||||
}
|
||||
|
||||
return assetCache(path, outChunk)
|
||||
|
||||
return assetCache(chunk.getFilename(), outChunk)
|
||||
|
||||
def processMap(asset):
|
||||
cache = assetGetCache(asset['path'])
|
||||
if cache is not None:
|
||||
return cache
|
||||
|
||||
# Path provided should be a directory.
|
||||
if not os.path.isdir(asset['path']):
|
||||
print(f"Error: Asset path {asset['path']} is not a directory.")
|
||||
map = Map(None)
|
||||
map.load(asset['path'])
|
||||
dir = map.getMapDirectory()
|
||||
|
||||
files = os.listdir(dir)
|
||||
if len(files) == 0:
|
||||
print(f"Error: No chunk files found in map directory {dir}.")
|
||||
sys.exit(1)
|
||||
|
||||
chunkFiles = []
|
||||
for fileName in files:
|
||||
if not fileName.endswith('.json'):
|
||||
continue
|
||||
|
||||
fNameNoExt = os.path.splitext(fileName)[0]
|
||||
fnPieces = fNameNoExt.split('_')
|
||||
if len(fnPieces) != 3:
|
||||
print(f"Error: Chunk filename {fileName} does not contain valid chunk coordinates.")
|
||||
sys.exit(1)
|
||||
chunk = Chunk(map, int(fnPieces[0]), int(fnPieces[1]), int(fnPieces[2]))
|
||||
chunk.load()
|
||||
result = processChunk(chunk)
|
||||
chunkFiles.extend(result['files'])
|
||||
|
||||
outMap = {
|
||||
'files': chunkFiles
|
||||
}
|
||||
return assetCache(asset['path'], outMap)
|
||||
|
||||
# List files
|
||||
chunkFiles = []
|
||||
for fileName in os.listdir(asset['path']):
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import os
|
||||
from PIL import Image
|
||||
from args import args
|
||||
import sys
|
||||
import datetime
|
||||
from assetcache import assetCache, assetGetCache
|
||||
from assetstool.args import args
|
||||
from assetstool.assetcache import assetCache, assetGetCache
|
||||
|
||||
palettes = []
|
||||
|
||||
|
||||
43
tools/assetstool/processscript.py
Normal file
43
tools/assetstool/processscript.py
Normal file
@@ -0,0 +1,43 @@
|
||||
import sys
|
||||
import os
|
||||
from assetstool.args import args
|
||||
from assetstool.assetcache import assetCache, assetGetCache
|
||||
from assetstool.assethelpers import getAssetRelativePath
|
||||
from dusk.defs import fileDefs
|
||||
|
||||
def processScript(asset):
|
||||
cache = assetGetCache(asset['path'])
|
||||
if cache is not None:
|
||||
return cache
|
||||
|
||||
# Load the lua file as a string
|
||||
with open(asset['path'], 'r', encoding='utf-8') as f:
|
||||
luaCode = f.read()
|
||||
|
||||
# TODO: I will precompile or minify the Lua code here in the future
|
||||
|
||||
# Replace all definitions in the code
|
||||
for key, val in fileDefs.items():
|
||||
luaCode = luaCode.replace(key, str(val))
|
||||
|
||||
# Create output Dusk Script File (DSF) data
|
||||
data = ""
|
||||
data += "DSF"
|
||||
data += luaCode
|
||||
|
||||
# Write to relative output file path.
|
||||
relative = getAssetRelativePath(asset['path'])
|
||||
fileNameWithoutExt = os.path.splitext(os.path.basename(asset['path']))[0]
|
||||
outputFileRelative = os.path.join(os.path.dirname(relative), f"{fileNameWithoutExt}.dsf")
|
||||
outputFilePath = os.path.join(args.output_assets, outputFileRelative)
|
||||
os.makedirs(os.path.dirname(outputFilePath), exist_ok=True)
|
||||
with open(outputFilePath, "wb") as f:
|
||||
f.write(data.encode('utf-8'))
|
||||
|
||||
outScript = {
|
||||
'data': data,
|
||||
'path': asset['path'],
|
||||
'files': [ outputFilePath ],
|
||||
'scriptPath': outputFileRelative,
|
||||
}
|
||||
return assetCache(asset['path'], outScript)
|
||||
@@ -1,12 +1,12 @@
|
||||
import json
|
||||
from processimage import processImage
|
||||
import sys
|
||||
from assethelpers import getAssetRelativePath
|
||||
import os
|
||||
import datetime
|
||||
from args import args
|
||||
from xml.etree import ElementTree
|
||||
from assetcache import assetGetCache, assetCache
|
||||
from assetstool.processimage import processImage
|
||||
from assetstool.assethelpers import getAssetRelativePath
|
||||
from assetstool.args import args
|
||||
from assetstool.assetcache import assetGetCache, assetCache
|
||||
|
||||
tilesets = []
|
||||
|
||||
|
||||
66
tools/assettool.py
Normal file
66
tools/assettool.py
Normal file
@@ -0,0 +1,66 @@
|
||||
import sys, os
|
||||
from assetstool.args import args
|
||||
from assetstool.processasset import processAsset
|
||||
from assetstool.processpalette import processPaletteList
|
||||
from assetstool.processtileset import processTilesetList
|
||||
from assetstool.processlanguage import processLanguageList
|
||||
from assetstool.assethelpers import getBuiltAssetsRelativePath
|
||||
import zipfile
|
||||
|
||||
# Parse input file args.
|
||||
inputAssets = []
|
||||
for inputArg in args.input:
|
||||
files = inputArg.split('$')
|
||||
for file in files:
|
||||
if str(file).strip() == '':
|
||||
continue
|
||||
|
||||
pieces = file.split('#')
|
||||
|
||||
if len(pieces) < 2:
|
||||
print(f"Error: Invalid input asset format '{file}'. Expected format: type#path[#option1%option2...]")
|
||||
sys.exit(1)
|
||||
|
||||
options = {}
|
||||
if len(pieces) > 2:
|
||||
optionParts = pieces[2].split('%')
|
||||
for part in optionParts:
|
||||
partSplit = part.split('=')
|
||||
|
||||
if len(partSplit) < 1:
|
||||
continue
|
||||
if len(partSplit) == 2:
|
||||
options[partSplit[0]] = partSplit[1]
|
||||
else:
|
||||
options[partSplit[0]] = True
|
||||
|
||||
inputAssets.append({
|
||||
'type': pieces[0],
|
||||
'path': pieces[1],
|
||||
'options': options
|
||||
})
|
||||
|
||||
if not inputAssets:
|
||||
print("Error: No input assets provided.")
|
||||
sys.exit(1)
|
||||
|
||||
# Process each asset.
|
||||
files = []
|
||||
for asset in inputAssets:
|
||||
asset = processAsset(asset)
|
||||
files.extend(asset['files'])
|
||||
|
||||
# Generate additional files
|
||||
files.extend(processLanguageList()['files'])
|
||||
|
||||
# Take assets and add to a zip archive.
|
||||
outputFileName = args.output_file
|
||||
print(f"Creating output file: {outputFileName}")
|
||||
with zipfile.ZipFile(outputFileName, 'w') as zipf:
|
||||
for file in files:
|
||||
relativeOutputPath = getBuiltAssetsRelativePath(file)
|
||||
zipf.write(file, arcname=relativeOutputPath)
|
||||
|
||||
# Generate additional headers.
|
||||
processPaletteList()
|
||||
processTilesetList()
|
||||
158
tools/dusk/chunk.py
Normal file
158
tools/dusk/chunk.py
Normal file
@@ -0,0 +1,158 @@
|
||||
import json
|
||||
import os
|
||||
from dusk.event import Event
|
||||
from dusk.defs import CHUNK_WIDTH, CHUNK_HEIGHT, CHUNK_DEPTH, CHUNK_VERTEX_COUNT_MAX, TILE_SHAPE_NULL
|
||||
from dusk.tile import Tile
|
||||
from dusk.entity import Entity
|
||||
from dusk.region import Region
|
||||
from editortool.map.vertexbuffer import VertexBuffer
|
||||
from OpenGL.GL import *
|
||||
|
||||
class Chunk:
|
||||
def __init__(self, map, x, y, z):
|
||||
self.map = map
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.z = z
|
||||
self.current = {}
|
||||
self.original = {}
|
||||
self.entities = {}
|
||||
self.regions = {}
|
||||
self.onChunkData = Event()
|
||||
self.dirty = False
|
||||
|
||||
self.tiles = {}
|
||||
self.vertexBuffer = VertexBuffer()
|
||||
|
||||
# Test Region
|
||||
region = self.regions[0] = Region(self)
|
||||
region.minX = 0
|
||||
region.minY = 0
|
||||
region.minZ = 0
|
||||
region.maxX = 32
|
||||
region.maxY = 32
|
||||
region.maxZ = 32
|
||||
region.updateVertexs()
|
||||
|
||||
# Gen tiles.
|
||||
tileIndex = 0
|
||||
for tz in range(CHUNK_DEPTH):
|
||||
for ty in range(CHUNK_HEIGHT):
|
||||
for tx in range(CHUNK_WIDTH):
|
||||
self.tiles[tileIndex] = Tile(self, tx, ty, tz, tileIndex)
|
||||
tileIndex += 1
|
||||
|
||||
# Update vertices
|
||||
self.tileUpdateVertices()
|
||||
|
||||
def reload(self, newX, newY, newZ):
|
||||
self.x = newX
|
||||
self.y = newY
|
||||
self.z = newZ
|
||||
self.entities = {}
|
||||
for tile in self.tiles.values():
|
||||
tile.chunkReload(newX, newY, newZ)
|
||||
self.load()
|
||||
|
||||
def tileUpdateVertices(self):
|
||||
self.vertexBuffer.clear()
|
||||
for tile in self.tiles.values():
|
||||
tile.buffer(self.vertexBuffer)
|
||||
self.vertexBuffer.buildData()
|
||||
|
||||
def load(self):
|
||||
fname = self.getFilename()
|
||||
if not fname or not os.path.exists(fname):
|
||||
self.new()
|
||||
return
|
||||
try:
|
||||
with open(fname, 'r') as f:
|
||||
data = json.load(f)
|
||||
|
||||
if not 'shapes' in data:
|
||||
data['shapes'] = []
|
||||
|
||||
# For each tile.
|
||||
for tile in self.tiles.values():
|
||||
tile.load(data)
|
||||
|
||||
# For each entity.
|
||||
self.entities = {}
|
||||
if 'entities' in data:
|
||||
for id, entData in enumerate(data['entities']):
|
||||
ent = Entity(self)
|
||||
ent.load(entData)
|
||||
self.entities[id] = ent
|
||||
|
||||
self.tileUpdateVertices()
|
||||
self.dirty = False
|
||||
self.onChunkData.invoke(self)
|
||||
self.map.onEntityData.invoke()
|
||||
except Exception as e:
|
||||
raise RuntimeError(f"Failed to load chunk file: {e}")
|
||||
|
||||
def save(self):
|
||||
if not self.isDirty():
|
||||
return
|
||||
|
||||
dataOut = {
|
||||
'shapes': [],
|
||||
'entities': []
|
||||
}
|
||||
|
||||
for tile in self.tiles.values():
|
||||
dataOut['shapes'].append(tile.shape)
|
||||
|
||||
for ent in self.entities.values():
|
||||
entData = {}
|
||||
ent.save(entData)
|
||||
dataOut['entities'].append(entData)
|
||||
|
||||
fname = self.getFilename()
|
||||
if not fname:
|
||||
raise ValueError("No filename specified for saving chunk.")
|
||||
try:
|
||||
with open(fname, 'w') as f:
|
||||
json.dump(dataOut, f)
|
||||
self.dirty = False
|
||||
self.onChunkData.invoke(self)
|
||||
except Exception as e:
|
||||
raise RuntimeError(f"Failed to save chunk file: {e}")
|
||||
|
||||
def new(self):
|
||||
for tile in self.tiles.values():
|
||||
tile.shape = TILE_SHAPE_NULL
|
||||
|
||||
self.tileUpdateVertices()
|
||||
self.dirty = False
|
||||
self.onChunkData.invoke(self)
|
||||
|
||||
def isDirty(self):
|
||||
return self.dirty
|
||||
|
||||
def getFilename(self):
|
||||
if not self.map or not hasattr(self.map, 'getMapDirectory'):
|
||||
return None
|
||||
dir_path = self.map.getMapDirectory()
|
||||
if dir_path is None:
|
||||
return None
|
||||
return f"{dir_path}/{self.x}_{self.y}_{self.z}.json"
|
||||
|
||||
def draw(self):
|
||||
self.vertexBuffer.draw()
|
||||
|
||||
def addEntity(self, localX=0, localY=0, localZ=0):
|
||||
ent = Entity(self, localX, localY, localZ)
|
||||
self.entities[len(self.entities)] = ent
|
||||
self.map.onEntityData.invoke()
|
||||
self.dirty = True
|
||||
return ent
|
||||
|
||||
def removeEntity(self, entity):
|
||||
for key, val in list(self.entities.items()):
|
||||
if val == entity:
|
||||
del self.entities[key]
|
||||
self.map.onEntityData.invoke()
|
||||
self.dirty = True
|
||||
return True
|
||||
return False
|
||||
49
tools/dusk/defs.py
Normal file
49
tools/dusk/defs.py
Normal file
@@ -0,0 +1,49 @@
|
||||
from dotenv import load_dotenv, dotenv_values
|
||||
import os
|
||||
import sys
|
||||
|
||||
current_file_path = os.path.abspath(__file__)
|
||||
duskDefsPath = os.path.join(os.path.dirname(current_file_path), "..", "..", "src", "duskdefs.env")
|
||||
|
||||
# Ensure the .env file exists
|
||||
if not os.path.isfile(duskDefsPath):
|
||||
print(f"Error: .env file not found at {duskDefsPath}")
|
||||
sys.exit(1)
|
||||
|
||||
load_dotenv(dotenv_path=duskDefsPath)
|
||||
defs = {key: os.getenv(key) for key in os.environ.keys()}
|
||||
|
||||
fileDefs = dotenv_values(dotenv_path=duskDefsPath)
|
||||
|
||||
# Parsed out definitions
|
||||
CHUNK_WIDTH = int(defs.get('CHUNK_WIDTH'))
|
||||
CHUNK_HEIGHT = int(defs.get('CHUNK_HEIGHT'))
|
||||
CHUNK_DEPTH = int(defs.get('CHUNK_DEPTH'))
|
||||
CHUNK_TILE_COUNT = CHUNK_WIDTH * CHUNK_HEIGHT * CHUNK_DEPTH
|
||||
CHUNK_VERTEX_COUNT_MAX = int(defs.get('CHUNK_VERTEX_COUNT_MAX'))
|
||||
|
||||
TILE_WIDTH = float(defs.get('TILE_WIDTH'))
|
||||
TILE_HEIGHT = float(defs.get('TILE_HEIGHT'))
|
||||
TILE_DEPTH = float(defs.get('TILE_DEPTH'))
|
||||
|
||||
RPG_CAMERA_PIXELS_PER_UNIT = float(defs.get('RPG_CAMERA_PIXELS_PER_UNIT'))
|
||||
RPG_CAMERA_Z_OFFSET = float(defs.get('RPG_CAMERA_Z_OFFSET'))
|
||||
RPG_CAMERA_FOV = float(defs.get('RPG_CAMERA_FOV'))
|
||||
|
||||
MAP_WIDTH = 5
|
||||
MAP_HEIGHT = 5
|
||||
MAP_DEPTH = 3
|
||||
MAP_CHUNK_COUNT = MAP_WIDTH * MAP_HEIGHT * MAP_DEPTH
|
||||
|
||||
TILE_SHAPES = {}
|
||||
for key in defs.keys():
|
||||
if key.startswith('TILE_SHAPE_'):
|
||||
globals()[key] = int(defs.get(key))
|
||||
TILE_SHAPES[key] = int(defs.get(key))
|
||||
|
||||
ENTITY_TYPES = {}
|
||||
for key in defs.keys():
|
||||
if key.startswith('ENTITY_TYPE_'):
|
||||
globals()[key] = int(defs.get(key))
|
||||
if key != 'ENTITY_TYPE_COUNT':
|
||||
ENTITY_TYPES[key] = int(defs.get(key))
|
||||
90
tools/dusk/entity.py
Normal file
90
tools/dusk/entity.py
Normal file
@@ -0,0 +1,90 @@
|
||||
from dusk.defs import ENTITY_TYPE_NULL, ENTITY_TYPE_NPC, CHUNK_WIDTH, CHUNK_HEIGHT, CHUNK_DEPTH, TILE_WIDTH, TILE_HEIGHT, TILE_DEPTH
|
||||
from editortool.map.vertexbuffer import VertexBuffer
|
||||
|
||||
class Entity:
|
||||
def __init__(self, chunk, localX=0, localY=0, localZ=0):
|
||||
self.type = ENTITY_TYPE_NPC
|
||||
self.name = "Unititled"
|
||||
self.localX = localX % CHUNK_WIDTH
|
||||
self.localY = localY % CHUNK_HEIGHT
|
||||
self.localZ = localZ % CHUNK_DEPTH
|
||||
|
||||
self.chunk = chunk
|
||||
self.vertexBuffer = VertexBuffer()
|
||||
pass
|
||||
|
||||
def load(self, obj):
|
||||
self.type = obj.get('type', ENTITY_TYPE_NULL)
|
||||
self.localX = obj.get('x', 0)
|
||||
self.localY = obj.get('y', 0)
|
||||
self.localZ = obj.get('z', 0)
|
||||
self.name = obj.get('name', "Untitled")
|
||||
pass
|
||||
|
||||
def save(self, obj):
|
||||
obj['type'] = self.type
|
||||
obj['name'] = self.name
|
||||
obj['x'] = self.localX
|
||||
obj['y'] = self.localY
|
||||
obj['z'] = self.localZ
|
||||
pass
|
||||
|
||||
def setType(self, entityType):
|
||||
if self.type == entityType:
|
||||
return
|
||||
self.type = entityType
|
||||
self.chunk.dirty = True
|
||||
self.chunk.map.onEntityData.invoke()
|
||||
|
||||
def setName(self, name):
|
||||
if self.name == name:
|
||||
return
|
||||
self.name = name
|
||||
self.chunk.dirty = True
|
||||
self.chunk.map.onEntityData.invoke()
|
||||
|
||||
def draw(self):
|
||||
self.vertexBuffer.clear()
|
||||
|
||||
startX = (self.chunk.x * CHUNK_WIDTH + self.localX) * TILE_WIDTH
|
||||
startY = (self.chunk.y * CHUNK_HEIGHT + self.localY) * TILE_HEIGHT
|
||||
startZ = (self.chunk.z * CHUNK_DEPTH + self.localZ) * TILE_DEPTH
|
||||
w = TILE_WIDTH
|
||||
h = TILE_HEIGHT
|
||||
d = TILE_DEPTH
|
||||
|
||||
# Center
|
||||
startX -= w / 2
|
||||
startY -= h / 2
|
||||
startZ -= d / 2
|
||||
|
||||
# Offset upwards a little
|
||||
startZ += 1
|
||||
|
||||
# Buffer simple quad at current position (need 6 positions)
|
||||
self.vertexBuffer.vertices = [
|
||||
startX, startY, startZ,
|
||||
startX + w, startY, startZ,
|
||||
startX + w, startY + h, startZ,
|
||||
startX, startY, startZ,
|
||||
startX + w, startY + h, startZ,
|
||||
startX, startY + h, startZ,
|
||||
]
|
||||
self.vertexBuffer.colors = [
|
||||
1.0, 0.0, 1.0, 1.0,
|
||||
1.0, 0.0, 1.0, 1.0,
|
||||
1.0, 0.0, 1.0, 1.0,
|
||||
1.0, 0.0, 1.0, 1.0,
|
||||
1.0, 0.0, 1.0, 1.0,
|
||||
1.0, 0.0, 1.0, 1.0,
|
||||
]
|
||||
self.vertexBuffer.uvs = [
|
||||
0.0, 0.0,
|
||||
1.0, 0.0,
|
||||
1.0, 1.0,
|
||||
0.0, 0.0,
|
||||
1.0, 1.0,
|
||||
0.0, 1.0,
|
||||
]
|
||||
self.vertexBuffer.buildData()
|
||||
self.vertexBuffer.draw()
|
||||
18
tools/dusk/event.py
Normal file
18
tools/dusk/event.py
Normal file
@@ -0,0 +1,18 @@
|
||||
class Event:
|
||||
def __init__(self):
|
||||
self._subscribers = []
|
||||
|
||||
def sub(self, callback):
|
||||
"""Subscribe a callback to the event."""
|
||||
if callback not in self._subscribers:
|
||||
self._subscribers.append(callback)
|
||||
|
||||
def unsub(self, callback):
|
||||
"""Unsubscribe a callback from the event."""
|
||||
if callback in self._subscribers:
|
||||
self._subscribers.remove(callback)
|
||||
|
||||
def invoke(self, *args, **kwargs):
|
||||
"""Invoke all subscribers with the given arguments."""
|
||||
for callback in self._subscribers:
|
||||
callback(*args, **kwargs)
|
||||
252
tools/dusk/map.py
Normal file
252
tools/dusk/map.py
Normal file
@@ -0,0 +1,252 @@
|
||||
import json
|
||||
from dusk.event import Event
|
||||
from PyQt5.QtWidgets import QFileDialog, QMessageBox
|
||||
from PyQt5.QtCore import QTimer
|
||||
import os
|
||||
from dusk.chunk import Chunk
|
||||
from dusk.defs import MAP_WIDTH, MAP_HEIGHT, MAP_DEPTH, CHUNK_WIDTH, CHUNK_HEIGHT, CHUNK_DEPTH
|
||||
import traceback
|
||||
|
||||
MAP_DEFAULT_PATH = os.path.join(os.path.dirname(__file__), '../../assets/map/')
|
||||
EDITOR_CONFIG_PATH = os.path.join(os.path.dirname(__file__), '.editor')
|
||||
|
||||
class Map:
|
||||
def __init__(self, parent):
|
||||
self.parent = parent
|
||||
self.data = {}
|
||||
self.dataOriginal = {}
|
||||
self.position = [None, None, None] # x, y, z
|
||||
self.topLeftX = None
|
||||
self.topLeftY = None
|
||||
self.topLeftZ = None
|
||||
self.chunks = {}
|
||||
self.onMapData = Event()
|
||||
self.onPositionChange = Event()
|
||||
self.onEntityData = Event()
|
||||
self.mapFileName = None
|
||||
self.lastFile = None
|
||||
self.firstLoad = True
|
||||
|
||||
index = 0
|
||||
for x in range(MAP_WIDTH):
|
||||
for y in range(MAP_HEIGHT):
|
||||
for z in range(MAP_DEPTH):
|
||||
self.chunks[index] = Chunk(self, x, y, z)
|
||||
index += 1
|
||||
|
||||
# Only in editor instances:
|
||||
self.moveTo(0, 0, 0)
|
||||
if parent is not None:
|
||||
QTimer.singleShot(16, self.loadLastFile)
|
||||
|
||||
def loadLastFile(self):
|
||||
if not os.path.exists(EDITOR_CONFIG_PATH):
|
||||
return
|
||||
try:
|
||||
with open(EDITOR_CONFIG_PATH, 'r') as f:
|
||||
config = json.load(f)
|
||||
lastFile = config.get('lastFile')
|
||||
lastPosition = config.get('lastPosition')
|
||||
leftPanelIndex = config.get('leftPanelIndex')
|
||||
if lastFile and os.path.exists(lastFile):
|
||||
self.load(lastFile)
|
||||
if lastPosition and isinstance(lastPosition, list) and len(lastPosition) == 3:
|
||||
self.moveTo(*lastPosition)
|
||||
if leftPanelIndex is not None:
|
||||
self.parent.leftPanel.tabs.setCurrentIndex(leftPanelIndex)
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
|
||||
def updateEditorConfig(self):
|
||||
if self.parent is None:
|
||||
return
|
||||
try:
|
||||
mapFileName = self.getMapFilename()
|
||||
config = {
|
||||
'lastFile': mapFileName if mapFileName else "",
|
||||
'lastPosition': self.position,
|
||||
'leftPanelIndex': self.parent.leftPanel.tabs.currentIndex()
|
||||
}
|
||||
config_dir = os.path.dirname(EDITOR_CONFIG_PATH)
|
||||
if not os.path.exists(config_dir):
|
||||
os.makedirs(config_dir, exist_ok=True)
|
||||
with open(EDITOR_CONFIG_PATH, 'w') as f:
|
||||
json.dump(config, f, indent=2)
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
|
||||
def newFile(self):
|
||||
self.data = {}
|
||||
self.dataOriginal = {}
|
||||
self.mapFileName = None
|
||||
self.lastFile = None
|
||||
for chunk in self.chunks.values():
|
||||
chunk.new()
|
||||
self.moveTo(0, 0, 0)
|
||||
self.onMapData.invoke(self.data)
|
||||
self.updateEditorConfig()
|
||||
|
||||
def save(self, fname=None):
|
||||
if not self.getMapFilename() and fname is None:
|
||||
filePath, _ = QFileDialog.getSaveFileName(None, "Save Map File", MAP_DEFAULT_PATH, "Map Files (*.json)")
|
||||
if not filePath:
|
||||
return
|
||||
self.mapFileName = filePath
|
||||
if fname:
|
||||
self.mapFileName = fname
|
||||
try:
|
||||
with open(self.getMapFilename(), 'w') as f:
|
||||
json.dump(self.data, f, indent=2)
|
||||
self.dataOriginal = json.loads(json.dumps(self.data)) # Deep copy
|
||||
for chunk in self.chunks.values():
|
||||
chunk.save()
|
||||
self.updateEditorConfig()
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
QMessageBox.critical(None, "Save Error", f"Failed to save map file:\n{e}")
|
||||
|
||||
def load(self, fileName):
|
||||
try:
|
||||
with open(fileName, 'r') as f:
|
||||
self.data = json.load(f)
|
||||
self.mapFileName = fileName
|
||||
self.dataOriginal = json.loads(json.dumps(self.data)) # Deep copy
|
||||
for chunk in self.chunks.values():
|
||||
chunk.load()
|
||||
self.onMapData.invoke(self.data)
|
||||
self.updateEditorConfig()
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
QMessageBox.critical(None, "Load Error", f"Failed to load map file:\n{e}")
|
||||
|
||||
def isMapFileDirty(self):
|
||||
return json.dumps(self.data, sort_keys=True) != json.dumps(self.dataOriginal, sort_keys=True)
|
||||
|
||||
def isDirty(self):
|
||||
return self.isMapFileDirty() or self.anyChunksDirty()
|
||||
|
||||
def getMapFilename(self):
|
||||
return self.mapFileName if self.mapFileName and os.path.exists(self.mapFileName) else None
|
||||
|
||||
def getMapDirectory(self):
|
||||
fname = self.getMapFilename()
|
||||
if not fname or not fname.endswith('.json'):
|
||||
return None
|
||||
return fname[:-5] # Remove '.json' extension
|
||||
|
||||
def anyChunksDirty(self):
|
||||
for chunk in self.chunks.values():
|
||||
if chunk.isDirty():
|
||||
return True
|
||||
return False
|
||||
|
||||
def moveTo(self, x, y, z):
|
||||
if self.position == [x, y, z]:
|
||||
return
|
||||
|
||||
# We need to decide if the chunks should be unloaded here or not.
|
||||
newTopLeftChunkX = x // CHUNK_WIDTH - (MAP_WIDTH // 2)
|
||||
newTopLeftChunkY = y // CHUNK_HEIGHT - (MAP_HEIGHT // 2)
|
||||
newTopLeftChunkZ = z // CHUNK_DEPTH - (MAP_DEPTH // 2)
|
||||
|
||||
if (newTopLeftChunkX != self.topLeftX or
|
||||
newTopLeftChunkY != self.topLeftY or
|
||||
newTopLeftChunkZ != self.topLeftZ):
|
||||
|
||||
chunksToUnload = []
|
||||
chunksToKeep = []
|
||||
for chunk in self.chunks.values():
|
||||
chunkWorldX = chunk.x
|
||||
chunkWorldY = chunk.y
|
||||
chunkWorldZ = chunk.z
|
||||
if (chunkWorldX < newTopLeftChunkX or
|
||||
chunkWorldX >= newTopLeftChunkX + MAP_WIDTH or
|
||||
chunkWorldY < newTopLeftChunkY or
|
||||
chunkWorldY >= newTopLeftChunkY + MAP_HEIGHT or
|
||||
chunkWorldZ < newTopLeftChunkZ or
|
||||
chunkWorldZ >= newTopLeftChunkZ + MAP_DEPTH):
|
||||
chunksToUnload.append(chunk)
|
||||
else:
|
||||
chunksToKeep.append(chunk)
|
||||
|
||||
# Unload chunks that are out of the new bounds.
|
||||
for chunk in chunksToUnload:
|
||||
if chunk.isDirty():
|
||||
print(f"Can't move map, some chunks are dirty: ({chunk.x}, {chunk.y}, {chunk.z})")
|
||||
return
|
||||
|
||||
# Now we can safely unload the chunks.
|
||||
chunkIndex = 0
|
||||
newChunks = {}
|
||||
for chunk in chunksToKeep:
|
||||
newChunks[chunkIndex] = chunk
|
||||
chunkIndex += 1
|
||||
|
||||
for xPos in range(newTopLeftChunkX, newTopLeftChunkX + MAP_WIDTH):
|
||||
for yPos in range(newTopLeftChunkY, newTopLeftChunkY + MAP_HEIGHT):
|
||||
for zPos in range(newTopLeftChunkZ, newTopLeftChunkZ + MAP_DEPTH):
|
||||
# Check if we already have this chunk.
|
||||
found = False
|
||||
for chunk in chunksToKeep:
|
||||
if chunk.x == xPos and chunk.y == yPos and chunk.z == zPos:
|
||||
found = True
|
||||
break
|
||||
if not found:
|
||||
# Create a new chunk.
|
||||
newChunk = chunksToUnload.pop()
|
||||
newChunk.reload(xPos, yPos, zPos)
|
||||
newChunks[chunkIndex] = newChunk
|
||||
chunkIndex += 1
|
||||
|
||||
self.chunks = newChunks
|
||||
self.topLeftX = newTopLeftChunkX
|
||||
self.topLeftY = newTopLeftChunkY
|
||||
self.topLeftZ = newTopLeftChunkZ
|
||||
|
||||
self.position = [x, y, z]
|
||||
self.onPositionChange.invoke(self.position)
|
||||
if not self.firstLoad:
|
||||
self.updateEditorConfig()
|
||||
self.firstLoad = False
|
||||
|
||||
def moveRelative(self, x, y, z):
|
||||
self.moveTo(
|
||||
self.position[0] + x,
|
||||
self.position[1] + y,
|
||||
self.position[2] + z
|
||||
)
|
||||
|
||||
def draw(self):
|
||||
for chunk in self.chunks.values():
|
||||
chunk.draw()
|
||||
|
||||
for chunk in self.chunks.values():
|
||||
for entity in chunk.entities.values():
|
||||
entity.draw()
|
||||
|
||||
# Only render on Region tab
|
||||
if self.parent.leftPanel.tabs.currentWidget() == self.parent.leftPanel.regionPanel:
|
||||
for chunk in self.chunks.values():
|
||||
for region in chunk.regions.values():
|
||||
region.draw()
|
||||
|
||||
def getChunkAtWorldPos(self, x, y, z):
|
||||
chunkX = x // CHUNK_WIDTH
|
||||
chunkY = y // CHUNK_HEIGHT
|
||||
chunkZ = z // CHUNK_DEPTH
|
||||
for chunk in self.chunks.values():
|
||||
if chunk.x == chunkX and chunk.y == chunkY and chunk.z == chunkZ:
|
||||
return chunk
|
||||
return None
|
||||
|
||||
def getTileAtWorldPos(self, x, y, z):
|
||||
chunk = self.getChunkAtWorldPos(x, y, z)
|
||||
if not chunk:
|
||||
print("No chunk found at position:", (x, y, z))
|
||||
return None
|
||||
|
||||
tileX = x % CHUNK_WIDTH
|
||||
tileY = y % CHUNK_HEIGHT
|
||||
tileZ = z % CHUNK_DEPTH
|
||||
tileIndex = tileX + tileY * CHUNK_WIDTH + tileZ * CHUNK_WIDTH * CHUNK_HEIGHT
|
||||
return chunk.tiles.get(tileIndex)
|
||||
141
tools/dusk/region.py
Normal file
141
tools/dusk/region.py
Normal file
@@ -0,0 +1,141 @@
|
||||
from dusk.defs import CHUNK_WIDTH, CHUNK_HEIGHT, CHUNK_DEPTH, TILE_WIDTH, TILE_HEIGHT, TILE_DEPTH
|
||||
from editortool.map.vertexbuffer import VertexBuffer
|
||||
from OpenGL.GL import *
|
||||
from OpenGL.GLU import *
|
||||
|
||||
class Region:
|
||||
def __init__(self, chunk):
|
||||
self.minX = 0
|
||||
self.minY = 0
|
||||
self.minZ = 0
|
||||
self.maxX = 0
|
||||
self.maxY = 0
|
||||
self.maxZ = 0
|
||||
self.chunk = chunk
|
||||
self.vertexBuffer = VertexBuffer()
|
||||
self.color = (1.0, 0.0, 0.0)
|
||||
self.updateVertexs()
|
||||
pass
|
||||
|
||||
def updateVertexs(self):
|
||||
# Draw a quad, semi transparent with solid outlines
|
||||
vminX = (self.minX * CHUNK_WIDTH) * TILE_WIDTH
|
||||
vminY = (self.minY * CHUNK_HEIGHT) * TILE_HEIGHT
|
||||
vminZ = (self.minZ * CHUNK_DEPTH) * TILE_DEPTH
|
||||
vmaxX = (self.maxX * CHUNK_WIDTH) * TILE_WIDTH
|
||||
vmaxY = (self.maxY * CHUNK_HEIGHT) * TILE_HEIGHT
|
||||
vmaxZ = (self.maxZ * CHUNK_DEPTH) * TILE_DEPTH
|
||||
alpha = 0.25
|
||||
|
||||
# Move back half a tile width
|
||||
vminX -= TILE_WIDTH / 2
|
||||
vmaxX -= TILE_WIDTH / 2
|
||||
vminY -= TILE_HEIGHT / 2
|
||||
vmaxY -= TILE_HEIGHT / 2
|
||||
vminZ -= TILE_DEPTH / 2
|
||||
vmaxZ -= TILE_DEPTH / 2
|
||||
|
||||
# Cube (6 verts per face)
|
||||
self.vertexBuffer.vertices = [
|
||||
# Front face
|
||||
vminX, vminY, vmaxZ,
|
||||
vmaxX, vminY, vmaxZ,
|
||||
vmaxX, vmaxY, vmaxZ,
|
||||
vminX, vminY, vmaxZ,
|
||||
vmaxX, vmaxY, vmaxZ,
|
||||
vminX, vmaxY, vmaxZ,
|
||||
|
||||
# Back face
|
||||
vmaxX, vminY, vminZ,
|
||||
vminX, vminY, vminZ,
|
||||
vminX, vmaxY, vminZ,
|
||||
vmaxX, vminY, vminZ,
|
||||
vminX, vmaxY, vminZ,
|
||||
vmaxX, vmaxY, vminZ,
|
||||
|
||||
# Left face
|
||||
vminX, vminY, vminZ,
|
||||
vminX, vminY, vmaxZ,
|
||||
vminX, vmaxY, vmaxZ,
|
||||
vminX, vminY, vminZ,
|
||||
vminX, vmaxY, vmaxZ,
|
||||
vminX, vmaxY, vminZ,
|
||||
|
||||
# Right face
|
||||
vmaxX, vminY, vmaxZ,
|
||||
vmaxX, vminY, vminZ,
|
||||
vmaxX, vmaxY, vminZ,
|
||||
vmaxX, vminY, vmaxZ,
|
||||
vmaxX, vmaxY, vminZ,
|
||||
vmaxX, vmaxY, vmaxZ,
|
||||
|
||||
# Top face
|
||||
vminX, vmaxY, vmaxZ,
|
||||
vmaxX, vmaxY, vmaxZ,
|
||||
vmaxX, vmaxY, vminZ,
|
||||
vminX, vmaxY, vmaxZ,
|
||||
vmaxX, vmaxY, vminZ,
|
||||
vminX, vmaxY, vminZ,
|
||||
|
||||
# Bottom face
|
||||
vminX, vminY, vminZ,
|
||||
vmaxX, vminY, vminZ,
|
||||
vmaxX, vminY, vmaxZ,
|
||||
vminX, vminY, vminZ,
|
||||
vmaxX, vminY, vmaxZ,
|
||||
vminX, vminY, vmaxZ,
|
||||
]
|
||||
|
||||
self.vertexBuffer.colors = [
|
||||
# Front face
|
||||
self.color[0], self.color[1], self.color[2], alpha,
|
||||
self.color[0], self.color[1], self.color[2], alpha,
|
||||
self.color[0], self.color[1], self.color[2], alpha,
|
||||
self.color[0], self.color[1], self.color[2], alpha,
|
||||
self.color[0], self.color[1], self.color[2], alpha,
|
||||
self.color[0], self.color[1], self.color[2], alpha,
|
||||
|
||||
# Back face
|
||||
self.color[0], self.color[1], self.color[2], alpha,
|
||||
self.color[0], self.color[1], self.color[2], alpha,
|
||||
self.color[0], self.color[1], self.color[2], alpha,
|
||||
self.color[0], self.color[1], self.color[2], alpha,
|
||||
self.color[0], self.color[1], self.color[2], alpha,
|
||||
self.color[0], self.color[1], self.color[2], alpha,
|
||||
|
||||
# Left face
|
||||
self.color[0], self.color[1], self.color[2], alpha,
|
||||
self.color[0], self.color[1], self.color[2], alpha,
|
||||
self.color[0], self.color[1], self.color[2], alpha,
|
||||
self.color[0], self.color[1], self.color[2], alpha,
|
||||
self.color[0], self.color[1], self.color[2], alpha,
|
||||
self.color[0], self.color[1], self.color[2], alpha,
|
||||
|
||||
# Right face
|
||||
self.color[0], self.color[1], self.color[2], alpha,
|
||||
self.color[0], self.color[1], self.color[2], alpha,
|
||||
self.color[0], self.color[1], self.color[2], alpha,
|
||||
self.color[0], self.color[1], self.color[2], alpha,
|
||||
self.color[0], self.color[1], self.color[2], alpha,
|
||||
self.color[0], self.color[1], self.color[2], alpha,
|
||||
|
||||
# Top face
|
||||
self.color[0], self.color[1], self.color[2], alpha,
|
||||
self.color[0], self.color[1], self.color[2], alpha,
|
||||
self.color[0], self.color[1], self.color[2], alpha,
|
||||
self.color[0], self.color[1], self.color[2], alpha,
|
||||
self.color[0], self.color[1], self.color[2], alpha,
|
||||
self.color[0], self.color[1], self.color[2], alpha,
|
||||
|
||||
# Bottom face
|
||||
self.color[0], self.color[1], self.color[2], alpha,
|
||||
self.color[0], self.color[1], self.color[2], alpha,
|
||||
self.color[0], self.color[1], self.color[2], alpha,
|
||||
self.color[0], self.color[1], self.color[2], alpha,
|
||||
self.color[0], self.color[1], self.color[2], alpha,
|
||||
self.color[0], self.color[1], self.color[2], alpha,
|
||||
]
|
||||
self.vertexBuffer.buildData()
|
||||
|
||||
def draw(self):
|
||||
self.vertexBuffer.draw()
|
||||
206
tools/dusk/tile.py
Normal file
206
tools/dusk/tile.py
Normal file
@@ -0,0 +1,206 @@
|
||||
from OpenGL.GL import *
|
||||
from dusk.defs import (
|
||||
TILE_WIDTH, TILE_HEIGHT, TILE_DEPTH,
|
||||
CHUNK_WIDTH, CHUNK_HEIGHT, CHUNK_DEPTH,
|
||||
TILE_SHAPE_NULL, TILE_SHAPE_FLOOR,
|
||||
TILE_SHAPE_RAMP_NORTH, TILE_SHAPE_RAMP_SOUTH,
|
||||
TILE_SHAPE_RAMP_EAST, TILE_SHAPE_RAMP_WEST,
|
||||
TILE_SHAPE_RAMP_SOUTHWEST, TILE_SHAPE_RAMP_SOUTHEAST,
|
||||
TILE_SHAPE_RAMP_NORTHWEST, TILE_SHAPE_RAMP_NORTHEAST
|
||||
)
|
||||
|
||||
def getItem(arr, index, default):
|
||||
if index < len(arr):
|
||||
return arr[index]
|
||||
return default
|
||||
|
||||
class Tile:
|
||||
def __init__(self, chunk, x, y, z, tileIndex):
|
||||
self.shape = TILE_SHAPE_NULL
|
||||
|
||||
self.chunk = chunk
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.z = z
|
||||
self.index = tileIndex
|
||||
|
||||
self.posX = x * TILE_WIDTH + chunk.x * CHUNK_WIDTH * TILE_WIDTH
|
||||
self.posY = y * TILE_HEIGHT + chunk.y * CHUNK_HEIGHT * TILE_HEIGHT
|
||||
self.posZ = z * TILE_DEPTH + chunk.z * CHUNK_DEPTH * TILE_DEPTH
|
||||
|
||||
def chunkReload(self, newX, newY, newZ):
|
||||
self.posX = self.x * TILE_WIDTH + newX * CHUNK_WIDTH * TILE_WIDTH
|
||||
self.posY = self.y * TILE_HEIGHT + newY * CHUNK_HEIGHT * TILE_HEIGHT
|
||||
self.posZ = self.z * TILE_DEPTH + newZ * CHUNK_DEPTH * TILE_DEPTH
|
||||
|
||||
def load(self, chunkData):
|
||||
self.shape = getItem(chunkData['shapes'], self.index, TILE_SHAPE_NULL)
|
||||
|
||||
def setShape(self, shape):
|
||||
if shape == self.shape:
|
||||
return
|
||||
|
||||
self.shape = shape
|
||||
self.chunk.dirty = True
|
||||
self.chunk.tileUpdateVertices()
|
||||
self.chunk.onChunkData.invoke(self.chunk)
|
||||
|
||||
def getBaseTileModel(self):
|
||||
vertices = []
|
||||
indices = []
|
||||
uvs = []
|
||||
colors = []
|
||||
|
||||
if self.shape == TILE_SHAPE_NULL:
|
||||
pass
|
||||
|
||||
elif self.shape == TILE_SHAPE_FLOOR:
|
||||
vertices = [
|
||||
(self.posX, self.posY, self.posZ),
|
||||
(self.posX + TILE_WIDTH, self.posY, self.posZ),
|
||||
(self.posX + TILE_WIDTH, self.posY + TILE_HEIGHT, self.posZ),
|
||||
(self.posX, self.posY + TILE_HEIGHT, self.posZ)
|
||||
]
|
||||
indices = [0, 1, 2, 0, 2, 3]
|
||||
uvs = [ (0, 0), (1, 0), (1, 1), (0, 1) ]
|
||||
colors = [ (255, 255, 255, 255) ] * 4
|
||||
|
||||
elif self.shape == TILE_SHAPE_RAMP_NORTH:
|
||||
vertices = [
|
||||
(self.posX, self.posY, self.posZ + TILE_DEPTH),
|
||||
(self.posX + TILE_WIDTH, self.posY, self.posZ + TILE_DEPTH),
|
||||
(self.posX + TILE_WIDTH, self.posY + TILE_HEIGHT, self.posZ),
|
||||
(self.posX, self.posY + TILE_HEIGHT, self.posZ)
|
||||
]
|
||||
indices = [0, 1, 2, 0, 2, 3]
|
||||
uvs = [ (0, 0), (1, 0), (1, 1), (0, 1) ]
|
||||
colors = [ (255, 0, 0, 255) ] * 4
|
||||
|
||||
elif self.shape == TILE_SHAPE_RAMP_SOUTH:
|
||||
vertices = [
|
||||
(self.posX, self.posY, self.posZ),
|
||||
(self.posX + TILE_WIDTH, self.posY, self.posZ),
|
||||
(self.posX + TILE_WIDTH, self.posY + TILE_HEIGHT, self.posZ + TILE_DEPTH),
|
||||
(self.posX, self.posY + TILE_HEIGHT, self.posZ + TILE_DEPTH)
|
||||
]
|
||||
indices = [0, 1, 2, 0, 2, 3]
|
||||
uvs = [ (0, 0), (1, 0), (1, 1), (0, 1) ]
|
||||
colors = [ (0, 255, 0, 255) ] * 4
|
||||
|
||||
elif self.shape == TILE_SHAPE_RAMP_EAST:
|
||||
vertices = [
|
||||
(self.posX, self.posY, self.posZ),
|
||||
(self.posX + TILE_WIDTH, self.posY, self.posZ + TILE_DEPTH),
|
||||
(self.posX + TILE_WIDTH, self.posY + TILE_HEIGHT, self.posZ + TILE_DEPTH),
|
||||
(self.posX, self.posY + TILE_HEIGHT, self.posZ)
|
||||
]
|
||||
indices = [0, 1, 2, 0, 2, 3]
|
||||
uvs = [ (0, 0), (1, 0), (1, 1), (0, 1) ]
|
||||
colors = [ (0, 0, 255, 255) ] * 4
|
||||
|
||||
elif self.shape == TILE_SHAPE_RAMP_WEST:
|
||||
vertices = [
|
||||
(self.posX, self.posY, self.posZ + TILE_DEPTH),
|
||||
(self.posX + TILE_WIDTH, self.posY, self.posZ),
|
||||
(self.posX + TILE_WIDTH, self.posY + TILE_HEIGHT, self.posZ),
|
||||
(self.posX, self.posY + TILE_HEIGHT, self.posZ + TILE_DEPTH)
|
||||
]
|
||||
indices = [0, 1, 2, 0, 2, 3]
|
||||
uvs = [ (0, 0), (1, 0), (1, 1), (0, 1) ]
|
||||
colors = [ (255, 255, 0, 255) ] * 4
|
||||
|
||||
elif self.shape == TILE_SHAPE_RAMP_SOUTHWEST:
|
||||
vertices = [
|
||||
(self.posX, self.posY, self.posZ + TILE_DEPTH),
|
||||
(self.posX + TILE_WIDTH, self.posY, self.posZ),
|
||||
(self.posX + TILE_WIDTH, self.posY + TILE_HEIGHT, self.posZ + TILE_DEPTH),
|
||||
(self.posX, self.posY + TILE_HEIGHT, self.posZ + TILE_DEPTH)
|
||||
]
|
||||
indices = [0, 1, 2, 0, 2, 3]
|
||||
uvs = [ (0, 0), (1, 0), (1, 1), (0, 1) ]
|
||||
colors = [ (255, 128, 0, 255) ] * 4
|
||||
|
||||
elif self.shape == TILE_SHAPE_RAMP_NORTHWEST:
|
||||
vertices = [
|
||||
(self.posX, self.posY, self.posZ + TILE_DEPTH),
|
||||
(self.posX + TILE_WIDTH, self.posY, self.posZ + TILE_DEPTH),
|
||||
(self.posX + TILE_WIDTH, self.posY + TILE_HEIGHT, self.posZ),
|
||||
(self.posX, self.posY + TILE_HEIGHT, self.posZ + TILE_DEPTH)
|
||||
]
|
||||
indices = [0, 1, 2, 0, 2, 3]
|
||||
uvs = [ (0, 0), (1, 0), (1, 1), (0, 1) ]
|
||||
colors = [ (128, 255, 0, 255) ] * 4
|
||||
|
||||
elif self.shape == TILE_SHAPE_RAMP_NORTHEAST:
|
||||
vertices = [
|
||||
(self.posX, self.posY, self.posZ + TILE_DEPTH),
|
||||
(self.posX + TILE_WIDTH, self.posY, self.posZ + TILE_DEPTH),
|
||||
(self.posX + TILE_WIDTH, self.posY + TILE_HEIGHT, self.posZ + TILE_DEPTH),
|
||||
(self.posX, self.posY + TILE_HEIGHT, self.posZ)
|
||||
]
|
||||
indices = [0, 1, 2, 0, 2, 3]
|
||||
uvs = [ (0, 0), (1, 0), (1, 1), (0, 1) ]
|
||||
colors = [ (0, 255, 128, 255) ] * 4
|
||||
|
||||
elif self.shape == TILE_SHAPE_RAMP_SOUTHEAST:
|
||||
vertices = [
|
||||
(self.posX, self.posY, self.posZ),
|
||||
(self.posX + TILE_WIDTH, self.posY, self.posZ + TILE_DEPTH),
|
||||
(self.posX + TILE_WIDTH, self.posY + TILE_HEIGHT, self.posZ + TILE_DEPTH),
|
||||
(self.posX, self.posY + TILE_HEIGHT, self.posZ + TILE_DEPTH)
|
||||
]
|
||||
indices = [0, 1, 2, 0, 2, 3]
|
||||
uvs = [ (0, 0), (1, 0), (1, 1), (0, 1) ]
|
||||
colors = [ (255, 128, 255, 255) ] * 4
|
||||
|
||||
else:
|
||||
# Solid black cube for unknown shape
|
||||
x0, y0, z0 = self.posX, self.posY, self.posZ
|
||||
x1, y1, z1 = self.posX + TILE_WIDTH, self.posY + TILE_HEIGHT, self.posZ + TILE_DEPTH
|
||||
vertices = [
|
||||
(x0, y0, z0), (x1, y0, z0), (x1, y1, z0), (x0, y1, z0), # bottom
|
||||
(x0, y0, z1), (x1, y0, z1), (x1, y1, z1), (x0, y1, z1) # top
|
||||
]
|
||||
indices = [
|
||||
0,1,2, 0,2,3, # bottom
|
||||
4,5,6, 4,6,7, # top
|
||||
0,1,5, 0,5,4, # front
|
||||
2,3,7, 2,7,6, # back
|
||||
1,2,6, 1,6,5, # right
|
||||
3,0,4, 3,4,7 # left
|
||||
]
|
||||
uvs = [ (0,0) ] * 8
|
||||
colors = [ (0,0,0,255) ] * 8
|
||||
|
||||
return {
|
||||
'vertices': vertices,
|
||||
'indices': indices,
|
||||
'uvs': uvs,
|
||||
'colors': colors
|
||||
}
|
||||
|
||||
def buffer(self, vertexBuffer):
|
||||
if self.shape == TILE_SHAPE_NULL:
|
||||
return
|
||||
|
||||
# New code:
|
||||
baseData = self.getBaseTileModel()
|
||||
|
||||
# Base data is indiced but we need to buffer unindiced data
|
||||
for index in baseData['indices']:
|
||||
verts = baseData['vertices'][index]
|
||||
uv = baseData['uvs'][index]
|
||||
color = baseData['colors'][index]
|
||||
|
||||
vertexBuffer.vertices.extend([
|
||||
verts[0] - (TILE_WIDTH / 2.0),
|
||||
verts[1] - (TILE_HEIGHT / 2.0),
|
||||
verts[2] - (TILE_DEPTH / 2.0)
|
||||
])
|
||||
|
||||
vertexBuffer.colors.extend([
|
||||
color[0] / 255.0,
|
||||
color[1] / 255.0,
|
||||
color[2] / 255.0,
|
||||
color[3] / 255.0
|
||||
])
|
||||
57
tools/editor.py
Executable file
57
tools/editor.py
Executable file
@@ -0,0 +1,57 @@
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
from PyQt5.QtWidgets import (
|
||||
QApplication, QVBoxLayout, QPushButton,
|
||||
QDialog
|
||||
)
|
||||
from OpenGL.GL import *
|
||||
from OpenGL.GLU import *
|
||||
from editortool.maptool import MapWindow
|
||||
from editortool.langtool import LangToolWindow
|
||||
from editortool.cutscenetool import CutsceneToolWindow
|
||||
|
||||
DEFAULT_TOOL = None
|
||||
DEFAULT_TOOL = "map"
|
||||
# DEFAULT_TOOL = "cutscene"
|
||||
|
||||
TOOLS = [
|
||||
("Map Editor", "map", MapWindow),
|
||||
("Language Editor", "language", LangToolWindow),
|
||||
("Cutscene Editor", "cutscene", CutsceneToolWindow),
|
||||
]
|
||||
|
||||
class EditorChoiceDialog(QDialog):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.setWindowTitle("Choose Tool")
|
||||
layout = QVBoxLayout(self)
|
||||
self.selected = None
|
||||
for label, key, win_cls in TOOLS:
|
||||
btn = QPushButton(label)
|
||||
btn.clicked.connect(lambda checked, w=win_cls: self.choose_tool(w))
|
||||
layout.addWidget(btn)
|
||||
|
||||
def choose_tool(self, win_cls):
|
||||
self.selected = win_cls
|
||||
self.accept()
|
||||
|
||||
def get_choice(self):
|
||||
return self.selected
|
||||
|
||||
def main():
|
||||
app = QApplication(sys.argv)
|
||||
tool_map = { key: win_cls for _, key, win_cls in TOOLS }
|
||||
if DEFAULT_TOOL in tool_map:
|
||||
win_cls = tool_map[DEFAULT_TOOL]
|
||||
else:
|
||||
choice_dialog = EditorChoiceDialog()
|
||||
if choice_dialog.exec_() == QDialog.Accepted:
|
||||
win_cls = choice_dialog.get_choice()
|
||||
else:
|
||||
sys.exit(0)
|
||||
win = win_cls()
|
||||
win.show()
|
||||
sys.exit(app.exec_())
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
58
tools/editortool/cutscene/cutsceneitemeditor.py
Normal file
58
tools/editortool/cutscene/cutsceneitemeditor.py
Normal file
@@ -0,0 +1,58 @@
|
||||
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLineEdit, QLabel, QSizePolicy, QComboBox, QHBoxLayout, QSpacerItem
|
||||
from PyQt5.QtCore import Qt, pyqtSignal
|
||||
from .cutscenewait import CutsceneWaitEditor
|
||||
from .cutscenetext import CutsceneTextEditor
|
||||
|
||||
EDITOR_MAP = (
|
||||
( "wait", "Wait", CutsceneWaitEditor ),
|
||||
( "text", "Text", CutsceneTextEditor )
|
||||
)
|
||||
|
||||
class CutsceneItemEditor(QWidget):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.layout = QVBoxLayout(self)
|
||||
self.layout.setAlignment(Qt.AlignTop | Qt.AlignLeft)
|
||||
self.layout.addWidget(QLabel("Item Properties:"))
|
||||
|
||||
rowLayout = QHBoxLayout()
|
||||
rowLayout.setSpacing(8)
|
||||
|
||||
rowLayout.addWidget(QLabel("Name:"))
|
||||
self.nameInput = QLineEdit()
|
||||
rowLayout.addWidget(self.nameInput)
|
||||
|
||||
rowLayout.addWidget(QLabel("Type:"))
|
||||
self.typeDropdown = QComboBox()
|
||||
self.typeDropdown.addItems([typeName for typeKey, typeName, editorClass in EDITOR_MAP])
|
||||
rowLayout.addWidget(self.typeDropdown)
|
||||
self.layout.addLayout(rowLayout)
|
||||
|
||||
self.activeEditor = None
|
||||
|
||||
# Events
|
||||
self.nameInput.textChanged.connect(self.onNameChanged)
|
||||
self.typeDropdown.currentTextChanged.connect(self.onTypeChanged)
|
||||
|
||||
# First load
|
||||
self.onNameChanged(self.nameInput.text())
|
||||
self.onTypeChanged(self.typeDropdown.currentText())
|
||||
|
||||
def onNameChanged(self, nameText):
|
||||
pass
|
||||
|
||||
def onTypeChanged(self, typeText):
|
||||
typeKey = typeText.lower()
|
||||
|
||||
# Remove existing editor
|
||||
if self.activeEditor:
|
||||
self.layout.removeWidget(self.activeEditor)
|
||||
self.activeEditor.deleteLater()
|
||||
self.activeEditor = None
|
||||
|
||||
# Create new editor
|
||||
for key, name, editorClass in EDITOR_MAP:
|
||||
if key == typeKey:
|
||||
self.activeEditor = editorClass()
|
||||
self.layout.addWidget(self.activeEditor)
|
||||
break
|
||||
54
tools/editortool/cutscene/cutscenemenubar.py
Normal file
54
tools/editortool/cutscene/cutscenemenubar.py
Normal file
@@ -0,0 +1,54 @@
|
||||
from PyQt5.QtWidgets import QMenuBar, QAction, QFileDialog, QMessageBox
|
||||
from PyQt5.QtGui import QKeySequence
|
||||
|
||||
class CutsceneMenuBar(QMenuBar):
|
||||
def __init__(self, parent):
|
||||
super().__init__(parent)
|
||||
self.parent = parent
|
||||
fileMenu = self.addMenu("File")
|
||||
|
||||
self.newAction = QAction("New", self)
|
||||
self.newAction.setShortcut(QKeySequence.New)
|
||||
self.newAction.triggered.connect(self.newFile)
|
||||
fileMenu.addAction(self.newAction)
|
||||
|
||||
self.openAction = QAction("Open", self)
|
||||
self.openAction.setShortcut(QKeySequence.Open)
|
||||
self.openAction.triggered.connect(self.openFile)
|
||||
fileMenu.addAction(self.openAction)
|
||||
|
||||
self.saveAction = QAction("Save", self)
|
||||
self.saveAction.setShortcut(QKeySequence.Save)
|
||||
self.saveAction.triggered.connect(self.saveFile)
|
||||
fileMenu.addAction(self.saveAction)
|
||||
|
||||
self.saveAsAction = QAction("Save As", self)
|
||||
self.saveAsAction.setShortcut(QKeySequence.SaveAs)
|
||||
self.saveAsAction.triggered.connect(self.saveFileAs)
|
||||
fileMenu.addAction(self.saveAsAction)
|
||||
|
||||
def newFile(self):
|
||||
self.parent.clearCutscene()
|
||||
|
||||
def openFile(self):
|
||||
path, _ = QFileDialog.getOpenFileName(self.parent, "Open Cutscene File", "", "JSON Files (*.json);;All Files (*)")
|
||||
if not path:
|
||||
return
|
||||
|
||||
# TODO: Load file contents into timeline
|
||||
self.parent.currentFile = path
|
||||
pass
|
||||
|
||||
def saveFile(self):
|
||||
if not self.parent.currentFile:
|
||||
self.saveFileAs()
|
||||
return
|
||||
|
||||
# TODO: Save timeline to self.parent.currentFile
|
||||
pass
|
||||
|
||||
def saveFileAs(self):
|
||||
path, _ = QFileDialog.getSaveFileName(self.parent, "Save Cutscene File As", "", "JSON Files (*.json);;All Files (*)")
|
||||
if path:
|
||||
self.parent.currentFile = path
|
||||
self.saveFile()
|
||||
21
tools/editortool/cutscene/cutscenetext.py
Normal file
21
tools/editortool/cutscene/cutscenetext.py
Normal file
@@ -0,0 +1,21 @@
|
||||
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QTextEdit
|
||||
from PyQt5.QtCore import Qt
|
||||
|
||||
class CutsceneTextEditor(QWidget):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
layout = QVBoxLayout(self)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.setSpacing(0)
|
||||
label = QLabel("Text:")
|
||||
label.setSizePolicy(label.sizePolicy().Expanding, label.sizePolicy().Fixed)
|
||||
layout.addWidget(label)
|
||||
self.textInput = QTextEdit()
|
||||
self.textInput.setSizePolicy(self.textInput.sizePolicy().Expanding, self.textInput.sizePolicy().Expanding)
|
||||
layout.addWidget(self.textInput, stretch=1)
|
||||
|
||||
def setText(self, text):
|
||||
self.textInput.setPlainText(text)
|
||||
|
||||
def getText(self):
|
||||
return self.textInput.toPlainText()
|
||||
20
tools/editortool/cutscene/cutscenewait.py
Normal file
20
tools/editortool/cutscene/cutscenewait.py
Normal file
@@ -0,0 +1,20 @@
|
||||
from PyQt5.QtWidgets import QWidget, QFormLayout, QDoubleSpinBox, QLabel
|
||||
|
||||
class CutsceneWaitEditor(QWidget):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
layout = QFormLayout(self)
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.setSpacing(0)
|
||||
self.waitTimeInput = QDoubleSpinBox()
|
||||
self.waitTimeInput.setMinimum(0.0)
|
||||
self.waitTimeInput.setMaximum(9999.0)
|
||||
self.waitTimeInput.setDecimals(2)
|
||||
self.waitTimeInput.setSingleStep(0.1)
|
||||
layout.addRow(QLabel("Wait Time (seconds):"), self.waitTimeInput)
|
||||
|
||||
def setWaitTime(self, value):
|
||||
self.waitTimeInput.setValue(value)
|
||||
|
||||
def getWaitTime(self):
|
||||
return self.waitTimeInput.value()
|
||||
101
tools/editortool/cutscenetool.py
Normal file
101
tools/editortool/cutscenetool.py
Normal file
@@ -0,0 +1,101 @@
|
||||
from PyQt5.QtWidgets import QMainWindow, QApplication, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QListWidget, QListWidgetItem, QMenuBar, QAction, QFileDialog, QMessageBox
|
||||
from PyQt5.QtGui import QKeySequence
|
||||
from editortool.cutscene.cutsceneitemeditor import CutsceneItemEditor
|
||||
from editortool.cutscene.cutscenemenubar import CutsceneMenuBar
|
||||
import sys
|
||||
|
||||
class CutsceneToolWindow(QMainWindow):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.setWindowTitle("Dusk Cutscene Editor")
|
||||
self.setGeometry(100, 100, 800, 600)
|
||||
self.nextItemNumber = 1 # Track next available number
|
||||
self.currentFile = None
|
||||
self.dirty = False # Track unsaved changes
|
||||
|
||||
# File menu (handled by CutsceneMenuBar)
|
||||
menubar = CutsceneMenuBar(self)
|
||||
self.setMenuBar(menubar)
|
||||
|
||||
# Main layout: horizontal split
|
||||
central = QWidget()
|
||||
mainLayout = QHBoxLayout(central)
|
||||
self.setCentralWidget(central)
|
||||
|
||||
# Timeline
|
||||
leftPanel = QWidget()
|
||||
leftLayout = QVBoxLayout(leftPanel)
|
||||
|
||||
self.timelineList = QListWidget()
|
||||
self.timelineList.setSelectionMode(QListWidget.SingleSelection)
|
||||
leftLayout.addWidget(QLabel("Cutscene Timeline"))
|
||||
leftLayout.addWidget(self.timelineList)
|
||||
|
||||
btnLayout = QHBoxLayout()
|
||||
self.addBtn = QPushButton("Add")
|
||||
self.removeBtn = QPushButton("Remove")
|
||||
btnLayout.addWidget(self.addBtn)
|
||||
btnLayout.addWidget(self.removeBtn)
|
||||
leftLayout.addLayout(btnLayout)
|
||||
mainLayout.addWidget(leftPanel, 2)
|
||||
|
||||
# Property editor
|
||||
self.editorPanel = QWidget()
|
||||
self.editorLayout = QVBoxLayout(self.editorPanel)
|
||||
self.itemEditor = None # Only create when needed
|
||||
mainLayout.addWidget(self.editorPanel, 3)
|
||||
|
||||
# Events
|
||||
self.timelineList.currentItemChanged.connect(self.onItemSelected)
|
||||
self.addBtn.clicked.connect(self.addCutsceneItem)
|
||||
self.removeBtn.clicked.connect(self.removeCutsceneItem)
|
||||
|
||||
def addCutsceneItem(self):
|
||||
name = f"Cutscene item {self.nextItemNumber}"
|
||||
timelineItem = QListWidgetItem(name)
|
||||
self.timelineList.addItem(timelineItem)
|
||||
self.timelineList.setCurrentItem(timelineItem)
|
||||
self.nextItemNumber += 1
|
||||
self.dirty = True
|
||||
|
||||
def removeCutsceneItem(self):
|
||||
row = self.timelineList.currentRow()
|
||||
if row < 0:
|
||||
return
|
||||
self.timelineList.takeItem(row)
|
||||
self.dirty = True
|
||||
|
||||
# Remove editor if nothing selected
|
||||
if self.timelineList.currentItem() is None or self.itemEditor is None:
|
||||
return
|
||||
|
||||
self.editorLayout.removeWidget(self.itemEditor)
|
||||
self.itemEditor.deleteLater()
|
||||
self.itemEditor = None
|
||||
|
||||
def clearCutscene(self):
|
||||
self.timelineList.clear()
|
||||
self.nextItemNumber = 1
|
||||
self.currentFile = None
|
||||
self.dirty = False
|
||||
if self.itemEditor:
|
||||
self.editorLayout.removeWidget(self.itemEditor)
|
||||
|
||||
def onItemSelected(self, current, previous):
|
||||
if current:
|
||||
if not self.itemEditor:
|
||||
self.itemEditor = CutsceneItemEditor()
|
||||
self.editorLayout.addWidget(self.itemEditor)
|
||||
return
|
||||
|
||||
if not self.itemEditor:
|
||||
return
|
||||
self.editorLayout.removeWidget(self.itemEditor)
|
||||
self.itemEditor.deleteLater()
|
||||
self.itemEditor = None
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = QApplication(sys.argv)
|
||||
window = CutsceneToolWindow()
|
||||
window.show()
|
||||
sys.exit(app.exec_())
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user