статистика на экране смерти
This commit is contained in:
+10
@@ -376,6 +376,16 @@
|
|||||||
padding: 0 20px;
|
padding: 0 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.game-over-overlay__stats {
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--text);
|
||||||
|
text-align: center;
|
||||||
|
line-height: 1.8;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
@keyframes chop-shake {
|
@keyframes chop-shake {
|
||||||
0%, 100% {
|
0%, 100% {
|
||||||
transform: translate(0, 0) rotate(0deg);
|
transform: translate(0, 0) rotate(0deg);
|
||||||
|
|||||||
@@ -56,6 +56,12 @@ const GameOverOverlay = observer(function GameOverOverlay({
|
|||||||
<div className="game-over-overlay__message">
|
<div className="game-over-overlay__message">
|
||||||
Вы прожили {store.day} {dayWord(store.day)}, но вас поймали кролики
|
Вы прожили {store.day} {dayWord(store.day)}, но вас поймали кролики
|
||||||
</div>
|
</div>
|
||||||
|
<div className="game-over-overlay__stats">
|
||||||
|
<div>Пройдено: {Math.round(store.character.distanceTraveled)} px</div>
|
||||||
|
<div>Срублено: {store.treesChoppedCount} деревьев, {store.bushesChoppedCount} кустов</div>
|
||||||
|
<div>Поймано кроликов: {store.rabbitsCaughtCount}</div>
|
||||||
|
<div>Заработано монет: {store.totalCoinsEarned.toFixed(1)}</div>
|
||||||
|
</div>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="menu-btn"
|
className="menu-btn"
|
||||||
|
|||||||
+30
-1
@@ -69,6 +69,15 @@ export class GameStore {
|
|||||||
bloodMoonBannerTimer = 0;
|
bloodMoonBannerTimer = 0;
|
||||||
upgradeLevels: Record<UpgradeId, number> = { ...DEFAULT_UPGRADE_LEVELS };
|
upgradeLevels: Record<UpgradeId, number> = { ...DEFAULT_UPGRADE_LEVELS };
|
||||||
|
|
||||||
|
// This life's run-summary stats, shown on the death screen — reset to 0 on a fresh game,
|
||||||
|
// restored from the save when continuing one (see initialize()).
|
||||||
|
treesChoppedCount = 0;
|
||||||
|
bushesChoppedCount = 0;
|
||||||
|
rabbitsCaughtCount = 0;
|
||||||
|
/** Cumulative coins earned from converting resources — unlike inventory.coins, this never goes
|
||||||
|
* down when coins are spent on upgrades. */
|
||||||
|
totalCoinsEarned = 0;
|
||||||
|
|
||||||
private coinProgress = 0;
|
private coinProgress = 0;
|
||||||
private saveProgress = 0;
|
private saveProgress = 0;
|
||||||
private lastTimestamp: number | null = null;
|
private lastTimestamp: number | null = null;
|
||||||
@@ -257,7 +266,7 @@ export class GameStore {
|
|||||||
this.upgradeLevels = { ...DEFAULT_UPGRADE_LEVELS, ...save?.upgradeLevels };
|
this.upgradeLevels = { ...DEFAULT_UPGRADE_LEVELS, ...save?.upgradeLevels };
|
||||||
|
|
||||||
if (save?.character && save.nodes && save.resourceDrops) {
|
if (save?.character && save.nodes && save.resourceDrops) {
|
||||||
this.character = new Character(save.character.x, save.character.y, save.characterHp ?? this.characterMaxHp);
|
this.character = new Character(save.character.x, save.character.y, save.characterHp ?? this.characterMaxHp, save.distanceTraveled ?? 0);
|
||||||
this.nodes = save.nodes.map((node) => ResourceNode.restore(node));
|
this.nodes = save.nodes.map((node) => ResourceNode.restore(node));
|
||||||
this.resourceDrops = save.resourceDrops.map((drop) => ResourceDrop.restore(drop));
|
this.resourceDrops = save.resourceDrops.map((drop) => ResourceDrop.restore(drop));
|
||||||
} else {
|
} else {
|
||||||
@@ -266,6 +275,13 @@ export class GameStore {
|
|||||||
this.resourceDrops = [];
|
this.resourceDrops = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This life's run-summary stats — restored from the save when continuing a life, reset to 0
|
||||||
|
// for a fresh one (matching the character branch above, since both represent the same life).
|
||||||
|
this.treesChoppedCount = save?.treesChoppedCount ?? 0;
|
||||||
|
this.bushesChoppedCount = save?.bushesChoppedCount ?? 0;
|
||||||
|
this.rabbitsCaughtCount = save?.rabbitsCaughtCount ?? 0;
|
||||||
|
this.totalCoinsEarned = save?.totalCoinsEarned ?? 0;
|
||||||
|
|
||||||
// Rabbits are restored/spawned independently of the world-state branch above so a save from
|
// Rabbits are restored/spawned independently of the world-state branch above so a save from
|
||||||
// before rabbits existed still loads fine — it just gets a freshly spawned batch of them.
|
// before rabbits existed still loads fine — it just gets a freshly spawned batch of them.
|
||||||
this.rabbits = save?.rabbits
|
this.rabbits = save?.rabbits
|
||||||
@@ -312,6 +328,11 @@ export class GameStore {
|
|||||||
day: this.day,
|
day: this.day,
|
||||||
hour: this.hour,
|
hour: this.hour,
|
||||||
characterHp: this.character.hp,
|
characterHp: this.character.hp,
|
||||||
|
distanceTraveled: this.character.distanceTraveled,
|
||||||
|
treesChoppedCount: this.treesChoppedCount,
|
||||||
|
bushesChoppedCount: this.bushesChoppedCount,
|
||||||
|
rabbitsCaughtCount: this.rabbitsCaughtCount,
|
||||||
|
totalCoinsEarned: this.totalCoinsEarned,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -585,6 +606,10 @@ export class GameStore {
|
|||||||
this.nodes = this.nodes.filter((n) => n.id !== node.id);
|
this.nodes = this.nodes.filter((n) => n.id !== node.id);
|
||||||
this.heldNodeId = null;
|
this.heldNodeId = null;
|
||||||
this.character.resetChopProgress();
|
this.character.resetChopProgress();
|
||||||
|
if (node.kind === 'tree')
|
||||||
|
this.treesChoppedCount += 1;
|
||||||
|
else
|
||||||
|
this.bushesChoppedCount += 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -631,6 +656,7 @@ export class GameStore {
|
|||||||
this.heldRabbitId = null;
|
this.heldRabbitId = null;
|
||||||
this.character.resetAttackProgress();
|
this.character.resetAttackProgress();
|
||||||
}
|
}
|
||||||
|
this.rabbitsCaughtCount += 1;
|
||||||
this.ejectDrop(rabbit, 'meat');
|
this.ejectDrop(rabbit, 'meat');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -697,14 +723,17 @@ export class GameStore {
|
|||||||
if (this.inventory.logs > 0) {
|
if (this.inventory.logs > 0) {
|
||||||
this.inventory.logs -= 1;
|
this.inventory.logs -= 1;
|
||||||
this.inventory.coins += this.coinsPerLog;
|
this.inventory.coins += this.coinsPerLog;
|
||||||
|
this.totalCoinsEarned += this.coinsPerLog;
|
||||||
}
|
}
|
||||||
if (this.inventory.twigs > 0) {
|
if (this.inventory.twigs > 0) {
|
||||||
this.inventory.twigs -= 1;
|
this.inventory.twigs -= 1;
|
||||||
this.inventory.coins += this.coinsPerTwig;
|
this.inventory.coins += this.coinsPerTwig;
|
||||||
|
this.totalCoinsEarned += this.coinsPerTwig;
|
||||||
}
|
}
|
||||||
if (this.inventory.meat > 0) {
|
if (this.inventory.meat > 0) {
|
||||||
this.inventory.meat -= 1;
|
this.inventory.meat -= 1;
|
||||||
this.inventory.coins += this.coinsPerMeat;
|
this.inventory.coins += this.coinsPerMeat;
|
||||||
|
this.totalCoinsEarned += this.coinsPerMeat;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,15 +9,18 @@ export class Character {
|
|||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
hp: number;
|
hp: number;
|
||||||
|
/** Total px moved so far this life — an odometer for the death-screen run summary. */
|
||||||
|
distanceTraveled: number;
|
||||||
|
|
||||||
private chopProgress = 0;
|
private chopProgress = 0;
|
||||||
private attackProgress = 0;
|
private attackProgress = 0;
|
||||||
private regenProgress = 0;
|
private regenProgress = 0;
|
||||||
|
|
||||||
constructor(x: number, y: number, hp: number) {
|
constructor(x: number, y: number, hp: number, distanceTraveled = 0) {
|
||||||
this.x = x;
|
this.x = x;
|
||||||
this.y = y;
|
this.y = y;
|
||||||
this.hp = hp;
|
this.hp = hp;
|
||||||
|
this.distanceTraveled = distanceTraveled;
|
||||||
makeAutoObservable(this, {}, { autoBind: true });
|
makeAutoObservable(this, {}, { autoBind: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,6 +39,7 @@ export class Character {
|
|||||||
const ratio = step / distance;
|
const ratio = step / distance;
|
||||||
this.x += dx * ratio;
|
this.x += dx * ratio;
|
||||||
this.y += dy * ratio;
|
this.y += dy * ratio;
|
||||||
|
this.distanceTraveled += step;
|
||||||
return distance - step;
|
return distance - step;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,15 @@ export interface SaveData {
|
|||||||
hour?: number;
|
hour?: number;
|
||||||
/** Absent on saves from before the character had hp, which default to a full characterMaxHp. */
|
/** Absent on saves from before the character had hp, which default to a full characterMaxHp. */
|
||||||
characterHp?: number;
|
characterHp?: number;
|
||||||
|
/** This life's run-summary stats (shown on the death screen) — absent on older saves or a
|
||||||
|
* fresh game, which default to 0. */
|
||||||
|
distanceTraveled?: number;
|
||||||
|
treesChoppedCount?: number;
|
||||||
|
bushesChoppedCount?: number;
|
||||||
|
rabbitsCaughtCount?: number;
|
||||||
|
/** Cumulative coins earned from converting resources — unlike inventory.coins, this never goes
|
||||||
|
* down when coins are spent on upgrades. */
|
||||||
|
totalCoinsEarned?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
function isValidSave(value: unknown): value is SaveData {
|
function isValidSave(value: unknown): value is SaveData {
|
||||||
|
|||||||
Reference in New Issue
Block a user