Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
0304ec6
Refactor main orchestration into modular helpers
PJensen Nov 6, 2025
9b986c3
Merge pull request #2 from PJensen/codex/refactor-main.js-for-maintai…
PJensen Nov 6, 2025
a3f5acb
adding "world-carving" experimental tool, renaming existing "fx-tool"…
PJensen Nov 6, 2025
6b88eb3
adjusting carving tool
PJensen Nov 6, 2025
d228c1a
Render analytic dungeon dungeon and FOV
PJensen Nov 6, 2025
ba055a9
Merge pull request #4 from PJensen/codex/build-environment-for-dungeo…
PJensen Nov 6, 2025
917a436
Show ground pickup tooltip within reach
PJensen Nov 6, 2025
2935a98
Merge pull request #5 from PJensen/codex/add-pickup-tooltip-for-nearb…
PJensen Nov 6, 2025
42e33ca
Improve overlay interactions and movement
PJensen Nov 6, 2025
0248639
Improve wall contact movement sliding
PJensen Nov 6, 2025
267c724
Merge pull request #6 from PJensen/codex/enhance-menu-ux-and-gameplay…
PJensen Nov 6, 2025
b0fa185
Refactor rules scripting to use shared ScriptRef
PJensen Nov 6, 2025
1ba639f
Merge pull request #7 from PJensen/codex/port-scripted-actions-to-scr…
PJensen Nov 6, 2025
10d69c4
feat: add emissive torch lighting
PJensen Nov 6, 2025
4b594b4
Tighten player FOV lighting and layer ordering
PJensen Nov 7, 2025
a61dbf4
Merge pull request #8 from PJensen/codex/implement-smooth-lighting-wi…
PJensen Nov 7, 2025
7d3107a
Add lightning gesture casting and improve movement
PJensen Nov 7, 2025
7dca1ba
Gate lightning gesture behind hold and flatten FOV light
PJensen Nov 7, 2025
52f8107
Merge pull request #9 from PJensen/codex/fix-wall-sticking-issue-and-…
PJensen Nov 7, 2025
6a19d0f
wooden bow + projectile
PJensen Nov 8, 2025
057768f
Merge pull request #10 from PJensen:local-features
PJensen Nov 8, 2025
7df4318
fire arrows improved, ammo, some lighting
PJensen Nov 8, 2025
7b00985
early meteor
PJensen Nov 8, 2025
0060837
early meteor
PJensen Nov 8, 2025
fb1d914
burning 2
PJensen Nov 8, 2025
6902bd8
burning 2
PJensen Nov 8, 2025
71e4450
meteor gesture fixed
PJensen Nov 8, 2025
e7b57f9
blast wave
PJensen Nov 8, 2025
19e002c
blast wave
PJensen Nov 8, 2025
b3a3b8a
quick-use, early
PJensen Nov 8, 2025
e820c6d
quick-use, early 2
PJensen Nov 8, 2025
eb69053
Fix touch targeting and ranged ammo handling
PJensen Nov 8, 2025
471da81
Merge pull request #11 from PJensen/codex/fix-mobile-tap-shooting-mec…
PJensen Nov 8, 2025
d591758
Implement grid-based movement and wall rendering
PJensen Nov 8, 2025
1b1ae6d
Stabilize tap movement and darken floor
PJensen Nov 8, 2025
e93b95b
Bias tap movement toward cardinals
PJensen Nov 9, 2025
40ad099
Improve tap cardinal bias and visuals
PJensen Nov 9, 2025
010e46e
Merge pull request #13 from PJensen/codex/implement-grid-based-8-dire…
PJensen Nov 9, 2025
a5776d0
Add demo door and reduce player collision radius
PJensen Nov 9, 2025
494d603
Merge pull request #14 from PJensen/codex/adjust-player-bounding-circ…
PJensen Nov 9, 2025
3b6c9e9
hall radius -> 1
PJensen Nov 9, 2025
fec3d2f
tweaking analytic floor color
PJensen Nov 9, 2025
6b9e801
expaning demo scene into 3 wings
PJensen Nov 9, 2025
c549fb8
adding a basic spike trap
PJensen Nov 9, 2025
f49d4d9
ammo consumption
PJensen Nov 9, 2025
af37db9
Integrate analytic dungeon systems into rules layer
PJensen Nov 12, 2025
dc3db74
Rewrite demo scene to showcase analytic multi-floor dungeon
PJensen Nov 12, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 32 additions & 3 deletions app/input/rulesDispatch.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
// App-owned translation from display/input Actions → rules intents on the ECS world.
// This file is allowed to import rules and the ECS World (per Separation Manifest).

import { MoveIntent, WaitIntent, DrinkIntent, CastSpellIntent, PickupIntent, EquipIntent, Position, ItemInfo } from "../../src/rules/components/index.js";
import { MoveIntent, WaitIntent, DrinkIntent, CastSpellIntent, PickupIntent, EquipIntent, Position, ItemInfo, FaceIntent } from "../../src/rules/components/index.js";
import { UseIntent } from "../../src/rules/components/Intents/UseIntent.js";
import { RangedAttackIntent } from "../../src/rules/components/Intents/RangedAttackIntent.js";
import { itemsAt } from "../../src/rules/utils/queries.js";

/**
Expand Down Expand Up @@ -36,8 +37,36 @@ export function makeRulesDispatcher(world, getActorId) {
break;
}
case "rules.castActiveSpell": {
const { spellId = 0, targetId = actorId } = action.payload || {};
world?.add?.(actorId, CastSpellIntent, { spellId, targetId });
const { spellId = 0, targetId = actorId, x = null, y = null, vx = null, vy = null } = action.payload || {};
const intent = { spellId, targetId };
if (Number.isFinite(x) && Number.isFinite(y)) { intent.x = x; intent.y = y; }
if (Number.isFinite(vx) && Number.isFinite(vy)) { intent.vx = vx; intent.vy = vy; }
world?.add?.(actorId, CastSpellIntent, intent);
world?.tick?.(1);
break;
}
case "rules.face": {
const { dx = null, dy = null, toX = null, toY = null } = action.payload || {};
const intent = {};
if (Number.isFinite(dx)) intent.dx = dx;
if (Number.isFinite(dy)) intent.dy = dy;
if (Number.isFinite(toX)) intent.toX = toX;
if (Number.isFinite(toY)) intent.toY = toY;
world?.add?.(actorId, FaceIntent, intent);
world?.tick?.(1);
break;
}
case "rules.shootRanged": {
world?.add?.(actorId, RangedAttackIntent, {});
world?.tick?.(1);
break;
}
case "rules.shootRangedAt": {
const { targetId = 0, x = null, y = null } = action.payload || {};
const intent = {};
if (Number.isInteger(targetId) && targetId > 0) intent.targetId = targetId;
if (Number.isFinite(x) && Number.isFinite(y)) { intent.toX = x; intent.toY = y; }
world?.add?.(actorId, RangedAttackIntent, intent);
world?.tick?.(1);
break;
}
Expand Down
90 changes: 87 additions & 3 deletions app/rules/scheduler.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,37 @@ import { useItemSystem } from "../../src/rules/systems/useItemSystem.js";
import { projectileSystem } from "../../src/rules/systems/projectileSystem.js";
import { interactionSystem } from "../../src/rules/systems/interactionSystem.js";
import { effectSystem } from "../../src/rules/systems/effectSystem.js";
import { monsterSpawnerSystem } from "../../src/rules/systems/monsterSpawnerSystem.js";
import { equipmentSystem } from "../../src/rules/systems/equipmentSystem.js";
import { waitSystem } from "../../src/rules/systems/waitSystem.js";
import { castSpellSystem } from "../../src/rules/systems/castSpellSystem.js";
import { rangedAttackSystem } from "../../src/rules/systems/rangedAttackSystem.js";
import { faceSystem } from "../../src/rules/systems/faceSystem.js";
import { aiChaseSystem } from "../../src/rules/systems/aiChaseSystem.js";
import { movementSystem } from "../../src/rules/systems/movementSystem.js";
import { combatSystem } from "../../src/rules/systems/combatSystem.js";
import { installAffixTriggers } from "../../src/rules/systems/affixTriggerSystem.js";
import { cleanupSystem } from "../../src/rules/systems/cleanupSystem.js";
import { trapSystem } from "../../src/rules/systems/trapSystem.js";
// Register trap scripts
import "../../src/rules/scripts/traps.js";
import {
FloorRef,
Position,
Facing,
GeomHandle,
FloorState,
LightingAccelHandle,
DungeonLevel,
PortalTrace,
LightSource
} from "../../src/rules/components/index.js";
import { KernelCache, getPortalsV } from "../../src/rules/analytic/index.js";
import { createGeomKernelSystem } from "../../src/rules/systems/geomKernelSystem.js";
import { createKernelPrewarmSystem } from "../../src/rules/systems/kernelPrewarmSystem.js";
import { createPortalUseSystem } from "../../src/rules/systems/portalUseSystem.js";
import { createLightingBakeSystem } from "../../src/rules/systems/lightingBakeSystem.js";
import { createFloorActivationSystem } from "../../src/rules/systems/floorActivationSystem.js";

/**
* @param {World} world
Expand All @@ -29,6 +52,26 @@ export function configureWorld(world) {
// Install affix event listeners once per world
installAffixTriggers(world);

const kernelCache = new KernelCache(4);

registerSystem(
createGeomKernelSystem({
geomHandleComponent: GeomHandle,
floorStateComponent: FloorState
}),
"intents"
);

registerSystem(
createKernelPrewarmSystem({
floorRefComponent: FloorRef,
positionComponent: Position,
cache: kernelCache,
portalsAccessor: getPortalsV
}),
"intents"
);

// Phase: intents (consume queued intents)
// Producers first (AI), then consumers (movement, interactions, etc.)
registerSystem(aiChaseSystem, 'intents');
Expand All @@ -40,6 +83,8 @@ export function configureWorld(world) {
registerSystem(projectileSystem, 'intents');
registerSystem(interactionSystem, 'intents');
registerSystem(castSpellSystem, 'intents');
registerSystem(faceSystem, 'intents');
registerSystem(rangedAttackSystem, 'intents');
registerSystem(movementSystem, 'intents');
registerSystem(combatSystem, 'intents');
// Run pickup after movement so stepping onto items can pick them up immediately
Expand All @@ -48,9 +93,50 @@ export function configureWorld(world) {
// Phase: effects (derived first, then per-turn effects)
registerSystem(equipmentSystem, 'effects');
registerSystem(effectSystem, 'effects');
registerSystem(monsterSpawnerSystem, 'effects');
// Trigger traps after movement and core effects
registerSystem(trapSystem, 'effects');
// Post-move auto-pickup runs after intents, within the same tick
registerSystem(autoPickupPostMoveSystem, 'effects');

registerSystem(
createPortalUseSystem({
floorRefComponent: FloorRef,
positionComponent: Position,
facingComponent: Facing,
portalTraceComponent: PortalTrace,
portalsAccessor: getPortalsV
}),
'effects'
);

registerSystem(
createLightingBakeSystem({
lightingAccelComponent: LightingAccelHandle,
geomHandleComponent: GeomHandle,
lightProvider: (floorId) => {
const lights = [];
for (const [id, light, pos, ref] of world.query(LightSource, Position, FloorRef)) {
if (!ref || ref.floorId !== floorId) continue;
lights.push({
id,
position: { x: pos.x, y: pos.y },
intensity: light.intensity ?? 1,
radius: light.radius ?? 0,
color: light.color ?? '#ffffff'
});
}
return lights;
}
}),
'effects'
);

registerSystem(
createFloorActivationSystem({ dungeonLevelComponent: DungeonLevel }),
'effects'
);

// Phase: cleanup (end-of-turn removals like killing entities with hp <= 0)
registerSystem(cleanupSystem, 'cleanup');

Expand Down Expand Up @@ -98,9 +184,7 @@ export function configureWorld(world) {
}

function shouldProfileRules() {
const params = new URLSearchParams(window.location.search || '');
const v = (params.get('rulesProfile') || (typeof localStorage !== 'undefined' ? localStorage.getItem('jshack.rulesProfile') : '0') || '0');
return v === '1' || v === 'true' || v === 'on';
return false;
}

function getRulesProfilerState() {
Expand Down
File renamed without changes.
Loading