pear can eat

This commit is contained in:
2026-03-15 19:26:12 +01:00
parent ca11f0f92f
commit fc0da039fa
3 changed files with 128 additions and 50 deletions

View File

@@ -1,7 +1,6 @@
import { Chibi } from "../../types/chibi/chibi";
import { EChibiInteraction } from "../../types/chibi/chibi-interaction";
import { Food } from "../../types/food";
import { RandomService } from "../random.service";
import { ChibiInteraction } from "./brains/aperio.brain";
export interface IBrain {
@@ -12,5 +11,5 @@ export interface IBrain {
exist(): void;
// function which resolves all interactions
resolveInteraction(interaction: EChibiInteraction, item?: Food): void
resolveInteraction(interaction: ChibiInteraction<any>): void
}

View File

@@ -1,27 +1,49 @@
import { Chibi } from "../../../types/chibi/chibi";
import { EChibiInteraction } from "../../../types/chibi/chibi-interaction";
import { ChibiState, isState, STATE_EATING, STATE_GRUMBLING, STATE_IDLE, STATE_SLEEPING, STATES } from "../../../types/chibi/chibi-state";
import { ChibiState, isState, STATE_AWAKE, STATE_EATING, STATE_GRUMBLING, STATE_IDLE, STATE_SLEEPING, STATES } from "../../../types/chibi/chibi-state";
import { EChibiStateName } from "../../../types/chibi/chibi-state-name";
import { Food } from "../../../types/food";
import { RandomOutcome, RandomService } from "../../random.service";
import { IBrain } from "../brain";
type ResolutionFunction = (interaction: EChibiInteraction) => ChibiState;
export type ChibiInteraction<T = null> = {
name: EChibiInteraction;
payload: T;
}
type ResolutionFunction = (interaction: ChibiInteraction<any>) => ChibiState;
type InteractionRecord = Partial<Record<EChibiInteraction, ResolutionFunction>>;
const MAX_TIME_AWAKE: number = 3;
const MAX_TIME_AWAKE: number = 10;
const HAPPINESS_FROM_SLEEP: number = 100;
const SADNESS_FROM_NEGLECT: number = 50;
const MAX_DURATION_GRUMBLE: number = 1;
const MAX_DURATION_EATING: number = 1;
export class Aperio implements IBrain {
private randomService!: RandomService;
private chibi!: Chibi;
private readonly defaultState: EChibiStateName = EChibiStateName.Sleeping;
private previousState: EChibiStateName = this.defaultState;
private hasBeenAwakeFor: number = 0;
private state!: ChibiState;
private timeSinceLastInteraction: number = 0;
private timeInCurrentState: number = 0;
private lastConsumedFoods: string[] = [];
private preferredFoods: string[] = [];
private state: ChibiState = STATES[this.defaultState];
private interactionMap: { [key in EChibiStateName]: InteractionRecord | undefined } = {
[EChibiStateName.Sleeping]: {
[EChibiInteraction.WakeUp]: (interaction: EChibiInteraction): ChibiState => this.wakeUp(interaction),
[EChibiInteraction.WakeUp]: (interaction: ChibiInteraction): ChibiState => this.wakeUp(interaction),
},
[EChibiStateName.Awake]: {
[EChibiInteraction.Feed]: (interaction: ChibiInteraction<Food>): ChibiState => this.eat(interaction),
},
[EChibiStateName.Awake]: undefined,
[EChibiStateName.Snoring]: undefined,
[EChibiStateName.Nightmare]: undefined,
[EChibiStateName.Grumbling]: undefined,
@@ -35,72 +57,123 @@ export class Aperio implements IBrain {
[EChibiStateName.Tired]: undefined
}
public readonly defaultState: EChibiStateName = EChibiStateName.Sleeping;
//needed: some sort of legal interactions map
//uses stateNames as keys
//then submap the interactions
//behind every interaction is a dedicated function
init(chibi: Chibi, randomService: RandomService): void {
this.randomService = randomService;
this.chibi = chibi;
this.state = STATES[chibi.state];
console.log(this.wakeUp);
}
exist(): void {
console.log('Brain is braining');
if (isState(this.state, EChibiStateName.Awake)) {
console.log('is awake');
this.hasBeenAwakeFor++;
if (this.hasBeenAwakeFor > MAX_TIME_AWAKE) {
this.state = STATE_SLEEPING;
this.chibi.state = EChibiStateName.Sleeping;
this.hasBeenAwakeFor = 0;
}
}
this.chibi.state = this.state.name;
this.timeInCurrentState++;
this.resolveAwake();
this.resolveSleeping();
this.resolveGrumbling();
this.resolveEating();
}
resolveInteraction(interaction: EChibiInteraction, item?: Food): void {
// reassign state based on resolving map
const resolution: ResolutionFunction | undefined = this.interactionMap[this.state.name]?.[interaction];
resolveInteraction(interaction: ChibiInteraction): void {
this.timeSinceLastInteraction = 0;
const specificMatch: ResolutionFunction | undefined = this.interactionMap[this.state.name]?.[interaction.name];
const awakeMatch: ResolutionFunction | undefined = this.interactionMap[EChibiStateName.Awake]?.[interaction.name];
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}`);
if (resolution) {
this.state = resolution(interaction);
this.changeState(resolution(interaction));
}
}
isAwake(): boolean {
return isState(this.state, EChibiStateName.Awake);
}
// resolvers
// functions which auitomatically resolve based on the existence cycle
resolveAwake(): void {
if (this.isAwake()) {
console.log(`${this.chibi.name} is awake.`);
this.timeSinceLastInteraction++;
this.hasBeenAwakeFor++;
if (this.timeInCurrentState % 10 === 0) {
this.saddenedByNeglect(this.timeSinceLastInteraction, SADNESS_FROM_NEGLECT);
}
if (this.hasBeenAwakeFor > MAX_TIME_AWAKE) {
this.changeState(STATE_SLEEPING);
}
}
}
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 {
if (isState(this.state, EChibiStateName.Sleeping)) {
console.log(`${this.chibi.name} is sleeping.`);
this.increaseHappyness(HAPPINESS_FROM_SLEEP);
const awakeChance = this.randomService.obtainRandom(2048);
if (awakeChance.matchesChance(1)) {
this.changeState(STATE_AWAKE);
}
}
}
resolveGrumbling(): void {
if (isState(this.state, EChibiStateName.Grumbling)) {
console.log(`${this.chibi.name} is grumbling.`);
if (this.timeInCurrentState > MAX_DURATION_GRUMBLE) {
this.changeState(STATE_SLEEPING);
}
}
}
resolveEating(): void {
if (isState(this.state, EChibiStateName.Eating)) {
console.log(`${this.chibi.name} is eating.`);
if (this.timeInCurrentState > MAX_DURATION_EATING) {
this.changeState(STATES[this.previousState]);
}
}
}
private changeState(state: ChibiState): void {
this.previousState = this.state.name;
this.state = state;
this.chibi.state = this.state.name;
this.timeInCurrentState = 0;
}
/*
pear mostly sleeps
visiting caethya will wake her up and go into lovey-dovey mode
she has a 1/2048 chance of waking up on her own
pears happyness goes up when she sleeps
pear never gets hungry
but she accepts foods she likes, which raise her happyness
as long as the same food is not offered thrice in a row (except banana split)
pears energy never goes down
pear will accept fights and enjoys them
pear will accept walks and enjoys them
pear will get sad if she is awake and you do not interact with her
pear gets offended if you suggest she go to sleep
after any interaction she has a 1/4 chance of going to sleep
*/
private wakeUp(interaction: EChibiInteraction): ChibiState {
// interaction functions
// functions which are triggered by user actions
private wakeUp(interaction: ChibiInteraction): ChibiState {
const wakeUpOutcome: RandomOutcome = this.randomService.obtainRandom(128);
const willWakeUp: boolean = wakeUpOutcome.matchesChance(1);
const willGrumble: boolean = wakeUpOutcome.matchesChance(12, 1);
console.log(wakeUpOutcome.random);
console.log(willWakeUp);
console.log(willGrumble);
if (willWakeUp) {
return STATE_IDLE;
} else if (willGrumble) {
@@ -109,10 +182,16 @@ export class Aperio implements IBrain {
return STATE_SLEEPING;
}
private resolveAwakeInteraction(interaction: EChibiInteraction, item?: Food): ChibiState {
if (EChibiInteraction.Feed) {
private eat(interaction: ChibiInteraction<Food>): ChibiState {
console.log(`${this.chibi.name} should eat ${interaction.payload.name}`);
const foodId = interaction.payload.id;
if (this.preferredFoods.includes(foodId)) {
this.lastConsumedFoods.push(foodId);
this.increaseHappyness(100);
return STATE_EATING;
} else {
}
return STATE_EATING;
}
return STATE_IDLE;
}
};

View File

@@ -13,8 +13,8 @@ import { BRAIN_MAP } from '../../logic/chibi-behaviour/brain-map';
import { TranslateableComponent } from '../../components/translateable.component';
import { Food } from '../../types/food';
import { interval } from 'rxjs';
import { ChibiState, STATES } from '../../types/chibi/chibi-state';
import { RandomService } from '../../logic/random.service';
import { ChibiInteraction } from '../../logic/chibi-behaviour/brains/aperio.brain';
enum EInteractionMenuState {
Neutral,
@@ -22,7 +22,7 @@ enum EInteractionMenuState {
Inventory
}
const THINKING_INTERVAL: number = 10000;
const THINKING_INTERVAL: number = 2000;
@Component({
selector: 'ff-interactions',
@@ -61,7 +61,7 @@ export class InteractionsComponent extends TranslateableComponent {
public interact(interaction: EChibiInteraction, item?: Food): void {
console.log(`${interaction} ${this.chibi().name} and item ${item}`);
this.brain.resolveInteraction(interaction, item);
this.brain.resolveInteraction({ name: interaction, payload: item } as ChibiInteraction<Food>);
}
public openFoodPantry(): void {