|
|
|
|
@ -12,6 +12,8 @@ interface NPCDialogProps {
|
|
|
|
|
npc: NPC;
|
|
|
|
|
heroQuests: HeroQuest[];
|
|
|
|
|
heroGold: number;
|
|
|
|
|
potionCost: number;
|
|
|
|
|
healCost: number;
|
|
|
|
|
onClose: () => void;
|
|
|
|
|
onQuestsChanged: () => void;
|
|
|
|
|
onHeroUpdated: (hero: HeroResponse) => void;
|
|
|
|
|
@ -228,17 +230,14 @@ function questTypeIcon(type: string): string {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---- Constants ----
|
|
|
|
|
|
|
|
|
|
const POTION_COST = 50;
|
|
|
|
|
const HEAL_COST = 30;
|
|
|
|
|
|
|
|
|
|
// ---- Component ----
|
|
|
|
|
|
|
|
|
|
export function NPCDialog({
|
|
|
|
|
npc,
|
|
|
|
|
heroQuests,
|
|
|
|
|
heroGold,
|
|
|
|
|
potionCost,
|
|
|
|
|
healCost,
|
|
|
|
|
onClose,
|
|
|
|
|
onQuestsChanged,
|
|
|
|
|
onHeroUpdated,
|
|
|
|
|
@ -308,24 +307,24 @@ export function NPCDialog({
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const handleBuyPotion = useCallback(() => {
|
|
|
|
|
if (heroGold < POTION_COST) {
|
|
|
|
|
if (heroGold < potionCost) {
|
|
|
|
|
onToast(tr.notEnoughGold, '#ff4444');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
buyPotion(telegramId)
|
|
|
|
|
.then((hero) => {
|
|
|
|
|
hapticImpact('medium');
|
|
|
|
|
onToast(t(tr.boughtPotion, { cost: POTION_COST }), '#88dd88');
|
|
|
|
|
onToast(t(tr.boughtPotion, { cost: potionCost }), '#88dd88');
|
|
|
|
|
onHeroUpdated(hero);
|
|
|
|
|
})
|
|
|
|
|
.catch((err) => {
|
|
|
|
|
console.warn('[NPCDialog] Failed to buy potion:', err);
|
|
|
|
|
onToast(tr.failedToBuyPotion, '#ff4444');
|
|
|
|
|
});
|
|
|
|
|
}, [telegramId, heroGold, onHeroUpdated, onToast]);
|
|
|
|
|
}, [telegramId, heroGold, potionCost, onHeroUpdated, onToast, tr]);
|
|
|
|
|
|
|
|
|
|
const handleHeal = useCallback(() => {
|
|
|
|
|
if (heroGold < HEAL_COST) {
|
|
|
|
|
if (heroGold < healCost) {
|
|
|
|
|
onToast(tr.notEnoughGold, '#ff4444');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
@ -339,7 +338,7 @@ export function NPCDialog({
|
|
|
|
|
console.warn('[NPCDialog] Failed to heal:', err);
|
|
|
|
|
onToast(tr.failedToHeal, '#ff4444');
|
|
|
|
|
});
|
|
|
|
|
}, [telegramId, heroGold, onHeroUpdated, onToast, npc.id]);
|
|
|
|
|
}, [telegramId, heroGold, healCost, onHeroUpdated, onToast, npc.id, tr]);
|
|
|
|
|
|
|
|
|
|
// Quests relevant to this NPC
|
|
|
|
|
const npcHeroQuests = heroQuests.filter(
|
|
|
|
|
@ -522,14 +521,14 @@ export function NPCDialog({
|
|
|
|
|
<div style={sectionTitleStyle}>Shop</div>
|
|
|
|
|
<button
|
|
|
|
|
style={
|
|
|
|
|
heroGold >= POTION_COST
|
|
|
|
|
heroGold >= potionCost
|
|
|
|
|
? { ...serviceBtnStyle, backgroundColor: 'rgba(68, 200, 68, 0.2)', color: '#88dd88' }
|
|
|
|
|
: { ...disabledBtnStyle, backgroundColor: 'rgba(68, 200, 68, 0.1)', color: '#88dd88' }
|
|
|
|
|
}
|
|
|
|
|
onClick={handleBuyPotion}
|
|
|
|
|
disabled={heroGold < POTION_COST}
|
|
|
|
|
disabled={heroGold < potionCost}
|
|
|
|
|
>
|
|
|
|
|
{'\uD83E\uDDEA'} {tr.buyPotion} — {POTION_COST} {tr.gold}
|
|
|
|
|
{'\uD83E\uDDEA'} {tr.buyPotion} — {potionCost} {tr.gold}
|
|
|
|
|
</button>
|
|
|
|
|
<div style={{ fontSize: 11, color: '#666', textAlign: 'center' }}>
|
|
|
|
|
Your gold: {heroGold}
|
|
|
|
|
@ -543,14 +542,14 @@ export function NPCDialog({
|
|
|
|
|
<div style={sectionTitleStyle}>Services</div>
|
|
|
|
|
<button
|
|
|
|
|
style={
|
|
|
|
|
heroGold >= HEAL_COST
|
|
|
|
|
heroGold >= healCost
|
|
|
|
|
? { ...serviceBtnStyle, backgroundColor: 'rgba(200, 68, 68, 0.2)', color: '#ff8888' }
|
|
|
|
|
: { ...disabledBtnStyle, backgroundColor: 'rgba(200, 68, 68, 0.1)', color: '#ff8888' }
|
|
|
|
|
}
|
|
|
|
|
onClick={handleHeal}
|
|
|
|
|
disabled={heroGold < HEAL_COST}
|
|
|
|
|
disabled={heroGold < healCost}
|
|
|
|
|
>
|
|
|
|
|
{'\u2764\uFE0F'} {tr.healToFull} — {HEAL_COST} {tr.gold}
|
|
|
|
|
{'\u2764\uFE0F'} {tr.healToFull} — {healCost} {tr.gold}
|
|
|
|
|
</button>
|
|
|
|
|
<div style={{ fontSize: 11, color: '#666', textAlign: 'center' }}>
|
|
|
|
|
Your gold: {heroGold}
|
|
|
|
|
|