81 lines
2.5 KiB
TypeScript
81 lines
2.5 KiB
TypeScript
import { Component, computed, input, output } from '@angular/core';
|
||
import type { EquipmentSlot, InventoryItem } from '../../core/api/game-api.models';
|
||
import { RARITY_LABELS } from '../../shared/item-card/item-card.component';
|
||
|
||
const SLOT_LABELS: Readonly<Record<EquipmentSlot, string>> = {
|
||
WEAPON: 'Waffe',
|
||
HEAD: 'Kopf',
|
||
CHEST: 'Brust',
|
||
HANDS: 'Handschuhe',
|
||
LEGS: 'Beine',
|
||
FEET: 'Stiefel',
|
||
AMULET: 'Amulett',
|
||
};
|
||
|
||
interface StatRow {
|
||
label: string;
|
||
value: number;
|
||
diff: number | null;
|
||
}
|
||
|
||
type StatKey = 'weaponDamage' | 'bonusAttack' | 'bonusHp' | 'bonusArmor';
|
||
const STAT_LABELS: ReadonlyArray<{ label: string; key: StatKey }> = [
|
||
{ label: 'Waffenschaden', key: 'weaponDamage' },
|
||
{ label: 'Angriff', key: 'bonusAttack' },
|
||
{ label: 'Leben', key: 'bonusHp' },
|
||
{ label: 'Rüstung', key: 'bonusArmor' },
|
||
];
|
||
|
||
/** Selected-item details and equip comparison (spec §32–37). */
|
||
@Component({
|
||
selector: 'app-inventory-detail-panel',
|
||
templateUrl: './inventory-detail-panel.component.html',
|
||
styleUrl: './inventory-detail-panel.component.scss',
|
||
})
|
||
export class InventoryDetailPanelComponent {
|
||
readonly item = input<InventoryItem | null>(null);
|
||
readonly equippedItemInSlot = input<InventoryItem | null>(null);
|
||
readonly characterLevel = input(1);
|
||
readonly busy = input(false);
|
||
readonly equip = output<string>();
|
||
|
||
protected readonly rarityLabel = computed(() => {
|
||
const item = this.item();
|
||
return item ? RARITY_LABELS[item.item.rarity] : '';
|
||
});
|
||
|
||
protected readonly slotLabel = computed(() => {
|
||
const slot = this.item()?.item.equipmentSlot;
|
||
return slot ? SLOT_LABELS[slot] : null;
|
||
});
|
||
|
||
protected readonly statRows = computed<StatRow[]>(() => {
|
||
const item = this.item();
|
||
if (!item) {
|
||
return [];
|
||
}
|
||
const compareTo = this.equippedItemInSlot();
|
||
const comparable = compareTo && compareTo.id !== item.id ? compareTo.item : null;
|
||
|
||
return STAT_LABELS.map(({ label, key }) => ({
|
||
label,
|
||
value: item.item[key],
|
||
diff: comparable ? item.item[key] - comparable[key] : null,
|
||
})).filter((row) => row.value > 0 || (row.diff ?? 0) !== 0);
|
||
});
|
||
|
||
protected readonly isEquippable = computed(() => !!this.item()?.item.equipmentSlot);
|
||
|
||
protected readonly meetsLevelRequirement = computed(() => {
|
||
const item = this.item();
|
||
return item ? item.item.requiredLevel <= this.characterLevel() : true;
|
||
});
|
||
|
||
protected onEquip(): void {
|
||
const item = this.item();
|
||
if (item) {
|
||
this.equip.emit(item.id);
|
||
}
|
||
}
|
||
}
|