Merge branch 'master' into worktree-playable-slice-0.6.5-renown-reputation
Reconciles Slice 0.6.5 (Renown & Reputation Foundation) against master's persistent-HP-and-regeneration slice, which landed independently and touches several of the same files (Character entity, CombatService, EquipmentService, the inventory detail panel). Conflict resolutions: - CharacterStatsService/EquipmentService constructor wiring: kept master's CharacterVitalsService injection, which this branch's version of the same files didn't have yet. - CombatService.performAction: kept master's HP-guard logic (characterTooWounded, vitals pause-on-enter) alongside this branch's multi-line calculate() call style. - Inventory detail panel (.html/.ts/.scss/.spec.ts): master had redesigned the panel (wrapping section, rarity styling, flavour text, a shared inventory.labels.ts) on top of the OLD level-gated component, since this branch's removal of the level gate (R4, Task 8/14) hadn't reached master yet. Kept master's visual redesign in full, but with the level-gate concept removed throughout: no requiredLevel stat block, no meetsLevelRequirement() branch in the equip button, no now-dead .detail__value--unmet SCSS rule. Kept both branches' independent tests (non-equippable-item, flavour-text). - inventory-page.component.ts: dropped master's dead characterLevel computed (nothing in the template read it, and the level concept is gone); kept its independent bagCells/bagUsed/bagCapacity grid feature, which has nothing to do with renown or level. Post-merge fixture repairs (three files failed the Angular bundle compile because they predate master's hpRegenPerSecond/hpRegenSince fields or master's item description field, neither conflict-marked since git considered them non-overlapping edits): - app.spec.ts: a 'renders loaded character values' test added on master after this branch forked still used the abolished level/ experience fields on its decoy fixture -- retargeted to renown. - inventory-detail-panel.component.spec.ts: the ashPelt fixture added by this branch's final-review follow-up predates master's required description field. - top-bar.component.spec.ts: this branch's fixture predates master's required hpRegenPerSecond/hpRegenSince fields. No database migration touches the same column: master's 1792000000000-AddHpRegeneration only adds characters.hp_regen_since, independent of this slice's 1791000000000-CreateRenownAndReputation. Timestamp ordering between the two was already correct with no rename needed. Verified: API 288/288 (267 from this slice + 21 from master), API build zero errors, web 237/237 (230 from this slice + 7 from master). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { DataSource, Repository } from 'typeorm';
|
||||
import { CharacterStatsService } from '../characters/character-stats.service';
|
||||
import { CharacterVitalsService } from '../characters/character-vitals.service';
|
||||
import { Character } from '../characters/entities/character.entity';
|
||||
import { Hunt } from '../hunting/entities/hunt.entity';
|
||||
import { HuntEncounter } from '../hunting/entities/hunt-encounter.entity';
|
||||
@@ -16,6 +17,7 @@ import { CombatEngineService } from './combat-engine.service';
|
||||
import { CombatEngineState, CombatIntent } from './combat-engine.types';
|
||||
import {
|
||||
characterNotFound,
|
||||
characterTooWounded,
|
||||
characterTravelling,
|
||||
combatAlreadyActive,
|
||||
combatAlreadyFinished,
|
||||
@@ -78,6 +80,7 @@ export class CombatService {
|
||||
private readonly travelService: TravelService,
|
||||
private readonly combatEngine: CombatEngineService,
|
||||
private readonly characterStats: CharacterStatsService,
|
||||
private readonly characterVitals: CharacterVitalsService,
|
||||
private readonly combatRewards: CombatRewardService,
|
||||
) {}
|
||||
|
||||
@@ -138,6 +141,11 @@ export class CombatService {
|
||||
character,
|
||||
manager,
|
||||
);
|
||||
if (playerStats.currentHp < 1) {
|
||||
throw characterTooWounded();
|
||||
}
|
||||
this.characterVitals.pause(character, playerStats.currentHp);
|
||||
await characters.save(character);
|
||||
|
||||
const combat = combats.create({
|
||||
characterId,
|
||||
@@ -146,7 +154,7 @@ export class CombatService {
|
||||
status: CombatStatus.ACTIVE,
|
||||
round: 1,
|
||||
playerMaxHp: playerStats.maxHp,
|
||||
playerCurrentHp: playerStats.maxHp,
|
||||
playerCurrentHp: playerStats.currentHp,
|
||||
monsterMaxHp: monster.maxHp,
|
||||
monsterCurrentHp: monster.maxHp,
|
||||
playerState: {
|
||||
@@ -220,7 +228,7 @@ export class CombatService {
|
||||
// no-op re-lock — but locking it first here keeps both code paths
|
||||
// consistent and avoids a lock-order inversion that could deadlock two
|
||||
// concurrent requests against the same character. Do not reorder this.
|
||||
await this.lockCharacter(characters, characterId);
|
||||
const character = await this.lockCharacter(characters, characterId);
|
||||
|
||||
const combat = await combats.findOne({
|
||||
where: { id: combatId, characterId },
|
||||
@@ -251,12 +259,16 @@ export class CombatService {
|
||||
combat.monsterState = result.state.monster.stats;
|
||||
if (combat.status !== CombatStatus.ACTIVE) {
|
||||
combat.completedAt = new Date();
|
||||
this.characterVitals.resume(character, combat.playerCurrentHp);
|
||||
await this.settleEncounter(
|
||||
manager.getRepository(HuntEncounter),
|
||||
combat.huntEncounterId,
|
||||
combat.status,
|
||||
);
|
||||
} else {
|
||||
this.characterVitals.pause(character, combat.playerCurrentHp);
|
||||
}
|
||||
await characters.save(character);
|
||||
await combats.save(combat);
|
||||
|
||||
const startingSequence = await combatEvents.count({
|
||||
@@ -284,7 +296,7 @@ export class CombatService {
|
||||
? await this.combatRewards.grantVictoryRewards(manager, combat)
|
||||
: null;
|
||||
|
||||
const [character, monster, events] = await Promise.all([
|
||||
const [reloadedCharacter, monster, events] = await Promise.all([
|
||||
this.loadCharacter(
|
||||
combat.characterId,
|
||||
manager.getRepository(Character),
|
||||
@@ -296,7 +308,7 @@ export class CombatService {
|
||||
this.loadEvents(combat.id, combatEvents),
|
||||
]);
|
||||
|
||||
return this.toCombatDto(combat, character.name, monster, events, rewards);
|
||||
return this.toCombatDto(combat, reloadedCharacter.name, monster, events, rewards);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user