Files
repterra-web/js/entities.js
T
deepseek 8fbe70d0b0 Repterra Web — full game: base building, power grid, taming & breeding, aquatic raiders, day/night, save/load
- Isometric canvas RTS vs dinosaur waves (fan demake of Repterra)
- Economy: houses/taxes, farms, foresters, quarries; colonist staffing
- Power grid: generators extend build range; brownout + recovery
- Defense: walls/gates, watchtowers (AA), cannon towers (ground-only)
- Taming: Primal Pen + Tamers collar weakened dinos; pets obey commands
- Breeding: tamed pairs incubate eggs at the pen; hatchlings grow up
- 7 dino species incl. flying Pteranodons and lake-raiding Suchomimus
- Telegraphed waves with direction arrows; day-15 final horde; 3 difficulties
- Day/night cycle, fog of war, minimap, synth audio, 1x-3x speeds
- Save/Load/Continue + dawn autosave (full JSON state snapshots)
- Tests: 80-assertion headless suite, browser boot + E2E, balance harness
2026-08-23 07:00:23 +00:00

143 lines
4.0 KiB
JavaScript

/* =========================================================
* REPRTERRA WEB — entities.js
* Factories & shared helpers for buildings/units/dinos.
* ========================================================= */
'use strict';
window.RTS = window.RTS || {};
RTS.entities = (function () {
const U = RTS.util;
let nextId = 1;
const E = {};
E.resetIds = () => { nextId = 1; };
// after loading a save, keep new ids above everything deserialized
E.setIdFloor = (n) => { if (n >= nextId) nextId = n + 1; };
// ---------- footprint ----------
// returns [x0,y0] of top-left tile for a building centered at cx,cy
E.footOrigin = function (cx, cy, size) {
return [Math.round(cx - size / 2), Math.round(cy - size / 2)];
};
// ---------- buildings ----------
E.makeBuilding = function (defId, cx, cy, opts) {
const C = RTS.CONFIG;
const def = C.BUILDINGS[defId];
const instant = opts && opts.instant;
const b = {
kind: 'building',
id: nextId++,
defId,
name: def.name,
size: def.size,
x: Math.round(cx), y: Math.round(cy), // center tile coords
hp: instant ? def.hp : def.hp * 0.15,
maxHp: def.hp,
done: !!instant,
progress: instant ? 1 : 0,
buildTime: instant ? 0 : 2.5 + def.size * 2.5,
workersNeed: def.workers || 0,
workers: 0,
powered: true,
active: true, // production on/off
cool: 0, // tower fire cooldown
trainQ: [], // barracks queue [{t, unit}]
trainT: 0,
rallyX: null, rallyY: null,
burnT: 0,
};
return b;
};
// ---------- human units ----------
E.makeUnit = function (unitId, x, y) {
const C = RTS.CONFIG;
const def = C.UNITS[unitId];
const simSt = (RTS.sim && RTS.sim.state) ? RTS.sim.state() : null;
const armorLvl = (simSt && simSt.upgrades) ? simSt.upgrades.armor || 0 : 0;
const hp = def.hp * (1 + 0.25 * armorLvl);
return {
kind: 'unit',
id: nextId++,
unitId,
name: def.name,
x, y,
hp, maxHp: hp,
speed: def.speed,
cool: 0,
path: null, pathI: 0,
tx: null, ty: null, // move goal
attackMove: false,
targetId: 0, // focused enemy
selected: false,
facing: U.rng() * Math.PI * 2,
animT: U.rng() * 10,
dead: false,
};
};
// ---------- dinosaurs ----------
E.makeDino = function (dinoId, x, y, mode, home) {
const C = RTS.CONFIG;
const def = C.DINOS[dinoId];
const rng = U.rng;
return {
kind: 'dino',
id: nextId++,
dinoId,
name: def.name,
x, y,
hp: def.hp, maxHp: def.hp,
dmg: def.dmg,
rof: def.rof,
speed: def.speed * rng.range(0.9, 1.12),
r: def.r,
flying: !!def.flying,
amphibious: !!def.amphibious,
ranged: def.ranged || 0,
bldMult: def.bldMult || 1,
bounty: def.bounty,
scale: def.scale,
color: def.color,
mode: mode || 'wave', // 'roam' | 'wave' | 'final'
homeX: home ? home.x : x, homeY: home ? home.y : y,
targetId: 0, targetType: '',
path: null, pathI: 0,
repathT: 0,
stuckT: 0,
lastDist: Infinity,
cool: 0,
facing: rng() * Math.PI * 2,
animT: rng() * 10,
wanderT: 0,
wx: x, wy: y,
aggro: !!(mode === 'wave'),
dead: false,
hitFlash: 0,
};
};
// ---------- projectiles ----------
E.makeProj = function (type, x, y, tx, ty, opts) {
return {
kind: 'proj',
id: nextId++,
type, // 'bullet' | 'shell' | 'spit'
x, y, z: opts && opts.z != null ? opts.z : 8,
tx, ty,
speed: opts && opts.speed || 14,
dmg: opts && opts.dmg || 5,
aoe: opts && opts.aoe || 0,
air: !!(opts && opts.air),
fromPlayer: !(opts && opts.enemy),
color: opts && opts.color || '#ffd76a',
trail: [],
arc: opts && opts.arc || 0, // lob height for shells/spits
t: 0, total: 1,
};
};
return E;
})();