minor fixes

master
Denis Ranneft 1 month ago
parent 945458d345
commit ab9486cb83

@ -1173,6 +1173,9 @@ export function App() {
const dismissToast = useCallback(() => setToast(null), []);
const questClaimDisabled =
gameState.phase === GamePhase.Dead || (gameState.hero?.hp ?? 0) <= 0;
return (
<I18nContext.Provider value={{ tr: translations, locale, setLocale: handleSetLocale }}>
<div style={appStyle}>
@ -1205,6 +1208,7 @@ export function App() {
quests={heroQuests}
onQuestClaim={handleQuestClaim}
onQuestAbandon={handleQuestAbandon}
questClaimDisabled={questClaimDisabled}
/>
)}
@ -1226,11 +1230,8 @@ export function App() {
<DeathScreen
visible={gameState.phase === GamePhase.Dead}
onRevive={handleRevive}
revivesRemaining={
gameState.hero?.subscriptionActive
? undefined
: Math.max(0, 2 - (gameState.hero?.reviveCount ?? 0))
}
subscriptionUnlimited={!!gameState.hero?.subscriptionActive}
revivesRemaining={Math.max(0, 2 - (gameState.hero?.reviveCount ?? 0))}
/>
{/* Toast Notification */}
@ -1292,6 +1293,7 @@ export function App() {
onQuestsChanged={refreshHeroQuests}
onHeroUpdated={handleNPCHeroUpdated}
onToast={(message, color) => setToast({ message, color })}
questClaimDisabled={questClaimDisabled}
/>
)}

@ -93,6 +93,7 @@ export const en = {
questLog: 'Quest Log',
noActiveQuests: 'No active quests. Visit an NPC to accept quests!',
claimRewards: 'Claim Rewards',
claimRewardsDisabledDead: 'Revive to claim quest rewards',
questDestination: 'Destination',
abandon: 'Abandon',
acceptQuest: 'Accept',
@ -127,6 +128,8 @@ export const en = {
youDied: 'YOU DIED',
reviveNow: 'REVIVE NOW',
freeRevivesLeft: 'Free revives left: {count}',
revivesUnlimitedSubscription: 'Unlimited revives (subscription)',
reviveNowWithCount: 'REVIVE NOW ({count})',
autoReviveIn: 'Auto-revive in {timer}s',
noFreeRevives: 'No free revives left \u2014 subscription required',

@ -95,6 +95,8 @@ export const ru: Translations = {
questLog: '\u0416\u0443\u0440\u043d\u0430\u043b \u0437\u0430\u0434\u0430\u043d\u0438\u0439',
noActiveQuests: '\u041d\u0435\u0442 \u0430\u043a\u0442\u0438\u0432\u043d\u044b\u0445 \u0437\u0430\u0434\u0430\u043d\u0438\u0439. \u041f\u043e\u0433\u043e\u0432\u043e\u0440\u0438\u0442\u0435 \u0441 NPC!',
claimRewards: '\u041f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043d\u0430\u0433\u0440\u0430\u0434\u0443',
claimRewardsDisabledDead:
'\u0412\u043e\u0441\u043a\u0440\u0435\u0441\u043d\u0438\u0442\u0435, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043d\u0430\u0433\u0440\u0430\u0434\u044b \u0437\u0430 \u0437\u0430\u0434\u0430\u043d\u0438\u044f',
questDestination: '\u041f\u0443\u043d\u043a\u0442 \u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f',
abandon: '\u041e\u0442\u043a\u0430\u0437\u0430\u0442\u044c\u0441\u044f',
acceptQuest: '\u041f\u0440\u0438\u043d\u044f\u0442\u044c',
@ -129,6 +131,8 @@ export const ru: Translations = {
youDied: '\u0412\u042b \u041f\u041e\u0413\u0418\u0411\u041b\u0418',
reviveNow: '\u0412\u041e\u0421\u041a\u0420\u0415\u0421\u0418\u0422\u042c',
freeRevivesLeft: '\u0411\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u044b\u0445 \u0432\u043e\u0441\u043a\u0440\u0435\u0448\u0435\u043d\u0438\u0439: {count}',
revivesUnlimitedSubscription: '\u0411\u0435\u0437\u043b\u0438\u043c\u0438\u0442\u043d\u044b\u0435 \u0432\u043e\u0441\u043a\u0440\u0435\u0448\u0435\u043d\u0438\u044f (\u043f\u043e\u0434\u043f\u0438\u0441\u043a\u0430)',
reviveNowWithCount: '\u0412\u041e\u0421\u041a\u0420\u0415\u0421\u0418\u0422\u042c ({count})',
autoReviveIn: '\u0410\u0432\u0442\u043e-\u0432\u043e\u0441\u043a\u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0447\u0435\u0440\u0435\u0437 {timer}\u0441',
noFreeRevives: '\u041d\u0435\u0442 \u0431\u0435\u0441\u043f\u043b\u0430\u0442\u043d\u044b\u0445 \u0432\u043e\u0441\u043a\u0440\u0435\u0448\u0435\u043d\u0438\u0439 \u2014 \u043d\u0443\u0436\u043d\u0430 \u043f\u043e\u0434\u043f\u0438\u0441\u043a\u0430',

@ -5,8 +5,10 @@ import { useT, t } from '../i18n';
interface DeathScreenProps {
visible: boolean;
onRevive: () => void;
/** Free revives left for non-subscribers; omit when subscribed / unlimited. */
revivesRemaining?: number;
/** Active subscription: manual revives are not limited by the free quota. */
subscriptionUnlimited?: boolean;
/** Free manual revives left (02) when not subscriptionUnlimited. */
revivesRemaining: number;
}
/** Full-screen dimming; `pointerEvents: 'none'` so HUD controls (e.g. hero sheet) stay clickable. */
@ -62,10 +64,15 @@ const buttonStyle: CSSProperties = {
transition: 'background-color 0.2s',
};
export function DeathScreen({ visible, onRevive, revivesRemaining }: DeathScreenProps) {
export function DeathScreen({
visible,
onRevive,
subscriptionUnlimited,
revivesRemaining,
}: DeathScreenProps) {
const tr = useT();
const [timer, setTimer] = useState(REVIVE_TIMER_SECONDS);
const canRevive = revivesRemaining === undefined || revivesRemaining > 0;
const canRevive = !!subscriptionUnlimited || revivesRemaining > 0;
// Countdown timer
useEffect(() => {
@ -101,11 +108,11 @@ export function DeathScreen({ visible, onRevive, revivesRemaining }: DeathScreen
<div style={deathPanelStyle}>
<div style={titleStyle}>{tr.youDied}</div>
<div style={timerStyle}>{canRevive ? timer : '—'}</div>
{revivesRemaining !== undefined && (
<div style={{ color: '#aaa', fontSize: 14 }}>
{t(tr.freeRevivesLeft, { count: Math.max(0, revivesRemaining) })}
{subscriptionUnlimited
? tr.revivesUnlimitedSubscription
: t(tr.freeRevivesLeft, { count: Math.max(0, revivesRemaining) })}
</div>
)}
<button
style={{
...buttonStyle,
@ -122,7 +129,9 @@ export function DeathScreen({ visible, onRevive, revivesRemaining }: DeathScreen
e.currentTarget.style.backgroundColor = '#cc3333';
}}
>
{tr.reviveNow}
{subscriptionUnlimited
? tr.reviveNow
: t(tr.reviveNowWithCount, { count: Math.max(0, revivesRemaining) })}
</button>
<div style={{ color: '#888', fontSize: 13 }}>
{canRevive ? t(tr.autoReviveIn, { timer }) : tr.noFreeRevives}

@ -124,6 +124,8 @@ interface HeroSheetModalProps {
quests: HeroQuest[];
onQuestClaim: (heroQuestId: number) => void;
onQuestAbandon: (heroQuestId: number) => void;
/** Disable claim while hero is dead (HP 0 / death phase). */
questClaimDisabled?: boolean;
}
export function HeroSheetModal({
@ -137,6 +139,7 @@ export function HeroSheetModal({
quests,
onQuestClaim,
onQuestAbandon,
questClaimDisabled = false,
}: HeroSheetModalProps) {
const [tab, setTab] = useState<HeroSheetTab>(initialTab);
const tr = useT();
@ -207,6 +210,7 @@ export function HeroSheetModal({
quests={quests}
onClaim={onQuestClaim}
onAbandon={onQuestAbandon}
claimDisabled={questClaimDisabled}
/>
)}
{tab === 'settings' && (

@ -18,6 +18,8 @@ interface NPCDialogProps {
onQuestsChanged: () => void;
onHeroUpdated: (hero: HeroResponse) => void;
onToast: (message: string, color: string) => void;
/** Block quest reward claim while hero is dead. */
questClaimDisabled?: boolean;
}
// ---- Styles ----
@ -242,6 +244,7 @@ export function NPCDialog({
onQuestsChanged,
onHeroUpdated,
onToast,
questClaimDisabled = false,
}: NPCDialogProps) {
const tr = useT();
const [availableQuests, setAvailableQuests] = useState<Quest[]>([]);
@ -419,8 +422,14 @@ export function NPCDialog({
)}
</div>
<button
style={claimBtnStyle}
onClick={() => handleClaimQuest(hq.id)}
type="button"
style={questClaimDisabled ? { ...claimBtnStyle, opacity: 0.45, cursor: 'not-allowed', animation: 'none' } : claimBtnStyle}
disabled={questClaimDisabled}
title={questClaimDisabled ? tr.claimRewardsDisabledDead : undefined}
onClick={() => {
if (questClaimDisabled) return;
handleClaimQuest(hq.id);
}}
>
{tr.claimRewards}
</button>

@ -9,6 +9,7 @@ interface QuestLogProps {
onClaim: (heroQuestId: number) => void;
onAbandon: (heroQuestId: number) => void;
onClose: () => void;
claimDisabled?: boolean;
}
// ---- Quest Type Icons ----
@ -167,6 +168,15 @@ const claimBtnStyle: CSSProperties = {
animation: 'quest-claim-glow 1.5s ease-in-out infinite',
};
const claimBtnDisabledStyle: CSSProperties = {
...claimBtnStyle,
opacity: 0.45,
cursor: 'not-allowed',
animation: 'none',
boxShadow: 'none',
textShadow: 'none',
};
const abandonBtnStyle: CSSProperties = {
padding: '8px 14px',
border: '1px solid rgba(255, 80, 80, 0.3)',
@ -189,10 +199,12 @@ interface QuestLogListProps {
quests: HeroQuest[];
onClaim: (heroQuestId: number) => void;
onAbandon: (heroQuestId: number) => void;
/** When true, completed-quest claim is blocked (e.g. hero dead). */
claimDisabled?: boolean;
}
/** Quest list body (embedded in Hero sheet or standalone panel). */
export function QuestLogList({ quests, onClaim, onAbandon }: QuestLogListProps) {
export function QuestLogList({ quests, onClaim, onAbandon, claimDisabled = false }: QuestLogListProps) {
const tr = useT();
const [expandedId, setExpandedId] = useState<number | null>(null);
@ -294,9 +306,13 @@ export function QuestLogList({ quests, onClaim, onAbandon }: QuestLogListProps)
<div style={actionRow}>
{isCompleted ? (
<button
style={claimBtnStyle}
type="button"
style={claimDisabled ? claimBtnDisabledStyle : claimBtnStyle}
disabled={claimDisabled}
title={claimDisabled ? tr.claimRewardsDisabledDead : undefined}
onClick={(e) => {
e.stopPropagation();
if (claimDisabled) return;
onClaim(q.id);
}}
>
@ -325,7 +341,7 @@ export function QuestLogList({ quests, onClaim, onAbandon }: QuestLogListProps)
);
}
export function QuestLog({ quests, onClaim, onAbandon, onClose }: QuestLogProps) {
export function QuestLog({ quests, onClaim, onAbandon, onClose, claimDisabled }: QuestLogProps) {
const tr = useT();
useEffect(() => {
const handleKey = (e: KeyboardEvent) => {
@ -350,7 +366,7 @@ export function QuestLog({ quests, onClaim, onAbandon, onClose }: QuestLogProps)
<span style={titleStyle}>{'\uD83D\uDCDC'} {tr.questLog}</span>
<button style={closeBtnStyle} onClick={onClose}>{'\u2715'}</button>
</div>
<QuestLogList quests={quests} onClaim={onClaim} onAbandon={onAbandon} />
<QuestLogList quests={quests} onClaim={onClaim} onAbandon={onAbandon} claimDisabled={claimDisabled} />
</div>
</div>
</>

Loading…
Cancel
Save