mirror of
https://github.com/tobspr/shapez.io.git
synced 2026-03-02 03:39:21 +00:00
Initial support for saving games
This commit is contained in:
@@ -2,21 +2,21 @@ import {
|
||||
Math_abs,
|
||||
Math_ceil,
|
||||
Math_floor,
|
||||
Math_max,
|
||||
Math_min,
|
||||
Math_random,
|
||||
performanceNow,
|
||||
Math_max,
|
||||
} from "../core/builtins";
|
||||
import { Rectangle } from "../core/rectangle";
|
||||
import { Signal, STOP_PROPAGATION } from "../core/signal";
|
||||
import { clamp, lerp } from "../core/utils";
|
||||
import { mixVector, Vector } from "../core/vector";
|
||||
import { globalConfig } from "../core/config";
|
||||
import { GameRoot } from "./root";
|
||||
import { BasicSerializableObject, types } from "../savegame/serialization";
|
||||
import { clickDetectorGlobals } from "../core/click_detector";
|
||||
import { globalConfig } from "../core/config";
|
||||
import { createLogger } from "../core/logging";
|
||||
import { queryParamOptions } from "../core/query_parameters";
|
||||
import { Rectangle } from "../core/rectangle";
|
||||
import { Signal, STOP_PROPAGATION } from "../core/signal";
|
||||
import { clamp } from "../core/utils";
|
||||
import { mixVector, Vector } from "../core/vector";
|
||||
import { BasicSerializableObject, types } from "../savegame/serialization";
|
||||
import { GameRoot } from "./root";
|
||||
|
||||
const logger = createLogger("camera");
|
||||
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
import { Component } from "../component";
|
||||
import { ShapeDefinition } from "../shape_definition";
|
||||
import { types } from "../../savegame/serialization";
|
||||
|
||||
export class HubComponent extends Component {
|
||||
static getId() {
|
||||
return "Hub";
|
||||
}
|
||||
|
||||
static getSchema() {
|
||||
return {
|
||||
definitionsToAnalyze: types.array(types.knownType(ShapeDefinition)),
|
||||
};
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Vector, enumDirection, enumDirectionToAngle, enumInvertedDirections } f
|
||||
import { BaseItem } from "../base_item";
|
||||
import { ShapeItem } from "../items/shape_item";
|
||||
import { ColorItem } from "../items/color_item";
|
||||
import { types } from "../../savegame/serialization";
|
||||
|
||||
/**
|
||||
* @enum {string?}
|
||||
@@ -26,7 +27,13 @@ export class ItemAcceptorComponent extends Component {
|
||||
|
||||
static getSchema() {
|
||||
return {
|
||||
// slots: "TODO",
|
||||
slots: types.array(
|
||||
types.structured({
|
||||
pos: types.vector,
|
||||
directions: types.array(types.enum(enumDirection)),
|
||||
filter: types.nullable(types.enum(enumItemAcceptorItemFilter)),
|
||||
})
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -35,7 +42,7 @@ export class ItemAcceptorComponent extends Component {
|
||||
* @param {object} param0
|
||||
* @param {Array<{pos: Vector, directions: enumDirection[], filter?: enumItemAcceptorItemFilter}>} param0.slots The slots from which we accept items
|
||||
*/
|
||||
constructor({ slots }) {
|
||||
constructor({ slots = [] }) {
|
||||
super();
|
||||
|
||||
this.setSlots(slots);
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { globalConfig } from "../../core/config";
|
||||
import { Vector, enumDirection, enumDirectionToVector } from "../../core/vector";
|
||||
import { BaseItem } from "../base_item";
|
||||
import { Component } from "../component";
|
||||
import { types } from "../../savegame/serialization";
|
||||
import { gItemRegistry } from "../../core/global_registries";
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
@@ -19,7 +20,15 @@ export class ItemEjectorComponent extends Component {
|
||||
|
||||
static getSchema() {
|
||||
return {
|
||||
// slots: "TODO"
|
||||
instantEject: types.bool,
|
||||
slots: types.array(
|
||||
types.structured({
|
||||
pos: types.vector,
|
||||
direction: types.enum(enumDirection),
|
||||
item: types.nullable(types.obj(gItemRegistry)),
|
||||
progress: types.ufloat,
|
||||
})
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -29,7 +38,7 @@ export class ItemEjectorComponent extends Component {
|
||||
* @param {Array<{pos: Vector, direction: enumDirection}>} param0.slots The slots to eject on
|
||||
* @param {boolean=} param0.instantEject If the ejection is instant
|
||||
*/
|
||||
constructor({ slots, instantEject = false }) {
|
||||
constructor({ slots = [], instantEject = false }) {
|
||||
super();
|
||||
|
||||
// How long items take to eject
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { BaseItem } from "../base_item";
|
||||
import { Component } from "../component";
|
||||
import { enumDirection, Vector } from "../../core/vector";
|
||||
import { types } from "../../savegame/serialization";
|
||||
import { gItemRegistry } from "../../core/global_registries";
|
||||
|
||||
/** @enum {string} */
|
||||
export const enumItemProcessorTypes = {
|
||||
@@ -21,7 +23,37 @@ export class ItemProcessorComponent extends Component {
|
||||
|
||||
static getSchema() {
|
||||
return {
|
||||
// TODO
|
||||
nextOutputSlot: types.uint,
|
||||
type: types.enum(enumItemProcessorTypes),
|
||||
inputsPerCharge: types.uint,
|
||||
beltUnderlays: types.array(
|
||||
types.structured({
|
||||
pos: types.vector,
|
||||
direction: types.enum(enumDirection),
|
||||
})
|
||||
),
|
||||
inputSlots: types.array(
|
||||
types.structured({
|
||||
item: types.obj(gItemRegistry),
|
||||
sourceSlot: types.uint,
|
||||
})
|
||||
),
|
||||
itemsToEject: types.array(
|
||||
types.structured({
|
||||
item: types.obj(gItemRegistry),
|
||||
requiredSlot: types.nullable(types.uint),
|
||||
preferredSlot: types.nullable(types.uint),
|
||||
})
|
||||
),
|
||||
secondsUntilEject: types.ufloat,
|
||||
itemConsumptionAnimations: types.array(
|
||||
types.structured({
|
||||
item: types.obj(gItemRegistry),
|
||||
slotIndex: types.uint,
|
||||
animProgress: types.ufloat,
|
||||
direction: types.enum(enumDirection),
|
||||
})
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -14,9 +14,8 @@ export class MinerComponent extends Component {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} param0
|
||||
*/
|
||||
constructor({}) {
|
||||
constructor() {
|
||||
super();
|
||||
this.lastMiningTime = 0;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,14 @@ export class StaticMapEntityComponent extends Component {
|
||||
}
|
||||
|
||||
static getSchema() {
|
||||
return {};
|
||||
return {
|
||||
origin: types.tileVector,
|
||||
tileSize: types.tileVector,
|
||||
rotation: types.float,
|
||||
originalRotation: types.float,
|
||||
spriteKey: types.nullable(types.string),
|
||||
silhouetteColor: types.nullable(types.string),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { BaseItem } from "../base_item";
|
||||
import { Component } from "../component";
|
||||
import { globalConfig } from "../../core/config";
|
||||
import { types } from "../../savegame/serialization";
|
||||
import { gItemRegistry } from "../../core/global_registries";
|
||||
|
||||
/** @enum {string} */
|
||||
export const enumUndergroundBeltMode = {
|
||||
@@ -13,6 +15,13 @@ export class UndergroundBeltComponent extends Component {
|
||||
return "UndergroundBelt";
|
||||
}
|
||||
|
||||
static getSchema() {
|
||||
return {
|
||||
mode: types.enum(enumUndergroundBeltMode),
|
||||
pendingItems: types.array(types.pair(types.obj(gItemRegistry), types.number)),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {object} param0
|
||||
|
||||
@@ -11,7 +11,6 @@ import { getDeviceDPI, resizeHighDPICanvas } from "../core/dpi_manager";
|
||||
import { DrawParameters } from "../core/draw_parameters";
|
||||
import { gMetaBuildingRegistry } from "../core/global_registries";
|
||||
import { createLogger } from "../core/logging";
|
||||
import { PerlinNoise } from "../core/perlin_noise";
|
||||
import { Vector } from "../core/vector";
|
||||
import { Savegame } from "../savegame/savegame";
|
||||
import { SavegameSerializer } from "../savegame/savegame_serializer";
|
||||
@@ -31,6 +30,7 @@ import { ShapeDefinitionManager } from "./shape_definition_manager";
|
||||
import { SoundProxy } from "./sound_proxy";
|
||||
import { GameTime } from "./time/game_time";
|
||||
import { ProductionAnalytics } from "./production_analytics";
|
||||
import { randomInt } from "../core/utils";
|
||||
|
||||
const logger = createLogger("ingame/core");
|
||||
|
||||
@@ -112,7 +112,6 @@ export class GameCore {
|
||||
root.shapeDefinitionMgr = new ShapeDefinitionManager(root);
|
||||
root.hubGoals = new HubGoals(root);
|
||||
root.productionAnalytics = new ProductionAnalytics(root);
|
||||
root.mapNoiseGenerator = new PerlinNoise(Math.random()); // TODO: Save seed
|
||||
root.buffers = new BufferMaintainer(root);
|
||||
|
||||
// Initialize the hud once everything is loaded
|
||||
@@ -136,6 +135,7 @@ export class GameCore {
|
||||
initNewGame() {
|
||||
logger.log("Initializing new game");
|
||||
this.root.gameIsFresh = true;
|
||||
this.root.map.seed = randomInt(0, 100000);
|
||||
|
||||
gMetaBuildingRegistry.findByClass(MetaHubBuilding).createAndPlaceEntity({
|
||||
root: this.root,
|
||||
|
||||
@@ -5,14 +5,13 @@ import { Component } from "./component";
|
||||
/* typehints:end */
|
||||
|
||||
import { globalConfig } from "../core/config";
|
||||
import { Vector, enumDirectionToVector, enumDirectionToAngle } from "../core/vector";
|
||||
import { enumDirectionToVector, enumDirectionToAngle } from "../core/vector";
|
||||
import { BasicSerializableObject, types } from "../savegame/serialization";
|
||||
import { EntityComponentStorage } from "./entity_components";
|
||||
import { Loader } from "../core/loader";
|
||||
import { drawRotatedSprite } from "../core/draw_utils";
|
||||
import { Math_radians } from "../core/builtins";
|
||||
// import { gFactionRegistry, gComponentRegistry } from "../core/global_registries";
|
||||
// import { EntityComponentStorage } from "./entity_components";
|
||||
import { gComponentRegistry } from "../core/global_registries";
|
||||
|
||||
export class Entity extends BasicSerializableObject {
|
||||
/**
|
||||
@@ -78,7 +77,7 @@ export class Entity extends BasicSerializableObject {
|
||||
static getSchema() {
|
||||
return {
|
||||
uid: types.uint,
|
||||
// components: types.keyValueMap(types.objData(gComponentRegistry), false)
|
||||
components: types.keyValueMap(types.objData(gComponentRegistry), false),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
8
src/js/game/game_speed_registry.js
Normal file
8
src/js/game/game_speed_registry.js
Normal file
@@ -0,0 +1,8 @@
|
||||
import { RegularGameSpeed } from "./time/regular_game_speed";
|
||||
import { gGameSpeedRegistry } from "../core/global_registries";
|
||||
|
||||
export function initGameSpeedRegistry() {
|
||||
gGameSpeedRegistry.register(RegularGameSpeed);
|
||||
|
||||
// Others are disabled for now
|
||||
}
|
||||
@@ -1,23 +1,43 @@
|
||||
import { BasicSerializableObject } from "../savegame/serialization";
|
||||
import { GameRoot } from "./root";
|
||||
import { ShapeDefinition, enumSubShape } from "./shape_definition";
|
||||
import { enumColors, enumShortcodeToColor, enumColorToShortcode } from "./colors";
|
||||
import { randomChoice, clamp, randomInt, findNiceIntegerValue } from "../core/utils";
|
||||
import { tutorialGoals, enumHubGoalRewards } from "./tutorial_goals";
|
||||
import { createLogger } from "../core/logging";
|
||||
import { globalConfig } from "../core/config";
|
||||
import { Math_random } from "../core/builtins";
|
||||
import { UPGRADES } from "./upgrades";
|
||||
import { enumItemProcessorTypes } from "./components/item_processor";
|
||||
import { globalConfig } from "../core/config";
|
||||
import { queryParamOptions } from "../core/query_parameters";
|
||||
|
||||
const logger = createLogger("hub_goals");
|
||||
import { clamp, findNiceIntegerValue, randomChoice, randomInt } from "../core/utils";
|
||||
import { BasicSerializableObject, types } from "../savegame/serialization";
|
||||
import { enumColors } from "./colors";
|
||||
import { enumItemProcessorTypes } from "./components/item_processor";
|
||||
import { GameRoot } from "./root";
|
||||
import { enumSubShape, ShapeDefinition } from "./shape_definition";
|
||||
import { enumHubGoalRewards, tutorialGoals } from "./tutorial_goals";
|
||||
import { UPGRADES } from "./upgrades";
|
||||
|
||||
export class HubGoals extends BasicSerializableObject {
|
||||
static getId() {
|
||||
return "HubGoals";
|
||||
}
|
||||
|
||||
static getSchema() {
|
||||
return {
|
||||
level: types.uint,
|
||||
storedShapes: types.keyValueMap(types.uint),
|
||||
upgradeLevels: types.keyValueMap(types.uint),
|
||||
|
||||
currentGoal: types.structured({
|
||||
definition: types.knownType(ShapeDefinition),
|
||||
required: types.uint,
|
||||
reward: types.nullable(types.enum(enumHubGoalRewards)),
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
deserialize(data) {
|
||||
const errorCode = super.deserialize(data);
|
||||
if (errorCode) {
|
||||
return errorCode;
|
||||
}
|
||||
|
||||
console.error("TODO: HubGoals deserialize() properly");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {GameRoot} root
|
||||
*/
|
||||
@@ -30,6 +50,7 @@ export class HubGoals extends BasicSerializableObject {
|
||||
|
||||
/**
|
||||
* Which story rewards we already gained
|
||||
* @type {Object.<string, number>}
|
||||
*/
|
||||
this.gainedRewards = {};
|
||||
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
import { BaseHUDPart } from "../base_hud_part";
|
||||
import { makeDiv } from "../../../core/utils";
|
||||
import { DynamicDomAttach } from "../dynamic_dom_attach";
|
||||
import { gMetaBuildingRegistry } from "../../../core/global_registries";
|
||||
import { MetaBuilding } from "../../meta_building";
|
||||
import { MetaSplitterBuilding } from "../../buildings/splitter";
|
||||
import { MetaCutterBuilding } from "../../buildings/cutter";
|
||||
import { enumHubGoalRewards } from "../../tutorial_goals";
|
||||
import { MetaTrashBuilding } from "../../buildings/trash";
|
||||
import { MetaMinerBuilding } from "../../buildings/miner";
|
||||
import { MetaPainterBuilding } from "../../buildings/painter";
|
||||
import { MetaMixerBuilding } from "../../buildings/mixer";
|
||||
import { MetaRotaterBuilding } from "../../buildings/rotater";
|
||||
import { MetaStackerBuilding } from "../../buildings/stacker";
|
||||
import { MetaUndergroundBeltBuilding } from "../../buildings/underground_belt";
|
||||
import { globalConfig } from "../../../core/config";
|
||||
import { gMetaBuildingRegistry } from "../../../core/global_registries";
|
||||
import { makeDiv } from "../../../core/utils";
|
||||
import { SOUNDS } from "../../../platform/sound";
|
||||
import { MetaCutterBuilding } from "../../buildings/cutter";
|
||||
import { MetaMixerBuilding } from "../../buildings/mixer";
|
||||
import { MetaPainterBuilding } from "../../buildings/painter";
|
||||
import { MetaRotaterBuilding } from "../../buildings/rotater";
|
||||
import { MetaSplitterBuilding } from "../../buildings/splitter";
|
||||
import { MetaStackerBuilding } from "../../buildings/stacker";
|
||||
import { MetaTrashBuilding } from "../../buildings/trash";
|
||||
import { MetaUndergroundBeltBuilding } from "../../buildings/underground_belt";
|
||||
import { enumHubGoalRewards, enumHubGoalRewardToString } from "../../tutorial_goals";
|
||||
import { BaseHUDPart } from "../base_hud_part";
|
||||
import { DynamicDomAttach } from "../dynamic_dom_attach";
|
||||
|
||||
export class HUDUnlockNotification extends BaseHUDPart {
|
||||
initialize() {
|
||||
@@ -58,10 +56,14 @@ export class HUDUnlockNotification extends BaseHUDPart {
|
||||
this.trackClicks(this.btnClose, this.close);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} level
|
||||
* @param {enumHubGoalRewards} reward
|
||||
*/
|
||||
showForLevel(level, reward) {
|
||||
this.elemTitle.innerText = "Level " + ("" + level).padStart(2, "0");
|
||||
|
||||
let html = `<span class='reward'>Unlocked ${reward}!</span>`;
|
||||
let html = `<span class='reward'>Unlocked ${enumHubGoalRewardToString[reward]}!</span>`;
|
||||
|
||||
const addBuildingExplanation = metaBuildingClass => {
|
||||
const metaBuilding = gMetaBuildingRegistry.findByClass(metaBuildingClass);
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
import { DrawParameters } from "../../core/draw_parameters";
|
||||
import { createLogger } from "../../core/logging";
|
||||
import { extendSchema } from "../../savegame/serialization";
|
||||
import { BaseItem } from "../base_item";
|
||||
import { enumColorsToHexCode, enumColors } from "../colors";
|
||||
import { makeOffscreenBuffer } from "../../core/buffer_utils";
|
||||
import { globalConfig } from "../../core/config";
|
||||
import { round1Digit } from "../../core/utils";
|
||||
import { Math_max, Math_round } from "../../core/builtins";
|
||||
import { smoothenDpi } from "../../core/dpi_manager";
|
||||
import { DrawParameters } from "../../core/draw_parameters";
|
||||
import { types } from "../../savegame/serialization";
|
||||
import { BaseItem } from "../base_item";
|
||||
import { enumColors, enumColorsToHexCode } from "../colors";
|
||||
|
||||
/** @enum {string} */
|
||||
const enumColorToMapBackground = {
|
||||
@@ -22,9 +18,15 @@ export class ColorItem extends BaseItem {
|
||||
}
|
||||
|
||||
static getSchema() {
|
||||
return extendSchema(BaseItem.getCachedSchema(), {
|
||||
// TODO
|
||||
});
|
||||
return types.enum(enumColors);
|
||||
}
|
||||
|
||||
serialize() {
|
||||
return this.color;
|
||||
}
|
||||
|
||||
deserialize(data) {
|
||||
this.color = data;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -33,7 +35,6 @@ export class ColorItem extends BaseItem {
|
||||
constructor(color) {
|
||||
super();
|
||||
this.color = color;
|
||||
|
||||
this.bufferGenerator = this.internalGenerateColorBuffer.bind(this);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import { BaseItem } from "../base_item";
|
||||
import { DrawParameters } from "../../core/draw_parameters";
|
||||
import { extendSchema } from "../../savegame/serialization";
|
||||
import { types } from "../../savegame/serialization";
|
||||
import { BaseItem } from "../base_item";
|
||||
import { ShapeDefinition } from "../shape_definition";
|
||||
import { createLogger } from "../../core/logging";
|
||||
|
||||
const logger = createLogger("shape_item");
|
||||
|
||||
export class ShapeItem extends BaseItem {
|
||||
static getId() {
|
||||
@@ -12,9 +9,15 @@ export class ShapeItem extends BaseItem {
|
||||
}
|
||||
|
||||
static getSchema() {
|
||||
return extendSchema(BaseItem.getCachedSchema(), {
|
||||
// TODO
|
||||
});
|
||||
return types.string;
|
||||
}
|
||||
|
||||
serialize() {
|
||||
return this.definition.getHash();
|
||||
}
|
||||
|
||||
deserialize(data) {
|
||||
this.definition = ShapeDefinition.fromShortKey(data);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,17 +9,32 @@ import { Math_floor } from "../core/builtins";
|
||||
import { createLogger } from "../core/logging";
|
||||
import { BaseItem } from "./base_item";
|
||||
import { MapChunkView } from "./map_chunk_view";
|
||||
import { randomInt } from "../core/utils";
|
||||
import { BasicSerializableObject, types } from "../savegame/serialization";
|
||||
|
||||
const logger = createLogger("map");
|
||||
|
||||
export class BaseMap {
|
||||
export class BaseMap extends BasicSerializableObject {
|
||||
static getId() {
|
||||
return "Map";
|
||||
}
|
||||
|
||||
static getSchema() {
|
||||
return {
|
||||
seed: types.uint,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {GameRoot} root
|
||||
*/
|
||||
constructor(root) {
|
||||
super();
|
||||
this.root = root;
|
||||
|
||||
this.seed = 0;
|
||||
|
||||
/**
|
||||
* Mapping of 'X|Y' to chunk
|
||||
* @type {Map<string, MapChunkView>} */
|
||||
|
||||
@@ -2,16 +2,10 @@
|
||||
import { GameRoot } from "./root";
|
||||
/* typehints:end */
|
||||
|
||||
import { Math_ceil, Math_max, Math_min, Math_random, Math_round } from "../core/builtins";
|
||||
import { Math_ceil, Math_max, Math_min, Math_round } from "../core/builtins";
|
||||
import { globalConfig } from "../core/config";
|
||||
import { createLogger } from "../core/logging";
|
||||
import {
|
||||
clamp,
|
||||
fastArrayDeleteValueIfContained,
|
||||
make2DUndefinedArray,
|
||||
randomChoice,
|
||||
randomInt,
|
||||
} from "../core/utils";
|
||||
import { clamp, fastArrayDeleteValueIfContained, make2DUndefinedArray } from "../core/utils";
|
||||
import { Vector } from "../core/vector";
|
||||
import { BaseItem } from "./base_item";
|
||||
import { enumColors } from "./colors";
|
||||
@@ -19,6 +13,7 @@ import { Entity } from "./entity";
|
||||
import { ColorItem } from "./items/color_item";
|
||||
import { ShapeItem } from "./items/shape_item";
|
||||
import { enumSubShape } from "./shape_definition";
|
||||
import { RandomNumberGenerator } from "../core/rng";
|
||||
|
||||
const logger = createLogger("map_chunk");
|
||||
|
||||
@@ -64,17 +59,18 @@ export class MapChunk {
|
||||
|
||||
/**
|
||||
* Generates a patch filled with the given item
|
||||
* @param {RandomNumberGenerator} rng
|
||||
* @param {number} patchSize
|
||||
* @param {BaseItem} item
|
||||
* @param {number=} overrideX Override the X position of the patch
|
||||
* @param {number=} overrideY Override the Y position of the patch
|
||||
*/
|
||||
internalGeneratePatch(patchSize, item, overrideX = null, overrideY = null) {
|
||||
internalGeneratePatch(rng, patchSize, item, overrideX = null, overrideY = null) {
|
||||
const border = Math_ceil(patchSize / 2 + 3);
|
||||
|
||||
// Find a position within the chunk which is not blocked
|
||||
let patchX = randomInt(border, globalConfig.mapChunkSize - border - 1);
|
||||
let patchY = randomInt(border, globalConfig.mapChunkSize - border - 1);
|
||||
let patchX = rng.nextIntRange(border, globalConfig.mapChunkSize - border - 1);
|
||||
let patchY = rng.nextIntRange(border, globalConfig.mapChunkSize - border - 1);
|
||||
|
||||
if (overrideX !== null) {
|
||||
patchX = overrideX;
|
||||
@@ -89,7 +85,6 @@ export class MapChunk {
|
||||
|
||||
// Each patch consists of multiple circles
|
||||
const numCircles = patchSize;
|
||||
// const numCircles = 1;
|
||||
|
||||
for (let i = 0; i <= numCircles; ++i) {
|
||||
// Determine circle parameters
|
||||
@@ -98,11 +93,11 @@ export class MapChunk {
|
||||
const circleOffsetRadius = (numCircles - i) / 2 + 2;
|
||||
|
||||
// We draw an elipsis actually
|
||||
const circleScaleY = 1 + (Math_random() * 2 - 1) * 0.1;
|
||||
const circleScaleX = 1 + (Math_random() * 2 - 1) * 0.1;
|
||||
const circleScaleX = rng.nextRange(0.9, 1.1);
|
||||
const circleScaleY = rng.nextRange(0.9, 1.1);
|
||||
|
||||
const circleX = patchX + randomInt(-circleOffsetRadius, circleOffsetRadius);
|
||||
const circleY = patchY + randomInt(-circleOffsetRadius, circleOffsetRadius);
|
||||
const circleX = patchX + rng.nextIntRange(-circleOffsetRadius, circleOffsetRadius);
|
||||
const circleY = patchY + rng.nextIntRange(-circleOffsetRadius, circleOffsetRadius);
|
||||
|
||||
for (let dx = -circleRadius * circleScaleX - 2; dx <= circleRadius * circleScaleX + 2; ++dx) {
|
||||
for (let dy = -circleRadius * circleScaleY - 2; dy <= circleRadius * circleScaleY + 2; ++dy) {
|
||||
@@ -135,24 +130,26 @@ export class MapChunk {
|
||||
|
||||
/**
|
||||
* Generates a color patch
|
||||
* @param {RandomNumberGenerator} rng
|
||||
* @param {number} colorPatchSize
|
||||
* @param {number} distanceToOriginInChunks
|
||||
*/
|
||||
internalGenerateColorPatch(colorPatchSize, distanceToOriginInChunks) {
|
||||
internalGenerateColorPatch(rng, colorPatchSize, distanceToOriginInChunks) {
|
||||
// First, determine available colors
|
||||
let availableColors = [enumColors.red, enumColors.green];
|
||||
if (distanceToOriginInChunks > 2) {
|
||||
availableColors.push(enumColors.blue);
|
||||
}
|
||||
this.internalGeneratePatch(colorPatchSize, new ColorItem(randomChoice(availableColors)));
|
||||
this.internalGeneratePatch(rng, colorPatchSize, new ColorItem(rng.choice(availableColors)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a shape patch
|
||||
* @param {RandomNumberGenerator} rng
|
||||
* @param {number} shapePatchSize
|
||||
* @param {number} distanceToOriginInChunks
|
||||
*/
|
||||
internalGenerateShapePatch(shapePatchSize, distanceToOriginInChunks) {
|
||||
internalGenerateShapePatch(rng, shapePatchSize, distanceToOriginInChunks) {
|
||||
/** @type {[enumSubShape, enumSubShape, enumSubShape, enumSubShape]} */
|
||||
let subShapes = null;
|
||||
|
||||
@@ -174,37 +171,38 @@ export class MapChunk {
|
||||
|
||||
if (distanceToOriginInChunks < 7) {
|
||||
// Initial chunk patches always have the same shape
|
||||
const subShape = this.internalGenerateRandomSubShape(weights);
|
||||
const subShape = this.internalGenerateRandomSubShape(rng, weights);
|
||||
subShapes = [subShape, subShape, subShape, subShape];
|
||||
} else if (distanceToOriginInChunks < 17) {
|
||||
// Later patches can also have mixed ones
|
||||
const subShapeA = this.internalGenerateRandomSubShape(weights);
|
||||
const subShapeB = this.internalGenerateRandomSubShape(weights);
|
||||
const subShapeA = this.internalGenerateRandomSubShape(rng, weights);
|
||||
const subShapeB = this.internalGenerateRandomSubShape(rng, weights);
|
||||
subShapes = [subShapeA, subShapeA, subShapeB, subShapeB];
|
||||
} else {
|
||||
// Finally there is a mix of everything
|
||||
subShapes = [
|
||||
this.internalGenerateRandomSubShape(weights),
|
||||
this.internalGenerateRandomSubShape(weights),
|
||||
this.internalGenerateRandomSubShape(weights),
|
||||
this.internalGenerateRandomSubShape(weights),
|
||||
this.internalGenerateRandomSubShape(rng, weights),
|
||||
this.internalGenerateRandomSubShape(rng, weights),
|
||||
this.internalGenerateRandomSubShape(rng, weights),
|
||||
this.internalGenerateRandomSubShape(rng, weights),
|
||||
];
|
||||
}
|
||||
|
||||
const definition = this.root.shapeDefinitionMgr.getDefinitionFromSimpleShapes(subShapes);
|
||||
this.internalGeneratePatch(shapePatchSize, new ShapeItem(definition));
|
||||
this.internalGeneratePatch(rng, shapePatchSize, new ShapeItem(definition));
|
||||
}
|
||||
|
||||
/**
|
||||
* Chooses a random shape with the given weights
|
||||
* @param {RandomNumberGenerator} rng
|
||||
* @param {Object.<enumSubShape, number>} weights
|
||||
* @returns {enumSubShape}
|
||||
*/
|
||||
internalGenerateRandomSubShape(weights) {
|
||||
internalGenerateRandomSubShape(rng, weights) {
|
||||
// @ts-ignore
|
||||
const sum = Object.values(weights).reduce((a, b) => a + b, 0);
|
||||
|
||||
const chosenNumber = randomInt(0, sum - 1);
|
||||
const chosenNumber = rng.nextIntRange(0, sum - 1);
|
||||
let accumulated = 0;
|
||||
for (const key in weights) {
|
||||
const weight = weights[key];
|
||||
@@ -222,7 +220,9 @@ export class MapChunk {
|
||||
* Generates the lower layer "terrain"
|
||||
*/
|
||||
generateLowerLayer() {
|
||||
if (this.generatePredefined()) {
|
||||
const rng = new RandomNumberGenerator(this.x + "|" + this.y + "|" + this.root.map.seed);
|
||||
|
||||
if (this.generatePredefined(rng)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -231,27 +231,28 @@ export class MapChunk {
|
||||
|
||||
// Determine how likely it is that there is a color patch
|
||||
const colorPatchChance = 0.9 - clamp(distanceToOriginInChunks / 25, 0, 1) * 0.5;
|
||||
if (Math_random() < colorPatchChance) {
|
||||
if (rng.next() < colorPatchChance) {
|
||||
const colorPatchSize = Math_max(2, Math_round(1 + clamp(distanceToOriginInChunks / 8, 0, 4)));
|
||||
this.internalGenerateColorPatch(colorPatchSize, distanceToOriginInChunks);
|
||||
this.internalGenerateColorPatch(rng, colorPatchSize, distanceToOriginInChunks);
|
||||
}
|
||||
|
||||
// Determine how likely it is that there is a shape patch
|
||||
const shapePatchChance = 0.9 - clamp(distanceToOriginInChunks / 25, 0, 1) * 0.5;
|
||||
if (Math_random() < shapePatchChance) {
|
||||
if (rng.next() < shapePatchChance) {
|
||||
const shapePatchSize = Math_max(2, Math_round(1 + clamp(distanceToOriginInChunks / 8, 0, 4)));
|
||||
this.internalGenerateShapePatch(shapePatchSize, distanceToOriginInChunks);
|
||||
this.internalGenerateShapePatch(rng, shapePatchSize, distanceToOriginInChunks);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this chunk has predefined contents, and if so returns true and generates the
|
||||
* predefined contents
|
||||
* @param {RandomNumberGenerator} rng
|
||||
* @returns {boolean}
|
||||
*/
|
||||
generatePredefined() {
|
||||
generatePredefined(rng) {
|
||||
if (this.x === 0 && this.y === 0) {
|
||||
this.internalGeneratePatch(2, new ColorItem(enumColors.red), 7, 7);
|
||||
this.internalGeneratePatch(rng, 2, new ColorItem(enumColors.red), 7, 7);
|
||||
return true;
|
||||
}
|
||||
if (this.x === -1 && this.y === 0) {
|
||||
@@ -261,7 +262,7 @@ export class MapChunk {
|
||||
enumSubShape.circle,
|
||||
enumSubShape.circle,
|
||||
]);
|
||||
this.internalGeneratePatch(2, new ShapeItem(definition), globalConfig.mapChunkSize - 9, 7);
|
||||
this.internalGeneratePatch(rng, 2, new ShapeItem(definition), globalConfig.mapChunkSize - 9, 7);
|
||||
return true;
|
||||
}
|
||||
if (this.x === 0 && this.y === -1) {
|
||||
@@ -271,12 +272,12 @@ export class MapChunk {
|
||||
enumSubShape.rect,
|
||||
enumSubShape.rect,
|
||||
]);
|
||||
this.internalGeneratePatch(2, new ShapeItem(definition), 5, globalConfig.mapChunkSize - 7);
|
||||
this.internalGeneratePatch(rng, 2, new ShapeItem(definition), 5, globalConfig.mapChunkSize - 7);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.x === -1 && this.y === -1) {
|
||||
this.internalGeneratePatch(2, new ColorItem(enumColors.green));
|
||||
this.internalGeneratePatch(rng, 2, new ColorItem(enumColors.green));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
import { Signal } from "../core/signal";
|
||||
import { RandomNumberGenerator } from "../core/rng";
|
||||
// import { gFactionRegistry } from "./global_registries";
|
||||
import { createLogger } from "../core/logging";
|
||||
|
||||
// Type hints
|
||||
@@ -11,12 +10,9 @@ import { GameTime } from "./time/game_time";
|
||||
import { EntityManager } from "./entity_manager";
|
||||
import { GameSystemManager } from "./game_system_manager";
|
||||
import { GameHUD } from "./hud/hud";
|
||||
// import { GameLogic } from "./game_logic";
|
||||
import { MapView } from "./map_view";
|
||||
import { Camera } from "./camera";
|
||||
// import { ParticleManager } from "../particles/particle_manager";
|
||||
import { InGameState } from "../states/ingame";
|
||||
// import { CanvasClickInterceptor } from "/canvas_click_interceptor";
|
||||
import { AutomaticSave } from "./automatic_save";
|
||||
import { Application } from "../application";
|
||||
import { SoundProxy } from "./sound_proxy";
|
||||
@@ -99,21 +95,12 @@ export class GameRoot {
|
||||
/** @type {GameTime} */
|
||||
this.time = null;
|
||||
|
||||
/** @type {PerlinNoise} */
|
||||
this.mapNoiseGenerator = null;
|
||||
|
||||
/** @type {HubGoals} */
|
||||
this.hubGoals = null;
|
||||
|
||||
/** @type {BufferMaintainer} */
|
||||
this.buffers = null;
|
||||
|
||||
// /** @type {ParticleManager} */
|
||||
// this.particleMgr = null;
|
||||
|
||||
// /** @type {ParticleManager} */
|
||||
// this.uiParticleMgr = null;
|
||||
|
||||
/** @type {CanvasClickInterceptor} */
|
||||
this.canvasClickInterceptor = null;
|
||||
|
||||
@@ -123,9 +110,6 @@ export class GameRoot {
|
||||
/** @type {SoundProxy} */
|
||||
this.soundProxy = null;
|
||||
|
||||
// /** @type {MinimapRenderer} */
|
||||
// this.minimapRenderer = null;
|
||||
|
||||
/** @type {ShapeDefinitionManager} */
|
||||
this.shapeDefinitionMgr = null;
|
||||
|
||||
@@ -147,7 +131,6 @@ export class GameRoot {
|
||||
// Game Hooks
|
||||
gameSaved: /** @type {TypedSignal<[]>} */ (new Signal()), // Game got saved
|
||||
gameRestored: /** @type {TypedSignal<[]>} */ (new Signal()), // Game got restored
|
||||
gameOver: /** @type {TypedSignal<[]>} */ (new Signal()), // Game over
|
||||
|
||||
storyGoalCompleted: /** @type {TypedSignal<[number, string]>} */ (new Signal()),
|
||||
upgradePurchased: /** @type {TypedSignal<[string]>} */ (new Signal()),
|
||||
@@ -182,20 +165,6 @@ export class GameRoot {
|
||||
this.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the root for game over, this sets the right flags and
|
||||
* detaches all signals so no bad stuff happens
|
||||
*/
|
||||
prepareGameOver() {
|
||||
this.gameInitialized = false;
|
||||
this.logicInitialized = false;
|
||||
// for (const key in this.signals) {
|
||||
// if (key !== "aboutToDestruct") {
|
||||
// this.signals[key].removeAll();
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the whole root and removes all properties
|
||||
*/
|
||||
|
||||
@@ -5,7 +5,7 @@ import { smoothenDpi } from "../core/dpi_manager";
|
||||
import { DrawParameters } from "../core/draw_parameters";
|
||||
import { createLogger } from "../core/logging";
|
||||
import { Vector } from "../core/vector";
|
||||
import { BasicSerializableObject } from "../savegame/serialization";
|
||||
import { BasicSerializableObject, types } from "../savegame/serialization";
|
||||
import { enumColors, enumColorsToHexCode, enumColorToShortcode, enumShortcodeToColor } from "./colors";
|
||||
|
||||
const rusha = require("rusha");
|
||||
@@ -74,6 +74,23 @@ export class ShapeDefinition extends BasicSerializableObject {
|
||||
return "ShapeDefinition";
|
||||
}
|
||||
|
||||
static getSchema() {
|
||||
return {};
|
||||
}
|
||||
|
||||
deserialize(data) {
|
||||
const errorCode = super.deserialize(data);
|
||||
if (errorCode) {
|
||||
return errorCode;
|
||||
}
|
||||
const definition = ShapeDefinition.fromShortKey(data);
|
||||
this.layers = definition.layers;
|
||||
}
|
||||
|
||||
serialize() {
|
||||
return this.getHash();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {object} param0
|
||||
|
||||
@@ -3,6 +3,7 @@ import { HubComponent } from "../components/hub";
|
||||
import { DrawParameters } from "../../core/draw_parameters";
|
||||
import { Entity } from "../entity";
|
||||
import { formatBigNumber } from "../../core/utils";
|
||||
import { enumHubGoalRewardToString } from "../tutorial_goals";
|
||||
|
||||
export class HubSystem extends GameSystemWithFilter {
|
||||
constructor(root) {
|
||||
@@ -77,7 +78,7 @@ export class HubSystem extends GameSystemWithFilter {
|
||||
context.font = "bold 11px GameFont";
|
||||
context.fillStyle = "#fd0752";
|
||||
context.textAlign = "center";
|
||||
context.fillText(goals.reward.toUpperCase(), pos.x, pos.y + 46);
|
||||
context.fillText(enumHubGoalRewardToString[goals.reward].toUpperCase(), pos.x, pos.y + 46);
|
||||
|
||||
// Level
|
||||
context.font = "bold 11px GameFont";
|
||||
|
||||
@@ -4,15 +4,30 @@ import { ShapeDefinition } from "./shape_definition";
|
||||
* @enum {string}
|
||||
*/
|
||||
export const enumHubGoalRewards = {
|
||||
reward_cutter_and_trash: "Cutting Shapes",
|
||||
reward_rotater: "Rotating",
|
||||
reward_painter: "Painting",
|
||||
reward_mixer: "Color Mixing",
|
||||
reward_stacker: "Combiner",
|
||||
reward_splitter: "Splitter/Merger",
|
||||
reward_tunnel: "Tunnel",
|
||||
reward_cutter_and_trash: "reward_cutter_and_trash",
|
||||
reward_rotater: "reward_rotater",
|
||||
reward_painter: "reward_painter",
|
||||
reward_mixer: "reward_mixer",
|
||||
reward_stacker: "reward_stacker",
|
||||
reward_splitter: "reward_splitter",
|
||||
reward_tunnel: "reward_tunnel",
|
||||
|
||||
no_reward: "Next level",
|
||||
no_reward: "no_reward",
|
||||
};
|
||||
|
||||
/**
|
||||
* @enum {string}
|
||||
*/
|
||||
export const enumHubGoalRewardToString = {
|
||||
[enumHubGoalRewards.reward_cutter_and_trash]: "Cutting Shapes",
|
||||
[enumHubGoalRewards.reward_rotater]: "Rotating",
|
||||
[enumHubGoalRewards.reward_painter]: "Painting",
|
||||
[enumHubGoalRewards.reward_mixer]: "Color Mixing",
|
||||
[enumHubGoalRewards.reward_stacker]: "Combiner",
|
||||
[enumHubGoalRewards.reward_splitter]: "Splitter/Merger",
|
||||
[enumHubGoalRewards.reward_tunnel]: "Tunnel",
|
||||
|
||||
[enumHubGoalRewards.no_reward]: "Next level",
|
||||
};
|
||||
|
||||
export const tutorialGoals = [
|
||||
|
||||
Reference in New Issue
Block a user