mirror of
https://github.com/tobspr/shapez.io.git
synced 2026-03-02 03:39:21 +00:00
Merge branch 'master' of https://github.com/tobspr/shapez.io
This commit is contained in:
@@ -57,6 +57,15 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .buildingsButton {
|
||||
display: grid;
|
||||
align-items: center;
|
||||
@include S(margin-top, 4px);
|
||||
> button {
|
||||
@include SuperSmallText;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
> .section {
|
||||
display: grid;
|
||||
@include S(grid-gap, 10px);
|
||||
@include S(grid-gap, 5px);
|
||||
grid-auto-flow: row;
|
||||
|
||||
> button {
|
||||
|
||||
@@ -72,8 +72,8 @@ export const globalConfig = {
|
||||
|
||||
readerAnalyzeIntervalSeconds: 10,
|
||||
|
||||
goalAcceptorMinimumDurationSeconds: 5,
|
||||
goalAcceptorsPerProducer: 4.5,
|
||||
goalAcceptorItemsRequired: 10,
|
||||
goalAcceptorsPerProducer: 5,
|
||||
puzzleModeSpeed: 3,
|
||||
puzzleMinBoundsSize: 2,
|
||||
puzzleMaxBoundsSize: 20,
|
||||
|
||||
@@ -101,8 +101,11 @@ export class Blueprint {
|
||||
const entity = this.entities[i];
|
||||
const staticComp = entity.components.StaticMapEntity;
|
||||
|
||||
staticComp.rotation = (staticComp.rotation + 90) % 360;
|
||||
staticComp.originalRotation = (staticComp.originalRotation + 90) % 360;
|
||||
if (staticComp.getMetaBuilding().getIsRotateable()) {
|
||||
staticComp.rotation = (staticComp.rotation + 90) % 360;
|
||||
staticComp.originalRotation = (staticComp.originalRotation + 90) % 360;
|
||||
}
|
||||
|
||||
staticComp.origin = staticComp.origin.rotateFastMultipleOf90(90);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,19 +31,26 @@ export class GoalAcceptorComponent extends Component {
|
||||
|
||||
clear() {
|
||||
// the last items we delivered
|
||||
/** @type {{ item: BaseItem; time: number; }[]} */
|
||||
this.deliveryHistory = [];
|
||||
/** @type {{ item: BaseItem; time: number; }} */
|
||||
this.lastDelivery = null;
|
||||
|
||||
this.currentDeliveredItems = 0;
|
||||
|
||||
// Used for animations
|
||||
this.displayPercentage = 0;
|
||||
}
|
||||
|
||||
getRequiredDeliveryHistorySize() {
|
||||
// clears items but doesn't instantly reset the progress bar
|
||||
clearItems() {
|
||||
this.lastDelivery = null;
|
||||
|
||||
this.currentDeliveredItems = 0;
|
||||
}
|
||||
|
||||
getRequiredSecondsPerItem() {
|
||||
return (
|
||||
(globalConfig.puzzleModeSpeed *
|
||||
globalConfig.goalAcceptorMinimumDurationSeconds *
|
||||
globalConfig.beltSpeedItemsPerSecond) /
|
||||
globalConfig.goalAcceptorsPerProducer
|
||||
globalConfig.goalAcceptorsPerProducer /
|
||||
(globalConfig.puzzleModeSpeed * globalConfig.beltSpeedItemsPerSecond)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,8 +166,8 @@ export class GameMode extends BasicSerializableObject {
|
||||
}
|
||||
|
||||
/** @returns {boolean} */
|
||||
getSupportsCopyPaste() {
|
||||
return true;
|
||||
getHasFreeCopyPaste() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @returns {boolean} */
|
||||
|
||||
@@ -27,6 +27,8 @@ export class HUDBlueprintPlacer extends BaseHUDPart {
|
||||
}
|
||||
|
||||
initialize() {
|
||||
this.isCopyPasteFree = this.root.gameMode.getHasFreeCopyPaste();
|
||||
|
||||
this.root.hud.signals.buildingsSelectedForCopy.add(this.createBlueprintFromBuildings, this);
|
||||
|
||||
/** @type {TypedTrackedState<Blueprint?>} */
|
||||
@@ -82,7 +84,7 @@ export class HUDBlueprintPlacer extends BaseHUDPart {
|
||||
|
||||
update() {
|
||||
const currentBlueprint = this.currentBlueprint.get();
|
||||
this.domAttach.update(currentBlueprint && currentBlueprint.getCost() > 0);
|
||||
this.domAttach.update(!this.isCopyPasteFree && currentBlueprint && currentBlueprint.getCost() > 0);
|
||||
this.trackedCanAfford.set(currentBlueprint && currentBlueprint.canAfford(this.root));
|
||||
}
|
||||
|
||||
@@ -114,7 +116,7 @@ export class HUDBlueprintPlacer extends BaseHUDPart {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!blueprint.canAfford(this.root)) {
|
||||
if (!this.isCopyPasteFree && !blueprint.canAfford(this.root)) {
|
||||
this.root.soundProxy.playUiError();
|
||||
return;
|
||||
}
|
||||
@@ -122,8 +124,10 @@ export class HUDBlueprintPlacer extends BaseHUDPart {
|
||||
const worldPos = this.root.camera.screenToWorld(pos);
|
||||
const tile = worldPos.toTileSpace();
|
||||
if (blueprint.tryPlace(this.root, tile)) {
|
||||
const cost = blueprint.getCost();
|
||||
this.root.hubGoals.takeShapeByKey(this.root.gameMode.getBlueprintShapeKey(), cost);
|
||||
if (!this.isCopyPasteFree) {
|
||||
const cost = blueprint.getCost();
|
||||
this.root.hubGoals.takeShapeByKey(this.root.gameMode.getBlueprintShapeKey(), cost);
|
||||
}
|
||||
this.root.soundProxy.playUi(SOUNDS.placeBuilding);
|
||||
}
|
||||
return STOP_PROPAGATION;
|
||||
@@ -131,7 +135,7 @@ export class HUDBlueprintPlacer extends BaseHUDPart {
|
||||
}
|
||||
|
||||
/**
|
||||
* Mose move handler
|
||||
* Mouse move handler
|
||||
*/
|
||||
onMouseMove() {
|
||||
// Prevent movement while blueprint is selected
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { globalConfig } from "../../../core/config";
|
||||
import { DrawParameters } from "../../../core/draw_parameters";
|
||||
import { gMetaBuildingRegistry } from "../../../core/global_registries";
|
||||
import { createLogger } from "../../../core/logging";
|
||||
import { STOP_PROPAGATION } from "../../../core/signal";
|
||||
import { formatBigNumberFull } from "../../../core/utils";
|
||||
@@ -7,6 +8,8 @@ import { Vector } from "../../../core/vector";
|
||||
import { ACHIEVEMENTS } from "../../../platform/achievement_provider";
|
||||
import { T } from "../../../translations";
|
||||
import { Blueprint } from "../../blueprint";
|
||||
import { MetaBlockBuilding } from "../../buildings/block";
|
||||
import { MetaConstantProducerBuilding } from "../../buildings/constant_producer";
|
||||
import { enumMouseButton } from "../../camera";
|
||||
import { Component } from "../../component";
|
||||
import { Entity } from "../../entity";
|
||||
@@ -260,7 +263,14 @@ export class HUDMassSelector extends BaseHUDPart {
|
||||
for (let x = realTileStart.x; x <= realTileEnd.x; ++x) {
|
||||
for (let y = realTileStart.y; y <= realTileEnd.y; ++y) {
|
||||
const contents = this.root.map.getLayerContentXY(x, y, this.root.currentLayer);
|
||||
|
||||
if (contents && this.root.logic.canDeleteBuilding(contents)) {
|
||||
const staticComp = contents.components.StaticMapEntity;
|
||||
|
||||
if (!staticComp.getMetaBuilding().getIsRemovable(this.root)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
this.selectedUids.add(contents.uid);
|
||||
}
|
||||
}
|
||||
@@ -320,6 +330,11 @@ export class HUDMassSelector extends BaseHUDPart {
|
||||
renderedUids.add(uid);
|
||||
|
||||
const staticComp = contents.components.StaticMapEntity;
|
||||
|
||||
if (!staticComp.getMetaBuilding().getIsRemovable(this.root)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const bounds = staticComp.getTileSpaceBounds();
|
||||
parameters.context.beginRoundedRect(
|
||||
bounds.x * globalConfig.tileSize + boundsBorder,
|
||||
|
||||
@@ -216,8 +216,8 @@ export class HUDPuzzleEditorReview extends BaseHUDPart {
|
||||
if (!goalComp.item) {
|
||||
return T.puzzleMenu.validation.goalAcceptorNoItem;
|
||||
}
|
||||
const required = goalComp.getRequiredDeliveryHistorySize();
|
||||
if (goalComp.deliveryHistory.length < required) {
|
||||
const required = globalConfig.goalAcceptorItemsRequired;
|
||||
if (goalComp.currentDeliveredItems < required) {
|
||||
return T.puzzleMenu.validation.goalAcceptorRateNotMet;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ import { makeDiv } from "../../../core/utils";
|
||||
import { T } from "../../../translations";
|
||||
import { StaticMapEntityComponent } from "../../components/static_map_entity";
|
||||
import { BaseHUDPart } from "../base_hud_part";
|
||||
import { gMetaBuildingRegistry } from "../../../core/global_registries";
|
||||
import { MetaBlockBuilding } from "../../buildings/block";
|
||||
|
||||
const logger = createLogger("puzzle-editor");
|
||||
|
||||
@@ -43,8 +45,13 @@ export class HUDPuzzleEditorSettings extends BaseHUDPart {
|
||||
|
||||
<div class="buttonBar">
|
||||
<button class="styledButton trim">${T.ingame.puzzleEditorSettings.trimZone}</button>
|
||||
<button class="styledButton clear">${T.ingame.puzzleEditorSettings.clearItems}</button>
|
||||
<button class="styledButton clearItems">${T.ingame.puzzleEditorSettings.clearItems}</button>
|
||||
</div>
|
||||
|
||||
<div class="buildingsButton">
|
||||
<button class="styledButton clearBuildings">${T.ingame.puzzleEditorSettings.clearBuildings}</button>
|
||||
</div>
|
||||
|
||||
</div>`
|
||||
);
|
||||
|
||||
@@ -53,14 +60,35 @@ export class HUDPuzzleEditorSettings extends BaseHUDPart {
|
||||
bind(".zoneHeight .minus", () => this.modifyZone(0, -1));
|
||||
bind(".zoneHeight .plus", () => this.modifyZone(0, 1));
|
||||
bind("button.trim", this.trim);
|
||||
bind("button.clear", this.clear);
|
||||
bind("button.clearItems", this.clearItems);
|
||||
bind("button.clearBuildings", this.clearBuildings);
|
||||
}
|
||||
}
|
||||
|
||||
clear() {
|
||||
clearItems() {
|
||||
this.root.logic.clearAllBeltsAndItems();
|
||||
}
|
||||
|
||||
clearBuildings() {
|
||||
for (const entity of this.root.entityMgr.getAllWithComponent(StaticMapEntityComponent)) {
|
||||
const staticComp = entity.components.StaticMapEntity;
|
||||
const signalComp = entity.components.ConstantSignal;
|
||||
const goalComp = entity.components.GoalAcceptor;
|
||||
|
||||
if (
|
||||
signalComp ||
|
||||
goalComp ||
|
||||
staticComp.getMetaBuilding().id === gMetaBuildingRegistry.findByClass(MetaBlockBuilding).id
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
this.root.map.removeStaticEntity(entity);
|
||||
this.root.entityMgr.destroyEntity(entity);
|
||||
}
|
||||
this.root.entityMgr.processDestroyList();
|
||||
}
|
||||
|
||||
trim() {
|
||||
// Now, find the center
|
||||
const buildings = this.root.entityMgr.entities.slice();
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { gMetaBuildingRegistry } from "../../../core/global_registries";
|
||||
import { createLogger } from "../../../core/logging";
|
||||
import { makeDiv } from "../../../core/utils";
|
||||
import { T } from "../../../translations";
|
||||
import { MetaBlockBuilding } from "../../buildings/block";
|
||||
import { StaticMapEntityComponent } from "../../components/static_map_entity";
|
||||
import { BaseHUDPart } from "../base_hud_part";
|
||||
|
||||
const logger = createLogger("puzzle-play");
|
||||
@@ -17,19 +20,41 @@ export class HUDPuzzlePlaySettings extends BaseHUDPart {
|
||||
null,
|
||||
["section"],
|
||||
`
|
||||
<button class="styledButton clear">${T.ingame.puzzleEditorSettings.clearItems}</button>
|
||||
<button class="styledButton clearItems">${T.ingame.puzzleEditorSettings.clearItems}</button>
|
||||
<button class="styledButton clearBuildings">${T.ingame.puzzleEditorSettings.clearBuildings}</button>
|
||||
|
||||
`
|
||||
);
|
||||
|
||||
bind("button.clear", this.clear);
|
||||
bind("button.clearItems", this.clearItems);
|
||||
bind("button.clearBuildings", this.clearBuildings);
|
||||
}
|
||||
}
|
||||
|
||||
clear() {
|
||||
clearItems() {
|
||||
this.root.logic.clearAllBeltsAndItems();
|
||||
}
|
||||
|
||||
clearBuildings() {
|
||||
for (const entity of this.root.entityMgr.getAllWithComponent(StaticMapEntityComponent)) {
|
||||
const staticComp = entity.components.StaticMapEntity;
|
||||
const signalComp = entity.components.ConstantSignal;
|
||||
const goalComp = entity.components.GoalAcceptor;
|
||||
|
||||
if (
|
||||
signalComp ||
|
||||
goalComp ||
|
||||
staticComp.getMetaBuilding().id === gMetaBuildingRegistry.findByClass(MetaBlockBuilding).id
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
this.root.map.removeStaticEntity(entity);
|
||||
this.root.entityMgr.destroyEntity(entity);
|
||||
}
|
||||
this.root.entityMgr.processDestroyList();
|
||||
}
|
||||
|
||||
initialize() {
|
||||
this.visible = true;
|
||||
}
|
||||
|
||||
@@ -158,10 +158,9 @@ export class MetaBuilding {
|
||||
|
||||
/**
|
||||
* Returns whether this building is rotateable
|
||||
* @param {string} variant
|
||||
* @returns {boolean}
|
||||
*/
|
||||
getIsRotateable(variant) {
|
||||
getIsRotateable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -243,7 +242,7 @@ export class MetaBuilding {
|
||||
* @return {{ rotation: number, rotationVariant: number, connectedEntities?: Array<Entity> }}
|
||||
*/
|
||||
computeOptimalDirectionAndRotationVariantAtTile({ root, tile, rotation, variant, layer }) {
|
||||
if (!this.getIsRotateable(variant)) {
|
||||
if (!this.getIsRotateable()) {
|
||||
return {
|
||||
rotation: 0,
|
||||
rotationVariant: 0,
|
||||
|
||||
@@ -7,6 +7,8 @@ import { types } from "../../savegame/serialization";
|
||||
import { enumGameModeTypes, GameMode } from "../game_mode";
|
||||
import { HUDPuzzleBackToMenu } from "../hud/parts/puzzle_back_to_menu";
|
||||
import { HUDPuzzleDLCLogo } from "../hud/parts/puzzle_dlc_logo";
|
||||
import { HUDBlueprintPlacer } from "../hud/parts/blueprint_placer";
|
||||
import { HUDMassSelector } from "../hud/parts/mass_selector";
|
||||
|
||||
export class PuzzleGameMode extends GameMode {
|
||||
static getType() {
|
||||
@@ -30,6 +32,8 @@ export class PuzzleGameMode extends GameMode {
|
||||
this.additionalHudParts = {
|
||||
puzzleBackToMenu: HUDPuzzleBackToMenu,
|
||||
puzzleDlcLogo: HUDPuzzleDLCLogo,
|
||||
blueprintPlacer: HUDBlueprintPlacer,
|
||||
massSelector: HUDMassSelector,
|
||||
};
|
||||
|
||||
this.zoneWidth = data.zoneWidth || 8;
|
||||
@@ -79,8 +83,8 @@ export class PuzzleGameMode extends GameMode {
|
||||
return false;
|
||||
}
|
||||
|
||||
getSupportsCopyPaste() {
|
||||
return false;
|
||||
getHasFreeCopyPaste() {
|
||||
return true;
|
||||
}
|
||||
|
||||
throughputDoesNotMatter() {
|
||||
|
||||
@@ -24,13 +24,15 @@ export class GoalAcceptorSystem extends GameSystemWithFilter {
|
||||
const entity = this.allEntities[i];
|
||||
const goalComp = entity.components.GoalAcceptor;
|
||||
|
||||
// filter the ones which are no longer active, or which are not the same
|
||||
goalComp.deliveryHistory = goalComp.deliveryHistory.filter(
|
||||
d =>
|
||||
now - d.time < globalConfig.goalAcceptorMinimumDurationSeconds && d.item === goalComp.item
|
||||
);
|
||||
if (!goalComp.lastDelivery) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (goalComp.deliveryHistory.length < goalComp.getRequiredDeliveryHistorySize()) {
|
||||
if (now - goalComp.lastDelivery.time > goalComp.getRequiredSecondsPerItem()) {
|
||||
goalComp.clearItems();
|
||||
}
|
||||
|
||||
if (goalComp.currentDeliveredItems < globalConfig.goalAcceptorItemsRequired) {
|
||||
allAccepted = false;
|
||||
}
|
||||
}
|
||||
@@ -64,8 +66,8 @@ export class GoalAcceptorSystem extends GameSystemWithFilter {
|
||||
const staticComp = contents[i].components.StaticMapEntity;
|
||||
const item = goalComp.item;
|
||||
|
||||
const requiredItemsForSuccess = goalComp.getRequiredDeliveryHistorySize();
|
||||
const percentage = clamp(goalComp.deliveryHistory.length / requiredItemsForSuccess, 0, 1);
|
||||
const requiredItemsForSuccess = globalConfig.goalAcceptorItemsRequired;
|
||||
const percentage = clamp(goalComp.currentDeliveredItems / requiredItemsForSuccess, 0, 1);
|
||||
|
||||
const center = staticComp.getTileSpaceBounds().getCenter().toWorldSpace();
|
||||
if (item) {
|
||||
@@ -78,7 +80,7 @@ export class GoalAcceptorSystem extends GameSystemWithFilter {
|
||||
);
|
||||
}
|
||||
|
||||
const isValid = item && goalComp.deliveryHistory.length >= requiredItemsForSuccess;
|
||||
const isValid = item && goalComp.currentDeliveredItems >= requiredItemsForSuccess;
|
||||
|
||||
parameters.context.translate(center.x, center.y);
|
||||
parameters.context.rotate((staticComp.rotation / 180) * Math.PI);
|
||||
@@ -90,7 +92,7 @@ export class GoalAcceptorSystem extends GameSystemWithFilter {
|
||||
|
||||
// progress arc
|
||||
|
||||
goalComp.displayPercentage = lerp(goalComp.displayPercentage, percentage, 0.3);
|
||||
goalComp.displayPercentage = lerp(goalComp.displayPercentage, percentage, 0.2);
|
||||
|
||||
const startAngle = Math.PI * 0.595;
|
||||
const maxAngle = Math.PI * 1.82;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { globalConfig } from "../../core/config";
|
||||
import { BaseItem } from "../base_item";
|
||||
import { enumColorMixingResults, enumColors } from "../colors";
|
||||
import {
|
||||
@@ -572,23 +573,23 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
|
||||
const item = payload.items[0].item;
|
||||
const now = this.root.time.now();
|
||||
|
||||
if (goalComp.item && !item.equals(goalComp.item)) {
|
||||
goalComp.clearItems();
|
||||
} else {
|
||||
goalComp.currentDeliveredItems = Math.min(
|
||||
goalComp.currentDeliveredItems + 1,
|
||||
globalConfig.goalAcceptorItemsRequired
|
||||
);
|
||||
}
|
||||
|
||||
if (this.root.gameMode.getIsEditor()) {
|
||||
// while playing in editor, assign the item
|
||||
goalComp.item = payload.items[0].item;
|
||||
goalComp.deliveryHistory.push({
|
||||
item,
|
||||
time: now,
|
||||
});
|
||||
} else {
|
||||
// otherwise, make sure it is the same, otherwise reset
|
||||
if (item.equals(goalComp.item)) {
|
||||
goalComp.deliveryHistory.push({
|
||||
item,
|
||||
time: now,
|
||||
});
|
||||
} else {
|
||||
goalComp.deliveryHistory = [];
|
||||
}
|
||||
}
|
||||
|
||||
goalComp.lastDelivery = {
|
||||
item,
|
||||
time: now,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user