Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 182428d6d6 | |||
| 8181a28557 | |||
| 88aed11d98 | |||
| 67010592b8 | |||
| dd22d6424a | |||
| e53775b97f | |||
| d326f6c1ac | |||
| 556111c00a | |||
| ebc7110e63 | |||
| af49d1c8bb | |||
| c71dbb1dfc | |||
| 432aa3c4fa | |||
| 5c93503710 | |||
| 0d58050def | |||
| 27bb78209a | |||
| 3201f5a25b | |||
| d3df8a96d3 | |||
| 7ec5266b1f | |||
| e74ff7c805 | |||
| 1d5f3c2959 | |||
| 469034b223 | |||
| f3a394de21 | |||
| cb23d4c11b | |||
| bbf5b706f0 |
@@ -0,0 +1,49 @@
|
|||||||
|
name: Setup devkitPro
|
||||||
|
description: Install devkitPro with GameCube/Wii packages on an Ubuntu runner
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: composite
|
||||||
|
steps:
|
||||||
|
- name: Install apt dependencies
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y \
|
||||||
|
cmake \
|
||||||
|
python3 \
|
||||||
|
python3-pip \
|
||||||
|
python3-polib \
|
||||||
|
python3-pil \
|
||||||
|
python3-dotenv \
|
||||||
|
python3-pyqt5 \
|
||||||
|
python3-opengl \
|
||||||
|
xorriso
|
||||||
|
- name: Install devkitPro pacman
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
sudo ln -sf /proc/self/mounts /etc/mtab
|
||||||
|
wget https://apt.devkitpro.org/install-devkitpro-pacman \
|
||||||
|
-O /tmp/install-devkitpro-pacman
|
||||||
|
echo "=== installer script contents ==="
|
||||||
|
cat /tmp/install-devkitpro-pacman
|
||||||
|
echo "================================="
|
||||||
|
chmod +x /tmp/install-devkitpro-pacman
|
||||||
|
sudo /tmp/install-devkitpro-pacman
|
||||||
|
echo "DEVKITPRO=/opt/devkitpro" >> $GITHUB_ENV
|
||||||
|
echo "DEVKITPPC=/opt/devkitpro/devkitPPC" >> $GITHUB_ENV
|
||||||
|
echo "/opt/devkitpro/tools/bin" >> $GITHUB_PATH
|
||||||
|
echo "/opt/devkitpro/devkitPPC/bin" >> $GITHUB_PATH
|
||||||
|
- name: Install devkitPro GameCube/Wii packages
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
sudo dkp-pacman -S --needed --noconfirm \
|
||||||
|
gamecube-dev \
|
||||||
|
gamecube-sdl2 \
|
||||||
|
ppc-liblzma \
|
||||||
|
ppc-libzip \
|
||||||
|
libogc2 \
|
||||||
|
gamecube-tools \
|
||||||
|
ppc-libmad \
|
||||||
|
ppc-zlib-ng \
|
||||||
|
ppc-bzip2 \
|
||||||
|
ppc-zstd
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
name: Setup pspdev
|
||||||
|
description: Install the pspdev PSP toolchain on an Ubuntu runner
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: composite
|
||||||
|
steps:
|
||||||
|
- name: Install dependencies
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y cmake python3 python3-pip python3-dotenv
|
||||||
|
- name: Install pspdev toolchain
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
wget -q \
|
||||||
|
https://github.com/pspdev/pspdev/releases/latest/download/pspdev-ubuntu-latest-x86_64.tar.gz \
|
||||||
|
-O /tmp/pspdev.tar.gz
|
||||||
|
sudo tar -xzf /tmp/pspdev.tar.gz -C /usr/local
|
||||||
|
echo "PSPDEV=/usr/local/pspdev" >> $GITHUB_ENV
|
||||||
|
echo "/usr/local/pspdev/bin" >> $GITHUB_PATH
|
||||||
+167
-64
@@ -8,23 +8,63 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v4
|
||||||
- name: Set up Docker
|
- name: Install dependencies
|
||||||
uses: docker/setup-docker-action@v5
|
run: |
|
||||||
- name: Run tests in Docker
|
sudo apt-get update
|
||||||
run: ./scripts/test-linux-docker.sh
|
sudo 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 \
|
||||||
|
xz-utils \
|
||||||
|
liblzma-dev \
|
||||||
|
libbz2-dev \
|
||||||
|
zlib1g-dev \
|
||||||
|
git \
|
||||||
|
libssl-dev
|
||||||
|
- name: Run tests
|
||||||
|
run: ./scripts/test-linux.sh
|
||||||
|
|
||||||
build-linux:
|
build-linux:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v4
|
||||||
- name: Set up Docker
|
- name: Install dependencies
|
||||||
uses: docker/setup-docker-action@v5
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo 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 \
|
||||||
|
xz-utils \
|
||||||
|
liblzma-dev \
|
||||||
|
libbz2-dev \
|
||||||
|
zlib1g-dev \
|
||||||
|
git \
|
||||||
|
libssl-dev
|
||||||
- name: Build Linux
|
- name: Build Linux
|
||||||
run: ./scripts/build-linux-docker.sh
|
run: ./scripts/build-linux.sh
|
||||||
- name: Upload Linux binary
|
- name: Upload Linux binary
|
||||||
uses: actions/upload-artifact@v6
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: dusk-linux
|
name: dusk-linux
|
||||||
path: build-linux/Dusk
|
path: build-linux/Dusk
|
||||||
@@ -34,53 +74,72 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v4
|
||||||
- name: Set up Docker
|
- name: Setup pspdev
|
||||||
uses: docker/setup-docker-action@v5
|
uses: ./.github/actions/setup-pspdev
|
||||||
- name: Build psp
|
- name: Build PSP
|
||||||
run: ./scripts/build-psp-docker.sh
|
run: ./scripts/build-psp.sh
|
||||||
- name: Move EBOOT.PBP to Dusk subfolder
|
- name: Move EBOOT.PBP to Dusk subfolder
|
||||||
run: |
|
run: |
|
||||||
mkdir -p ./git-artifcats/Dusk/PSP/GAME/Dusk
|
mkdir -p ./git-artifcats/Dusk/PSP/GAME/Dusk
|
||||||
cp build-psp/EBOOT.PBP ./git-artifcats/Dusk/PSP/GAME/Dusk/EBOOT.PBP
|
cp build-psp/EBOOT.PBP ./git-artifcats/Dusk/PSP/GAME/Dusk/EBOOT.PBP
|
||||||
- name: Upload psp binary
|
- name: Upload PSP binary
|
||||||
uses: actions/upload-artifact@v6
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: dusk-psp
|
name: dusk-psp
|
||||||
path: ./git-artifcats/Dusk
|
path: ./git-artifcats/Dusk
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
# build-vita:
|
|
||||||
# runs-on: ubuntu-latest
|
|
||||||
# steps:
|
|
||||||
# - name: Checkout repository
|
|
||||||
# uses: actions/checkout@v6
|
|
||||||
# - name: Set up Docker
|
|
||||||
# uses: docker/setup-docker-action@v5
|
|
||||||
# - name: Build Vita
|
|
||||||
# run: ./scripts/build-vita-docker.sh
|
|
||||||
# - name: Upload Vita binary
|
|
||||||
# uses: actions/upload-artifact@v6
|
|
||||||
# with:
|
|
||||||
# name: dusk-vita
|
|
||||||
# path: build-vita/Dusk.vpk
|
|
||||||
# if-no-files-found: error
|
|
||||||
|
|
||||||
build-knulli:
|
build-knulli:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: debian:trixie
|
||||||
steps:
|
steps:
|
||||||
|
- name: Install Node.js
|
||||||
|
run: apt-get update && apt-get install -y nodejs
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v4
|
||||||
- name: Set up Docker
|
- name: Install dependencies
|
||||||
uses: docker/setup-docker-action@v5
|
run: |
|
||||||
- name: Build knulli
|
dpkg --add-architecture arm64
|
||||||
run: ./scripts/build-knulli-docker.sh
|
apt-get update
|
||||||
|
apt-get install -y --no-install-recommends \
|
||||||
|
crossbuild-essential-arm64 \
|
||||||
|
ca-certificates \
|
||||||
|
pkg-config \
|
||||||
|
cmake \
|
||||||
|
make \
|
||||||
|
ninja-build \
|
||||||
|
git \
|
||||||
|
file \
|
||||||
|
python3 \
|
||||||
|
python3-pip \
|
||||||
|
python3-polib \
|
||||||
|
python3-pil \
|
||||||
|
python3-dotenv \
|
||||||
|
python3-pyqt5 \
|
||||||
|
python3-opengl \
|
||||||
|
liblua5.4-dev:arm64 \
|
||||||
|
xz-utils:arm64 \
|
||||||
|
libbz2-dev:arm64 \
|
||||||
|
zlib1g-dev:arm64 \
|
||||||
|
libzip-dev:arm64 \
|
||||||
|
libssl-dev:arm64 \
|
||||||
|
libsdl2-dev:arm64 \
|
||||||
|
liblzma-dev:arm64 \
|
||||||
|
libopengl0:arm64 \
|
||||||
|
libgl1:arm64 \
|
||||||
|
libegl1:arm64 \
|
||||||
|
libgles2:arm64 \
|
||||||
|
libgl1-mesa-dev:arm64
|
||||||
|
- name: Build Knulli
|
||||||
|
run: ./scripts/build-knulli.sh
|
||||||
- name: Move output to Dusk subfolder
|
- name: Move output to Dusk subfolder
|
||||||
run: |
|
run: |
|
||||||
mkdir -p ./git-artifcats/Dusk
|
mkdir -p ./git-artifcats/Dusk
|
||||||
cp -r build-knulli/dusk ./git-artifcats/Dusk
|
cp -r build-knulli/dusk ./git-artifcats/Dusk
|
||||||
- name: Upload knulli binary
|
- name: Upload Knulli binary
|
||||||
uses: actions/upload-artifact@v6
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: dusk-knulli
|
name: dusk-knulli
|
||||||
path: ./git-artifcats/Dusk
|
path: ./git-artifcats/Dusk
|
||||||
@@ -88,20 +147,31 @@ jobs:
|
|||||||
|
|
||||||
build-gamecube:
|
build-gamecube:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: ghcr.io/extremscorner/libogc2:latest
|
||||||
steps:
|
steps:
|
||||||
|
- name: Install Node.js
|
||||||
|
run: apt-get update && apt-get install -y nodejs
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v4
|
||||||
- name: Set up Docker
|
- name: Install additional dependencies
|
||||||
uses: docker/setup-docker-action@v5
|
run: |
|
||||||
|
apt-get install -y \
|
||||||
|
python3-pip python3-polib python3-pil \
|
||||||
|
python3-dotenv python3-pyqt5 python3-opengl
|
||||||
|
dkp-pacman -Syu --noconfirm
|
||||||
|
dkp-pacman -S --needed --noconfirm \
|
||||||
|
gamecube-sdl2 ppc-liblzma ppc-libzip \
|
||||||
|
gamecube-tools ppc-libmad ppc-zlib-ng ppc-bzip2 ppc-zstd
|
||||||
- name: Build GameCube
|
- name: Build GameCube
|
||||||
run: ./scripts/build-gamecube-docker.sh
|
run: ./scripts/build-gamecube.sh
|
||||||
- name: Copy output files.
|
- name: Copy output files
|
||||||
run: |
|
run: |
|
||||||
mkdir -p ./git-artifcats/Dusk
|
mkdir -p ./git-artifcats/Dusk
|
||||||
cp build-gamecube/Dusk.dol ./git-artifcats/Dusk/Dusk.dol
|
cp build-gamecube/Dusk.dol ./git-artifcats/Dusk/Dusk.dol
|
||||||
cp build-gamecube/dusk.dsk ./git-artifcats/Dusk/dusk.dsk
|
cp build-gamecube/dusk.dsk ./git-artifcats/Dusk/dusk.dsk
|
||||||
- name: Upload GameCube binary
|
- name: Upload GameCube binary
|
||||||
uses: actions/upload-artifact@v6
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: dusk-gamecube
|
name: dusk-gamecube
|
||||||
path: ./git-artifcats/Dusk
|
path: ./git-artifcats/Dusk
|
||||||
@@ -109,21 +179,32 @@ jobs:
|
|||||||
|
|
||||||
build-gamecube-iso:
|
build-gamecube-iso:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: ghcr.io/extremscorner/libogc2:latest
|
||||||
steps:
|
steps:
|
||||||
|
- name: Install Node.js
|
||||||
|
run: apt-get update && apt-get install -y nodejs
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v4
|
||||||
- name: Set up Docker
|
- name: Install additional dependencies
|
||||||
uses: docker/setup-docker-action@v5
|
run: |
|
||||||
|
apt-get install -y \
|
||||||
|
python3-pip python3-polib python3-pil \
|
||||||
|
python3-dotenv python3-pyqt5 python3-opengl xorriso
|
||||||
|
dkp-pacman -Syu --noconfirm
|
||||||
|
dkp-pacman -S --needed --noconfirm \
|
||||||
|
gamecube-sdl2 ppc-liblzma ppc-libzip \
|
||||||
|
gamecube-tools ppc-libmad ppc-zlib-ng ppc-bzip2 ppc-zstd
|
||||||
- name: Build GameCube ISO
|
- name: Build GameCube ISO
|
||||||
run: ./scripts/build-gamecube-iso-docker.sh
|
run: ./scripts/build-gamecube-iso.sh
|
||||||
- name: Copy output files.
|
- name: Copy output files
|
||||||
run: |
|
run: |
|
||||||
mkdir -p ./git-artifcats/Dusk
|
mkdir -p ./git-artifcats/Dusk
|
||||||
cp build-gamecube-iso/Dusk-NTSC-J.iso ./git-artifcats/Dusk/Dusk-NTSC-J.iso
|
cp build-gamecube-iso/Dusk-NTSC-J.iso ./git-artifcats/Dusk/Dusk-NTSC-J.iso
|
||||||
cp build-gamecube-iso/Dusk-NTSC-U.iso ./git-artifcats/Dusk/Dusk-NTSC-U.iso
|
cp build-gamecube-iso/Dusk-NTSC-U.iso ./git-artifcats/Dusk/Dusk-NTSC-U.iso
|
||||||
cp build-gamecube-iso/Dusk-PAL.iso ./git-artifcats/Dusk/Dusk-PAL.iso
|
cp build-gamecube-iso/Dusk-PAL.iso ./git-artifcats/Dusk/Dusk-PAL.iso
|
||||||
- name: Upload GameCube ISO
|
- name: Upload GameCube ISO
|
||||||
uses: actions/upload-artifact@v6
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: dusk-gamecube-iso
|
name: dusk-gamecube-iso
|
||||||
path: ./git-artifcats/Dusk
|
path: ./git-artifcats/Dusk
|
||||||
@@ -131,21 +212,32 @@ jobs:
|
|||||||
|
|
||||||
build-wii:
|
build-wii:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: ghcr.io/extremscorner/libogc2:latest
|
||||||
steps:
|
steps:
|
||||||
|
- name: Install Node.js
|
||||||
|
run: apt-get update && apt-get install -y nodejs
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v4
|
||||||
- name: Set up Docker
|
- name: Install additional dependencies
|
||||||
uses: docker/setup-docker-action@v5
|
run: |
|
||||||
|
apt-get install -y \
|
||||||
|
python3-pip python3-polib python3-pil \
|
||||||
|
python3-dotenv python3-pyqt5 python3-opengl
|
||||||
|
dkp-pacman -Syu --noconfirm
|
||||||
|
dkp-pacman -S --needed --noconfirm \
|
||||||
|
gamecube-sdl2 ppc-liblzma ppc-libzip \
|
||||||
|
gamecube-tools ppc-libmad ppc-zlib-ng ppc-bzip2 ppc-zstd
|
||||||
- name: Build Wii
|
- name: Build Wii
|
||||||
run: ./scripts/build-wii-docker.sh
|
run: ./scripts/build-wii.sh
|
||||||
- name: Copy output files.
|
- name: Copy output files
|
||||||
run: |
|
run: |
|
||||||
mkdir -p ./git-artifcats/Dusk/apps/Dusk
|
mkdir -p ./git-artifcats/Dusk/apps/Dusk
|
||||||
cp build-wii/boot.dol ./git-artifcats/Dusk/apps/Dusk/boot.dol
|
cp build-wii/boot.dol ./git-artifcats/Dusk/apps/Dusk/boot.dol
|
||||||
cp build-wii/dusk.dsk ./git-artifcats/Dusk/apps/Dusk/dusk.dsk
|
cp build-wii/dusk.dsk ./git-artifcats/Dusk/apps/Dusk/dusk.dsk
|
||||||
cp build-wii/meta.xml ./git-artifcats/Dusk/apps/Dusk/meta.xml
|
cp build-wii/meta.xml ./git-artifcats/Dusk/apps/Dusk/meta.xml
|
||||||
- name: Upload Wii binary
|
- name: Upload Wii binary
|
||||||
uses: actions/upload-artifact@v6
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: dusk-wii
|
name: dusk-wii
|
||||||
path: ./git-artifcats/Dusk
|
path: ./git-artifcats/Dusk
|
||||||
@@ -153,21 +245,32 @@ jobs:
|
|||||||
|
|
||||||
build-wii-iso:
|
build-wii-iso:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
container:
|
||||||
|
image: ghcr.io/extremscorner/libogc2:latest
|
||||||
steps:
|
steps:
|
||||||
|
- name: Install Node.js
|
||||||
|
run: apt-get update && apt-get install -y nodejs
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v4
|
||||||
- name: Set up Docker
|
- name: Install additional dependencies
|
||||||
uses: docker/setup-docker-action@v5
|
run: |
|
||||||
|
apt-get install -y \
|
||||||
|
python3-pip python3-polib python3-pil \
|
||||||
|
python3-dotenv python3-pyqt5 python3-opengl xorriso
|
||||||
|
dkp-pacman -Syu --noconfirm
|
||||||
|
dkp-pacman -S --needed --noconfirm \
|
||||||
|
gamecube-sdl2 ppc-liblzma ppc-libzip \
|
||||||
|
gamecube-tools ppc-libmad ppc-zlib-ng ppc-bzip2 ppc-zstd
|
||||||
- name: Build Wii ISO
|
- name: Build Wii ISO
|
||||||
run: ./scripts/build-wii-iso-docker.sh
|
run: ./scripts/build-wii-iso.sh
|
||||||
- name: Copy output files.
|
- name: Copy output files
|
||||||
run: |
|
run: |
|
||||||
mkdir -p ./git-artifcats/Dusk
|
mkdir -p ./git-artifcats/Dusk
|
||||||
cp build-wii-iso/Dusk-NTSC-J.iso ./git-artifcats/Dusk/Dusk-NTSC-J.iso
|
cp build-wii-iso/Dusk-NTSC-J.iso ./git-artifcats/Dusk/Dusk-NTSC-J.iso
|
||||||
cp build-wii-iso/Dusk-NTSC-U.iso ./git-artifcats/Dusk/Dusk-NTSC-U.iso
|
cp build-wii-iso/Dusk-NTSC-U.iso ./git-artifcats/Dusk/Dusk-NTSC-U.iso
|
||||||
cp build-wii-iso/Dusk-PAL.iso ./git-artifcats/Dusk/Dusk-PAL.iso
|
cp build-wii-iso/Dusk-PAL.iso ./git-artifcats/Dusk/Dusk-PAL.iso
|
||||||
- name: Upload Wii ISO
|
- name: Upload Wii ISO
|
||||||
uses: actions/upload-artifact@v6
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: dusk-wii-iso
|
name: dusk-wii-iso
|
||||||
path: ./git-artifcats/Dusk
|
path: ./git-artifcats/Dusk
|
||||||
|
|||||||
@@ -423,6 +423,29 @@ above the declaration with no blank line in between.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Color system
|
||||||
|
|
||||||
|
Colors are defined in `src/dusk/display/color.csv` and code-generated
|
||||||
|
into a `color.h` header by `tools/color/csv/__main__.py`.
|
||||||
|
|
||||||
|
Each row in the CSV has `name,r,g,b,a` with channel values in `[0.0, 1.0]`.
|
||||||
|
The script emits four `#define` variants per color plus a bare alias:
|
||||||
|
|
||||||
|
```
|
||||||
|
COLOR_<NAME>_4B color4b(r8, g8, b8, a8) // default alias target
|
||||||
|
COLOR_<NAME>_3B color3b(r8, g8, b8)
|
||||||
|
COLOR_<NAME>_3F color3f(rf, gf, bf)
|
||||||
|
COLOR_<NAME>_4F color4f(rf, gf, bf, af)
|
||||||
|
COLOR_<NAME> COLOR_<NAME>_4B
|
||||||
|
```
|
||||||
|
|
||||||
|
`color_t` is `color4b_t` (four `uint8_t` channels).
|
||||||
|
|
||||||
|
To add a new color, append a row to `color.csv` and rebuild — do not
|
||||||
|
hand-edit the generated header.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Tests
|
## Tests
|
||||||
- Tests live in `test/` mirroring `src/dusk/` structure.
|
- Tests live in `test/` mirroring `src/dusk/` structure.
|
||||||
- Use cmocka; include `dusktest.h`.
|
- Use cmocka; include `dusktest.h`.
|
||||||
|
|||||||
Binary file not shown.
@@ -56,7 +56,6 @@ add_subdirectory(animation)
|
|||||||
add_subdirectory(event)
|
add_subdirectory(event)
|
||||||
add_subdirectory(assert)
|
add_subdirectory(assert)
|
||||||
add_subdirectory(asset)
|
add_subdirectory(asset)
|
||||||
add_subdirectory(cutscene)
|
|
||||||
add_subdirectory(console)
|
add_subdirectory(console)
|
||||||
add_subdirectory(display)
|
add_subdirectory(display)
|
||||||
add_subdirectory(log)
|
add_subdirectory(log)
|
||||||
|
|||||||
@@ -15,3 +15,4 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
|||||||
add_subdirectory(display)
|
add_subdirectory(display)
|
||||||
add_subdirectory(locale)
|
add_subdirectory(locale)
|
||||||
add_subdirectory(json)
|
add_subdirectory(json)
|
||||||
|
add_subdirectory(chunk)
|
||||||
@@ -39,4 +39,10 @@ assetloadercallbacks_t ASSET_LOADER_CALLBACKS[ASSET_LOADER_TYPE_COUNT] = {
|
|||||||
.loadAsync = assetJsonLoaderAsync,
|
.loadAsync = assetJsonLoaderAsync,
|
||||||
.dispose = assetJsonDispose
|
.dispose = assetJsonDispose
|
||||||
},
|
},
|
||||||
|
|
||||||
|
[ASSET_LOADER_TYPE_CHUNK] = {
|
||||||
|
.loadSync = assetChunkLoaderSync,
|
||||||
|
.loadAsync = assetChunkLoaderAsync,
|
||||||
|
.dispose = assetChunkDispose
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include "asset/loader/display/assettilesetloader.h"
|
#include "asset/loader/display/assettilesetloader.h"
|
||||||
#include "asset/loader/locale/assetlocaleloader.h"
|
#include "asset/loader/locale/assetlocaleloader.h"
|
||||||
#include "asset/loader/json/assetjsonloader.h"
|
#include "asset/loader/json/assetjsonloader.h"
|
||||||
|
#include "asset/loader/chunk/assetchunkloader.h"
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
ASSET_LOADER_TYPE_NULL,
|
ASSET_LOADER_TYPE_NULL,
|
||||||
@@ -20,6 +21,7 @@ typedef enum {
|
|||||||
ASSET_LOADER_TYPE_TILESET,
|
ASSET_LOADER_TYPE_TILESET,
|
||||||
ASSET_LOADER_TYPE_LOCALE,
|
ASSET_LOADER_TYPE_LOCALE,
|
||||||
ASSET_LOADER_TYPE_JSON,
|
ASSET_LOADER_TYPE_JSON,
|
||||||
|
ASSET_LOADER_TYPE_CHUNK,
|
||||||
|
|
||||||
ASSET_LOADER_TYPE_COUNT
|
ASSET_LOADER_TYPE_COUNT
|
||||||
} assetloadertype_t;
|
} assetloadertype_t;
|
||||||
@@ -30,6 +32,7 @@ typedef union {
|
|||||||
assettilesetloaderinput_t tileset;
|
assettilesetloaderinput_t tileset;
|
||||||
assetlocaleloaderinput_t locale;
|
assetlocaleloaderinput_t locale;
|
||||||
assetjsonloaderinput_t json;
|
assetjsonloaderinput_t json;
|
||||||
|
assetchunkloaderinput_t chunk;
|
||||||
} assetloaderinput_t;
|
} assetloaderinput_t;
|
||||||
|
|
||||||
typedef union {
|
typedef union {
|
||||||
@@ -38,6 +41,7 @@ typedef union {
|
|||||||
assettilesetloaderloading_t tileset;
|
assettilesetloaderloading_t tileset;
|
||||||
assetlocaleloaderloading_t locale;
|
assetlocaleloaderloading_t locale;
|
||||||
assetjsonloaderloading_t json;
|
assetjsonloaderloading_t json;
|
||||||
|
assetchunkloaderloading_t chunk;
|
||||||
} assetloaderloading_t;
|
} assetloaderloading_t;
|
||||||
|
|
||||||
typedef union {
|
typedef union {
|
||||||
@@ -46,6 +50,7 @@ typedef union {
|
|||||||
assettilesetoutput_t tileset;
|
assettilesetoutput_t tileset;
|
||||||
assetlocaleoutput_t locale;
|
assetlocaleoutput_t locale;
|
||||||
assetjsonoutput_t json;
|
assetjsonoutput_t json;
|
||||||
|
assetchunkoutput_t chunk;
|
||||||
} assetloaderoutput_t;
|
} assetloaderoutput_t;
|
||||||
|
|
||||||
typedef struct assetloading_s assetloading_t;
|
typedef struct assetloading_s assetloading_t;
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
# Copyright (c) 2026 Dominic Masters
|
||||||
|
#
|
||||||
|
# This software is released under the MIT License.
|
||||||
|
# https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
|
# Sources
|
||||||
|
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||||
|
PUBLIC
|
||||||
|
assetchunkloader.c
|
||||||
|
)
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "assetchunkloader.h"
|
||||||
|
#include "assert/assert.h"
|
||||||
|
#include "util/memory.h"
|
||||||
|
#include "util/endian.h"
|
||||||
|
#include "asset/loader/assetloading.h"
|
||||||
|
#include "asset/loader/assetentry.h"
|
||||||
|
|
||||||
|
errorret_t assetChunkLoaderAsync(assetloading_t *loading) {
|
||||||
|
assertNotNull(loading, "Loading cannot be NULL");
|
||||||
|
assertNotMainThread("Should be called from an async thread.");
|
||||||
|
|
||||||
|
if(loading->loading.chunk.state != ASSET_CHUNK_LOADING_STATE_READ_FILE) {
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
assertNull(loading->loading.chunk.data, "Data already defined?");
|
||||||
|
|
||||||
|
assetfile_t *file = &loading->loading.chunk.file;
|
||||||
|
assetLoaderErrorChain(loading,
|
||||||
|
assetFileInit(file, loading->entry->name, NULL, NULL)
|
||||||
|
);
|
||||||
|
|
||||||
|
uint8_t *data = memoryAllocate(file->size);
|
||||||
|
assetLoaderErrorChain(loading, assetFileOpen(file));
|
||||||
|
assetLoaderErrorChain(loading, assetFileRead(file, data, file->size));
|
||||||
|
assetLoaderErrorChain(loading, assetFileClose(file));
|
||||||
|
assetLoaderErrorChain(loading, assetFileDispose(file));
|
||||||
|
assertTrue(
|
||||||
|
file->lastRead == file->size,
|
||||||
|
"Failed to read entire chunk file."
|
||||||
|
);
|
||||||
|
|
||||||
|
loading->loading.chunk.data = data;
|
||||||
|
loading->loading.chunk.state = ASSET_CHUNK_LOADING_STATE_PARSE;
|
||||||
|
loading->entry->state = ASSET_ENTRY_STATE_PENDING_SYNC;
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t assetChunkLoaderSync(assetloading_t *loading) {
|
||||||
|
assertNotNull(loading, "Loading cannot be NULL");
|
||||||
|
assertTrue(loading->type == ASSET_LOADER_TYPE_CHUNK, "Invalid type.");
|
||||||
|
assertIsMainThread("Must be called from the main thread.");
|
||||||
|
|
||||||
|
switch(loading->loading.chunk.state) {
|
||||||
|
case ASSET_CHUNK_LOADING_STATE_INITIAL:
|
||||||
|
loading->loading.chunk.state = ASSET_CHUNK_LOADING_STATE_READ_FILE;
|
||||||
|
loading->entry->state = ASSET_ENTRY_STATE_PENDING_ASYNC;
|
||||||
|
errorOk();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ASSET_CHUNK_LOADING_STATE_PARSE:
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *data = loading->loading.chunk.data;
|
||||||
|
assertNotNull(data, "Chunk data should have been loaded by now.");
|
||||||
|
|
||||||
|
if(data[0] != 'D' || data[1] != 'C' || data[2] != 'F') {
|
||||||
|
memoryFree(data);
|
||||||
|
assetLoaderErrorThrow(loading, "Invalid chunk file header");
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t version = endianLittleToHost32(*(uint32_t *)(data + 4));
|
||||||
|
if(version != ASSET_CHUNK_FILE_VERSION) {
|
||||||
|
memoryFree(data);
|
||||||
|
assetLoaderErrorThrow(
|
||||||
|
loading, "Unsupported chunk file version %u", version
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
assetchunkoutput_t *out = &loading->entry->data.chunk;
|
||||||
|
size_t offset = 8;
|
||||||
|
|
||||||
|
size_t tileSize = CHUNK_TILE_COUNT * sizeof(tile_t);
|
||||||
|
memoryCopy(out->tiles, data + offset, tileSize);
|
||||||
|
offset += tileSize;
|
||||||
|
|
||||||
|
out->vertCount = endianLittleToHost32(*(uint32_t *)(data + offset));
|
||||||
|
offset += sizeof(uint32_t);
|
||||||
|
|
||||||
|
assertTrue(
|
||||||
|
out->vertCount <= CHUNK_VERTEX_COUNT,
|
||||||
|
"Chunk vertex count exceeds maximum."
|
||||||
|
);
|
||||||
|
|
||||||
|
memoryCopy(
|
||||||
|
out->vertices,
|
||||||
|
data + offset,
|
||||||
|
out->vertCount * sizeof(meshvertex_t)
|
||||||
|
);
|
||||||
|
|
||||||
|
memoryFree(data);
|
||||||
|
loading->loading.chunk.data = NULL;
|
||||||
|
|
||||||
|
loading->entry->state = ASSET_ENTRY_STATE_LOADED;
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t assetChunkDispose(assetentry_t *entry) {
|
||||||
|
assertNotNull(entry, "Entry cannot be NULL");
|
||||||
|
assertTrue(entry->type == ASSET_LOADER_TYPE_CHUNK, "Invalid type.");
|
||||||
|
assertIsMainThread("Must be called from the main thread.");
|
||||||
|
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "asset/assetfile.h"
|
||||||
|
#include "rpg/overworld/chunk.h"
|
||||||
|
|
||||||
|
#define ASSET_CHUNK_FILE_VERSION 1
|
||||||
|
|
||||||
|
typedef struct assetloading_s assetloading_t;
|
||||||
|
typedef struct assetentry_s assetentry_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
void *nothing;
|
||||||
|
} assetchunkloaderinput_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
ASSET_CHUNK_LOADING_STATE_INITIAL,
|
||||||
|
ASSET_CHUNK_LOADING_STATE_READ_FILE,
|
||||||
|
ASSET_CHUNK_LOADING_STATE_PARSE,
|
||||||
|
ASSET_CHUNK_LOADING_STATE_DONE
|
||||||
|
} assetchunkloadingstate_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
assetfile_t file;
|
||||||
|
assetchunkloadingstate_t state;
|
||||||
|
uint8_t *data;
|
||||||
|
} assetchunkloaderloading_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
tile_t tiles[CHUNK_TILE_COUNT];
|
||||||
|
uint32_t vertCount;
|
||||||
|
meshvertex_t vertices[CHUNK_VERTEX_COUNT];
|
||||||
|
} assetchunkoutput_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asynchronous loader for chunk assets. Reads the raw DCF file bytes into
|
||||||
|
* the loading buffer so the sync phase can parse without blocking the
|
||||||
|
* main thread on I/O.
|
||||||
|
*
|
||||||
|
* @param loading Loading information for the asset being loaded.
|
||||||
|
* @return Error code indicating success or failure of the load operation.
|
||||||
|
*/
|
||||||
|
errorret_t assetChunkLoaderAsync(assetloading_t *loading);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Synchronous loader for chunk assets. Validates the DCF binary previously
|
||||||
|
* read by the async phase and populates the output assetchunkoutput_t.
|
||||||
|
*
|
||||||
|
* @param loading Loading information for the asset being loaded.
|
||||||
|
* @return Error code indicating success or failure of the load operation.
|
||||||
|
*/
|
||||||
|
errorret_t assetChunkLoaderSync(assetloading_t *loading);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disposer for chunk assets.
|
||||||
|
*
|
||||||
|
* @param entry Asset entry containing the chunk data to dispose.
|
||||||
|
* @return Error code indicating success or failure of the dispose operation.
|
||||||
|
*/
|
||||||
|
errorret_t assetChunkDispose(assetentry_t *entry);
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
// Copyright (c) 2026 Dominic Masters
|
|
||||||
//
|
|
||||||
// This software is released under the MIT License.
|
|
||||||
// https://opensource.org/licenses/MIT
|
|
||||||
|
|
||||||
#include "cutscene.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
#include "util/memory.h"
|
|
||||||
#include "console/console.h"
|
|
||||||
#include "time/time.h"
|
|
||||||
|
|
||||||
cutscene_t CUTSCENE;
|
|
||||||
|
|
||||||
errorret_t cutsceneInit(void) {
|
|
||||||
memoryZero(&CUTSCENE, sizeof(cutscene_t));
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
|
|
||||||
errorret_t cutsceneUpdate(void) {
|
|
||||||
#ifdef DUSK_TIME_DYNAMIC
|
|
||||||
if(TIME.dynamicUpdate) {
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if(!CUTSCENE.active) errorOk();
|
|
||||||
|
|
||||||
cutsceneevent_t *event = &CUTSCENE.events[CUTSCENE.eventCurrent];
|
|
||||||
if(event->onUpdate) errorChain(event->onUpdate());
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
|
|
||||||
errorret_t cutscenePlay(
|
|
||||||
const cutsceneevent_t *events,
|
|
||||||
const uint8_t eventCount
|
|
||||||
) {
|
|
||||||
assertNotNull(events, "Events cannot be null");
|
|
||||||
assertTrue(eventCount > 0, "Event count must be greater than zero");
|
|
||||||
assertTrue(
|
|
||||||
eventCount <= CUTSCENE_EVENT_COUNT_MAX,
|
|
||||||
"Event count exceeds CUTSCENE_EVENT_COUNT_MAX"
|
|
||||||
);
|
|
||||||
|
|
||||||
if(CUTSCENE.active) {
|
|
||||||
errorChain(cutsceneStop());
|
|
||||||
}
|
|
||||||
|
|
||||||
memoryCopy(CUTSCENE.events, events, sizeof(cutsceneevent_t) * eventCount);
|
|
||||||
CUTSCENE.eventCount = eventCount;
|
|
||||||
CUTSCENE.eventCurrent = 0;
|
|
||||||
CUTSCENE.active = true;
|
|
||||||
|
|
||||||
cutsceneevent_t *firstEvent = &CUTSCENE.events[0];
|
|
||||||
if(firstEvent->onStart) errorChain(firstEvent->onStart());
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
|
|
||||||
errorret_t cutsceneAdvance(void) {
|
|
||||||
if(!CUTSCENE.active) errorOk();
|
|
||||||
|
|
||||||
cutsceneevent_t *currentEvent = &CUTSCENE.events[CUTSCENE.eventCurrent];
|
|
||||||
if(currentEvent->onEnd) errorChain(currentEvent->onEnd());
|
|
||||||
CUTSCENE.eventCurrent++;
|
|
||||||
|
|
||||||
if(CUTSCENE.eventCurrent >= CUTSCENE.eventCount) {
|
|
||||||
if(CUTSCENE.onStop) errorChain(CUTSCENE.onStop());
|
|
||||||
CUTSCENE.active = false;
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
|
|
||||||
cutsceneevent_t *nextEvent = &CUTSCENE.events[CUTSCENE.eventCurrent];
|
|
||||||
if(nextEvent->onStart) errorChain(nextEvent->onStart());
|
|
||||||
consolePrint("Cutscene advance");
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
|
|
||||||
errorret_t cutsceneStop(void) {
|
|
||||||
if(!CUTSCENE.active) errorOk();
|
|
||||||
|
|
||||||
cutsceneevent_t *currentEvent = &CUTSCENE.events[CUTSCENE.eventCurrent];
|
|
||||||
if(currentEvent->onEnd) errorChain(currentEvent->onEnd());
|
|
||||||
|
|
||||||
if(CUTSCENE.onStop) errorChain(CUTSCENE.onStop());
|
|
||||||
|
|
||||||
CUTSCENE.active = false;
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
|
|
||||||
errorret_t cutsceneDispose(void) {
|
|
||||||
errorChain(cutsceneStop());
|
|
||||||
errorOk();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool_t cutsceneIsActive(void) {
|
|
||||||
return CUTSCENE.active;
|
|
||||||
}
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
// Copyright (c) 2026 Dominic Masters
|
|
||||||
//
|
|
||||||
// This software is released under the MIT License.
|
|
||||||
// https://opensource.org/licenses/MIT
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "error/error.h"
|
|
||||||
|
|
||||||
#define CUTSCENE_EVENT_COUNT_MAX 16
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
errorret_t (*onStart)(void);
|
|
||||||
errorret_t (*onEnd)(void);
|
|
||||||
errorret_t (*onUpdate)(void);
|
|
||||||
} cutsceneevent_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
cutsceneevent_t events[CUTSCENE_EVENT_COUNT_MAX];
|
|
||||||
uint8_t eventCount;
|
|
||||||
uint8_t eventCurrent;
|
|
||||||
errorret_t (*onStop)(void);
|
|
||||||
bool_t active;
|
|
||||||
} cutscene_t;
|
|
||||||
|
|
||||||
extern cutscene_t CUTSCENE;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the cutscene manager.
|
|
||||||
*
|
|
||||||
* @return Any error state that happened.
|
|
||||||
*/
|
|
||||||
errorret_t cutsceneInit(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ticks the active cutscene event, calling its onUpdate callback.
|
|
||||||
* Does nothing when no cutscene is playing.
|
|
||||||
*
|
|
||||||
* @return Any error state that happened.
|
|
||||||
*/
|
|
||||||
errorret_t cutsceneUpdate(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Copies the given event array and begins playing from the first
|
|
||||||
* event. If a cutscene is already playing it is stopped first.
|
|
||||||
*
|
|
||||||
* @param events Array of events to copy.
|
|
||||||
* @param eventCount Number of events. Must be > 0 and
|
|
||||||
* <= CUTSCENE_EVENT_COUNT_MAX.
|
|
||||||
* @return Any error state that happened.
|
|
||||||
*/
|
|
||||||
errorret_t cutscenePlay(
|
|
||||||
const cutsceneevent_t *events,
|
|
||||||
const uint8_t eventCount
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ends the current event and starts the next one.
|
|
||||||
* Marks the cutscene as inactive after the last event ends.
|
|
||||||
* Does nothing when no cutscene is playing.
|
|
||||||
*
|
|
||||||
* @return Any error state that happened.
|
|
||||||
*/
|
|
||||||
errorret_t cutsceneAdvance(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ends the current event and stops the cutscene immediately.
|
|
||||||
* Does nothing when no cutscene is playing.
|
|
||||||
*
|
|
||||||
* @return Any error state that happened.
|
|
||||||
*/
|
|
||||||
errorret_t cutsceneStop(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Disposes of the cutscene manager, stopping any active cutscene.
|
|
||||||
*
|
|
||||||
* @return Any error state that happened.
|
|
||||||
*/
|
|
||||||
errorret_t cutsceneDispose(void);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether a cutscene is currently playing.
|
|
||||||
*
|
|
||||||
* @return true if a cutscene is active.
|
|
||||||
*/
|
|
||||||
bool_t cutsceneIsActive(void);
|
|
||||||
@@ -13,7 +13,6 @@
|
|||||||
#include "rpg/rpg.h"
|
#include "rpg/rpg.h"
|
||||||
#include "display/display.h"
|
#include "display/display.h"
|
||||||
#include "scene/scene.h"
|
#include "scene/scene.h"
|
||||||
#include "cutscene/cutscene.h"
|
|
||||||
#include "asset/asset.h"
|
#include "asset/asset.h"
|
||||||
#include "ui/ui.h"
|
#include "ui/ui.h"
|
||||||
#include "assert/assert.h"
|
#include "assert/assert.h"
|
||||||
@@ -42,7 +41,6 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) {
|
|||||||
errorChain(localeManagerInit());
|
errorChain(localeManagerInit());
|
||||||
errorChain(displayInit());
|
errorChain(displayInit());
|
||||||
errorChain(uiInit());
|
errorChain(uiInit());
|
||||||
errorChain(cutsceneInit());
|
|
||||||
errorChain(rpgInit());
|
errorChain(rpgInit());
|
||||||
errorChain(networkInit());
|
errorChain(networkInit());
|
||||||
errorChain(sceneInit());
|
errorChain(sceneInit());
|
||||||
@@ -61,7 +59,6 @@ errorret_t engineUpdate(void) {
|
|||||||
inputUpdate();
|
inputUpdate();
|
||||||
consoleUpdate();
|
consoleUpdate();
|
||||||
errorChain(rpgUpdate());
|
errorChain(rpgUpdate());
|
||||||
errorChain(cutsceneUpdate());
|
|
||||||
errorChain(sceneUpdate());
|
errorChain(sceneUpdate());
|
||||||
errorChain(assetUpdate());
|
errorChain(assetUpdate());
|
||||||
errorChain(uiUpdate());
|
errorChain(uiUpdate());
|
||||||
@@ -77,7 +74,6 @@ void engineExit(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
errorret_t engineDispose(void) {
|
errorret_t engineDispose(void) {
|
||||||
cutsceneDispose();
|
|
||||||
errorChain(sceneDispose());
|
errorChain(sceneDispose());
|
||||||
errorChain(networkDispose());
|
errorChain(networkDispose());
|
||||||
errorChain(rpgDispose());
|
errorChain(rpgDispose());
|
||||||
|
|||||||
@@ -54,3 +54,9 @@ const cutsceneitem_t * cutsceneSystemGetCurrentItem() {
|
|||||||
|
|
||||||
return &CUTSCENE_SYSTEM.scene->items[CUTSCENE_SYSTEM.currentItem];
|
return &CUTSCENE_SYSTEM.scene->items[CUTSCENE_SYSTEM.currentItem];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cutsceneSystemDispose() {
|
||||||
|
CUTSCENE_SYSTEM.scene = NULL;
|
||||||
|
CUTSCENE_SYSTEM.currentItem = 0xFF;
|
||||||
|
CUTSCENE_SYSTEM.mode = CUTSCENE_MODE_NONE;
|
||||||
|
}
|
||||||
@@ -48,3 +48,8 @@ void cutsceneSystemUpdate();
|
|||||||
* @return Pointer to the current cutscene item.
|
* @return Pointer to the current cutscene item.
|
||||||
*/
|
*/
|
||||||
const cutsceneitem_t * cutsceneSystemGetCurrentItem();
|
const cutsceneitem_t * cutsceneSystemGetCurrentItem();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disposes of the cutscene system, stopping any active cutscene.
|
||||||
|
*/
|
||||||
|
void cutsceneSystemDispose();
|
||||||
@@ -7,4 +7,5 @@
|
|||||||
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||||
PUBLIC
|
PUBLIC
|
||||||
cutsceneitem.c
|
cutsceneitem.c
|
||||||
|
cutsceneentitymove.c
|
||||||
)
|
)
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "cutsceneentitymove.h"
|
||||||
|
#include "rpg/cutscene/cutscenesystem.h"
|
||||||
|
#include "rpg/entity/entity.h"
|
||||||
|
|
||||||
|
void cutsceneEntityMoveStart(const cutsceneitem_t *item) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void cutsceneEntityMoveUpdate(const cutsceneitem_t *item) {
|
||||||
|
entity_t *entity = &ENTITIES[item->entityMove.entityIndex];
|
||||||
|
|
||||||
|
if(worldPosIsEqual(entity->position, item->entityMove.target)) {
|
||||||
|
cutsceneSystemNext();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
entitydir_t dir;
|
||||||
|
if(entity->position.x != item->entityMove.target.x) {
|
||||||
|
dir = entity->position.x < item->entityMove.target.x
|
||||||
|
? ENTITY_DIR_EAST : ENTITY_DIR_WEST;
|
||||||
|
} else {
|
||||||
|
dir = entity->position.y < item->entityMove.target.y
|
||||||
|
? ENTITY_DIR_SOUTH : ENTITY_DIR_NORTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(entityCanTurn(entity)) entityTurn(entity, dir);
|
||||||
|
|
||||||
|
if(item->entityMove.run) {
|
||||||
|
if(entityCanRun(entity)) entityRun(entity, dir);
|
||||||
|
} else {
|
||||||
|
if(entityCanWalk(entity)) entityWalk(entity, dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "cutsceneitem.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the start of an entity move cutscene item.
|
||||||
|
*
|
||||||
|
* @param item The cutscene item.
|
||||||
|
*/
|
||||||
|
void cutsceneEntityMoveStart(const cutsceneitem_t *item);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates an entity move cutscene item, steering the entity one step
|
||||||
|
* per frame toward the target and advancing the cutscene on arrival.
|
||||||
|
*
|
||||||
|
* @param item The cutscene item.
|
||||||
|
*/
|
||||||
|
void cutsceneEntityMoveUpdate(const cutsceneitem_t *item);
|
||||||
@@ -6,6 +6,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "rpg/cutscene/cutscenesystem.h"
|
#include "rpg/cutscene/cutscenesystem.h"
|
||||||
|
#include "cutsceneentitymove.h"
|
||||||
#include "input/input.h"
|
#include "input/input.h"
|
||||||
#include "time/time.h"
|
#include "time/time.h"
|
||||||
|
|
||||||
@@ -31,6 +32,10 @@ void cutsceneItemStart(const cutsceneitem_t *item, cutsceneitemdata_t *data) {
|
|||||||
if(item->cutscene != NULL) cutsceneSystemStartCutscene(item->cutscene);
|
if(item->cutscene != NULL) cutsceneSystemStartCutscene(item->cutscene);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case CUTSCENE_ITEM_TYPE_ENTITY_MOVE:
|
||||||
|
cutsceneEntityMoveStart(item);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -48,6 +53,10 @@ void cutsceneItemUpdate(const cutsceneitem_t *item, cutsceneitemdata_t *data) {
|
|||||||
if(data->wait <= 0) cutsceneSystemNext();
|
if(data->wait <= 0) cutsceneSystemNext();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case CUTSCENE_ITEM_TYPE_ENTITY_MOVE:
|
||||||
|
cutsceneEntityMoveUpdate(item);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include "cutscenewait.h"
|
#include "cutscenewait.h"
|
||||||
#include "cutscenecallback.h"
|
#include "cutscenecallback.h"
|
||||||
#include "cutscenetext.h"
|
#include "cutscenetext.h"
|
||||||
|
#include "rpg/overworld/worldpos.h"
|
||||||
|
|
||||||
typedef struct cutscene_s cutscene_t;
|
typedef struct cutscene_s cutscene_t;
|
||||||
|
|
||||||
@@ -17,7 +18,8 @@ typedef enum {
|
|||||||
CUTSCENE_ITEM_TYPE_TEXT,
|
CUTSCENE_ITEM_TYPE_TEXT,
|
||||||
CUTSCENE_ITEM_TYPE_CALLBACK,
|
CUTSCENE_ITEM_TYPE_CALLBACK,
|
||||||
CUTSCENE_ITEM_TYPE_WAIT,
|
CUTSCENE_ITEM_TYPE_WAIT,
|
||||||
CUTSCENE_ITEM_TYPE_CUTSCENE
|
CUTSCENE_ITEM_TYPE_CUTSCENE,
|
||||||
|
CUTSCENE_ITEM_TYPE_ENTITY_MOVE
|
||||||
} cutsceneitemtype_t;
|
} cutsceneitemtype_t;
|
||||||
|
|
||||||
typedef struct cutsceneitem_s {
|
typedef struct cutsceneitem_s {
|
||||||
@@ -29,6 +31,11 @@ typedef struct cutsceneitem_s {
|
|||||||
cutscenecallback_t callback;
|
cutscenecallback_t callback;
|
||||||
cutscenewait_t wait;
|
cutscenewait_t wait;
|
||||||
const cutscene_t *cutscene;
|
const cutscene_t *cutscene;
|
||||||
|
struct {
|
||||||
|
uint8_t entityIndex;
|
||||||
|
worldpos_t target;
|
||||||
|
bool_t run;
|
||||||
|
} entityMove;
|
||||||
};
|
};
|
||||||
} cutsceneitem_t;
|
} cutsceneitem_t;
|
||||||
|
|
||||||
|
|||||||
@@ -7,9 +7,10 @@
|
|||||||
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||||
PUBLIC
|
PUBLIC
|
||||||
entity.c
|
entity.c
|
||||||
entityanim.c
|
|
||||||
entitydir.c
|
entitydir.c
|
||||||
entityinteract.c
|
|
||||||
npc.c
|
|
||||||
player.c
|
player.c
|
||||||
)
|
)
|
||||||
|
|
||||||
|
add_subdirectory(anim)
|
||||||
|
add_subdirectory(interact)
|
||||||
|
add_subdirectory(npc)
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# Copyright (c) 2026 Dominic Masters
|
||||||
|
#
|
||||||
|
# This software is released under the MIT License.
|
||||||
|
# https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
|
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||||
|
PUBLIC
|
||||||
|
entityanim.c
|
||||||
|
entityanimidle.c
|
||||||
|
entityanimturn.c
|
||||||
|
entityanimwalk.c
|
||||||
|
entityanimrun.c
|
||||||
|
)
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "rpg/entity/entity.h"
|
||||||
|
#include "time/time.h"
|
||||||
|
|
||||||
|
const entityanimcallback_t ENTITY_ANIM_CALLBACKS[ENTITY_ANIM_COUNT] = {
|
||||||
|
[ENTITY_ANIM_IDLE] = { entityAnimIdleUpdate },
|
||||||
|
[ENTITY_ANIM_TURN] = { entityAnimTurnUpdate },
|
||||||
|
[ENTITY_ANIM_WALK] = { entityAnimWalkUpdate },
|
||||||
|
[ENTITY_ANIM_RUN] = { entityAnimRunUpdate },
|
||||||
|
};
|
||||||
|
|
||||||
|
void entityAnimUpdate(entity_t *entity) {
|
||||||
|
if(entity->animation != ENTITY_ANIM_IDLE) {
|
||||||
|
entity->animTime -= TIME.delta;
|
||||||
|
if(entity->animTime <= 0) {
|
||||||
|
entity->animation = ENTITY_ANIM_IDLE;
|
||||||
|
entity->animTime = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ENTITY_ANIM_CALLBACKS[entity->animation].update(entity);
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "dusk.h"
|
||||||
|
#include "entityanimidle.h"
|
||||||
|
#include "entityanimturn.h"
|
||||||
|
#include "entityanimwalk.h"
|
||||||
|
#include "entityanimrun.h"
|
||||||
|
|
||||||
|
typedef struct entity_s entity_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
ENTITY_ANIM_IDLE,
|
||||||
|
ENTITY_ANIM_TURN,
|
||||||
|
ENTITY_ANIM_WALK,
|
||||||
|
ENTITY_ANIM_RUN,
|
||||||
|
ENTITY_ANIM_COUNT
|
||||||
|
} entityanim_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
/** Updates the render position for this animation state. */
|
||||||
|
void (*update)(entity_t *entity);
|
||||||
|
} entityanimcallback_t;
|
||||||
|
|
||||||
|
extern const entityanimcallback_t ENTITY_ANIM_CALLBACKS[ENTITY_ANIM_COUNT];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the entity animation timer and render position.
|
||||||
|
*
|
||||||
|
* @param entity Pointer to the entity to update.
|
||||||
|
*/
|
||||||
|
void entityAnimUpdate(entity_t *entity);
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "rpg/entity/entity.h"
|
||||||
|
|
||||||
|
void entityAnimIdleUpdate(entity_t *entity) {
|
||||||
|
entity->renderPosition[0] = (float_t)entity->position.x;
|
||||||
|
entity->renderPosition[1] = (float_t)entity->position.y;
|
||||||
|
entity->renderPosition[2] = (float_t)entity->position.z;
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "dusk.h"
|
||||||
|
|
||||||
|
typedef struct entity_s entity_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates render position for the idle animation state.
|
||||||
|
*
|
||||||
|
* @param entity Pointer to the entity to update.
|
||||||
|
*/
|
||||||
|
void entityAnimIdleUpdate(entity_t *entity);
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "rpg/entity/entity.h"
|
||||||
|
|
||||||
|
void entityAnimRunUpdate(entity_t *entity) {
|
||||||
|
float_t t = 1.0f - (entity->animTime / ENTITY_ANIM_RUN_DURATION);
|
||||||
|
entity->renderPosition[0] = (float_t)entity->lastPosition.x + t * (
|
||||||
|
(float_t)entity->position.x - (float_t)entity->lastPosition.x
|
||||||
|
);
|
||||||
|
entity->renderPosition[1] = (float_t)entity->lastPosition.y + t * (
|
||||||
|
(float_t)entity->position.y - (float_t)entity->lastPosition.y
|
||||||
|
);
|
||||||
|
entity->renderPosition[2] = (float_t)entity->lastPosition.z + t * (
|
||||||
|
(float_t)entity->position.z - (float_t)entity->lastPosition.z
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "dusk.h"
|
||||||
|
#include "time/time.h"
|
||||||
|
|
||||||
|
typedef struct entity_s entity_t;
|
||||||
|
|
||||||
|
#define ENTITY_ANIM_RUN_DURATION TIME_TICKS_TO_TIME(6)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates render position for the run animation state.
|
||||||
|
*
|
||||||
|
* @param entity Pointer to the entity to update.
|
||||||
|
*/
|
||||||
|
void entityAnimRunUpdate(entity_t *entity);
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "rpg/entity/entity.h"
|
||||||
|
|
||||||
|
void entityAnimTurnUpdate(entity_t *entity) {
|
||||||
|
entity->renderPosition[0] = (float_t)entity->position.x;
|
||||||
|
entity->renderPosition[1] = (float_t)entity->position.y;
|
||||||
|
entity->renderPosition[2] = (float_t)entity->position.z;
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "dusk.h"
|
||||||
|
#include "time/time.h"
|
||||||
|
|
||||||
|
typedef struct entity_s entity_t;
|
||||||
|
|
||||||
|
#define ENTITY_ANIM_TURN_DURATION TIME_TICKS_TO_TIME(2)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates render position for the turn animation state.
|
||||||
|
*
|
||||||
|
* @param entity Pointer to the entity to update.
|
||||||
|
*/
|
||||||
|
void entityAnimTurnUpdate(entity_t *entity);
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "rpg/entity/entity.h"
|
||||||
|
|
||||||
|
void entityAnimWalkUpdate(entity_t *entity) {
|
||||||
|
float_t t = 1.0f - (entity->animTime / ENTITY_ANIM_WALK_DURATION);
|
||||||
|
entity->renderPosition[0] = (float_t)entity->lastPosition.x + t * (
|
||||||
|
(float_t)entity->position.x - (float_t)entity->lastPosition.x
|
||||||
|
);
|
||||||
|
entity->renderPosition[1] = (float_t)entity->lastPosition.y + t * (
|
||||||
|
(float_t)entity->position.y - (float_t)entity->lastPosition.y
|
||||||
|
);
|
||||||
|
entity->renderPosition[2] = (float_t)entity->lastPosition.z + t * (
|
||||||
|
(float_t)entity->position.z - (float_t)entity->lastPosition.z
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "dusk.h"
|
||||||
|
#include "time/time.h"
|
||||||
|
|
||||||
|
typedef struct entity_s entity_t;
|
||||||
|
|
||||||
|
#define ENTITY_ANIM_WALK_DURATION TIME_TICKS_TO_TIME(10)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates render position for the walk animation state.
|
||||||
|
*
|
||||||
|
* @param entity Pointer to the entity to update.
|
||||||
|
*/
|
||||||
|
void entityAnimWalkUpdate(entity_t *entity);
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
#include "util/math.h"
|
#include "util/math.h"
|
||||||
#include "rpg/cutscene/cutscenemode.h"
|
#include "rpg/cutscene/cutscenemode.h"
|
||||||
#include "rpg/overworld/map.h"
|
#include "rpg/overworld/map.h"
|
||||||
|
#include "rpg/overworld/chunk.h"
|
||||||
#include "rpg/overworld/tile.h"
|
#include "rpg/overworld/tile.h"
|
||||||
|
|
||||||
entity_t ENTITIES[ENTITY_COUNT];
|
entity_t ENTITIES[ENTITY_COUNT];
|
||||||
@@ -28,6 +29,7 @@ void entityInit(entity_t *entity, const entitytype_t type) {
|
|||||||
memoryZero(entity, sizeof(entity_t));
|
memoryZero(entity, sizeof(entity_t));
|
||||||
entity->id = (uint8_t)(entity - ENTITIES);
|
entity->id = (uint8_t)(entity - ENTITIES);
|
||||||
entity->type = type;
|
entity->type = type;
|
||||||
|
entity->chunkIndex = 0xFF;
|
||||||
|
|
||||||
if(ENTITY_CALLBACKS[type].init != NULL) ENTITY_CALLBACKS[type].init(entity);
|
if(ENTITY_CALLBACKS[type].init != NULL) ENTITY_CALLBACKS[type].init(entity);
|
||||||
}
|
}
|
||||||
@@ -41,6 +43,9 @@ void entityUpdate(entity_t *entity) {
|
|||||||
entityAnimUpdate(entity);
|
entityAnimUpdate(entity);
|
||||||
|
|
||||||
// Movement code.
|
// Movement code.
|
||||||
|
if(ENTITY_CALLBACKS[entity->type].freeMovement != NULL) {
|
||||||
|
ENTITY_CALLBACKS[entity->type].freeMovement(entity);
|
||||||
|
}
|
||||||
if(
|
if(
|
||||||
cutsceneModeIsInputAllowed() &&
|
cutsceneModeIsInputAllowed() &&
|
||||||
ENTITY_CALLBACKS[entity->type].movement != NULL
|
ENTITY_CALLBACKS[entity->type].movement != NULL
|
||||||
@@ -214,3 +219,31 @@ uint8_t entityGetAvailable() {
|
|||||||
|
|
||||||
return 0xFF;
|
return 0xFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void entitySetChunk(entity_t *entity, const uint8_t chunkIndex) {
|
||||||
|
assertNotNull(entity, "Entity pointer cannot be NULL");
|
||||||
|
|
||||||
|
if(entity->chunkIndex != 0xFF) {
|
||||||
|
chunk_t *old = mapGetChunk(entity->chunkIndex);
|
||||||
|
if(old != NULL) {
|
||||||
|
for(uint8_t i = 0; i < CHUNK_ENTITY_COUNT_MAX; i++) {
|
||||||
|
if(old->entities[i] != entity->id) continue;
|
||||||
|
old->entities[i] = 0xFF;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
entity->chunkIndex = chunkIndex;
|
||||||
|
|
||||||
|
if(chunkIndex != 0xFF) {
|
||||||
|
chunk_t *next = mapGetChunk(chunkIndex);
|
||||||
|
if(next != NULL) {
|
||||||
|
for(uint8_t i = 0; i < CHUNK_ENTITY_COUNT_MAX; i++) {
|
||||||
|
if(next->entities[i] != 0xFF) continue;
|
||||||
|
next->entities[i] = entity->id;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,10 +7,10 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "entitydir.h"
|
#include "entitydir.h"
|
||||||
#include "entityanim.h"
|
#include "anim/entityanim.h"
|
||||||
#include "entityinteract.h"
|
#include "interact/entityinteract.h"
|
||||||
#include "entitytype.h"
|
#include "entitytype.h"
|
||||||
#include "npc.h"
|
#include "npc/npc.h"
|
||||||
|
|
||||||
typedef struct map_s map_t;
|
typedef struct map_s map_t;
|
||||||
|
|
||||||
@@ -29,6 +29,8 @@ typedef struct entity_s {
|
|||||||
float_t animTime;
|
float_t animTime;
|
||||||
|
|
||||||
entityinteract_t interact;
|
entityinteract_t interact;
|
||||||
|
|
||||||
|
uint8_t chunkIndex;
|
||||||
} entity_t;
|
} entity_t;
|
||||||
|
|
||||||
extern entity_t ENTITIES[ENTITY_COUNT];
|
extern entity_t ENTITIES[ENTITY_COUNT];
|
||||||
@@ -111,3 +113,12 @@ entity_t *entityGetAt(const worldpos_t pos);
|
|||||||
* @return The index of an available entity, or 0xFF if none are available.
|
* @return The index of an available entity, or 0xFF if none are available.
|
||||||
*/
|
*/
|
||||||
uint8_t entityGetAvailable();
|
uint8_t entityGetAvailable();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assigns an entity to a chunk, removing it from its current chunk first.
|
||||||
|
* Pass 0xFF as chunkIndex to detach the entity from any chunk.
|
||||||
|
*
|
||||||
|
* @param entity Pointer to the entity.
|
||||||
|
* @param chunkIndex Index of the chunk to assign to, or 0xFF for none.
|
||||||
|
*/
|
||||||
|
void entitySetChunk(entity_t *entity, const uint8_t chunkIndex);
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "entity.h"
|
|
||||||
#include "time/time.h"
|
|
||||||
|
|
||||||
void entityAnimUpdate(entity_t *entity) {
|
|
||||||
if(entity->animation != ENTITY_ANIM_IDLE) {
|
|
||||||
entity->animTime -= TIME.delta;
|
|
||||||
if(entity->animTime <= 0) {
|
|
||||||
entity->animation = ENTITY_ANIM_IDLE;
|
|
||||||
entity->animTime = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(
|
|
||||||
entity->animation == ENTITY_ANIM_WALK ||
|
|
||||||
entity->animation == ENTITY_ANIM_RUN
|
|
||||||
) {
|
|
||||||
float_t duration = entity->animation == ENTITY_ANIM_WALK ?
|
|
||||||
ENTITY_ANIM_WALK_DURATION : ENTITY_ANIM_RUN_DURATION;
|
|
||||||
float_t t = 1.0f - (entity->animTime / duration);
|
|
||||||
entity->renderPosition[0] = (float_t)entity->lastPosition.x + t * (
|
|
||||||
(float_t)entity->position.x - (float_t)entity->lastPosition.x
|
|
||||||
);
|
|
||||||
entity->renderPosition[1] = (float_t)entity->lastPosition.y + t * (
|
|
||||||
(float_t)entity->position.y - (float_t)entity->lastPosition.y
|
|
||||||
);
|
|
||||||
entity->renderPosition[2] = (float_t)entity->lastPosition.z + t * (
|
|
||||||
(float_t)entity->position.z - (float_t)entity->lastPosition.z
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
entity->renderPosition[0] = (float_t)entity->position.x;
|
|
||||||
entity->renderPosition[1] = (float_t)entity->position.y;
|
|
||||||
entity->renderPosition[2] = (float_t)entity->position.z;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "time/time.h"
|
|
||||||
|
|
||||||
#define ENTITY_ANIM_TURN_DURATION TIME_TICKS_TO_TIME(2)
|
|
||||||
#define ENTITY_ANIM_WALK_DURATION TIME_TICKS_TO_TIME(6)
|
|
||||||
#define ENTITY_ANIM_RUN_DURATION TIME_TICKS_TO_TIME(3)
|
|
||||||
|
|
||||||
typedef struct entity_s entity_t;
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
ENTITY_ANIM_IDLE,
|
|
||||||
ENTITY_ANIM_TURN,
|
|
||||||
ENTITY_ANIM_WALK,
|
|
||||||
ENTITY_ANIM_RUN,
|
|
||||||
} entityanim_t;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the entity's animation state.
|
|
||||||
*
|
|
||||||
* @param entity Pointer to the entity to update.
|
|
||||||
*/
|
|
||||||
void entityAnimUpdate(entity_t *entity);
|
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "rpg/entity/player.h"
|
#include "rpg/entity/player.h"
|
||||||
#include "npc.h"
|
#include "npc/npc.h"
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
ENTITY_TYPE_NULL,
|
ENTITY_TYPE_NULL,
|
||||||
@@ -33,11 +33,17 @@ typedef struct {
|
|||||||
void (*init)(entity_t *entity);
|
void (*init)(entity_t *entity);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Movement callback for the entity type.
|
* Movement callback for the entity type. Gated by cutscene input.
|
||||||
* @param entity Pointer to the entity to move.
|
* @param entity Pointer to the entity to move.
|
||||||
*/
|
*/
|
||||||
void (*movement)(entity_t *entity);
|
void (*movement)(entity_t *entity);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free movement callback. Always runs regardless of cutscene state.
|
||||||
|
* @param entity Pointer to the entity to move.
|
||||||
|
*/
|
||||||
|
void (*freeMovement)(entity_t *entity);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interaction callback for the entity type.
|
* Interaction callback for the entity type.
|
||||||
* @param player Pointer to the player entity.
|
* @param player Pointer to the player entity.
|
||||||
@@ -58,6 +64,7 @@ static const entitycallback_t ENTITY_CALLBACKS[ENTITY_TYPE_COUNT] = {
|
|||||||
[ENTITY_TYPE_NPC] = {
|
[ENTITY_TYPE_NPC] = {
|
||||||
.init = npcInit,
|
.init = npcInit,
|
||||||
.movement = npcMovement,
|
.movement = npcMovement,
|
||||||
|
.freeMovement = npcFreeMovement,
|
||||||
.interact = npcInteract
|
.interact = npcInteract
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -5,5 +5,5 @@
|
|||||||
|
|
||||||
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||||
PUBLIC
|
PUBLIC
|
||||||
cutscene.c
|
entityinteract.c
|
||||||
)
|
)
|
||||||
+1
-1
@@ -5,7 +5,7 @@
|
|||||||
* https://opensource.org/licenses/MIT
|
* https://opensource.org/licenses/MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "entity.h"
|
#include "rpg/entity/entity.h"
|
||||||
#include "assert/assert.h"
|
#include "assert/assert.h"
|
||||||
#include "rpg/cutscene/cutscenesystem.h"
|
#include "rpg/cutscene/cutscenesystem.h"
|
||||||
#include "ui/rpg/uitextboxmain.h"
|
#include "ui/rpg/uitextboxmain.h"
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "entity.h"
|
|
||||||
#include "assert/assert.h"
|
|
||||||
|
|
||||||
#include "rpg/cutscene/scene/testcutscene.h"
|
|
||||||
#include "rpg/rpgtextbox.h"
|
|
||||||
|
|
||||||
void npcInit(entity_t *entity) {
|
|
||||||
assertNotNull(entity, "Entity pointer cannot be NULL");
|
|
||||||
}
|
|
||||||
|
|
||||||
void npcMovement(entity_t *entity) {
|
|
||||||
assertNotNull(entity, "Entity pointer cannot be NULL");
|
|
||||||
}
|
|
||||||
|
|
||||||
bool_t npcInteract(entity_t *player, entity_t *npc) {
|
|
||||||
assertNotNull(player, "Player entity pointer cannot be NULL");
|
|
||||||
assertNotNull(npc, "NPC entity pointer cannot be NULL");
|
|
||||||
|
|
||||||
cutsceneSystemStartCutscene(&TEST_CUTSCENE);
|
|
||||||
|
|
||||||
// rpgTextboxShow(RPG_TEXTBOX_POS_BOTTOM, "Hello World!");
|
|
||||||
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2025 Dominic Masters
|
|
||||||
*
|
|
||||||
* This software is released under the MIT License.
|
|
||||||
* https://opensource.org/licenses/MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "dusk.h"
|
|
||||||
|
|
||||||
typedef struct entity_s entity_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
void *nothing;
|
|
||||||
} npc_t;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes an NPC entity.
|
|
||||||
*
|
|
||||||
* @param entity Pointer to the entity structure to initialize.
|
|
||||||
*/
|
|
||||||
void npcInit(entity_t *entity);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates an NPC entity.
|
|
||||||
*
|
|
||||||
* @param entity Pointer to the entity structure to update.
|
|
||||||
*/
|
|
||||||
void npcMovement(entity_t *entity);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles interaction with an NPC entity.
|
|
||||||
*
|
|
||||||
* @param player Pointer to the player entity.
|
|
||||||
* @param npc Pointer to the NPC entity.
|
|
||||||
*/
|
|
||||||
bool_t npcInteract(entity_t *player, entity_t *npc);
|
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# Copyright (c) 2026 Dominic Masters
|
||||||
|
#
|
||||||
|
# This software is released under the MIT License.
|
||||||
|
# https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
|
# Sources
|
||||||
|
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||||
|
PUBLIC
|
||||||
|
npc.c
|
||||||
|
npcturn.c
|
||||||
|
npcwalk.c
|
||||||
|
npcpath.c
|
||||||
|
)
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "rpg/entity/entity.h"
|
||||||
|
#include "assert/assert.h"
|
||||||
|
|
||||||
|
#include "rpg/cutscene/scene/testcutscene.h"
|
||||||
|
#include "rpg/rpgtextbox.h"
|
||||||
|
|
||||||
|
const npcmovecallback_t NPC_MOVE_CALLBACKS[NPC_MOVE_TYPE_COUNT] = {
|
||||||
|
[NPC_MOVE_TYPE_NULL] = { NULL, NULL },
|
||||||
|
[NPC_MOVE_TYPE_RANDOM_TURN] = { npcRandomTurnInit, npcRandomTurnMovement },
|
||||||
|
[NPC_MOVE_TYPE_RANDOM_WALK] = {
|
||||||
|
npcRandomWalkInit,
|
||||||
|
npcRandomWalkMovement
|
||||||
|
},
|
||||||
|
[NPC_MOVE_TYPE_RANDOM_TURN_AND_WALK] = {
|
||||||
|
npcRandomTurnAndWalkInit,
|
||||||
|
npcRandomTurnAndWalkMovement
|
||||||
|
},
|
||||||
|
[NPC_MOVE_TYPE_PATH] = { npcPathInit, npcPathMovement, true },
|
||||||
|
};
|
||||||
|
|
||||||
|
void npcInit(entity_t *entity) {
|
||||||
|
assertNotNull(entity, "Entity pointer cannot be NULL");
|
||||||
|
}
|
||||||
|
|
||||||
|
void npcSetMoveType(entity_t *entity, const npcmovetype_t moveType) {
|
||||||
|
assertNotNull(entity, "Entity pointer cannot be NULL");
|
||||||
|
npc_t *npc = &entity->data.npc;
|
||||||
|
npc->moveType = moveType;
|
||||||
|
if(NPC_MOVE_CALLBACKS[moveType].init != NULL) {
|
||||||
|
NPC_MOVE_CALLBACKS[moveType].init(npc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void npcMovement(entity_t *entity) {
|
||||||
|
assertNotNull(entity, "Entity pointer cannot be NULL");
|
||||||
|
npc_t *npc = &entity->data.npc;
|
||||||
|
const npcmovecallback_t *cb = &NPC_MOVE_CALLBACKS[npc->moveType];
|
||||||
|
if(!cb->alwaysRun && cb->movement != NULL) cb->movement(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
void npcFreeMovement(entity_t *entity) {
|
||||||
|
assertNotNull(entity, "Entity pointer cannot be NULL");
|
||||||
|
npc_t *npc = &entity->data.npc;
|
||||||
|
const npcmovecallback_t *cb = &NPC_MOVE_CALLBACKS[npc->moveType];
|
||||||
|
if(cb->alwaysRun && cb->movement != NULL) cb->movement(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool_t npcInteract(entity_t *player, entity_t *npc) {
|
||||||
|
assertNotNull(player, "Player entity pointer cannot be NULL");
|
||||||
|
assertNotNull(npc, "NPC entity pointer cannot be NULL");
|
||||||
|
|
||||||
|
cutsceneSystemStartCutscene(&TEST_CUTSCENE);
|
||||||
|
// rpgTextboxShow(RPG_TEXTBOX_POS_BOTTOM, "Hello World!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "dusk.h"
|
||||||
|
#include "npcturn.h"
|
||||||
|
#include "npcwalk.h"
|
||||||
|
#include "npcpath.h"
|
||||||
|
|
||||||
|
typedef struct entity_s entity_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
NPC_MOVE_TYPE_NULL,
|
||||||
|
NPC_MOVE_TYPE_RANDOM_TURN,
|
||||||
|
NPC_MOVE_TYPE_RANDOM_WALK,
|
||||||
|
NPC_MOVE_TYPE_RANDOM_TURN_AND_WALK,
|
||||||
|
NPC_MOVE_TYPE_PATH,
|
||||||
|
NPC_MOVE_TYPE_COUNT
|
||||||
|
} npcmovetype_t;
|
||||||
|
|
||||||
|
typedef union {
|
||||||
|
npcrandomturn_t randomTurn;
|
||||||
|
npcrandomwalk_t randomWalk;
|
||||||
|
npcrandomturnandwalk_t randomTurnAndWalk;
|
||||||
|
npcpath_t path;
|
||||||
|
} npcmovedata_t;
|
||||||
|
|
||||||
|
typedef struct npc_s {
|
||||||
|
npcmovetype_t moveType;
|
||||||
|
npcmovedata_t moveData;
|
||||||
|
} npc_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
/** Called once when the move type is set. */
|
||||||
|
void (*init)(npc_t *npc);
|
||||||
|
/** Called each movement tick. */
|
||||||
|
void (*movement)(entity_t *entity);
|
||||||
|
/** True if movement runs regardless of cutscene state. */
|
||||||
|
bool_t alwaysRun;
|
||||||
|
} npcmovecallback_t;
|
||||||
|
|
||||||
|
extern const npcmovecallback_t NPC_MOVE_CALLBACKS[NPC_MOVE_TYPE_COUNT];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes an NPC entity.
|
||||||
|
*
|
||||||
|
* @param entity Pointer to the entity structure to initialize.
|
||||||
|
*/
|
||||||
|
void npcInit(entity_t *entity);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the movement type for an NPC entity.
|
||||||
|
*
|
||||||
|
* @param entity Pointer to the entity structure.
|
||||||
|
* @param moveType The movement type to set.
|
||||||
|
*/
|
||||||
|
void npcSetMoveType(entity_t *entity, const npcmovetype_t moveType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Movement callback for an NPC entity. Gated by cutscene input.
|
||||||
|
*
|
||||||
|
* @param entity Pointer to the entity structure to update.
|
||||||
|
*/
|
||||||
|
void npcMovement(entity_t *entity);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free movement callback for an NPC entity. Runs always-run move types
|
||||||
|
* regardless of cutscene state.
|
||||||
|
*
|
||||||
|
* @param entity Pointer to the entity structure to update.
|
||||||
|
*/
|
||||||
|
void npcFreeMovement(entity_t *entity);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles interaction with an NPC entity.
|
||||||
|
*
|
||||||
|
* @param player Pointer to the player entity.
|
||||||
|
* @param npc Pointer to the NPC entity.
|
||||||
|
*/
|
||||||
|
bool_t npcInteract(entity_t *player, entity_t *npc);
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "npc.h"
|
||||||
|
#include "rpg/entity/entity.h"
|
||||||
|
#include "rpg/overworld/worldpos.h"
|
||||||
|
|
||||||
|
void npcPathInit(npc_t *npc) {
|
||||||
|
npcpath_t *path = &npc->moveData.path;
|
||||||
|
path->count = 0;
|
||||||
|
path->index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void npcPathMovement(entity_t *entity) {
|
||||||
|
npcpath_t *path = &entity->data.npc.moveData.path;
|
||||||
|
if(path->count == 0) return;
|
||||||
|
if(!entityCanWalk(entity)) return;
|
||||||
|
|
||||||
|
// Advance past any waypoints already reached (including the current one).
|
||||||
|
worldpos_t *target = &path->positions[path->index];
|
||||||
|
if(worldPosIsEqual(entity->position, *target)) {
|
||||||
|
path->index = (path->index + 1) % path->count;
|
||||||
|
target = &path->positions[path->index];
|
||||||
|
// New target is the same tile - nothing to do this tick
|
||||||
|
if(worldPosIsEqual(entity->position, *target)) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
entitydir_t dir;
|
||||||
|
worldpos_t pos = entity->position;
|
||||||
|
if(pos.x != target->x) {
|
||||||
|
dir = pos.x < target->x ? ENTITY_DIR_EAST : ENTITY_DIR_WEST;
|
||||||
|
} else if(pos.y != target->y) {
|
||||||
|
dir = pos.y < target->y ? ENTITY_DIR_NORTH : ENTITY_DIR_SOUTH;
|
||||||
|
} else {
|
||||||
|
// x and y match but z differs - step to correct z via ramp logic
|
||||||
|
dir = pos.z < target->z ? ENTITY_DIR_NORTH : ENTITY_DIR_SOUTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
entityWalk(entity, dir);
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "dusk.h"
|
||||||
|
#include "rpg/overworld/worldpos.h"
|
||||||
|
|
||||||
|
typedef struct npc_s npc_t;
|
||||||
|
typedef struct entity_s entity_t;
|
||||||
|
|
||||||
|
/** Maximum number of waypoints in an NPC path. */
|
||||||
|
#define NPC_PATH_COUNT_MAX 8
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
worldpos_t positions[NPC_PATH_COUNT_MAX];
|
||||||
|
uint8_t count;
|
||||||
|
uint8_t index;
|
||||||
|
} npcpath_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the path movement data for an NPC.
|
||||||
|
*
|
||||||
|
* @param npc Pointer to the NPC to initialize.
|
||||||
|
*/
|
||||||
|
void npcPathInit(npc_t *npc);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Movement tick for an NPC following a path.
|
||||||
|
*
|
||||||
|
* @param entity Pointer to the entity to update.
|
||||||
|
*/
|
||||||
|
void npcPathMovement(entity_t *entity);
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "npc.h"
|
||||||
|
#include "rpg/entity/entity.h"
|
||||||
|
#include "util/random.h"
|
||||||
|
#include "time/time.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
void npcRandomTurnInit(npc_t *npc) {
|
||||||
|
npcrandomturn_t *turn = &npc->moveData.randomTurn;
|
||||||
|
turn->frequencyMin = NPC_RANDOM_TURN_FREQUENCY_MIN_DEFAULT;
|
||||||
|
turn->frequencyMax = NPC_RANDOM_TURN_FREQUENCY_MAX_DEFAULT;
|
||||||
|
turn->timer = randomFloat(turn->frequencyMin, turn->frequencyMax);
|
||||||
|
}
|
||||||
|
|
||||||
|
void npcRandomTurnMovement(entity_t *entity) {
|
||||||
|
npcrandomturn_t *turn = &entity->data.npc.moveData.randomTurn;
|
||||||
|
turn->timer -= TIME.delta;
|
||||||
|
if(turn->timer > 0.0f) return;
|
||||||
|
turn->timer = randomFloat(turn->frequencyMin, turn->frequencyMax);
|
||||||
|
if(entityCanTurn(entity)) entityTurn(entity, (entitydir_t)(rand() % 4));
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "dusk.h"
|
||||||
|
|
||||||
|
typedef struct npc_s npc_t;
|
||||||
|
typedef struct entity_s entity_t;
|
||||||
|
|
||||||
|
/** Default min/max seconds between NPC random-turn ticks. */
|
||||||
|
#define NPC_RANDOM_TURN_FREQUENCY_MIN_DEFAULT 2.0f
|
||||||
|
#define NPC_RANDOM_TURN_FREQUENCY_MAX_DEFAULT 4.0f
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
float_t frequencyMin;
|
||||||
|
float_t frequencyMax;
|
||||||
|
float_t timer;
|
||||||
|
} npcrandomturn_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the random-turn movement data for an NPC.
|
||||||
|
*
|
||||||
|
* @param npc Pointer to the NPC to initialize.
|
||||||
|
*/
|
||||||
|
void npcRandomTurnInit(npc_t *npc);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Movement tick for an NPC using random-turn movement.
|
||||||
|
*
|
||||||
|
* @param entity Pointer to the entity to update.
|
||||||
|
*/
|
||||||
|
void npcRandomTurnMovement(entity_t *entity);
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "npc.h"
|
||||||
|
#include "rpg/entity/entity.h"
|
||||||
|
#include "util/random.h"
|
||||||
|
#include "time/time.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
void npcRandomWalkInit(npc_t *npc) {
|
||||||
|
npcrandomwalk_t *walk = &npc->moveData.randomWalk;
|
||||||
|
walk->frequencyMin = NPC_RANDOM_WALK_FREQUENCY_MIN_DEFAULT;
|
||||||
|
walk->frequencyMax = NPC_RANDOM_WALK_FREQUENCY_MAX_DEFAULT;
|
||||||
|
walk->timer = randomFloat(walk->frequencyMin, walk->frequencyMax);
|
||||||
|
}
|
||||||
|
|
||||||
|
void npcRandomWalkMovement(entity_t *entity) {
|
||||||
|
npcrandomwalk_t *walk = &entity->data.npc.moveData.randomWalk;
|
||||||
|
walk->timer -= TIME.delta;
|
||||||
|
if(walk->timer > 0.0f) return;
|
||||||
|
walk->timer = randomFloat(walk->frequencyMin, walk->frequencyMax);
|
||||||
|
if(entityCanWalk(entity)) entityWalk(entity, (entitydir_t)(rand() % 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
void npcRandomTurnAndWalkInit(npc_t *npc) {
|
||||||
|
npcRandomTurnInit(npc);
|
||||||
|
npcrandomturnandwalk_t *tw = &npc->moveData.randomTurnAndWalk;
|
||||||
|
tw->walk.frequencyMin = NPC_RANDOM_WALK_FREQUENCY_MIN_DEFAULT;
|
||||||
|
tw->walk.frequencyMax = NPC_RANDOM_WALK_FREQUENCY_MAX_DEFAULT;
|
||||||
|
tw->walk.timer = randomFloat(tw->walk.frequencyMin, tw->walk.frequencyMax);
|
||||||
|
}
|
||||||
|
|
||||||
|
void npcRandomTurnAndWalkMovement(entity_t *entity) {
|
||||||
|
npcrandomturnandwalk_t *tw = &entity->data.npc.moveData.randomTurnAndWalk;
|
||||||
|
|
||||||
|
tw->turn.timer -= TIME.delta;
|
||||||
|
if(tw->turn.timer <= 0.0f) {
|
||||||
|
tw->turn.timer = randomFloat(tw->turn.frequencyMin, tw->turn.frequencyMax);
|
||||||
|
if(entityCanTurn(entity)) entityTurn(entity, (entitydir_t)(rand() % 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
tw->walk.timer -= TIME.delta;
|
||||||
|
if(tw->walk.timer <= 0.0f) {
|
||||||
|
tw->walk.timer = randomFloat(tw->walk.frequencyMin, tw->walk.frequencyMax);
|
||||||
|
if(entityCanWalk(entity)) entityWalk(entity, (entitydir_t)(rand() % 4));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "dusk.h"
|
||||||
|
#include "npcturn.h"
|
||||||
|
|
||||||
|
/** Default min/max seconds between NPC random-walk ticks. */
|
||||||
|
#define NPC_RANDOM_WALK_FREQUENCY_MIN_DEFAULT 2.0f
|
||||||
|
#define NPC_RANDOM_WALK_FREQUENCY_MAX_DEFAULT 5.0f
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
float_t frequencyMin;
|
||||||
|
float_t frequencyMax;
|
||||||
|
float_t timer;
|
||||||
|
} npcrandomwalk_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
npcrandomturn_t turn;
|
||||||
|
npcrandomwalk_t walk;
|
||||||
|
} npcrandomturnandwalk_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the random-walk movement data for an NPC.
|
||||||
|
*
|
||||||
|
* @param npc Pointer to the NPC to initialize.
|
||||||
|
*/
|
||||||
|
void npcRandomWalkInit(npc_t *npc);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Movement tick for an NPC using random-walk movement.
|
||||||
|
*
|
||||||
|
* @param entity Pointer to the entity to update.
|
||||||
|
*/
|
||||||
|
void npcRandomWalkMovement(entity_t *entity);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the random-turn-and-walk movement data for an NPC.
|
||||||
|
*
|
||||||
|
* @param npc Pointer to the NPC to initialize.
|
||||||
|
*/
|
||||||
|
void npcRandomTurnAndWalkInit(npc_t *npc);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Movement tick for an NPC using random-turn-and-walk movement.
|
||||||
|
*
|
||||||
|
* @param entity Pointer to the entity to update.
|
||||||
|
*/
|
||||||
|
void npcRandomTurnAndWalkMovement(entity_t *entity);
|
||||||
|
|
||||||
@@ -33,13 +33,24 @@ void playerInput(entity_t *entity) {
|
|||||||
// Can player act?
|
// Can player act?
|
||||||
if(UI_FOCUS.count > 0) return;
|
if(UI_FOCUS.count > 0) return;
|
||||||
|
|
||||||
// Turn
|
// Turn - only if not already holding the direction we face
|
||||||
const playerinputdirmap_t *dirMap = PLAYER_INPUT_DIR_MAP;
|
const playerinputdirmap_t *dirMap = PLAYER_INPUT_DIR_MAP;
|
||||||
|
bool_t holdingFaced = false;
|
||||||
|
do {
|
||||||
|
if(!inputIsDown(dirMap->action)) continue;
|
||||||
|
if(entity->direction != dirMap->direction) continue;
|
||||||
|
holdingFaced = true;
|
||||||
|
break;
|
||||||
|
} while((++dirMap)->action != 0xFF);
|
||||||
|
|
||||||
|
if(!holdingFaced) {
|
||||||
|
dirMap = PLAYER_INPUT_DIR_MAP;
|
||||||
do {
|
do {
|
||||||
if(!inputIsDown(dirMap->action)) continue;
|
if(!inputIsDown(dirMap->action)) continue;
|
||||||
if(entity->direction == dirMap->direction) continue;
|
if(entity->direction == dirMap->direction) continue;
|
||||||
return entityTurn(entity, dirMap->direction);
|
return entityTurn(entity, dirMap->direction);
|
||||||
} while((++dirMap)->action != 0xFF);
|
} while((++dirMap)->action != 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
// Walk / Run
|
// Walk / Run
|
||||||
bool_t running = inputIsDown(INPUT_ACTION_CANCEL);
|
bool_t running = inputIsDown(INPUT_ACTION_CANCEL);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "backpack.h"
|
#include "backpack.h"
|
||||||
|
#include "assert/assert.h"
|
||||||
|
|
||||||
backpack_t BACKPACK;
|
backpack_t BACKPACK;
|
||||||
|
|
||||||
|
|||||||
@@ -11,3 +11,4 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
|||||||
worldpos.c
|
worldpos.c
|
||||||
tile.c
|
tile.c
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ typedef struct chunk_s {
|
|||||||
meshvertex_t vertices[CHUNK_VERTEX_COUNT];
|
meshvertex_t vertices[CHUNK_VERTEX_COUNT];
|
||||||
uint32_t vertCount;
|
uint32_t vertCount;
|
||||||
mesh_t mesh;
|
mesh_t mesh;
|
||||||
color_t testColor;
|
|
||||||
|
|
||||||
// uint8_t meshCount;
|
// uint8_t meshCount;
|
||||||
// meshvertex_t vertices[CHUNK_VERTEX_COUNT_MAX];
|
// meshvertex_t vertices[CHUNK_VERTEX_COUNT_MAX];
|
||||||
|
|||||||
@@ -202,84 +202,65 @@ errorret_t mapDispose() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void mapChunkUnload(chunk_t* chunk) {
|
void mapChunkUnload(chunk_t* chunk) {
|
||||||
|
uint8_t chunkIndex = (uint8_t)(chunk - MAP.chunks);
|
||||||
for(uint8_t i = 0; i < CHUNK_ENTITY_COUNT_MAX; i++) {
|
for(uint8_t i = 0; i < CHUNK_ENTITY_COUNT_MAX; i++) {
|
||||||
if(chunk->entities[i] == 0xFF) break;
|
if(chunk->entities[i] == 0xFF) continue;
|
||||||
entity_t *entity = &ENTITIES[chunk->entities[i]];
|
entity_t *entity = &ENTITIES[chunk->entities[i]];
|
||||||
|
assertTrue(
|
||||||
|
entity->chunkIndex == chunkIndex,
|
||||||
|
"Entity chunk index does not match chunk"
|
||||||
|
);
|
||||||
|
if(entity->type == ENTITY_TYPE_PLAYER) {
|
||||||
|
entitySetChunk(entity, 0xFF);
|
||||||
|
} else {
|
||||||
entity->type = ENTITY_TYPE_NULL;
|
entity->type = ENTITY_TYPE_NULL;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
chunk->vertCount = 0;
|
chunk->vertCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
errorret_t mapChunkLoad(chunk_t* chunk) {
|
errorret_t mapChunkLoad(chunk_t* chunk) {
|
||||||
if(!mapIsLoaded()) errorThrow("No map loaded");
|
if(!mapIsLoaded()) errorThrow("No map loaded");
|
||||||
|
|
||||||
color_t color = COLOR_WHITE;
|
|
||||||
if(chunk->position.y % 2 == 0) {
|
|
||||||
if(chunk->position.x % 2 == 0) {
|
|
||||||
color = COLOR_BLACK;
|
|
||||||
} else {
|
|
||||||
color = COLOR_WHITE;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if(chunk->position.x % 2 == 0) {
|
|
||||||
color = COLOR_WHITE;
|
|
||||||
} else {
|
|
||||||
color = COLOR_BLACK;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// if(chunk->position.x == 0 && chunk->position.y == 0 && chunk->position.z == 0) {
|
|
||||||
// color = COLOR_RED;
|
|
||||||
// }
|
|
||||||
chunk->testColor = color;
|
|
||||||
|
|
||||||
memorySet(chunk->tiles, TILE_SHAPE_GROUND, sizeof(chunk->tiles));
|
|
||||||
memorySet(chunk->entities, 0xFF, sizeof(chunk->entities));
|
memorySet(chunk->entities, 0xFF, sizeof(chunk->entities));
|
||||||
chunk->vertCount = 0;
|
chunk->vertCount = 0;
|
||||||
|
|
||||||
if(chunk->position.z != 0) {
|
char_t name[64];
|
||||||
|
stringFormat(
|
||||||
|
name, sizeof(name),
|
||||||
|
"chunks/%d_%d_%d.dcf",
|
||||||
|
(int32_t)chunk->position.x,
|
||||||
|
(int32_t)chunk->position.y,
|
||||||
|
(int32_t)chunk->position.z
|
||||||
|
);
|
||||||
|
|
||||||
|
if(!assetFileExists(name)) {
|
||||||
|
memorySet(chunk->tiles, TILE_SHAPE_GROUND, sizeof(chunk->tiles));
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set Chunk sprites.
|
assetentry_t *entry = assetLock(name, ASSET_LOADER_TYPE_CHUNK, NULL);
|
||||||
vec3 spriteMin = {
|
assertNotNull(entry, "Failed to get chunk asset entry");
|
||||||
chunk->position.x * CHUNK_WIDTH,
|
|
||||||
chunk->position.y * CHUNK_HEIGHT,
|
|
||||||
chunk->position.z * CHUNK_DEPTH
|
|
||||||
};
|
|
||||||
|
|
||||||
spritebatchsprite_t sprites[CHUNK_TILE_COUNT];
|
errorret_t ret = assetRequireLoaded(entry);
|
||||||
uint32_t i = 0;
|
if(errorIsNotOk(ret)) {
|
||||||
for(uint8_t x = 0; x < CHUNK_WIDTH; x++) {
|
assetUnlockEntry(entry);
|
||||||
for(uint8_t y = 0; y < CHUNK_HEIGHT; y++) {
|
return ret;
|
||||||
glm_vec3_copy(spriteMin, sprites[i].min);
|
|
||||||
glm_vec3_add(
|
|
||||||
sprites[i].min,
|
|
||||||
(vec3){ x, y, 0 },
|
|
||||||
sprites[i].min
|
|
||||||
);
|
|
||||||
|
|
||||||
glm_vec3_copy(sprites[i].min, sprites[i].max);
|
|
||||||
glm_vec3_add(
|
|
||||||
sprites[i].max,
|
|
||||||
(vec3){ 1, 1, 0 },
|
|
||||||
sprites[i].max
|
|
||||||
);
|
|
||||||
|
|
||||||
glm_vec2_copy((vec2){ 0, 0 }, sprites[i].uvMin);
|
|
||||||
glm_vec2_copy((vec2){ 1, 1 }, sprites[i].uvMax);
|
|
||||||
|
|
||||||
i++;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
chunk->vertCount = i * QUAD_VERTEX_COUNT;
|
memoryCopy(
|
||||||
spriteBatchBufferToMesh(
|
chunk->tiles, entry->data.chunk.tiles, sizeof(chunk->tiles)
|
||||||
sprites,
|
);
|
||||||
i,
|
chunk->vertCount = entry->data.chunk.vertCount;
|
||||||
|
memoryCopy(
|
||||||
chunk->vertices,
|
chunk->vertices,
|
||||||
chunk->vertCount
|
entry->data.chunk.vertices,
|
||||||
|
chunk->vertCount * sizeof(meshvertex_t)
|
||||||
);
|
);
|
||||||
errorChain(meshFlush(&chunk->mesh, 0, chunk->vertCount));
|
assetUnlockEntry(entry);
|
||||||
|
|
||||||
|
if(chunk->vertCount == 0) errorOk();
|
||||||
|
errorChain(meshFlush(&chunk->mesh, 0, chunk->vertCount));
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,13 +12,13 @@
|
|||||||
|
|
||||||
#define CHUNK_WIDTH 16
|
#define CHUNK_WIDTH 16
|
||||||
#define CHUNK_HEIGHT 16
|
#define CHUNK_HEIGHT 16
|
||||||
#define CHUNK_DEPTH 8
|
#define CHUNK_DEPTH 32
|
||||||
|
|
||||||
#define CHUNK_TILE_COUNT (CHUNK_WIDTH * CHUNK_HEIGHT * CHUNK_DEPTH)
|
#define CHUNK_TILE_COUNT (CHUNK_WIDTH * CHUNK_HEIGHT * CHUNK_DEPTH)
|
||||||
|
|
||||||
#define MAP_CHUNK_WIDTH 4
|
#define MAP_CHUNK_WIDTH 5
|
||||||
#define MAP_CHUNK_HEIGHT 4
|
#define MAP_CHUNK_HEIGHT 3
|
||||||
#define MAP_CHUNK_DEPTH 3
|
#define MAP_CHUNK_DEPTH 1
|
||||||
#define MAP_CHUNK_COUNT (MAP_CHUNK_WIDTH * MAP_CHUNK_HEIGHT * MAP_CHUNK_DEPTH)
|
#define MAP_CHUNK_COUNT (MAP_CHUNK_WIDTH * MAP_CHUNK_HEIGHT * MAP_CHUNK_DEPTH)
|
||||||
|
|
||||||
#define ENTITY_COUNT 32
|
#define ENTITY_COUNT 32
|
||||||
|
|||||||
@@ -37,13 +37,33 @@ errorret_t rpgInit(void) {
|
|||||||
entityInit(ent, ENTITY_TYPE_PLAYER);
|
entityInit(ent, ENTITY_TYPE_PLAYER);
|
||||||
RPG_CAMERA.mode = RPG_CAMERA_MODE_FOLLOW_ENTITY;
|
RPG_CAMERA.mode = RPG_CAMERA_MODE_FOLLOW_ENTITY;
|
||||||
RPG_CAMERA.followEntity.followEntityId = ent->id;
|
RPG_CAMERA.followEntity.followEntityId = ent->id;
|
||||||
|
{
|
||||||
|
chunkpos_t cp;
|
||||||
|
worldPosToChunkPos(&ent->position, &cp);
|
||||||
|
chunkindex_t ci = mapGetChunkIndexAt(cp);
|
||||||
|
if(ci != -1) entitySetChunk(ent, (uint8_t)ci);
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t npcIndex = entityGetAvailable();
|
uint8_t npcIndex = entityGetAvailable();
|
||||||
entity_t *npc = &ENTITIES[npcIndex];
|
entity_t *npc = &ENTITIES[npcIndex];
|
||||||
entityInit(npc, ENTITY_TYPE_NPC);
|
entityInit(npc, ENTITY_TYPE_NPC);
|
||||||
|
npcSetMoveType(npc, NPC_MOVE_TYPE_PATH);
|
||||||
npc->position = (worldpos_t){ 3, 3, 0 };
|
npc->position = (worldpos_t){ 3, 3, 0 };
|
||||||
npc->interact.type = ENTITY_INTERACT_PRINT;
|
npc->interact.type = ENTITY_INTERACT_PRINT;
|
||||||
npc->interact.data.message = "hello world";
|
npc->interact.data.message = "hello world";
|
||||||
|
{
|
||||||
|
chunkpos_t cp;
|
||||||
|
worldPosToChunkPos(&npc->position, &cp);
|
||||||
|
chunkindex_t ci = mapGetChunkIndexAt(cp);
|
||||||
|
if(ci != -1) entitySetChunk(npc, (uint8_t)ci);
|
||||||
|
}
|
||||||
|
|
||||||
|
npcpath_t *path = &npc->data.npc.moveData.path;
|
||||||
|
path->positions[0] = (worldpos_t){ 3, 3, 0 };
|
||||||
|
path->positions[1] = (worldpos_t){ 10, 3, 0 };
|
||||||
|
path->positions[2] = (worldpos_t){ 10, 10, 0 };
|
||||||
|
path->positions[3] = (worldpos_t){ 3, 10, 0 };
|
||||||
|
path->count = 4;
|
||||||
|
|
||||||
// All Good!
|
// All Good!
|
||||||
errorOk();
|
errorOk();
|
||||||
@@ -72,6 +92,7 @@ errorret_t rpgUpdate(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
errorret_t rpgDispose(void) {
|
errorret_t rpgDispose(void) {
|
||||||
|
cutsceneSystemDispose();
|
||||||
errorChain(mapDispose());
|
errorChain(mapDispose());
|
||||||
|
|
||||||
errorOk();
|
errorOk();
|
||||||
|
|||||||
@@ -13,15 +13,32 @@
|
|||||||
#include "display/screen/screen.h"
|
#include "display/screen/screen.h"
|
||||||
#include "display/shader/shaderunlit.h"
|
#include "display/shader/shaderunlit.h"
|
||||||
#include "display/spritebatch/spritebatch.h"
|
#include "display/spritebatch/spritebatch.h"
|
||||||
|
#include "display/texture/texture.h"
|
||||||
|
|
||||||
#include "rpg/overworld/map.h"
|
#include "rpg/overworld/map.h"
|
||||||
#include "rpg/entity/entity.h"
|
#include "rpg/entity/entity.h"
|
||||||
#include "rpg/rpgcamera.h"
|
#include "rpg/rpgcamera.h"
|
||||||
|
|
||||||
|
#define TEXTURE_CHUNK_SIZE 16
|
||||||
|
|
||||||
|
static texture_t TEXTURE_CHUNK;
|
||||||
|
static color_t TEXTURE_CHUNK_PIXELS[TEXTURE_CHUNK_SIZE * TEXTURE_CHUNK_SIZE];
|
||||||
|
|
||||||
errorret_t sceneOverworldInit(scenedata_t *sceneData) {
|
errorret_t sceneOverworldInit(scenedata_t *sceneData) {
|
||||||
assertNotNull(sceneData, "Scene data cannot be null");
|
assertNotNull(sceneData, "Scene data cannot be null");
|
||||||
|
|
||||||
|
for(uint32_t i = 0; i < TEXTURE_CHUNK_SIZE * TEXTURE_CHUNK_SIZE; i++) {
|
||||||
|
uint8_t r = (uint8_t)((i & 7) * 36);
|
||||||
|
uint8_t g = (uint8_t)(((i >> 3) & 7) * 36);
|
||||||
|
uint8_t b = (uint8_t)((i >> 6) * 85);
|
||||||
|
TEXTURE_CHUNK_PIXELS[i] = color4b(r, g, b, 255);
|
||||||
|
}
|
||||||
|
errorChain(textureInit(
|
||||||
|
&TEXTURE_CHUNK,
|
||||||
|
TEXTURE_CHUNK_SIZE, TEXTURE_CHUNK_SIZE,
|
||||||
|
TEXTURE_FORMAT_RGBA,
|
||||||
|
(texturedata_t){ .rgbaColors = TEXTURE_CHUNK_PIXELS }
|
||||||
|
));
|
||||||
|
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
@@ -84,7 +101,7 @@ errorret_t sceneOverworldRender(scenedata_t *sceneData) {
|
|||||||
shadermaterial_t chunkMaterial = {
|
shadermaterial_t chunkMaterial = {
|
||||||
.unlit = {
|
.unlit = {
|
||||||
.color = COLOR_WHITE,
|
.color = COLOR_WHITE,
|
||||||
.texture = NULL
|
.texture = &TEXTURE_CHUNK
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -98,7 +115,6 @@ errorret_t sceneOverworldRender(scenedata_t *sceneData) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
chunkMaterial.unlit.color = chunk->testColor;
|
|
||||||
errorChain(shaderSetMaterial(&SHADER_UNLIT, &chunkMaterial));
|
errorChain(shaderSetMaterial(&SHADER_UNLIT, &chunkMaterial));
|
||||||
errorChain(meshDraw(&chunk->mesh, 0, chunk->vertCount));
|
errorChain(meshDraw(&chunk->mesh, 0, chunk->vertCount));
|
||||||
i++;
|
i++;
|
||||||
@@ -109,35 +125,30 @@ errorret_t sceneOverworldRender(scenedata_t *sceneData) {
|
|||||||
|
|
||||||
// Entities
|
// Entities
|
||||||
{
|
{
|
||||||
uint8_t spriteCount = 0;
|
|
||||||
spritebatchsprite_t sprites[ENTITY_COUNT];
|
|
||||||
for(uint8_t i = 0; i < ENTITY_COUNT; i++) {
|
for(uint8_t i = 0; i < ENTITY_COUNT; i++) {
|
||||||
entity_t *ent = &ENTITIES[i];
|
entity_t *ent = &ENTITIES[i];
|
||||||
if(ent->type == ENTITY_TYPE_NULL) continue;
|
if(ent->type == ENTITY_TYPE_NULL) continue;
|
||||||
|
|
||||||
glm_vec3_copy(ent->renderPosition, sprites[spriteCount].min);
|
spritebatchsprite_t sprite;
|
||||||
glm_vec3_copy(ent->renderPosition, sprites[spriteCount].max);
|
glm_vec3_copy(ent->renderPosition, sprite.min);
|
||||||
glm_vec3_add(
|
glm_vec3_copy(ent->renderPosition, sprite.max);
|
||||||
sprites[spriteCount].max,
|
glm_vec3_add(sprite.max, (vec3){ 1, 1, 0 }, sprite.max);
|
||||||
(vec3){ 1, 1, 0 },
|
glm_vec2_copy((vec2){ 0, 0 }, sprite.uvMin);
|
||||||
sprites[spriteCount].max
|
glm_vec2_copy((vec2){ 1, 1 }, sprite.uvMax);
|
||||||
);
|
|
||||||
|
|
||||||
glm_vec2_copy((vec2){ 0, 0 }, sprites[spriteCount].uvMin);
|
color_t color;
|
||||||
glm_vec2_copy((vec2){ 1, 1 }, sprites[spriteCount].uvMax);
|
switch(ent->direction) {
|
||||||
|
case ENTITY_DIR_NORTH: color = COLOR_YELLOW; break;
|
||||||
spriteCount++;
|
case ENTITY_DIR_EAST: color = COLOR_RED; break;
|
||||||
|
case ENTITY_DIR_SOUTH: color = COLOR_GREEN; break;
|
||||||
|
case ENTITY_DIR_WEST: color = COLOR_BLUE; break;
|
||||||
|
default: color = COLOR_CYAN; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(spriteCount) {
|
|
||||||
shadermaterial_t material = {
|
shadermaterial_t material = {
|
||||||
.unlit = {
|
.unlit = { .color = color, .texture = NULL }
|
||||||
.color = COLOR_CYAN,
|
|
||||||
.texture = NULL
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
// material.unlit.texture = &TEXTURE_TEST;
|
spriteBatchBuffer(&sprite, 1, &SHADER_UNLIT, material);
|
||||||
spriteBatchBuffer(sprites, spriteCount, &SHADER_UNLIT, material);
|
|
||||||
spriteBatchFlush();
|
spriteBatchFlush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -148,7 +159,7 @@ errorret_t sceneOverworldRender(scenedata_t *sceneData) {
|
|||||||
errorret_t sceneOverworldDispose(scenedata_t *sceneData) {
|
errorret_t sceneOverworldDispose(scenedata_t *sceneData) {
|
||||||
assertNotNull(sceneData, "Scene data cannot be null");
|
assertNotNull(sceneData, "Scene data cannot be null");
|
||||||
|
|
||||||
|
errorChain(textureDispose(&TEXTURE_CHUNK));
|
||||||
|
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
@@ -10,6 +10,7 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
|||||||
crypt.c
|
crypt.c
|
||||||
endian.c
|
endian.c
|
||||||
memory.c
|
memory.c
|
||||||
|
random.c
|
||||||
string.c
|
string.c
|
||||||
math.c
|
math.c
|
||||||
sort.c
|
sort.c
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "random.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
float_t randomFloat(const float_t min, const float_t max) {
|
||||||
|
return min + ((float_t)rand() / (float_t)RAND_MAX) * (max - min);
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "dusk.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a random float between min (inclusive) and max (exclusive).
|
||||||
|
*
|
||||||
|
* @param min Lower bound.
|
||||||
|
* @param max Upper bound.
|
||||||
|
* @returns A random float in [min, max).
|
||||||
|
*/
|
||||||
|
float_t randomFloat(const float_t min, const float_t max);
|
||||||
Reference in New Issue
Block a user