bugfixes, offline mode

This commit is contained in:
azykov@mail.ru 2026-02-07 21:27:29 +03:00
parent 9af9c7867a
commit 9f544a32ab
10 changed files with 71 additions and 74 deletions

View File

@ -74,6 +74,8 @@
.school {
grid-area: school;
padding: 1rem;
overflow-y: auto;
}
.unit {

View File

@ -7,6 +7,8 @@ import { DebugView } from './views/DebugView'
export const App = observer(function () {
console.dir(performance.now());
return (
<div className="App">
<StatsView />

View File

@ -37,6 +37,7 @@ export class Calendar {
public static deserialize(data: CalendarDto): Calendar {
const calendar = new Calendar();
calendar.day = data.day;
calendar.progress = Progress.deserialize(data.progress, true, calendar.handleProgress.bind(calendar));
return calendar;

View File

@ -10,6 +10,10 @@ export class Game {
makeAutoObservable(this);
}
public restart() {
this.school = new School();
}
public save(): void {
const data = this.school.serialize();
localStorage.setItem('save', JSON.stringify(data))

View File

@ -6,7 +6,7 @@ import { Upgrades } from './upgrades';
import { PartialResourceSet, ResourceSet } from '../types/resources';
import { addResourceSets, fullResourceSet, multiplyResourceSet, roundResourceSet, subtractResourceSets } from '../utils/resources';
import { v7 as uuid } from 'uuid';
import { GameClock } from '../utils/gameClock';
import { GameClock, nowTime } from '../utils/gameClock';
import { Calendar, CalendarDto } from './calendar';
import { getRandomHumanName } from '../utils/humanNames';
import { Time as Time } from './time';
@ -50,12 +50,10 @@ export class School {
}
public tick(time: number) {
const scaledTime = time;
this.calendar.progress.tick(scaledTime);
this.calendar.progress.tick(time);
for (let unit of this.units)
unit.progress.tick(scaledTime);
unit.progress.tick(time);
this.tps = GameClock.tps;
}

View File

@ -18,10 +18,10 @@ export const upgrades: UpgradePrefab[] = [
{
id: 'moreStudents',
name: "Больше учеников",
description: (level) => `Увеличить количество учеников с ${level} до ${level + 1}`,
description: (level) => `Увеличить количество учеников с ${level + 1} до ${level + 2}`,
costToView: (level) => fullResourceSet(),
costToOpen: (level) => ({ reputation: 20 * (level / 10 + 1) }),
costToBuy: (level) => ({ gold: 10 }),
costToBuy: (level) => ({ gold: 10 * (level + 1) }),
execute: (level) => {
game.school.increaseStudentCount();
},

View File

@ -1,6 +1,10 @@
import { game } from "../model/game";
import { ExternalSignal } from "./mobx/externalSignal";
export function nowTime() {
return performance.timeOrigin + performance.now();
}
export class GameClock {
public static lastTime = 0;
@ -9,27 +13,18 @@ export class GameClock {
public static readonly pausedSignal = new ExternalSignal('GameClock.paused');
// public static async start(): Promise<void> {
// let lastTime = performance.now();
// setInterval(() => {
// let now = performance.now();
// let delta = now - lastTime;
// school.tick(delta / 1000);
// lastTime = now;
// }, 33);
// }
public static start(): void {
const loop = (now: number) => {
this.lastTime = nowTime();
const loop = () => {
if (GameClock.paused)
return;
let now = nowTime();
let delta = now - GameClock.lastTime;
GameClock.lastTime = now;
if (GameClock.paused)
delta *= 0;
GameClock.frames.push(now);
while (GameClock.frames[0] < now - 1000)
GameClock.frames.shift();
@ -63,12 +58,20 @@ export class GameClock {
}
public static pause() {
if (GameClock.paused)
return;
GameClock.paused = true;
GameClock.pausedSignal.trigger();
}
public static resume() {
if (!GameClock.paused)
return;
GameClock.paused = false;
GameClock.pausedSignal.trigger();
GameClock.start();
}
}

View File

@ -1,22 +0,0 @@
import { observer } from "mobx-react-lite";
import { school } from "../model/school";
import { Student } from "../model/student";
import { Owner } from "../model/owner";
import { OwnerUnitView } from "./OwnerUnitView";
import { IUnit } from "../types/unit";
export const UnitListView = observer(function () {
function renderUnit(unit: IUnit) {
if (unit instanceof Owner)
return <OwnerUnitView unit={unit} />
// // else if (unit instanceof Student)
// // return StudentView({ unit });
else
return '???';
}
return <ul>{
school.units.map((unit) => <li className="unit" key={unit.id}>{renderUnit(unit)}</li>)
}</ul>
});

View File

@ -7,6 +7,10 @@ export const DebugView = observer(function () {
const [skipTime, setSkipTime] = useState<number>(5);
const isDebugMode = location.hostname === "localhost" ||
location.hostname === "127.0.0.1" ||
location.hostname === "[::1]";
function handleTimeScaleChange(event: ChangeEvent<HTMLInputElement>): void {
game.school.time.setTimeScale(Number(event.target.value));
}
@ -15,6 +19,10 @@ export const DebugView = observer(function () {
GameClock.skipTime(skipTime);
}
function handleRestartClick(): void {
game.restart();
}
function handleSaveClick(): void {
game.save();
}
@ -22,6 +30,7 @@ export const DebugView = observer(function () {
function handleLoadClick(): void {
game.school.time.pause();
game.load();
game.school.time.resume();
}
function handleTickClick(): void {
@ -32,7 +41,9 @@ export const DebugView = observer(function () {
return <div className="debug">
<div>
Время
<div className="timeScale">
{
isDebugMode
? <div className="timeScale">
Масштаб
<input
type="range"
@ -45,18 +56,25 @@ export const DebugView = observer(function () {
/>
x{game.school.time.timeScale}
</div>
: <></>
}
{
game.school.time.paused
? <button onClick={() => game.school.time.resume()}>|&gt;</button>
: <button onClick={() => game.school.time.pause()}>||</button>
}
<button onClick={handleSkipTime}>
{
isDebugMode
? <button onClick={handleSkipTime}>
<input type="number" value={skipTime} onChange={(e) => setSkipTime(Number(e.target.value))}></input>
Пропустить
</button>
: <></>
}
<button onClick={handleTickClick}>Tick</button>
</div>
<div>
<button onClick={handleRestartClick}>Restart</button>
<button onClick={handleSaveClick}>Save</button>
<button onClick={handleLoadClick}>Load</button>
</div>

View File

@ -8,15 +8,6 @@ import { game } from "../model/game";
export const UnitListView = observer(function () {
function renderUnit(unit: IUnit) {
if (unit instanceof Owner)
return <OwnerUnitView unit={unit} />
else if (unit instanceof Student)
return <StudentUnitView unit={unit} />
else
return '???';
}
return <div className="units">
<div className="owner">
<OwnerUnitView unit={game.school.owner} />