**Motivations:** - Initialisation du versionning git pour le projet **Root causes:** - N/A (Nouveau projet) **Correctifs:** - N/A **Evolutions:** - Structure initiale du projet - Ajout du .gitignore **Pages affectées:** - Tous les fichiers
99 lines
3.6 KiB
JavaScript
99 lines
3.6 KiB
JavaScript
import { LootTables } from "./loot-tables.js";
|
|
import { getSellValue } from "./economy.js";
|
|
import { getIncomeMultiplier } from "./mutation-rules.js";
|
|
|
|
const CELL_PITCH = 52;
|
|
const GRID_PADDING = 6;
|
|
const CELL_CENTER_OFFSET = 24;
|
|
|
|
/**
|
|
* Center of a cell in layer pixel coordinates (1-based x, y).
|
|
* @param {number} x 1-based column
|
|
* @param {number} y 1-based row
|
|
* @returns {{ cx: number, cy: number }}
|
|
*/
|
|
function cellCenterPx(x, y) {
|
|
const cx = GRID_PADDING + (x - 1) * CELL_PITCH + CELL_CENTER_OFFSET;
|
|
const cy = GRID_PADDING + (y - 1) * CELL_PITCH + CELL_CENTER_OFFSET;
|
|
return { cx, cy };
|
|
}
|
|
|
|
/**
|
|
* Weighted center of the zoo by animal value (sell value). Visitors are attracted to expensive animals.
|
|
* @param {import("./types.js").GameState} state
|
|
* @param {number} gridWidth
|
|
* @param {number} gridHeight
|
|
* @returns {{ centerX: number, centerY: number }}
|
|
*/
|
|
export function getAttractionCenter(state, gridWidth, gridHeight) {
|
|
let sumW = 0;
|
|
let sumWx = 0;
|
|
let sumWy = 0;
|
|
for (const [key, cell] of Object.entries(state.grid.cells)) {
|
|
if (cell.kind === "animal") {
|
|
const def = LootTables.Animals[cell.id];
|
|
if (def !== null && def !== undefined) {
|
|
const mut = getIncomeMultiplier(cell.mutation ?? "none");
|
|
const w = getSellValue(def.baseIncomePerSecond, cell.level ?? 1, mut, def.sellFactor);
|
|
const [x, y] = key.split("_").map(Number);
|
|
const { cx, cy } = cellCenterPx(x, y);
|
|
sumW += w;
|
|
sumWx += w * cx;
|
|
sumWy += w * cy;
|
|
}
|
|
}
|
|
}
|
|
if (sumW <= 0) {
|
|
const cx = GRID_PADDING + (gridWidth * CELL_PITCH - 4) / 2;
|
|
const cy = GRID_PADDING + (gridHeight * CELL_PITCH - 4) / 2;
|
|
return { centerX: cx, centerY: cy };
|
|
}
|
|
return {
|
|
centerX: sumWx / sumW,
|
|
centerY: sumWy / sumW,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Cell key (1-based x, y) at the given pixel position in the grid layer.
|
|
* @param {number} px
|
|
* @param {number} py
|
|
* @param {number} gridWidth
|
|
* @param {number} gridHeight
|
|
* @returns {string} key "x_y" or empty if out of bounds
|
|
*/
|
|
export function getCellKeyFromPixelPosition(px, py, gridWidth, gridHeight) {
|
|
const x = 1 + Math.round((px - GRID_PADDING - CELL_CENTER_OFFSET) / CELL_PITCH);
|
|
const y = 1 + Math.round((py - GRID_PADDING - CELL_CENTER_OFFSET) / CELL_PITCH);
|
|
if (x < 1 || x > gridWidth || y < 1 || y > gridHeight) return "";
|
|
return `${x}_${y}`;
|
|
}
|
|
|
|
/**
|
|
* Unique position for visitor i: orbits around the attraction center with per-visitor phase, radius and speed.
|
|
* A second harmonic gives figure-8 / Lissajous-style paths so each visitor has a distinct walk.
|
|
* @param {{ i: number, n: number, t: number, centerX: number, centerY: number, gridWidth: number, gridHeight: number }} opts
|
|
* @returns {{ px: number, py: number }}
|
|
*/
|
|
export function getVisitorPosition(opts) {
|
|
const { i, n, t, centerX, centerY, gridWidth, gridHeight } = opts;
|
|
const phase1 = (i / Math.max(1, n)) * Math.PI * 2 + ((i * 17) % 100) * 0.01;
|
|
const phase2 = ((i * 13) % 100) * 0.063;
|
|
const radius1 = 28 + (i * 31) % 55;
|
|
const radius2 = 12 + (i * 11) % 18;
|
|
const speed1 = 0.15 + ((i * 7) % 50) * 0.008;
|
|
const speed2 = 0.08 + ((i * 19) % 40) * 0.006;
|
|
const angle1 = phase1 + t * speed1;
|
|
const angle2 = phase2 + t * speed2;
|
|
const px = centerX + radius1 * Math.cos(angle1) + radius2 * Math.cos(angle2 * 1.3);
|
|
const py = centerY + radius1 * Math.sin(angle1) + radius2 * Math.sin(angle2 * 0.9);
|
|
const minX = GRID_PADDING;
|
|
const minY = GRID_PADDING;
|
|
const maxX = GRID_PADDING + gridWidth * CELL_PITCH - 4 - 20;
|
|
const maxY = GRID_PADDING + gridHeight * CELL_PITCH - 4 - 20;
|
|
return {
|
|
px: Math.max(minX, Math.min(maxX, px)),
|
|
py: Math.max(minY, Math.min(maxY, py)),
|
|
};
|
|
}
|