Fix buildings not working at their advertised speed, closes #440, closes #442, closes #437, closes #449
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 1.3 MiB |
Before Width: | Height: | Size: 272 KiB After Width: | Height: | Size: 277 KiB |
Before Width: | Height: | Size: 660 KiB After Width: | Height: | Size: 658 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 4.5 KiB |
@ -1,58 +1,58 @@
|
|||||||
import { types } from "../../savegame/serialization";
|
import { types } from "../../savegame/serialization";
|
||||||
import { BaseItem } from "../base_item";
|
import { BaseItem } from "../base_item";
|
||||||
import { Component } from "../component";
|
import { Component } from "../component";
|
||||||
import { typeItemSingleton } from "../item_resolver";
|
import { typeItemSingleton } from "../item_resolver";
|
||||||
|
|
||||||
const chainBufferSize = 3;
|
const chainBufferSize = 6;
|
||||||
|
|
||||||
export class MinerComponent extends Component {
|
export class MinerComponent extends Component {
|
||||||
static getId() {
|
static getId() {
|
||||||
return "Miner";
|
return "Miner";
|
||||||
}
|
}
|
||||||
|
|
||||||
static getSchema() {
|
static getSchema() {
|
||||||
// cachedMinedItem is not serialized.
|
// cachedMinedItem is not serialized.
|
||||||
return {
|
return {
|
||||||
lastMiningTime: types.ufloat,
|
lastMiningTime: types.ufloat,
|
||||||
itemChainBuffer: types.array(typeItemSingleton),
|
itemChainBuffer: types.array(typeItemSingleton),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
duplicateWithoutContents() {
|
duplicateWithoutContents() {
|
||||||
return new MinerComponent({
|
return new MinerComponent({
|
||||||
chainable: this.chainable,
|
chainable: this.chainable,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor({ chainable = false }) {
|
constructor({ chainable = false }) {
|
||||||
super();
|
super();
|
||||||
this.lastMiningTime = 0;
|
this.lastMiningTime = 0;
|
||||||
this.chainable = chainable;
|
this.chainable = chainable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores items from other miners which were chained to this
|
* Stores items from other miners which were chained to this
|
||||||
* miner.
|
* miner.
|
||||||
* @type {Array<BaseItem>}
|
* @type {Array<BaseItem>}
|
||||||
*/
|
*/
|
||||||
this.itemChainBuffer = [];
|
this.itemChainBuffer = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {BaseItem}
|
* @type {BaseItem}
|
||||||
*/
|
*/
|
||||||
this.cachedMinedItem = null;
|
this.cachedMinedItem = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {BaseItem} item
|
* @param {BaseItem} item
|
||||||
*/
|
*/
|
||||||
tryAcceptChainedItem(item) {
|
tryAcceptChainedItem(item) {
|
||||||
if (this.itemChainBuffer.length > chainBufferSize) {
|
if (this.itemChainBuffer.length > chainBufferSize) {
|
||||||
// Well, this one is full
|
// Well, this one is full
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.itemChainBuffer.push(item);
|
this.itemChainBuffer.push(item);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,140 +1,144 @@
|
|||||||
import { globalConfig } from "../../core/config";
|
import { globalConfig } from "../../core/config";
|
||||||
import { DrawParameters } from "../../core/draw_parameters";
|
import { DrawParameters } from "../../core/draw_parameters";
|
||||||
import { enumDirectionToVector } from "../../core/vector";
|
import { enumDirectionToVector } from "../../core/vector";
|
||||||
import { BaseItem } from "../base_item";
|
import { BaseItem } from "../base_item";
|
||||||
import { MinerComponent } from "../components/miner";
|
import { MinerComponent } from "../components/miner";
|
||||||
import { Entity } from "../entity";
|
import { Entity } from "../entity";
|
||||||
import { GameSystemWithFilter } from "../game_system_with_filter";
|
import { GameSystemWithFilter } from "../game_system_with_filter";
|
||||||
import { MapChunkView } from "../map_chunk_view";
|
import { MapChunkView } from "../map_chunk_view";
|
||||||
|
|
||||||
export class MinerSystem extends GameSystemWithFilter {
|
export class MinerSystem extends GameSystemWithFilter {
|
||||||
constructor(root) {
|
constructor(root) {
|
||||||
super(root, [MinerComponent]);
|
super(root, [MinerComponent]);
|
||||||
}
|
}
|
||||||
|
|
||||||
update() {
|
update() {
|
||||||
let miningSpeed = this.root.hubGoals.getMinerBaseSpeed();
|
let miningSpeed = this.root.hubGoals.getMinerBaseSpeed();
|
||||||
if (G_IS_DEV && globalConfig.debug.instantMiners) {
|
if (G_IS_DEV && globalConfig.debug.instantMiners) {
|
||||||
miningSpeed *= 100;
|
miningSpeed *= 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < this.allEntities.length; ++i) {
|
for (let i = 0; i < this.allEntities.length; ++i) {
|
||||||
const entity = this.allEntities[i];
|
const entity = this.allEntities[i];
|
||||||
|
|
||||||
// Check if miner is above an actual tile
|
// Check if miner is above an actual tile
|
||||||
|
|
||||||
const minerComp = entity.components.Miner;
|
const minerComp = entity.components.Miner;
|
||||||
|
|
||||||
if (!minerComp.cachedMinedItem) {
|
if (!minerComp.cachedMinedItem) {
|
||||||
const staticComp = entity.components.StaticMapEntity;
|
const staticComp = entity.components.StaticMapEntity;
|
||||||
const tileBelow = this.root.map.getLowerLayerContentXY(
|
const tileBelow = this.root.map.getLowerLayerContentXY(
|
||||||
staticComp.origin.x,
|
staticComp.origin.x,
|
||||||
staticComp.origin.y
|
staticComp.origin.y
|
||||||
);
|
);
|
||||||
if (!tileBelow) {
|
if (!tileBelow) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
minerComp.cachedMinedItem = tileBelow;
|
minerComp.cachedMinedItem = tileBelow;
|
||||||
}
|
}
|
||||||
|
|
||||||
// First, try to get rid of chained items
|
// First, try to get rid of chained items
|
||||||
if (minerComp.itemChainBuffer.length > 0) {
|
if (minerComp.itemChainBuffer.length > 0) {
|
||||||
if (this.tryPerformMinerEject(entity, minerComp.itemChainBuffer[0])) {
|
if (this.tryPerformMinerEject(entity, minerComp.itemChainBuffer[0])) {
|
||||||
minerComp.itemChainBuffer.shift();
|
minerComp.itemChainBuffer.shift();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.root.time.isIngameTimerExpired(minerComp.lastMiningTime, 1 / miningSpeed)) {
|
const mineDuration = 1 / miningSpeed;
|
||||||
if (this.tryPerformMinerEject(entity, minerComp.cachedMinedItem)) {
|
const timeSinceMine = this.root.time.now() - minerComp.lastMiningTime;
|
||||||
// Analytics hook
|
if (timeSinceMine > mineDuration) {
|
||||||
this.root.signals.itemProduced.dispatch(minerComp.cachedMinedItem);
|
// Store how much we overflowed
|
||||||
|
const buffer = Math.min(timeSinceMine - mineDuration, this.root.dynamicTickrate.deltaSeconds);
|
||||||
// Actually mine
|
|
||||||
minerComp.lastMiningTime = this.root.time.now();
|
if (this.tryPerformMinerEject(entity, minerComp.cachedMinedItem)) {
|
||||||
}
|
// Analytics hook
|
||||||
}
|
this.root.signals.itemProduced.dispatch(minerComp.cachedMinedItem);
|
||||||
}
|
// Store mining time
|
||||||
}
|
minerComp.lastMiningTime = this.root.time.now() - buffer;
|
||||||
|
}
|
||||||
/**
|
}
|
||||||
*
|
}
|
||||||
* @param {Entity} entity
|
}
|
||||||
* @param {BaseItem} item
|
|
||||||
*/
|
/**
|
||||||
tryPerformMinerEject(entity, item) {
|
*
|
||||||
const minerComp = entity.components.Miner;
|
* @param {Entity} entity
|
||||||
const ejectComp = entity.components.ItemEjector;
|
* @param {BaseItem} item
|
||||||
const staticComp = entity.components.StaticMapEntity;
|
*/
|
||||||
|
tryPerformMinerEject(entity, item) {
|
||||||
// Check if we are a chained miner
|
const minerComp = entity.components.Miner;
|
||||||
if (minerComp.chainable) {
|
const ejectComp = entity.components.ItemEjector;
|
||||||
const ejectingSlot = ejectComp.slots[0];
|
const staticComp = entity.components.StaticMapEntity;
|
||||||
const ejectingPos = staticComp.localTileToWorld(ejectingSlot.pos);
|
|
||||||
const ejectingDirection = staticComp.localDirectionToWorld(ejectingSlot.direction);
|
// Check if we are a chained miner
|
||||||
|
if (minerComp.chainable) {
|
||||||
const targetTile = ejectingPos.add(enumDirectionToVector[ejectingDirection]);
|
const ejectingSlot = ejectComp.slots[0];
|
||||||
const targetContents = this.root.map.getTileContent(targetTile, "regular");
|
const ejectingPos = staticComp.localTileToWorld(ejectingSlot.pos);
|
||||||
|
const ejectingDirection = staticComp.localDirectionToWorld(ejectingSlot.direction);
|
||||||
// Check if we are connected to another miner and thus do not eject directly
|
|
||||||
if (targetContents) {
|
const targetTile = ejectingPos.add(enumDirectionToVector[ejectingDirection]);
|
||||||
const targetMinerComp = targetContents.components.Miner;
|
const targetContents = this.root.map.getTileContent(targetTile, "regular");
|
||||||
if (targetMinerComp) {
|
|
||||||
if (targetMinerComp.tryAcceptChainedItem(item)) {
|
// Check if we are connected to another miner and thus do not eject directly
|
||||||
return true;
|
if (targetContents) {
|
||||||
} else {
|
const targetMinerComp = targetContents.components.Miner;
|
||||||
return false;
|
if (targetMinerComp) {
|
||||||
}
|
if (targetMinerComp.tryAcceptChainedItem(item)) {
|
||||||
}
|
return true;
|
||||||
}
|
} else {
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
// Seems we are a regular miner or at the end of a row, try actually ejecting
|
}
|
||||||
if (ejectComp.tryEject(0, item)) {
|
}
|
||||||
return true;
|
}
|
||||||
}
|
|
||||||
return false;
|
// Seems we are a regular miner or at the end of a row, try actually ejecting
|
||||||
}
|
if (ejectComp.tryEject(0, item)) {
|
||||||
|
return true;
|
||||||
/**
|
}
|
||||||
*
|
return false;
|
||||||
* @param {DrawParameters} parameters
|
}
|
||||||
* @param {MapChunkView} chunk
|
|
||||||
*/
|
/**
|
||||||
drawChunk(parameters, chunk) {
|
*
|
||||||
const contents = chunk.containedEntitiesByLayer.regular;
|
* @param {DrawParameters} parameters
|
||||||
|
* @param {MapChunkView} chunk
|
||||||
for (let i = 0; i < contents.length; ++i) {
|
*/
|
||||||
const entity = contents[i];
|
drawChunk(parameters, chunk) {
|
||||||
const minerComp = entity.components.Miner;
|
const contents = chunk.containedEntitiesByLayer.regular;
|
||||||
if (!minerComp) {
|
|
||||||
continue;
|
for (let i = 0; i < contents.length; ++i) {
|
||||||
}
|
const entity = contents[i];
|
||||||
|
const minerComp = entity.components.Miner;
|
||||||
const staticComp = entity.components.StaticMapEntity;
|
if (!minerComp) {
|
||||||
if (!minerComp.cachedMinedItem) {
|
continue;
|
||||||
continue;
|
}
|
||||||
}
|
|
||||||
|
const staticComp = entity.components.StaticMapEntity;
|
||||||
// Draw the item background - this is to hide the ejected item animation from
|
if (!minerComp.cachedMinedItem) {
|
||||||
// the item ejecto
|
continue;
|
||||||
|
}
|
||||||
const padding = 3;
|
|
||||||
const destX = staticComp.origin.x * globalConfig.tileSize + padding;
|
// Draw the item background - this is to hide the ejected item animation from
|
||||||
const destY = staticComp.origin.y * globalConfig.tileSize + padding;
|
// the item ejecto
|
||||||
const dimensions = globalConfig.tileSize - 2 * padding;
|
|
||||||
|
const padding = 3;
|
||||||
if (parameters.visibleRect.containsRect4Params(destX, destY, dimensions, dimensions)) {
|
const destX = staticComp.origin.x * globalConfig.tileSize + padding;
|
||||||
parameters.context.fillStyle = minerComp.cachedMinedItem.getBackgroundColorAsResource();
|
const destY = staticComp.origin.y * globalConfig.tileSize + padding;
|
||||||
parameters.context.fillRect(destX, destY, dimensions, dimensions);
|
const dimensions = globalConfig.tileSize - 2 * padding;
|
||||||
}
|
|
||||||
|
if (parameters.visibleRect.containsRect4Params(destX, destY, dimensions, dimensions)) {
|
||||||
minerComp.cachedMinedItem.drawItemCenteredClipped(
|
parameters.context.fillStyle = minerComp.cachedMinedItem.getBackgroundColorAsResource();
|
||||||
(0.5 + staticComp.origin.x) * globalConfig.tileSize,
|
parameters.context.fillRect(destX, destY, dimensions, dimensions);
|
||||||
(0.5 + staticComp.origin.y) * globalConfig.tileSize,
|
}
|
||||||
parameters,
|
|
||||||
globalConfig.defaultItemDiameter
|
minerComp.cachedMinedItem.drawItemCenteredClipped(
|
||||||
);
|
(0.5 + staticComp.origin.x) * globalConfig.tileSize,
|
||||||
}
|
(0.5 + staticComp.origin.y) * globalConfig.tileSize,
|
||||||
}
|
parameters,
|
||||||
}
|
globalConfig.defaultItemDiameter
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,214 +1,200 @@
|
|||||||
/* typehints:start */
|
/* typehints:start */
|
||||||
import { GameRoot } from "../root";
|
import { GameRoot } from "../root";
|
||||||
/* typehints:end */
|
/* typehints:end */
|
||||||
|
|
||||||
import { types, BasicSerializableObject } from "../../savegame/serialization";
|
import { types, BasicSerializableObject } from "../../savegame/serialization";
|
||||||
import { RegularGameSpeed } from "./regular_game_speed";
|
import { RegularGameSpeed } from "./regular_game_speed";
|
||||||
import { BaseGameSpeed } from "./base_game_speed";
|
import { BaseGameSpeed } from "./base_game_speed";
|
||||||
import { PausedGameSpeed } from "./paused_game_speed";
|
import { PausedGameSpeed } from "./paused_game_speed";
|
||||||
import { FastForwardGameSpeed } from "./fast_forward_game_speed";
|
import { gGameSpeedRegistry } from "../../core/global_registries";
|
||||||
import { gGameSpeedRegistry } from "../../core/global_registries";
|
import { globalConfig } from "../../core/config";
|
||||||
import { globalConfig } from "../../core/config";
|
import { createLogger } from "../../core/logging";
|
||||||
import { checkTimerExpired, quantizeFloat } from "../../core/utils";
|
|
||||||
import { createLogger } from "../../core/logging";
|
const logger = createLogger("game_time");
|
||||||
|
|
||||||
const logger = createLogger("game_time");
|
export class GameTime extends BasicSerializableObject {
|
||||||
|
/**
|
||||||
export class GameTime extends BasicSerializableObject {
|
* @param {GameRoot} root
|
||||||
/**
|
*/
|
||||||
* @param {GameRoot} root
|
constructor(root) {
|
||||||
*/
|
super();
|
||||||
constructor(root) {
|
this.root = root;
|
||||||
super();
|
|
||||||
this.root = root;
|
// Current ingame time seconds, not incremented while paused
|
||||||
|
this.timeSeconds = 0;
|
||||||
// Current ingame time seconds, not incremented while paused
|
|
||||||
this.timeSeconds = 0;
|
// Current "realtime", a timer which always is incremented no matter whether the game is paused or no
|
||||||
|
this.realtimeSeconds = 0;
|
||||||
// Current "realtime", a timer which always is incremented no matter whether the game is paused or no
|
|
||||||
this.realtimeSeconds = 0;
|
// The adjustment, used when loading savegames so we can continue where we were
|
||||||
|
this.realtimeAdjust = 0;
|
||||||
// The adjustment, used when loading savegames so we can continue where we were
|
|
||||||
this.realtimeAdjust = 0;
|
/** @type {BaseGameSpeed} */
|
||||||
|
this.speed = new RegularGameSpeed(this.root);
|
||||||
/** @type {BaseGameSpeed} */
|
|
||||||
this.speed = new RegularGameSpeed(this.root);
|
// Store how much time we have in bucket
|
||||||
|
this.logicTimeBudget = 0;
|
||||||
// Store how much time we have in bucket
|
}
|
||||||
this.logicTimeBudget = 0;
|
|
||||||
}
|
static getId() {
|
||||||
|
return "GameTime";
|
||||||
static getId() {
|
}
|
||||||
return "GameTime";
|
|
||||||
}
|
static getSchema() {
|
||||||
|
return {
|
||||||
static getSchema() {
|
timeSeconds: types.float,
|
||||||
return {
|
speed: types.obj(gGameSpeedRegistry),
|
||||||
timeSeconds: types.float,
|
realtimeSeconds: types.float,
|
||||||
speed: types.obj(gGameSpeedRegistry),
|
};
|
||||||
realtimeSeconds: types.float,
|
}
|
||||||
};
|
|
||||||
}
|
/**
|
||||||
|
* Fetches the new "real" time, called from the core once per frame, since performance now() is kinda slow
|
||||||
/**
|
*/
|
||||||
* Fetches the new "real" time, called from the core once per frame, since performance now() is kinda slow
|
updateRealtimeNow() {
|
||||||
*/
|
this.realtimeSeconds = performance.now() / 1000.0 + this.realtimeAdjust;
|
||||||
updateRealtimeNow() {
|
}
|
||||||
this.realtimeSeconds = performance.now() / 1000.0 + this.realtimeAdjust;
|
|
||||||
}
|
/**
|
||||||
|
* Returns the ingame time in milliseconds
|
||||||
/**
|
*/
|
||||||
* Returns the ingame time in milliseconds
|
getTimeMs() {
|
||||||
*/
|
return this.timeSeconds * 1000.0;
|
||||||
getTimeMs() {
|
}
|
||||||
return this.timeSeconds * 1000.0;
|
|
||||||
}
|
/**
|
||||||
|
* Returns how many seconds we are in the grace period
|
||||||
/**
|
* @returns {number}
|
||||||
* Safe check to check if a timer is expired. quantizes numbers
|
*/
|
||||||
* @param {number} lastTick Last tick of the timer
|
getRemainingGracePeriodSeconds() {
|
||||||
* @param {number} tickRateSeconds Interval of the timer in seconds
|
return 0;
|
||||||
*/
|
}
|
||||||
isIngameTimerExpired(lastTick, tickRateSeconds) {
|
|
||||||
return checkTimerExpired(this.timeSeconds, lastTick, tickRateSeconds);
|
/**
|
||||||
}
|
* Returns if we are currently in the grace period
|
||||||
|
* @returns {boolean}
|
||||||
/**
|
*/
|
||||||
* Returns how many seconds we are in the grace period
|
getIsWithinGracePeriod() {
|
||||||
* @returns {number}
|
return this.getRemainingGracePeriodSeconds() > 0;
|
||||||
*/
|
}
|
||||||
getRemainingGracePeriodSeconds() {
|
|
||||||
return 0;
|
/**
|
||||||
}
|
* Internal method to generate new logic time budget
|
||||||
|
* @param {number} deltaMs
|
||||||
/**
|
*/
|
||||||
* Returns if we are currently in the grace period
|
internalAddDeltaToBudget(deltaMs) {
|
||||||
* @returns {boolean}
|
// Only update if game is supposed to update
|
||||||
*/
|
if (this.root.hud.shouldPauseGame()) {
|
||||||
getIsWithinGracePeriod() {
|
this.logicTimeBudget = 0;
|
||||||
return this.getRemainingGracePeriodSeconds() > 0;
|
} else {
|
||||||
}
|
const multiplier = this.getSpeed().getTimeMultiplier();
|
||||||
|
this.logicTimeBudget += deltaMs * multiplier;
|
||||||
/**
|
}
|
||||||
* Internal method to generate new logic time budget
|
|
||||||
* @param {number} deltaMs
|
// Check for too big pile of updates -> reduce it to 1
|
||||||
*/
|
let maxLogicSteps = Math.max(
|
||||||
internalAddDeltaToBudget(deltaMs) {
|
3,
|
||||||
// Only update if game is supposed to update
|
(this.speed.getMaxLogicStepsInQueue() * this.root.dynamicTickrate.currentTickRate) / 60
|
||||||
if (this.root.hud.shouldPauseGame()) {
|
);
|
||||||
this.logicTimeBudget = 0;
|
if (G_IS_DEV && globalConfig.debug.framePausesBetweenTicks) {
|
||||||
} else {
|
maxLogicSteps *= 1 + globalConfig.debug.framePausesBetweenTicks;
|
||||||
const multiplier = this.getSpeed().getTimeMultiplier();
|
}
|
||||||
this.logicTimeBudget += deltaMs * multiplier;
|
|
||||||
}
|
if (this.logicTimeBudget > this.root.dynamicTickrate.deltaMs * maxLogicSteps) {
|
||||||
|
this.logicTimeBudget = this.root.dynamicTickrate.deltaMs * maxLogicSteps;
|
||||||
// Check for too big pile of updates -> reduce it to 1
|
}
|
||||||
let maxLogicSteps = Math.max(
|
}
|
||||||
3,
|
|
||||||
(this.speed.getMaxLogicStepsInQueue() * this.root.dynamicTickrate.currentTickRate) / 60
|
/**
|
||||||
);
|
* Performs update ticks based on the queued logic budget
|
||||||
if (G_IS_DEV && globalConfig.debug.framePausesBetweenTicks) {
|
* @param {number} deltaMs
|
||||||
maxLogicSteps *= 1 + globalConfig.debug.framePausesBetweenTicks;
|
* @param {function():boolean} updateMethod
|
||||||
}
|
*/
|
||||||
|
performTicks(deltaMs, updateMethod) {
|
||||||
if (this.logicTimeBudget > this.root.dynamicTickrate.deltaMs * maxLogicSteps) {
|
this.internalAddDeltaToBudget(deltaMs);
|
||||||
this.logicTimeBudget = this.root.dynamicTickrate.deltaMs * maxLogicSteps;
|
|
||||||
}
|
const speedAtStart = this.root.time.getSpeed();
|
||||||
}
|
|
||||||
|
let effectiveDelta = this.root.dynamicTickrate.deltaMs;
|
||||||
/**
|
if (G_IS_DEV && globalConfig.debug.framePausesBetweenTicks) {
|
||||||
* Performs update ticks based on the queued logic budget
|
effectiveDelta += globalConfig.debug.framePausesBetweenTicks * this.root.dynamicTickrate.deltaMs;
|
||||||
* @param {number} deltaMs
|
}
|
||||||
* @param {function():boolean} updateMethod
|
|
||||||
*/
|
// Update physics & logic
|
||||||
performTicks(deltaMs, updateMethod) {
|
while (this.logicTimeBudget >= effectiveDelta) {
|
||||||
this.internalAddDeltaToBudget(deltaMs);
|
this.logicTimeBudget -= effectiveDelta;
|
||||||
|
|
||||||
const speedAtStart = this.root.time.getSpeed();
|
if (!updateMethod()) {
|
||||||
|
// Gameover happened or so, do not update anymore
|
||||||
let effectiveDelta = this.root.dynamicTickrate.deltaMs;
|
return;
|
||||||
if (G_IS_DEV && globalConfig.debug.framePausesBetweenTicks) {
|
}
|
||||||
effectiveDelta += globalConfig.debug.framePausesBetweenTicks * this.root.dynamicTickrate.deltaMs;
|
|
||||||
}
|
// Step game time
|
||||||
|
this.timeSeconds += this.root.dynamicTickrate.deltaSeconds;
|
||||||
// Update physics & logic
|
|
||||||
while (this.logicTimeBudget >= effectiveDelta) {
|
// Game time speed changed, need to abort since our logic steps are no longer valid
|
||||||
this.logicTimeBudget -= effectiveDelta;
|
if (speedAtStart.getId() !== this.speed.getId()) {
|
||||||
|
logger.warn(
|
||||||
if (!updateMethod()) {
|
"Skipping update because speed changed from",
|
||||||
// Gameover happened or so, do not update anymore
|
speedAtStart.getId(),
|
||||||
return;
|
"to",
|
||||||
}
|
this.speed.getId()
|
||||||
|
);
|
||||||
// Step game time
|
break;
|
||||||
this.timeSeconds = quantizeFloat(this.timeSeconds + this.root.dynamicTickrate.deltaSeconds);
|
}
|
||||||
|
}
|
||||||
// Game time speed changed, need to abort since our logic steps are no longer valid
|
}
|
||||||
if (speedAtStart.getId() !== this.speed.getId()) {
|
|
||||||
logger.warn(
|
/**
|
||||||
"Skipping update because speed changed from",
|
* Returns ingame time in seconds
|
||||||
speedAtStart.getId(),
|
* @returns {number} seconds
|
||||||
"to",
|
*/
|
||||||
this.speed.getId()
|
now() {
|
||||||
);
|
return this.timeSeconds;
|
||||||
break;
|
}
|
||||||
}
|
|
||||||
}
|
/**
|
||||||
}
|
* Returns "real" time in seconds
|
||||||
|
* @returns {number} seconds
|
||||||
/**
|
*/
|
||||||
* Returns ingame time in seconds
|
realtimeNow() {
|
||||||
* @returns {number} seconds
|
return this.realtimeSeconds;
|
||||||
*/
|
}
|
||||||
now() {
|
|
||||||
return this.timeSeconds;
|
/**
|
||||||
}
|
* Returns "real" time in seconds
|
||||||
|
* @returns {number} seconds
|
||||||
/**
|
*/
|
||||||
* Returns "real" time in seconds
|
systemNow() {
|
||||||
* @returns {number} seconds
|
return (this.realtimeSeconds - this.realtimeAdjust) * 1000.0;
|
||||||
*/
|
}
|
||||||
realtimeNow() {
|
|
||||||
return this.realtimeSeconds;
|
getIsPaused() {
|
||||||
}
|
return this.speed.getId() === PausedGameSpeed.getId();
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Returns "real" time in seconds
|
getSpeed() {
|
||||||
* @returns {number} seconds
|
return this.speed;
|
||||||
*/
|
}
|
||||||
systemNow() {
|
|
||||||
return (this.realtimeSeconds - this.realtimeAdjust) * 1000.0;
|
setSpeed(speed) {
|
||||||
}
|
assert(speed instanceof BaseGameSpeed, "Not a valid game speed");
|
||||||
|
if (this.speed.getId() === speed.getId()) {
|
||||||
getIsPaused() {
|
logger.warn("Same speed set than current one:", speed.constructor.getId());
|
||||||
return this.speed.getId() === PausedGameSpeed.getId();
|
}
|
||||||
}
|
this.speed = speed;
|
||||||
|
}
|
||||||
getSpeed() {
|
|
||||||
return this.speed;
|
deserialize(data) {
|
||||||
}
|
const errorCode = super.deserialize(data);
|
||||||
|
if (errorCode) {
|
||||||
setSpeed(speed) {
|
return errorCode;
|
||||||
assert(speed instanceof BaseGameSpeed, "Not a valid game speed");
|
}
|
||||||
if (this.speed.getId() === speed.getId()) {
|
|
||||||
logger.warn("Same speed set than current one:", speed.constructor.getId());
|
// Adjust realtime now difference so they match
|
||||||
}
|
this.realtimeAdjust = this.realtimeSeconds - performance.now() / 1000.0;
|
||||||
this.speed = speed;
|
this.updateRealtimeNow();
|
||||||
}
|
|
||||||
|
this.speed.initializeAfterDeserialize(this.root);
|
||||||
deserialize(data) {
|
}
|
||||||
const errorCode = super.deserialize(data);
|
}
|
||||||
if (errorCode) {
|
|
||||||
return errorCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adjust realtime now difference so they match
|
|
||||||
this.realtimeAdjust = this.realtimeSeconds - performance.now() / 1000.0;
|
|
||||||
this.updateRealtimeNow();
|
|
||||||
|
|
||||||
// Make sure we have a quantizied time
|
|
||||||
this.timeSeconds = quantizeFloat(this.timeSeconds);
|
|
||||||
|
|
||||||
this.speed.initializeAfterDeserialize(this.root);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|