feat(rewards): remove classical XP from the combat reward pipeline

This commit is contained in:
Bastian Wagner
2026-08-21 13:23:46 +02:00
parent 6fb61792e7
commit c9d9144b5f
2 changed files with 5 additions and 18 deletions

View File

@@ -120,12 +120,11 @@ function combat(overrides: Partial<Combat> = {}): Combat {
function createState(overrides: Partial<State> = {}): State { function createState(overrides: Partial<State> = {}): State {
return { return {
characters: [{ id: CHARACTER_ID, experience: 12, silver: 3 } as Character], characters: [{ id: CHARACTER_ID, silver: 3 } as Character],
monsters: [ monsters: [
{ {
id: ASH_RAT_ID, id: ASH_RAT_ID,
key: 'ash-rat', key: 'ash-rat',
experienceReward: 8,
silverMin: 4, silverMin: 4,
silverMax: 7, silverMax: 7,
lootTableId: ASH_RAT_TABLE, lootTableId: ASH_RAT_TABLE,
@@ -133,7 +132,6 @@ function createState(overrides: Partial<State> = {}): State {
{ {
id: ROAD_BANDIT_ID, id: ROAD_BANDIT_ID,
key: 'road-bandit', key: 'road-bandit',
experienceReward: 16,
silverMin: 9, silverMin: 9,
silverMax: 15, silverMax: 15,
lootTableId: ROAD_BANDIT_TABLE, lootTableId: ROAD_BANDIT_TABLE,
@@ -189,7 +187,6 @@ describe('CombatRewardService', () => {
service(state).grantVictoryRewards(fakeManager(state), combat({ status: CombatStatus.ACTIVE })), service(state).grantVictoryRewards(fakeManager(state), combat({ status: CombatStatus.ACTIVE })),
).rejects.toMatchObject({ code: 'COMBAT_NOT_WON' }); ).rejects.toMatchObject({ code: 'COMBAT_NOT_WON' });
expect(state.combatRewards).toHaveLength(0); expect(state.combatRewards).toHaveLength(0);
expect(state.characters[0].experience).toBe(12);
expect(state.characters[0].silver).toBe(3); expect(state.characters[0].silver).toBe(3);
}); });
@@ -207,13 +204,13 @@ describe('CombatRewardService', () => {
const reward = await service(state).grantVictoryRewards(fakeManager(state), combat()); const reward = await service(state).grantVictoryRewards(fakeManager(state), combat());
expect(reward).toEqual({ experience: 8, silver: 6, items: [] }); expect(reward).toEqual({ silver: 6, items: [] });
expect(state.combatRewards).toHaveLength(1); expect(state.combatRewards).toHaveLength(1);
}); });
}); });
describe('Aschenratte', () => { describe('Aschenratte', () => {
it('grants 8 XP and a silver roll inside 4-7, persisted on the character', async () => { it('grants a silver roll inside 4-7, persisted on the character', async () => {
const state = createState(); const state = createState();
const reward = await service(state, fakeLoot(), fixedRandom(0)).grantVictoryRewards( const reward = await service(state, fakeLoot(), fixedRandom(0)).grantVictoryRewards(
@@ -221,9 +218,7 @@ describe('CombatRewardService', () => {
combat(), combat(),
); );
expect(reward.experience).toBe(8);
expect(reward.silver).toBe(4); expect(reward.silver).toBe(4);
expect(state.characters[0].experience).toBe(20);
expect(state.characters[0].silver).toBe(7); expect(state.characters[0].silver).toBe(7);
}); });
@@ -242,7 +237,7 @@ describe('CombatRewardService', () => {
describe('Straßenräuber', () => { describe('Straßenräuber', () => {
const banditCombat = combat({ monsterDefinitionId: ROAD_BANDIT_ID }); const banditCombat = combat({ monsterDefinitionId: ROAD_BANDIT_ID });
it('grants 16 XP and a silver roll inside 9-15', async () => { it('grants a silver roll inside 9-15', async () => {
const state = createState(); const state = createState();
const reward = await service(state, fakeLoot(), fixedRandom(0)).grantVictoryRewards( const reward = await service(state, fakeLoot(), fixedRandom(0)).grantVictoryRewards(
@@ -250,7 +245,6 @@ describe('CombatRewardService', () => {
banditCombat, banditCombat,
); );
expect(reward.experience).toBe(16);
expect(reward.silver).toBe(9); expect(reward.silver).toBe(9);
}); });
@@ -336,7 +330,6 @@ describe('CombatRewardService', () => {
expect(state.combatRewardItems).toHaveLength(1); expect(state.combatRewardItems).toHaveLength(1);
expect(state.characterItems).toHaveLength(1); expect(state.characterItems).toHaveLength(1);
expect(state.characterItems[0].quantity).toBe(1); expect(state.characterItems[0].quantity).toBe(1);
expect(state.characters[0].experience).toBe(20);
expect(state.characters[0].silver).toBe(7); expect(state.characters[0].silver).toBe(7);
expect(loot.rollLoot).toHaveBeenCalledTimes(1); expect(loot.rollLoot).toHaveBeenCalledTimes(1);
}); });
@@ -395,7 +388,6 @@ describe('CombatRewardService', () => {
// Every rolled item definition is resolved before any mutation, so a // Every rolled item definition is resolved before any mutation, so a
// missing one must leave no reward row and no character grant behind. // missing one must leave no reward row and no character grant behind.
expect(state.combatRewards).toHaveLength(0); expect(state.combatRewards).toHaveLength(0);
expect(state.characters[0].experience).toBe(12);
expect(state.characters[0].silver).toBe(3); expect(state.characters[0].silver).toBe(3);
}); });
}); });

View File

@@ -27,7 +27,6 @@ export interface CombatRewardItemDto {
} }
export interface CombatRewardDto { export interface CombatRewardDto {
experience: number;
silver: number; silver: number;
items: CombatRewardItemDto[]; items: CombatRewardItemDto[];
} }
@@ -76,7 +75,6 @@ export class CombatRewardService {
throw rewardStateInvalid(); throw rewardStateInvalid();
} }
const experience = monster.experienceReward;
const silver = rollInclusive( const silver = rollInclusive(
this.randomSource, this.randomSource,
monster.silverMin, monster.silverMin,
@@ -111,7 +109,6 @@ export class CombatRewardService {
if (!character) { if (!character) {
throw rewardStateInvalid(); throw rewardStateInvalid();
} }
character.experience += experience;
character.silver += silver; character.silver += silver;
await characters.save(character); await characters.save(character);
@@ -119,7 +116,6 @@ export class CombatRewardService {
rewards.create({ rewards.create({
combatId: combat.id, combatId: combat.id,
characterId: combat.characterId, characterId: combat.characterId,
experienceGranted: experience,
silverGranted: silver, silverGranted: silver,
}), }),
); );
@@ -171,7 +167,7 @@ export class CombatRewardService {
granted.sort((a, b) => a.itemDefinitionId.localeCompare(b.itemDefinitionId)); granted.sort((a, b) => a.itemDefinitionId.localeCompare(b.itemDefinitionId));
const items = granted.map((entry) => entry.dto); const items = granted.map((entry) => entry.dto);
return { experience, silver, items }; return { silver, items };
} }
/** Reads a persisted reward so a refresh replays it (spec §25, §48). */ /** Reads a persisted reward so a refresh replays it (spec §25, §48). */
@@ -217,7 +213,6 @@ export class CombatRewardService {
} }
return { return {
experience: reward.experienceGranted,
silver: reward.silverGranted, silver: reward.silverGranted,
items, items,
}; };