Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fb41147197 | ||
|
|
aa9f5eff20 | ||
|
|
18c416d4d2 | ||
|
|
77fd434226 | ||
|
|
122f159b94 | ||
|
|
35553c18dd | ||
|
|
bef71d391a | ||
|
|
b48b3a4f8f | ||
|
|
076f6cf97c |
@@ -1,5 +1,8 @@
|
||||
node_modules/
|
||||
dist/
|
||||
data/
|
||||
backups/
|
||||
game_assets/
|
||||
*.tsbuildinfo
|
||||
vite.config.js
|
||||
vite.config.d.ts
|
||||
@@ -9,3 +12,4 @@ vite.config.d.ts
|
||||
!.env.example
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
/public/basis/
|
||||
|
||||
@@ -6,6 +6,13 @@ These requirements apply to every screen, feature, game system, and code change
|
||||
|
||||
AYN Thor Android app is the primary product. Browser build is a secondary access path for players without an AYN Thor. Do not weaken the Thor experience to simplify the browser version; provide an adaptive browser fallback instead.
|
||||
|
||||
## Game asset workflow
|
||||
|
||||
- `game_assets/` is an ignored local source library. Never load or import a runtime asset directly from it.
|
||||
- When adopting a model, animation, texture, audio file, or other game asset from `game_assets/`, copy it into the tracked `src/assets/game/` runtime library first. Preserve its path relative to `game_assets/`.
|
||||
- Use `pnpm assets:import <path-within-game_assets>` for the copy. Use `--replace` only to deliberately update an existing runtime copy. Copy required companion files and license/attribution notices with assets that are not self-contained.
|
||||
- Reference only `src/assets/game/` copies from game code. Keep `game_assets/` as source material for future imports, conversions, and authoring work.
|
||||
|
||||
## AYN Thor display and layout targets
|
||||
|
||||
Design and test against both physical displays in landscape orientation. Physical panel resolution and Android CSS/layout viewport are different concepts; record and validate both.
|
||||
@@ -84,14 +91,29 @@ Stable locked 60 FPS is a release requirement, not a best-effort goal.
|
||||
- Favor algorithms and data layouts that reduce CPU and RAM together. Do not trade a large permanent memory increase for a tiny unmeasured CPU gain.
|
||||
- Performance fixes must preserve deterministic rules, input responsiveness, visual clarity, and controller accessibility.
|
||||
|
||||
## Verification scope
|
||||
|
||||
- Verify the changed code and its direct dependencies. Do not run tests, visual QA, or controller checks for unrelated screens or systems by default.
|
||||
- For a screen change, check that screen's applicable display layouts, browser fallback, and controller path only.
|
||||
- For a reusable mechanic or domain change, run its focused tests plus direct consumers affected by the change.
|
||||
- Run viewport, display-layout, and browser-fallback testing only when a change adds or modifies a screen, menu, modal, HUD, overlay, or other UI/layout behavior.
|
||||
- Combat rules, encounter mechanics, world-space telegraphs, animations, VFX, tuning, and other gameplay-only changes do not require viewport testing unless they also change UI or screen layout.
|
||||
- Expand to broader regression or full-suite verification only for shared foundations, cross-cutting changes, risky refactors, release validation, or when explicitly requested.
|
||||
- State what was verified and any deliberately unverified scope in the handoff.
|
||||
|
||||
## Definition of done
|
||||
|
||||
A screen or mechanic is not complete until:
|
||||
A screen or UI change is not complete until:
|
||||
|
||||
1. Its main-display UI is designed and verified at the approximately `960 x 540` Android CSS/layout viewport, with rendering validated against the `1920 x 1080` physical panel.
|
||||
2. Its applicable secondary UI is designed and verified at the approximately `620 x 540` Android CSS/layout viewport, with rendering validated against the `1240 x 1080` physical panel.
|
||||
3. It has a complete single-display browser fallback.
|
||||
4. Every action is reachable and understandable using only a controller, with no click-to-focus step.
|
||||
5. Shared game logic is modular, typed, and testable outside the renderer/UI.
|
||||
6. It introduces no known resource leak, unbounded work, or avoidable hot-path allocation.
|
||||
7. Representative Thor hardware sustains the locked 60 FPS target, or the change includes measured evidence and an explicit approved exception.
|
||||
|
||||
Every code change is not complete until:
|
||||
|
||||
1. Shared game logic is modular, typed, and testable outside the renderer/UI.
|
||||
2. It introduces no known resource leak, unbounded work, or avoidable hot-path allocation.
|
||||
3. Representative Thor hardware sustains the locked 60 FPS target, or the change includes measured evidence and an explicit approved exception.
|
||||
|
||||
Gameplay-only mechanic and domain changes do not inherit the screen/UI viewport requirements above.
|
||||
|
||||
@@ -4,6 +4,17 @@ This game uses the same proven local-Gitea pattern as `testgame`: clone from the
|
||||
Gitea bare repository on the TrueNAS filesystem, mount that working checkout
|
||||
into one Node container, and update it with a local Git pull plus app restart.
|
||||
|
||||
## What the TrueNAS server does
|
||||
|
||||
The `iwanttoheal-mmo` TrueNAS app is the game's live online server. One Node
|
||||
process serves the browser bundle and authenticated `/api` routes on port `4173`.
|
||||
The reverse proxy exposes both at `https://iwanttoheal.phenomrom.com`.
|
||||
|
||||
SQLite persists accounts, sessions, cloud saves, boss-kill rankings, and
|
||||
roguelike records under `/app/data/game.db`. The separate data mount survives
|
||||
container replacement. TrueNAS Gitea remains the source repository used for
|
||||
deployment; it is not the game database.
|
||||
|
||||
## Paths
|
||||
|
||||
```text
|
||||
@@ -13,6 +24,9 @@ Local Gitea bare repository:
|
||||
Runnable working checkout:
|
||||
/mnt/usbssds/apps/iwanttoheal-mmo/app
|
||||
|
||||
Persistent game data:
|
||||
/mnt/usbssds/apps/iwanttoheal-mmo/data
|
||||
|
||||
Public URL:
|
||||
https://iwanttoheal.phenomrom.com
|
||||
|
||||
@@ -39,7 +53,7 @@ sudo find /mnt -type d -name "i-want-to-heal-mmo.git" -prune -print 2>/dev/null
|
||||
Clone entirely through the local filesystem:
|
||||
|
||||
```sh
|
||||
sudo mkdir -p /mnt/usbssds/apps/iwanttoheal-mmo
|
||||
sudo mkdir -p /mnt/usbssds/apps/iwanttoheal-mmo/{app,data}
|
||||
sudo git config --global --add safe.directory \
|
||||
/mnt/.ix-apps/app_mounts/gitea/data/git/repositories/phenom/i-want-to-heal-mmo.git
|
||||
sudo git clone \
|
||||
@@ -72,8 +86,10 @@ services:
|
||||
iwanttoheal:
|
||||
image: node:24-bookworm-slim
|
||||
command: >-
|
||||
sh -lc "corepack pnpm install --frozen-lockfile && corepack pnpm run build && corepack pnpm start"
|
||||
sh -lc "corepack pnpm install --frozen-lockfile && corepack pnpm run db:init && corepack pnpm run build && corepack pnpm start"
|
||||
environment:
|
||||
CORS_ORIGINS: "https://iwanttoheal.phenomrom.com,capacitor://localhost,http://localhost"
|
||||
DATA_DIR: /app/data
|
||||
HOST: 0.0.0.0
|
||||
PORT: "4173"
|
||||
init: true
|
||||
@@ -82,11 +98,21 @@ services:
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- /mnt/usbssds/apps/iwanttoheal-mmo/app:/app
|
||||
- /mnt/usbssds/apps/iwanttoheal-mmo/data:/app/data
|
||||
working_dir: /app
|
||||
```
|
||||
|
||||
This game has no server database. Do not add `db:init`, `/app/data`, cookie,
|
||||
CORS, or proxy environment settings from the older game.
|
||||
Do not remove the `/app/data` mount or place the SQLite database inside the source
|
||||
checkout. Back up `/mnt/usbssds/apps/iwanttoheal-mmo/data/game.db` before database
|
||||
migrations or destructive maintenance.
|
||||
|
||||
From the app checkout, create a consistent SQLite backup with:
|
||||
|
||||
```sh
|
||||
DATA_DIR=/mnt/usbssds/apps/iwanttoheal-mmo/data \
|
||||
BACKUP_DIR=/mnt/usbssds/apps/iwanttoheal-mmo/backups \
|
||||
corepack pnpm run db:backup
|
||||
```
|
||||
|
||||
The separate volume protects the old app files and data. The YAML still maps
|
||||
host port `4173`, so the old and MMO apps cannot run simultaneously while both
|
||||
|
||||
@@ -2,16 +2,23 @@
|
||||
|
||||
Playable low-poly third-person combat vertical slice for AYN Thor's dual displays.
|
||||
|
||||
Offline-first frontend includes three timestamped save slots, optional account sync, local/online overwrite controls, Hunter Profile statistics, boss collection logs, Settings, and PvE/PvP mode entry points.
|
||||
Offline-first frontend includes three timestamped save slots, TrueNAS accounts
|
||||
and cloud saves, Hunter Profile statistics, boss and roguelike leaderboards,
|
||||
boss collection logs, Settings, and PvE/PvP mode entry points. Offline saves
|
||||
remain playable without an account and can be uploaded after sign-in.
|
||||
|
||||
## Run
|
||||
|
||||
```bash
|
||||
pnpm install
|
||||
pnpm dev:api
|
||||
# In a second terminal:
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
The development server listens on `0.0.0.0:4173`.
|
||||
The Vite development server listens on `0.0.0.0:4173` and proxies `/api` to the
|
||||
local production/API server on `127.0.0.1:4174`. Both processes use the same
|
||||
client API contract as TrueNAS.
|
||||
|
||||
## Android / AYN Thor test APK
|
||||
|
||||
@@ -23,6 +30,10 @@ installable debug APK:
|
||||
pnpm android:apk
|
||||
```
|
||||
|
||||
Build explicitly against the TrueNAS API with `pnpm android:apk:truenas`.
|
||||
Native builds also default to `https://iwanttoheal.phenomrom.com` when no API
|
||||
base override is supplied.
|
||||
|
||||
Output is written under `android/app/build/outputs/apk/debug/`. With Android
|
||||
platform tools and a connected Thor, build and install it with:
|
||||
|
||||
@@ -41,12 +52,25 @@ state rather than two independent WebViews.
|
||||
Complete first-install, local-Gitea clone, YAML, update, and verification steps:
|
||||
[DEPLOYMENT.md](DEPLOYMENT.md).
|
||||
|
||||
The production preview server uses the existing deployment address and port:
|
||||
The live TrueNAS web server uses the existing deployment address and port:
|
||||
|
||||
- Public URL: `https://iwanttoheal.phenomrom.com`
|
||||
- Host/container port: `4173`
|
||||
- App directory: `/mnt/usbssds/apps/iwanttoheal-mmo/app`
|
||||
|
||||
### Server architecture
|
||||
|
||||
TrueNAS is the online production game server. Its Node process serves the browser
|
||||
application and authenticated `/api` routes on port `4173`; the reverse proxy
|
||||
exposes both at the public URL above. SQLite data persists at `/app/data/game.db`
|
||||
through the separate TrueNAS data mount.
|
||||
|
||||
The server owns account credentials, 30-day sessions, three cloud-save slots per
|
||||
account, boss-kill rankings, and roguelike highest-round rankings. Passwords use
|
||||
scrypt with per-account salts; clients store only opaque session tokens. Local
|
||||
saves remain available for offline play. Gitea is a separate TrueNAS service used
|
||||
for source hosting and deployment.
|
||||
|
||||
Clone from the TrueNAS-local Gitea bare repository into the app directory, then
|
||||
deploy `compose.yaml`. The expected source path is:
|
||||
|
||||
@@ -54,18 +78,16 @@ deploy `compose.yaml`. The expected source path is:
|
||||
/mnt/.ix-apps/app_mounts/gitea/data/git/repositories/phenom/i-want-to-heal-mmo.git
|
||||
```
|
||||
|
||||
Compared with the old game configuration:
|
||||
Current configuration:
|
||||
|
||||
- use `corepack pnpm install --frozen-lockfile`, not `npm ci`;
|
||||
- remove `npm run db:init` because this game has no server database;
|
||||
- remove `COOKIE_SECURE`, `CORS_ORIGINS`, and `TRUST_PROXY`; the static Vite
|
||||
preview server does not consume them;
|
||||
- remove `/app/data`; offline saves live in each player's browser/Android WebView
|
||||
storage, not on TrueNAS.
|
||||
- run `pnpm db:init` before starting the production server;
|
||||
- mount `/mnt/usbssds/apps/iwanttoheal-mmo/data` at `/app/data`;
|
||||
- keep `CORS_ORIGINS` configured for the public site and Capacitor host;
|
||||
- keep the source checkout and persistent data in separate mounts.
|
||||
|
||||
The existing reverse proxy can keep forwarding the public hostname to port
|
||||
`4173`. Add server data and authentication environment variables only when an
|
||||
actual sync API is introduced.
|
||||
The existing reverse proxy forwards the public hostname to port `4173`, including
|
||||
all `/api` routes.
|
||||
|
||||
## Publish updates to Gitea
|
||||
|
||||
@@ -109,18 +131,18 @@ outside the repository.
|
||||
- `WASD` / left stick: move
|
||||
- `Q` and `E` / D-pad: cycle party target
|
||||
- `1`–`6`: cast Smite, Renew, Shield, Purify, Radiance, Flash Heal
|
||||
- Gamepad: `X`, `Y`, `B`, `A`, `LB`, `RB` map to those abilities
|
||||
- Gamepad: PlayStation `□`, `△`, `○`, `✕`, `L1`, `R1` map to those abilities
|
||||
- `M`: tactical map
|
||||
- `I`: inventory and item tooltip
|
||||
- `Enter` / Start: begin or reset encounter
|
||||
- `Enter` / `START`: begin or reset encounter
|
||||
|
||||
Touch controls on lower display support party targeting, ability casting, map, and inventory.
|
||||
|
||||
## Prototype scope
|
||||
## Current game scope
|
||||
|
||||
- Five-member AI party with Disc Priest healer
|
||||
- Animated Druid healer, Knight tank, Ranger, Rogue, and Mage party models
|
||||
- One animated boss: Bulldrome, using the Bull model at 180% of its original prototype scale
|
||||
- Animated bosses using canonical tracked game models
|
||||
- Telegraph, charge, 0.75-second knockdown, and return-to-tank behavior
|
||||
- Three-charge cycle into a five-second stack marker and 300-damage shared pounce
|
||||
- Tank pressure, party-wide Cinder Nova, dispellable Ember Brand
|
||||
|
||||
@@ -102,7 +102,9 @@ public abstract class ControllerBridgeActivity extends BridgeActivity {
|
||||
|
||||
float leftStickX = event.getAxisValue(MotionEvent.AXIS_X);
|
||||
float leftStickY = event.getAxisValue(MotionEvent.AXIS_Y);
|
||||
dispatchNativeControllerMotion(leftStickX, leftStickY);
|
||||
float rightStickX = controllerAxisValue(event, MotionEvent.AXIS_Z, MotionEvent.AXIS_RX);
|
||||
float rightStickY = controllerAxisValue(event, MotionEvent.AXIS_RZ, MotionEvent.AXIS_RY);
|
||||
dispatchNativeControllerMotion(leftStickX, leftStickY, rightStickX, rightStickY);
|
||||
|
||||
Set<String> currentTokens = new HashSet<>();
|
||||
addAxisTokens(currentTokens, event.getAxisValue(MotionEvent.AXIS_HAT_X), "Button14", "Button15");
|
||||
@@ -150,6 +152,14 @@ public abstract class ControllerBridgeActivity extends BridgeActivity {
|
||||
if (value >= AXIS_DEAD_ZONE) tokens.add(positive);
|
||||
}
|
||||
|
||||
private float controllerAxisValue(MotionEvent event, int primaryAxis, int fallbackAxis) {
|
||||
InputDevice device = event.getDevice();
|
||||
if (device != null && device.getMotionRange(primaryAxis) != null) {
|
||||
return event.getAxisValue(primaryAxis);
|
||||
}
|
||||
return event.getAxisValue(fallbackAxis);
|
||||
}
|
||||
|
||||
private void dispatchNativeControllerToken(String token, boolean repeat) {
|
||||
if (bridge == null || bridge.getWebView() == null) return;
|
||||
String script =
|
||||
@@ -161,18 +171,19 @@ public abstract class ControllerBridgeActivity extends BridgeActivity {
|
||||
});
|
||||
}
|
||||
|
||||
private void dispatchNativeControllerMotion(float x, float y) {
|
||||
private void dispatchNativeControllerMotion(float moveX, float moveY, float lookX, float lookY) {
|
||||
if (bridge == null || bridge.getWebView() == null) return;
|
||||
String script =
|
||||
"window.dispatchEvent(new CustomEvent('iwt-native-controller-motion',"
|
||||
+ "{detail:{x:" + x + ",y:" + y + "}}));";
|
||||
+ "{detail:{moveX:" + moveX + ",moveY:" + moveY
|
||||
+ ",lookX:" + lookX + ",lookY:" + lookY + "}}));";
|
||||
bridge.getWebView().post(() -> bridge.getWebView().evaluateJavascript(script, null));
|
||||
}
|
||||
|
||||
private void clearHeldControllerState() {
|
||||
activeMotionTokens.clear();
|
||||
lastMotionDispatchAt.clear();
|
||||
dispatchNativeControllerMotion(0.0f, 0.0f);
|
||||
dispatchNativeControllerMotion(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
if (bridge == null || bridge.getWebView() == null) return;
|
||||
bridge.getWebView().post(() -> bridge.getWebView().evaluateJavascript(
|
||||
"window.dispatchEvent(new Event('iwt-native-controller-reset'));",
|
||||
|
||||
@@ -2,8 +2,10 @@ services:
|
||||
iwanttoheal:
|
||||
image: node:24-bookworm-slim
|
||||
command: >-
|
||||
sh -lc "corepack pnpm install --frozen-lockfile && corepack pnpm run build && corepack pnpm start"
|
||||
sh -lc "corepack pnpm install --frozen-lockfile && corepack pnpm run db:init && corepack pnpm run build && corepack pnpm start"
|
||||
environment:
|
||||
CORS_ORIGINS: "https://iwanttoheal.phenomrom.com,capacitor://localhost,http://localhost"
|
||||
DATA_DIR: /app/data
|
||||
HOST: 0.0.0.0
|
||||
PORT: "4173"
|
||||
init: true
|
||||
@@ -12,4 +14,5 @@ services:
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- /mnt/usbssds/apps/iwanttoheal-mmo/app:/app
|
||||
- /mnt/usbssds/apps/iwanttoheal-mmo/data:/app/data
|
||||
working_dir: /app
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
PRAGMA foreign_keys = ON;
|
||||
PRAGMA journal_mode = WAL;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS accounts (
|
||||
id INTEGER PRIMARY KEY,
|
||||
username TEXT NOT NULL,
|
||||
canonical_username TEXT NOT NULL UNIQUE,
|
||||
password_hash TEXT NOT NULL,
|
||||
password_salt TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS sessions (
|
||||
id INTEGER PRIMARY KEY,
|
||||
account_id INTEGER NOT NULL REFERENCES accounts(id) ON DELETE CASCADE,
|
||||
token_hash TEXT NOT NULL UNIQUE,
|
||||
expires_at TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS sessions_token_hash_idx ON sessions(token_hash);
|
||||
CREATE INDEX IF NOT EXISTS sessions_expires_at_idx ON sessions(expires_at);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS hunter_saves (
|
||||
account_id INTEGER NOT NULL REFERENCES accounts(id) ON DELETE CASCADE,
|
||||
slot_id INTEGER NOT NULL CHECK (slot_id BETWEEN 1 AND 3),
|
||||
hunter_name TEXT NOT NULL,
|
||||
save_json TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (account_id, slot_id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS boss_kill_records (
|
||||
account_id INTEGER NOT NULL REFERENCES accounts(id) ON DELETE CASCADE,
|
||||
slot_id INTEGER NOT NULL CHECK (slot_id BETWEEN 1 AND 3),
|
||||
boss_id TEXT NOT NULL,
|
||||
kills INTEGER NOT NULL DEFAULT 0 CHECK (kills >= 0),
|
||||
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (account_id, slot_id, boss_id),
|
||||
FOREIGN KEY (account_id, slot_id) REFERENCES hunter_saves(account_id, slot_id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS boss_kill_rank_idx
|
||||
ON boss_kill_records (boss_id, kills DESC, updated_at ASC);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS roguelike_records (
|
||||
account_id INTEGER NOT NULL REFERENCES accounts(id) ON DELETE CASCADE,
|
||||
slot_id INTEGER NOT NULL CHECK (slot_id BETWEEN 1 AND 3),
|
||||
highest_round INTEGER NOT NULL DEFAULT 0 CHECK (highest_round >= 0),
|
||||
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (account_id, slot_id),
|
||||
FOREIGN KEY (account_id, slot_id) REFERENCES hunter_saves(account_id, slot_id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS roguelike_rank_idx
|
||||
ON roguelike_records (highest_round DESC, updated_at ASC);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS rogue_trials_endless_records (
|
||||
account_id INTEGER NOT NULL REFERENCES accounts(id) ON DELETE CASCADE,
|
||||
slot_id INTEGER NOT NULL CHECK (slot_id BETWEEN 1 AND 3),
|
||||
highest_boss_kills INTEGER NOT NULL DEFAULT 0 CHECK (highest_boss_kills >= 0),
|
||||
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (account_id, slot_id),
|
||||
FOREIGN KEY (account_id, slot_id) REFERENCES hunter_saves(account_id, slot_id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS rogue_trials_endless_rank_idx
|
||||
ON rogue_trials_endless_records (highest_boss_kills DESC, updated_at ASC);
|
||||
|
Before Width: | Height: | Size: 113 KiB |
|
Before Width: | Height: | Size: 436 KiB |
|
Before Width: | Height: | Size: 67 KiB |
|
Before Width: | Height: | Size: 98 KiB |
|
Before Width: | Height: | Size: 422 KiB |
|
Before Width: | Height: | Size: 3.9 KiB |
@@ -1,35 +0,0 @@
|
||||
# Blender3D MTL File: Rathalos.blend
|
||||
# Material Count: 3
|
||||
newmtl Rathalos2_em002_BM_02.png
|
||||
Ns 96.078431
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.640000 0.640000 0.640000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 0
|
||||
map_Kd em002_BM_02.png
|
||||
|
||||
|
||||
newmtl Rathalos1_em002_BM_01.png
|
||||
Ns 96.078431
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.640000 0.640000 0.640000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ni 1.000000
|
||||
d 1.000000
|
||||
illum 0
|
||||
map_Kd em002_BM_01.png
|
||||
|
||||
|
||||
newmtl Rathalos3_em002_BM_03.png
|
||||
Ns 96.078431
|
||||
Ka 0.000000 0.000000 0.000000
|
||||
Kd 0.640000 0.640000 0.640000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ni 1.000000
|
||||
d 0.000000
|
||||
illum 0
|
||||
map_Kd em002_BM_03.png
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 4.8 MiB |
|
Before Width: | Height: | Size: 769 KiB |
|
Before Width: | Height: | Size: 415 KiB |
@@ -1,64 +0,0 @@
|
||||
|
||||
|
||||
KayKit : Character Animations (1.1)
|
||||
|
||||
Created/distributed by Kay Lousberg (www.kaylousberg.com)
|
||||
Creation date: 10/12/2025 09:00
|
||||
|
||||
------------------------------
|
||||
|
||||
License: (Creative Commons Zero, CC0)
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
|
||||
This content is free to use in personal, educational and commercial projects.
|
||||
You can support me by purchasing an asset pack at the EXTRA or SOURCE tiers, leaving a donation/tip on itch.io, or joining my Patreon.
|
||||
If you wish to credit me you can do so by crediting "Kay Lousberg, www.kaylousberg.com" (this is not mandatory)
|
||||
|
||||
------------------------------
|
||||
|
||||
This asset pack is here thanks to all the wonderful people who support KayKit on Patreon and those who buy EXTRA or SOURCE packs on itch.io.
|
||||
|
||||
And a big, special thank you to my Super Supporters on Patreon:
|
||||
|
||||
- Brian McBarron
|
||||
- Silva
|
||||
- Joseph Preston
|
||||
- Leon Burkhardt
|
||||
- NickyBHobbying
|
||||
- Emilia
|
||||
- Darren Laing
|
||||
- Magic Pig Games
|
||||
- Lennart Steinke
|
||||
- OWSC
|
||||
- tenthtonman
|
||||
- Ducain23
|
||||
- flo-bit
|
||||
- Jezzy Sukaemi
|
||||
- Martyn Rushton
|
||||
- Joe
|
||||
- estee
|
||||
- Gustavo
|
||||
- AngyZinn
|
||||
- Alex Cea
|
||||
- Matthew A
|
||||
- Stephen Williams
|
||||
- Wenpu Ng
|
||||
- Kevin
|
||||
- Legionof Light
|
||||
- Dazusu
|
||||
- Arnaud Schlupp
|
||||
- JB
|
||||
- Neil Castle
|
||||
- Cedrico Serbruyns
|
||||
- ovenaut
|
||||
- Pawel Jarosz
|
||||
|
||||
------------------------------
|
||||
|
||||
|
||||
Patreon: http://patreon.com/kaylousberg
|
||||
|
||||
Follow here for updates:
|
||||
http://twitter.com/KayLousberg
|
||||
https://mastodon.gamedev.place/@Kay
|
||||
http://twitter.com/KayLousberg
|
||||
@@ -1,136 +0,0 @@
|
||||
{
|
||||
"asset" : {
|
||||
"generator" : "Khronos glTF Blender I/O v3.4.50",
|
||||
"version" : "2.0"
|
||||
},
|
||||
"scene" : 0,
|
||||
"scenes" : [
|
||||
{
|
||||
"name" : "Scene",
|
||||
"nodes" : [
|
||||
0
|
||||
]
|
||||
}
|
||||
],
|
||||
"nodes" : [
|
||||
{
|
||||
"mesh" : 0,
|
||||
"name" : "banner_blue"
|
||||
}
|
||||
],
|
||||
"materials" : [
|
||||
{
|
||||
"name" : "texture",
|
||||
"pbrMetallicRoughness" : {
|
||||
"baseColorTexture" : {
|
||||
"index" : 0
|
||||
},
|
||||
"metallicFactor" : 0,
|
||||
"roughnessFactor" : 0.44999998807907104
|
||||
}
|
||||
}
|
||||
],
|
||||
"meshes" : [
|
||||
{
|
||||
"name" : "banner_blue",
|
||||
"primitives" : [
|
||||
{
|
||||
"attributes" : {
|
||||
"POSITION" : 0,
|
||||
"TEXCOORD_0" : 1,
|
||||
"NORMAL" : 2
|
||||
},
|
||||
"indices" : 3,
|
||||
"material" : 0
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"textures" : [
|
||||
{
|
||||
"sampler" : 0,
|
||||
"source" : 0
|
||||
}
|
||||
],
|
||||
"images" : [
|
||||
{
|
||||
"mimeType" : "image/png",
|
||||
"name" : "dungeon_texture",
|
||||
"uri" : "dungeon_texture.png"
|
||||
}
|
||||
],
|
||||
"accessors" : [
|
||||
{
|
||||
"bufferView" : 0,
|
||||
"componentType" : 5126,
|
||||
"count" : 187,
|
||||
"max" : [
|
||||
0.7500076293945312,
|
||||
3.7274699211120605,
|
||||
0.6895275712013245
|
||||
],
|
||||
"min" : [
|
||||
-0.749992847442627,
|
||||
0.5313930511474609,
|
||||
0.3776381015777588
|
||||
],
|
||||
"type" : "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView" : 1,
|
||||
"componentType" : 5126,
|
||||
"count" : 187,
|
||||
"type" : "VEC2"
|
||||
},
|
||||
{
|
||||
"bufferView" : 2,
|
||||
"componentType" : 5126,
|
||||
"count" : 187,
|
||||
"type" : "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView" : 3,
|
||||
"componentType" : 5123,
|
||||
"count" : 291,
|
||||
"type" : "SCALAR"
|
||||
}
|
||||
],
|
||||
"bufferViews" : [
|
||||
{
|
||||
"buffer" : 0,
|
||||
"byteLength" : 2244,
|
||||
"byteOffset" : 0,
|
||||
"target" : 34962
|
||||
},
|
||||
{
|
||||
"buffer" : 0,
|
||||
"byteLength" : 1496,
|
||||
"byteOffset" : 2244,
|
||||
"target" : 34962
|
||||
},
|
||||
{
|
||||
"buffer" : 0,
|
||||
"byteLength" : 2244,
|
||||
"byteOffset" : 3740,
|
||||
"target" : 34962
|
||||
},
|
||||
{
|
||||
"buffer" : 0,
|
||||
"byteLength" : 582,
|
||||
"byteOffset" : 5984,
|
||||
"target" : 34963
|
||||
}
|
||||
],
|
||||
"samplers" : [
|
||||
{
|
||||
"magFilter" : 9729,
|
||||
"minFilter" : 9987
|
||||
}
|
||||
],
|
||||
"buffers" : [
|
||||
{
|
||||
"byteLength" : 6568,
|
||||
"uri" : "banner_blue.bin"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
{
|
||||
"asset" : {
|
||||
"generator" : "Khronos glTF Blender I/O v3.4.50",
|
||||
"version" : "2.0"
|
||||
},
|
||||
"scene" : 0,
|
||||
"scenes" : [
|
||||
{
|
||||
"name" : "Scene",
|
||||
"nodes" : [
|
||||
0
|
||||
]
|
||||
}
|
||||
],
|
||||
"nodes" : [
|
||||
{
|
||||
"mesh" : 0,
|
||||
"name" : "banner_brown"
|
||||
}
|
||||
],
|
||||
"materials" : [
|
||||
{
|
||||
"name" : "texture",
|
||||
"pbrMetallicRoughness" : {
|
||||
"baseColorTexture" : {
|
||||
"index" : 0
|
||||
},
|
||||
"metallicFactor" : 0,
|
||||
"roughnessFactor" : 0.44999998807907104
|
||||
}
|
||||
}
|
||||
],
|
||||
"meshes" : [
|
||||
{
|
||||
"name" : "banner_brown",
|
||||
"primitives" : [
|
||||
{
|
||||
"attributes" : {
|
||||
"POSITION" : 0,
|
||||
"TEXCOORD_0" : 1,
|
||||
"NORMAL" : 2
|
||||
},
|
||||
"indices" : 3,
|
||||
"material" : 0
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"textures" : [
|
||||
{
|
||||
"sampler" : 0,
|
||||
"source" : 0
|
||||
}
|
||||
],
|
||||
"images" : [
|
||||
{
|
||||
"mimeType" : "image/png",
|
||||
"name" : "dungeon_texture",
|
||||
"uri" : "dungeon_texture.png"
|
||||
}
|
||||
],
|
||||
"accessors" : [
|
||||
{
|
||||
"bufferView" : 0,
|
||||
"componentType" : 5126,
|
||||
"count" : 187,
|
||||
"max" : [
|
||||
0.7500076293945312,
|
||||
3.7274699211120605,
|
||||
0.6895275712013245
|
||||
],
|
||||
"min" : [
|
||||
-0.749992847442627,
|
||||
0.5313930511474609,
|
||||
0.3776381015777588
|
||||
],
|
||||
"type" : "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView" : 1,
|
||||
"componentType" : 5126,
|
||||
"count" : 187,
|
||||
"type" : "VEC2"
|
||||
},
|
||||
{
|
||||
"bufferView" : 2,
|
||||
"componentType" : 5126,
|
||||
"count" : 187,
|
||||
"type" : "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView" : 3,
|
||||
"componentType" : 5123,
|
||||
"count" : 291,
|
||||
"type" : "SCALAR"
|
||||
}
|
||||
],
|
||||
"bufferViews" : [
|
||||
{
|
||||
"buffer" : 0,
|
||||
"byteLength" : 2244,
|
||||
"byteOffset" : 0,
|
||||
"target" : 34962
|
||||
},
|
||||
{
|
||||
"buffer" : 0,
|
||||
"byteLength" : 1496,
|
||||
"byteOffset" : 2244,
|
||||
"target" : 34962
|
||||
},
|
||||
{
|
||||
"buffer" : 0,
|
||||
"byteLength" : 2244,
|
||||
"byteOffset" : 3740,
|
||||
"target" : 34962
|
||||
},
|
||||
{
|
||||
"buffer" : 0,
|
||||
"byteLength" : 582,
|
||||
"byteOffset" : 5984,
|
||||
"target" : 34963
|
||||
}
|
||||
],
|
||||
"samplers" : [
|
||||
{
|
||||
"magFilter" : 9729,
|
||||
"minFilter" : 9987
|
||||
}
|
||||
],
|
||||
"buffers" : [
|
||||
{
|
||||
"byteLength" : 6568,
|
||||
"uri" : "banner_brown.bin"
|
||||
}
|
||||
]
|
||||
}
|
||||