feat: audit Phase 1 — P0/P1 quick wins
All checks were successful
CI/CD — Build & Deploy / Build & Deploy (push) Successful in 35s
All checks were successful
CI/CD — Build & Deploy / Build & Deploy (push) Successful in 35s
- Fix vitalité: HP initial = 100 + (vitalité-1)×10 - Arme de départ: Bâton de Roseau équipé à la création - Rebalance forge: niv3 200, niv4 400, niv5 700 (−30%) - Confirmation avant vente d'item (confirm dialog) - Fix forge costs dupliqués (shop sellback + inventaire)
This commit is contained in:
@@ -23,9 +23,9 @@ const RARITY_LABELS: Record<string, string> = {
|
|||||||
const FORGE_TABLE = [
|
const FORGE_TABLE = [
|
||||||
{ level: 1, gold: 50, endurance: 10, risk: '0%', bonus: '+2' },
|
{ level: 1, gold: 50, endurance: 10, risk: '0%', bonus: '+2' },
|
||||||
{ level: 2, gold: 100, endurance: 10, risk: '0%', bonus: '+4' },
|
{ level: 2, gold: 100, endurance: 10, risk: '0%', bonus: '+4' },
|
||||||
{ level: 3, gold: 250, endurance: 10, risk: '20%', bonus: '+6' },
|
{ level: 3, gold: 200, endurance: 10, risk: '20%', bonus: '+6' },
|
||||||
{ level: 4, gold: 500, endurance: 10, risk: '30%', bonus: '+8' },
|
{ level: 4, gold: 400, endurance: 10, risk: '30%', bonus: '+8' },
|
||||||
{ level: 5, gold: 1000, endurance: 10, risk: '40%', bonus: '+10' },
|
{ level: 5, gold: 700, endurance: 10, risk: '40%', bonus: '+10' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const TABS = [
|
const TABS = [
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ function ItemCard({ ci, onEquip, onUnequip, onSell, selling }: {
|
|||||||
const forgeBonusDEF = item.type === 'armor' ? ci.forgeLevel * 2 : 0;
|
const forgeBonusDEF = item.type === 'armor' ? ci.forgeLevel * 2 : 0;
|
||||||
const totalATK = item.attackBonus + forgeBonusATK;
|
const totalATK = item.attackBonus + forgeBonusATK;
|
||||||
const totalDEF = item.defenseBonus + forgeBonusDEF;
|
const totalDEF = item.defenseBonus + forgeBonusDEF;
|
||||||
const FORGE_COSTS: Record<number, number> = { 1: 50, 2: 100, 3: 250, 4: 500, 5: 1000 };
|
const FORGE_COSTS: Record<number, number> = { 1: 50, 2: 100, 3: 200, 4: 400, 5: 700 };
|
||||||
let forgeInvestment = 0;
|
let forgeInvestment = 0;
|
||||||
for (let i = 1; i <= ci.forgeLevel; i++) forgeInvestment += FORGE_COSTS[i] ?? 0;
|
for (let i = 1; i <= ci.forgeLevel; i++) forgeInvestment += FORGE_COSTS[i] ?? 0;
|
||||||
const sellPrice = Math.floor(((item as any).buyPrice || 0) * 0.4 + forgeInvestment * 0.5);
|
const sellPrice = Math.floor(((item as any).buyPrice || 0) * 0.4 + forgeInvestment * 0.5);
|
||||||
@@ -125,7 +125,7 @@ export function InventoryPage() {
|
|||||||
key={ci.id} ci={ci}
|
key={ci.id} ci={ci}
|
||||||
onEquip={() => equipMut.mutate(ci.id)}
|
onEquip={() => equipMut.mutate(ci.id)}
|
||||||
onUnequip={() => unequipMut.mutate('weapon')}
|
onUnequip={() => unequipMut.mutate('weapon')}
|
||||||
onSell={() => sellMut.mutate(ci.id)}
|
onSell={() => { if (confirm(`Vendre ${ci.item.name} ?`)) sellMut.mutate(ci.id); }}
|
||||||
selling={sellMut.isPending}
|
selling={sellMut.isPending}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
@@ -145,7 +145,7 @@ export function InventoryPage() {
|
|||||||
key={ci.id} ci={ci}
|
key={ci.id} ci={ci}
|
||||||
onEquip={() => equipMut.mutate(ci.id)}
|
onEquip={() => equipMut.mutate(ci.id)}
|
||||||
onUnequip={() => unequipMut.mutate('armor')}
|
onUnequip={() => unequipMut.mutate('armor')}
|
||||||
onSell={() => sellMut.mutate(ci.id)}
|
onSell={() => { if (confirm(`Vendre ${ci.item.name} ?`)) sellMut.mutate(ci.id); }}
|
||||||
selling={sellMut.isPending}
|
selling={sellMut.isPending}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ import { LevelThreshold } from './entities/level-threshold.entity';
|
|||||||
import { CreateCharacterDto } from './dto/create-character.dto';
|
import { CreateCharacterDto } from './dto/create-character.dto';
|
||||||
import { DistributeStatsDto } from './dto/distribute-stats.dto';
|
import { DistributeStatsDto } from './dto/distribute-stats.dto';
|
||||||
import { User } from '../user/user.entity';
|
import { User } from '../user/user.entity';
|
||||||
|
import { Item } from '../item/item.entity';
|
||||||
|
import { CharacterItem } from '../item/character-item.entity';
|
||||||
import { xpRequiredForLevel } from '../combat/combat.engine';
|
import { xpRequiredForLevel } from '../combat/combat.engine';
|
||||||
|
|
||||||
const STAT_POOL = 10; // 5 stats × 1 base + 5 points à distribuer
|
const STAT_POOL = 10; // 5 stats × 1 base + 5 points à distribuer
|
||||||
@@ -53,6 +55,7 @@ export class CharacterService {
|
|||||||
throw new ConflictException('Ce joueur possède déjà un personnage');
|
throw new ConflictException('Ce joueur possède déjà un personnage');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const baseHp = 100 + (dto.vitalite - 1) * 10; // vitalité 1 = 100 HP, chaque point = +10
|
||||||
const character = this.characterRepository.create({
|
const character = this.characterRepository.create({
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
name: dto.name,
|
name: dto.name,
|
||||||
@@ -61,12 +64,29 @@ export class CharacterService {
|
|||||||
intelligence: dto.intelligence,
|
intelligence: dto.intelligence,
|
||||||
chance: dto.chance,
|
chance: dto.chance,
|
||||||
vitalite: dto.vitalite,
|
vitalite: dto.vitalite,
|
||||||
|
hpMax: baseHp,
|
||||||
|
hpCurrent: baseHp,
|
||||||
enduranceSaved: 100,
|
enduranceSaved: 100,
|
||||||
lastEnduranceTs: new Date(),
|
lastEnduranceTs: new Date(),
|
||||||
enduranceMax: 100,
|
enduranceMax: 100,
|
||||||
});
|
});
|
||||||
|
|
||||||
const saved = await this.characterRepository.save(character);
|
const saved = await this.characterRepository.save(character);
|
||||||
|
|
||||||
|
// Arme de départ — Bâton de Roseau équipé automatiquement
|
||||||
|
const starterWeapon = await this.dataSource.getRepository(Item)
|
||||||
|
.findOne({ where: { name: 'Bâton de Roseau' } });
|
||||||
|
if (starterWeapon) {
|
||||||
|
await this.dataSource.getRepository(CharacterItem).save(
|
||||||
|
this.dataSource.getRepository(CharacterItem).create({
|
||||||
|
characterId: saved.id,
|
||||||
|
itemId: starterWeapon.id,
|
||||||
|
forgeLevel: 0,
|
||||||
|
equipped: true,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...saved,
|
...saved,
|
||||||
enduranceCurrent: this.calculateEndurance(saved),
|
enduranceCurrent: this.calculateEndurance(saved),
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ const FORGE_BONUS_PER_LEVEL = 2;
|
|||||||
const FORGE_GOLD_COST: Record<number, number> = {
|
const FORGE_GOLD_COST: Record<number, number> = {
|
||||||
1: 50,
|
1: 50,
|
||||||
2: 100,
|
2: 100,
|
||||||
3: 250,
|
3: 200,
|
||||||
4: 500,
|
4: 400,
|
||||||
5: 1000,
|
5: 700,
|
||||||
};
|
};
|
||||||
|
|
||||||
const FORGE_ENDURANCE_COST = 10;
|
const FORGE_ENDURANCE_COST = 10;
|
||||||
|
|||||||
@@ -154,7 +154,7 @@ export class ShopService {
|
|||||||
if (charItem.equipped) throw new BadRequestException('Déséquipez l\'item avant de le vendre');
|
if (charItem.equipped) throw new BadRequestException('Déséquipez l\'item avant de le vendre');
|
||||||
|
|
||||||
// Prix de vente = base + investissement forge (coûts cumulés * 50%)
|
// Prix de vente = base + investissement forge (coûts cumulés * 50%)
|
||||||
const FORGE_GOLD_COST: Record<number, number> = { 1: 50, 2: 100, 3: 250, 4: 500, 5: 1000 };
|
const FORGE_GOLD_COST: Record<number, number> = { 1: 50, 2: 100, 3: 200, 4: 400, 5: 700 };
|
||||||
let forgeInvestment = 0;
|
let forgeInvestment = 0;
|
||||||
for (let i = 1; i <= charItem.forgeLevel; i++) {
|
for (let i = 1; i <= charItem.forgeLevel; i++) {
|
||||||
forgeInvestment += FORGE_GOLD_COST[i] ?? 0;
|
forgeInvestment += FORGE_GOLD_COST[i] ?? 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user