1
0
mirror of https://github.com/tobspr/shapez.io.git synced 2025-06-13 13:04:03 +00:00

Merge pull request #2 from EmeraldBlock/master

make things make sense
This commit is contained in:
EmeraldBlock 2020-09-28 15:54:30 -05:00 committed by GitHub
commit 1d65a7302a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
104 changed files with 18065 additions and 13877 deletions

4
.gitattributes vendored
View File

@ -1,4 +0,0 @@
*.wav filter=lfs diff=lfs merge=lfs -text
*.webm filter=lfs diff=lfs merge=lfs -text
*.mp3 filter=lfs diff=lfs merge=lfs -text
*.psd filter=lfs diff=lfs merge=lfs -text

View File

@ -22,8 +22,6 @@ Your goal is to produce shapes by cutting, rotating, merging and painting parts
## Building
- Make sure git `git lfs` extension is on your path
- Run `git lfs pull` to download sound assets
- Make sure `ffmpeg` is on your path
- Install Node.js and Yarn
- Run `yarn` in the root folder

1
gulp/.gitattributes vendored
View File

@ -1 +0,0 @@
*.wav filter=lfs diff=lfs merge=lfs -text

View File

@ -54,19 +54,19 @@ function gulptasksHTML($, gulp, buildFolder) {
document.head.appendChild(css);
// Append async css
const asyncCss = document.createElement("link");
asyncCss.rel = "stylesheet";
asyncCss.type = "text/css";
asyncCss.media = "none";
asyncCss.setAttribute("onload", "this.media='all'");
asyncCss.href = cachebust("async-resources.css");
if (integrity) {
asyncCss.setAttribute(
"integrity",
computeIntegrityHash(path.join(buildFolder, "async-resources.css"))
);
}
document.head.appendChild(asyncCss);
// const asyncCss = document.createElement("link");
// asyncCss.rel = "stylesheet";
// asyncCss.type = "text/css";
// asyncCss.media = "none";
// asyncCss.setAttribute("onload", "this.media='all'");
// asyncCss.href = cachebust("async-resources.css");
// if (integrity) {
// asyncCss.setAttribute(
// "integrity",
// computeIntegrityHash(path.join(buildFolder, "async-resources.css"))
// );
// }
// document.head.appendChild(asyncCss);
if (app) {
// Append cordova link

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.3 MiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 274 KiB

After

Width:  |  Height:  |  Size: 283 KiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 713 KiB

After

Width:  |  Height:  |  Size: 706 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:df7487fb5e8cb34cecee2519b9c3162a5107d2d7b1301c4a550904cfb108a015
size 223361394

BIN
res_raw/sounds/sfx/copy.wav Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@ -1,8 +1,24 @@
#ingame_HUD_BetaOverlay {
position: fixed;
@include S(top, 10px);
@include S(right, 15px);
left: 50%;
transform: translateX(-50%);
color: $colorRedBright;
@include Heading;
text-transform: uppercase;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
h2 {
@include PlainText;
}
span {
color: #555;
@include SuperSmallText;
}
}

View File

@ -178,6 +178,27 @@
display: list-item;
}
}
.ingameItemChooser {
@include S(margin, 20px, 0);
display: grid;
grid-auto-flow: column;
grid-auto-columns: 1fr;
@include S(grid-column-gap, 3px);
canvas {
pointer-events: all;
@include S(width, 25px);
@include S(height, 25px);
position: relative;
cursor: pointer;
@include IncreasedClickArea(3px);
&:hover {
opacity: 0.9;
}
}
}
}
> .buttons {

View File

@ -8,6 +8,7 @@ import { Signal } from "./signal";
import { SOUNDS, MUSIC } from "../platform/sound";
import { AtlasDefinition, atlasFiles } from "./atlas_definitions";
import { initBuildingCodesAfterResourcesLoaded } from "../game/meta_building_registry";
import { cachebust } from "./cachebust";
const logger = createLogger("background_loader");
@ -110,6 +111,7 @@ export class BackgroundResourcesLoader {
essentialBareGameSounds,
essentialBareGameAtlases
)
.then(() => this.internalPreloadCss("async-resources.scss"))
.catch(err => {
logger.warn("⏰ Failed to load essentials for bare game:", err);
})
@ -136,6 +138,21 @@ export class BackgroundResourcesLoader {
});
}
internalPreloadCss(name) {
return new Promise((resolve, reject) => {
const link = document.createElement("link");
link.onload = resolve;
link.onerror = reject;
link.setAttribute("rel", "stylesheet");
link.setAttribute("media", "all");
link.setAttribute("type", "text/css");
link.setAttribute("href", cachebust("async-resources.css"));
document.head.appendChild(link);
});
}
/**
* @param {Array<string>} sprites
* @param {Array<string>} sounds

View File

@ -20,6 +20,7 @@ export const THIRDPARTY_URLS = {
discord: "https://discord.gg/HN7EVzV",
github: "https://github.com/tobspr/shapez.io",
reddit: "https://www.reddit.com/r/shapezio",
shapeViewer: "https://viewer.shapez.io",
standaloneStorePage: "https://store.steampowered.com/app/1318690/shapezio/",
};

View File

@ -60,6 +60,8 @@ export class Dialog {
this.buttonSignals[buttonId] = new Signal();
}
this.valueChosen = new Signal();
this.timeouts = [];
this.clickDetectors = [];
@ -431,10 +433,12 @@ export class DialogWithForm extends Dialog {
for (let i = 0; i < this.formElements.length; ++i) {
const elem = this.formElements[i];
elem.bindEvents(div, this.clickDetectors);
elem.valueChosen.add(this.closeRequested.dispatch, this.closeRequested);
elem.valueChosen.add(this.valueChosen.dispatch, this.valueChosen);
}
waitNextFrame().then(() => {
this.formElements[0].focus();
this.formElements[this.formElements.length - 1].focus();
});
return div;

View File

@ -1,9 +1,13 @@
import { BaseItem } from "../game/base_item";
import { ClickDetector } from "./click_detector";
import { Signal } from "./signal";
export class FormElement {
constructor(id, label) {
this.id = id;
this.label = label;
this.valueChosen = new Signal();
}
getHtml() {
@ -148,3 +152,70 @@ export class FormElementCheckbox extends FormElement {
focus(parent) {}
}
export class FormElementItemChooser extends FormElement {
/**
*
* @param {object} param0
* @param {string} param0.id
* @param {string=} param0.label
* @param {Array<BaseItem>} param0.items
*/
constructor({ id, label, items = [] }) {
super(id, label);
this.items = items;
this.element = null;
/**
* @type {BaseItem}
*/
this.chosenItem = null;
}
getHtml() {
let classes = [];
return `
<div class="formElement">
${this.label ? `<label>${this.label}</label>` : ""}
<div class="ingameItemChooser input" data-formId="${this.id}"></div>
</div>
`;
}
/**
* @param {HTMLElement} parent
* @param {Array<ClickDetector>} clickTrackers
*/
bindEvents(parent, clickTrackers) {
this.element = this.getFormElement(parent);
for (let i = 0; i < this.items.length; ++i) {
const item = this.items[i];
const canvas = document.createElement("canvas");
canvas.width = 128;
canvas.height = 128;
const context = canvas.getContext("2d");
item.drawFullSizeOnCanvas(context, 128);
this.element.appendChild(canvas);
const detector = new ClickDetector(canvas, {});
clickTrackers.push(detector);
detector.click.add(() => {
this.chosenItem = item;
this.valueChosen.dispatch(item);
});
}
}
isValid() {
return true;
}
getValue() {
return null;
}
focus() {}
}

View File

@ -298,9 +298,7 @@ export function formatBigNumber(num, separator = T.global.decimalSeparator, prec
}
const suffix = T.global.suffix[bigNumberSuffixTranslationKeys[suffixIndex]];
const leadingDigitsRounded = Number(leadingDigits.toPrecision(precision));
const leadingDigitsNoTrailingDecimal = leadingDigitsRounded
.toString()
.replace(".", separator);
const leadingDigitsNoTrailingDecimal = leadingDigitsRounded.toString().replace(".", separator);
return sign + leadingDigitsNoTrailingDecimal + suffix;
}
@ -669,3 +667,14 @@ export function safeModulo(n, m) {
export function smoothPulse(time) {
return Math.sin(time * 4) * 0.5 + 0.5;
}
/**
* Fills in a <link> tag
* @param {string} translation
* @param {string} link
*/
export function fillInLinkIntoTranslation(translation, link) {
return translation
.replace("<link>", "<a href='" + link + "' target='_blank'>")
.replace("</link>", "</a>");
}

View File

@ -28,6 +28,15 @@ export class BaseItem extends BasicSerializableObject {
return "shape";
}
/**
* Returns a string id of the item
* @returns {string}
*/
getAsCopyableKey() {
abstract;
return "";
}
/**
* Returns if the item equals the other itme
* @param {BaseItem} other
@ -51,6 +60,15 @@ export class BaseItem extends BasicSerializableObject {
return false;
}
/**
* Draws the item to a canvas
* @param {CanvasRenderingContext2D} context
* @param {number} size
*/
drawFullSizeOnCanvas(context, size) {
abstract;
}
/**
* Draws the item at the given position
* @param {number} x

View File

@ -1,12 +1,11 @@
import { globalConfig } from "../core/config";
import { DrawParameters } from "../core/draw_parameters";
import { Loader } from "../core/loader";
import { createLogger } from "../core/logging";
import { findNiceIntegerValue } from "../core/utils";
import { Vector } from "../core/vector";
import { Entity } from "./entity";
import { GameRoot } from "./root";
import { findNiceIntegerValue } from "../core/utils";
import { blueprintShape } from "./upgrades";
import { globalConfig } from "../core/config";
const logger = createLogger("blueprint");

View File

@ -53,6 +53,6 @@ export class MetaWireTunnelBuilding extends MetaBuilding {
* @param {Entity} entity
*/
setupEntityComponents(entity) {
entity.addComponent(new WireTunnelComponent({}));
entity.addComponent(new WireTunnelComponent());
}
}

View File

@ -502,10 +502,10 @@ export class Camera extends BasicSerializableObject {
}
const prevZoom = this.zoomLevel;
const delta = Math.sign(event.deltaY) * -0.15 * this.root.app.settings.getScrollWheelSensitivity();
const scale = 1 + 0.15 * this.root.app.settings.getScrollWheelSensitivity();
assert(Number.isFinite(delta), "Got invalid delta in mouse wheel event: " + event.deltaY);
assert(Number.isFinite(this.zoomLevel), "Got invalid zoom level *before* wheel: " + this.zoomLevel);
this.zoomLevel *= 1 + delta;
this.zoomLevel *= (event.deltaY < 0) ? scale : 1/scale;
assert(Number.isFinite(this.zoomLevel), "Got invalid zoom level *after* wheel: " + this.zoomLevel);
this.clampZoomLevel();

View File

@ -1,8 +1,10 @@
import { globalConfig } from "../core/config";
import { RandomNumberGenerator } from "../core/rng";
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 { enumAnalyticsDataSource } from "./production_analytics";
import { GameRoot } from "./root";
import { enumSubShape, ShapeDefinition } from "./shape_definition";
import { enumHubGoalRewards, tutorialGoals } from "./tutorial_goals";
@ -18,12 +20,6 @@ export class HubGoals extends BasicSerializableObject {
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)),
}),
};
}
@ -53,15 +49,7 @@ export class HubGoals extends BasicSerializableObject {
}
// Compute current goal
const goal = tutorialGoals[this.level - 1];
if (goal) {
this.currentGoal = {
/** @type {ShapeDefinition} */
definition: this.root.shapeDefinitionMgr.getShapeFromShortKey(goal.shape),
required: goal.required,
reward: goal.reward,
};
}
this.computeNextGoal();
}
/**
@ -106,7 +94,7 @@ export class HubGoals extends BasicSerializableObject {
this.upgradeImprovements[key] = 1;
}
this.createNextGoal();
this.computeNextGoal();
// Allow quickly switching goals in dev mode
if (G_IS_DEV) {
@ -155,6 +143,13 @@ export class HubGoals extends BasicSerializableObject {
* Returns how much of the current goal was already delivered
*/
getCurrentGoalDelivered() {
if (this.currentGoal.throughputOnly) {
return this.root.productionAnalytics.getCurrentShapeRate(
enumAnalyticsDataSource.delivered,
this.currentGoal.definition
);
}
return this.getShapesStored(this.currentGoal.definition);
}
@ -189,9 +184,8 @@ export class HubGoals extends BasicSerializableObject {
this.root.signals.shapeDelivered.dispatch(definition);
// Check if we have enough for the next level
const targetHash = this.currentGoal.definition.getHash();
if (
this.storedShapes[targetHash] >= this.currentGoal.required ||
this.getCurrentGoalDelivered() >= this.currentGoal.required ||
(G_IS_DEV && globalConfig.debug.rewardsInstant)
) {
this.onGoalCompleted();
@ -201,24 +195,28 @@ export class HubGoals extends BasicSerializableObject {
/**
* Creates the next goal
*/
createNextGoal() {
computeNextGoal() {
const storyIndex = this.level - 1;
if (storyIndex < tutorialGoals.length) {
const { shape, required, reward } = tutorialGoals[storyIndex];
const { shape, required, reward, throughputOnly } = tutorialGoals[storyIndex];
this.currentGoal = {
/** @type {ShapeDefinition} */
definition: this.root.shapeDefinitionMgr.getShapeFromShortKey(shape),
required,
reward,
throughputOnly,
};
return;
}
const required = 4 + (this.level - 27) * 0.25;
this.currentGoal = {
/** @type {ShapeDefinition} */
definition: this.createRandomShape(),
required: findNiceIntegerValue(1000 + Math.pow(this.level * 2000, 0.8)),
definition: this.computeFreeplayShape(this.level),
required,
reward: enumHubGoalRewards.no_reward_freeplay,
throughputOnly: true,
};
}
@ -231,7 +229,7 @@ export class HubGoals extends BasicSerializableObject {
this.root.app.gameAnalytics.handleLevelCompleted(this.level);
++this.level;
this.createNextGoal();
this.computeNextGoal();
this.root.signals.storyGoalCompleted.dispatch(this.level - 1, reward);
}
@ -325,15 +323,85 @@ export class HubGoals extends BasicSerializableObject {
}
/**
* Picks random colors which are close to each other
* @param {RandomNumberGenerator} rng
*/
generateRandomColorSet(rng, allowUncolored = false) {
const colorWheel = [
enumColors.red,
enumColors.yellow,
enumColors.green,
enumColors.cyan,
enumColors.blue,
enumColors.purple,
enumColors.red,
enumColors.yellow,
];
const universalColors = [enumColors.white];
if (allowUncolored) {
universalColors.push(enumColors.uncolored);
}
const index = rng.nextIntRangeInclusive(0, colorWheel.length - 3);
const pickedColors = colorWheel.slice(index, index + 3);
pickedColors.push(rng.choice(universalColors));
return pickedColors;
}
/**
* Creates a (seeded) random shape
* @param {number} level
* @returns {ShapeDefinition}
*/
createRandomShape() {
computeFreeplayShape(level) {
const layerCount = clamp(this.level / 25, 2, 4);
/** @type {Array<import("./shape_definition").ShapeLayer>} */
let layers = [];
const randomColor = () => randomChoice(Object.values(enumColors));
const randomShape = () => randomChoice(Object.values(enumSubShape));
const rng = new RandomNumberGenerator(this.root.map.seed + "/" + level);
const colors = this.generateRandomColorSet(rng, level > 35);
let pickedSymmetry = null; // pairs of quadrants that must be the same
let availableShapes = [enumSubShape.rect, enumSubShape.circle, enumSubShape.star];
if (rng.next() < 0.5) {
pickedSymmetry = [
// radial symmetry
[0, 2],
[1, 3],
];
availableShapes.push(enumSubShape.windmill); // windmill looks good only in radial symmetry
} else {
const symmetries = [
[
// horizontal axis
[0, 3],
[1, 2],
],
[
// vertical axis
[0, 1],
[2, 3],
],
[
// diagonal axis
[0, 2],
[1],
[3],
],
[
// other diagonal axis
[1, 3],
[0],
[2],
],
];
pickedSymmetry = rng.choice(symmetries);
}
const randomColor = () => rng.choice(colors);
const randomShape = () => rng.choice(Object.values(enumSubShape));
let anyIsMissingTwo = false;
@ -341,23 +409,24 @@ export class HubGoals extends BasicSerializableObject {
/** @type {import("./shape_definition").ShapeLayer} */
const layer = [null, null, null, null];
for (let quad = 0; quad < 4; ++quad) {
for (let j = 0; j < pickedSymmetry.length; ++j) {
const group = pickedSymmetry[j];
const shape = randomShape();
const color = randomColor();
for (let k = 0; k < group.length; ++k) {
const quad = group[k];
layer[quad] = {
subShape: randomShape(),
color: randomColor(),
subShape: shape,
color,
};
}
// Sometimes shapes are missing
if (Math.random() > 0.85) {
layer[randomInt(0, 3)] = null;
}
// Sometimes they actually are missing *two* ones!
// Make sure at max only one layer is missing it though, otherwise we could
// create an uncreateable shape
if (Math.random() > 0.95 && !anyIsMissingTwo) {
layer[randomInt(0, 3)] = null;
if (level > 75 && rng.next() > 0.95 && !anyIsMissingTwo) {
layer[rng.nextIntRange(0, 4)] = null;
anyIsMissingTwo = true;
}

View File

@ -44,6 +44,8 @@ import { HUDWireInfo } from "./parts/wire_info";
import { HUDLeverToggle } from "./parts/lever_toggle";
import { HUDLayerPreview } from "./parts/layer_preview";
import { HUDMinerHighlight } from "./parts/miner_highlight";
import { HUDBetaOverlay } from "./parts/beta_overlay";
import { HUDPerformanceWarning } from "./parts/performance_warning";
export class GameHUD {
/**
@ -75,7 +77,6 @@ export class GameHUD {
pinnedShapes: new HUDPinnedShapes(this.root),
notifications: new HUDNotifications(this.root),
settingsMenu: new HUDSettingsMenu(this.root),
// betaOverlay: new HUDBetaOverlay(this.root),
debugInfo: new HUDDebugInfo(this.root),
dialogs: new HUDModalDialogs(this.root),
screenshotExporter: new HUDScreenshotExporter(this.root),
@ -85,6 +86,7 @@ export class GameHUD {
layerPreview: new HUDLayerPreview(this.root),
minerHighlight: new HUDMinerHighlight(this.root),
performanceWarning: new HUDPerformanceWarning(this.root),
// Typing hints
/* typehints:start */
@ -137,6 +139,10 @@ export class GameHUD {
this.parts.sandboxController = new HUDSandboxController(this.root);
}
if (!G_IS_RELEASE) {
this.parts.betaOverlay = new HUDBetaOverlay(this.root);
}
const frag = document.createDocumentFragment();
for (const key in this.parts) {
this.parts[key].createElements(frag);

View File

@ -3,7 +3,12 @@ import { makeDiv } from "../../../core/utils";
export class HUDBetaOverlay extends BaseHUDPart {
createElements(parent) {
this.element = makeDiv(parent, "ingame_HUD_BetaOverlay", [], "CLOSED BETA");
this.element = makeDiv(
parent,
"ingame_HUD_BetaOverlay",
[],
"<h2>CLOSED BETA VERSION</h2><span>This version is unstable, might crash and is not final!</span>"
);
}
initialize() {}

View File

@ -334,7 +334,11 @@ export class HUDBuildingPlacerLogic extends BaseHUDPart {
const tileBelow = this.root.map.getLowerLayerContentXY(tile.x, tile.y);
// Check if there's a shape or color item below, if so select the miner
if (tileBelow && this.root.app.settings.getAllSettings().pickMinerOnPatch) {
if (
tileBelow &&
this.root.app.settings.getAllSettings().pickMinerOnPatch &&
this.root.currentLayer === "regular"
) {
this.currentMetaBuilding.set(gMetaBuildingRegistry.findByClass(MetaMinerBuilding));
// Select chained miner if available, since that's always desired once unlocked

View File

@ -0,0 +1,16 @@
import { T } from "../../../translations";
import { BaseHUDPart } from "../base_hud_part";
export class HUDPerformanceWarning extends BaseHUDPart {
initialize() {
this.warningShown = false;
this.root.signals.entityManuallyPlaced.add(this.checkAfterPlace, this);
}
checkAfterPlace() {
if (!this.warningShown && this.root.entityMgr.entities.length > 10000) {
this.root.hud.parts.dialogs.showInfo(T.dialogs.entityWarning.title, T.dialogs.entityWarning.desc);
this.warningShown = true;
}
}
}

View File

@ -4,6 +4,8 @@ import { ShapeDefinition } from "../../shape_definition";
import { BaseHUDPart } from "../base_hud_part";
import { blueprintShape, UPGRADES } from "../../upgrades";
import { enumHubGoalRewards } from "../../tutorial_goals";
import { enumAnalyticsDataSource } from "../../production_analytics";
import { T } from "../../../translations";
/**
* Manages the pinned shapes on the left side of the screen
@ -22,11 +24,13 @@ export class HUDPinnedShapes extends BaseHUDPart {
* convenient. Also allows for cleaning up handles.
* @type {Array<{
* key: string,
* definition: ShapeDefinition,
* amountLabel: HTMLElement,
* lastRenderedValue: string,
* element: HTMLElement,
* detector?: ClickDetector,
* infoDetector?: ClickDetector
* infoDetector?: ClickDetector,
* throughputOnly?: boolean
* }>}
*/
this.handles = [];
@ -163,29 +167,40 @@ export class HUDPinnedShapes extends BaseHUDPart {
this.handles = [];
// Pin story goal
this.internalPinShape(currentKey, false, "goal");
this.internalPinShape({
key: currentKey,
canUnpin: false,
className: "goal",
throughputOnly: currentGoal.throughputOnly,
});
// Pin blueprint shape as well
if (this.root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_blueprints)) {
this.internalPinShape(blueprintShape, false, "blueprint");
this.internalPinShape({
key: blueprintShape,
canUnpin: false,
className: "blueprint",
});
}
// Pin manually pinned shapes
for (let i = 0; i < this.pinnedShapes.length; ++i) {
const key = this.pinnedShapes[i];
if (key !== currentKey) {
this.internalPinShape(key);
this.internalPinShape({ key });
}
}
}
/**
* Pins a new shape
* @param {string} key
* @param {boolean} canUnpin
* @param {string=} className
* @param {object} param0
* @param {string} param0.key
* @param {boolean=} param0.canUnpin
* @param {string=} param0.className
* @param {boolean=} param0.throughputOnly
*/
internalPinShape(key, canUnpin = true, className = null) {
internalPinShape({ key, canUnpin = true, className = null, throughputOnly = false }) {
const definition = this.root.shapeDefinitionMgr.getShapeFromShortKey(key);
const element = makeDiv(this.element, null, ["shape"]);
@ -229,11 +244,13 @@ export class HUDPinnedShapes extends BaseHUDPart {
this.handles.push({
key,
definition,
element,
amountLabel,
lastRenderedValue: "",
detector,
infoDetector,
throughputOnly,
});
}
@ -244,8 +261,20 @@ export class HUDPinnedShapes extends BaseHUDPart {
for (let i = 0; i < this.handles.length; ++i) {
const handle = this.handles[i];
const currentValue = this.root.hubGoals.getShapesStoredByKey(handle.key);
const currentValueFormatted = formatBigNumber(currentValue);
let currentValue = this.root.hubGoals.getShapesStoredByKey(handle.key);
let currentValueFormatted = formatBigNumber(currentValue);
if (handle.throughputOnly) {
currentValue = this.root.productionAnalytics.getCurrentShapeRate(
enumAnalyticsDataSource.delivered,
handle.definition
);
currentValueFormatted = T.ingame.statistics.shapesDisplayUnits.second.replace(
"<shapes>",
String(currentValue)
);
}
if (currentValueFormatted !== handle.lastRenderedValue) {
handle.lastRenderedValue = currentValueFormatted;
handle.amountLabel.innerText = currentValueFormatted;

View File

@ -113,7 +113,7 @@ export class HUDSandboxController extends BaseHUDPart {
modifyLevel(amount) {
const hubGoals = this.root.hubGoals;
hubGoals.level = Math.max(1, hubGoals.level + amount);
hubGoals.createNextGoal();
hubGoals.computeNextGoal();
// Clear all shapes of this level
hubGoals.storedShapes[hubGoals.currentGoal.definition.getHash()] = 0;

View File

@ -90,17 +90,15 @@ export class HUDShop extends BaseHUDPart {
// Max level
handle.elemDescription.innerText = T.ingame.shop.maximumLevel.replace(
"<currentMult>",
currentTierMultiplier.toString()
formatBigNumber(currentTierMultiplier)
);
continue;
}
// Set description
handle.elemDescription.innerText = T.shopUpgrades[upgradeId].description
.replace("<currentMult>", currentTierMultiplier.toString())
.replace("<newMult>", (currentTierMultiplier + tierHandle.improvement).toString())
// Backwards compatibility
.replace("<gain>", (tierHandle.improvement * 100.0).toString());
.replace("<currentMult>", formatBigNumber(currentTierMultiplier))
.replace("<newMult>", formatBigNumber(currentTierMultiplier + tierHandle.improvement));
tierHandle.required.forEach(({ shape, amount }) => {
const container = makeDiv(handle.elemRequirements, null, ["requirement"]);

View File

@ -4,11 +4,12 @@ import { makeDiv } from "../../../core/utils";
import { SOUNDS } from "../../../platform/sound";
import { T } from "../../../translations";
import { defaultBuildingVariant } from "../../meta_building";
import { enumHubGoalRewards } from "../../tutorial_goals";
import { enumHubGoalRewards, tutorialGoals } from "../../tutorial_goals";
import { BaseHUDPart } from "../base_hud_part";
import { DynamicDomAttach } from "../dynamic_dom_attach";
import { enumHubGoalRewardsToContentUnlocked } from "../../tutorial_goals_mappings";
import { InputReceiver } from "../../../core/input_receiver";
import { enumNotificationType } from "./notifications";
export class HUDUnlockNotification extends BaseHUDPart {
initialize() {
@ -50,6 +51,14 @@ export class HUDUnlockNotification extends BaseHUDPart {
* @param {enumHubGoalRewards} reward
*/
showForLevel(level, reward) {
if (level > tutorialGoals.length) {
this.root.hud.signals.notification.dispatch(
T.ingame.notifications.freeplayLevelComplete.replace("<level>", String(level)),
enumNotificationType.success
);
return;
}
this.root.app.inputMgr.makeSureAttachedAndOnTop(this.inputReciever);
this.elemTitle.innerText = T.ingame.levelCompleteNotification.levelTitle.replace(
"<level>",

View File

@ -1,12 +1,18 @@
import { makeOffscreenBuffer } from "../../../core/buffer_utils";
import { globalConfig, IS_DEMO } from "../../../core/config";
import { globalConfig, IS_DEMO, THIRDPARTY_URLS } from "../../../core/config";
import { DrawParameters } from "../../../core/draw_parameters";
import { Loader } from "../../../core/loader";
import { DialogWithForm } from "../../../core/modal_dialog_elements";
import { FormElementInput } from "../../../core/modal_dialog_forms";
import { Rectangle } from "../../../core/rectangle";
import { STOP_PROPAGATION } from "../../../core/signal";
import { arrayDeleteValue, lerp, makeDiv, removeAllChildren } from "../../../core/utils";
import {
arrayDeleteValue,
fillInLinkIntoTranslation,
lerp,
makeDiv,
removeAllChildren,
} from "../../../core/utils";
import { Vector } from "../../../core/vector";
import { T } from "../../../translations";
import { BaseItem } from "../../base_item";
@ -272,7 +278,7 @@ export class HUDWaypoints extends BaseHUDPart {
const dialog = new DialogWithForm({
app: this.root.app,
title: waypoint ? T.dialogs.createMarker.titleEdit : T.dialogs.createMarker.title,
desc: T.dialogs.createMarker.desc,
desc: fillInLinkIntoTranslation(T.dialogs.createMarker.desc, THIRDPARTY_URLS.shapeViewer),
formElements: [markerNameInput],
buttons: waypoint ? ["delete:bad", "cancel", "ok:good"] : ["cancel", "ok:good"],
});

View File

@ -1,13 +1,14 @@
import { makeOffscreenBuffer } from "../../../core/buffer_utils";
import { globalConfig } from "../../../core/config";
import { DrawParameters } from "../../../core/draw_parameters";
import { KEYMAPPINGS } from "../../key_action_mapper";
import { THEME } from "../../theme";
import { BaseHUDPart } from "../base_hud_part";
import { Loader } from "../../../core/loader";
import { lerp } from "../../../core/utils";
import { SOUNDS } from "../../../platform/sound";
import { KEYMAPPINGS } from "../../key_action_mapper";
import { enumHubGoalRewards } from "../../tutorial_goals";
import { BaseHUDPart } from "../base_hud_part";
const copy = require("clipboard-copy");
const wiresBackgroundDpi = 4;
export class HUDWiresOverlay extends BaseHUDPart {
@ -16,6 +17,7 @@ export class HUDWiresOverlay extends BaseHUDPart {
initialize() {
// Probably not the best location, but the one which makes most sense
this.root.keyMapper.getBinding(KEYMAPPINGS.ingame.switchLayers).add(this.switchLayers, this);
this.root.keyMapper.getBinding(KEYMAPPINGS.placement.copyWireValue).add(this.copyWireValue, this);
this.generateTilePattern();
@ -54,8 +56,54 @@ export class HUDWiresOverlay extends BaseHUDPart {
update() {
const desiredAlpha = this.root.currentLayer === "wires" ? 1.0 : 0.0;
// On low performance, skip the fade
if (this.root.entityMgr.entities.length > 5000 || this.root.dynamicTickrate.averageFps < 50) {
this.currentAlpha = desiredAlpha;
} else {
this.currentAlpha = lerp(this.currentAlpha, desiredAlpha, 0.12);
}
}
/**
* Copies the wires value below the cursor
*/
copyWireValue() {
if (this.root.currentLayer !== "wires") {
return;
}
const mousePos = this.root.app.mousePosition;
if (!mousePos) {
return;
}
const tile = this.root.camera.screenToWorld(mousePos).toTileSpace();
const contents = this.root.map.getLayerContentXY(tile.x, tile.y, "wires");
if (!contents) {
return;
}
let value = null;
if (contents.components.Wire) {
const network = contents.components.Wire.linkedNetwork;
if (network && network.hasValue()) {
value = network.currentValue;
}
}
if (contents.components.ConstantSignal) {
value = contents.components.ConstantSignal.signal;
}
if (value) {
copy(value.getAsCopyableKey());
this.root.soundProxy.playUi(SOUNDS.copy);
} else {
copy("");
this.root.soundProxy.playUiError();
}
}
/**
*

View File

@ -8,6 +8,9 @@ import { MetaVirtualProcessorBuilding } from "../../buildings/virtual_processor"
import { MetaTransistorBuilding } from "../../buildings/transistor";
import { MetaAnalyzerBuilding } from "../../buildings/analyzer";
import { MetaComparatorBuilding } from "../../buildings/comparator";
import { MetaReaderBuilding } from "../../buildings/reader";
import { MetaFilterBuilding } from "../../buildings/filter";
import { MetaDisplayBuilding } from "../../buildings/display";
export class HUDWiresToolbar extends HUDBaseToolbar {
constructor(root) {
@ -16,13 +19,18 @@ export class HUDWiresToolbar extends HUDBaseToolbar {
MetaWireBuilding,
MetaWireTunnelBuilding,
MetaConstantSignalBuilding,
MetaLeverBuilding,
MetaLogicGateBuilding,
MetaVirtualProcessorBuilding,
MetaAnalyzerBuilding,
MetaComparatorBuilding,
MetaTransistorBuilding,
],
secondaryBuildings: [
MetaReaderBuilding,
MetaLeverBuilding,
MetaFilterBuilding,
MetaDisplayBuilding,
],
visibilityCondition: () =>
!this.root.camera.getIsMapOverlayActive() && this.root.currentLayer === "wires",
htmlElementId: "ingame_HUD_wires_toolbar",

View File

@ -26,6 +26,13 @@ export class BooleanItem extends BaseItem {
return "boolean";
}
/**
* @returns {string}
*/
getAsCopyableKey() {
return this.value ? "1" : "0";
}
/**
* @param {number} value
*/
@ -56,6 +63,21 @@ export class BooleanItem extends BaseItem {
}
sprite.drawCachedCentered(parameters, x, y, diameter);
}
/**
* Draws the item to a canvas
* @param {CanvasRenderingContext2D} context
* @param {number} size
*/
drawFullSizeOnCanvas(context, size) {
let sprite;
if (this.value) {
sprite = Loader.getSprite("sprites/wires/boolean_true.png");
} else {
sprite = Loader.getSprite("sprites/wires/boolean_false.png");
}
sprite.drawCentered(context, size / 2, size / 2, size);
}
}
export const BOOL_FALSE_SINGLETON = new BooleanItem(0);

View File

@ -28,6 +28,13 @@ export class ColorItem extends BaseItem {
return "color";
}
/**
* @returns {string}
*/
getAsCopyableKey() {
return this.color;
}
/**
* @param {BaseItem} other
*/
@ -47,6 +54,18 @@ export class ColorItem extends BaseItem {
return THEME.map.resources[this.color];
}
/**
* Draws the item to a canvas
* @param {CanvasRenderingContext2D} context
* @param {number} size
*/
drawFullSizeOnCanvas(context, size) {
if (!this.cachedSprite) {
this.cachedSprite = Loader.getSprite("sprites/colors/" + this.color + ".png");
}
this.cachedSprite.drawCentered(context, size / 2, size / 2, size);
}
/**
* @param {number} x
* @param {number} y

View File

@ -27,6 +27,13 @@ export class ShapeItem extends BaseItem {
return "shape";
}
/**
* @returns {string}
*/
getAsCopyableKey() {
return this.definition.getHash();
}
/**
* @param {BaseItem} other
*/
@ -50,6 +57,15 @@ export class ShapeItem extends BaseItem {
return THEME.map.resources.shape;
}
/**
* Draws the item to a canvas
* @param {CanvasRenderingContext2D} context
* @param {number} size
*/
drawFullSizeOnCanvas(context, size) {
this.definition.drawFullSizeOnCanvas(context, size);
}
/**
* @param {number} x
* @param {number} y

View File

@ -67,12 +67,11 @@ export const KEYMAPPINGS = {
wire: { keyCode: key("1") },
wire_tunnel: { keyCode: key("2") },
constant_signal: { keyCode: key("3") },
lever_wires: { keyCode: key("4") },
logic_gate: { keyCode: key("5") },
virtual_processor: { keyCode: key("6") },
analyzer: { keyCode: key("7") },
comparator: { keyCode: key("8") },
transistor: { keyCode: key("9") },
logic_gate: { keyCode: key("4") },
virtual_processor: { keyCode: key("5") },
analyzer: { keyCode: key("6") },
comparator: { keyCode: key("7") },
transistor: { keyCode: key("8") },
},
placement: {
@ -82,6 +81,8 @@ export const KEYMAPPINGS = {
cycleBuildingVariants: { keyCode: key("T") },
cycleBuildings: { keyCode: 9 }, // TAB
switchDirectionLockSide: { keyCode: key("R") },
copyWireValue: { keyCode: key("Z") },
},
massSelect: {

View File

@ -297,6 +297,15 @@ export class ShapeDefinition extends BasicSerializableObject {
parameters.context.drawImage(canvas, x - diameter / 2, y - diameter / 2, diameter, diameter);
}
/**
* Draws the item to a canvas
* @param {CanvasRenderingContext2D} context
* @param {number} size
*/
drawFullSizeOnCanvas(context, size) {
this.internalGenerateShapeBuffer(null, context, size, size, 1);
}
/**
* Generates this shape as a canvas
* @param {number} size

View File

@ -1,6 +1,9 @@
import trim from "trim";
import { THIRDPARTY_URLS } from "../../core/config";
import { DialogWithForm } from "../../core/modal_dialog_elements";
import { FormElementInput } from "../../core/modal_dialog_forms";
import { FormElementInput, FormElementItemChooser } from "../../core/modal_dialog_forms";
import { fillInLinkIntoTranslation } from "../../core/utils";
import { T } from "../../translations";
import { BaseItem } from "../base_item";
import { enumColors } from "../colors";
import { ConstantSignalComponent } from "../components/constant_signal";
@ -9,6 +12,7 @@ import { GameSystemWithFilter } from "../game_system_with_filter";
import { BOOL_FALSE_SINGLETON, BOOL_TRUE_SINGLETON } from "../items/boolean_item";
import { COLOR_ITEM_SINGLETONS } from "../items/color_item";
import { ShapeDefinition } from "../shape_definition";
import { blueprintShape } from "../upgrades";
export class ConstantSignalSystem extends GameSystemWithFilter {
constructor(root) {
@ -41,23 +45,35 @@ export class ConstantSignalSystem extends GameSystemWithFilter {
const signalValueInput = new FormElementInput({
id: "signalValue",
label: null,
label: fillInLinkIntoTranslation(T.dialogs.editSignal.descShortKey, THIRDPARTY_URLS.shapeViewer),
placeholder: "",
defaultValue: "",
validator: val => this.parseSignalCode(val),
});
const itemInput = new FormElementItemChooser({
id: "signalItem",
label: null,
items: [
BOOL_FALSE_SINGLETON,
BOOL_TRUE_SINGLETON,
...Object.values(COLOR_ITEM_SINGLETONS),
this.root.shapeDefinitionMgr.getShapeItemFromShortKey(blueprintShape),
],
});
const dialog = new DialogWithForm({
app: this.root.app,
title: "Set Signal",
desc: "Enter a shape code, color or '0' or '1'",
formElements: [signalValueInput],
title: T.dialogs.editSignal.title,
desc: T.dialogs.editSignal.descItems,
formElements: [itemInput, signalValueInput],
buttons: ["cancel:bad:escape", "ok:good:enter"],
closeButton: false,
});
this.root.hud.parts.dialogs.internalShowDialog(dialog);
// When confirmed, set the signal
dialog.buttonSignals.ok.add(() => {
const closeHandler = () => {
if (!this.root || !this.root.entityMgr) {
// Game got stopped
return;
@ -75,8 +91,16 @@ export class ConstantSignalSystem extends GameSystemWithFilter {
return;
}
if (itemInput.chosenItem) {
console.log(itemInput.chosenItem);
constantComp.signal = itemInput.chosenItem;
} else {
constantComp.signal = this.parseSignalCode(signalValueInput.getValue());
});
}
};
dialog.buttonSignals.ok.add(closeHandler);
dialog.valueChosen.add(closeHandler);
// When cancelled, destroy the entity again
dialog.buttonSignals.cancel.add(() => {

View File

@ -1,15 +1,15 @@
import { globalConfig } from "../../core/config";
import { smoothenDpi } from "../../core/dpi_manager";
import { DrawParameters } from "../../core/draw_parameters";
import { drawSpriteClipped } from "../../core/draw_utils";
import { Loader } from "../../core/loader";
import { Rectangle } from "../../core/rectangle";
import { ORIGINAL_SPRITE_SCALE } from "../../core/sprites";
import { formatBigNumber } from "../../core/utils";
import { T } from "../../translations";
import { HubComponent } from "../components/hub";
import { Entity } from "../entity";
import { GameSystemWithFilter } from "../game_system_with_filter";
import { globalConfig } from "../../core/config";
import { smoothenDpi } from "../../core/dpi_manager";
import { drawSpriteClipped } from "../../core/draw_utils";
import { Rectangle } from "../../core/rectangle";
import { ORIGINAL_SPRITE_SCALE } from "../../core/sprites";
const HUB_SIZE_TILES = 4;
const HUB_SIZE_PIXELS = HUB_SIZE_TILES * globalConfig.tileSize;
@ -73,14 +73,24 @@ export class HubSystem extends GameSystemWithFilter {
const textOffsetX = 70;
const textOffsetY = 61;
if (goals.throughputOnly) {
// Throughput
const deliveredText = T.ingame.statistics.shapesDisplayUnits.second.replace(
"<shapes>",
formatBigNumber(goals.required)
);
context.font = "bold 12px GameFont";
context.fillStyle = "#64666e";
context.textAlign = "left";
context.fillText(deliveredText, textOffsetX, textOffsetY);
} else {
// Deliver count
const delivered = this.root.hubGoals.getCurrentGoalDelivered();
const deliveredText = "" + formatBigNumber(delivered);
if (delivered > 9999) {
if (delivered > 999) {
context.font = "bold 16px GameFont";
} else if (delivered > 999) {
context.font = "bold 20px GameFont";
} else {
context.font = "bold 25px GameFont";
}
@ -92,6 +102,7 @@ export class HubSystem extends GameSystemWithFilter {
context.font = "13px GameFont";
context.fillStyle = "#a4a6b0";
context.fillText("/ " + formatBigNumber(goals.required), textOffsetX, textOffsetY + 13);
}
// Reward
const rewardText = T.storyRewards[goals.reward].title.toUpperCase();

View File

@ -3,7 +3,7 @@ import { enumColors } from "../colors";
import { enumLogicGateType, LogicGateComponent } from "../components/logic_gate";
import { enumPinSlotType } from "../components/wired_pins";
import { GameSystemWithFilter } from "../game_system_with_filter";
import { BOOL_FALSE_SINGLETON, BOOL_TRUE_SINGLETON, isTruthyItem } from "../items/boolean_item";
import { BOOL_FALSE_SINGLETON, BOOL_TRUE_SINGLETON, BooleanItem, isTruthyItem } from "../items/boolean_item";
import { ColorItem, COLOR_ITEM_SINGLETONS } from "../items/color_item";
import { ShapeItem } from "../items/shape_item";
import { ShapeDefinition } from "../shape_definition";

View File

@ -143,10 +143,10 @@ export const tutorialGoals = [
// 14
// Belt reader
{
// @todo
shape: "CuCuCuCu",
required: 0,
shape: "--Cg----:--Cr----", // unused
required: 16, // Per second!
reward: enumHubGoalRewards.reward_belt_reader,
throughputOnly: true,
},
// 15
@ -176,8 +176,7 @@ export const tutorialGoals = [
// 18
// Rotater (180deg)
{
// @TODO
shape: "CuCuCuCu",
shape: "Sg----Sg:CgCgCgCg:--CyCy--", // unused
required: 20000,
reward: enumHubGoalRewards.reward_rotater_180,
},
@ -185,8 +184,7 @@ export const tutorialGoals = [
// 19
// Compact splitter
{
// @TODO
shape: "CuCuCuCu",
shape: "CpRpCp--:SwSwSwSw",
required: 25000,
reward: enumHubGoalRewards.reward_splitter,
},
@ -195,15 +193,14 @@ export const tutorialGoals = [
// WIRES
{
shape: finalGameShape,
required: 50000,
required: 25000,
reward: enumHubGoalRewards.reward_wires_filters_and_levers,
},
// 21
// Display
{
// @TODO
shape: "CuCuCuCu",
shape: "CrCrCrCr:CwCwCwCw:CrCrCrCr:CwCwCwCw",
required: 25000,
reward: enumHubGoalRewards.reward_display,
},
@ -211,43 +208,37 @@ export const tutorialGoals = [
// 22
// Constant signal
{
// @TODO
shape: "CuCuCuCu",
required: 30000,
shape: "Cg----Cr:Cw----Cw:Sy------:Cy----Cy",
required: 25000,
reward: enumHubGoalRewards.reward_constant_signal,
},
// 23
// Quad Painter
{
// @TODO
shape: "CuCuCuCu",
// shape: "WrRgWrRg:CwCrCwCr:SgSgSgSg", // processors t4 (two variants)
required: 35000,
shape: "CcSyCcSy:SyCcSyCc:CcSyCcSy",
required: 5000,
reward: enumHubGoalRewards.reward_painter_quad,
},
// 24 Logic gates
{
// @TODO
shape: "CuCuCuCu",
required: 40000,
shape: "CcRcCcRc:RwCwRwCw:Sr--Sw--:CyCyCyCy",
required: 10000,
reward: enumHubGoalRewards.reward_logic_gates,
},
// 25 Virtual Processing
{
// @TODO
shape: "CuCuCuCu",
required: 45000,
shape: "Rg--Rg--:CwRwCwRw:--Rg--Rg",
required: 10000,
reward: enumHubGoalRewards.reward_virtual_processing,
},
// 26 Freeplay
{
// @TODO
shape: "CuCuCuCu",
required: 100000,
shape: "CbCuCbCu:Sr------:--CrSrCr:CwCwCwCw",
required: 10000,
reward: enumHubGoalRewards.reward_freeplay,
},
];

View File

@ -2,10 +2,28 @@ import { findNiceIntegerValue } from "../core/utils";
import { ShapeDefinition } from "./shape_definition";
export const finalGameShape = "RuCw--Cw:----Ru--";
export const rocketShape = "CbCuCbCu:Sr------:--CrSrCr:CwCwCwCw";
export const blueprintShape = "CbCbCbRb:CwCwCwCw";
const fixedImprovements = [0.5, 0.5, 1, 1, 2, 2];
const numEndgameUpgrades = G_IS_DEV || G_IS_STANDALONE ? 20 - fixedImprovements.length - 1 : 0;
function generateEndgameUpgrades() {
return new Array(numEndgameUpgrades).fill(null).map((_, i) => ({
required: [
{ shape: blueprintShape, amount: 30000 + i * 10000 },
{ shape: finalGameShape, amount: 20000 + i * 5000 },
{ shape: rocketShape, amount: 20000 + i * 5000 },
],
excludePrevious: true,
}));
}
for (let i = 0; i < numEndgameUpgrades; ++i) {
fixedImprovements.push(0.1);
}
/** @typedef {{
* shape: string,
* amount: number
@ -41,6 +59,7 @@ export const UPGRADES = {
required: [{ shape: finalGameShape, amount: 50000 }],
excludePrevious: true,
},
...generateEndgameUpgrades(),
],
miner: [
@ -63,6 +82,7 @@ export const UPGRADES = {
required: [{ shape: finalGameShape, amount: 50000 }],
excludePrevious: true,
},
...generateEndgameUpgrades(),
],
processors: [
@ -85,6 +105,7 @@ export const UPGRADES = {
required: [{ shape: finalGameShape, amount: 50000 }],
excludePrevious: true,
},
...generateEndgameUpgrades(),
],
painting: [
@ -107,6 +128,7 @@ export const UPGRADES = {
required: [{ shape: finalGameShape, amount: 50000 }],
excludePrevious: true,
},
...generateEndgameUpgrades(),
],
};

3
src/js/globals.d.ts vendored
View File

@ -19,9 +19,6 @@ declare const G_BUILD_VERSION: string;
declare const G_ALL_UI_IMAGES: Array<string>;
declare const G_IS_RELEASE: boolean;
// Node require
declare function require(...args): any;
// Polyfills
declare interface String {
replaceAll(search: string, replacement: string): string;

View File

@ -25,6 +25,7 @@ export const SOUNDS = {
destroyBuilding: "destroy_building",
placeBuilding: "place_building",
placeBelt: "place_belt",
copy: "copy",
};
export const MUSIC = {

View File

@ -19,6 +19,7 @@ import { getCodeFromBuildingData } from "../../game/building_codes.js";
import { StaticMapEntityComponent } from "../../game/components/static_map_entity.js";
import { Entity } from "../../game/entity.js";
import { defaultBuildingVariant, MetaBuilding } from "../../game/meta_building.js";
import { finalGameShape } from "../../game/upgrades.js";
import { SavegameInterface_V1005 } from "./1005.js";
const schema = require("./1006.json");
@ -151,11 +152,26 @@ export class SavegameInterface_V1006 extends SavegameInterface_V1005 {
stored[shapeKey] = rebalance(stored[shapeKey]);
}
stored[finalGameShape] = 0;
// Reduce goals
if (dump.hubGoals.currentGoal) {
dump.hubGoals.currentGoal.required = rebalance(dump.hubGoals.currentGoal.required);
}
let level = Math.min(19, dump.hubGoals.level);
const levelMapping = {
14: 15,
15: 16,
16: 17,
17: 18,
18: 19,
19: 20,
};
dump.hubGoals.level = levelMapping[level] || level;
// Update entities
const entities = dump.entities;
for (let i = 0; i < entities.length; ++i) {

View File

@ -3,7 +3,6 @@
const fs = require("fs");
const matchAll = require("match-all");
const path = require("path");
const YAWN = require("yawn-yaml/cjs");
const YAML = require("yaml");
const files = fs
@ -55,8 +54,6 @@ function match(originalObj, translatedObj, path = "/") {
} else {
console.warn(" | Unknown type: ", typeof valueOriginal);
}
// const matching = translatedObj[key];
}
for (const key in translatedObj) {
@ -71,12 +68,13 @@ for (let i = 0; i < files.length; ++i) {
const filePath = path.join(__dirname, "translations", files[i]);
console.log("Processing", files[i]);
const translatedContents = fs.readFileSync(filePath).toString("utf-8");
const translated = YAML.parse(translatedContents);
const handle = new YAWN(translatedContents);
const json = handle.json;
const json = YAML.parse(translatedContents);
match(original, json, "/");
handle.json = json;
fs.writeFileSync(filePath, handle.yaml, "utf-8");
const stringified = YAML.stringify(json, {
indent: 4,
simpleKeys: true,
});
fs.writeFileSync(filePath, stringified, "utf-8");
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -268,7 +268,13 @@ dialogs:
createMarker:
title: New Marker
titleEdit: Edit Marker
desc: Give it a meaningful name, you can also include a <strong>short key</strong> of a shape (Which you can generate <a href="https://viewer.shapez.io" target="_blank">here</a>)
desc: Give it a meaningful name, you can also include a <strong>short key</strong> of a shape (Which you can generate <link>here</link>)
editSignal:
title: Set Signal
descItems: >-
Choose a pre-defined item:
descShortKey: ... or enter the <strong>short key</strong> of a shape (Which you can generate <link>here</link>)
markerDemoLimit:
desc: You can only create two custom markers in the demo. Get the standalone for unlimited markers!
@ -281,6 +287,10 @@ dialogs:
title: Rename Savegame
desc: You can rename your savegame here.
entityWarning:
title: Performance Warning
desc: You have placed a lot of buildings, this is just a friendly reminder that the game can not handle an endless count of buildings - So try to keep your factories compact!
ingame:
# This is shown in the top left corner and displays useful keybindings in
# every situation
@ -350,6 +360,7 @@ ingame:
notifications:
newUpgrade: A new upgrade is available!
gameSaved: Your game has been saved.
freeplayLevelComplete: Level <level> has been completed!
# The "Upgrades" window
shop:
@ -360,7 +371,8 @@ ingame:
tier: Tier <x>
# The roman number for each tier
tierLabels: [I, II, III, IV, V, VI, VII, VIII, IX, X]
tierLabels:
[I, II, III, IV, V, VI, VII, VIII, IX, X, XI, XII, XIII, XIV, XV, XVI, XVII, XVIII, XIX, XX]
maximumLevel: MAXIMUM LEVEL (Speed x<currentMult>)
@ -788,13 +800,13 @@ storyRewards:
no_reward_freeplay:
title: Next level
desc: >-
Congratulations! By the way, more content is planned for the standalone!
Congratulations!
reward_freeplay:
title: Freeplay
desc: >-
You did it! You unlocked the <strong>free-play mode</strong>! This means that shapes are now <strong>randomly</strong> generated!<br><br>
Since the hub will only require low quantities from now on, I highly recommend to build a machine which automatically delivers the requested shape!<br><br>
Since the hub will require a <strong>throughput</strong> from now on, I highly recommend to build a machine which automatically delivers the requested shape!<br><br>
The HUB outputs the requested shape on the wires layer, so all you have to do is to analyze it and automatically configure your factory based on that.
settings:
@ -1039,8 +1051,7 @@ keybindings:
wire: *wire
constant_signal: *constant_signal
logic_gate: Logic Gate
lever: Switch (regular)
lever_wires: Switch (wires)
lever: *lever
filter: *filter
wire_tunnel: *wire_tunnel
display: *display
@ -1062,7 +1073,8 @@ keybindings:
lockBeltDirection: Enable belt planner
switchDirectionLockSide: >-
Planner: Switch side
copyWireValue: >-
Wires: Copy value below cursor
massSelectStart: Hold and drag to start
massSelectSelectMultiple: Select multiple areas
massSelectCopy: Copy area

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More