Add choice-based events with a resolution modal
Standard/kaos events can now present 2 options instead of a fixed effect, pausing new event triggers until the player answers. Adds 4 choice events as a first batch; resources keep accruing while a choice is pending. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,9 @@
|
||||
import { STANDARD_EVENTS, KAOS_EVENTS, type GameEvent } from './events.ts';
|
||||
|
||||
export interface PendingEvent {
|
||||
event: GameEvent;
|
||||
kind: 'event' | 'kaos';
|
||||
}
|
||||
import { UPGRADES } from './upgrades.ts';
|
||||
|
||||
export interface State {
|
||||
@@ -24,6 +29,7 @@ export interface State {
|
||||
usedEventIds: Set<string>;
|
||||
nextEventTick: number;
|
||||
nextKaosTick: number;
|
||||
pendingEvent: PendingEvent | null;
|
||||
}
|
||||
|
||||
export interface LogEntry {
|
||||
@@ -66,6 +72,7 @@ export function createState(): State {
|
||||
usedEventIds: new Set(),
|
||||
nextEventTick: EVENT_INTERVAL_TICKS,
|
||||
nextKaosTick: EVENT_INTERVAL_TICKS * 3,
|
||||
pendingEvent: null,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -107,6 +114,12 @@ export function tick(state: State): State {
|
||||
const s = { ...state, areas: { ...state.areas }, upgrades: new Set(state.upgrades), log: [...state.log], usedEventIds: new Set(state.usedEventIds) };
|
||||
s.tick += 1;
|
||||
|
||||
// A choice event is awaiting the player's answer — freeze new events, keep resources flowing.
|
||||
if (s.pendingEvent) {
|
||||
s.nextEventTick += 1;
|
||||
s.nextKaosTick += 1;
|
||||
}
|
||||
|
||||
// Resource accumulation
|
||||
s.andakt += s.andaktRate * s.troende * DT;
|
||||
s.inflytande += INFLYTANDE_BASE_RATE * s.inflytandeRate * Math.pow(s.troende, 1.2) * DT;
|
||||
@@ -124,24 +137,34 @@ export function tick(state: State): State {
|
||||
s.hemlighet = Math.max(0, s.hemlighet - (troendeDrain + dreadDrain + ritualDrain) * DT);
|
||||
|
||||
// Standard event
|
||||
if (s.tick >= s.nextEventTick) {
|
||||
if (!s.pendingEvent && s.tick >= s.nextEventTick) {
|
||||
const ev = pickEvent(STANDARD_EVENTS, s);
|
||||
if (ev) {
|
||||
s.log.unshift({ tick: s.tick, text: ev.text, kind: 'event' });
|
||||
applyEffect(s, ev.effect);
|
||||
s.usedEventIds.add(ev.id);
|
||||
if (ev.choices?.length) {
|
||||
s.log.unshift({ tick: s.tick, text: ev.text, kind: 'event' });
|
||||
s.pendingEvent = { event: ev, kind: 'event' };
|
||||
} else {
|
||||
s.log.unshift({ tick: s.tick, text: ev.text, kind: 'event' });
|
||||
applyEffect(s, ev.effect);
|
||||
}
|
||||
}
|
||||
s.nextEventTick = s.tick + EVENT_INTERVAL_TICKS + Math.floor(Math.random() * 60);
|
||||
if (s.log.length > 40) s.log = s.log.slice(0, 40);
|
||||
}
|
||||
|
||||
// Kaos event
|
||||
if (s.kaos >= KAOS_THRESHOLD || s.tick >= s.nextKaosTick) {
|
||||
if (!s.pendingEvent && (s.kaos >= KAOS_THRESHOLD || s.tick >= s.nextKaosTick)) {
|
||||
const ev = pickEvent(KAOS_EVENTS, s);
|
||||
if (ev) {
|
||||
s.log.unshift({ tick: s.tick, text: `⚡ ${ev.text}`, kind: 'kaos' });
|
||||
applyEffect(s, ev.effect);
|
||||
s.usedEventIds.add(ev.id);
|
||||
if (ev.choices?.length) {
|
||||
s.log.unshift({ tick: s.tick, text: `⚡ ${ev.text}`, kind: 'kaos' });
|
||||
s.pendingEvent = { event: ev, kind: 'kaos' };
|
||||
} else {
|
||||
s.log.unshift({ tick: s.tick, text: `⚡ ${ev.text}`, kind: 'kaos' });
|
||||
applyEffect(s, ev.effect);
|
||||
}
|
||||
}
|
||||
s.kaos = Math.max(0, s.kaos - 80);
|
||||
s.nextKaosTick = s.tick + EVENT_INTERVAL_TICKS * 5 + Math.floor(Math.random() * 120);
|
||||
@@ -161,6 +184,29 @@ export function tick(state: State): State {
|
||||
return s;
|
||||
}
|
||||
|
||||
export function resolveEventChoice(state: State, choiceIndex: number): State {
|
||||
if (!state.pendingEvent) return state;
|
||||
const choice = state.pendingEvent.event.choices?.[choiceIndex];
|
||||
if (!choice) return state;
|
||||
|
||||
const s = { ...state, areas: { ...state.areas }, upgrades: new Set(state.upgrades), log: [...state.log], usedEventIds: new Set(state.usedEventIds) };
|
||||
applyEffect(s, choice.effect);
|
||||
s.log.unshift({ tick: s.tick, text: `↳ ${choice.resultText}`, kind: s.pendingEvent!.kind });
|
||||
if (s.log.length > 40) s.log = s.log.slice(0, 40);
|
||||
s.pendingEvent = null;
|
||||
|
||||
if (s.hemlighet <= 0) {
|
||||
s.phase = 'lost';
|
||||
s.lostReason = 'Utredarna slog till vid gryningen. Källaren är tom. Mattan ligger kvar. Under mattan finns fortfarande bordet.';
|
||||
s.log.unshift({ tick: s.tick, text: s.lostReason, kind: 'system' });
|
||||
} else if (s.ritual >= 100) {
|
||||
s.phase = 'won';
|
||||
s.log.unshift({ tick: s.tick, text: 'Det är gjort. Turning Torso pekar rakt ned nu. Havet är stilla. Alltför stilla.', kind: 'system' });
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
interface LocationVisit {
|
||||
text: string;
|
||||
effect: Partial<{ andakt: number; dread: number; kaos: number; ritual: number; hemlighet: number }>;
|
||||
|
||||
Reference in New Issue
Block a user