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:
86
web/js/visitor-incidents.js
Normal file
86
web/js/visitor-incidents.js
Normal file
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
* Visitor incidents (thirst, bin full, bench required, animal too far, want photo).
|
||||
* Appear more often during wait phases; resolve by click for bonus, or timeout applies penalty.
|
||||
*/
|
||||
|
||||
import { GameConfig } from "./config.js";
|
||||
|
||||
/** Incident type keys for i18n and display. */
|
||||
export const INCIDENT_TYPES = ["thirst", "bin", "bench", "animalFar", "photo"];
|
||||
|
||||
/** Emoji per incident type for bubble display. */
|
||||
export const INCIDENT_EMOJI = { thirst: "💧", bin: "🗑️", bench: "🪑", animalFar: "🦌", photo: "📷" };
|
||||
|
||||
/**
|
||||
* True when player is in a wait phase (truck moving, sale pending validation, etc.).
|
||||
* @param {import("./types.js").GameState} state
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isInWaitPhase(state) {
|
||||
if (state.eggPurchaseTruck && state.eggPurchaseTruck.startAt) return true;
|
||||
if (state.truckSale && state.truckSale.startAt) return true;
|
||||
const api = state.salesFromApi;
|
||||
if (api && api.asBuyerUndelivered && api.asBuyerUndelivered.length > 0) {
|
||||
const nowMs = Date.now();
|
||||
for (const s of api.asBuyerUndelivered) {
|
||||
const validatedAtMs = s.validated_at ? new Date(s.validated_at).getTime() : 0;
|
||||
const pending = (s.status === "sold" || s.status === "validated") && validatedAtMs > nowMs;
|
||||
if (pending) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawn and expire incidents. Call after tickVisitorArrivals.
|
||||
* @param {import("./types.js").GameState} state
|
||||
* @param {number} nowUnix
|
||||
*/
|
||||
export function tickVisitorIncidents(state, nowUnix) {
|
||||
const arrivals = state.visitorArrivals ?? [];
|
||||
const cfg = GameConfig.Visitor;
|
||||
const baseChance = cfg?.IncidentChanceBase ?? 0.002;
|
||||
const waitMult = cfg?.IncidentChanceWaitMultiplier ?? 4;
|
||||
const timeoutSec = cfg?.IncidentTimeoutSeconds ?? 45;
|
||||
const penalty = cfg?.IncidentUnresolvedAttractivityPenalty ?? 0.2;
|
||||
const inWait = isInWaitPhase(state);
|
||||
const chance = inWait ? baseChance * waitMult : baseChance;
|
||||
const toRemove = [];
|
||||
|
||||
for (let i = 0; i < arrivals.length; i++) {
|
||||
const v = arrivals[i];
|
||||
if (v.incidentType !== null && v.incidentType !== undefined) {
|
||||
if (nowUnix - (v.incidentSince ?? nowUnix) >= timeoutSec) {
|
||||
state.attractivityBonusFromIncidents = (state.attractivityBonusFromIncidents ?? 0) - penalty;
|
||||
toRemove.push(i);
|
||||
}
|
||||
} else if (Math.random() < chance) {
|
||||
v.incidentType = INCIDENT_TYPES[Math.floor(Math.random() * INCIDENT_TYPES.length)];
|
||||
v.incidentSince = nowUnix;
|
||||
}
|
||||
}
|
||||
for (let r = toRemove.length - 1; r >= 0; r--) {
|
||||
arrivals.splice(toRemove[r], 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve incident for visitor at index: clear incident, add coins and attractivity bonus. Mutates state.
|
||||
* @param {import("./types.js").GameState} state
|
||||
* @param {number} visitorIndex
|
||||
* @returns {boolean} true if an incident was resolved
|
||||
*/
|
||||
export function resolveIncident(state, visitorIndex) {
|
||||
const arrivals = state.visitorArrivals ?? [];
|
||||
const v = arrivals[visitorIndex];
|
||||
if (!v || (v.incidentType === null || v.incidentType === undefined)) return false;
|
||||
const cfg = GameConfig.Visitor;
|
||||
const coinBonus = cfg?.IncidentResolveCoinBonus ?? 8;
|
||||
const attractivityBonus = cfg?.IncidentResolveAttractivityBonus ?? 0.15;
|
||||
state.coins += coinBonus;
|
||||
state.attractivityBonusFromIncidents = (state.attractivityBonusFromIncidents ?? 0) + attractivityBonus;
|
||||
if (state.stats) state.stats.coinsEarned = (state.stats.coinsEarned ?? 0) + coinBonus;
|
||||
delete v.incidentType;
|
||||
delete v.incidentSince;
|
||||
return true;
|
||||
}
|
||||
Reference in New Issue
Block a user