use interaction resolve matching

This commit is contained in:
2026-03-27 20:03:50 +01:00
parent ecc82a9dce
commit 5858e2808d
5 changed files with 110 additions and 62 deletions

View File

@@ -1,7 +1,14 @@
import { Chibi, ChibiId } from "../../types/chibi/chibi"; import { Chibi, ChibiId, ChibiStatBar } from "../../types/chibi/chibi";
import { EChibiName } from "../../types/chibi/chibi-name"; import { EChibiName } from "../../types/chibi/chibi-name";
import { EChibiStateName } from "../../types/chibi/chibi-state-name"; import { EChibiStateName } from "../../types/chibi/chibi-state-name";
export const statBar = (current: number, max: number): ChibiStatBar => {
return {
current,
max
}
}
export const MAX_STAT_VALUE: number = 100000; export const MAX_STAT_VALUE: number = 100000;
export const CHIBIS: Chibi[] = [ export const CHIBIS: Chibi[] = [
@@ -12,11 +19,11 @@ export const CHIBIS: Chibi[] = [
spritePath: '', spritePath: '',
experience: 0, experience: 0,
unlocked: true, unlocked: true,
happyness: MAX_STAT_VALUE, happyness: statBar(MAX_STAT_VALUE, MAX_STAT_VALUE),
health: MAX_STAT_VALUE, health: statBar(MAX_STAT_VALUE, MAX_STAT_VALUE),
hunger: MAX_STAT_VALUE, hunger: statBar(MAX_STAT_VALUE, MAX_STAT_VALUE),
boredom: MAX_STAT_VALUE, boredom: statBar(MAX_STAT_VALUE, MAX_STAT_VALUE),
energy: MAX_STAT_VALUE, energy: statBar(MAX_STAT_VALUE, MAX_STAT_VALUE),
attentionSpan: 1000, attentionSpan: 1000,
recoveryRate: 1000, recoveryRate: 1000,
constitution: 1000, constitution: 1000,
@@ -24,4 +31,23 @@ export const CHIBIS: Chibi[] = [
maintenanceCals: 0, maintenanceCals: 0,
state: EChibiStateName.Sleeping state: EChibiStateName.Sleeping
}, },
{
id: '238df3e7-3003-4471-91f5-a7ecf3151e18' as ChibiId,
name: EChibiName.Caethya,
iconPath: '',
spritePath: '',
experience: 0,
unlocked: true,
happyness: statBar(MAX_STAT_VALUE, MAX_STAT_VALUE),
health: statBar(MAX_STAT_VALUE, MAX_STAT_VALUE),
hunger: statBar(MAX_STAT_VALUE, MAX_STAT_VALUE),
boredom: statBar(MAX_STAT_VALUE, MAX_STAT_VALUE),
energy: statBar(MAX_STAT_VALUE, MAX_STAT_VALUE),
attentionSpan: 1000,
recoveryRate: 1000,
constitution: 1000,
power: 1000,
maintenanceCals: 0,
state: EChibiStateName.Idle
},
]; ];

View File

@@ -6,6 +6,8 @@ import { Food } from "../../../types/food";
import { RandomOutcome, RandomService } from "../../random.service"; import { RandomOutcome, RandomService } from "../../random.service";
import { IBrain } from "../brain"; import { IBrain } from "../brain";
export type InteractionMap = { [key in EChibiStateName]: InteractionRecord | undefined }
export type ChibiInteraction<T = null> = { export type ChibiInteraction<T = null> = {
name: EChibiInteraction; name: EChibiInteraction;
payload: T; payload: T;
@@ -37,7 +39,7 @@ export class Aperio implements IBrain {
private preferredFoods: string[] = []; private preferredFoods: string[] = [];
private state: ChibiState = STATES[this.defaultState]; private state: ChibiState = STATES[this.defaultState];
private interactionMap: { [key in EChibiStateName]: InteractionRecord | undefined } = { private interactionMap: InteractionMap = {
[EChibiStateName.Sleeping]: { [EChibiStateName.Sleeping]: {
[EChibiInteraction.WakeUp]: (interaction: ChibiInteraction): ChibiState => this.wakeUp(interaction), [EChibiInteraction.WakeUp]: (interaction: ChibiInteraction): ChibiState => this.wakeUp(interaction),
}, },
@@ -65,6 +67,9 @@ export class Aperio implements IBrain {
exist(): void { exist(): void {
this.timeInCurrentState++; this.timeInCurrentState++;
//this sucks, resolve should be generic
//stuff into an existMap which contains the resolve functions
//then find correct function to execute the same way we do for interactions
this.resolveAwake(); this.resolveAwake();
this.resolveSleeping(); this.resolveSleeping();
this.resolveGrumbling(); this.resolveGrumbling();
@@ -73,11 +78,8 @@ export class Aperio implements IBrain {
resolveInteraction(interaction: ChibiInteraction): void { resolveInteraction(interaction: ChibiInteraction): void {
this.timeSinceLastInteraction = 0; this.timeSinceLastInteraction = 0;
const specificMatch: ResolutionFunction | undefined = this.interactionMap[this.state.name]?.[interaction.name]; const resolution = this.matchInteraction(this.interactionMap, interaction.name, STATES[this.chibi.state]);
const awakeMatch: ResolutionFunction | undefined = this.interactionMap[EChibiStateName.Awake]?.[interaction.name]; console.log('resolution', resolution);
const asleepMatch: ResolutionFunction | undefined = this.interactionMap[EChibiStateName.Sleeping]?.[interaction.name];
const resolution: ResolutionFunction | undefined = specificMatch || (this.isAwake() ? awakeMatch : asleepMatch);
console.log(`specific match: ${specificMatch}`);
console.log(`${this.chibi.name} should ${interaction.name} with payload: ${interaction.payload}`); console.log(`${this.chibi.name} should ${interaction.name} with payload: ${interaction.payload}`);
if (resolution) { if (resolution) {
this.changeState(resolution(interaction)); this.changeState(resolution(interaction));
@@ -88,6 +90,17 @@ export class Aperio implements IBrain {
return isState(this.state, EChibiStateName.Awake); return isState(this.state, EChibiStateName.Awake);
} }
matchInteraction(map: InteractionMap, interaction: EChibiInteraction, state: ChibiState): ResolutionFunction | undefined {
const directMatch = map?.[state.name]?.[interaction];
if (directMatch) {
return directMatch;
}
if (state.parent) {
return this.matchInteraction(map, interaction, state.parent);
}
return undefined;
}
// resolvers // resolvers
// functions which auitomatically resolve based on the existence cycle // functions which auitomatically resolve based on the existence cycle
@@ -107,16 +120,6 @@ export class Aperio implements IBrain {
} }
} }
increaseHappyness(increase: number): void {
console.log(`${this.chibi.name} happyness increases.`);
this.chibi.happyness = this.chibi.happyness + increase;
}
saddenedByNeglect(neglectTime: number, sadness: number): void {
console.log(`${this.chibi.name} happyness decreases.`);
this.chibi.happyness = this.chibi.happyness - (neglectTime * sadness);
}
resolveSleeping(): void { resolveSleeping(): void {
if (isState(this.state, EChibiStateName.Sleeping)) { if (isState(this.state, EChibiStateName.Sleeping)) {
console.log(`${this.chibi.name} is sleeping.`); console.log(`${this.chibi.name} is sleeping.`);
@@ -147,6 +150,17 @@ export class Aperio implements IBrain {
} }
} }
private increaseHappyness(increase: number): void {
console.log(`${this.chibi.name} happyness increases.`);
this.chibi.happyness.current = this.chibi.happyness.current + increase;
}
private saddenedByNeglect(neglectTime: number, sadness: number): void {
console.log(`${this.chibi.name} happyness decreases.`);
this.chibi.happyness.current = this.chibi.happyness.current - (neglectTime * sadness);
}
private changeState(state: ChibiState): void { private changeState(state: ChibiState): void {
this.previousState = this.state.name; this.previousState = this.state.name;
this.state = state; this.state = state;

View File

@@ -14,43 +14,45 @@
} }
</div> </div>
<div class="h-1/3 min-h-20 bg-primary-300 relative"> <div class="h-1/3 min-h-20 bg-primary-300 relative">
<ff-food-pantry @if (chibi()) {
[isVisible]="shownMenu == EInteractionMenuState.Pantry" <ff-food-pantry
(onClose)="closeMenu()" [isVisible]="shownMenu == EInteractionMenuState.Pantry"
(onFoodChosen)="interact(EChibiInteraction.Feed, $event)" (onClose)="closeMenu()"
></ff-food-pantry> (onFoodChosen)="interact(EChibiInteraction.Feed, $event)"
></ff-food-pantry>
<ff-inventory <ff-inventory
[isVisible]="shownMenu == EInteractionMenuState.Inventory" [isVisible]="shownMenu == EInteractionMenuState.Inventory"
(onClose)="closeMenu()" (onClose)="closeMenu()"
></ff-inventory> ></ff-inventory>
<ff-visitor-list <ff-visitor-list
[isVisible]="shownMenu == EInteractionMenuState.VisitorList" [isVisible]="shownMenu == EInteractionMenuState.VisitorList"
(onClose)="closeMenu()" (onClose)="closeMenu()"
[host]="chibi()" [host]="chibi()"
[visitors]="[]" [visitors]="[]"
></ff-visitor-list> ></ff-visitor-list>
<div class="flex flex-row gap-5 py-4 container mx-auto"> <div class="flex flex-row gap-5 py-4 container mx-auto">
<ff-button (click)="openInventory()">{{ <ff-button (click)="openInventory()">{{
lang.game.actions.giveItem lang.game.actions.giveItem
}}</ff-button>
<ff-button (click)="openFoodPantry()">{{
lang.game.actions.feed
}}</ff-button>
<ff-button (click)="openVisitorList()">{{
lang.game.actions.inviteVisitor
}}</ff-button>
@if (isAwake()) {
<ff-button (click)="interact(EChibiInteraction.WakeUp)">{{
lang.game.actions.wakeUp
}}</ff-button> }}</ff-button>
} @else { <ff-button (click)="openFoodPantry()">{{
<ff-button (click)="interact(EChibiInteraction.PutToSleep)">{{ lang.game.actions.feed
lang.game.actions.putToSleep
}}</ff-button> }}</ff-button>
} <ff-button (click)="openVisitorList()">{{
</div> lang.game.actions.inviteVisitor
}}</ff-button>
@if (isAwake()) {
<ff-button (click)="interact(EChibiInteraction.PutToSleep)">{{
lang.game.actions.putToSleep
}}</ff-button>
} @else {
<ff-button (click)="interact(EChibiInteraction.WakeUp)">{{
lang.game.actions.wakeUp
}}</ff-button>
}
</div>
}
</div> </div>
</div> </div>

View File

@@ -78,6 +78,7 @@ export class InteractionsComponent extends TranslateableComponent implements OnD
this.brain.resolveInteraction({ name: interaction, payload: item } as ChibiInteraction<Food>); this.brain.resolveInteraction({ name: interaction, payload: item } as ChibiInteraction<Food>);
} }
public openFoodPantry(): void { public openFoodPantry(): void {
this.interactionMenuState.set(EInteractionMenuState.Pantry); this.interactionMenuState.set(EInteractionMenuState.Pantry);
} }

View File

@@ -8,7 +8,7 @@ export type ChibiPreview = {
id: ChibiId; id: ChibiId;
name: EChibiName; name: EChibiName;
iconPath: string; iconPath: string;
happyness: number; happyness: ChibiStatBar;
level: number; level: number;
}; };
@@ -23,11 +23,11 @@ export type Chibi = {
} & ChibiWellnessStats & ChibiTraits; } & ChibiWellnessStats & ChibiTraits;
export type ChibiWellnessStats = { export type ChibiWellnessStats = {
happyness: number; happyness: ChibiStatBar;
health: number; health: ChibiStatBar;
hunger: number; //feed them hunger: ChibiStatBar; //feed them
boredom: number; //do activities to alleviate boredom boredom: ChibiStatBar; //do activities to alleviate boredom
energy: number; //let them rest energy: ChibiStatBar; //let them rest
}; };
export type ChibiTraits = { export type ChibiTraits = {
@@ -37,3 +37,8 @@ export type ChibiTraits = {
power: number; //how much damage they deal power: number; //how much damage they deal
maintenanceCals: number; //how quickly they get hungry maintenanceCals: number; //how quickly they get hungry
}; };
export type ChibiStatBar = {
current: number;
max: number;
}