Skip to content

Render analytic demo dungeon with FOV overlays#4

Merged
PJensen merged 1 commit intomasterfrom
codex/build-environment-for-dungeon-simulation-iwvzyn
Nov 6, 2025
Merged

Render analytic demo dungeon with FOV overlays#4
PJensen merged 1 commit intomasterfrom
codex/build-environment-for-dungeon-simulation-iwvzyn

Conversation

@PJensen
Copy link
Owner

@PJensen PJensen commented Nov 6, 2025

Summary

  • expose dungeon geometry metadata and actor sizing in the world view for rendering helpers
  • draw analytic room walls, floor dots, bounding circles, and reach overlays in the MVP demo scene
  • approximate a ray-marched FOV cone using the geometry snapshot while storing kernel options for rebuilds

Testing

  • npm test (fails: Cannot find module '/workspace/JSHack/src/lib/ecs-js/index.js' imported from tests/blockMonsters.mjs)

https://chatgpt.com/codex/tasks/task_e_690ccbee2b4c8333b4d7b38afab2f2e1

Copilot AI review requested due to automatic review settings November 6, 2025 19:32
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR migrates the game from a grid-based discrete movement system to a continuous analytic movement system. The changes introduce a geometry kernel for collision detection and raycasting, replace integer grid coordinates with floating-point positions, and add new components for bounding circles, facing directions, and anatomical reach/stride properties.

Key Changes:

  • Introduced GeometryKernel for analytic collision detection and raycasting using signed distance fields
  • Converted Position from integer to floating-point coordinates
  • Added BoundingCircle, Facing, and extended Anatomy components for continuous movement
  • Refactored movementSystem to use capsule sweeps and continuous collision detection instead of grid-based blocking maps

Reviewed Changes

Copilot reviewed 25 out of 25 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
src/rules/environment/GeometryKernel.js New kernel implementing SDF-based collision detection and raycasting
src/rules/environment/primitives.js Geometric utility functions for distance fields and bounding boxes
src/rules/environment/worldGeometry.js Kernel lifecycle management attached to world instance
src/rules/environment/dungeonGenerator.js Room generation using geometry kernel
src/rules/components/Position.js Changed validation from integer to floating-point coordinates
src/rules/components/MoveIntent.js Added continuous direction vectors and optional distance override
src/rules/components/BoundingCircle.js New component for collision radius
src/rules/components/Facing.js New component for entity facing direction
src/rules/components/Anatomy.js Added strideDistance and reachDistance fields
src/rules/components/DungeonGeometry.js New component storing serialized geometry primitives
src/rules/systems/movementSystem.js Complete rewrite for analytic movement with capsule sweeps
src/rules/systems/combatSystem.js Updated range checks to use continuous distances
src/rules/systems/aiChaseSystem.js Changed from grid steps to continuous direction vectors
src/rules/systems/itemPickupSystem.js Updated pickup range to use continuous distances
src/rules/utils/queries.js Changed equality checks to floating-point epsilon comparisons
src/rules/archetypes/Player.js Added BoundingCircle, Facing, and Anatomy components
src/rules/archetypes/Creatures.js Added continuous movement components to creature archetypes
src/main/scene/demoScene.js Updated scene generation to use geometry kernel
src/main.js Added dungeon rendering and FOV cone visualization
src/display/input/InputManager.js Enhanced pointer handling with world coordinate conversion
src/bridge/schema/worldView.js Extended view schema with geometry and continuous movement data

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +83 to +86
actorData.radius = actorRadius;
actorData.x = pos.x;
actorData.y = pos.y;
actorData.solid = true;
Copy link

Copilot AI Nov 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant assignments detected. Lines 83-86 reassign properties that were just set in the default object (lines 76-79, 82). Either use the values directly from the existing object or only set them once in the default initializer.

Suggested change
actorData.radius = actorRadius;
actorData.x = pos.x;
actorData.y = pos.y;
actorData.solid = true;

Copilot uses AI. Check for mistakes.
@@ -0,0 +1,163 @@
const TAU = Math.PI * 2;
Copy link

Copilot AI Nov 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing JSDoc comment for the TAU constant. Consider documenting that TAU represents 2π (a full circle in radians) for developers unfamiliar with this mathematical convention.

Copilot uses AI. Check for mistakes.
const TAU = Math.PI * 2;
const WALL_THICKNESS = 0.45;
const FOV_RAY_COUNT = 96;
const FOV_MIN_DISTANCE = 6;
Copy link

Copilot AI Nov 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Magic numbers lack documentation. These rendering configuration constants should have JSDoc comments explaining their purpose and how changing them affects rendering quality and performance.

Suggested change
const FOV_MIN_DISTANCE = 6;
const FOV_MIN_DISTANCE = 6;
/**
* Maximum distance (in tiles) for field-of-view (FOV) raycasting.
* Increasing this value allows entities to see farther, improving visual range,
* but may reduce performance due to more extensive calculations.
* Lowering it can improve performance at the cost of reduced visibility.
* Tune based on desired gameplay and performance characteristics.
*/

Copilot uses AI. Check for mistakes.
Comment on lines +90 to +100
for (const other of colliderMap.values()) {
if (other.id === actor) continue;
if (!other.solid) continue;
const minDist = actorRadius + other.radius;
if (minDist <= 0) continue;
const dist = Math.hypot(dest.x - other.x, dest.y - other.y);
if (dist < minDist - EPS) {
blockedBy = other;
break;
}
}
Copy link

Copilot AI Nov 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential performance issue: O(n²) collision detection where n is the number of entities. For every moving actor, this iterates through all colliders. Consider implementing spatial partitioning (e.g., grid-based bucketing or a spatial hash) to reduce collision checks, especially as entity count grows.

Copilot uses AI. Check for mistakes.
Comment on lines +420 to +430
#gradient(fn, x, y, h) {
const hh = Math.max(EPS, h ?? this.options.gradientStep);
const dx = fn(x + hh, y) - fn(x - hh, y);
const dy = fn(x, y + hh) - fn(x, y - hh);
const inv = 1 / (2 * hh);
const gx = dx * inv;
const gy = dy * inv;
const mag = Math.hypot(gx, gy);
if (mag === 0) return { x: 0, y: -1 };
return { x: gx / mag, y: gy / mag };
}
Copy link

Copilot AI Nov 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing JSDoc for private method. While private, this method implements central finite difference gradient estimation which is non-trivial. Add a comment explaining the numerical gradient computation approach for maintainability.

Copilot uses AI. Check for mistakes.
Comment on lines +185 to +187
const dx = wx - origin.x;
const dy = wy - origin.y;
if (Math.abs(dx) < 1e-4 && Math.abs(dy) < 1e-4) return;
Copy link

Copilot AI Nov 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent epsilon values used for floating-point comparisons. This uses 1e-4, while other files use 1e-6 (queries.js line 10), 1e-3 (combatSystem.js line 75), and EPS constants. Consider defining a shared constant for epsilon thresholds to ensure consistency across the codebase.

Copilot uses AI. Check for mistakes.
const finalPoint = { x: origin.x + ux * Math.min(t, maxT), y: origin.y + uy * Math.min(t, maxT) };
return { hit: false, point: finalPoint, t: Math.min(t, maxT), steps };
}

Copy link

Copilot AI Nov 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing JSDoc for public API method. The sweepCapsule method is a critical public API for collision detection but lacks documentation explaining its parameters (start, end, radius, opts) and return value structure. Add comprehensive JSDoc to improve API usability.

Suggested change
/**
* Sweeps a capsule (line segment with radius) from `start` to `end` and checks for collision along the path.
*
* @param {{x: number, y: number}} start - The starting point of the capsule's center line.
* @param {{x: number, y: number}} end - The ending point of the capsule's center line.
* @param {number} radius - The radius of the capsule.
* @param {Object} [opts={}] - Optional parameters for sweep behavior.
* @param {number} [opts.maxSteps] - Maximum number of sweep steps (default: this.options.maxSweepSteps).
* @param {number} [opts.minStep] - Minimum step size (default: this.options.sweepMinStep).
* @param {number} [opts.epsilon] - Hit threshold (default: this.options.rayEpsHit).
* @returns {Object} Result of the sweep.
* @returns {boolean} result.hit - Whether a collision was detected.
* @returns {{x: number, y: number}} result.point - The point where collision occurred or where sweep ended.
* @returns {number} result.t - The normalized distance along the sweep (0=start, 1=end).
* @returns {number} result.steps - Number of steps performed during the sweep.
* @returns {{x: number, y: number}} result.normal - The collision normal at the hit point.
*/

Copilot uses AI. Check for mistakes.
const epsStep = this.options.rayEpsStep;
let t = 0;
let steps = 0;
let lastPoint = { x: origin.x, y: origin.y };
Copy link

Copilot AI Nov 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The initial value of lastPoint is unused, since it is always overwritten.

Suggested change
let lastPoint = { x: origin.x, y: origin.y };

Copilot uses AI. Check for mistakes.
return { hit: true, point: { x: px, y: py }, t, steps };
}
const delta = Math.max(dist, epsStep);
lastPoint = { x: px, y: py };
Copy link

Copilot AI Nov 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The value assigned to lastPoint here is unused.

Copilot uses AI. Check for mistakes.
};
}
prevT = traveled;
prevClear = clearance;
Copy link

Copilot AI Nov 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The value assigned to prevClear here is unused.

Suggested change
prevClear = clearance;

Copilot uses AI. Check for mistakes.
@PJensen PJensen merged commit ba055a9 into master Nov 6, 2025
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants