mirror of
https://github.com/tobspr/shapez.io.git
synced 2026-03-02 03:39:21 +00:00
Add support for different building variants
This commit is contained in:
@@ -1,23 +1,11 @@
|
||||
// $icons: ;
|
||||
|
||||
// @each $icon in $icons {
|
||||
// [data-icon="#{$icon}"] {
|
||||
// background-image: uiResource("res/ui/#{$icon}");
|
||||
// }
|
||||
// }
|
||||
|
||||
$buildings: belt, cutter, miner, mixer, painter, rotater, splitter, stacker, trash, underground_belt;
|
||||
|
||||
@each $building in $buildings {
|
||||
[data-icon="building_tutorials/#{$building}.png"] {
|
||||
background-image: uiResource("res/ui/building_tutorials/#{$building}.png") !important;
|
||||
}
|
||||
}
|
||||
|
||||
$upgrades: belt, miner, painting, processors;
|
||||
@each $upgrade in $upgrades {
|
||||
[data-icon="upgrades/#{$upgrade}.png"] {
|
||||
background-image: uiResource("res/ui/upgrades/#{$upgrade}.png") !important;
|
||||
[data-icon="building_icons/#{$building}.png"] {
|
||||
background-image: uiResource("res/ui/building_icons/#{$building}.png") !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#ingame_HUD_building_placer {
|
||||
#ingame_HUD_PlacementHints {
|
||||
position: fixed;
|
||||
@include S(top, 40px);
|
||||
@include S(top, 60px);
|
||||
@include S(right, 10px);
|
||||
|
||||
display: grid;
|
||||
@@ -54,3 +54,60 @@
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
#ingame_HUD_PlacerVariants {
|
||||
position: absolute;
|
||||
@include S(left, 10px);
|
||||
@include S(bottom, 80px);
|
||||
display: grid;
|
||||
grid-auto-flow: row;
|
||||
@include S(grid-gap, 5px);
|
||||
|
||||
.explanation {
|
||||
@include PlainText;
|
||||
.keybinding {
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
|
||||
.variant {
|
||||
@include S(border-radius, 4px);
|
||||
background: rgba(0, 10, 20, 0.1);
|
||||
display: grid;
|
||||
position: relative;
|
||||
grid-template-columns: auto 1fr;
|
||||
align-items: center;
|
||||
@include S(padding, 5px);
|
||||
@include S(grid-gap, 10px);
|
||||
|
||||
&.active {
|
||||
background-color: rgba(74, 163, 223, 0.6);
|
||||
}
|
||||
|
||||
$iconSize: 25px;
|
||||
|
||||
.iconWrap {
|
||||
grid-column: 1 / 2;
|
||||
grid-row: 1 / 2;
|
||||
position: relative;
|
||||
@include S(width, $iconSize);
|
||||
@include S(height, $iconSize);
|
||||
background: center center / contain no-repeat;
|
||||
|
||||
&[data-tile-w="2"] {
|
||||
@include S(width, 2 * $iconSize);
|
||||
}
|
||||
|
||||
&[data-tile-h="2"] {
|
||||
@include S(height, 2 * $iconSize);
|
||||
}
|
||||
}
|
||||
|
||||
.label {
|
||||
grid-column: 2 / 3;
|
||||
grid-row: 1 / 2;
|
||||
@include PlainText;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
.buildings {
|
||||
display: grid;
|
||||
grid-auto-flow: column;
|
||||
@include S(padding, 0, 5px);
|
||||
@include S(margin-bottom, 2px);
|
||||
|
||||
.building {
|
||||
color: $accentColorDark;
|
||||
@@ -33,19 +33,17 @@
|
||||
justify-content: center;
|
||||
@include S(padding, 5px);
|
||||
@include S(padding-bottom, 1px);
|
||||
$buildingIconSize: 32px;
|
||||
@include S(width, 35px);
|
||||
@include S(height, 40px);
|
||||
|
||||
background: center center / 70% no-repeat;
|
||||
|
||||
&:not(.unlocked) {
|
||||
@include S(width, 30px);
|
||||
.tooltip {
|
||||
display: none !important;
|
||||
}
|
||||
.keybinding,
|
||||
.iconWrap {
|
||||
opacity: 0.01;
|
||||
}
|
||||
opacity: 0.8;
|
||||
background-image: none !important;
|
||||
|
||||
&::before {
|
||||
opacity: 0.5;
|
||||
content: " ";
|
||||
background: uiResource("locked_building.png") center center / #{D(20px)} #{D(20px)}
|
||||
no-repeat;
|
||||
@@ -58,8 +56,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
@include S(border-radius, 4px);
|
||||
|
||||
&.selected {
|
||||
background: rgba(74, 163, 223, 0.3) !important;
|
||||
background-color: rgba(74, 163, 223, 0.3) !important;
|
||||
transform: scale(1.05);
|
||||
.keybinding {
|
||||
color: #111;
|
||||
@@ -69,35 +69,11 @@
|
||||
pointer-events: all;
|
||||
transition: all 0.05s ease-in-out;
|
||||
transition-property: background-color, transform;
|
||||
|
||||
&.unlocked:hover {
|
||||
background: rgba($accentColorDark, 0.1);
|
||||
background-color: rgba($accentColorDark, 0.1);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.iconWrap {
|
||||
position: relative;
|
||||
@include S(width, $buildingIconSize);
|
||||
@include S(height, $buildingIconSize);
|
||||
@include S(margin-top, 3px);
|
||||
@include S(margin-bottom, 6px);
|
||||
}
|
||||
|
||||
.label {
|
||||
@include SuperSmallText;
|
||||
display: none;
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
&[data-tilewidth="2"] {
|
||||
.iconWrap {
|
||||
@include S(width, 2 * $buildingIconSize);
|
||||
}
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,8 +8,11 @@
|
||||
@include S(padding, 9px);
|
||||
@include PlainText;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.keybinding {
|
||||
@include S(margin, 0, 4px);
|
||||
position: relative;
|
||||
top: unset;
|
||||
left: unset;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#ingame_HUD_PinnedShapes {
|
||||
position: absolute;
|
||||
@include S(left, 9px);
|
||||
@include S(top, 120px);
|
||||
@include S(top, 150px);
|
||||
@include PlainText;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@@ -101,7 +101,7 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
@include S(width, 65px);
|
||||
@include S(width, 75px);
|
||||
|
||||
button.pin {
|
||||
@include S(width, 12px);
|
||||
|
||||
@@ -148,11 +148,13 @@
|
||||
grid-column: 3 / 4;
|
||||
grid-row: 1 / 2;
|
||||
@include Heading;
|
||||
align-self: center;
|
||||
text-align: right;
|
||||
color: #55595a;
|
||||
}
|
||||
|
||||
canvas.graph {
|
||||
@include S(width, 300px);
|
||||
@include S(width, 270px);
|
||||
@include S(height, 40px);
|
||||
@include S(border-radius, 0, 0, 2px, 2px);
|
||||
$color: rgba(0, 10, 20, 0.04);
|
||||
|
||||
@@ -39,11 +39,32 @@
|
||||
@import "ingame_hud/notifications";
|
||||
@import "ingame_hud/settings_menu";
|
||||
|
||||
// Z-Index
|
||||
$elements: ingame_Canvas, ingame_VignetteOverlay, ingame_HUD_building_placer, ingame_HUD_PinnedShapes,
|
||||
ingame_HUD_buildings_toolbar, ingame_HUD_GameMenu, ingame_HUD_KeybindingOverlay, ingame_HUD_Notifications,
|
||||
ingame_HUD_Shop, ingame_HUD_Statistics, ingame_HUD_BetaOverlay, ingame_HUD_MassSelector,
|
||||
ingame_HUD_UnlockNotification, ingame_HUD_SettingsMenu;
|
||||
// prettier-ignore
|
||||
$elements:
|
||||
// Base
|
||||
ingame_Canvas,
|
||||
ingame_VignetteOverlay,
|
||||
|
||||
// Ingame overlays
|
||||
ingame_HUD_PlacementHints,
|
||||
ingame_HUD_PlacerVariants,
|
||||
|
||||
// Regular hud
|
||||
ingame_HUD_PinnedShapes,
|
||||
ingame_HUD_buildings_toolbar,
|
||||
ingame_HUD_GameMenu,
|
||||
ingame_HUD_KeybindingOverlay,
|
||||
ingame_HUD_MassSelector,
|
||||
ingame_HUD_Notifications,
|
||||
|
||||
// Overlays
|
||||
ingame_HUD_BetaOverlay,
|
||||
|
||||
// Dialogs
|
||||
ingame_HUD_Shop,
|
||||
ingame_HUD_Statistics,
|
||||
ingame_HUD_UnlockNotification,
|
||||
ingame_HUD_SettingsMenu;
|
||||
|
||||
$zindex: 100;
|
||||
|
||||
@@ -57,7 +78,7 @@ $zindex: 100;
|
||||
|
||||
body.uiHidden {
|
||||
#ingame_HUD_buildings_toolbar,
|
||||
#ingame_HUD_building_placer,
|
||||
#ingame_HUD_PlacementHints,
|
||||
#ingame_HUD_GameMenu,
|
||||
#ingame_HUD_MassSelector,
|
||||
#ingame_HUD_PinnedShapes,
|
||||
|
||||
@@ -144,6 +144,15 @@
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
|
||||
.browserWarning {
|
||||
@include S(margin-bottom, 10px);
|
||||
background-color: $colorRedBright;
|
||||
@include PlainText;
|
||||
color: #fff;
|
||||
@include S(border-radius, 4px);
|
||||
@include S(padding, 5px);
|
||||
}
|
||||
|
||||
.playButton {
|
||||
@include SuperHeading;
|
||||
@include S(width, 130px);
|
||||
|
||||
@@ -28,7 +28,8 @@ export const globalConfig = {
|
||||
physicsDeltaSeconds: 0,
|
||||
|
||||
// Update physics at N fps, independent of rendering
|
||||
physicsUpdateRate: 55,
|
||||
// physicsUpdateRate: 55,
|
||||
physicsUpdateRate: 120,
|
||||
|
||||
// Map
|
||||
mapChunkSize: 32,
|
||||
@@ -47,6 +48,7 @@ export const globalConfig = {
|
||||
cutter: 1 / 4,
|
||||
rotater: 1 / 1,
|
||||
painter: 1 / 3,
|
||||
painterDouble: 1 / 3,
|
||||
mixer: 1 / 2,
|
||||
stacker: 1 / 5,
|
||||
},
|
||||
@@ -71,7 +73,7 @@ export const globalConfig = {
|
||||
|
||||
debug: {
|
||||
/* dev:start */
|
||||
// fastGameEnter: true,
|
||||
fastGameEnter: true,
|
||||
noArtificialDelays: true,
|
||||
// disableSavegameWrite: true,
|
||||
showEntityBounds: false,
|
||||
|
||||
@@ -750,15 +750,6 @@ export function checkTimerExpired(now, lastTick, tickRate) {
|
||||
* Returns if the game supports this browser
|
||||
*/
|
||||
export function isSupportedBrowser() {
|
||||
if (navigator.userAgent.toLowerCase().indexOf("firefox") >= 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return isSupportedBrowserForMultiplayer();
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/questions/4565112/javascript-how-to-find-out-if-the-user-browser-is-chrome/13348618#13348618
|
||||
export function isSupportedBrowserForMultiplayer() {
|
||||
// please note,
|
||||
// that IE11 now returns undefined again for window.chrome
|
||||
// and new Opera 30 outputs true for window.chrome
|
||||
@@ -776,7 +767,6 @@ export function isSupportedBrowserForMultiplayer() {
|
||||
var winNav = window.navigator;
|
||||
var vendorName = winNav.vendor;
|
||||
// @ts-ignore
|
||||
var isOpera = typeof window.opr !== "undefined";
|
||||
var isIEedge = winNav.userAgent.indexOf("Edge") > -1;
|
||||
var isIOSChrome = winNav.userAgent.match("CriOS");
|
||||
|
||||
|
||||
@@ -30,5 +30,6 @@ export class BaseItem extends BasicSerializableObject {
|
||||
|
||||
getBackgroundColorAsResource() {
|
||||
abstract;
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,11 @@ import { enumDirection, Vector } from "../../core/vector";
|
||||
import { ItemEjectorComponent } from "../components/item_ejector";
|
||||
import { MinerComponent } from "../components/miner";
|
||||
import { Entity } from "../entity";
|
||||
import { MetaBuilding } from "../meta_building";
|
||||
import { MetaBuilding, defaultBuildingVariant } from "../meta_building";
|
||||
import { GameRoot } from "../root";
|
||||
|
||||
/** @enum {string} */
|
||||
export const enumMinerVariants = { chainable: "chainable" };
|
||||
|
||||
export class MetaMinerBuilding extends MetaBuilding {
|
||||
constructor() {
|
||||
@@ -21,16 +25,46 @@ export class MetaMinerBuilding extends MetaBuilding {
|
||||
return "Place over a shape or color to extract it. Six extractors fill exactly one belt.";
|
||||
}
|
||||
|
||||
getAvailableVariants(root) {
|
||||
return [defaultBuildingVariant, enumMinerVariants.chainable];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {GameRoot} root
|
||||
* @param {object} param0
|
||||
* @param {Vector} param0.origin
|
||||
* @param {number} param0.rotation
|
||||
* @param {number} param0.rotationVariant
|
||||
* @param {string} param0.variant
|
||||
*/
|
||||
performAdditionalPlacementChecks(root, { origin, rotation, rotationVariant, variant }) {
|
||||
// Make sure its placed above a resource
|
||||
const lowerLayer = root.map.getLowerLayerContentXY(origin.x, origin.y);
|
||||
if (!lowerLayer) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the entity at the given location
|
||||
* @param {Entity} entity
|
||||
*/
|
||||
setupEntityComponents(entity) {
|
||||
entity.addComponent(new MinerComponent());
|
||||
entity.addComponent(new MinerComponent({}));
|
||||
entity.addComponent(
|
||||
new ItemEjectorComponent({
|
||||
slots: [{ pos: new Vector(0, 0), direction: enumDirection.top }],
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Entity} entity
|
||||
* @param {*} variant
|
||||
*/
|
||||
updateVariant(entity, variant) {
|
||||
entity.components.Miner.chainable = variant === enumMinerVariants.chainable;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,17 +4,27 @@ import { enumItemAcceptorItemFilter, ItemAcceptorComponent } from "../components
|
||||
import { ItemEjectorComponent } from "../components/item_ejector";
|
||||
import { enumItemProcessorTypes, ItemProcessorComponent } from "../components/item_processor";
|
||||
import { Entity } from "../entity";
|
||||
import { MetaBuilding } from "../meta_building";
|
||||
import { MetaBuilding, defaultBuildingVariant } from "../meta_building";
|
||||
import { enumHubGoalRewards } from "../tutorial_goals";
|
||||
import { GameRoot } from "../root";
|
||||
|
||||
/** @enum {string} */
|
||||
export const enumPainterVariants = { double: "double" };
|
||||
|
||||
export class MetaPainterBuilding extends MetaBuilding {
|
||||
constructor() {
|
||||
super("painter");
|
||||
}
|
||||
|
||||
getDimensions() {
|
||||
return new Vector(2, 1);
|
||||
getDimensions(variant) {
|
||||
switch (variant) {
|
||||
case defaultBuildingVariant:
|
||||
return new Vector(2, 1);
|
||||
case enumPainterVariants.double:
|
||||
return new Vector(2, 2);
|
||||
default:
|
||||
assertAlways(false, "Unknown painter variant: " + variant);
|
||||
}
|
||||
}
|
||||
|
||||
getName() {
|
||||
@@ -29,6 +39,10 @@ export class MetaPainterBuilding extends MetaBuilding {
|
||||
return "#cd9b7d";
|
||||
}
|
||||
|
||||
getAvailableVariants(root) {
|
||||
return [defaultBuildingVariant, enumPainterVariants.double];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {GameRoot} root
|
||||
*/
|
||||
@@ -41,16 +55,11 @@ export class MetaPainterBuilding extends MetaBuilding {
|
||||
* @param {Entity} entity
|
||||
*/
|
||||
setupEntityComponents(entity) {
|
||||
entity.addComponent(
|
||||
new ItemProcessorComponent({
|
||||
inputsPerCharge: 2,
|
||||
processorType: enumItemProcessorTypes.painter,
|
||||
})
|
||||
);
|
||||
entity.addComponent(new ItemProcessorComponent({}));
|
||||
|
||||
entity.addComponent(
|
||||
new ItemEjectorComponent({
|
||||
slots: [{ pos: new Vector(0, 0), direction: enumDirection.top }],
|
||||
slots: [{ pos: new Vector(1, 0), direction: enumDirection.right }],
|
||||
})
|
||||
);
|
||||
entity.addComponent(
|
||||
@@ -58,16 +67,69 @@ export class MetaPainterBuilding extends MetaBuilding {
|
||||
slots: [
|
||||
{
|
||||
pos: new Vector(0, 0),
|
||||
directions: [enumDirection.bottom],
|
||||
directions: [enumDirection.left],
|
||||
filter: enumItemAcceptorItemFilter.shape,
|
||||
},
|
||||
{
|
||||
pos: new Vector(1, 0),
|
||||
directions: [enumDirection.bottom],
|
||||
directions: [enumDirection.top],
|
||||
filter: enumItemAcceptorItemFilter.color,
|
||||
},
|
||||
],
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Entity} entity
|
||||
* @param {string} variant
|
||||
*/
|
||||
updateVariant(entity, variant) {
|
||||
switch (variant) {
|
||||
case defaultBuildingVariant: {
|
||||
entity.components.ItemAcceptor.setSlots([
|
||||
{
|
||||
pos: new Vector(0, 0),
|
||||
directions: [enumDirection.left],
|
||||
filter: enumItemAcceptorItemFilter.shape,
|
||||
},
|
||||
{
|
||||
pos: new Vector(1, 0),
|
||||
directions: [enumDirection.top],
|
||||
filter: enumItemAcceptorItemFilter.color,
|
||||
},
|
||||
]);
|
||||
|
||||
entity.components.ItemProcessor.type = enumItemProcessorTypes.painter;
|
||||
entity.components.ItemProcessor.inputsPerCharge = 2;
|
||||
break;
|
||||
}
|
||||
case enumPainterVariants.double: {
|
||||
entity.components.ItemAcceptor.setSlots([
|
||||
{
|
||||
pos: new Vector(0, 0),
|
||||
directions: [enumDirection.left],
|
||||
filter: enumItemAcceptorItemFilter.shape,
|
||||
},
|
||||
{
|
||||
pos: new Vector(0, 1),
|
||||
directions: [enumDirection.left],
|
||||
filter: enumItemAcceptorItemFilter.shape,
|
||||
},
|
||||
{
|
||||
pos: new Vector(1, 0),
|
||||
directions: [enumDirection.top],
|
||||
filter: enumItemAcceptorItemFilter.color,
|
||||
},
|
||||
]);
|
||||
|
||||
entity.components.ItemProcessor.type = enumItemProcessorTypes.painterDouble;
|
||||
entity.components.ItemProcessor.inputsPerCharge = 3;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assertAlways(false, "Unknown painter variant: " + variant);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,17 +4,27 @@ import { ItemAcceptorComponent } from "../components/item_acceptor";
|
||||
import { ItemEjectorComponent } from "../components/item_ejector";
|
||||
import { enumItemProcessorTypes, ItemProcessorComponent } from "../components/item_processor";
|
||||
import { Entity } from "../entity";
|
||||
import { MetaBuilding } from "../meta_building";
|
||||
import { MetaBuilding, defaultBuildingVariant } from "../meta_building";
|
||||
import { GameRoot } from "../root";
|
||||
import { enumHubGoalRewards } from "../tutorial_goals";
|
||||
|
||||
/** @enum {string} */
|
||||
export const enumSplitterVariants = { compact: "compact" };
|
||||
|
||||
export class MetaSplitterBuilding extends MetaBuilding {
|
||||
constructor() {
|
||||
super("splitter");
|
||||
}
|
||||
|
||||
getDimensions() {
|
||||
return new Vector(2, 1);
|
||||
getDimensions(variant) {
|
||||
switch (variant) {
|
||||
case defaultBuildingVariant:
|
||||
return new Vector(2, 1);
|
||||
case enumSplitterVariants.compact:
|
||||
return new Vector(1, 1);
|
||||
default:
|
||||
assertAlways(false, "Unknown splitter variant: " + variant);
|
||||
}
|
||||
}
|
||||
|
||||
getName() {
|
||||
@@ -29,6 +39,10 @@ export class MetaSplitterBuilding extends MetaBuilding {
|
||||
return "Multifunctional - Evenly distributes all inputs onto all outputs.";
|
||||
}
|
||||
|
||||
getAvailableVariants(root) {
|
||||
return [defaultBuildingVariant, enumSplitterVariants.compact];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {GameRoot} root
|
||||
*/
|
||||
@@ -60,11 +74,6 @@ export class MetaSplitterBuilding extends MetaBuilding {
|
||||
new ItemProcessorComponent({
|
||||
inputsPerCharge: 1,
|
||||
processorType: enumItemProcessorTypes.splitter,
|
||||
|
||||
beltUnderlays: [
|
||||
{ pos: new Vector(0, 0), direction: enumDirection.top },
|
||||
{ pos: new Vector(1, 0), direction: enumDirection.top },
|
||||
],
|
||||
})
|
||||
);
|
||||
|
||||
@@ -77,4 +86,62 @@ export class MetaSplitterBuilding extends MetaBuilding {
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Entity} entity
|
||||
* @param {string} variant
|
||||
*/
|
||||
updateVariant(entity, variant) {
|
||||
switch (variant) {
|
||||
case defaultBuildingVariant: {
|
||||
entity.components.ItemAcceptor.setSlots([
|
||||
{
|
||||
pos: new Vector(0, 0),
|
||||
directions: [enumDirection.bottom],
|
||||
},
|
||||
{
|
||||
pos: new Vector(1, 0),
|
||||
directions: [enumDirection.bottom],
|
||||
},
|
||||
]);
|
||||
|
||||
entity.components.ItemEjector.setSlots([
|
||||
{ pos: new Vector(0, 0), direction: enumDirection.top },
|
||||
{ pos: new Vector(1, 0), direction: enumDirection.top },
|
||||
]);
|
||||
|
||||
entity.components.ItemProcessor.beltUnderlays = [
|
||||
{ pos: new Vector(0, 0), direction: enumDirection.top },
|
||||
{ pos: new Vector(1, 0), direction: enumDirection.top },
|
||||
];
|
||||
|
||||
break;
|
||||
}
|
||||
case enumSplitterVariants.compact: {
|
||||
entity.components.ItemAcceptor.setSlots([
|
||||
{
|
||||
pos: new Vector(0, 0),
|
||||
directions: [enumDirection.bottom],
|
||||
},
|
||||
{
|
||||
pos: new Vector(0, 0),
|
||||
directions: [enumDirection.right],
|
||||
},
|
||||
]);
|
||||
|
||||
entity.components.ItemEjector.setSlots([
|
||||
{ pos: new Vector(0, 0), direction: enumDirection.top },
|
||||
]);
|
||||
|
||||
entity.components.ItemProcessor.beltUnderlays = [
|
||||
{ pos: new Vector(0, 0), direction: enumDirection.top },
|
||||
];
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assertAlways(false, "Unknown painter variant: " + variant);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ export class Camera extends BasicSerializableObject {
|
||||
this.clampZoomLevel();
|
||||
|
||||
/** @type {Vector} */
|
||||
this.center = new Vector(0, 0);
|
||||
this.center = new Vector(2 * globalConfig.tileSize, 2 * globalConfig.tileSize);
|
||||
|
||||
// Input handling
|
||||
this.currentlyMoving = false;
|
||||
@@ -150,8 +150,7 @@ export class Camera extends BasicSerializableObject {
|
||||
* Finds a good initial zoom level
|
||||
*/
|
||||
findInitialZoom() {
|
||||
return 3;
|
||||
const desiredWorldSpaceWidth = 20 * globalConfig.tileSize;
|
||||
const desiredWorldSpaceWidth = 15 * globalConfig.tileSize;
|
||||
const zoomLevelX = this.root.gameWidth / desiredWorldSpaceWidth;
|
||||
const zoomLevelY = this.root.gameHeight / desiredWorldSpaceWidth;
|
||||
|
||||
@@ -327,7 +326,11 @@ export class Camera extends BasicSerializableObject {
|
||||
mapper.getBinding("map_move_right").add(() => (this.keyboardForce.x = 1));
|
||||
mapper.getBinding("map_move_left").add(() => (this.keyboardForce.x = -1));
|
||||
|
||||
mapper.getBinding("center_map").add(() => (this.desiredCenter = new Vector(0, 0)));
|
||||
mapper.getBinding("center_map").add(() => this.centerOnMap());
|
||||
}
|
||||
|
||||
centerOnMap() {
|
||||
this.desiredCenter = new Vector(0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,6 +13,7 @@ export const enumItemProcessorTypes = {
|
||||
trash: "trash",
|
||||
mixer: "mixer",
|
||||
painter: "painter",
|
||||
painterDouble: "painterDouble",
|
||||
hub: "hub",
|
||||
};
|
||||
|
||||
@@ -60,12 +61,16 @@ export class ItemProcessorComponent extends Component {
|
||||
/**
|
||||
*
|
||||
* @param {object} param0
|
||||
* @param {enumItemProcessorTypes} param0.processorType Which type of processor this is
|
||||
* @param {number} param0.inputsPerCharge How many items this machine needs until it can start working
|
||||
* @param {enumItemProcessorTypes=} param0.processorType Which type of processor this is
|
||||
* @param {number=} param0.inputsPerCharge How many items this machine needs until it can start working
|
||||
* @param {Array<{pos: Vector, direction: enumDirection}>=} param0.beltUnderlays Where to render belt underlays
|
||||
*
|
||||
*/
|
||||
constructor({ processorType = enumItemProcessorTypes.splitter, inputsPerCharge, beltUnderlays = [] }) {
|
||||
constructor({
|
||||
processorType = enumItemProcessorTypes.splitter,
|
||||
inputsPerCharge = 1,
|
||||
beltUnderlays = [],
|
||||
}) {
|
||||
super();
|
||||
|
||||
// Which slot to emit next, this is only a preference and if it can't emit
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import { globalConfig } from "../../core/config";
|
||||
import { types } from "../../savegame/serialization";
|
||||
import { Component } from "../component";
|
||||
import { BaseItem } from "../base_item";
|
||||
import { gItemRegistry } from "../../core/global_registries";
|
||||
|
||||
const chainBufferSize = 10;
|
||||
|
||||
export class MinerComponent extends Component {
|
||||
static getId() {
|
||||
@@ -10,13 +14,37 @@ export class MinerComponent extends Component {
|
||||
static getSchema() {
|
||||
return {
|
||||
lastMiningTime: types.ufloat,
|
||||
chainable: types.bool,
|
||||
itemChainBuffer: types.array(types.obj(gItemRegistry)),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
constructor() {
|
||||
constructor({ chainable = false }) {
|
||||
super();
|
||||
this.lastMiningTime = 0;
|
||||
this.chainable = chainable;
|
||||
|
||||
/**
|
||||
* Stores items from other miners which were chained to this
|
||||
* miner.
|
||||
* @type {Array<BaseItem>}
|
||||
*/
|
||||
this.itemChainBuffer = [];
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {BaseItem} item
|
||||
*/
|
||||
tryAcceptChainedItem(item) {
|
||||
if (this.itemChainBuffer.length > chainBufferSize) {
|
||||
// Well, this one is full
|
||||
return false;
|
||||
}
|
||||
|
||||
this.itemChainBuffer.push(item);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ import { SoundProxy } from "./sound_proxy";
|
||||
import { GameTime } from "./time/game_time";
|
||||
import { ProductionAnalytics } from "./production_analytics";
|
||||
import { randomInt } from "../core/utils";
|
||||
import { defaultBuildingVariant } from "./meta_building";
|
||||
|
||||
const logger = createLogger("ingame/core");
|
||||
|
||||
@@ -143,6 +144,7 @@ export class GameCore {
|
||||
rotation: 0,
|
||||
originalRotation: 0,
|
||||
rotationVariant: 0,
|
||||
variant: defaultBuildingVariant,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -387,6 +387,11 @@ export class HubGoals extends BasicSerializableObject {
|
||||
case enumItemProcessorTypes.stacker:
|
||||
case enumItemProcessorTypes.mixer:
|
||||
case enumItemProcessorTypes.painter:
|
||||
case enumItemProcessorTypes.painterDouble:
|
||||
assert(
|
||||
globalConfig.buildingSpeeds[processorType],
|
||||
"Processor type has no speed set in globalConfig.buildingSpeeds: " + processorType
|
||||
);
|
||||
return (
|
||||
globalConfig.beltSpeedItemsPerSecond *
|
||||
this.upgradeImprovements.processors *
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { BaseHUDPart } from "../base_hud_part";
|
||||
import { MetaBuilding } from "../../meta_building";
|
||||
import { MetaBuilding, defaultBuildingVariant } from "../../meta_building";
|
||||
import { DrawParameters } from "../../../core/draw_parameters";
|
||||
import { globalConfig } from "../../../core/config";
|
||||
import { StaticMapEntityComponent } from "../../components/static_map_entity";
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
enumInvertedDirections,
|
||||
enumDirectionToVector,
|
||||
} from "../../../core/vector";
|
||||
import { pulseAnimation, makeDiv } from "../../../core/utils";
|
||||
import { pulseAnimation, makeDiv, removeAllChildren } from "../../../core/utils";
|
||||
import { DynamicDomAttach } from "../dynamic_dom_attach";
|
||||
import { TrackedState } from "../../../core/tracked_state";
|
||||
import { Math_abs, Math_radians, Math_degrees } from "../../../core/builtins";
|
||||
@@ -32,6 +32,7 @@ export class HUDBuildingPlacer extends BaseHUDPart {
|
||||
keyActionMapper.getBinding("back").add(this.abortPlacement, this);
|
||||
|
||||
keyActionMapper.getBinding("rotate_while_placing").add(this.tryRotate, this);
|
||||
keyActionMapper.getBinding("cycle_variants").add(this.cycleVariants, this);
|
||||
|
||||
this.domAttach = new DynamicDomAttach(this.root, this.element, {});
|
||||
|
||||
@@ -40,6 +41,15 @@ export class HUDBuildingPlacer extends BaseHUDPart {
|
||||
this.root.camera.upPostHandler.add(this.abortDragging, this);
|
||||
|
||||
this.currentlyDragging = false;
|
||||
this.currentVariant = new TrackedState(this.rerenderVariants, this);
|
||||
|
||||
this.variantsAttach = new DynamicDomAttach(this.root, this.variantsElement, {});
|
||||
|
||||
/**
|
||||
* Stores which variants for each building we prefer, this is based on what
|
||||
* the user last selected
|
||||
*/
|
||||
this.preferredVariants = {};
|
||||
|
||||
/**
|
||||
* The tile we last dragged onto
|
||||
@@ -55,7 +65,7 @@ export class HUDBuildingPlacer extends BaseHUDPart {
|
||||
}
|
||||
|
||||
createElements(parent) {
|
||||
this.element = makeDiv(parent, "ingame_HUD_building_placer", [], ``);
|
||||
this.element = makeDiv(parent, "ingame_HUD_PlacementHints", [], ``);
|
||||
|
||||
this.buildingInfoElements = {};
|
||||
this.buildingInfoElements.label = makeDiv(this.element, null, ["buildingLabel"], "Extract");
|
||||
@@ -63,6 +73,8 @@ export class HUDBuildingPlacer extends BaseHUDPart {
|
||||
this.buildingInfoElements.descText = makeDiv(this.buildingInfoElements.desc, null, ["text"], "");
|
||||
this.buildingInfoElements.hotkey = makeDiv(this.buildingInfoElements.desc, null, ["hotkey"], "");
|
||||
this.buildingInfoElements.tutorialImage = makeDiv(this.element, null, ["buildingImage"]);
|
||||
|
||||
this.variantsElement = makeDiv(parent, "ingame_HUD_PlacerVariants");
|
||||
}
|
||||
|
||||
abortPlacement() {
|
||||
@@ -106,8 +118,17 @@ export class HUDBuildingPlacer extends BaseHUDPart {
|
||||
const oldPos = this.lastDragTile;
|
||||
const newPos = this.root.camera.screenToWorld(pos).toTileSpace();
|
||||
|
||||
if (this.root.camera.desiredCenter) {
|
||||
// Camera is moving
|
||||
this.lastDragTile = newPos;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!oldPos.equals(newPos)) {
|
||||
if (metaBuilding.getRotateAutomaticallyWhilePlacing()) {
|
||||
if (
|
||||
metaBuilding.getRotateAutomaticallyWhilePlacing(this.currentVariant.get()) &&
|
||||
!this.root.app.inputMgr.ctrlIsDown
|
||||
) {
|
||||
const delta = newPos.sub(oldPos);
|
||||
const angleDeg = Math_degrees(delta.angle());
|
||||
this.currentBaseRotation = (Math.round(angleDeg / 90) * 90 + 360) % 360;
|
||||
@@ -177,7 +198,6 @@ export class HUDBuildingPlacer extends BaseHUDPart {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {MetaBuilding} metaBuilding
|
||||
*/
|
||||
onSelectedMetaBuildingChanged(metaBuilding) {
|
||||
@@ -189,16 +209,18 @@ export class HUDBuildingPlacer extends BaseHUDPart {
|
||||
const binding = this.root.gameState.keyActionMapper.getBinding(
|
||||
"building_" + metaBuilding.getId()
|
||||
);
|
||||
|
||||
this.buildingInfoElements.hotkey.innerHTML = "Hotkey: " + binding.getKeyCodeString();
|
||||
|
||||
const variant = this.preferredVariants[metaBuilding.getId()] || defaultBuildingVariant;
|
||||
this.currentVariant.set(variant);
|
||||
|
||||
this.fakeEntity = new Entity(null);
|
||||
metaBuilding.setupEntityComponents(this.fakeEntity, null);
|
||||
metaBuilding.setupEntityComponents(this.fakeEntity, null, variant);
|
||||
this.fakeEntity.addComponent(
|
||||
new StaticMapEntityComponent({
|
||||
origin: new Vector(0, 0),
|
||||
rotation: 0,
|
||||
tileSize: metaBuilding.getDimensions().copy(),
|
||||
tileSize: metaBuilding.getDimensions(this.currentVariant.get()).copy(),
|
||||
})
|
||||
);
|
||||
|
||||
@@ -210,6 +232,74 @@ export class HUDBuildingPlacer extends BaseHUDPart {
|
||||
this.currentlyDragging = false;
|
||||
this.fakeEntity = null;
|
||||
}
|
||||
|
||||
// Since it depends on both, rerender twice
|
||||
this.rerenderVariants();
|
||||
}
|
||||
|
||||
rerenderVariants() {
|
||||
removeAllChildren(this.variantsElement);
|
||||
|
||||
const metaBuilding = this.currentMetaBuilding.get();
|
||||
|
||||
if (!metaBuilding) {
|
||||
return;
|
||||
}
|
||||
const availableVariants = metaBuilding.getAvailableVariants(this.root);
|
||||
if (availableVariants.length === 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
makeDiv(
|
||||
this.variantsElement,
|
||||
null,
|
||||
["explanation"],
|
||||
`
|
||||
Press <code class='keybinding'>${this.root.gameState.keyActionMapper
|
||||
.getBinding("cycle_variants")
|
||||
.getKeyCodeString()}</code> to cycle variants.
|
||||
`
|
||||
);
|
||||
|
||||
for (let i = 0; i < availableVariants.length; ++i) {
|
||||
const variant = availableVariants[i];
|
||||
|
||||
const element = makeDiv(this.variantsElement, null, ["variant"]);
|
||||
element.classList.toggle("active", variant === this.currentVariant.get());
|
||||
makeDiv(element, null, ["label"], variant);
|
||||
|
||||
const iconSize = 64;
|
||||
|
||||
const dimensions = metaBuilding.getDimensions(variant);
|
||||
const sprite = metaBuilding.getPreviewSprite(0, variant);
|
||||
const spriteWrapper = makeDiv(element, null, ["iconWrap"]);
|
||||
spriteWrapper.setAttribute("data-tile-w", dimensions.x);
|
||||
spriteWrapper.setAttribute("data-tile-h", dimensions.y);
|
||||
|
||||
spriteWrapper.innerHTML = sprite.getAsHTML(iconSize * dimensions.x, iconSize * dimensions.y);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cycles through the variants
|
||||
*/
|
||||
cycleVariants() {
|
||||
const metaBuilding = this.currentMetaBuilding.get();
|
||||
if (!metaBuilding) {
|
||||
this.currentVariant.set(defaultBuildingVariant);
|
||||
} else {
|
||||
const availableVariants = metaBuilding.getAvailableVariants(this.root);
|
||||
const index = availableVariants.indexOf(this.currentVariant.get());
|
||||
assert(
|
||||
index >= 0,
|
||||
"Current variant was invalid: " + this.currentVariant.get() + " out of " + availableVariants
|
||||
);
|
||||
const newIndex = (index + 1) % availableVariants.length;
|
||||
const newVariant = availableVariants[newIndex];
|
||||
this.currentVariant.set(newVariant);
|
||||
|
||||
this.preferredVariants[metaBuilding.getId()] = newVariant;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -290,6 +380,7 @@ export class HUDBuildingPlacer extends BaseHUDPart {
|
||||
rotationVariant,
|
||||
originalRotation: this.currentBaseRotation,
|
||||
building: this.currentMetaBuilding.get(),
|
||||
variant: this.currentVariant.get(),
|
||||
})
|
||||
) {
|
||||
// Succesfully placed
|
||||
@@ -317,10 +408,12 @@ export class HUDBuildingPlacer extends BaseHUDPart {
|
||||
if (this.root.camera.zoomLevel < globalConfig.mapChunkOverviewMinZoom) {
|
||||
// Dont allow placing in overview mode
|
||||
this.domAttach.update(false);
|
||||
this.variantsAttach.update(false);
|
||||
return;
|
||||
}
|
||||
|
||||
this.domAttach.update(this.currentMetaBuilding.get());
|
||||
this.variantsAttach.update(this.currentMetaBuilding.get());
|
||||
const metaBuilding = this.currentMetaBuilding.get();
|
||||
|
||||
if (!metaBuilding) {
|
||||
@@ -382,7 +475,9 @@ export class HUDBuildingPlacer extends BaseHUDPart {
|
||||
const staticComp = this.fakeEntity.components.StaticMapEntity;
|
||||
staticComp.origin = tile;
|
||||
staticComp.rotation = rotation;
|
||||
staticComp.tileSize = metaBuilding.getDimensions(this.currentVariant.get());
|
||||
metaBuilding.updateRotationVariant(this.fakeEntity, rotationVariant);
|
||||
metaBuilding.updateVariant(this.fakeEntity, this.currentVariant.get());
|
||||
|
||||
// Check if we could place the buildnig
|
||||
const canBuild = this.root.logic.checkCanPlaceBuilding({
|
||||
@@ -390,6 +485,7 @@ export class HUDBuildingPlacer extends BaseHUDPart {
|
||||
rotation,
|
||||
rotationVariant,
|
||||
building: metaBuilding,
|
||||
variant: this.currentVariant.get(),
|
||||
});
|
||||
|
||||
// Fade in / out
|
||||
@@ -419,7 +515,7 @@ export class HUDBuildingPlacer extends BaseHUDPart {
|
||||
parameters.context.globalAlpha = 1;
|
||||
|
||||
// HACK to draw the entity sprite
|
||||
const previewSprite = metaBuilding.getBlueprintSprite(rotationVariant);
|
||||
const previewSprite = metaBuilding.getBlueprintSprite(rotationVariant, this.currentVariant.get());
|
||||
staticComp.origin = worldPos.divideScalar(globalConfig.tileSize).subScalars(0.5, 0.5);
|
||||
staticComp.drawSpriteOnFullEntityBounds(parameters, previewSprite);
|
||||
staticComp.origin = tile;
|
||||
|
||||
@@ -57,24 +57,14 @@ export class HUDBuildingsToolbar extends BaseHUDPart {
|
||||
const actionMapper = this.root.gameState.keyActionMapper;
|
||||
|
||||
const items = makeDiv(this.element, null, ["buildings"]);
|
||||
const iconSize = 32;
|
||||
|
||||
for (let i = 0; i < toolbarBuildings.length; ++i) {
|
||||
const metaBuilding = gMetaBuildingRegistry.findByClass(toolbarBuildings[i]);
|
||||
const binding = actionMapper.getBinding("building_" + metaBuilding.getId());
|
||||
|
||||
const dimensions = metaBuilding.getDimensions();
|
||||
const itemContainer = makeDiv(items, null, ["building"]);
|
||||
itemContainer.setAttribute("data-tilewidth", dimensions.x);
|
||||
itemContainer.setAttribute("data-tileheight", dimensions.y);
|
||||
itemContainer.setAttribute("data-icon", "building_icons/" + metaBuilding.getId() + ".png");
|
||||
|
||||
const label = makeDiv(itemContainer, null, ["label"]);
|
||||
label.innerText = metaBuilding.getName();
|
||||
|
||||
const sprite = metaBuilding.getPreviewSprite(0);
|
||||
|
||||
const spriteWrapper = makeDiv(itemContainer, null, ["iconWrap"]);
|
||||
spriteWrapper.innerHTML = sprite.getAsHTML(iconSize * dimensions.x, iconSize * dimensions.y);
|
||||
binding.add(() => this.selectBuildingForPlacement(metaBuilding));
|
||||
|
||||
this.trackClicks(itemContainer, () => this.selectBuildingForPlacement(metaBuilding), {
|
||||
|
||||
@@ -72,6 +72,11 @@ export class HUDKeybindingOverlay extends BaseHUDPart {
|
||||
<code class="keybinding shift">ALT</code>
|
||||
<label>Reverse orientation</label>
|
||||
</div>
|
||||
|
||||
<div class="binding placementOnly">
|
||||
<code class="keybinding shift">CTRL</code>
|
||||
<label>Disable auto orientation</label>
|
||||
</div>
|
||||
` +
|
||||
(queryParamOptions.betaMode
|
||||
? `
|
||||
|
||||
@@ -5,12 +5,7 @@ import { Application } from "../../../application";
|
||||
import { SOUNDS } from "../../../platform/sound";
|
||||
import { DynamicDomAttach } from "../dynamic_dom_attach";
|
||||
import { BaseHUDPart } from "../base_hud_part";
|
||||
import {
|
||||
Dialog,
|
||||
DialogLoading,
|
||||
DialogVideoTutorial,
|
||||
DialogOptionChooser,
|
||||
} from "../../../core/modal_dialog_elements";
|
||||
import { Dialog, DialogLoading, DialogOptionChooser } from "../../../core/modal_dialog_elements";
|
||||
import { makeDiv } from "../../../core/utils";
|
||||
|
||||
export class HUDModalDialogs extends BaseHUDPart {
|
||||
|
||||
@@ -94,7 +94,7 @@ export class HUDShapeStatisticsHandle {
|
||||
if (displayMode === enumDisplayMode.detailed) {
|
||||
const graphDpi = globalConfig.statisticsGraphDpi;
|
||||
|
||||
const w = 300;
|
||||
const w = 270;
|
||||
const h = 40;
|
||||
|
||||
if (!this.graphCanvas) {
|
||||
|
||||
@@ -48,6 +48,8 @@ export const defaultKeybindings = {
|
||||
building_abort_placement: { keyCode: key("Q") },
|
||||
|
||||
rotate_while_placing: { keyCode: key("R") },
|
||||
|
||||
cycle_variants: { keyCode: key("T") },
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -51,13 +51,14 @@ export class GameLogic {
|
||||
* @param {Vector} param0.origin
|
||||
* @param {number} param0.rotation
|
||||
* @param {number} param0.rotationVariant
|
||||
* @param {string} param0.variant
|
||||
* @param {MetaBuilding} param0.building
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isAreaFreeToBuild({ origin, rotation, rotationVariant, building }) {
|
||||
isAreaFreeToBuild({ origin, rotation, rotationVariant, variant, building }) {
|
||||
const checker = new StaticMapEntityComponent({
|
||||
origin,
|
||||
tileSize: building.getDimensions(),
|
||||
tileSize: building.getDimensions(variant),
|
||||
rotation,
|
||||
});
|
||||
|
||||
@@ -122,16 +123,30 @@ export class GameLogic {
|
||||
* @param {Vector} param0.origin
|
||||
* @param {number} param0.rotation
|
||||
* @param {number} param0.rotationVariant
|
||||
* @param {string} param0.variant
|
||||
* @param {MetaBuilding} param0.building
|
||||
*/
|
||||
checkCanPlaceBuilding({ origin, rotation, rotationVariant, building }) {
|
||||
checkCanPlaceBuilding({ origin, rotation, rotationVariant, variant, building }) {
|
||||
if (!building.getIsUnlocked(this.root)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
!building.performAdditionalPlacementChecks(this.root, {
|
||||
origin,
|
||||
rotation,
|
||||
rotationVariant,
|
||||
variant,
|
||||
})
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.isAreaFreeToBuild({
|
||||
origin,
|
||||
rotation,
|
||||
rotationVariant,
|
||||
variant,
|
||||
building,
|
||||
});
|
||||
}
|
||||
@@ -143,14 +158,15 @@ export class GameLogic {
|
||||
* @param {number} param0.rotation
|
||||
* @param {number} param0.originalRotation
|
||||
* @param {number} param0.rotationVariant
|
||||
* @param {string} param0.variant
|
||||
* @param {MetaBuilding} param0.building
|
||||
*/
|
||||
tryPlaceBuilding({ origin, rotation, rotationVariant, originalRotation, building }) {
|
||||
if (this.checkCanPlaceBuilding({ origin, rotation, rotationVariant, building })) {
|
||||
tryPlaceBuilding({ origin, rotation, rotationVariant, originalRotation, variant, building }) {
|
||||
if (this.checkCanPlaceBuilding({ origin, rotation, rotationVariant, variant, building })) {
|
||||
// Remove any removeable entities below
|
||||
const checker = new StaticMapEntityComponent({
|
||||
origin,
|
||||
tileSize: building.getDimensions(),
|
||||
tileSize: building.getDimensions(variant),
|
||||
rotation,
|
||||
});
|
||||
|
||||
@@ -174,6 +190,7 @@ export class GameLogic {
|
||||
rotation,
|
||||
rotationVariant,
|
||||
originalRotation,
|
||||
variant,
|
||||
});
|
||||
|
||||
this.root.soundProxy.playUi(building.getPlacementSound());
|
||||
|
||||
@@ -6,6 +6,8 @@ import { Entity } from "./entity";
|
||||
import { StaticMapEntityComponent } from "./components/static_map_entity";
|
||||
import { SOUNDS } from "../platform/sound";
|
||||
|
||||
export const defaultBuildingVariant = "default";
|
||||
|
||||
export class MetaBuilding {
|
||||
/**
|
||||
*
|
||||
@@ -25,7 +27,7 @@ export class MetaBuilding {
|
||||
/**
|
||||
* Should return the dimensions of the building
|
||||
*/
|
||||
getDimensions() {
|
||||
getDimensions(variant = defaultBuildingVariant) {
|
||||
return new Vector(1, 1);
|
||||
}
|
||||
|
||||
@@ -60,8 +62,9 @@ export class MetaBuilding {
|
||||
|
||||
/**
|
||||
* Whether to rotate automatically in the dragging direction while placing
|
||||
* @param {string} variant
|
||||
*/
|
||||
getRotateAutomaticallyWhilePlacing() {
|
||||
getRotateAutomaticallyWhilePlacing(variant) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -73,20 +76,37 @@ export class MetaBuilding {
|
||||
return SOUNDS.placeBuilding;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {GameRoot} root
|
||||
*/
|
||||
getAvailableVariants(root) {
|
||||
return [defaultBuildingVariant];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a preview sprite
|
||||
* @returns {AtlasSprite}
|
||||
*/
|
||||
getPreviewSprite(rotationVariant = 0) {
|
||||
return Loader.getSprite("sprites/buildings/" + this.id + ".png");
|
||||
getPreviewSprite(rotationVariant = 0, variant = defaultBuildingVariant) {
|
||||
return Loader.getSprite(
|
||||
"sprites/buildings/" +
|
||||
this.id +
|
||||
(variant === defaultBuildingVariant ? "" : "-" + variant) +
|
||||
".png"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a sprite for blueprints
|
||||
* @returns {AtlasSprite}
|
||||
*/
|
||||
getBlueprintSprite(rotationVariant = 0) {
|
||||
return Loader.getSprite("sprites/blueprints/" + this.id + ".png");
|
||||
getBlueprintSprite(rotationVariant = 0, variant = defaultBuildingVariant) {
|
||||
return Loader.getSprite(
|
||||
"sprites/blueprints/" +
|
||||
this.id +
|
||||
(variant === defaultBuildingVariant ? "" : "-" + variant) +
|
||||
".png"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -112,30 +132,49 @@ export class MetaBuilding {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should perform additional placement checks
|
||||
* @param {GameRoot} root
|
||||
* @param {object} param0
|
||||
* @param {Vector} param0.origin
|
||||
* @param {number} param0.rotation
|
||||
* @param {number} param0.rotationVariant
|
||||
* @param {string} param0.variant
|
||||
*/
|
||||
performAdditionalPlacementChecks(root, { origin, rotation, rotationVariant, variant }) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the entity at the given location
|
||||
* @param {object} param0
|
||||
* @param {GameRoot} param0.root
|
||||
* @param {Vector} param0.origin Origin tile
|
||||
* @param {number=} param0.rotation Rotation
|
||||
* @param {number=} param0.originalRotation Original Rotation
|
||||
* @param {number=} param0.rotationVariant Rotation variant
|
||||
* @param {number} param0.originalRotation Original Rotation
|
||||
* @param {number} param0.rotationVariant Rotation variant
|
||||
* @param {string} param0.variant
|
||||
*/
|
||||
createAndPlaceEntity({ root, origin, rotation = 0, originalRotation = 0, rotationVariant = 0 }) {
|
||||
createAndPlaceEntity({ root, origin, rotation, originalRotation, rotationVariant, variant }) {
|
||||
const entity = new Entity(root);
|
||||
entity.addComponent(
|
||||
new StaticMapEntityComponent({
|
||||
spriteKey: "sprites/buildings/" + this.id + ".png",
|
||||
spriteKey:
|
||||
"sprites/buildings/" +
|
||||
this.id +
|
||||
(variant === defaultBuildingVariant ? "" : "-" + variant) +
|
||||
".png",
|
||||
origin: new Vector(origin.x, origin.y),
|
||||
rotation,
|
||||
originalRotation,
|
||||
tileSize: this.getDimensions().copy(),
|
||||
tileSize: this.getDimensions(variant).copy(),
|
||||
silhouetteColor: this.getSilhouetteColor(),
|
||||
})
|
||||
);
|
||||
|
||||
this.setupEntityComponents(entity, root);
|
||||
this.updateRotationVariant(entity, rotationVariant);
|
||||
this.updateVariant(entity, variant);
|
||||
|
||||
root.map.placeStaticEntity(entity);
|
||||
root.entityMgr.registerEntity(entity);
|
||||
@@ -169,6 +208,13 @@ export class MetaBuilding {
|
||||
*/
|
||||
updateRotationVariant(entity, rotationVariant) {}
|
||||
|
||||
/**
|
||||
* Should update the entity to match the given variant
|
||||
* @param {Entity} entity
|
||||
* @param {string} variant
|
||||
*/
|
||||
updateVariant(entity, variant) {}
|
||||
|
||||
// PRIVATE INTERFACE
|
||||
|
||||
/**
|
||||
|
||||
@@ -63,6 +63,10 @@ export class BeltSystem extends GameSystemWithFilter {
|
||||
* @param {Entity} entity
|
||||
*/
|
||||
updateSurroundingBeltPlacement(entity) {
|
||||
if (!this.root.gameInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
const staticComp = entity.components.StaticMapEntity;
|
||||
if (!staticComp) {
|
||||
return;
|
||||
|
||||
@@ -16,11 +16,6 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
|
||||
constructor(root) {
|
||||
super(root, [ItemProcessorComponent]);
|
||||
|
||||
this.sprites = {};
|
||||
for (const key in enumItemProcessorTypes) {
|
||||
this.sprites[key] = Loader.getSprite("sprites/buildings/" + key + ".png");
|
||||
}
|
||||
|
||||
this.underlayBeltSprites = [
|
||||
Loader.getSprite("sprites/belt/forward_0.png"),
|
||||
Loader.getSprite("sprites/belt/forward_1.png"),
|
||||
@@ -121,6 +116,12 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
|
||||
const items = processorComp.inputSlots;
|
||||
processorComp.inputSlots = [];
|
||||
|
||||
/** @type {Object.<string, { item: BaseItem, sourceSlot: number }>} */
|
||||
const itemsBySlot = {};
|
||||
for (let i = 0; i < items.length; ++i) {
|
||||
itemsBySlot[items[i].sourceSlot] = items[i];
|
||||
}
|
||||
|
||||
const baseSpeed = this.root.hubGoals.getProcessorBaseSpeed(processorComp.type);
|
||||
processorComp.secondsUntilEject = 1 / baseSpeed;
|
||||
|
||||
@@ -185,11 +186,9 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
|
||||
// STACKER
|
||||
|
||||
case enumItemProcessorTypes.stacker: {
|
||||
const item1 = items[0];
|
||||
const item2 = items[1];
|
||||
const lowerItem = /** @type {ShapeItem} */ (itemsBySlot[0].item);
|
||||
const upperItem = /** @type {ShapeItem} */ (itemsBySlot[1].item);
|
||||
|
||||
const lowerItem = /** @type {ShapeItem} */ (item1.sourceSlot === 0 ? item1.item : item2.item);
|
||||
const upperItem = /** @type {ShapeItem} */ (item1.sourceSlot === 1 ? item1.item : item2.item);
|
||||
assert(lowerItem instanceof ShapeItem, "Input for lower stack is not a shape");
|
||||
assert(upperItem instanceof ShapeItem, "Input for upper stack is not a shape");
|
||||
|
||||
@@ -238,11 +237,8 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
|
||||
// PAINTER
|
||||
|
||||
case enumItemProcessorTypes.painter: {
|
||||
const item1 = items[0];
|
||||
const item2 = items[1];
|
||||
|
||||
const shapeItem = /** @type {ShapeItem} */ (item1.sourceSlot === 0 ? item1.item : item2.item);
|
||||
const colorItem = /** @type {ColorItem} */ (item1.sourceSlot === 1 ? item1.item : item2.item);
|
||||
const shapeItem = /** @type {ShapeItem} */ (itemsBySlot[0].item);
|
||||
const colorItem = /** @type {ColorItem} */ (itemsBySlot[1].item);
|
||||
|
||||
const colorizedDefinition = this.root.shapeDefinitionMgr.shapeActionPaintWith(
|
||||
shapeItem.definition,
|
||||
@@ -256,6 +252,38 @@ export class ItemProcessorSystem extends GameSystemWithFilter {
|
||||
break;
|
||||
}
|
||||
|
||||
// PAINTER (DOUBLE)
|
||||
|
||||
case enumItemProcessorTypes.painterDouble: {
|
||||
console.log("YUP");
|
||||
const shapeItem1 = /** @type {ShapeItem} */ (itemsBySlot[0].item);
|
||||
const shapeItem2 = /** @type {ShapeItem} */ (itemsBySlot[1].item);
|
||||
const colorItem = /** @type {ColorItem} */ (itemsBySlot[2].item);
|
||||
|
||||
assert(shapeItem1 instanceof ShapeItem, "Input for painter is not a shape");
|
||||
assert(shapeItem2 instanceof ShapeItem, "Input for painter is not a shape");
|
||||
assert(colorItem instanceof ColorItem, "Input for painter is not a color");
|
||||
|
||||
const colorizedDefinition1 = this.root.shapeDefinitionMgr.shapeActionPaintWith(
|
||||
shapeItem1.definition,
|
||||
colorItem.color
|
||||
);
|
||||
|
||||
const colorizedDefinition2 = this.root.shapeDefinitionMgr.shapeActionPaintWith(
|
||||
shapeItem2.definition,
|
||||
colorItem.color
|
||||
);
|
||||
outItems.push({
|
||||
item: new ShapeItem(colorizedDefinition1),
|
||||
});
|
||||
|
||||
outItems.push({
|
||||
item: new ShapeItem(colorizedDefinition2),
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// HUB
|
||||
|
||||
case enumItemProcessorTypes.hub: {
|
||||
|
||||
@@ -4,6 +4,9 @@ import { MinerComponent } from "../components/miner";
|
||||
import { GameSystemWithFilter } from "../game_system_with_filter";
|
||||
import { MapChunkView } from "../map_chunk_view";
|
||||
import { ShapeItem } from "../items/shape_item";
|
||||
import { enumDirectionToVector } from "../../core/vector";
|
||||
import { Entity } from "../entity";
|
||||
import { BaseItem } from "../base_item";
|
||||
|
||||
export class MinerSystem extends GameSystemWithFilter {
|
||||
constructor(root) {
|
||||
@@ -19,35 +22,76 @@ export class MinerSystem extends GameSystemWithFilter {
|
||||
const staticComp = entity.components.StaticMapEntity;
|
||||
const ejectComp = entity.components.ItemEjector;
|
||||
|
||||
if (this.root.time.isIngameTimerExpired(minerComp.lastMiningTime, 1 / miningSpeed)) {
|
||||
if (!ejectComp.canEjectOnSlot(0)) {
|
||||
// We can't eject further
|
||||
// First, try to get rid of chained items
|
||||
if (minerComp.itemChainBuffer.length > 0) {
|
||||
if (this.tryPerformMinerEject(entity, minerComp.itemChainBuffer[0])) {
|
||||
minerComp.itemChainBuffer.shift();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Actually mine
|
||||
minerComp.lastMiningTime = this.root.time.now();
|
||||
|
||||
if (this.root.time.isIngameTimerExpired(minerComp.lastMiningTime, 1 / miningSpeed)) {
|
||||
const lowerLayerItem = this.root.map.getLowerLayerContentXY(
|
||||
staticComp.origin.x,
|
||||
staticComp.origin.y
|
||||
);
|
||||
|
||||
// TODO: Should not be required actually
|
||||
if (!lowerLayerItem) {
|
||||
// Nothing below;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Analytics hook
|
||||
this.root.signals.itemProduced.dispatch(lowerLayerItem);
|
||||
if (this.tryPerformMinerEject(entity, lowerLayerItem)) {
|
||||
// Analytics hook
|
||||
this.root.signals.itemProduced.dispatch(lowerLayerItem);
|
||||
|
||||
// Try actually ejecting
|
||||
if (!ejectComp.tryEject(0, lowerLayerItem)) {
|
||||
assert(false, "Failed to eject");
|
||||
// Actually mine
|
||||
minerComp.lastMiningTime = this.root.time.now();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Entity} entity
|
||||
* @param {BaseItem} item
|
||||
*/
|
||||
tryPerformMinerEject(entity, item) {
|
||||
const minerComp = entity.components.Miner;
|
||||
const ejectComp = entity.components.ItemEjector;
|
||||
const staticComp = entity.components.StaticMapEntity;
|
||||
|
||||
// Check if we are a chained miner
|
||||
if (minerComp.chainable) {
|
||||
const ejectingSlot = ejectComp.slots[0];
|
||||
const ejectingPos = staticComp.localTileToWorld(ejectingSlot.pos);
|
||||
const ejectingDirection = staticComp.localDirectionToWorld(ejectingSlot.direction);
|
||||
|
||||
const targetTile = ejectingPos.add(enumDirectionToVector[ejectingDirection]);
|
||||
const targetContents = this.root.map.getTileContent(targetTile);
|
||||
|
||||
// Check if we are connected to another miner and thus do not eject directly
|
||||
if (targetContents) {
|
||||
const targetMinerComp = targetContents.components.Miner;
|
||||
if (targetMinerComp) {
|
||||
if (targetMinerComp.tryAcceptChainedItem(item)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Seems we are a regular miner or at the end of a row, try actually ejecting
|
||||
if (ejectComp.tryEject(0, item)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {DrawParameters} parameters
|
||||
|
||||
@@ -118,13 +118,13 @@ export const tutorialGoals = [
|
||||
|
||||
// Stacker
|
||||
{
|
||||
shape: "CcCcRgRg",
|
||||
shape: "CgScScCg",
|
||||
required: 3000,
|
||||
reward: enumHubGoalRewards.no_reward,
|
||||
},
|
||||
|
||||
{
|
||||
shape: "RgRgRgRg:CcCcCcCc",
|
||||
shape: "RpRpRpRp:CcCcCcCc",
|
||||
required: 4000,
|
||||
reward: enumHubGoalRewards.no_reward,
|
||||
},
|
||||
|
||||
@@ -5,8 +5,8 @@ import {
|
||||
makeDiv,
|
||||
formatSecondsToTimeAgo,
|
||||
generateFileDownload,
|
||||
removeAllChildren,
|
||||
waitNextFrame,
|
||||
isSupportedBrowser,
|
||||
} from "../core/utils";
|
||||
import { ReadWriteProxy } from "../core/read_write_proxy";
|
||||
import { HUDModalDialogs } from "../game/hud/parts/modal_dialogs";
|
||||
@@ -61,6 +61,14 @@ export class MainMenuState extends GameState {
|
||||
`
|
||||
}
|
||||
<div class="mainContainer">
|
||||
${
|
||||
isSupportedBrowser()
|
||||
? ""
|
||||
: `
|
||||
<div class="browserWarning">This game is optimized for Google Chrome. Your browser is not supported or slow!</div>
|
||||
`
|
||||
}
|
||||
|
||||
<button class="playButton styledButton">Play</button>
|
||||
<button class="importButton styledButton">Import savegame</button>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user