diff --git a/server/src/systems/__tests__/socialSystem.test.ts b/server/src/systems/__tests__/socialSystem.test.ts index 887eced..a2d4f89 100644 --- a/server/src/systems/__tests__/socialSystem.test.ts +++ b/server/src/systems/__tests__/socialSystem.test.ts @@ -341,6 +341,37 @@ describe('socialSystem', () => { expect(saAfter.globalCooldown).toBe(40); }); + it('drifts sociability and empathy on positive social outcome', () => { + const world = new World(); + const a = createNPC(world, 5, 5); + const b = createNPC(world, 8, 5); + addStats(world, a, { sociability: 10.0, empathy: 10.0 }); + addStats(world, b); + const sa = world.getComponent(a, 'socialState')!; + const sb = world.getComponent(b, 'socialState')!; + sa.phase = 'emoting'; sa.partnerId = b; sa.phaseTimer = 1; sa.outcome = 'positive'; + sb.phase = 'emoting'; sb.partnerId = a; sb.phaseTimer = 1; sb.outcome = 'positive'; + socialSystem(world); + const stats = world.getComponent(a, 'stats')!; + expect(stats.sociability).toBeCloseTo(10.05); + expect(stats.empathy).toBeCloseTo(10.05); + }); + + it('drifts temperament on negative social outcome', () => { + const world = new World(); + const a = createNPC(world, 5, 5); + const b = createNPC(world, 8, 5); + addStats(world, a, { temperament: 10.0 }); + addStats(world, b); + const sa = world.getComponent(a, 'socialState')!; + const sb = world.getComponent(b, 'socialState')!; + sa.phase = 'emoting'; sa.partnerId = b; sa.phaseTimer = 1; sa.outcome = 'negative'; + sb.phase = 'emoting'; sb.partnerId = a; sb.phaseTimer = 1; sb.outcome = 'negative'; + socialSystem(world); + const stats = world.getComponent(a, 'stats')!; + expect(stats.temperament).toBeCloseTo(10.02); + }); + it('high empathy biases positive outcome', () => { const world = new World(); let positiveCount = 0; diff --git a/server/src/systems/socialSystem.ts b/server/src/systems/socialSystem.ts index 9aefa53..0deff58 100644 --- a/server/src/systems/socialSystem.ts +++ b/server/src/systems/socialSystem.ts @@ -3,6 +3,7 @@ import { SOCIAL_GLOBAL_COOLDOWN, SOCIAL_PAIR_COOLDOWN, HUNGER_THRESHOLD, ENERGY_THRESHOLD, Direction, type SocialState, type Position, type Movement, type NPCBrain, type Needs, type EntityId, + type Stats, } from '@dflike/shared'; import type { World } from '../ecs/World.js'; import { getEffectiveStat } from './statHelpers.js'; @@ -102,14 +103,36 @@ export function socialSystem(world: World): void { const socA = getEffectiveStat(world, e, 'sociability'); social.globalCooldown = Math.round(SOCIAL_GLOBAL_COOLDOWN * (1 - (socA - 10) * 0.04)); social.pairCooldowns.set(partnerId!, SOCIAL_PAIR_COOLDOWN); + + const socB = getEffectiveStat(world, partnerId!, 'sociability'); + partnerSocial.globalCooldown = Math.round(SOCIAL_GLOBAL_COOLDOWN * (1 - (socB - 10) * 0.04)); + partnerSocial.pairCooldowns.set(e, SOCIAL_PAIR_COOLDOWN); + + // Baseline stat drift + const statsA = world.getComponent(e, 'stats'); + if (statsA) { + if (social.outcome === 'positive') { + statsA.sociability = Math.min(18, statsA.sociability + 0.05); + statsA.empathy = Math.min(18, statsA.empathy + 0.05); + } else { + statsA.temperament = Math.min(18, statsA.temperament + 0.02); + } + } + const statsB = world.getComponent(partnerId!, 'stats'); + if (statsB) { + if (partnerSocial.outcome === 'positive') { + statsB.sociability = Math.min(18, statsB.sociability + 0.05); + statsB.empathy = Math.min(18, statsB.empathy + 0.05); + } else { + statsB.temperament = Math.min(18, statsB.temperament + 0.02); + } + } + social.phase = 'none'; social.partnerId = null; social.phaseTimer = 0; social.outcome = null; - const socB = getEffectiveStat(world, partnerId!, 'sociability'); - partnerSocial.globalCooldown = Math.round(SOCIAL_GLOBAL_COOLDOWN * (1 - (socB - 10) * 0.04)); - partnerSocial.pairCooldowns.set(e, SOCIAL_PAIR_COOLDOWN); partnerSocial.phase = 'none'; partnerSocial.partnerId = null; partnerSocial.phaseTimer = 0;