Initial commit
**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
This commit is contained in:
98
web/js/visitor-attraction.js
Normal file
98
web/js/visitor-attraction.js
Normal file
@@ -0,0 +1,98 @@
|
||||
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)),
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user