Merge branch 'master' into logic-pain-fix
@ -59,7 +59,7 @@ This project is based on ES5. Some ES2015 features are used but most of them are
|
|||||||
|
|
||||||
1. Create the component file in `src/js/game/components/<name_lowercase>.js`
|
1. Create the component file in `src/js/game/components/<name_lowercase>.js`
|
||||||
2. Create a component class (e.g. `MyFancyComponent`) which `extends Component`
|
2. Create a component class (e.g. `MyFancyComponent`) which `extends Component`
|
||||||
3. Create a `static getId()` method which should return the `CamelCaseName` without component (e.g. `MyFancy`)
|
3. Create a `static getId()` method which should return the `PascalCaseName` without component (e.g. `MyFancy`)
|
||||||
4. If any data needs to be persisted, create a `static getSchema()` which should return the properties to be saved (See other components)
|
4. If any data needs to be persisted, create a `static getSchema()` which should return the properties to be saved (See other components)
|
||||||
5. Add a constructor. **The constructor must be called with optional parameters only!** `new MyFancyComponent({})` should always work.
|
5. Add a constructor. **The constructor must be called with optional parameters only!** `new MyFancyComponent({})` should always work.
|
||||||
6. Add any props you need in the constructor.
|
6. Add any props you need in the constructor.
|
||||||
|
BIN
res/ui/building_icons/virtual_processor.png
Normal file
After Width: | Height: | Size: 7.7 KiB |
BIN
res/ui/icons/display_sorted.png
Normal file
After Width: | Height: | Size: 430 B |
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.3 MiB |
Before Width: | Height: | Size: 238 KiB After Width: | Height: | Size: 266 KiB |
Before Width: | Height: | Size: 567 KiB After Width: | Height: | Size: 636 KiB |
@ -277,6 +277,12 @@
|
|||||||
<key type="filename">sprites/blueprints/underground_belt_entry.png</key>
|
<key type="filename">sprites/blueprints/underground_belt_entry.png</key>
|
||||||
<key type="filename">sprites/blueprints/underground_belt_exit-tier2.png</key>
|
<key type="filename">sprites/blueprints/underground_belt_exit-tier2.png</key>
|
||||||
<key type="filename">sprites/blueprints/underground_belt_exit.png</key>
|
<key type="filename">sprites/blueprints/underground_belt_exit.png</key>
|
||||||
|
<key type="filename">sprites/blueprints/virtual_processor-analyzer.png</key>
|
||||||
|
<key type="filename">sprites/blueprints/virtual_processor-rotater.png</key>
|
||||||
|
<key type="filename">sprites/blueprints/virtual_processor-shapecompare.png</key>
|
||||||
|
<key type="filename">sprites/blueprints/virtual_processor-unstacker.png</key>
|
||||||
|
<key type="filename">sprites/blueprints/virtual_processor.png</key>
|
||||||
|
<key type="filename">sprites/blueprints/wire_tunnel-coating.png</key>
|
||||||
<key type="filename">sprites/blueprints/wire_tunnel.png</key>
|
<key type="filename">sprites/blueprints/wire_tunnel.png</key>
|
||||||
<key type="filename">sprites/buildings/constant_signal.png</key>
|
<key type="filename">sprites/buildings/constant_signal.png</key>
|
||||||
<key type="filename">sprites/buildings/display.png</key>
|
<key type="filename">sprites/buildings/display.png</key>
|
||||||
@ -295,6 +301,12 @@
|
|||||||
<key type="filename">sprites/buildings/underground_belt_entry.png</key>
|
<key type="filename">sprites/buildings/underground_belt_entry.png</key>
|
||||||
<key type="filename">sprites/buildings/underground_belt_exit-tier2.png</key>
|
<key type="filename">sprites/buildings/underground_belt_exit-tier2.png</key>
|
||||||
<key type="filename">sprites/buildings/underground_belt_exit.png</key>
|
<key type="filename">sprites/buildings/underground_belt_exit.png</key>
|
||||||
|
<key type="filename">sprites/buildings/virtual_processor-analyzer.png</key>
|
||||||
|
<key type="filename">sprites/buildings/virtual_processor-rotater.png</key>
|
||||||
|
<key type="filename">sprites/buildings/virtual_processor-shapecompare.png</key>
|
||||||
|
<key type="filename">sprites/buildings/virtual_processor-unstacker.png</key>
|
||||||
|
<key type="filename">sprites/buildings/virtual_processor.png</key>
|
||||||
|
<key type="filename">sprites/buildings/wire_tunnel-coating.png</key>
|
||||||
<key type="filename">sprites/buildings/wire_tunnel.png</key>
|
<key type="filename">sprites/buildings/wire_tunnel.png</key>
|
||||||
<key type="filename">sprites/wires/lever_on.png</key>
|
<key type="filename">sprites/wires/lever_on.png</key>
|
||||||
<key type="filename">sprites/wires/sets/color_cross.png</key>
|
<key type="filename">sprites/wires/sets/color_cross.png</key>
|
||||||
@ -533,6 +545,8 @@
|
|||||||
</struct>
|
</struct>
|
||||||
<key type="filename">sprites/wires/boolean_false.png</key>
|
<key type="filename">sprites/wires/boolean_false.png</key>
|
||||||
<key type="filename">sprites/wires/boolean_true.png</key>
|
<key type="filename">sprites/wires/boolean_true.png</key>
|
||||||
|
<key type="filename">sprites/wires/network_conflict.png</key>
|
||||||
|
<key type="filename">sprites/wires/network_empty.png</key>
|
||||||
<key type="filename">sprites/wires/wires_preview.png</key>
|
<key type="filename">sprites/wires/wires_preview.png</key>
|
||||||
<struct type="IndividualSpriteSettings">
|
<struct type="IndividualSpriteSettings">
|
||||||
<key>pivotPoint</key>
|
<key>pivotPoint</key>
|
||||||
|
Before Width: | Height: | Size: 9.1 KiB After Width: | Height: | Size: 10 KiB |
BIN
res_raw/sprites/blueprints/splitter-compact-merge-inverse.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
res_raw/sprites/blueprints/splitter-compact-merge.png
Normal file
After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 44 KiB |
BIN
res_raw/sprites/blueprints/virtual_processor-analyzer.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
res_raw/sprites/blueprints/virtual_processor-rotater.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
res_raw/sprites/blueprints/virtual_processor-shapecompare.png
Normal file
After Width: | Height: | Size: 8.3 KiB |
BIN
res_raw/sprites/blueprints/virtual_processor-unstacker.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
res_raw/sprites/blueprints/virtual_processor.png
Normal file
After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 8.1 KiB After Width: | Height: | Size: 9.1 KiB |
BIN
res_raw/sprites/buildings/splitter-compact-merge-inverse.png
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
res_raw/sprites/buildings/splitter-compact-merge.png
Normal file
After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 28 KiB |
BIN
res_raw/sprites/buildings/virtual_processor-analyzer.png
Normal file
After Width: | Height: | Size: 9.5 KiB |
BIN
res_raw/sprites/buildings/virtual_processor-rotater.png
Normal file
After Width: | Height: | Size: 9.3 KiB |
BIN
res_raw/sprites/buildings/virtual_processor-shapecompare.png
Normal file
After Width: | Height: | Size: 7.6 KiB |
BIN
res_raw/sprites/buildings/virtual_processor-unstacker.png
Normal file
After Width: | Height: | Size: 8.8 KiB |
BIN
res_raw/sprites/buildings/virtual_processor.png
Normal file
After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 9.5 KiB |
@ -664,7 +664,7 @@ iframe {
|
|||||||
user-select: all;
|
user-select: all;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Steam overlay fiy
|
// Steam overlay fix
|
||||||
#steamOverlayCanvasFix {
|
#steamOverlayCanvasFix {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0px;
|
top: 0px;
|
||||||
|
@ -1,38 +1,38 @@
|
|||||||
$buildings: belt, cutter, miner, mixer, painter, rotater, splitter, stacker, trash, underground_belt, wire,
|
$buildings: belt, cutter, miner, mixer, painter, rotater, splitter, stacker, trash, underground_belt, wire,
|
||||||
constant_signal, logic_gate, lever, filter, wire_tunnel, display;
|
constant_signal, logic_gate, lever, filter, wire_tunnel, display, virtual_processor;
|
||||||
|
|
||||||
@each $building in $buildings {
|
@each $building in $buildings {
|
||||||
[data-icon="building_icons/#{$building}.png"] {
|
[data-icon="building_icons/#{$building}.png"] {
|
||||||
background-image: uiResource("res/ui/building_icons/#{$building}.png") !important;
|
background-image: uiResource("res/ui/building_icons/#{$building}.png") !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$buildingsAndVariants: belt, splitter, splitter-compact, splitter-compact-inverse, underground_belt,
|
$buildingsAndVariants: belt, splitter, splitter-compact, splitter-compact-inverse, underground_belt,
|
||||||
underground_belt-tier2, miner, miner-chainable, cutter, cutter-quad, rotater, rotater-ccw, rotater-fl,
|
underground_belt-tier2, miner, miner-chainable, cutter, cutter-quad, rotater, rotater-ccw, rotater-fl,
|
||||||
stacker, mixer, painter, painter-double, painter-quad, trash, trash-storage;
|
stacker, mixer, painter, painter-double, painter-quad, trash, trash-storage;
|
||||||
@each $building in $buildingsAndVariants {
|
@each $building in $buildingsAndVariants {
|
||||||
[data-icon="building_tutorials/#{$building}.png"] {
|
[data-icon="building_tutorials/#{$building}.png"] {
|
||||||
background-image: uiResource("res/ui/building_tutorials/#{$building}.png") !important;
|
background-image: uiResource("res/ui/building_tutorials/#{$building}.png") !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Special case
|
// Special case
|
||||||
[data-icon="building_tutorials/painter-mirrored.png"] {
|
[data-icon="building_tutorials/painter-mirrored.png"] {
|
||||||
background-image: uiResource("res/ui/building_tutorials/painter.png") !important;
|
background-image: uiResource("res/ui/building_tutorials/painter.png") !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
$icons: notification_saved, notification_success, notification_upgrade;
|
$icons: notification_saved, notification_success, notification_upgrade;
|
||||||
@each $icon in $icons {
|
@each $icon in $icons {
|
||||||
[data-icon="icons/#{$icon}.png"] {
|
[data-icon="icons/#{$icon}.png"] {
|
||||||
background-image: uiResource("res/ui/icons/#{$icon}.png") !important;
|
background-image: uiResource("res/ui/icons/#{$icon}.png") !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$languages: en, de, cs, da, et, es-419, fr, it, pt-BR, sv, tr, el, ru, uk, zh-TW, zh-CN, nb, mt-MT, ar, nl, vi,
|
$languages: en, de, cs, da, et, es-419, fr, it, pt-BR, sv, tr, el, ru, uk, zh-TW, zh-CN, nb, mt-MT, ar, nl, vi,
|
||||||
th, hu, pl, ja, kor, no, pt-PT;
|
th, hu, pl, ja, kor, no, pt-PT;
|
||||||
|
|
||||||
@each $language in $languages {
|
@each $language in $languages {
|
||||||
[data-languageicon="#{$language}"] {
|
[data-languageicon="#{$language}"] {
|
||||||
background-image: uiResource("languages/#{$language}.svg") !important;
|
background-image: uiResource("languages/#{$language}.svg") !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,20 +14,37 @@
|
|||||||
@include S(padding, 1px, 10px);
|
@include S(padding, 1px, 10px);
|
||||||
border: 0;
|
border: 0;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
border-radius: 0;
|
|
||||||
@include IncreasedClickArea(1px);
|
@include IncreasedClickArea(1px);
|
||||||
@include S(min-width, 30px);
|
@include S(min-width, 30px);
|
||||||
color: #fff;
|
color: #fff;
|
||||||
opacity: 0.25;
|
opacity: 0.25;
|
||||||
@include S(margin-left, 1px);
|
@include S(border-radius, $globalBorderRadius);
|
||||||
|
|
||||||
|
border-radius: 0;
|
||||||
|
&:first-child {
|
||||||
|
@include S(border-top-left-radius, $globalBorderRadius);
|
||||||
|
@include S(border-bottom-left-radius, $globalBorderRadius);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
@include S(border-top-right-radius, $globalBorderRadius);
|
||||||
|
@include S(border-bottom-right-radius, $globalBorderRadius);
|
||||||
|
}
|
||||||
|
|
||||||
&.displayIcons,
|
&.displayIcons,
|
||||||
&.displayDetailed {
|
&.displayDetailed,
|
||||||
|
&.displaySorted {
|
||||||
background: uiResource("icons/display_list.png") center center / #{D(15px)} no-repeat;
|
background: uiResource("icons/display_list.png") center center / #{D(15px)} no-repeat;
|
||||||
&.displayIcons {
|
&.displayIcons {
|
||||||
background-image: uiResource("icons/display_icons.png");
|
background-image: uiResource("icons/display_icons.png");
|
||||||
background-size: #{D(11.5px)};
|
background-size: #{D(11.5px)};
|
||||||
}
|
}
|
||||||
|
&.displaySorted {
|
||||||
|
background-image: uiResource("icons/display_sorted.png");
|
||||||
|
background-size: #{D(11.5px)};
|
||||||
|
margin-right: 4px;
|
||||||
|
@include S(padding, 1px, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
background-color: #44484a !important;
|
background-color: #44484a !important;
|
||||||
@ -80,10 +97,7 @@
|
|||||||
background: #f4f4f4;
|
background: #f4f4f4;
|
||||||
@include S(margin-bottom, 4px);
|
@include S(margin-bottom, 4px);
|
||||||
display: grid;
|
display: grid;
|
||||||
|
@include S(border-radius, $globalBorderRadius);
|
||||||
@include DarkThemeOverride {
|
|
||||||
background: #222428;
|
|
||||||
}
|
|
||||||
|
|
||||||
grid-template-columns: 1fr auto;
|
grid-template-columns: 1fr auto;
|
||||||
@include S(padding, 5px);
|
@include S(padding, 5px);
|
||||||
@ -91,6 +105,18 @@
|
|||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.pinned {
|
||||||
|
background: #e3e5e9;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include DarkThemeOverride {
|
||||||
|
background: #222428;
|
||||||
|
|
||||||
|
&.pinned {
|
||||||
|
background: darken(#222428, 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
canvas.icon {
|
canvas.icon {
|
||||||
grid-column: 1 / 2;
|
grid-column: 1 / 2;
|
||||||
grid-row: 1 / 2;
|
grid-row: 1 / 2;
|
||||||
@ -100,7 +126,6 @@
|
|||||||
|
|
||||||
.counter {
|
.counter {
|
||||||
@include SuperSmallText;
|
@include SuperSmallText;
|
||||||
|
|
||||||
@include S(padding, 0, 3px);
|
@include S(padding, 0, 3px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -109,6 +134,7 @@
|
|||||||
.dialogInner {
|
.dialogInner {
|
||||||
&[data-displaymode="detailed"] .displayDetailed,
|
&[data-displaymode="detailed"] .displayDetailed,
|
||||||
&[data-displaymode="icons"] .displayIcons,
|
&[data-displaymode="icons"] .displayIcons,
|
||||||
|
&[data-sorted="true"] .displaySorted,
|
||||||
&[data-datasource="produced"] .modeProduced,
|
&[data-datasource="produced"] .modeProduced,
|
||||||
&[data-datasource="delivered"] .modeDelivered,
|
&[data-datasource="delivered"] .modeDelivered,
|
||||||
&[data-datasource="stored"] .modeStored {
|
&[data-datasource="stored"] .modeStored {
|
||||||
@ -132,7 +158,6 @@
|
|||||||
.counter {
|
.counter {
|
||||||
grid-column: 1 / 2;
|
grid-column: 1 / 2;
|
||||||
grid-row: 2 / 3;
|
grid-row: 2 / 3;
|
||||||
background: rgba(0, 10, 20, 0.05);
|
|
||||||
justify-self: end;
|
justify-self: end;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,124 +1,124 @@
|
|||||||
// Control here whether to inline all resources or instead load them
|
// Control here whether to inline all resources or instead load them
|
||||||
@function uiResource($pth) {
|
@function uiResource($pth) {
|
||||||
@if (str-index($string: $pth, $substring: ".noinline")) {
|
@if (str-index($string: $pth, $substring: ".noinline")) {
|
||||||
@return resolve($pth);
|
@return resolve($pth);
|
||||||
}
|
}
|
||||||
@return inline($pth);
|
@return inline($pth);
|
||||||
}
|
}
|
||||||
|
|
||||||
@import "icons";
|
@import "icons";
|
||||||
@import "trigonometry";
|
@import "trigonometry";
|
||||||
@import "material_colors";
|
@import "material_colors";
|
||||||
@import "dynamic_ui";
|
@import "dynamic_ui";
|
||||||
@import "variables";
|
@import "variables";
|
||||||
|
|
||||||
@import "mixins";
|
@import "mixins";
|
||||||
@import "common";
|
@import "common";
|
||||||
@import "animations";
|
@import "animations";
|
||||||
@import "game_state";
|
@import "game_state";
|
||||||
@import "application_error";
|
@import "application_error";
|
||||||
@import "textual_game_state";
|
@import "textual_game_state";
|
||||||
@import "adinplay";
|
@import "adinplay";
|
||||||
|
|
||||||
@import "states/preload";
|
@import "states/preload";
|
||||||
@import "states/main_menu";
|
@import "states/main_menu";
|
||||||
@import "states/ingame";
|
@import "states/ingame";
|
||||||
@import "states/keybindings";
|
@import "states/keybindings";
|
||||||
@import "states/settings";
|
@import "states/settings";
|
||||||
@import "states/about";
|
@import "states/about";
|
||||||
@import "states/mobile_warning";
|
@import "states/mobile_warning";
|
||||||
@import "states/changelog";
|
@import "states/changelog";
|
||||||
|
|
||||||
@import "ingame_hud/buildings_toolbar";
|
@import "ingame_hud/buildings_toolbar";
|
||||||
@import "ingame_hud/building_placer";
|
@import "ingame_hud/building_placer";
|
||||||
@import "ingame_hud/beta_overlay";
|
@import "ingame_hud/beta_overlay";
|
||||||
@import "ingame_hud/keybindings_overlay";
|
@import "ingame_hud/keybindings_overlay";
|
||||||
@import "ingame_hud/unlock_notification";
|
@import "ingame_hud/unlock_notification";
|
||||||
@import "ingame_hud/shop";
|
@import "ingame_hud/shop";
|
||||||
@import "ingame_hud/game_menu";
|
@import "ingame_hud/game_menu";
|
||||||
@import "ingame_hud/dialogs";
|
@import "ingame_hud/dialogs";
|
||||||
@import "ingame_hud/vignette_overlay";
|
@import "ingame_hud/vignette_overlay";
|
||||||
@import "ingame_hud/statistics";
|
@import "ingame_hud/statistics";
|
||||||
@import "ingame_hud/pinned_shapes";
|
@import "ingame_hud/pinned_shapes";
|
||||||
@import "ingame_hud/notifications";
|
@import "ingame_hud/notifications";
|
||||||
@import "ingame_hud/settings_menu";
|
@import "ingame_hud/settings_menu";
|
||||||
@import "ingame_hud/debug_info";
|
@import "ingame_hud/debug_info";
|
||||||
@import "ingame_hud/entity_debugger";
|
@import "ingame_hud/entity_debugger";
|
||||||
@import "ingame_hud/tutorial_hints";
|
@import "ingame_hud/tutorial_hints";
|
||||||
@import "ingame_hud/watermark";
|
@import "ingame_hud/watermark";
|
||||||
@import "ingame_hud/blueprint_placer";
|
@import "ingame_hud/blueprint_placer";
|
||||||
@import "ingame_hud/waypoints";
|
@import "ingame_hud/waypoints";
|
||||||
@import "ingame_hud/interactive_tutorial";
|
@import "ingame_hud/interactive_tutorial";
|
||||||
@import "ingame_hud/color_blind_helper";
|
@import "ingame_hud/color_blind_helper";
|
||||||
@import "ingame_hud/shape_viewer";
|
@import "ingame_hud/shape_viewer";
|
||||||
@import "ingame_hud/sandbox_controller";
|
@import "ingame_hud/sandbox_controller";
|
||||||
|
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
$elements:
|
$elements:
|
||||||
// Base
|
// Base
|
||||||
ingame_Canvas,
|
ingame_Canvas,
|
||||||
ingame_VignetteOverlay,
|
ingame_VignetteOverlay,
|
||||||
|
|
||||||
// Ingame overlays
|
// Ingame overlays
|
||||||
ingame_HUD_Waypoints,
|
ingame_HUD_Waypoints,
|
||||||
ingame_HUD_PlacementHints,
|
ingame_HUD_PlacementHints,
|
||||||
ingame_HUD_PlacerVariants,
|
ingame_HUD_PlacerVariants,
|
||||||
|
|
||||||
// Regular hud
|
// Regular hud
|
||||||
ingame_HUD_PinnedShapes,
|
ingame_HUD_PinnedShapes,
|
||||||
ingame_HUD_GameMenu,
|
ingame_HUD_GameMenu,
|
||||||
ingame_HUD_KeybindingOverlay,
|
ingame_HUD_KeybindingOverlay,
|
||||||
ingame_HUD_Notifications,
|
ingame_HUD_Notifications,
|
||||||
ingame_HUD_DebugInfo,
|
ingame_HUD_DebugInfo,
|
||||||
ingame_HUD_EntityDebugger,
|
ingame_HUD_EntityDebugger,
|
||||||
ingame_HUD_InteractiveTutorial,
|
ingame_HUD_InteractiveTutorial,
|
||||||
ingame_HUD_TutorialHints,
|
ingame_HUD_TutorialHints,
|
||||||
ingame_HUD_buildings_toolbar,
|
ingame_HUD_buildings_toolbar,
|
||||||
ingame_HUD_wires_toolbar,
|
ingame_HUD_wires_toolbar,
|
||||||
ingame_HUD_BlueprintPlacer,
|
ingame_HUD_BlueprintPlacer,
|
||||||
ingame_HUD_Waypoints_Hint,
|
ingame_HUD_Waypoints_Hint,
|
||||||
ingame_HUD_Watermark,
|
ingame_HUD_Watermark,
|
||||||
ingame_HUD_ColorBlindBelowTileHelper,
|
ingame_HUD_ColorBlindBelowTileHelper,
|
||||||
ingame_HUD_SandboxController,
|
ingame_HUD_SandboxController,
|
||||||
|
|
||||||
// Overlays
|
// Overlays
|
||||||
ingame_HUD_BetaOverlay,
|
ingame_HUD_BetaOverlay,
|
||||||
|
|
||||||
// Dialogs
|
// Dialogs
|
||||||
ingame_HUD_Shop,
|
ingame_HUD_Shop,
|
||||||
ingame_HUD_Statistics,
|
ingame_HUD_Statistics,
|
||||||
ingame_HUD_ShapeViewer,
|
ingame_HUD_ShapeViewer,
|
||||||
ingame_HUD_UnlockNotification,
|
ingame_HUD_UnlockNotification,
|
||||||
ingame_HUD_SettingsMenu,
|
ingame_HUD_SettingsMenu,
|
||||||
ingame_HUD_ModalDialogs;
|
ingame_HUD_ModalDialogs;
|
||||||
|
|
||||||
$zindex: 100;
|
$zindex: 100;
|
||||||
|
|
||||||
@each $elem in $elements {
|
@each $elem in $elements {
|
||||||
##{$elem} {
|
##{$elem} {
|
||||||
z-index: $zindex;
|
z-index: $zindex;
|
||||||
}
|
}
|
||||||
|
|
||||||
$zindex: $zindex + 10;
|
$zindex: $zindex + 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.uiHidden {
|
body.uiHidden {
|
||||||
#ingame_HUD_buildings_toolbar,
|
.ingame_buildingsToolbar,
|
||||||
#ingame_HUD_PlacementHints,
|
#ingame_HUD_PlacementHints,
|
||||||
#ingame_HUD_GameMenu,
|
#ingame_HUD_GameMenu,
|
||||||
#ingame_HUD_PinnedShapes,
|
#ingame_HUD_PinnedShapes,
|
||||||
#ingame_HUD_Notifications,
|
#ingame_HUD_Notifications,
|
||||||
#ingame_HUD_TutorialHints,
|
#ingame_HUD_TutorialHints,
|
||||||
#ingame_HUD_Waypoints,
|
#ingame_HUD_Waypoints,
|
||||||
#ingame_HUD_Waypoints_Hint {
|
#ingame_HUD_Waypoints_Hint {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
body.modalDialogActive,
|
body.modalDialogActive,
|
||||||
body.externalAdOpen,
|
body.externalAdOpen,
|
||||||
body.ingameDialogOpen {
|
body.ingameDialogOpen {
|
||||||
> *:not(.ingameDialog):not(.modalDialogParent):not(.loadingDialog):not(.gameLoadingOverlay):not(#ingame_HUD_ModalDialogs):not(.noBlur) {
|
> *:not(.ingameDialog):not(.modalDialogParent):not(.loadingDialog):not(.gameLoadingOverlay):not(#ingame_HUD_ModalDialogs):not(.noBlur) {
|
||||||
// filter: blur(5px) !important;
|
// filter: blur(5px) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,300 +1,304 @@
|
|||||||
export const CHANGELOG = [
|
export const CHANGELOG = [
|
||||||
{
|
{
|
||||||
version: "1.2.0",
|
version: "1.2.0",
|
||||||
date: "unreleased",
|
date: "unreleased",
|
||||||
entries: [
|
entries: [
|
||||||
"WIRES",
|
"WIRES",
|
||||||
"Reworked menu UI design (by dengr1605)",
|
"Reworked menu UI design (by dengr1605)",
|
||||||
"Allow holding ALT in belt planner to reverse direction (by jakobhellermann)",
|
"Allow holding ALT in belt planner to reverse direction (by jakobhellermann)",
|
||||||
"Clear cursor when trying to pipette the same building twice (by hexy)",
|
"Clear cursor when trying to pipette the same building twice (by hexy)",
|
||||||
"Fixed level 18 stacker bug: If you experienced it already, you know it, if not, I don't want to spoiler (by hexy)",
|
"Fixed level 18 stacker bug: If you experienced it already, you know it, if not, I don't want to spoiler (by hexy)",
|
||||||
"Added keybinding to close menus (by isaisstillalive / Sandwichs-del)",
|
"Added keybinding to close menus (by isaisstillalive / Sandwichs-del)",
|
||||||
"Fix rare crash regarding the buildings toolbar (by isaisstillalive)",
|
"Fix rare crash regarding the buildings toolbar (by isaisstillalive)",
|
||||||
"Fixed some phrases (by EnderDoom77)",
|
"Fixed some phrases (by EnderDoom77)",
|
||||||
"Zoom towards mouse cursor (by Dimava)",
|
"Zoom towards mouse cursor (by Dimava)",
|
||||||
"Added multiple settings to optimize the performance",
|
"Added multiple settings to optimize the performance",
|
||||||
"Updated the soundtrack again, it is now 40 minutes in total!",
|
"Updated the soundtrack again, it is now 40 minutes in total!",
|
||||||
"Updated and added new translations (Thanks to all contributors!)",
|
"Added a button to the statistics dialog to disable the sorting (by squeek502)",
|
||||||
"Allow editing waypoints (by isaisstillalive)",
|
"Updated and added new translations (Thanks to all contributors!)",
|
||||||
"Show confirmation when cutting area which is too expensive to get pasted again (by isaisstillalive)",
|
"Added setting to be able to delete buildings while placing (inspired by hexy)",
|
||||||
"Show mouse and camera tile on debug overlay (F4) (by dengr)",
|
"Mark pinned shapes in statistics dialog and show them first (inspired by davidburhans)",
|
||||||
"Fix tunnels entrances connecting to exits sometimes when they shouldn't",
|
"There are now compact 1x1 splitters available to be unlocked!",
|
||||||
"The initial belt planner direction is now based on the cursor movement (by MizardX)",
|
"Allow editing waypoints (by isaisstillalive)",
|
||||||
"Fix preferred variant not getting saved when clicking on the hud (by Danacus)",
|
"Show confirmation when cutting area which is too expensive to get pasted again (by isaisstillalive)",
|
||||||
],
|
"Show mouse and camera tile on debug overlay (F4) (by dengr)",
|
||||||
},
|
"Fix tunnels entrances connecting to exits sometimes when they shouldn't",
|
||||||
{
|
"The initial belt planner direction is now based on the cursor movement (by MizardX)",
|
||||||
version: "1.1.19",
|
"Fix preferred variant not getting saved when clicking on the hud (by Danacus)",
|
||||||
date: "02.07.2020",
|
],
|
||||||
entries: [
|
},
|
||||||
"There are now notifications every 15 minutes in the demo version to buy the full version (For further details and the reason, check the #surveys channel in the Discord)",
|
{
|
||||||
"I'm still working on the wires update, I hope to release it mid july!",
|
version: "1.1.19",
|
||||||
],
|
date: "02.07.2020",
|
||||||
},
|
entries: [
|
||||||
{
|
"There are now notifications every 15 minutes in the demo version to buy the full version (For further details and the reason, check the #surveys channel in the Discord)",
|
||||||
version: "1.1.18",
|
"I'm still working on the wires update, I hope to release it mid july!",
|
||||||
date: "27.06.2020",
|
],
|
||||||
entries: [
|
},
|
||||||
"Huge performance improvements - up to double fps and tick-rate! This will wipe out all current items on belts.",
|
{
|
||||||
"Reduce story shapes required until unlocking blueprints",
|
version: "1.1.18",
|
||||||
"Allow clicking on variants to select them",
|
date: "27.06.2020",
|
||||||
"Add 'copy key' button to shape viewer",
|
entries: [
|
||||||
"Add more FPS to the belt animation and fix belt animation seeming to go 'backwards' on high belt speeds",
|
"Huge performance improvements - up to double fps and tick-rate! This will wipe out all current items on belts.",
|
||||||
"Fix deconstruct sound being played when right clicking hub",
|
"Reduce story shapes required until unlocking blueprints",
|
||||||
"Allow clicking 'Q' over a shape or color patch to automatically select the miner building (by Gerdon262)",
|
"Allow clicking on variants to select them",
|
||||||
"Update belt placement performance on huge factories (by Phlosioneer)",
|
"Add 'copy key' button to shape viewer",
|
||||||
"Fix duplicate waypoints with a shape not rendering (by hexy)",
|
"Add more FPS to the belt animation and fix belt animation seeming to go 'backwards' on high belt speeds",
|
||||||
"Fix smart tunnel placement deleting wrong tunnels (by mordof)",
|
"Fix deconstruct sound being played when right clicking hub",
|
||||||
"Add setting (on by default) to store the last used rotation per building instead of globally storing it (by Magos)",
|
"Allow clicking 'Q' over a shape or color patch to automatically select the miner building (by Gerdon262)",
|
||||||
"Added chinese (traditional) translation",
|
"Update belt placement performance on huge factories (by Phlosioneer)",
|
||||||
"Updated translations",
|
"Fix duplicate waypoints with a shape not rendering (by hexy)",
|
||||||
],
|
"Fix smart tunnel placement deleting wrong tunnels (by mordof)",
|
||||||
},
|
"Add setting (on by default) to store the last used rotation per building instead of globally storing it (by Magos)",
|
||||||
{
|
"Added chinese (traditional) translation",
|
||||||
version: "1.1.17",
|
"Updated translations",
|
||||||
date: "22.06.2020",
|
],
|
||||||
entries: [
|
},
|
||||||
"Color blind mode! You can now activate it in the settings and it will show you which color is below your cursor (Either resource or on the belt)",
|
{
|
||||||
"Add info buttons to all shapes so you can figure out how they are built! (And also, which colors they have)",
|
version: "1.1.17",
|
||||||
"Allow configuring autosave interval and disabling it in the settings",
|
date: "22.06.2020",
|
||||||
"The smart-tunnel placement has been reworked to properly replace belts. Thus the setting has been turned on again by default",
|
entries: [
|
||||||
"The soundtrack now has a higher quality on the standalone version than the web version",
|
"Color blind mode! You can now activate it in the settings and it will show you which color is below your cursor (Either resource or on the belt)",
|
||||||
"Add setting to disable cut/delete warnings (by hexy)",
|
"Add info buttons to all shapes so you can figure out how they are built! (And also, which colors they have)",
|
||||||
"Fix bug where belts in blueprints don't orient correctly (by hexy)",
|
"Allow configuring autosave interval and disabling it in the settings",
|
||||||
"Fix camera moving weird after dragging and holding (by hexy)",
|
"The smart-tunnel placement has been reworked to properly replace belts. Thus the setting has been turned on again by default",
|
||||||
"Fix keybinding for pipette showing while pasting blueprints",
|
"The soundtrack now has a higher quality on the standalone version than the web version",
|
||||||
"Improve visibility of shape background in dark mode",
|
"Add setting to disable cut/delete warnings (by hexy)",
|
||||||
"Added sound when destroying a building",
|
"Fix bug where belts in blueprints don't orient correctly (by hexy)",
|
||||||
"Added swedish translation",
|
"Fix camera moving weird after dragging and holding (by hexy)",
|
||||||
"Update tutorial image for tier 2 tunnels to explain mix/match (by jimmyshadow1)",
|
"Fix keybinding for pipette showing while pasting blueprints",
|
||||||
],
|
"Improve visibility of shape background in dark mode",
|
||||||
},
|
"Added sound when destroying a building",
|
||||||
{
|
"Added swedish translation",
|
||||||
version: "1.1.16",
|
"Update tutorial image for tier 2 tunnels to explain mix/match (by jimmyshadow1)",
|
||||||
date: "21.06.2020",
|
],
|
||||||
entries: [
|
},
|
||||||
"You can now pickup buildings below your cursor with 'Q'!",
|
{
|
||||||
"The game soundtrack has been extended! There are now 4 songs with over 13 minutes of playtime from <a href='https://soundcloud.com/pettersumelius' target='blank'>Peppsen</a>!",
|
version: "1.1.16",
|
||||||
"Refactor keybindings overlay to show more appropriate keybindings",
|
date: "21.06.2020",
|
||||||
"Show keybindings for area-select in the upper left instead",
|
entries: [
|
||||||
"Automatically deselect area when selecting a new building",
|
"You can now pickup buildings below your cursor with 'Q'!",
|
||||||
"Raise markers limit from 14 characters to 71 (by Joker-vD)",
|
"The game soundtrack has been extended! There are now 4 songs with over 13 minutes of playtime from <a href='https://soundcloud.com/pettersumelius' target='blank'>Peppsen</a>!",
|
||||||
"Optimize performance by caching extractor items (by Phlosioneer)",
|
"Refactor keybindings overlay to show more appropriate keybindings",
|
||||||
"Added setting to enable compact building infos, which only show ratios and hide the image / description",
|
"Show keybindings for area-select in the upper left instead",
|
||||||
"Apply dark theme to menu as well (by dengr1065)",
|
"Automatically deselect area when selecting a new building",
|
||||||
"Fix belt planner not placing the last belt",
|
"Raise markers limit from 14 characters to 71 (by Joker-vD)",
|
||||||
"Fix buildings getting deleted when right clicking while placing a blueprint",
|
"Optimize performance by caching extractor items (by Phlosioneer)",
|
||||||
"Fix for exporting screenshots for huge bases (It was showing an empty file) (by xSparfuchs)",
|
"Added setting to enable compact building infos, which only show ratios and hide the image / description",
|
||||||
"Fix buttons not responding when using right click directly after left click (by davidburhans)",
|
"Apply dark theme to menu as well (by dengr1065)",
|
||||||
"Fix hub marker being hidden by building info panel",
|
"Fix belt planner not placing the last belt",
|
||||||
"Disable dialog background blur since it can cause performance issues",
|
"Fix buildings getting deleted when right clicking while placing a blueprint",
|
||||||
"Added simplified chinese translations",
|
"Fix for exporting screenshots for huge bases (It was showing an empty file) (by xSparfuchs)",
|
||||||
"Update translations (Thanks to all translators!)",
|
"Fix buttons not responding when using right click directly after left click (by davidburhans)",
|
||||||
],
|
"Fix hub marker being hidden by building info panel",
|
||||||
},
|
"Disable dialog background blur since it can cause performance issues",
|
||||||
{
|
"Added simplified chinese translations",
|
||||||
version: "1.1.15",
|
"Update translations (Thanks to all translators!)",
|
||||||
date: "17.06.2020",
|
],
|
||||||
entries: [
|
},
|
||||||
"You can now place straight belts (and tunnels) by holding SHIFT! (For you, @giantwaffle ❤️)",
|
{
|
||||||
"Added continue button to main menu and add seperate 'New game' button (by jaysc)",
|
version: "1.1.15",
|
||||||
"Added setting to disable smart tunnel placement introduced with the last update",
|
date: "17.06.2020",
|
||||||
"Added setting to disable vignette",
|
entries: [
|
||||||
"Update translations",
|
"You can now place straight belts (and tunnels) by holding SHIFT! (For you, @giantwaffle ❤️)",
|
||||||
],
|
"Added continue button to main menu and add seperate 'New game' button (by jaysc)",
|
||||||
},
|
"Added setting to disable smart tunnel placement introduced with the last update",
|
||||||
{
|
"Added setting to disable vignette",
|
||||||
version: "1.1.14",
|
"Update translations",
|
||||||
date: "16.06.2020",
|
],
|
||||||
entries: [
|
},
|
||||||
"There is now an indicator (compass) to the HUB for the HUB Marker!",
|
{
|
||||||
"You can now include shape short keys in markers to render shape icons instead of text!",
|
version: "1.1.14",
|
||||||
"Added mirrored variant of the painter",
|
date: "16.06.2020",
|
||||||
"When placing tunnels, unnecessary belts inbetween are now removed!",
|
entries: [
|
||||||
"You can now drag tunnels and they will automatically expand! (Just try it out, its intuitive)",
|
"There is now an indicator (compass) to the HUB for the HUB Marker!",
|
||||||
],
|
"You can now include shape short keys in markers to render shape icons instead of text!",
|
||||||
},
|
"Added mirrored variant of the painter",
|
||||||
{
|
"When placing tunnels, unnecessary belts inbetween are now removed!",
|
||||||
version: "1.1.13",
|
"You can now drag tunnels and they will automatically expand! (Just try it out, its intuitive)",
|
||||||
date: "15.06.2020",
|
],
|
||||||
entries: [
|
},
|
||||||
"Added shift modifier for faster pan (by jaysc)",
|
{
|
||||||
"Added Japanese translations",
|
version: "1.1.13",
|
||||||
"Added Portuguese (Portugal) translations",
|
date: "15.06.2020",
|
||||||
"Updated icon for Spanish (Latin America) - It was showing a Spanish flag before",
|
entries: [
|
||||||
"Updated existing translations",
|
"Added shift modifier for faster pan (by jaysc)",
|
||||||
],
|
"Added Japanese translations",
|
||||||
},
|
"Added Portuguese (Portugal) translations",
|
||||||
{
|
"Updated icon for Spanish (Latin America) - It was showing a Spanish flag before",
|
||||||
version: "1.1.12",
|
"Updated existing translations",
|
||||||
date: "14.06.2020",
|
],
|
||||||
entries: [
|
},
|
||||||
"Huge performance improvements! The game should now run up to 60% faster!",
|
{
|
||||||
"Added norwegian translation",
|
version: "1.1.12",
|
||||||
],
|
date: "14.06.2020",
|
||||||
},
|
entries: [
|
||||||
{
|
"Huge performance improvements! The game should now run up to 60% faster!",
|
||||||
version: "1.1.11",
|
"Added norwegian translation",
|
||||||
date: "13.06.2020",
|
],
|
||||||
entries: [
|
},
|
||||||
"Pinned shapes are now smart, they dynamically update their goal and also unpin when no longer required. Completed objectives are now rendered transparent.",
|
{
|
||||||
"You can now cut areas, and also paste the last blueprint again! (by hexy)",
|
version: "1.1.11",
|
||||||
"You can now export your whole base as an image by pressing F3!",
|
date: "13.06.2020",
|
||||||
"Improve upgrade number rounding, so there are no goals like '37.4k', instead it will now be '35k'",
|
entries: [
|
||||||
"You can now configure the camera movement speed when using WASD (by mini-bomba)",
|
"Pinned shapes are now smart, they dynamically update their goal and also unpin when no longer required. Completed objectives are now rendered transparent.",
|
||||||
"Selecting an area now is relative to the world and thus does not move when moving the screen (by Dimava)",
|
"You can now cut areas, and also paste the last blueprint again! (by hexy)",
|
||||||
"Allow higher tick-rates up to 500hz (This will burn your PC!)",
|
"You can now export your whole base as an image by pressing F3!",
|
||||||
"Fix bug regarding number rounding",
|
"Improve upgrade number rounding, so there are no goals like '37.4k', instead it will now be '35k'",
|
||||||
"Fix dialog text being hardly readable in dark theme",
|
"You can now configure the camera movement speed when using WASD (by mini-bomba)",
|
||||||
"Fix app not starting when the savegames were corrupted - there is now a better error message as well.",
|
"Selecting an area now is relative to the world and thus does not move when moving the screen (by Dimava)",
|
||||||
"Further translation updates - Big thanks to all contributors!",
|
"Allow higher tick-rates up to 500hz (This will burn your PC!)",
|
||||||
],
|
"Fix bug regarding number rounding",
|
||||||
},
|
"Fix dialog text being hardly readable in dark theme",
|
||||||
{
|
"Fix app not starting when the savegames were corrupted - there is now a better error message as well.",
|
||||||
version: "1.1.10",
|
"Further translation updates - Big thanks to all contributors!",
|
||||||
date: "12.06.2020",
|
],
|
||||||
entries: [
|
},
|
||||||
"There are now linux builds on steam! Please report any issues in the Discord!",
|
{
|
||||||
"Steam cloud saves are now available!",
|
version: "1.1.10",
|
||||||
"Added and update more translations (Big thank you to all translators!)",
|
date: "12.06.2020",
|
||||||
"Prevent invalid connection if existing underground tunnel entrance exists (by jaysc)",
|
entries: [
|
||||||
],
|
"There are now linux builds on steam! Please report any issues in the Discord!",
|
||||||
},
|
"Steam cloud saves are now available!",
|
||||||
{
|
"Added and update more translations (Big thank you to all translators!)",
|
||||||
version: "1.1.9",
|
"Prevent invalid connection if existing underground tunnel entrance exists (by jaysc)",
|
||||||
date: "11.06.2020",
|
],
|
||||||
entries: [
|
},
|
||||||
"Support for translations! Interested in helping out? Check out the <a target='_blank' href='https://github.com/tobspr/shapez.io/tree/master/translations'>translation guide</a>!",
|
{
|
||||||
"Update stacker artwork to clarify how it works",
|
version: "1.1.9",
|
||||||
"Update keybinding hints on the top left to be more accurate",
|
date: "11.06.2020",
|
||||||
"Make it more clear when blueprints are unlocked when trying to use them",
|
entries: [
|
||||||
"Fix pinned shape icons not being visible in dark mode",
|
"Support for translations! Interested in helping out? Check out the <a target='_blank' href='https://github.com/tobspr/shapez.io/tree/master/translations'>translation guide</a>!",
|
||||||
"Fix being able to select buildings via hotkeys in map overview mode",
|
"Update stacker artwork to clarify how it works",
|
||||||
"Make shapes unpinnable in the upgrades tab (By hexy)",
|
"Update keybinding hints on the top left to be more accurate",
|
||||||
],
|
"Make it more clear when blueprints are unlocked when trying to use them",
|
||||||
},
|
"Fix pinned shape icons not being visible in dark mode",
|
||||||
{
|
"Fix being able to select buildings via hotkeys in map overview mode",
|
||||||
version: "1.1.8",
|
"Make shapes unpinnable in the upgrades tab (By hexy)",
|
||||||
date: "07.06.2020",
|
],
|
||||||
entries: [
|
},
|
||||||
"You can now purchase the standalone on steam! <a href='https://steam.shapez.io' target='blank'>View steam page</a>",
|
{
|
||||||
"Added ability to create markers in the demo, but only two.",
|
version: "1.1.8",
|
||||||
"Contest #01 has ended! I'll now work through the entries, select the 5 I like most and present them to the community to vote for!",
|
date: "07.06.2020",
|
||||||
],
|
entries: [
|
||||||
},
|
"You can now purchase the standalone on steam! <a href='https://steam.shapez.io' target='blank'>View steam page</a>",
|
||||||
{
|
"Added ability to create markers in the demo, but only two.",
|
||||||
version: "1.1.7",
|
"Contest #01 has ended! I'll now work through the entries, select the 5 I like most and present them to the community to vote for!",
|
||||||
date: "04.06.2020",
|
],
|
||||||
entries: ["HOTFIX: Fix savegames not showing up on the standalone version"],
|
},
|
||||||
},
|
{
|
||||||
{
|
version: "1.1.7",
|
||||||
version: "1.1.6",
|
date: "04.06.2020",
|
||||||
date: "04.06.2020",
|
entries: ["HOTFIX: Fix savegames not showing up on the standalone version"],
|
||||||
entries: [
|
},
|
||||||
"The steam release will happen on the <strong>7th of June</strong> - Be sure to add it to your wishlist! <a href='https://steam.shapez.io' target='blank'>View on steam</a>",
|
{
|
||||||
"Fixed level complete dialog being blurred when the shop was opened before",
|
version: "1.1.6",
|
||||||
"Standalone: Increased icon visibility for windows builds",
|
date: "04.06.2020",
|
||||||
"Web version: Fixed firefox not loading the game when browsing in private mode",
|
entries: [
|
||||||
],
|
"The steam release will happen on the <strong>7th of June</strong> - Be sure to add it to your wishlist! <a href='https://steam.shapez.io' target='blank'>View on steam</a>",
|
||||||
},
|
"Fixed level complete dialog being blurred when the shop was opened before",
|
||||||
|
"Standalone: Increased icon visibility for windows builds",
|
||||||
{
|
"Web version: Fixed firefox not loading the game when browsing in private mode",
|
||||||
version: "1.1.5",
|
],
|
||||||
date: "03.06.2020",
|
},
|
||||||
entries: ["Added weekly contests!"],
|
|
||||||
},
|
{
|
||||||
{
|
version: "1.1.5",
|
||||||
version: "1.1.4",
|
date: "03.06.2020",
|
||||||
date: "01.06.2020",
|
entries: ["Added weekly contests!"],
|
||||||
entries: ["Add 'interactive' tutorial for the first level to improve onboarding experience"],
|
},
|
||||||
},
|
{
|
||||||
{
|
version: "1.1.4",
|
||||||
version: "1.1.3",
|
date: "01.06.2020",
|
||||||
date: "01.06.2020",
|
entries: ["Add 'interactive' tutorial for the first level to improve onboarding experience"],
|
||||||
entries: [
|
},
|
||||||
"Added setting to configure zoom / mouse wheel / touchpad sensitivity",
|
{
|
||||||
"Fix belts being too slow when copied via blueprint (by Dimava)",
|
version: "1.1.3",
|
||||||
"Allow binding mouse buttons to actions (by Dimava)",
|
date: "01.06.2020",
|
||||||
"Increase readability of certain HUD elements",
|
entries: [
|
||||||
],
|
"Added setting to configure zoom / mouse wheel / touchpad sensitivity",
|
||||||
},
|
"Fix belts being too slow when copied via blueprint (by Dimava)",
|
||||||
{
|
"Allow binding mouse buttons to actions (by Dimava)",
|
||||||
version: "1.1.2",
|
"Increase readability of certain HUD elements",
|
||||||
date: "30.05.2020",
|
],
|
||||||
entries: [
|
},
|
||||||
"The official trailer is now ready! Check it out <a href='https://www.youtube.com/watch?v=KyorY1uIqiQ' target='_blank'>here</a>!",
|
{
|
||||||
"The <a href='https://steam.shapez.io' target='_blank'>steam page</a> is now live!",
|
version: "1.1.2",
|
||||||
"Experimental linux builds are now available! Please give me feedback on them in the Discord",
|
date: "30.05.2020",
|
||||||
"Allow hovering pinned shapes to enlarge them",
|
entries: [
|
||||||
"Allow deselecting blueprints with right click and 'Q'",
|
"The official trailer is now ready! Check it out <a href='https://www.youtube.com/watch?v=KyorY1uIqiQ' target='_blank'>here</a>!",
|
||||||
"Move default key for deleting from 'X' to 'DEL'",
|
"The <a href='https://steam.shapez.io' target='_blank'>steam page</a> is now live!",
|
||||||
"Show confirmation when deleting more than 100 buildings",
|
"Experimental linux builds are now available! Please give me feedback on them in the Discord",
|
||||||
"Reintroduce 'SPACE' keybinding to center on map",
|
"Allow hovering pinned shapes to enlarge them",
|
||||||
"Improved keybinding hints",
|
"Allow deselecting blueprints with right click and 'Q'",
|
||||||
"Fixed some keybindings showing as 'undefined'",
|
"Move default key for deleting from 'X' to 'DEL'",
|
||||||
],
|
"Show confirmation when deleting more than 100 buildings",
|
||||||
},
|
"Reintroduce 'SPACE' keybinding to center on map",
|
||||||
{
|
"Improved keybinding hints",
|
||||||
version: "1.1.1",
|
"Fixed some keybindings showing as 'undefined'",
|
||||||
date: "28.05.2020",
|
],
|
||||||
entries: ["Fix crash when 'Show Hints' setting was turned off"],
|
},
|
||||||
},
|
{
|
||||||
{
|
version: "1.1.1",
|
||||||
version: "1.1.0",
|
date: "28.05.2020",
|
||||||
date: "28.05.2020",
|
entries: ["Fix crash when 'Show Hints' setting was turned off"],
|
||||||
entries: [
|
},
|
||||||
"BLUEPRINTS! They are unlocked at level 12 and cost a special shape to build.",
|
{
|
||||||
"MAP MARKERS! Press 'M' to create a waypoint and be able to jump to it",
|
version: "1.1.0",
|
||||||
"Savegame levels are now shown in the main menu. For existing games, save them again to make the level show up.",
|
date: "28.05.2020",
|
||||||
"Allow holding SHIFT to rotate counter clockwise",
|
entries: [
|
||||||
"Added confirmation when deleting more than 500 buildings at a time",
|
"BLUEPRINTS! They are unlocked at level 12 and cost a special shape to build.",
|
||||||
"Added background to toolbar to increase contrast",
|
"MAP MARKERS! Press 'M' to create a waypoint and be able to jump to it",
|
||||||
"Further decerase requirements of first levels",
|
"Savegame levels are now shown in the main menu. For existing games, save them again to make the level show up.",
|
||||||
"Pinned shapes now are saved",
|
"Allow holding SHIFT to rotate counter clockwise",
|
||||||
"Allow placing extractors anywhere again, but they don't work at all if not placed on a resource",
|
"Added confirmation when deleting more than 500 buildings at a time",
|
||||||
"Show dialog explaining some keybindings after completing level 4",
|
"Added background to toolbar to increase contrast",
|
||||||
"Fix keys being stuck when opening a dialog",
|
"Further decerase requirements of first levels",
|
||||||
"Swapped shape order for painting upgrades",
|
"Pinned shapes now are saved",
|
||||||
"Allow changing all keybindings, including CTRL, ALT and SHIFT (by Dimava)",
|
"Allow placing extractors anywhere again, but they don't work at all if not placed on a resource",
|
||||||
"Fix cycling through keybindings selecting locked buildings as well (by Dimava)",
|
"Show dialog explaining some keybindings after completing level 4",
|
||||||
"There is now a github action, checking all pull requests with eslint. (by mrHedgehog)",
|
"Fix keys being stuck when opening a dialog",
|
||||||
],
|
"Swapped shape order for painting upgrades",
|
||||||
},
|
"Allow changing all keybindings, including CTRL, ALT and SHIFT (by Dimava)",
|
||||||
{
|
"Fix cycling through keybindings selecting locked buildings as well (by Dimava)",
|
||||||
version: "1.0.4",
|
"There is now a github action, checking all pull requests with eslint. (by mrHedgehog)",
|
||||||
date: "26.05.2020",
|
],
|
||||||
entries: [
|
},
|
||||||
"Reduce cost of first painting upgrade, and change 'Shape Processing' to 'Cutting, Rotating & Stacking'",
|
{
|
||||||
"Add dialog after completing level 2 to check out the upgrades tab.",
|
version: "1.0.4",
|
||||||
"Allow changing the keybindings in the demo version",
|
date: "26.05.2020",
|
||||||
],
|
entries: [
|
||||||
},
|
"Reduce cost of first painting upgrade, and change 'Shape Processing' to 'Cutting, Rotating & Stacking'",
|
||||||
{
|
"Add dialog after completing level 2 to check out the upgrades tab.",
|
||||||
version: "1.0.3",
|
"Allow changing the keybindings in the demo version",
|
||||||
date: "24.05.2020",
|
],
|
||||||
entries: [
|
},
|
||||||
"Reduced the amount of shapes required for the first 5 levels to make it easier to get into the game.",
|
{
|
||||||
],
|
version: "1.0.3",
|
||||||
},
|
date: "24.05.2020",
|
||||||
{
|
entries: [
|
||||||
version: "1.0.2",
|
"Reduced the amount of shapes required for the first 5 levels to make it easier to get into the game.",
|
||||||
date: "23.05.2020",
|
],
|
||||||
entries: [
|
},
|
||||||
"Introduced changelog",
|
{
|
||||||
"Removed 'early access' label because the game isn't actually early access - its in a pretty good state already! (No worries, a lot more updates will follow!)",
|
version: "1.0.2",
|
||||||
"Added a 'Show hint' button which shows a small video for almost all levels to help out",
|
date: "23.05.2020",
|
||||||
"Now showing proper descriptions when completing levels, with instructions on what the gained reward does.",
|
entries: [
|
||||||
"Show a landing page on mobile devices about the game not being ready to be played on mobile yet",
|
"Introduced changelog",
|
||||||
"Fix painters and mixers being affected by the shape processors upgrade and not the painter one",
|
"Removed 'early access' label because the game isn't actually early access - its in a pretty good state already! (No worries, a lot more updates will follow!)",
|
||||||
"Added 'multiplace' setting which is equivalent to holding SHIFT all the time",
|
"Added a 'Show hint' button which shows a small video for almost all levels to help out",
|
||||||
"Added keybindings to zoom in / zoom out",
|
"Now showing proper descriptions when completing levels, with instructions on what the gained reward does.",
|
||||||
"Tunnels now also show connection lines to tunnel exits, instead of just tunnel entries",
|
"Show a landing page on mobile devices about the game not being ready to be played on mobile yet",
|
||||||
"Lots of minor fixes and improvements",
|
"Fix painters and mixers being affected by the shape processors upgrade and not the painter one",
|
||||||
],
|
"Added 'multiplace' setting which is equivalent to holding SHIFT all the time",
|
||||||
},
|
"Added keybindings to zoom in / zoom out",
|
||||||
{
|
"Tunnels now also show connection lines to tunnel exits, instead of just tunnel entries",
|
||||||
version: "1.0.1",
|
"Lots of minor fixes and improvements",
|
||||||
date: "21.05.2020",
|
],
|
||||||
entries: ["Initial release!"],
|
},
|
||||||
},
|
{
|
||||||
];
|
version: "1.0.1",
|
||||||
|
date: "21.05.2020",
|
||||||
|
entries: ["Initial release!"],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
@ -1,434 +1,443 @@
|
|||||||
/* typehints:start */
|
/* typehints:start */
|
||||||
import { Application } from "../application";
|
import { Application } from "../application";
|
||||||
/* typehints:end */
|
/* typehints:end */
|
||||||
|
|
||||||
import { Signal, STOP_PROPAGATION } from "./signal";
|
import { Signal, STOP_PROPAGATION } from "./signal";
|
||||||
import { arrayDeleteValue, waitNextFrame } from "./utils";
|
import { arrayDeleteValue, waitNextFrame } from "./utils";
|
||||||
import { ClickDetector } from "./click_detector";
|
import { ClickDetector } from "./click_detector";
|
||||||
import { SOUNDS } from "../platform/sound";
|
import { SOUNDS } from "../platform/sound";
|
||||||
import { InputReceiver } from "./input_receiver";
|
import { InputReceiver } from "./input_receiver";
|
||||||
import { FormElement } from "./modal_dialog_forms";
|
import { FormElement } from "./modal_dialog_forms";
|
||||||
import { globalConfig } from "./config";
|
import { globalConfig } from "./config";
|
||||||
import { getStringForKeyCode } from "../game/key_action_mapper";
|
import { getStringForKeyCode } from "../game/key_action_mapper";
|
||||||
import { createLogger } from "./logging";
|
import { createLogger } from "./logging";
|
||||||
import { T } from "../translations";
|
import { T } from "../translations";
|
||||||
|
|
||||||
const kbEnter = 13;
|
const kbEnter = 13;
|
||||||
const kbCancel = 27;
|
const kbCancel = 27;
|
||||||
|
|
||||||
const logger = createLogger("dialogs");
|
const logger = createLogger("dialogs");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Basic text based dialog
|
* Basic text based dialog
|
||||||
*/
|
*/
|
||||||
export class Dialog {
|
export class Dialog {
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Constructs a new dialog with the given options
|
* Constructs a new dialog with the given options
|
||||||
* @param {object} param0
|
* @param {object} param0
|
||||||
* @param {Application} param0.app
|
* @param {Application} param0.app
|
||||||
* @param {string} param0.title Title of the dialog
|
* @param {string} param0.title Title of the dialog
|
||||||
* @param {string} param0.contentHTML Inner dialog html
|
* @param {string} param0.contentHTML Inner dialog html
|
||||||
* @param {Array<string>} param0.buttons
|
* @param {Array<string>} param0.buttons
|
||||||
* Button list, each button contains of up to 3 parts seperated by ':'.
|
* Button list, each button contains of up to 3 parts seperated by ':'.
|
||||||
* Part 0: The id, one of the one defined in dialog_buttons.yaml
|
* Part 0: The id, one of the one defined in dialog_buttons.yaml
|
||||||
* Part 1: The style, either good, bad or misc
|
* Part 1: The style, either good, bad or misc
|
||||||
* Part 2 (optional): Additional parameters seperated by '/', available are:
|
* Part 2 (optional): Additional parameters seperated by '/', available are:
|
||||||
* timeout: This button is only available after some waiting time
|
* timeout: This button is only available after some waiting time
|
||||||
* kb_enter: This button is triggered by the enter key
|
* kb_enter: This button is triggered by the enter key
|
||||||
* kb_escape This button is triggered by the escape key
|
* kb_escape This button is triggered by the escape key
|
||||||
* @param {string=} param0.type The dialog type, either "info" or "warn"
|
* @param {string=} param0.type The dialog type, either "info" or "warn"
|
||||||
* @param {boolean=} param0.closeButton Whether this dialog has a close button
|
* @param {boolean=} param0.closeButton Whether this dialog has a close button
|
||||||
*/
|
*/
|
||||||
constructor({ app, title, contentHTML, buttons, type = "info", closeButton = false }) {
|
constructor({ app, title, contentHTML, buttons, type = "info", closeButton = false }) {
|
||||||
this.app = app;
|
this.app = app;
|
||||||
this.title = title;
|
this.title = title;
|
||||||
this.contentHTML = contentHTML;
|
this.contentHTML = contentHTML;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.buttonIds = buttons;
|
this.buttonIds = buttons;
|
||||||
this.closeButton = closeButton;
|
this.closeButton = closeButton;
|
||||||
|
|
||||||
this.closeRequested = new Signal();
|
this.closeRequested = new Signal();
|
||||||
this.buttonSignals = {};
|
this.buttonSignals = {};
|
||||||
|
|
||||||
for (let i = 0; i < buttons.length; ++i) {
|
for (let i = 0; i < buttons.length; ++i) {
|
||||||
if (G_IS_DEV && globalConfig.debug.disableTimedButtons) {
|
if (G_IS_DEV && globalConfig.debug.disableTimedButtons) {
|
||||||
this.buttonIds[i] = this.buttonIds[i].replace(":timeout", "");
|
this.buttonIds[i] = this.buttonIds[i].replace(":timeout", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
const buttonId = this.buttonIds[i].split(":")[0];
|
const buttonId = this.buttonIds[i].split(":")[0];
|
||||||
this.buttonSignals[buttonId] = new Signal();
|
this.buttonSignals[buttonId] = new Signal();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.timeouts = [];
|
this.timeouts = [];
|
||||||
this.clickDetectors = [];
|
this.clickDetectors = [];
|
||||||
|
|
||||||
this.inputReciever = new InputReceiver("dialog-" + this.title);
|
this.inputReciever = new InputReceiver("dialog-" + this.title);
|
||||||
|
|
||||||
this.inputReciever.keydown.add(this.handleKeydown, this);
|
this.inputReciever.keydown.add(this.handleKeydown, this);
|
||||||
|
|
||||||
this.enterHandler = null;
|
this.enterHandler = null;
|
||||||
this.escapeHandler = null;
|
this.escapeHandler = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal keydown handler
|
* Internal keydown handler
|
||||||
* @param {object} param0
|
* @param {object} param0
|
||||||
* @param {number} param0.keyCode
|
* @param {number} param0.keyCode
|
||||||
* @param {boolean} param0.shift
|
* @param {boolean} param0.shift
|
||||||
* @param {boolean} param0.alt
|
* @param {boolean} param0.alt
|
||||||
*/
|
*/
|
||||||
handleKeydown({ keyCode, shift, alt }) {
|
handleKeydown({ keyCode, shift, alt }) {
|
||||||
if (keyCode === kbEnter && this.enterHandler) {
|
if (keyCode === kbEnter && this.enterHandler) {
|
||||||
this.internalButtonHandler(this.enterHandler);
|
this.internalButtonHandler(this.enterHandler);
|
||||||
return STOP_PROPAGATION;
|
return STOP_PROPAGATION;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (keyCode === kbCancel && this.escapeHandler) {
|
if (keyCode === kbCancel && this.escapeHandler) {
|
||||||
this.internalButtonHandler(this.escapeHandler);
|
this.internalButtonHandler(this.escapeHandler);
|
||||||
return STOP_PROPAGATION;
|
return STOP_PROPAGATION;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internalButtonHandler(id, ...payload) {
|
internalButtonHandler(id, ...payload) {
|
||||||
this.app.inputMgr.popReciever(this.inputReciever);
|
this.app.inputMgr.popReciever(this.inputReciever);
|
||||||
|
|
||||||
if (id !== "close-button") {
|
if (id !== "close-button") {
|
||||||
this.buttonSignals[id].dispatch(...payload);
|
this.buttonSignals[id].dispatch(...payload);
|
||||||
}
|
}
|
||||||
this.closeRequested.dispatch();
|
this.closeRequested.dispatch();
|
||||||
}
|
}
|
||||||
|
|
||||||
createElement() {
|
createElement() {
|
||||||
const elem = document.createElement("div");
|
const elem = document.createElement("div");
|
||||||
elem.classList.add("ingameDialog");
|
elem.classList.add("ingameDialog");
|
||||||
|
|
||||||
this.dialogElem = document.createElement("div");
|
this.dialogElem = document.createElement("div");
|
||||||
this.dialogElem.classList.add("dialogInner");
|
this.dialogElem.classList.add("dialogInner");
|
||||||
|
|
||||||
if (this.type) {
|
if (this.type) {
|
||||||
this.dialogElem.classList.add(this.type);
|
this.dialogElem.classList.add(this.type);
|
||||||
}
|
}
|
||||||
elem.appendChild(this.dialogElem);
|
elem.appendChild(this.dialogElem);
|
||||||
|
|
||||||
const title = document.createElement("h1");
|
const title = document.createElement("h1");
|
||||||
title.innerText = this.title;
|
title.innerText = this.title;
|
||||||
title.classList.add("title");
|
title.classList.add("title");
|
||||||
this.dialogElem.appendChild(title);
|
this.dialogElem.appendChild(title);
|
||||||
|
|
||||||
if (this.closeButton) {
|
if (this.closeButton) {
|
||||||
this.dialogElem.classList.add("hasCloseButton");
|
this.dialogElem.classList.add("hasCloseButton");
|
||||||
|
|
||||||
const closeBtn = document.createElement("button");
|
const closeBtn = document.createElement("button");
|
||||||
closeBtn.classList.add("closeButton");
|
closeBtn.classList.add("closeButton");
|
||||||
|
|
||||||
this.trackClicks(closeBtn, () => this.internalButtonHandler("close-button"), {
|
this.trackClicks(closeBtn, () => this.internalButtonHandler("close-button"), {
|
||||||
applyCssClass: "pressedSmallElement",
|
applyCssClass: "pressedSmallElement",
|
||||||
});
|
});
|
||||||
|
|
||||||
title.appendChild(closeBtn);
|
title.appendChild(closeBtn);
|
||||||
this.inputReciever.backButton.add(() => this.internalButtonHandler("close-button"));
|
this.inputReciever.backButton.add(() => this.internalButtonHandler("close-button"));
|
||||||
}
|
}
|
||||||
|
|
||||||
const content = document.createElement("div");
|
const content = document.createElement("div");
|
||||||
content.classList.add("content");
|
content.classList.add("content");
|
||||||
content.innerHTML = this.contentHTML;
|
content.innerHTML = this.contentHTML;
|
||||||
this.dialogElem.appendChild(content);
|
this.dialogElem.appendChild(content);
|
||||||
|
|
||||||
if (this.buttonIds.length > 0) {
|
if (this.buttonIds.length > 0) {
|
||||||
const buttons = document.createElement("div");
|
const buttons = document.createElement("div");
|
||||||
buttons.classList.add("buttons");
|
buttons.classList.add("buttons");
|
||||||
|
|
||||||
// Create buttons
|
// Create buttons
|
||||||
for (let i = 0; i < this.buttonIds.length; ++i) {
|
for (let i = 0; i < this.buttonIds.length; ++i) {
|
||||||
const [buttonId, buttonStyle, rawParams] = this.buttonIds[i].split(":");
|
const [buttonId, buttonStyle, rawParams] = this.buttonIds[i].split(":");
|
||||||
|
|
||||||
const button = document.createElement("button");
|
const button = document.createElement("button");
|
||||||
button.classList.add("button");
|
button.classList.add("button");
|
||||||
button.classList.add("styledButton");
|
button.classList.add("styledButton");
|
||||||
button.classList.add(buttonStyle);
|
button.classList.add(buttonStyle);
|
||||||
button.innerText = T.dialogs.buttons[buttonId];
|
button.innerText = T.dialogs.buttons[buttonId];
|
||||||
|
|
||||||
const params = (rawParams || "").split("/");
|
const params = (rawParams || "").split("/");
|
||||||
const useTimeout = params.indexOf("timeout") >= 0;
|
const useTimeout = params.indexOf("timeout") >= 0;
|
||||||
|
|
||||||
const isEnter = params.indexOf("enter") >= 0;
|
const isEnter = params.indexOf("enter") >= 0;
|
||||||
const isEscape = params.indexOf("escape") >= 0;
|
const isEscape = params.indexOf("escape") >= 0;
|
||||||
|
|
||||||
if (isEscape && this.closeButton) {
|
if (isEscape && this.closeButton) {
|
||||||
logger.warn("Showing dialog with close button, and additional cancel button");
|
logger.warn("Showing dialog with close button, and additional cancel button");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (useTimeout) {
|
if (useTimeout) {
|
||||||
button.classList.add("timedButton");
|
button.classList.add("timedButton");
|
||||||
const timeout = setTimeout(() => {
|
const timeout = setTimeout(() => {
|
||||||
button.classList.remove("timedButton");
|
button.classList.remove("timedButton");
|
||||||
arrayDeleteValue(this.timeouts, timeout);
|
arrayDeleteValue(this.timeouts, timeout);
|
||||||
}, 5000);
|
}, 5000);
|
||||||
this.timeouts.push(timeout);
|
this.timeouts.push(timeout);
|
||||||
}
|
}
|
||||||
if (isEnter || isEscape) {
|
if (isEnter || isEscape) {
|
||||||
// if (this.app.settings.getShowKeyboardShortcuts()) {
|
// if (this.app.settings.getShowKeyboardShortcuts()) {
|
||||||
// Show keybinding
|
// Show keybinding
|
||||||
const spacer = document.createElement("code");
|
const spacer = document.createElement("code");
|
||||||
spacer.classList.add("keybinding");
|
spacer.classList.add("keybinding");
|
||||||
spacer.innerHTML = getStringForKeyCode(isEnter ? kbEnter : kbCancel);
|
spacer.innerHTML = getStringForKeyCode(isEnter ? kbEnter : kbCancel);
|
||||||
button.appendChild(spacer);
|
button.appendChild(spacer);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
if (isEnter) {
|
if (isEnter) {
|
||||||
this.enterHandler = buttonId;
|
this.enterHandler = buttonId;
|
||||||
}
|
}
|
||||||
if (isEscape) {
|
if (isEscape) {
|
||||||
this.escapeHandler = buttonId;
|
this.escapeHandler = buttonId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.trackClicks(button, () => this.internalButtonHandler(buttonId));
|
this.trackClicks(button, () => this.internalButtonHandler(buttonId));
|
||||||
buttons.appendChild(button);
|
buttons.appendChild(button);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.dialogElem.appendChild(buttons);
|
this.dialogElem.appendChild(buttons);
|
||||||
} else {
|
} else {
|
||||||
this.dialogElem.classList.add("buttonless");
|
this.dialogElem.classList.add("buttonless");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.element = elem;
|
this.element = elem;
|
||||||
this.app.inputMgr.pushReciever(this.inputReciever);
|
this.app.inputMgr.pushReciever(this.inputReciever);
|
||||||
|
|
||||||
return this.element;
|
return this.element;
|
||||||
}
|
}
|
||||||
|
|
||||||
setIndex(index) {
|
setIndex(index) {
|
||||||
this.element.style.zIndex = index;
|
this.element.style.zIndex = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
if (!this.element) {
|
if (!this.element) {
|
||||||
assert(false, "Tried to destroy dialog twice");
|
assert(false, "Tried to destroy dialog twice");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// We need to do this here, because if the backbutton event gets
|
// We need to do this here, because if the backbutton event gets
|
||||||
// dispatched to the modal dialogs, it will not call the internalButtonHandler,
|
// dispatched to the modal dialogs, it will not call the internalButtonHandler,
|
||||||
// and thus our receiver stays attached the whole time
|
// and thus our receiver stays attached the whole time
|
||||||
this.app.inputMgr.destroyReceiver(this.inputReciever);
|
this.app.inputMgr.destroyReceiver(this.inputReciever);
|
||||||
|
|
||||||
for (let i = 0; i < this.clickDetectors.length; ++i) {
|
for (let i = 0; i < this.clickDetectors.length; ++i) {
|
||||||
this.clickDetectors[i].cleanup();
|
this.clickDetectors[i].cleanup();
|
||||||
}
|
}
|
||||||
this.clickDetectors = [];
|
this.clickDetectors = [];
|
||||||
|
|
||||||
this.element.remove();
|
this.element.remove();
|
||||||
this.element = null;
|
this.element = null;
|
||||||
|
|
||||||
for (let i = 0; i < this.timeouts.length; ++i) {
|
for (let i = 0; i < this.timeouts.length; ++i) {
|
||||||
clearTimeout(this.timeouts[i]);
|
clearTimeout(this.timeouts[i]);
|
||||||
}
|
}
|
||||||
this.timeouts = [];
|
this.timeouts = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
hide() {
|
hide() {
|
||||||
this.element.classList.remove("visible");
|
this.element.classList.remove("visible");
|
||||||
}
|
}
|
||||||
|
|
||||||
show() {
|
show() {
|
||||||
this.element.classList.add("visible");
|
this.element.classList.add("visible");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper method to track clicks on an element
|
* Helper method to track clicks on an element
|
||||||
* @param {Element} elem
|
* @param {Element} elem
|
||||||
* @param {function():void} handler
|
* @param {function():void} handler
|
||||||
* @param {import("./click_detector").ClickDetectorConstructorArgs=} args
|
* @param {import("./click_detector").ClickDetectorConstructorArgs=} args
|
||||||
* @returns {ClickDetector}
|
* @returns {ClickDetector}
|
||||||
*/
|
*/
|
||||||
trackClicks(elem, handler, args = {}) {
|
trackClicks(elem, handler, args = {}) {
|
||||||
const detector = new ClickDetector(elem, args);
|
const detector = new ClickDetector(elem, args);
|
||||||
detector.click.add(handler, this);
|
detector.click.add(handler, this);
|
||||||
this.clickDetectors.push(detector);
|
this.clickDetectors.push(detector);
|
||||||
return detector;
|
return detector;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dialog which simply shows a loading spinner
|
* Dialog which simply shows a loading spinner
|
||||||
*/
|
*/
|
||||||
export class DialogLoading extends Dialog {
|
export class DialogLoading extends Dialog {
|
||||||
constructor(app) {
|
constructor(app) {
|
||||||
super({
|
super({
|
||||||
app,
|
app,
|
||||||
title: "",
|
title: "",
|
||||||
contentHTML: "",
|
contentHTML: "",
|
||||||
buttons: [],
|
buttons: [],
|
||||||
type: "loading",
|
type: "loading",
|
||||||
});
|
});
|
||||||
|
|
||||||
// Loading dialog can not get closed with back button
|
// Loading dialog can not get closed with back button
|
||||||
this.inputReciever.backButton.removeAll();
|
this.inputReciever.backButton.removeAll();
|
||||||
this.inputReciever.context = "dialog-loading";
|
this.inputReciever.context = "dialog-loading";
|
||||||
}
|
}
|
||||||
|
|
||||||
createElement() {
|
createElement() {
|
||||||
const elem = document.createElement("div");
|
const elem = document.createElement("div");
|
||||||
elem.classList.add("ingameDialog");
|
elem.classList.add("ingameDialog");
|
||||||
elem.classList.add("loadingDialog");
|
elem.classList.add("loadingDialog");
|
||||||
this.element = elem;
|
this.element = elem;
|
||||||
|
|
||||||
const loader = document.createElement("div");
|
const loader = document.createElement("div");
|
||||||
loader.classList.add("prefab_LoadingTextWithAnim");
|
loader.classList.add("prefab_LoadingTextWithAnim");
|
||||||
loader.classList.add("loadingIndicator");
|
loader.classList.add("loadingIndicator");
|
||||||
loader.innerText = T.global.loading;
|
loader.innerText = T.global.loading;
|
||||||
elem.appendChild(loader);
|
elem.appendChild(loader);
|
||||||
|
|
||||||
this.app.inputMgr.pushReciever(this.inputReciever);
|
this.app.inputMgr.pushReciever(this.inputReciever);
|
||||||
|
|
||||||
return elem;
|
return elem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DialogOptionChooser extends Dialog {
|
export class DialogOptionChooser extends Dialog {
|
||||||
constructor({ app, title, options }) {
|
constructor({ app, title, options }) {
|
||||||
let html = "<div class='optionParent'>";
|
let html = "<div class='optionParent'>";
|
||||||
|
|
||||||
options.options.forEach(({ value, text, desc = null, iconPrefix = null }) => {
|
options.options.forEach(({ value, text, desc = null, iconPrefix = null }) => {
|
||||||
const descHtml = desc ? `<span class="desc">${desc}</span>` : "";
|
const descHtml = desc ? `<span class="desc">${desc}</span>` : "";
|
||||||
let iconHtml = iconPrefix ? `<span class="icon icon-${iconPrefix}-${value}"></span>` : "";
|
let iconHtml = iconPrefix ? `<span class="icon icon-${iconPrefix}-${value}"></span>` : "";
|
||||||
html += `
|
html += `
|
||||||
<div class='option ${value === options.active ? "active" : ""} ${
|
<div class='option ${value === options.active ? "active" : ""} ${
|
||||||
iconPrefix ? "hasIcon" : ""
|
iconPrefix ? "hasIcon" : ""
|
||||||
}' data-optionvalue='${value}'>
|
}' data-optionvalue='${value}'>
|
||||||
${iconHtml}
|
${iconHtml}
|
||||||
<span class='title'>${text}</span>
|
<span class='title'>${text}</span>
|
||||||
${descHtml}
|
${descHtml}
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
});
|
});
|
||||||
|
|
||||||
html += "</div>";
|
html += "</div>";
|
||||||
super({
|
super({
|
||||||
app,
|
app,
|
||||||
title,
|
title,
|
||||||
contentHTML: html,
|
contentHTML: html,
|
||||||
buttons: [],
|
buttons: [],
|
||||||
type: "info",
|
type: "info",
|
||||||
closeButton: true,
|
closeButton: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.options = options;
|
this.options = options;
|
||||||
this.initialOption = options.active;
|
this.initialOption = options.active;
|
||||||
|
|
||||||
this.buttonSignals.optionSelected = new Signal();
|
this.buttonSignals.optionSelected = new Signal();
|
||||||
}
|
}
|
||||||
|
|
||||||
createElement() {
|
createElement() {
|
||||||
const div = super.createElement();
|
const div = super.createElement();
|
||||||
this.dialogElem.classList.add("optionChooserDialog");
|
this.dialogElem.classList.add("optionChooserDialog");
|
||||||
|
|
||||||
div.querySelectorAll("[data-optionvalue]").forEach(handle => {
|
div.querySelectorAll("[data-optionvalue]").forEach(handle => {
|
||||||
const value = handle.getAttribute("data-optionvalue");
|
const value = handle.getAttribute("data-optionvalue");
|
||||||
if (!handle) {
|
if (!handle) {
|
||||||
logger.error("Failed to bind option value in dialog:", value);
|
logger.error("Failed to bind option value in dialog:", value);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Need click detector here to forward elements, otherwise scrolling does not work
|
// Need click detector here to forward elements, otherwise scrolling does not work
|
||||||
const detector = new ClickDetector(handle, {
|
const detector = new ClickDetector(handle, {
|
||||||
consumeEvents: false,
|
consumeEvents: false,
|
||||||
preventDefault: false,
|
preventDefault: false,
|
||||||
clickSound: null,
|
clickSound: null,
|
||||||
applyCssClass: "pressedOption",
|
applyCssClass: "pressedOption",
|
||||||
targetOnly: true,
|
targetOnly: true,
|
||||||
});
|
});
|
||||||
this.clickDetectors.push(detector);
|
this.clickDetectors.push(detector);
|
||||||
|
|
||||||
if (value !== this.initialOption) {
|
if (value !== this.initialOption) {
|
||||||
detector.click.add(() => {
|
detector.click.add(() => {
|
||||||
const selected = div.querySelector(".option.active");
|
const selected = div.querySelector(".option.active");
|
||||||
if (selected) {
|
if (selected) {
|
||||||
selected.classList.remove("active");
|
selected.classList.remove("active");
|
||||||
} else {
|
} else {
|
||||||
logger.warn("No selected option");
|
logger.warn("No selected option");
|
||||||
}
|
}
|
||||||
handle.classList.add("active");
|
handle.classList.add("active");
|
||||||
this.app.sound.playUiSound(SOUNDS.uiClick);
|
this.app.sound.playUiSound(SOUNDS.uiClick);
|
||||||
this.internalButtonHandler("optionSelected", value);
|
this.internalButtonHandler("optionSelected", value);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return div;
|
return div;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DialogWithForm extends Dialog {
|
export class DialogWithForm extends Dialog {
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {object} param0
|
* @param {object} param0
|
||||||
* @param {Application} param0.app
|
* @param {Application} param0.app
|
||||||
* @param {string} param0.title
|
* @param {string} param0.title
|
||||||
* @param {string} param0.desc
|
* @param {string} param0.desc
|
||||||
* @param {array=} param0.buttons
|
* @param {array=} param0.buttons
|
||||||
* @param {string=} param0.confirmButtonId
|
* @param {string=} param0.confirmButtonId
|
||||||
* @param {string=} param0.extraButton
|
* @param {string=} param0.extraButton
|
||||||
* @param {Array<FormElement>} param0.formElements
|
* @param {boolean=} param0.closeButton
|
||||||
*/
|
* @param {Array<FormElement>} param0.formElements
|
||||||
constructor({ app, title, desc, formElements, buttons = ["cancel", "ok:good"], confirmButtonId = "ok" }) {
|
*/
|
||||||
let html = "";
|
constructor({
|
||||||
html += desc + "<br>";
|
app,
|
||||||
for (let i = 0; i < formElements.length; ++i) {
|
title,
|
||||||
html += formElements[i].getHtml();
|
desc,
|
||||||
}
|
formElements,
|
||||||
|
buttons = ["cancel", "ok:good"],
|
||||||
super({
|
confirmButtonId = "ok",
|
||||||
app,
|
closeButton = true,
|
||||||
title: title,
|
}) {
|
||||||
contentHTML: html,
|
let html = "";
|
||||||
buttons: buttons,
|
html += desc + "<br>";
|
||||||
type: "info",
|
for (let i = 0; i < formElements.length; ++i) {
|
||||||
closeButton: true,
|
html += formElements[i].getHtml();
|
||||||
});
|
}
|
||||||
this.confirmButtonId = confirmButtonId;
|
|
||||||
this.formElements = formElements;
|
super({
|
||||||
|
app,
|
||||||
this.enterHandler = confirmButtonId;
|
title: title,
|
||||||
}
|
contentHTML: html,
|
||||||
|
buttons: buttons,
|
||||||
internalButtonHandler(id, ...payload) {
|
type: "info",
|
||||||
if (id === this.confirmButtonId) {
|
closeButton,
|
||||||
if (this.hasAnyInvalid()) {
|
});
|
||||||
this.dialogElem.classList.remove("errorShake");
|
this.confirmButtonId = confirmButtonId;
|
||||||
waitNextFrame().then(() => {
|
this.formElements = formElements;
|
||||||
if (this.dialogElem) {
|
|
||||||
this.dialogElem.classList.add("errorShake");
|
this.enterHandler = confirmButtonId;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
this.app.sound.playUiSound(SOUNDS.uiError);
|
internalButtonHandler(id, ...payload) {
|
||||||
return;
|
if (id === this.confirmButtonId) {
|
||||||
}
|
if (this.hasAnyInvalid()) {
|
||||||
}
|
this.dialogElem.classList.remove("errorShake");
|
||||||
|
waitNextFrame().then(() => {
|
||||||
super.internalButtonHandler(id, payload);
|
if (this.dialogElem) {
|
||||||
}
|
this.dialogElem.classList.add("errorShake");
|
||||||
|
}
|
||||||
hasAnyInvalid() {
|
});
|
||||||
for (let i = 0; i < this.formElements.length; ++i) {
|
this.app.sound.playUiSound(SOUNDS.uiError);
|
||||||
if (!this.formElements[i].isValid()) {
|
return;
|
||||||
return true;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return false;
|
super.internalButtonHandler(id, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
createElement() {
|
hasAnyInvalid() {
|
||||||
const div = super.createElement();
|
for (let i = 0; i < this.formElements.length; ++i) {
|
||||||
|
if (!this.formElements[i].isValid()) {
|
||||||
for (let i = 0; i < this.formElements.length; ++i) {
|
return true;
|
||||||
const elem = this.formElements[i];
|
}
|
||||||
elem.bindEvents(div, this.clickDetectors);
|
}
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
waitNextFrame().then(() => {
|
|
||||||
this.formElements[0].focus();
|
createElement() {
|
||||||
});
|
const div = super.createElement();
|
||||||
|
|
||||||
return div;
|
for (let i = 0; i < this.formElements.length; ++i) {
|
||||||
}
|
const elem = this.formElements[i];
|
||||||
}
|
elem.bindEvents(div, this.clickDetectors);
|
||||||
|
}
|
||||||
|
|
||||||
|
waitNextFrame().then(() => {
|
||||||
|
this.formElements[0].focus();
|
||||||
|
});
|
||||||
|
|
||||||
|
return div;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -53,27 +53,6 @@ export class Rectangle {
|
|||||||
return a.left <= b.right && b.left <= a.right && a.top <= b.bottom && b.top <= a.bottom;
|
return a.left <= b.right && b.left <= a.right && a.top <= b.bottom && b.top <= a.bottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a rectangle arround a rotated point
|
|
||||||
* @param {Array<Vector>} points
|
|
||||||
* @param {number} angle
|
|
||||||
* @returns {Rectangle}
|
|
||||||
*/
|
|
||||||
static getAroundPointsRotated(points, angle) {
|
|
||||||
let minX = 1e10;
|
|
||||||
let minY = 1e10;
|
|
||||||
let maxX = -1e10;
|
|
||||||
let maxY = -1e10;
|
|
||||||
for (let i = 0; i < points.length; ++i) {
|
|
||||||
const rotated = points[i].rotated(angle);
|
|
||||||
minX = Math.min(minX, rotated.x);
|
|
||||||
minY = Math.min(minY, rotated.y);
|
|
||||||
maxX = Math.max(maxX, rotated.x);
|
|
||||||
maxY = Math.max(maxY, rotated.y);
|
|
||||||
}
|
|
||||||
return new Rectangle(minX, minY, maxX - minX, maxY - minY);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copies this instance
|
* Copies this instance
|
||||||
* @returns {Rectangle}
|
* @returns {Rectangle}
|
||||||
@ -82,28 +61,6 @@ export class Rectangle {
|
|||||||
return new Rectangle(this.x, this.y, this.w, this.h);
|
return new Rectangle(this.x, this.y, this.w, this.h);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Ensures the rectangle contains the given square
|
|
||||||
* @param {number} centerX
|
|
||||||
* @param {number} centerY
|
|
||||||
* @param {number} halfWidth
|
|
||||||
* @param {number} halfHeight
|
|
||||||
*/
|
|
||||||
extendBySquare(centerX, centerY, halfWidth, halfHeight) {
|
|
||||||
if (this.isEmpty()) {
|
|
||||||
// Just assign values since this rectangle is empty
|
|
||||||
this.x = centerX - halfWidth;
|
|
||||||
this.y = centerY - halfHeight;
|
|
||||||
this.w = halfWidth * 2;
|
|
||||||
this.h = halfHeight * 2;
|
|
||||||
} else {
|
|
||||||
this.setLeft(Math.min(this.x, centerX - halfWidth));
|
|
||||||
this.setRight(Math.max(this.right(), centerX + halfWidth));
|
|
||||||
this.setTop(Math.min(this.y, centerY - halfHeight));
|
|
||||||
this.setBottom(Math.max(this.bottom(), centerY + halfHeight));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns if this rectangle is empty
|
* Returns if this rectangle is empty
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
@ -259,14 +216,6 @@ export class Rectangle {
|
|||||||
return new Rectangle(this.x - amount, this.y - amount, this.w + 2 * amount, this.h + 2 * amount);
|
return new Rectangle(this.x - amount, this.y - amount, this.w + 2 * amount, this.h + 2 * amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper for computing a culling area. Returns the top left tile
|
|
||||||
* @returns {Vector}
|
|
||||||
*/
|
|
||||||
getMinStartTile() {
|
|
||||||
return new Vector(this.x, this.y).snapWorldToTile();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns if the given rectangle is contained
|
* Returns if the given rectangle is contained
|
||||||
* @param {Rectangle} rect
|
* @param {Rectangle} rect
|
||||||
@ -394,7 +343,7 @@ export class Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a new recangle in tile space which includes all tiles which are visible in this rect
|
* Returns a new rectangle in tile space which includes all tiles which are visible in this rect
|
||||||
* @returns {Rectangle}
|
* @returns {Rectangle}
|
||||||
*/
|
*/
|
||||||
toTileCullRectangle() {
|
toTileCullRectangle() {
|
||||||
|
@ -1,122 +1,122 @@
|
|||||||
import { formatItemsPerSecond } from "../../core/utils";
|
import { formatItemsPerSecond } from "../../core/utils";
|
||||||
import { enumDirection, Vector } from "../../core/vector";
|
import { enumDirection, Vector } from "../../core/vector";
|
||||||
import { T } from "../../translations";
|
import { T } from "../../translations";
|
||||||
import { ItemAcceptorComponent } from "../components/item_acceptor";
|
import { ItemAcceptorComponent } from "../components/item_acceptor";
|
||||||
import { ItemEjectorComponent } from "../components/item_ejector";
|
import { ItemEjectorComponent } from "../components/item_ejector";
|
||||||
import { enumItemProcessorTypes, ItemProcessorComponent } from "../components/item_processor";
|
import { enumItemProcessorTypes, ItemProcessorComponent } from "../components/item_processor";
|
||||||
import { Entity } from "../entity";
|
import { Entity } from "../entity";
|
||||||
import { defaultBuildingVariant, MetaBuilding } from "../meta_building";
|
import { defaultBuildingVariant, MetaBuilding } from "../meta_building";
|
||||||
import { GameRoot } from "../root";
|
import { GameRoot } from "../root";
|
||||||
import { enumHubGoalRewards } from "../tutorial_goals";
|
import { enumHubGoalRewards } from "../tutorial_goals";
|
||||||
|
|
||||||
/** @enum {string} */
|
/** @enum {string} */
|
||||||
export const enumCutterVariants = { quad: "quad" };
|
export const enumCutterVariants = { quad: "quad" };
|
||||||
|
|
||||||
export class MetaCutterBuilding extends MetaBuilding {
|
export class MetaCutterBuilding extends MetaBuilding {
|
||||||
constructor() {
|
constructor() {
|
||||||
super("cutter");
|
super("cutter");
|
||||||
}
|
}
|
||||||
|
|
||||||
getSilhouetteColor() {
|
getSilhouetteColor() {
|
||||||
return "#7dcda2";
|
return "#7dcda2";
|
||||||
}
|
}
|
||||||
|
|
||||||
getDimensions(variant) {
|
getDimensions(variant) {
|
||||||
switch (variant) {
|
switch (variant) {
|
||||||
case defaultBuildingVariant:
|
case defaultBuildingVariant:
|
||||||
return new Vector(2, 1);
|
return new Vector(2, 1);
|
||||||
case enumCutterVariants.quad:
|
case enumCutterVariants.quad:
|
||||||
return new Vector(4, 1);
|
return new Vector(4, 1);
|
||||||
default:
|
default:
|
||||||
assertAlways(false, "Unknown splitter variant: " + variant);
|
assertAlways(false, "Unknown cutter variant: " + variant);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {GameRoot} root
|
* @param {GameRoot} root
|
||||||
* @param {string} variant
|
* @param {string} variant
|
||||||
* @returns {Array<[string, string]>}
|
* @returns {Array<[string, string]>}
|
||||||
*/
|
*/
|
||||||
getAdditionalStatistics(root, variant) {
|
getAdditionalStatistics(root, variant) {
|
||||||
const speed = root.hubGoals.getProcessorBaseSpeed(
|
const speed = root.hubGoals.getProcessorBaseSpeed(
|
||||||
variant === enumCutterVariants.quad
|
variant === enumCutterVariants.quad
|
||||||
? enumItemProcessorTypes.cutterQuad
|
? enumItemProcessorTypes.cutterQuad
|
||||||
: enumItemProcessorTypes.cutter
|
: enumItemProcessorTypes.cutter
|
||||||
);
|
);
|
||||||
return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(speed)]];
|
return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(speed)]];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {GameRoot} root
|
* @param {GameRoot} root
|
||||||
*/
|
*/
|
||||||
getAvailableVariants(root) {
|
getAvailableVariants(root) {
|
||||||
if (root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_cutter_quad)) {
|
if (root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_cutter_quad)) {
|
||||||
return [defaultBuildingVariant, enumCutterVariants.quad];
|
return [defaultBuildingVariant, enumCutterVariants.quad];
|
||||||
}
|
}
|
||||||
return super.getAvailableVariants(root);
|
return super.getAvailableVariants(root);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {GameRoot} root
|
* @param {GameRoot} root
|
||||||
*/
|
*/
|
||||||
getIsUnlocked(root) {
|
getIsUnlocked(root) {
|
||||||
return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_cutter_and_trash);
|
return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_cutter_and_trash);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the entity at the given location
|
* Creates the entity at the given location
|
||||||
* @param {Entity} entity
|
* @param {Entity} entity
|
||||||
*/
|
*/
|
||||||
setupEntityComponents(entity) {
|
setupEntityComponents(entity) {
|
||||||
entity.addComponent(
|
entity.addComponent(
|
||||||
new ItemProcessorComponent({
|
new ItemProcessorComponent({
|
||||||
inputsPerCharge: 1,
|
inputsPerCharge: 1,
|
||||||
processorType: enumItemProcessorTypes.cutter,
|
processorType: enumItemProcessorTypes.cutter,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
entity.addComponent(new ItemEjectorComponent({}));
|
entity.addComponent(new ItemEjectorComponent({}));
|
||||||
entity.addComponent(
|
entity.addComponent(
|
||||||
new ItemAcceptorComponent({
|
new ItemAcceptorComponent({
|
||||||
slots: [
|
slots: [
|
||||||
{
|
{
|
||||||
pos: new Vector(0, 0),
|
pos: new Vector(0, 0),
|
||||||
directions: [enumDirection.bottom],
|
directions: [enumDirection.bottom],
|
||||||
filter: "shape",
|
filter: "shape",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {Entity} entity
|
* @param {Entity} entity
|
||||||
* @param {number} rotationVariant
|
* @param {number} rotationVariant
|
||||||
* @param {string} variant
|
* @param {string} variant
|
||||||
*/
|
*/
|
||||||
updateVariants(entity, rotationVariant, variant) {
|
updateVariants(entity, rotationVariant, variant) {
|
||||||
switch (variant) {
|
switch (variant) {
|
||||||
case defaultBuildingVariant: {
|
case defaultBuildingVariant: {
|
||||||
entity.components.ItemEjector.setSlots([
|
entity.components.ItemEjector.setSlots([
|
||||||
{ pos: new Vector(0, 0), direction: enumDirection.top },
|
{ pos: new Vector(0, 0), direction: enumDirection.top },
|
||||||
{ pos: new Vector(1, 0), direction: enumDirection.top },
|
{ pos: new Vector(1, 0), direction: enumDirection.top },
|
||||||
]);
|
]);
|
||||||
entity.components.ItemProcessor.type = enumItemProcessorTypes.cutter;
|
entity.components.ItemProcessor.type = enumItemProcessorTypes.cutter;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case enumCutterVariants.quad: {
|
case enumCutterVariants.quad: {
|
||||||
entity.components.ItemEjector.setSlots([
|
entity.components.ItemEjector.setSlots([
|
||||||
{ pos: new Vector(0, 0), direction: enumDirection.top },
|
{ pos: new Vector(0, 0), direction: enumDirection.top },
|
||||||
{ pos: new Vector(1, 0), direction: enumDirection.top },
|
{ pos: new Vector(1, 0), direction: enumDirection.top },
|
||||||
{ pos: new Vector(2, 0), direction: enumDirection.top },
|
{ pos: new Vector(2, 0), direction: enumDirection.top },
|
||||||
{ pos: new Vector(3, 0), direction: enumDirection.top },
|
{ pos: new Vector(3, 0), direction: enumDirection.top },
|
||||||
]);
|
]);
|
||||||
entity.components.ItemProcessor.type = enumItemProcessorTypes.cutterQuad;
|
entity.components.ItemProcessor.type = enumItemProcessorTypes.cutterQuad;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
assertAlways(false, "Unknown painter variant: " + variant);
|
assertAlways(false, "Unknown painter variant: " + variant);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,61 +1,57 @@
|
|||||||
import { enumDirection, Vector } from "../../core/vector";
|
import { enumDirection, Vector } from "../../core/vector";
|
||||||
import { enumPinSlotType, WiredPinsComponent } from "../components/wired_pins";
|
import { enumPinSlotType, WiredPinsComponent } from "../components/wired_pins";
|
||||||
import { Entity } from "../entity";
|
import { Entity } from "../entity";
|
||||||
import { MetaBuilding } from "../meta_building";
|
import { MetaBuilding } from "../meta_building";
|
||||||
import { GameRoot } from "../root";
|
import { GameRoot } from "../root";
|
||||||
import { LeverComponent } from "../components/lever";
|
import { LeverComponent } from "../components/lever";
|
||||||
|
|
||||||
export class MetaLeverBuilding extends MetaBuilding {
|
export class MetaLeverBuilding extends MetaBuilding {
|
||||||
constructor() {
|
constructor() {
|
||||||
super("lever");
|
super("lever");
|
||||||
}
|
}
|
||||||
|
|
||||||
getSilhouetteColor() {
|
getSilhouetteColor() {
|
||||||
// @todo: Render differently based on if its activated or not
|
// @todo: Render differently based on if its activated or not
|
||||||
return "#1a678b";
|
return "#1a678b";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {GameRoot} root
|
* @param {GameRoot} root
|
||||||
*/
|
*/
|
||||||
getIsUnlocked(root) {
|
getIsUnlocked(root) {
|
||||||
// @todo
|
// @todo
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
getIsRotateable() {
|
getDimensions() {
|
||||||
return false;
|
return new Vector(1, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
getDimensions() {
|
getSprite() {
|
||||||
return new Vector(1, 1);
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
getSprite() {
|
getShowWiresLayerPreview() {
|
||||||
return null;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
getShowWiresLayerPreview() {
|
/**
|
||||||
return true;
|
* Creates the entity at the given location
|
||||||
}
|
* @param {Entity} entity
|
||||||
|
*/
|
||||||
/**
|
setupEntityComponents(entity) {
|
||||||
* Creates the entity at the given location
|
entity.addComponent(
|
||||||
* @param {Entity} entity
|
new WiredPinsComponent({
|
||||||
*/
|
slots: [
|
||||||
setupEntityComponents(entity) {
|
{
|
||||||
entity.addComponent(
|
pos: new Vector(0, 0),
|
||||||
new WiredPinsComponent({
|
direction: enumDirection.top,
|
||||||
slots: [
|
type: enumPinSlotType.logicalEjector,
|
||||||
{
|
},
|
||||||
pos: new Vector(0, 0),
|
],
|
||||||
direction: enumDirection.top,
|
})
|
||||||
type: enumPinSlotType.logicalEjector,
|
);
|
||||||
},
|
|
||||||
],
|
entity.addComponent(new LeverComponent({}));
|
||||||
})
|
}
|
||||||
);
|
}
|
||||||
|
|
||||||
entity.addComponent(new LeverComponent({}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,158 +1,197 @@
|
|||||||
import { enumDirection, Vector } from "../../core/vector";
|
import { enumDirection, Vector } from "../../core/vector";
|
||||||
import { ItemAcceptorComponent } from "../components/item_acceptor";
|
import { ItemAcceptorComponent } from "../components/item_acceptor";
|
||||||
import { ItemEjectorComponent } from "../components/item_ejector";
|
import { ItemEjectorComponent } from "../components/item_ejector";
|
||||||
import { enumItemProcessorTypes, ItemProcessorComponent } from "../components/item_processor";
|
import { enumItemProcessorTypes, ItemProcessorComponent } from "../components/item_processor";
|
||||||
import { Entity } from "../entity";
|
import { Entity } from "../entity";
|
||||||
import { MetaBuilding, defaultBuildingVariant } from "../meta_building";
|
import { MetaBuilding, defaultBuildingVariant } from "../meta_building";
|
||||||
import { GameRoot } from "../root";
|
import { GameRoot } from "../root";
|
||||||
import { enumHubGoalRewards } from "../tutorial_goals";
|
import { enumHubGoalRewards } from "../tutorial_goals";
|
||||||
import { T } from "../../translations";
|
import { T } from "../../translations";
|
||||||
import { formatItemsPerSecond } from "../../core/utils";
|
import { formatItemsPerSecond } from "../../core/utils";
|
||||||
import { BeltUnderlaysComponent } from "../components/belt_underlays";
|
import { BeltUnderlaysComponent } from "../components/belt_underlays";
|
||||||
|
|
||||||
/** @enum {string} */
|
/** @enum {string} */
|
||||||
export const enumSplitterVariants = { compact: "compact", compactInverse: "compact-inverse" };
|
export const enumSplitterVariants = {
|
||||||
|
compact: "compact",
|
||||||
export class MetaSplitterBuilding extends MetaBuilding {
|
compactInverse: "compact-inverse",
|
||||||
constructor() {
|
compactMerge: "compact-merge",
|
||||||
super("splitter");
|
compactMergeInverse: "compact-merge-inverse",
|
||||||
}
|
};
|
||||||
|
|
||||||
getDimensions(variant) {
|
export class MetaSplitterBuilding extends MetaBuilding {
|
||||||
switch (variant) {
|
constructor() {
|
||||||
case defaultBuildingVariant:
|
super("splitter");
|
||||||
return new Vector(2, 1);
|
}
|
||||||
case enumSplitterVariants.compact:
|
|
||||||
case enumSplitterVariants.compactInverse:
|
getDimensions(variant) {
|
||||||
return new Vector(1, 1);
|
switch (variant) {
|
||||||
default:
|
case defaultBuildingVariant:
|
||||||
assertAlways(false, "Unknown splitter variant: " + variant);
|
return new Vector(2, 1);
|
||||||
}
|
case enumSplitterVariants.compact:
|
||||||
}
|
case enumSplitterVariants.compactInverse:
|
||||||
|
case enumSplitterVariants.compactMerge:
|
||||||
/**
|
case enumSplitterVariants.compactMergeInverse:
|
||||||
* @param {GameRoot} root
|
return new Vector(1, 1);
|
||||||
* @param {string} variant
|
default:
|
||||||
* @returns {Array<[string, string]>}
|
assertAlways(false, "Unknown splitter variant: " + variant);
|
||||||
*/
|
}
|
||||||
getAdditionalStatistics(root, variant) {
|
}
|
||||||
const speed = root.hubGoals.getProcessorBaseSpeed(enumItemProcessorTypes.splitter);
|
|
||||||
return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(speed)]];
|
/**
|
||||||
}
|
* @param {GameRoot} root
|
||||||
|
* @param {string} variant
|
||||||
getSilhouetteColor() {
|
* @returns {Array<[string, string]>}
|
||||||
return "#444";
|
*/
|
||||||
}
|
getAdditionalStatistics(root, variant) {
|
||||||
|
const speed = root.hubGoals.getProcessorBaseSpeed(enumItemProcessorTypes.splitter);
|
||||||
/**
|
return [[T.ingame.buildingPlacement.infoTexts.speed, formatItemsPerSecond(speed)]];
|
||||||
* @param {GameRoot} root
|
}
|
||||||
*/
|
|
||||||
getAvailableVariants(root) {
|
getSilhouetteColor() {
|
||||||
if (root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_splitter_compact)) {
|
return "#444";
|
||||||
return [
|
}
|
||||||
defaultBuildingVariant,
|
|
||||||
enumSplitterVariants.compact,
|
/**
|
||||||
enumSplitterVariants.compactInverse,
|
* @param {GameRoot} root
|
||||||
];
|
*/
|
||||||
}
|
getAvailableVariants(root) {
|
||||||
return super.getAvailableVariants(root);
|
let available = [defaultBuildingVariant];
|
||||||
}
|
|
||||||
|
if (root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_splitter_compact)) {
|
||||||
/**
|
available.push(enumSplitterVariants.compact, enumSplitterVariants.compactInverse);
|
||||||
* @param {GameRoot} root
|
}
|
||||||
*/
|
|
||||||
getIsUnlocked(root) {
|
if (root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_merger_compact)) {
|
||||||
return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_splitter);
|
available.push(enumSplitterVariants.compactMerge, enumSplitterVariants.compactMergeInverse);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
return available;
|
||||||
* Creates the entity at the given location
|
}
|
||||||
* @param {Entity} entity
|
|
||||||
*/
|
/**
|
||||||
setupEntityComponents(entity) {
|
* @param {GameRoot} root
|
||||||
entity.addComponent(
|
*/
|
||||||
new ItemAcceptorComponent({
|
getIsUnlocked(root) {
|
||||||
slots: [], // set later
|
return root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_splitter);
|
||||||
})
|
}
|
||||||
);
|
|
||||||
|
/**
|
||||||
entity.addComponent(
|
* Creates the entity at the given location
|
||||||
new ItemProcessorComponent({
|
* @param {Entity} entity
|
||||||
inputsPerCharge: 1,
|
*/
|
||||||
processorType: enumItemProcessorTypes.splitter,
|
setupEntityComponents(entity) {
|
||||||
})
|
entity.addComponent(
|
||||||
);
|
new ItemAcceptorComponent({
|
||||||
|
slots: [], // set later
|
||||||
entity.addComponent(
|
})
|
||||||
new ItemEjectorComponent({
|
);
|
||||||
slots: [], // set later
|
|
||||||
})
|
entity.addComponent(
|
||||||
);
|
new ItemProcessorComponent({
|
||||||
|
inputsPerCharge: 1,
|
||||||
entity.addComponent(new BeltUnderlaysComponent({ underlays: [] }));
|
processorType: enumItemProcessorTypes.splitter,
|
||||||
}
|
})
|
||||||
|
);
|
||||||
/**
|
|
||||||
*
|
entity.addComponent(
|
||||||
* @param {Entity} entity
|
new ItemEjectorComponent({
|
||||||
* @param {number} rotationVariant
|
slots: [], // set later
|
||||||
* @param {string} variant
|
})
|
||||||
*/
|
);
|
||||||
updateVariants(entity, rotationVariant, variant) {
|
|
||||||
switch (variant) {
|
entity.addComponent(new BeltUnderlaysComponent({ underlays: [] }));
|
||||||
case defaultBuildingVariant: {
|
}
|
||||||
entity.components.ItemAcceptor.setSlots([
|
|
||||||
{
|
/**
|
||||||
pos: new Vector(0, 0),
|
*
|
||||||
directions: [enumDirection.bottom],
|
* @param {Entity} entity
|
||||||
},
|
* @param {number} rotationVariant
|
||||||
{
|
* @param {string} variant
|
||||||
pos: new Vector(1, 0),
|
*/
|
||||||
directions: [enumDirection.bottom],
|
updateVariants(entity, rotationVariant, variant) {
|
||||||
},
|
switch (variant) {
|
||||||
]);
|
case defaultBuildingVariant: {
|
||||||
|
entity.components.ItemAcceptor.setSlots([
|
||||||
entity.components.ItemEjector.setSlots([
|
{
|
||||||
{ pos: new Vector(0, 0), direction: enumDirection.top },
|
pos: new Vector(0, 0),
|
||||||
{ pos: new Vector(1, 0), direction: enumDirection.top },
|
directions: [enumDirection.bottom],
|
||||||
]);
|
},
|
||||||
|
{
|
||||||
entity.components.BeltUnderlays.underlays = [
|
pos: new Vector(1, 0),
|
||||||
{ pos: new Vector(0, 0), direction: enumDirection.top },
|
directions: [enumDirection.bottom],
|
||||||
{ pos: new Vector(1, 0), direction: enumDirection.top },
|
},
|
||||||
];
|
]);
|
||||||
|
|
||||||
break;
|
entity.components.ItemEjector.setSlots([
|
||||||
}
|
{ pos: new Vector(0, 0), direction: enumDirection.top },
|
||||||
case enumSplitterVariants.compact:
|
{ pos: new Vector(1, 0), direction: enumDirection.top },
|
||||||
case enumSplitterVariants.compactInverse: {
|
]);
|
||||||
entity.components.ItemAcceptor.setSlots([
|
|
||||||
{
|
entity.components.BeltUnderlays.underlays = [
|
||||||
pos: new Vector(0, 0),
|
{ pos: new Vector(0, 0), direction: enumDirection.top },
|
||||||
directions: [enumDirection.bottom],
|
{ pos: new Vector(1, 0), direction: enumDirection.top },
|
||||||
},
|
];
|
||||||
{
|
|
||||||
pos: new Vector(0, 0),
|
break;
|
||||||
directions: [
|
}
|
||||||
variant === enumSplitterVariants.compactInverse
|
case enumSplitterVariants.compact:
|
||||||
? enumDirection.left
|
case enumSplitterVariants.compactInverse: {
|
||||||
: enumDirection.right,
|
entity.components.ItemAcceptor.setSlots([
|
||||||
],
|
{
|
||||||
},
|
pos: new Vector(0, 0),
|
||||||
]);
|
directions: [enumDirection.bottom],
|
||||||
|
},
|
||||||
entity.components.ItemEjector.setSlots([
|
{
|
||||||
{ pos: new Vector(0, 0), direction: enumDirection.top },
|
pos: new Vector(0, 0),
|
||||||
]);
|
directions: [
|
||||||
|
variant === enumSplitterVariants.compactInverse
|
||||||
entity.components.BeltUnderlays.underlays = [
|
? enumDirection.left
|
||||||
{ pos: new Vector(0, 0), direction: enumDirection.top },
|
: enumDirection.right,
|
||||||
];
|
],
|
||||||
|
},
|
||||||
break;
|
]);
|
||||||
}
|
|
||||||
default:
|
entity.components.ItemEjector.setSlots([
|
||||||
assertAlways(false, "Unknown painter variant: " + variant);
|
{ pos: new Vector(0, 0), direction: enumDirection.top },
|
||||||
}
|
]);
|
||||||
}
|
|
||||||
}
|
entity.components.BeltUnderlays.underlays = [
|
||||||
|
{ pos: new Vector(0, 0), direction: enumDirection.top },
|
||||||
|
];
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case enumSplitterVariants.compactMerge:
|
||||||
|
case enumSplitterVariants.compactMergeInverse: {
|
||||||
|
entity.components.ItemAcceptor.setSlots([
|
||||||
|
{
|
||||||
|
pos: new Vector(0, 0),
|
||||||
|
directions: [enumDirection.bottom],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
entity.components.ItemEjector.setSlots([
|
||||||
|
{
|
||||||
|
pos: new Vector(0, 0),
|
||||||
|
direction: enumDirection.top,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pos: new Vector(0, 0),
|
||||||
|
direction:
|
||||||
|
variant === enumSplitterVariants.compactMergeInverse
|
||||||
|
? enumDirection.left
|
||||||
|
: enumDirection.right,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
entity.components.BeltUnderlays.underlays = [
|
||||||
|
{ pos: new Vector(0, 0), direction: enumDirection.top },
|
||||||
|
];
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
assertAlways(false, "Unknown splitter variant: " + variant);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
151
src/js/game/buildings/virtual_processor.js
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
import { Vector, enumDirection } from "../../core/vector";
|
||||||
|
import { LogicGateComponent, enumLogicGateType } from "../components/logic_gate";
|
||||||
|
import { WiredPinsComponent, enumPinSlotType } from "../components/wired_pins";
|
||||||
|
import { Entity } from "../entity";
|
||||||
|
import { defaultBuildingVariant, MetaBuilding } from "../meta_building";
|
||||||
|
import { GameRoot } from "../root";
|
||||||
|
|
||||||
|
/** @enum {string} */
|
||||||
|
export const enumVirtualProcessorVariants = {
|
||||||
|
analyzer: "analyzer",
|
||||||
|
rotater: "rotater",
|
||||||
|
unstacker: "unstacker",
|
||||||
|
shapecompare: "shapecompare",
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @enum {string} */
|
||||||
|
export const enumVariantToGate = {
|
||||||
|
[defaultBuildingVariant]: enumLogicGateType.cutter,
|
||||||
|
[enumVirtualProcessorVariants.analyzer]: enumLogicGateType.analyzer,
|
||||||
|
[enumVirtualProcessorVariants.rotater]: enumLogicGateType.rotater,
|
||||||
|
[enumVirtualProcessorVariants.unstacker]: enumLogicGateType.unstacker,
|
||||||
|
[enumVirtualProcessorVariants.shapecompare]: enumLogicGateType.shapecompare,
|
||||||
|
};
|
||||||
|
|
||||||
|
export class MetaVirtualProcessorBuilding extends MetaBuilding {
|
||||||
|
constructor() {
|
||||||
|
super("virtual_processor");
|
||||||
|
}
|
||||||
|
|
||||||
|
getSilhouetteColor() {
|
||||||
|
return "#823cab";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {GameRoot} root
|
||||||
|
*/
|
||||||
|
getIsUnlocked(root) {
|
||||||
|
// @todo
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @returns {"wires"} **/
|
||||||
|
getLayer() {
|
||||||
|
return "wires";
|
||||||
|
}
|
||||||
|
|
||||||
|
getDimensions() {
|
||||||
|
return new Vector(1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
getAvailableVariants() {
|
||||||
|
return [
|
||||||
|
defaultBuildingVariant,
|
||||||
|
enumVirtualProcessorVariants.rotater,
|
||||||
|
enumVirtualProcessorVariants.unstacker,
|
||||||
|
enumVirtualProcessorVariants.analyzer,
|
||||||
|
enumVirtualProcessorVariants.shapecompare,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getRenderPins() {
|
||||||
|
// We already have it included
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {Entity} entity
|
||||||
|
* @param {number} rotationVariant
|
||||||
|
*/
|
||||||
|
updateVariants(entity, rotationVariant, variant) {
|
||||||
|
const gateType = enumVariantToGate[variant];
|
||||||
|
entity.components.LogicGate.type = gateType;
|
||||||
|
const pinComp = entity.components.WiredPins;
|
||||||
|
switch (gateType) {
|
||||||
|
case enumLogicGateType.cutter:
|
||||||
|
case enumLogicGateType.analyzer:
|
||||||
|
case enumLogicGateType.unstacker: {
|
||||||
|
pinComp.setSlots([
|
||||||
|
{
|
||||||
|
pos: new Vector(0, 0),
|
||||||
|
direction: enumDirection.left,
|
||||||
|
type: enumPinSlotType.logicalEjector,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pos: new Vector(0, 0),
|
||||||
|
direction: enumDirection.right,
|
||||||
|
type: enumPinSlotType.logicalEjector,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pos: new Vector(0, 0),
|
||||||
|
direction: enumDirection.bottom,
|
||||||
|
type: enumPinSlotType.logicalAcceptor,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case enumLogicGateType.rotater: {
|
||||||
|
pinComp.setSlots([
|
||||||
|
{
|
||||||
|
pos: new Vector(0, 0),
|
||||||
|
direction: enumDirection.top,
|
||||||
|
type: enumPinSlotType.logicalEjector,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pos: new Vector(0, 0),
|
||||||
|
direction: enumDirection.bottom,
|
||||||
|
type: enumPinSlotType.logicalAcceptor,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case enumLogicGateType.shapecompare: {
|
||||||
|
pinComp.setSlots([
|
||||||
|
{
|
||||||
|
pos: new Vector(0, 0),
|
||||||
|
direction: enumDirection.top,
|
||||||
|
type: enumPinSlotType.logicalEjector,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pos: new Vector(0, 0),
|
||||||
|
direction: enumDirection.left,
|
||||||
|
type: enumPinSlotType.logicalAcceptor,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pos: new Vector(0, 0),
|
||||||
|
direction: enumDirection.right,
|
||||||
|
type: enumPinSlotType.logicalAcceptor,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
assertAlways("unknown logic gate type: " + gateType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the entity at the given location
|
||||||
|
* @param {Entity} entity
|
||||||
|
*/
|
||||||
|
setupEntityComponents(entity) {
|
||||||
|
entity.addComponent(
|
||||||
|
new WiredPinsComponent({
|
||||||
|
slots: [],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
entity.addComponent(new LogicGateComponent({}));
|
||||||
|
}
|
||||||
|
}
|
@ -1,30 +1,36 @@
|
|||||||
import { Component } from "../component";
|
import { Component } from "../component";
|
||||||
|
|
||||||
/** @enum {string} */
|
/** @enum {string} */
|
||||||
export const enumLogicGateType = {
|
export const enumLogicGateType = {
|
||||||
and: "and",
|
and: "and",
|
||||||
not: "not",
|
not: "not",
|
||||||
xor: "xor",
|
xor: "xor",
|
||||||
or: "or",
|
or: "or",
|
||||||
transistor: "transistor",
|
transistor: "transistor",
|
||||||
};
|
|
||||||
|
analyzer: "analyzer",
|
||||||
export class LogicGateComponent extends Component {
|
rotater: "rotater",
|
||||||
static getId() {
|
unstacker: "unstacker",
|
||||||
return "LogicGate";
|
cutter: "cutter",
|
||||||
}
|
shapecompare: "shapecompare",
|
||||||
|
};
|
||||||
duplicateWithoutContents() {
|
|
||||||
return new LogicGateComponent({ type: this.type });
|
export class LogicGateComponent extends Component {
|
||||||
}
|
static getId() {
|
||||||
|
return "LogicGate";
|
||||||
/**
|
}
|
||||||
*
|
|
||||||
* @param {object} param0
|
duplicateWithoutContents() {
|
||||||
* @param {enumLogicGateType=} param0.type
|
return new LogicGateComponent({ type: this.type });
|
||||||
*/
|
}
|
||||||
constructor({ type = enumLogicGateType.and }) {
|
|
||||||
super();
|
/**
|
||||||
this.type = type;
|
*
|
||||||
}
|
* @param {object} param0
|
||||||
}
|
* @param {enumLogicGateType=} param0.type
|
||||||
|
*/
|
||||||
|
constructor({ type = enumLogicGateType.and }) {
|
||||||
|
super();
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,492 +1,492 @@
|
|||||||
/* typehints:start */
|
/* typehints:start */
|
||||||
import { Application } from "../application";
|
import { Application } from "../application";
|
||||||
/* typehints:end */
|
/* typehints:end */
|
||||||
import { BufferMaintainer } from "../core/buffer_maintainer";
|
import { BufferMaintainer } from "../core/buffer_maintainer";
|
||||||
import { disableImageSmoothing, enableImageSmoothing, registerCanvas } from "../core/buffer_utils";
|
import { disableImageSmoothing, enableImageSmoothing, registerCanvas } from "../core/buffer_utils";
|
||||||
import { globalConfig } from "../core/config";
|
import { globalConfig } from "../core/config";
|
||||||
import { getDeviceDPI, resizeHighDPICanvas } from "../core/dpi_manager";
|
import { getDeviceDPI, resizeHighDPICanvas } from "../core/dpi_manager";
|
||||||
import { DrawParameters } from "../core/draw_parameters";
|
import { DrawParameters } from "../core/draw_parameters";
|
||||||
import { gMetaBuildingRegistry } from "../core/global_registries";
|
import { gMetaBuildingRegistry } from "../core/global_registries";
|
||||||
import { createLogger } from "../core/logging";
|
import { createLogger } from "../core/logging";
|
||||||
import { Rectangle } from "../core/rectangle";
|
import { Rectangle } from "../core/rectangle";
|
||||||
import { randomInt, round2Digits, round3Digits } from "../core/utils";
|
import { randomInt, round2Digits, round3Digits } from "../core/utils";
|
||||||
import { Vector } from "../core/vector";
|
import { Vector } from "../core/vector";
|
||||||
import { Savegame } from "../savegame/savegame";
|
import { Savegame } from "../savegame/savegame";
|
||||||
import { SavegameSerializer } from "../savegame/savegame_serializer";
|
import { SavegameSerializer } from "../savegame/savegame_serializer";
|
||||||
import { AutomaticSave } from "./automatic_save";
|
import { AutomaticSave } from "./automatic_save";
|
||||||
import { MetaHubBuilding } from "./buildings/hub";
|
import { MetaHubBuilding } from "./buildings/hub";
|
||||||
import { Camera } from "./camera";
|
import { Camera } from "./camera";
|
||||||
import { DynamicTickrate } from "./dynamic_tickrate";
|
import { DynamicTickrate } from "./dynamic_tickrate";
|
||||||
import { EntityManager } from "./entity_manager";
|
import { EntityManager } from "./entity_manager";
|
||||||
import { GameSystemManager } from "./game_system_manager";
|
import { GameSystemManager } from "./game_system_manager";
|
||||||
import { HubGoals } from "./hub_goals";
|
import { HubGoals } from "./hub_goals";
|
||||||
import { GameHUD } from "./hud/hud";
|
import { GameHUD } from "./hud/hud";
|
||||||
import { KeyActionMapper } from "./key_action_mapper";
|
import { KeyActionMapper } from "./key_action_mapper";
|
||||||
import { GameLogic } from "./logic";
|
import { GameLogic } from "./logic";
|
||||||
import { MapView } from "./map_view";
|
import { MapView } from "./map_view";
|
||||||
import { defaultBuildingVariant } from "./meta_building";
|
import { defaultBuildingVariant } from "./meta_building";
|
||||||
import { ProductionAnalytics } from "./production_analytics";
|
import { ProductionAnalytics } from "./production_analytics";
|
||||||
import { GameRoot } from "./root";
|
import { GameRoot } from "./root";
|
||||||
import { ShapeDefinitionManager } from "./shape_definition_manager";
|
import { ShapeDefinitionManager } from "./shape_definition_manager";
|
||||||
import { SoundProxy } from "./sound_proxy";
|
import { SoundProxy } from "./sound_proxy";
|
||||||
import { GameTime } from "./time/game_time";
|
import { GameTime } from "./time/game_time";
|
||||||
import { ORIGINAL_SPRITE_SCALE } from "../core/sprites";
|
import { ORIGINAL_SPRITE_SCALE } from "../core/sprites";
|
||||||
|
|
||||||
const logger = createLogger("ingame/core");
|
const logger = createLogger("ingame/core");
|
||||||
|
|
||||||
// Store the canvas so we can reuse it later
|
// Store the canvas so we can reuse it later
|
||||||
/** @type {HTMLCanvasElement} */
|
/** @type {HTMLCanvasElement} */
|
||||||
let lastCanvas = null;
|
let lastCanvas = null;
|
||||||
/** @type {CanvasRenderingContext2D} */
|
/** @type {CanvasRenderingContext2D} */
|
||||||
let lastContext = null;
|
let lastContext = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The core manages the root and represents the whole game. It wraps the root, since
|
* The core manages the root and represents the whole game. It wraps the root, since
|
||||||
* the root class is just a data holder.
|
* the root class is just a data holder.
|
||||||
*/
|
*/
|
||||||
export class GameCore {
|
export class GameCore {
|
||||||
/** @param {Application} app */
|
/** @param {Application} app */
|
||||||
constructor(app) {
|
constructor(app) {
|
||||||
this.app = app;
|
this.app = app;
|
||||||
|
|
||||||
/** @type {GameRoot} */
|
/** @type {GameRoot} */
|
||||||
this.root = null;
|
this.root = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set to true at the beginning of a logic update and cleared when its finished.
|
* Set to true at the beginning of a logic update and cleared when its finished.
|
||||||
* This is to prevent doing a recursive logic update which can lead to unexpected
|
* This is to prevent doing a recursive logic update which can lead to unexpected
|
||||||
* behaviour.
|
* behaviour.
|
||||||
*/
|
*/
|
||||||
this.duringLogicUpdate = false;
|
this.duringLogicUpdate = false;
|
||||||
|
|
||||||
// Cached
|
// Cached
|
||||||
this.boundInternalTick = this.updateLogic.bind(this);
|
this.boundInternalTick = this.updateLogic.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the root object which stores all game related data. The state
|
* Initializes the root object which stores all game related data. The state
|
||||||
* is required as a back reference (used sometimes)
|
* is required as a back reference (used sometimes)
|
||||||
* @param {import("../states/ingame").InGameState} parentState
|
* @param {import("../states/ingame").InGameState} parentState
|
||||||
* @param {Savegame} savegame
|
* @param {Savegame} savegame
|
||||||
*/
|
*/
|
||||||
initializeRoot(parentState, savegame) {
|
initializeRoot(parentState, savegame) {
|
||||||
// Construct the root element, this is the data representation of the game
|
// Construct the root element, this is the data representation of the game
|
||||||
this.root = new GameRoot(this.app);
|
this.root = new GameRoot(this.app);
|
||||||
this.root.gameState = parentState;
|
this.root.gameState = parentState;
|
||||||
this.root.keyMapper = parentState.keyActionMapper;
|
this.root.keyMapper = parentState.keyActionMapper;
|
||||||
this.root.savegame = savegame;
|
this.root.savegame = savegame;
|
||||||
this.root.gameWidth = this.app.screenWidth;
|
this.root.gameWidth = this.app.screenWidth;
|
||||||
this.root.gameHeight = this.app.screenHeight;
|
this.root.gameHeight = this.app.screenHeight;
|
||||||
|
|
||||||
// Initialize canvas element & context
|
// Initialize canvas element & context
|
||||||
this.internalInitCanvas();
|
this.internalInitCanvas();
|
||||||
|
|
||||||
// Members
|
// Members
|
||||||
const root = this.root;
|
const root = this.root;
|
||||||
|
|
||||||
// This isn't nice, but we need it right here
|
// This isn't nice, but we need it right here
|
||||||
root.keyMapper = new KeyActionMapper(root, this.root.gameState.inputReciever);
|
root.keyMapper = new KeyActionMapper(root, this.root.gameState.inputReciever);
|
||||||
|
|
||||||
// Needs to come first
|
// Needs to come first
|
||||||
root.dynamicTickrate = new DynamicTickrate(root);
|
root.dynamicTickrate = new DynamicTickrate(root);
|
||||||
|
|
||||||
// Init classes
|
// Init classes
|
||||||
root.camera = new Camera(root);
|
root.camera = new Camera(root);
|
||||||
root.map = new MapView(root);
|
root.map = new MapView(root);
|
||||||
root.logic = new GameLogic(root);
|
root.logic = new GameLogic(root);
|
||||||
root.hud = new GameHUD(root);
|
root.hud = new GameHUD(root);
|
||||||
root.time = new GameTime(root);
|
root.time = new GameTime(root);
|
||||||
root.automaticSave = new AutomaticSave(root);
|
root.automaticSave = new AutomaticSave(root);
|
||||||
root.soundProxy = new SoundProxy(root);
|
root.soundProxy = new SoundProxy(root);
|
||||||
|
|
||||||
// Init managers
|
// Init managers
|
||||||
root.entityMgr = new EntityManager(root);
|
root.entityMgr = new EntityManager(root);
|
||||||
root.systemMgr = new GameSystemManager(root);
|
root.systemMgr = new GameSystemManager(root);
|
||||||
root.shapeDefinitionMgr = new ShapeDefinitionManager(root);
|
root.shapeDefinitionMgr = new ShapeDefinitionManager(root);
|
||||||
root.hubGoals = new HubGoals(root);
|
root.hubGoals = new HubGoals(root);
|
||||||
root.productionAnalytics = new ProductionAnalytics(root);
|
root.productionAnalytics = new ProductionAnalytics(root);
|
||||||
root.buffers = new BufferMaintainer(root);
|
root.buffers = new BufferMaintainer(root);
|
||||||
|
|
||||||
// Initialize the hud once everything is loaded
|
// Initialize the hud once everything is loaded
|
||||||
this.root.hud.initialize();
|
this.root.hud.initialize();
|
||||||
|
|
||||||
// Initial resize event, it might be possible that the screen
|
// Initial resize event, it might be possible that the screen
|
||||||
// resized later during init tho, which is why will emit it later
|
// resized later during init tho, which is why will emit it later
|
||||||
// again anyways
|
// again anyways
|
||||||
this.resize(this.app.screenWidth, this.app.screenHeight);
|
this.resize(this.app.screenWidth, this.app.screenHeight);
|
||||||
|
|
||||||
if (G_IS_DEV) {
|
if (G_IS_DEV) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
window.globalRoot = root;
|
window.globalRoot = root;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes a new game, this means creating a new map and centering on the
|
* Initializes a new game, this means creating a new map and centering on the
|
||||||
* playerbase
|
* playerbase
|
||||||
* */
|
* */
|
||||||
initNewGame() {
|
initNewGame() {
|
||||||
logger.log("Initializing new game");
|
logger.log("Initializing new game");
|
||||||
this.root.gameIsFresh = true;
|
this.root.gameIsFresh = true;
|
||||||
this.root.map.seed = randomInt(0, 100000);
|
this.root.map.seed = randomInt(0, 100000);
|
||||||
|
|
||||||
// Place the hub
|
// Place the hub
|
||||||
const hub = gMetaBuildingRegistry.findByClass(MetaHubBuilding).createEntity({
|
const hub = gMetaBuildingRegistry.findByClass(MetaHubBuilding).createEntity({
|
||||||
root: this.root,
|
root: this.root,
|
||||||
origin: new Vector(-2, -2),
|
origin: new Vector(-2, -2),
|
||||||
rotation: 0,
|
rotation: 0,
|
||||||
originalRotation: 0,
|
originalRotation: 0,
|
||||||
rotationVariant: 0,
|
rotationVariant: 0,
|
||||||
variant: defaultBuildingVariant,
|
variant: defaultBuildingVariant,
|
||||||
});
|
});
|
||||||
this.root.map.placeStaticEntity(hub);
|
this.root.map.placeStaticEntity(hub);
|
||||||
this.root.entityMgr.registerEntity(hub);
|
this.root.entityMgr.registerEntity(hub);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inits an existing game by loading the raw savegame data and deserializing it.
|
* Inits an existing game by loading the raw savegame data and deserializing it.
|
||||||
* Also runs basic validity checks.
|
* Also runs basic validity checks.
|
||||||
*/
|
*/
|
||||||
initExistingGame() {
|
initExistingGame() {
|
||||||
logger.log("Initializing existing game");
|
logger.log("Initializing existing game");
|
||||||
const serializer = new SavegameSerializer();
|
const serializer = new SavegameSerializer();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const status = serializer.deserialize(this.root.savegame.getCurrentDump(), this.root);
|
const status = serializer.deserialize(this.root.savegame.getCurrentDump(), this.root);
|
||||||
if (!status.isGood()) {
|
if (!status.isGood()) {
|
||||||
logger.error("savegame-deserialize-failed:" + status.reason);
|
logger.error("savegame-deserialize-failed:" + status.reason);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
logger.error("Exception during deserialization:", ex);
|
logger.error("Exception during deserialization:", ex);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
this.root.gameIsFresh = false;
|
this.root.gameIsFresh = false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the render canvas
|
* Initializes the render canvas
|
||||||
*/
|
*/
|
||||||
internalInitCanvas() {
|
internalInitCanvas() {
|
||||||
let canvas, context;
|
let canvas, context;
|
||||||
if (!lastCanvas) {
|
if (!lastCanvas) {
|
||||||
logger.log("Creating new canvas");
|
logger.log("Creating new canvas");
|
||||||
canvas = document.createElement("canvas");
|
canvas = document.createElement("canvas");
|
||||||
canvas.id = "ingame_Canvas";
|
canvas.id = "ingame_Canvas";
|
||||||
canvas.setAttribute("opaque", "true");
|
canvas.setAttribute("opaque", "true");
|
||||||
canvas.setAttribute("webkitOpaque", "true");
|
canvas.setAttribute("webkitOpaque", "true");
|
||||||
canvas.setAttribute("mozOpaque", "true");
|
canvas.setAttribute("mozOpaque", "true");
|
||||||
this.root.gameState.getDivElement().appendChild(canvas);
|
this.root.gameState.getDivElement().appendChild(canvas);
|
||||||
context = canvas.getContext("2d", { alpha: false });
|
context = canvas.getContext("2d", { alpha: false });
|
||||||
|
|
||||||
lastCanvas = canvas;
|
lastCanvas = canvas;
|
||||||
lastContext = context;
|
lastContext = context;
|
||||||
} else {
|
} else {
|
||||||
logger.log("Reusing canvas");
|
logger.log("Reusing canvas");
|
||||||
if (lastCanvas.parentElement) {
|
if (lastCanvas.parentElement) {
|
||||||
lastCanvas.parentElement.removeChild(lastCanvas);
|
lastCanvas.parentElement.removeChild(lastCanvas);
|
||||||
}
|
}
|
||||||
this.root.gameState.getDivElement().appendChild(lastCanvas);
|
this.root.gameState.getDivElement().appendChild(lastCanvas);
|
||||||
|
|
||||||
canvas = lastCanvas;
|
canvas = lastCanvas;
|
||||||
context = lastContext;
|
context = lastContext;
|
||||||
|
|
||||||
lastContext.clearRect(0, 0, lastCanvas.width, lastCanvas.height);
|
lastContext.clearRect(0, 0, lastCanvas.width, lastCanvas.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
// globalConfig.smoothing.smoothMainCanvas = getDeviceDPI() < 1.5;
|
// globalConfig.smoothing.smoothMainCanvas = getDeviceDPI() < 1.5;
|
||||||
// globalConfig.smoothing.smoothMainCanvas = true;
|
// globalConfig.smoothing.smoothMainCanvas = true;
|
||||||
|
|
||||||
canvas.classList.toggle("smoothed", globalConfig.smoothing.smoothMainCanvas);
|
canvas.classList.toggle("smoothed", globalConfig.smoothing.smoothMainCanvas);
|
||||||
|
|
||||||
// Oof, use :not() instead
|
// Oof, use :not() instead
|
||||||
canvas.classList.toggle("unsmoothed", !globalConfig.smoothing.smoothMainCanvas);
|
canvas.classList.toggle("unsmoothed", !globalConfig.smoothing.smoothMainCanvas);
|
||||||
|
|
||||||
if (globalConfig.smoothing.smoothMainCanvas) {
|
if (globalConfig.smoothing.smoothMainCanvas) {
|
||||||
enableImageSmoothing(context);
|
enableImageSmoothing(context);
|
||||||
} else {
|
} else {
|
||||||
disableImageSmoothing(context);
|
disableImageSmoothing(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.root.canvas = canvas;
|
this.root.canvas = canvas;
|
||||||
this.root.context = context;
|
this.root.context = context;
|
||||||
|
|
||||||
registerCanvas(canvas, context);
|
registerCanvas(canvas, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destructs the root, freeing all resources
|
* Destructs the root, freeing all resources
|
||||||
*/
|
*/
|
||||||
destruct() {
|
destruct() {
|
||||||
if (lastCanvas && lastCanvas.parentElement) {
|
if (lastCanvas && lastCanvas.parentElement) {
|
||||||
lastCanvas.parentElement.removeChild(lastCanvas);
|
lastCanvas.parentElement.removeChild(lastCanvas);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.root.destruct();
|
this.root.destruct();
|
||||||
delete this.root;
|
delete this.root;
|
||||||
this.root = null;
|
this.root = null;
|
||||||
this.app = null;
|
this.app = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
tick(deltaMs) {
|
tick(deltaMs) {
|
||||||
const root = this.root;
|
const root = this.root;
|
||||||
|
|
||||||
// Extract current real time
|
// Extract current real time
|
||||||
root.time.updateRealtimeNow();
|
root.time.updateRealtimeNow();
|
||||||
|
|
||||||
// Camera is always updated, no matter what
|
// Camera is always updated, no matter what
|
||||||
root.camera.update(deltaMs);
|
root.camera.update(deltaMs);
|
||||||
|
|
||||||
// Perform logic ticks
|
// Perform logic ticks
|
||||||
this.root.time.performTicks(deltaMs, this.boundInternalTick);
|
this.root.time.performTicks(deltaMs, this.boundInternalTick);
|
||||||
|
|
||||||
// Update analytics
|
// Update analytics
|
||||||
root.productionAnalytics.update();
|
root.productionAnalytics.update();
|
||||||
|
|
||||||
// Update automatic save after everything finished
|
// Update automatic save after everything finished
|
||||||
root.automaticSave.update();
|
root.automaticSave.update();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldRender() {
|
shouldRender() {
|
||||||
if (this.root.queue.requireRedraw) {
|
if (this.root.queue.requireRedraw) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (this.root.hud.shouldPauseRendering()) {
|
if (this.root.hud.shouldPauseRendering()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do not render
|
// Do not render
|
||||||
if (!this.app.isRenderable()) {
|
if (!this.app.isRenderable()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateLogic() {
|
updateLogic() {
|
||||||
const root = this.root;
|
const root = this.root;
|
||||||
|
|
||||||
root.dynamicTickrate.beginTick();
|
root.dynamicTickrate.beginTick();
|
||||||
|
|
||||||
if (G_IS_DEV && globalConfig.debug.disableLogicTicks) {
|
if (G_IS_DEV && globalConfig.debug.disableLogicTicks) {
|
||||||
root.dynamicTickrate.endTick();
|
root.dynamicTickrate.endTick();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.duringLogicUpdate = true;
|
this.duringLogicUpdate = true;
|
||||||
|
|
||||||
// Update entities, this removes destroyed entities
|
// Update entities, this removes destroyed entities
|
||||||
root.entityMgr.update();
|
root.entityMgr.update();
|
||||||
|
|
||||||
// IMPORTANT: At this point, the game might be game over. Stop if this is the case
|
// IMPORTANT: At this point, the game might be game over. Stop if this is the case
|
||||||
if (!this.root) {
|
if (!this.root) {
|
||||||
logger.log("Root destructed, returning false");
|
logger.log("Root destructed, returning false");
|
||||||
root.dynamicTickrate.endTick();
|
root.dynamicTickrate.endTick();
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
root.systemMgr.update();
|
root.systemMgr.update();
|
||||||
// root.particleMgr.update();
|
// root.particleMgr.update();
|
||||||
|
|
||||||
this.duringLogicUpdate = false;
|
this.duringLogicUpdate = false;
|
||||||
root.dynamicTickrate.endTick();
|
root.dynamicTickrate.endTick();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
resize(w, h) {
|
resize(w, h) {
|
||||||
this.root.gameWidth = w;
|
this.root.gameWidth = w;
|
||||||
this.root.gameHeight = h;
|
this.root.gameHeight = h;
|
||||||
resizeHighDPICanvas(this.root.canvas, w, h, globalConfig.smoothing.smoothMainCanvas);
|
resizeHighDPICanvas(this.root.canvas, w, h, globalConfig.smoothing.smoothMainCanvas);
|
||||||
this.root.signals.resized.dispatch(w, h);
|
this.root.signals.resized.dispatch(w, h);
|
||||||
this.root.queue.requireRedraw = true;
|
this.root.queue.requireRedraw = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
postLoadHook() {
|
postLoadHook() {
|
||||||
logger.log("Dispatching post load hook");
|
logger.log("Dispatching post load hook");
|
||||||
this.root.signals.postLoadHook.dispatch();
|
this.root.signals.postLoadHook.dispatch();
|
||||||
|
|
||||||
if (!this.root.gameIsFresh) {
|
if (!this.root.gameIsFresh) {
|
||||||
// Also dispatch game restored hook on restored savegames
|
// Also dispatch game restored hook on restored savegames
|
||||||
this.root.signals.gameRestored.dispatch();
|
this.root.signals.gameRestored.dispatch();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.root.gameInitialized = true;
|
this.root.gameInitialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
draw() {
|
draw() {
|
||||||
const root = this.root;
|
const root = this.root;
|
||||||
const systems = root.systemMgr.systems;
|
const systems = root.systemMgr.systems;
|
||||||
|
|
||||||
this.root.dynamicTickrate.onFrameRendered();
|
this.root.dynamicTickrate.onFrameRendered();
|
||||||
|
|
||||||
if (!this.shouldRender()) {
|
if (!this.shouldRender()) {
|
||||||
// Always update hud tho
|
// Always update hud tho
|
||||||
root.hud.update();
|
root.hud.update();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.root.signals.gameFrameStarted.dispatch();
|
this.root.signals.gameFrameStarted.dispatch();
|
||||||
|
|
||||||
root.queue.requireRedraw = false;
|
root.queue.requireRedraw = false;
|
||||||
|
|
||||||
// Gather context and save all state
|
// Gather context and save all state
|
||||||
const context = root.context;
|
const context = root.context;
|
||||||
context.save();
|
context.save();
|
||||||
if (G_IS_DEV) {
|
if (G_IS_DEV) {
|
||||||
context.fillStyle = "#a10000";
|
context.fillStyle = "#a10000";
|
||||||
context.fillRect(0, 0, window.innerWidth * 3, window.innerHeight * 3);
|
context.fillRect(0, 0, window.innerWidth * 3, window.innerHeight * 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute optimal zoom level and atlas scale
|
// Compute optimal zoom level and atlas scale
|
||||||
const zoomLevel = root.camera.zoomLevel;
|
const zoomLevel = root.camera.zoomLevel;
|
||||||
const lowQuality = root.app.settings.getAllSettings().lowQualityTextures;
|
const lowQuality = root.app.settings.getAllSettings().lowQualityTextures;
|
||||||
const effectiveZoomLevel =
|
const effectiveZoomLevel =
|
||||||
(zoomLevel / globalConfig.assetsDpi) * getDeviceDPI() * globalConfig.assetsSharpness;
|
(zoomLevel / globalConfig.assetsDpi) * getDeviceDPI() * globalConfig.assetsSharpness;
|
||||||
|
|
||||||
let desiredAtlasScale = "0.25";
|
let desiredAtlasScale = "0.25";
|
||||||
if (effectiveZoomLevel > 0.8 && !lowQuality) {
|
if (effectiveZoomLevel > 0.8 && !lowQuality) {
|
||||||
desiredAtlasScale = ORIGINAL_SPRITE_SCALE;
|
desiredAtlasScale = ORIGINAL_SPRITE_SCALE;
|
||||||
} else if (effectiveZoomLevel > 0.4 && !lowQuality) {
|
} else if (effectiveZoomLevel > 0.4 && !lowQuality) {
|
||||||
desiredAtlasScale = "0.5";
|
desiredAtlasScale = "0.5";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Construct parameters required for drawing
|
// Construct parameters required for drawing
|
||||||
const params = new DrawParameters({
|
const params = new DrawParameters({
|
||||||
context: context,
|
context: context,
|
||||||
visibleRect: root.camera.getVisibleRect(),
|
visibleRect: root.camera.getVisibleRect(),
|
||||||
desiredAtlasScale,
|
desiredAtlasScale,
|
||||||
zoomLevel,
|
zoomLevel,
|
||||||
root: root,
|
root: root,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (G_IS_DEV && globalConfig.debug.testCulling) {
|
if (G_IS_DEV && globalConfig.debug.testCulling) {
|
||||||
context.clearRect(0, 0, root.gameWidth, root.gameHeight);
|
context.clearRect(0, 0, root.gameWidth, root.gameHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transform to world space
|
// Transform to world space
|
||||||
|
|
||||||
if (G_IS_DEV && globalConfig.debug.testClipping) {
|
if (G_IS_DEV && globalConfig.debug.testClipping) {
|
||||||
params.visibleRect = params.visibleRect.expandedInAllDirections(
|
params.visibleRect = params.visibleRect.expandedInAllDirections(
|
||||||
-200 / this.root.camera.zoomLevel
|
-200 / this.root.camera.zoomLevel
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
root.camera.transform(context);
|
root.camera.transform(context);
|
||||||
|
|
||||||
assert(context.globalAlpha === 1.0, "Global alpha not 1 on frame start");
|
assert(context.globalAlpha === 1.0, "Global alpha not 1 on frame start");
|
||||||
|
|
||||||
// Update hud
|
// Update hud
|
||||||
root.hud.update();
|
root.hud.update();
|
||||||
|
|
||||||
// Main rendering order
|
// Main rendering order
|
||||||
// -----
|
// -----
|
||||||
|
|
||||||
if (this.root.camera.getIsMapOverlayActive()) {
|
if (this.root.camera.getIsMapOverlayActive()) {
|
||||||
// Map overview
|
// Map overview
|
||||||
root.map.drawOverlay(params);
|
root.map.drawOverlay(params);
|
||||||
} else {
|
} else {
|
||||||
// Background (grid, resources, etc)
|
// Background (grid, resources, etc)
|
||||||
root.map.drawBackground(params);
|
root.map.drawBackground(params);
|
||||||
|
|
||||||
// Belt items
|
// Belt items
|
||||||
systems.belt.drawBeltItems(params);
|
systems.belt.drawBeltItems(params);
|
||||||
|
|
||||||
// Miner & Static map entities etc.
|
// Miner & Static map entities etc.
|
||||||
root.map.drawForeground(params);
|
root.map.drawForeground(params);
|
||||||
|
|
||||||
// HUB Overlay
|
// HUB Overlay
|
||||||
systems.hub.draw(params);
|
systems.hub.draw(params);
|
||||||
|
|
||||||
// Green wires overlay
|
// Green wires overlay
|
||||||
root.hud.parts.wiresOverlay.draw(params);
|
root.hud.parts.wiresOverlay.draw(params);
|
||||||
|
|
||||||
if (this.root.currentLayer === "wires") {
|
if (this.root.currentLayer === "wires") {
|
||||||
// Static map entities
|
// Static map entities
|
||||||
root.map.drawWiresForegroundLayer(params);
|
root.map.drawWiresForegroundLayer(params);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (G_IS_DEV) {
|
if (G_IS_DEV) {
|
||||||
root.map.drawStaticEntityDebugOverlays(params);
|
root.map.drawStaticEntityDebugOverlays(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (G_IS_DEV && globalConfig.debug.renderBeltPaths) {
|
if (G_IS_DEV && globalConfig.debug.renderBeltPaths) {
|
||||||
systems.belt.drawBeltPathDebug(params);
|
systems.belt.drawBeltPathDebug(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
// END OF GAME CONTENT
|
// END OF GAME CONTENT
|
||||||
// -----
|
// -----
|
||||||
|
|
||||||
// Finally, draw the hud. Nothing should come after that
|
// Finally, draw the hud. Nothing should come after that
|
||||||
root.hud.draw(params);
|
root.hud.draw(params);
|
||||||
|
|
||||||
assert(context.globalAlpha === 1.0, "Global alpha not 1 on frame end before restore");
|
assert(context.globalAlpha === 1.0, "Global alpha not 1 on frame end before restore");
|
||||||
|
|
||||||
// Restore to screen space
|
// Restore to screen space
|
||||||
context.restore();
|
context.restore();
|
||||||
|
|
||||||
// Restore parameters
|
// Restore parameters
|
||||||
params.zoomLevel = 1;
|
params.zoomLevel = 1;
|
||||||
params.desiredAtlasScale = ORIGINAL_SPRITE_SCALE;
|
params.desiredAtlasScale = ORIGINAL_SPRITE_SCALE;
|
||||||
params.visibleRect = new Rectangle(0, 0, this.root.gameWidth, this.root.gameHeight);
|
params.visibleRect = new Rectangle(0, 0, this.root.gameWidth, this.root.gameHeight);
|
||||||
if (G_IS_DEV && globalConfig.debug.testClipping) {
|
if (G_IS_DEV && globalConfig.debug.testClipping) {
|
||||||
params.visibleRect = params.visibleRect.expandedInAllDirections(-200);
|
params.visibleRect = params.visibleRect.expandedInAllDirections(-200);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw overlays, those are screen space
|
// Draw overlays, those are screen space
|
||||||
root.hud.drawOverlays(params);
|
root.hud.drawOverlays(params);
|
||||||
|
|
||||||
assert(context.globalAlpha === 1.0, "context.globalAlpha not 1 on frame end");
|
assert(context.globalAlpha === 1.0, "context.globalAlpha not 1 on frame end");
|
||||||
|
|
||||||
if (G_IS_DEV && globalConfig.debug.simulateSlowRendering) {
|
if (G_IS_DEV && globalConfig.debug.simulateSlowRendering) {
|
||||||
let sum = 0;
|
let sum = 0;
|
||||||
for (let i = 0; i < 1e8; ++i) {
|
for (let i = 0; i < 1e8; ++i) {
|
||||||
sum += i;
|
sum += i;
|
||||||
}
|
}
|
||||||
if (Math.random() > 0.95) {
|
if (Math.random() > 0.95) {
|
||||||
console.log(sum);
|
console.log(sum);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (G_IS_DEV && globalConfig.debug.showAtlasInfo) {
|
if (G_IS_DEV && globalConfig.debug.showAtlasInfo) {
|
||||||
context.font = "13px GameFont";
|
context.font = "13px GameFont";
|
||||||
context.fillStyle = "blue";
|
context.fillStyle = "blue";
|
||||||
context.fillText(
|
context.fillText(
|
||||||
"Atlas: " +
|
"Atlas: " +
|
||||||
desiredAtlasScale +
|
desiredAtlasScale +
|
||||||
" / Zoom: " +
|
" / Zoom: " +
|
||||||
round2Digits(zoomLevel) +
|
round2Digits(zoomLevel) +
|
||||||
" / Effective Zoom: " +
|
" / Effective Zoom: " +
|
||||||
round2Digits(effectiveZoomLevel),
|
round2Digits(effectiveZoomLevel),
|
||||||
20,
|
20,
|
||||||
600
|
600
|
||||||
);
|
);
|
||||||
|
|
||||||
const stats = this.root.buffers.getStats();
|
const stats = this.root.buffers.getStats();
|
||||||
context.fillText(
|
context.fillText(
|
||||||
"Buffers: " +
|
"Buffers: " +
|
||||||
stats.rootKeys +
|
stats.rootKeys +
|
||||||
" root keys, " +
|
" root keys, " +
|
||||||
stats.subKeys +
|
stats.subKeys +
|
||||||
" sub keys / buffers / VRAM: " +
|
" sub keys / buffers / VRAM: " +
|
||||||
round2Digits(stats.vramBytes / (1024 * 1024)) +
|
round2Digits(stats.vramBytes / (1024 * 1024)) +
|
||||||
" MB",
|
" MB",
|
||||||
|
|
||||||
20,
|
20,
|
||||||
620
|
620
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (G_IS_DEV && globalConfig.debug.testClipping) {
|
if (G_IS_DEV && globalConfig.debug.testClipping) {
|
||||||
context.strokeStyle = "red";
|
context.strokeStyle = "red";
|
||||||
context.lineWidth = 1;
|
context.lineWidth = 1;
|
||||||
context.beginPath();
|
context.beginPath();
|
||||||
context.rect(200, 200, this.root.gameWidth - 400, this.root.gameHeight - 400);
|
context.rect(200, 200, this.root.gameWidth - 400, this.root.gameHeight - 400);
|
||||||
context.stroke();
|
context.stroke();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,308 +1,317 @@
|
|||||||
import { BaseHUDPart } from "../base_hud_part";
|
import { BaseHUDPart } from "../base_hud_part";
|
||||||
import { Vector } from "../../../core/vector";
|
import { Vector } from "../../../core/vector";
|
||||||
import { STOP_PROPAGATION } from "../../../core/signal";
|
import { STOP_PROPAGATION } from "../../../core/signal";
|
||||||
import { DrawParameters } from "../../../core/draw_parameters";
|
import { DrawParameters } from "../../../core/draw_parameters";
|
||||||
import { Entity } from "../../entity";
|
import { Entity } from "../../entity";
|
||||||
import { Loader } from "../../../core/loader";
|
import { Loader } from "../../../core/loader";
|
||||||
import { globalConfig } from "../../../core/config";
|
import { globalConfig } from "../../../core/config";
|
||||||
import { makeDiv, formatBigNumber, formatBigNumberFull } from "../../../core/utils";
|
import { makeDiv, formatBigNumber, formatBigNumberFull } from "../../../core/utils";
|
||||||
import { DynamicDomAttach } from "../dynamic_dom_attach";
|
import { DynamicDomAttach } from "../dynamic_dom_attach";
|
||||||
import { createLogger } from "../../../core/logging";
|
import { createLogger } from "../../../core/logging";
|
||||||
import { enumMouseButton } from "../../camera";
|
import { enumMouseButton } from "../../camera";
|
||||||
import { T } from "../../../translations";
|
import { T } from "../../../translations";
|
||||||
import { KEYMAPPINGS } from "../../key_action_mapper";
|
import { KEYMAPPINGS } from "../../key_action_mapper";
|
||||||
import { THEME } from "../../theme";
|
import { THEME } from "../../theme";
|
||||||
import { enumHubGoalRewards } from "../../tutorial_goals";
|
import { enumHubGoalRewards } from "../../tutorial_goals";
|
||||||
import { Blueprint } from "../../blueprint";
|
import { Blueprint } from "../../blueprint";
|
||||||
|
|
||||||
const logger = createLogger("hud/mass_selector");
|
const logger = createLogger("hud/mass_selector");
|
||||||
|
|
||||||
export class HUDMassSelector extends BaseHUDPart {
|
export class HUDMassSelector extends BaseHUDPart {
|
||||||
createElements(parent) {}
|
createElements(parent) {}
|
||||||
|
|
||||||
initialize() {
|
initialize() {
|
||||||
this.currentSelectionStartWorld = null;
|
this.currentSelectionStartWorld = null;
|
||||||
this.currentSelectionEnd = null;
|
this.currentSelectionEnd = null;
|
||||||
this.selectedUids = new Set();
|
this.selectedUids = new Set();
|
||||||
|
|
||||||
this.root.signals.entityQueuedForDestroy.add(this.onEntityDestroyed, this);
|
this.root.signals.entityQueuedForDestroy.add(this.onEntityDestroyed, this);
|
||||||
this.root.hud.signals.pasteBlueprintRequested.add(this.clearSelection, this);
|
this.root.hud.signals.pasteBlueprintRequested.add(this.clearSelection, this);
|
||||||
|
|
||||||
this.root.camera.downPreHandler.add(this.onMouseDown, this);
|
this.root.camera.downPreHandler.add(this.onMouseDown, this);
|
||||||
this.root.camera.movePreHandler.add(this.onMouseMove, this);
|
this.root.camera.movePreHandler.add(this.onMouseMove, this);
|
||||||
this.root.camera.upPostHandler.add(this.onMouseUp, this);
|
this.root.camera.upPostHandler.add(this.onMouseUp, this);
|
||||||
|
|
||||||
this.root.keyMapper.getBinding(KEYMAPPINGS.general.back).add(this.onBack, this);
|
this.root.keyMapper.getBinding(KEYMAPPINGS.general.back).add(this.onBack, this);
|
||||||
this.root.keyMapper
|
this.root.keyMapper
|
||||||
.getBinding(KEYMAPPINGS.massSelect.confirmMassDelete)
|
.getBinding(KEYMAPPINGS.massSelect.confirmMassDelete)
|
||||||
.add(this.confirmDelete, this);
|
.add(this.confirmDelete, this);
|
||||||
this.root.keyMapper.getBinding(KEYMAPPINGS.massSelect.massSelectCut).add(this.confirmCut, this);
|
this.root.keyMapper.getBinding(KEYMAPPINGS.massSelect.massSelectCut).add(this.confirmCut, this);
|
||||||
this.root.keyMapper.getBinding(KEYMAPPINGS.massSelect.massSelectCopy).add(this.startCopy, this);
|
this.root.keyMapper.getBinding(KEYMAPPINGS.massSelect.massSelectCopy).add(this.startCopy, this);
|
||||||
|
|
||||||
this.root.hud.signals.selectedPlacementBuildingChanged.add(this.clearSelection, this);
|
this.root.hud.signals.selectedPlacementBuildingChanged.add(this.clearSelection, this);
|
||||||
this.root.signals.editModeChanged.add(this.clearSelection, this);
|
this.root.signals.editModeChanged.add(this.clearSelection, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the destroy callback and makes sure we clean our list
|
* Handles the destroy callback and makes sure we clean our list
|
||||||
* @param {Entity} entity
|
* @param {Entity} entity
|
||||||
*/
|
*/
|
||||||
onEntityDestroyed(entity) {
|
onEntityDestroyed(entity) {
|
||||||
this.selectedUids.delete(entity.uid);
|
this.selectedUids.delete(entity.uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
onBack() {
|
onBack() {
|
||||||
// Clear entities on escape
|
// Clear entities on escape
|
||||||
if (this.selectedUids.size > 0) {
|
if (this.selectedUids.size > 0) {
|
||||||
this.selectedUids = new Set();
|
this.selectedUids = new Set();
|
||||||
return STOP_PROPAGATION;
|
return STOP_PROPAGATION;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clears the entire selection
|
* Clears the entire selection
|
||||||
*/
|
*/
|
||||||
clearSelection() {
|
clearSelection() {
|
||||||
this.selectedUids = new Set();
|
this.selectedUids = new Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
confirmDelete() {
|
confirmDelete() {
|
||||||
if (
|
if (
|
||||||
!this.root.app.settings.getAllSettings().disableCutDeleteWarnings &&
|
!this.root.app.settings.getAllSettings().disableCutDeleteWarnings &&
|
||||||
this.selectedUids.size > 100
|
this.selectedUids.size > 100
|
||||||
) {
|
) {
|
||||||
const { ok } = this.root.hud.parts.dialogs.showWarning(
|
const { ok } = this.root.hud.parts.dialogs.showWarning(
|
||||||
T.dialogs.massDeleteConfirm.title,
|
T.dialogs.massDeleteConfirm.title,
|
||||||
T.dialogs.massDeleteConfirm.desc.replace(
|
T.dialogs.massDeleteConfirm.desc.replace(
|
||||||
"<count>",
|
"<count>",
|
||||||
"" + formatBigNumberFull(this.selectedUids.size)
|
"" + formatBigNumberFull(this.selectedUids.size)
|
||||||
),
|
),
|
||||||
["cancel:good:escape", "ok:bad:enter"]
|
["cancel:good:escape", "ok:bad:enter"]
|
||||||
);
|
);
|
||||||
ok.add(() => this.doDelete());
|
ok.add(() => this.doDelete());
|
||||||
} else {
|
} else {
|
||||||
this.doDelete();
|
this.doDelete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
doDelete() {
|
doDelete() {
|
||||||
const entityUids = Array.from(this.selectedUids);
|
const entityUids = Array.from(this.selectedUids);
|
||||||
for (let i = 0; i < entityUids.length; ++i) {
|
for (let i = 0; i < entityUids.length; ++i) {
|
||||||
const uid = entityUids[i];
|
const uid = entityUids[i];
|
||||||
const entity = this.root.entityMgr.findByUid(uid);
|
const entity = this.root.entityMgr.findByUid(uid);
|
||||||
if (!this.root.logic.tryDeleteBuilding(entity)) {
|
if (!this.root.logic.tryDeleteBuilding(entity)) {
|
||||||
logger.error("Error in mass delete, could not remove building");
|
logger.error("Error in mass delete, could not remove building");
|
||||||
this.selectedUids.delete(uid);
|
this.selectedUids.delete(uid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
startCopy() {
|
startCopy() {
|
||||||
if (this.selectedUids.size > 0) {
|
if (this.selectedUids.size > 0) {
|
||||||
if (!this.root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_blueprints)) {
|
if (!this.root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_blueprints)) {
|
||||||
this.root.hud.parts.dialogs.showInfo(
|
this.root.hud.parts.dialogs.showInfo(
|
||||||
T.dialogs.blueprintsNotUnlocked.title,
|
T.dialogs.blueprintsNotUnlocked.title,
|
||||||
T.dialogs.blueprintsNotUnlocked.desc
|
T.dialogs.blueprintsNotUnlocked.desc
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.root.hud.signals.buildingsSelectedForCopy.dispatch(Array.from(this.selectedUids));
|
this.root.hud.signals.buildingsSelectedForCopy.dispatch(Array.from(this.selectedUids));
|
||||||
this.selectedUids = new Set();
|
this.selectedUids = new Set();
|
||||||
this.root.soundProxy.playUiClick();
|
this.root.soundProxy.playUiClick();
|
||||||
} else {
|
} else {
|
||||||
this.root.soundProxy.playUiError();
|
this.root.soundProxy.playUiError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
confirmCut() {
|
confirmCut() {
|
||||||
if (!this.root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_blueprints)) {
|
if (!this.root.hubGoals.isRewardUnlocked(enumHubGoalRewards.reward_blueprints)) {
|
||||||
this.root.hud.parts.dialogs.showInfo(
|
this.root.hud.parts.dialogs.showInfo(
|
||||||
T.dialogs.blueprintsNotUnlocked.title,
|
T.dialogs.blueprintsNotUnlocked.title,
|
||||||
T.dialogs.blueprintsNotUnlocked.desc
|
T.dialogs.blueprintsNotUnlocked.desc
|
||||||
);
|
);
|
||||||
} else if (
|
} else if (
|
||||||
!this.root.app.settings.getAllSettings().disableCutDeleteWarnings &&
|
!this.root.app.settings.getAllSettings().disableCutDeleteWarnings &&
|
||||||
this.selectedUids.size > 100
|
this.selectedUids.size > 100
|
||||||
) {
|
) {
|
||||||
const { ok } = this.root.hud.parts.dialogs.showWarning(
|
const { ok } = this.root.hud.parts.dialogs.showWarning(
|
||||||
T.dialogs.massCutConfirm.title,
|
T.dialogs.massCutConfirm.title,
|
||||||
T.dialogs.massCutConfirm.desc.replace(
|
T.dialogs.massCutConfirm.desc.replace(
|
||||||
"<count>",
|
"<count>",
|
||||||
"" + formatBigNumberFull(this.selectedUids.size)
|
"" + formatBigNumberFull(this.selectedUids.size)
|
||||||
),
|
),
|
||||||
["cancel:good:escape", "ok:bad:enter"]
|
["cancel:good:escape", "ok:bad:enter"]
|
||||||
);
|
);
|
||||||
ok.add(() => this.doCut());
|
ok.add(() => this.doCut());
|
||||||
} else {
|
} else {
|
||||||
this.doCut();
|
this.doCut();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
doCut() {
|
doCut() {
|
||||||
if (this.selectedUids.size > 0) {
|
if (this.selectedUids.size > 0) {
|
||||||
const entityUids = Array.from(this.selectedUids);
|
const entityUids = Array.from(this.selectedUids);
|
||||||
|
|
||||||
const cutAction = () => {
|
const cutAction = () => {
|
||||||
// copy code relies on entities still existing, so must copy before deleting.
|
// copy code relies on entities still existing, so must copy before deleting.
|
||||||
this.root.hud.signals.buildingsSelectedForCopy.dispatch(entityUids);
|
this.root.hud.signals.buildingsSelectedForCopy.dispatch(entityUids);
|
||||||
|
|
||||||
for (let i = 0; i < entityUids.length; ++i) {
|
for (let i = 0; i < entityUids.length; ++i) {
|
||||||
const uid = entityUids[i];
|
const uid = entityUids[i];
|
||||||
const entity = this.root.entityMgr.findByUid(uid);
|
const entity = this.root.entityMgr.findByUid(uid);
|
||||||
if (!this.root.logic.tryDeleteBuilding(entity)) {
|
if (!this.root.logic.tryDeleteBuilding(entity)) {
|
||||||
logger.error("Error in mass cut, could not remove building");
|
logger.error("Error in mass cut, could not remove building");
|
||||||
this.selectedUids.delete(uid);
|
this.selectedUids.delete(uid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const blueprint = Blueprint.fromUids(this.root, entityUids);
|
const blueprint = Blueprint.fromUids(this.root, entityUids);
|
||||||
if (blueprint.canAfford(this.root)) {
|
if (blueprint.canAfford(this.root)) {
|
||||||
cutAction();
|
cutAction();
|
||||||
} else {
|
} else {
|
||||||
const { cancel, ok } = this.root.hud.parts.dialogs.showWarning(
|
const { cancel, ok } = this.root.hud.parts.dialogs.showWarning(
|
||||||
T.dialogs.massCutInsufficientConfirm.title,
|
T.dialogs.massCutInsufficientConfirm.title,
|
||||||
T.dialogs.massCutInsufficientConfirm.desc,
|
T.dialogs.massCutInsufficientConfirm.desc,
|
||||||
["cancel:good:escape", "ok:bad:enter"]
|
["cancel:good:escape", "ok:bad:enter"]
|
||||||
);
|
);
|
||||||
ok.add(cutAction);
|
ok.add(cutAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.root.soundProxy.playUiClick();
|
this.root.soundProxy.playUiClick();
|
||||||
} else {
|
} else {
|
||||||
this.root.soundProxy.playUiError();
|
this.root.soundProxy.playUiError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* mouse down pre handler
|
* mouse down pre handler
|
||||||
* @param {Vector} pos
|
* @param {Vector} pos
|
||||||
* @param {enumMouseButton} mouseButton
|
* @param {enumMouseButton} mouseButton
|
||||||
*/
|
*/
|
||||||
onMouseDown(pos, mouseButton) {
|
onMouseDown(pos, mouseButton) {
|
||||||
if (!this.root.keyMapper.getBinding(KEYMAPPINGS.massSelect.massSelectStart).pressed) {
|
if (!this.root.keyMapper.getBinding(KEYMAPPINGS.massSelect.massSelectStart).pressed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mouseButton !== enumMouseButton.left) {
|
if (mouseButton !== enumMouseButton.left) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.root.keyMapper.getBinding(KEYMAPPINGS.massSelect.massSelectSelectMultiple).pressed) {
|
if (!this.root.keyMapper.getBinding(KEYMAPPINGS.massSelect.massSelectSelectMultiple).pressed) {
|
||||||
// Start new selection
|
// Start new selection
|
||||||
this.selectedUids = new Set();
|
this.selectedUids = new Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.currentSelectionStartWorld = this.root.camera.screenToWorld(pos.copy());
|
this.currentSelectionStartWorld = this.root.camera.screenToWorld(pos.copy());
|
||||||
this.currentSelectionEnd = pos.copy();
|
this.currentSelectionEnd = pos.copy();
|
||||||
return STOP_PROPAGATION;
|
return STOP_PROPAGATION;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* mouse move pre handler
|
* mouse move pre handler
|
||||||
* @param {Vector} pos
|
* @param {Vector} pos
|
||||||
*/
|
*/
|
||||||
onMouseMove(pos) {
|
onMouseMove(pos) {
|
||||||
if (this.currentSelectionStartWorld) {
|
if (this.currentSelectionStartWorld) {
|
||||||
this.currentSelectionEnd = pos.copy();
|
this.currentSelectionEnd = pos.copy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMouseUp() {
|
onMouseUp() {
|
||||||
if (this.currentSelectionStartWorld) {
|
if (this.currentSelectionStartWorld) {
|
||||||
const worldStart = this.currentSelectionStartWorld;
|
const worldStart = this.currentSelectionStartWorld;
|
||||||
const worldEnd = this.root.camera.screenToWorld(this.currentSelectionEnd);
|
const worldEnd = this.root.camera.screenToWorld(this.currentSelectionEnd);
|
||||||
|
|
||||||
const tileStart = worldStart.toTileSpace();
|
const tileStart = worldStart.toTileSpace();
|
||||||
const tileEnd = worldEnd.toTileSpace();
|
const tileEnd = worldEnd.toTileSpace();
|
||||||
|
|
||||||
const realTileStart = tileStart.min(tileEnd);
|
const realTileStart = tileStart.min(tileEnd);
|
||||||
const realTileEnd = tileStart.max(tileEnd);
|
const realTileEnd = tileStart.max(tileEnd);
|
||||||
|
|
||||||
for (let x = realTileStart.x; x <= realTileEnd.x; ++x) {
|
for (let x = realTileStart.x; x <= realTileEnd.x; ++x) {
|
||||||
for (let y = realTileStart.y; y <= realTileEnd.y; ++y) {
|
for (let y = realTileStart.y; y <= realTileEnd.y; ++y) {
|
||||||
const contents = this.root.map.getLayerContentXY(x, y, this.root.currentLayer);
|
const contents = this.root.map.getLayerContentXY(x, y, this.root.currentLayer);
|
||||||
if (contents && this.root.logic.canDeleteBuilding(contents)) {
|
if (contents && this.root.logic.canDeleteBuilding(contents)) {
|
||||||
this.selectedUids.add(contents.uid);
|
this.selectedUids.add(contents.uid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.currentSelectionStartWorld = null;
|
this.currentSelectionStartWorld = null;
|
||||||
this.currentSelectionEnd = null;
|
this.currentSelectionEnd = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {DrawParameters} parameters
|
* @param {DrawParameters} parameters
|
||||||
*/
|
*/
|
||||||
draw(parameters) {
|
draw(parameters) {
|
||||||
const boundsBorder = 2;
|
const boundsBorder = 2;
|
||||||
|
|
||||||
if (this.currentSelectionStartWorld) {
|
if (this.currentSelectionStartWorld) {
|
||||||
const worldStart = this.currentSelectionStartWorld;
|
const worldStart = this.currentSelectionStartWorld;
|
||||||
const worldEnd = this.root.camera.screenToWorld(this.currentSelectionEnd);
|
const worldEnd = this.root.camera.screenToWorld(this.currentSelectionEnd);
|
||||||
|
|
||||||
const realWorldStart = worldStart.min(worldEnd);
|
const realWorldStart = worldStart.min(worldEnd);
|
||||||
const realWorldEnd = worldStart.max(worldEnd);
|
const realWorldEnd = worldStart.max(worldEnd);
|
||||||
|
|
||||||
const tileStart = worldStart.toTileSpace();
|
const tileStart = worldStart.toTileSpace();
|
||||||
const tileEnd = worldEnd.toTileSpace();
|
const tileEnd = worldEnd.toTileSpace();
|
||||||
|
|
||||||
const realTileStart = tileStart.min(tileEnd);
|
const realTileStart = tileStart.min(tileEnd);
|
||||||
const realTileEnd = tileStart.max(tileEnd);
|
const realTileEnd = tileStart.max(tileEnd);
|
||||||
|
|
||||||
parameters.context.lineWidth = 1;
|
parameters.context.lineWidth = 1;
|
||||||
parameters.context.fillStyle = THEME.map.selectionBackground;
|
parameters.context.fillStyle = THEME.map.selectionBackground;
|
||||||
parameters.context.strokeStyle = THEME.map.selectionOutline;
|
parameters.context.strokeStyle = THEME.map.selectionOutline;
|
||||||
parameters.context.beginPath();
|
parameters.context.beginPath();
|
||||||
parameters.context.rect(
|
parameters.context.rect(
|
||||||
realWorldStart.x,
|
realWorldStart.x,
|
||||||
realWorldStart.y,
|
realWorldStart.y,
|
||||||
realWorldEnd.x - realWorldStart.x,
|
realWorldEnd.x - realWorldStart.x,
|
||||||
realWorldEnd.y - realWorldStart.y
|
realWorldEnd.y - realWorldStart.y
|
||||||
);
|
);
|
||||||
parameters.context.fill();
|
parameters.context.fill();
|
||||||
parameters.context.stroke();
|
parameters.context.stroke();
|
||||||
|
|
||||||
parameters.context.fillStyle = THEME.map.selectionOverlay;
|
parameters.context.fillStyle = THEME.map.selectionOverlay;
|
||||||
|
|
||||||
for (let x = realTileStart.x; x <= realTileEnd.x; ++x) {
|
const renderedUids = new Set();
|
||||||
for (let y = realTileStart.y; y <= realTileEnd.y; ++y) {
|
|
||||||
const contents = this.root.map.getLayerContentXY(x, y, this.root.currentLayer);
|
for (let x = realTileStart.x; x <= realTileEnd.x; ++x) {
|
||||||
if (contents && this.root.logic.canDeleteBuilding(contents)) {
|
for (let y = realTileStart.y; y <= realTileEnd.y; ++y) {
|
||||||
const staticComp = contents.components.StaticMapEntity;
|
const contents = this.root.map.getLayerContentXY(x, y, this.root.currentLayer);
|
||||||
const bounds = staticComp.getTileSpaceBounds();
|
if (contents && this.root.logic.canDeleteBuilding(contents)) {
|
||||||
parameters.context.beginRoundedRect(
|
// Prevent rendering the overlay twice
|
||||||
bounds.x * globalConfig.tileSize + boundsBorder,
|
const uid = contents.uid;
|
||||||
bounds.y * globalConfig.tileSize + boundsBorder,
|
if (renderedUids.has(uid)) {
|
||||||
bounds.w * globalConfig.tileSize - 2 * boundsBorder,
|
continue;
|
||||||
bounds.h * globalConfig.tileSize - 2 * boundsBorder,
|
}
|
||||||
2
|
renderedUids.add(uid);
|
||||||
);
|
|
||||||
parameters.context.fill();
|
const staticComp = contents.components.StaticMapEntity;
|
||||||
}
|
const bounds = staticComp.getTileSpaceBounds();
|
||||||
}
|
parameters.context.beginRoundedRect(
|
||||||
}
|
bounds.x * globalConfig.tileSize + boundsBorder,
|
||||||
}
|
bounds.y * globalConfig.tileSize + boundsBorder,
|
||||||
|
bounds.w * globalConfig.tileSize - 2 * boundsBorder,
|
||||||
parameters.context.fillStyle = THEME.map.selectionOverlay;
|
bounds.h * globalConfig.tileSize - 2 * boundsBorder,
|
||||||
this.selectedUids.forEach(uid => {
|
2
|
||||||
const entity = this.root.entityMgr.findByUid(uid);
|
);
|
||||||
const staticComp = entity.components.StaticMapEntity;
|
parameters.context.fill();
|
||||||
const bounds = staticComp.getTileSpaceBounds();
|
}
|
||||||
parameters.context.beginRoundedRect(
|
}
|
||||||
bounds.x * globalConfig.tileSize + boundsBorder,
|
}
|
||||||
bounds.y * globalConfig.tileSize + boundsBorder,
|
}
|
||||||
bounds.w * globalConfig.tileSize - 2 * boundsBorder,
|
|
||||||
bounds.h * globalConfig.tileSize - 2 * boundsBorder,
|
parameters.context.fillStyle = THEME.map.selectionOverlay;
|
||||||
2
|
this.selectedUids.forEach(uid => {
|
||||||
);
|
const entity = this.root.entityMgr.findByUid(uid);
|
||||||
parameters.context.fill();
|
const staticComp = entity.components.StaticMapEntity;
|
||||||
});
|
const bounds = staticComp.getTileSpaceBounds();
|
||||||
}
|
parameters.context.beginRoundedRect(
|
||||||
}
|
bounds.x * globalConfig.tileSize + boundsBorder,
|
||||||
|
bounds.y * globalConfig.tileSize + boundsBorder,
|
||||||
|
bounds.w * globalConfig.tileSize - 2 * boundsBorder,
|
||||||
|
bounds.h * globalConfig.tileSize - 2 * boundsBorder,
|
||||||
|
2
|
||||||
|
);
|
||||||
|
parameters.context.fill();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,133 +1,133 @@
|
|||||||
import { InputReceiver } from "../../../core/input_receiver";
|
import { InputReceiver } from "../../../core/input_receiver";
|
||||||
import { makeDiv, removeAllChildren } from "../../../core/utils";
|
import { makeDiv, removeAllChildren } from "../../../core/utils";
|
||||||
import { T } from "../../../translations";
|
import { T } from "../../../translations";
|
||||||
import { KeyActionMapper, KEYMAPPINGS } from "../../key_action_mapper";
|
import { KeyActionMapper, KEYMAPPINGS } from "../../key_action_mapper";
|
||||||
import { ShapeDefinition } from "../../shape_definition";
|
import { ShapeDefinition } from "../../shape_definition";
|
||||||
import { BaseHUDPart } from "../base_hud_part";
|
import { BaseHUDPart } from "../base_hud_part";
|
||||||
import { DynamicDomAttach } from "../dynamic_dom_attach";
|
import { DynamicDomAttach } from "../dynamic_dom_attach";
|
||||||
|
|
||||||
const copy = require("clipboard-copy");
|
const copy = require("clipboard-copy");
|
||||||
|
|
||||||
export class HUDShapeViewer extends BaseHUDPart {
|
export class HUDShapeViewer extends BaseHUDPart {
|
||||||
createElements(parent) {
|
createElements(parent) {
|
||||||
this.background = makeDiv(parent, "ingame_HUD_ShapeViewer", ["ingameDialog"]);
|
this.background = makeDiv(parent, "ingame_HUD_ShapeViewer", ["ingameDialog"]);
|
||||||
|
|
||||||
// DIALOG Inner / Wrapper
|
// DIALOG Inner / Wrapper
|
||||||
this.dialogInner = makeDiv(this.background, null, ["dialogInner"]);
|
this.dialogInner = makeDiv(this.background, null, ["dialogInner"]);
|
||||||
this.title = makeDiv(this.dialogInner, null, ["title"], T.ingame.shapeViewer.title);
|
this.title = makeDiv(this.dialogInner, null, ["title"], T.ingame.shapeViewer.title);
|
||||||
this.closeButton = makeDiv(this.title, null, ["closeButton"]);
|
this.closeButton = makeDiv(this.title, null, ["closeButton"]);
|
||||||
this.trackClicks(this.closeButton, this.close);
|
this.trackClicks(this.closeButton, this.close);
|
||||||
this.contentDiv = makeDiv(this.dialogInner, null, ["content"]);
|
this.contentDiv = makeDiv(this.dialogInner, null, ["content"]);
|
||||||
|
|
||||||
this.renderArea = makeDiv(this.contentDiv, null, ["renderArea"]);
|
this.renderArea = makeDiv(this.contentDiv, null, ["renderArea"]);
|
||||||
this.infoArea = makeDiv(this.contentDiv, null, ["infoArea"]);
|
this.infoArea = makeDiv(this.contentDiv, null, ["infoArea"]);
|
||||||
|
|
||||||
// Create button to copy the shape area
|
// Create button to copy the shape area
|
||||||
this.copyButton = document.createElement("button");
|
this.copyButton = document.createElement("button");
|
||||||
this.copyButton.classList.add("styledButton", "copyKey");
|
this.copyButton.classList.add("styledButton", "copyKey");
|
||||||
this.copyButton.innerText = T.ingame.shapeViewer.copyKey;
|
this.copyButton.innerText = T.ingame.shapeViewer.copyKey;
|
||||||
this.infoArea.appendChild(this.copyButton);
|
this.infoArea.appendChild(this.copyButton);
|
||||||
}
|
}
|
||||||
|
|
||||||
initialize() {
|
initialize() {
|
||||||
this.root.hud.signals.viewShapeDetailsRequested.add(this.renderForShape, this);
|
this.root.hud.signals.viewShapeDetailsRequested.add(this.renderForShape, this);
|
||||||
|
|
||||||
this.domAttach = new DynamicDomAttach(this.root, this.background, {
|
this.domAttach = new DynamicDomAttach(this.root, this.background, {
|
||||||
attachClass: "visible",
|
attachClass: "visible",
|
||||||
});
|
});
|
||||||
|
|
||||||
this.currentShapeKey = null;
|
this.currentShapeKey = null;
|
||||||
|
|
||||||
this.inputReciever = new InputReceiver("shape_viewer");
|
this.inputReciever = new InputReceiver("shape_viewer");
|
||||||
this.keyActionMapper = new KeyActionMapper(this.root, this.inputReciever);
|
this.keyActionMapper = new KeyActionMapper(this.root, this.inputReciever);
|
||||||
|
|
||||||
this.keyActionMapper.getBinding(KEYMAPPINGS.general.back).add(this.close, this);
|
this.keyActionMapper.getBinding(KEYMAPPINGS.general.back).add(this.close, this);
|
||||||
|
|
||||||
this.trackClicks(this.copyButton, this.onCopyKeyRequested);
|
this.trackClicks(this.copyButton, this.onCopyKeyRequested);
|
||||||
|
|
||||||
this.close();
|
this.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the copying of a key was requested
|
* Called when the copying of a key was requested
|
||||||
*/
|
*/
|
||||||
onCopyKeyRequested() {
|
onCopyKeyRequested() {
|
||||||
if (this.currentShapeKey) {
|
if (this.currentShapeKey) {
|
||||||
copy(this.currentShapeKey);
|
copy(this.currentShapeKey);
|
||||||
this.close();
|
this.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes the dialog
|
* Closes the dialog
|
||||||
*/
|
*/
|
||||||
close() {
|
close() {
|
||||||
this.visible = false;
|
this.visible = false;
|
||||||
document.body.classList.remove("ingameDialogOpen");
|
document.body.classList.remove("ingameDialogOpen");
|
||||||
this.root.app.inputMgr.makeSureDetached(this.inputReciever);
|
this.root.app.inputMgr.makeSureDetached(this.inputReciever);
|
||||||
this.update();
|
this.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows the viewer for a given definition
|
* Shows the viewer for a given definition
|
||||||
* @param {ShapeDefinition} definition
|
* @param {ShapeDefinition} definition
|
||||||
*/
|
*/
|
||||||
renderForShape(definition) {
|
renderForShape(definition) {
|
||||||
this.visible = true;
|
this.visible = true;
|
||||||
document.body.classList.add("ingameDialogOpen");
|
document.body.classList.add("ingameDialogOpen");
|
||||||
this.root.app.inputMgr.makeSureAttachedAndOnTop(this.inputReciever);
|
this.root.app.inputMgr.makeSureAttachedAndOnTop(this.inputReciever);
|
||||||
|
|
||||||
removeAllChildren(this.renderArea);
|
removeAllChildren(this.renderArea);
|
||||||
|
|
||||||
this.currentShapeKey = definition.getHash();
|
this.currentShapeKey = definition.getHash();
|
||||||
|
|
||||||
const layers = definition.layers;
|
const layers = definition.layers;
|
||||||
this.contentDiv.setAttribute("data-layers", layers.length);
|
this.contentDiv.setAttribute("data-layers", layers.length);
|
||||||
|
|
||||||
for (let i = 0; i < layers.length; ++i) {
|
for (let i = layers.length - 1; i >= 0; --i) {
|
||||||
const layerElem = makeDiv(this.renderArea, null, ["layer", "layer-" + i]);
|
const layerElem = makeDiv(this.renderArea, null, ["layer", "layer-" + i]);
|
||||||
|
|
||||||
let fakeLayers = [];
|
let fakeLayers = [];
|
||||||
for (let k = 0; k < i; ++k) {
|
for (let k = 0; k < i; ++k) {
|
||||||
fakeLayers.push([null, null, null, null]);
|
fakeLayers.push([null, null, null, null]);
|
||||||
}
|
}
|
||||||
fakeLayers.push(layers[i]);
|
fakeLayers.push(layers[i]);
|
||||||
|
|
||||||
const thisLayerOnly = new ShapeDefinition({ layers: fakeLayers });
|
const thisLayerOnly = new ShapeDefinition({ layers: fakeLayers });
|
||||||
const thisLayerCanvas = thisLayerOnly.generateAsCanvas(160);
|
const thisLayerCanvas = thisLayerOnly.generateAsCanvas(160);
|
||||||
layerElem.appendChild(thisLayerCanvas);
|
layerElem.appendChild(thisLayerCanvas);
|
||||||
|
|
||||||
for (let quad = 0; quad < 4; ++quad) {
|
for (let quad = 0; quad < 4; ++quad) {
|
||||||
const quadElem = makeDiv(layerElem, null, ["quad", "quad-" + quad]);
|
const quadElem = makeDiv(layerElem, null, ["quad", "quad-" + quad]);
|
||||||
|
|
||||||
const contents = layers[i][quad];
|
const contents = layers[i][quad];
|
||||||
if (contents) {
|
if (contents) {
|
||||||
const colorLabelElem = makeDiv(
|
const colorLabelElem = makeDiv(
|
||||||
quadElem,
|
quadElem,
|
||||||
null,
|
null,
|
||||||
["colorLabel"],
|
["colorLabel"],
|
||||||
T.ingame.colors[contents.color]
|
T.ingame.colors[contents.color]
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
const emptyLabelElem = makeDiv(
|
const emptyLabelElem = makeDiv(
|
||||||
quadElem,
|
quadElem,
|
||||||
null,
|
null,
|
||||||
["emptyLabel"],
|
["emptyLabel"],
|
||||||
T.ingame.shapeViewer.empty
|
T.ingame.shapeViewer.empty
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cleans up everything
|
* Cleans up everything
|
||||||
*/
|
*/
|
||||||
cleanup() {
|
cleanup() {
|
||||||
document.body.classList.remove("ingameDialogOpen");
|
document.body.classList.remove("ingameDialogOpen");
|
||||||
}
|
}
|
||||||
|
|
||||||
update() {
|
update() {
|
||||||
this.domAttach.update(this.visible);
|
this.domAttach.update(this.visible);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,9 +47,11 @@ export class HUDStatistics extends BaseHUDPart {
|
|||||||
this.trackClicks(button, () => this.setDataSource(dataSource));
|
this.trackClicks(button, () => this.setDataSource(dataSource));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const buttonDisplaySorted = makeButton(this.filtersDisplayMode, ["displaySorted"]);
|
||||||
const buttonDisplayDetailed = makeButton(this.filtersDisplayMode, ["displayDetailed"]);
|
const buttonDisplayDetailed = makeButton(this.filtersDisplayMode, ["displayDetailed"]);
|
||||||
const buttonDisplayIcons = makeButton(this.filtersDisplayMode, ["displayIcons"]);
|
const buttonDisplayIcons = makeButton(this.filtersDisplayMode, ["displayIcons"]);
|
||||||
|
|
||||||
|
this.trackClicks(buttonDisplaySorted, () => this.toggleSorted());
|
||||||
this.trackClicks(buttonDisplayIcons, () => this.setDisplayMode(enumDisplayMode.icons));
|
this.trackClicks(buttonDisplayIcons, () => this.setDisplayMode(enumDisplayMode.icons));
|
||||||
this.trackClicks(buttonDisplayDetailed, () => this.setDisplayMode(enumDisplayMode.detailed));
|
this.trackClicks(buttonDisplayDetailed, () => this.setDisplayMode(enumDisplayMode.detailed));
|
||||||
|
|
||||||
@ -80,6 +82,21 @@ export class HUDStatistics extends BaseHUDPart {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {boolean} sorted
|
||||||
|
*/
|
||||||
|
setSorted(sorted) {
|
||||||
|
this.sorted = sorted;
|
||||||
|
this.dialogInner.setAttribute("data-sorted", String(sorted));
|
||||||
|
if (this.visible) {
|
||||||
|
this.rerenderFull();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleSorted() {
|
||||||
|
this.setSorted(!this.sorted);
|
||||||
|
}
|
||||||
|
|
||||||
initialize() {
|
initialize() {
|
||||||
this.domAttach = new DynamicDomAttach(this.root, this.background, {
|
this.domAttach = new DynamicDomAttach(this.root, this.background, {
|
||||||
attachClass: "visible",
|
attachClass: "visible",
|
||||||
@ -95,6 +112,7 @@ export class HUDStatistics extends BaseHUDPart {
|
|||||||
/** @type {Object.<string, HUDShapeStatisticsHandle>} */
|
/** @type {Object.<string, HUDShapeStatisticsHandle>} */
|
||||||
this.activeHandles = {};
|
this.activeHandles = {};
|
||||||
|
|
||||||
|
this.setSorted(true);
|
||||||
this.setDataSource(enumAnalyticsDataSource.produced);
|
this.setDataSource(enumAnalyticsDataSource.produced);
|
||||||
this.setDisplayMode(enumDisplayMode.detailed);
|
this.setDisplayMode(enumDisplayMode.detailed);
|
||||||
|
|
||||||
@ -183,7 +201,22 @@ export class HUDStatistics extends BaseHUDPart {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
entries.sort((a, b) => b[1] - a[1]);
|
const pinnedShapes = this.root.hud.parts.pinnedShapes;
|
||||||
|
|
||||||
|
entries.sort((a, b) => {
|
||||||
|
const aPinned = pinnedShapes.isShapePinned(a[0]);
|
||||||
|
const bPinned = pinnedShapes.isShapePinned(b[0]);
|
||||||
|
|
||||||
|
if (aPinned !== bPinned) {
|
||||||
|
return aPinned ? -1 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort by shape key for some consistency
|
||||||
|
if (!this.sorted || b[1] == a[1]) {
|
||||||
|
return b[0].localeCompare(a[0]);
|
||||||
|
}
|
||||||
|
return b[1] - a[1];
|
||||||
|
});
|
||||||
|
|
||||||
let rendered = new Set();
|
let rendered = new Set();
|
||||||
|
|
||||||
|
@ -74,6 +74,11 @@ export class HUDShapeStatisticsHandle {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.element.classList.toggle(
|
||||||
|
"pinned",
|
||||||
|
this.root.hud.parts.pinnedShapes.isShapePinned(this.definition.getHash())
|
||||||
|
);
|
||||||
|
|
||||||
switch (dataSource) {
|
switch (dataSource) {
|
||||||
case enumAnalyticsDataSource.stored: {
|
case enumAnalyticsDataSource.stored: {
|
||||||
this.counter.innerText = formatBigNumber(
|
this.counter.innerText = formatBigNumber(
|
||||||
@ -87,15 +92,10 @@ export class HUDShapeStatisticsHandle {
|
|||||||
(this.root.productionAnalytics.getCurrentShapeRate(dataSource, this.definition) /
|
(this.root.productionAnalytics.getCurrentShapeRate(dataSource, this.definition) /
|
||||||
globalConfig.analyticsSliceDurationSeconds) *
|
globalConfig.analyticsSliceDurationSeconds) *
|
||||||
60;
|
60;
|
||||||
this.counter.innerText = T.ingame.statistics.shapesPerMinute.replace(
|
this.counter.innerText = T.ingame.statistics.shapesPerSecond.replace(
|
||||||
"<shapes>",
|
"<shapes>",
|
||||||
formatBigNumber(rate)
|
formatBigNumber(rate / 60)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (G_IS_DEV && globalConfig.debug.detailedStatistics) {
|
|
||||||
this.counter.innerText = "" + round2Digits(rate / 60) + " /s";
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,27 @@
|
|||||||
import { HUDBaseToolbar } from "./base_toolbar";
|
import { HUDBaseToolbar } from "./base_toolbar";
|
||||||
import { MetaWireBuilding } from "../../buildings/wire";
|
import { MetaWireBuilding } from "../../buildings/wire";
|
||||||
import { MetaConstantSignalBuilding } from "../../buildings/constant_signal";
|
import { MetaConstantSignalBuilding } from "../../buildings/constant_signal";
|
||||||
import { MetaLogicGateBuilding } from "../../buildings/logic_gate";
|
import { MetaLogicGateBuilding } from "../../buildings/logic_gate";
|
||||||
import { MetaLeverBuilding } from "../../buildings/lever";
|
import { MetaLeverBuilding } from "../../buildings/lever";
|
||||||
import { MetaWireTunnelBuilding } from "../../buildings/wire_tunnel";
|
import { MetaWireTunnelBuilding } from "../../buildings/wire_tunnel";
|
||||||
|
import { MetaVirtualProcessorBuilding } from "../../buildings/virtual_processor";
|
||||||
const supportedBuildings = [
|
|
||||||
MetaWireBuilding,
|
const supportedBuildings = [
|
||||||
MetaWireTunnelBuilding,
|
MetaWireBuilding,
|
||||||
MetaConstantSignalBuilding,
|
MetaWireTunnelBuilding,
|
||||||
MetaLogicGateBuilding,
|
MetaConstantSignalBuilding,
|
||||||
MetaLeverBuilding,
|
MetaLogicGateBuilding,
|
||||||
];
|
MetaLeverBuilding,
|
||||||
|
MetaVirtualProcessorBuilding,
|
||||||
export class HUDWiresToolbar extends HUDBaseToolbar {
|
];
|
||||||
constructor(root) {
|
|
||||||
super(root, {
|
export class HUDWiresToolbar extends HUDBaseToolbar {
|
||||||
supportedBuildings,
|
constructor(root) {
|
||||||
visibilityCondition: () =>
|
super(root, {
|
||||||
!this.root.camera.getIsMapOverlayActive() && this.root.currentLayer === "wires",
|
supportedBuildings,
|
||||||
htmlElementId: "ingame_HUD_wires_toolbar",
|
visibilityCondition: () =>
|
||||||
});
|
!this.root.camera.getIsMapOverlayActive() && this.root.currentLayer === "wires",
|
||||||
}
|
htmlElementId: "ingame_HUD_wires_toolbar",
|
||||||
}
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,456 +1,457 @@
|
|||||||
/* typehints:start */
|
/* typehints:start */
|
||||||
import { GameRoot } from "./root";
|
import { GameRoot } from "./root";
|
||||||
import { InputReceiver } from "../core/input_receiver";
|
import { InputReceiver } from "../core/input_receiver";
|
||||||
import { Application } from "../application";
|
import { Application } from "../application";
|
||||||
/* typehints:end */
|
/* typehints:end */
|
||||||
|
|
||||||
import { Signal, STOP_PROPAGATION } from "../core/signal";
|
import { Signal, STOP_PROPAGATION } from "../core/signal";
|
||||||
import { IS_MOBILE } from "../core/config";
|
import { IS_MOBILE } from "../core/config";
|
||||||
import { T } from "../translations";
|
import { T } from "../translations";
|
||||||
function key(str) {
|
function key(str) {
|
||||||
return str.toUpperCase().charCodeAt(0);
|
return str.toUpperCase().charCodeAt(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const KEYMAPPINGS = {
|
export const KEYMAPPINGS = {
|
||||||
general: {
|
general: {
|
||||||
confirm: { keyCode: 13 }, // enter
|
confirm: { keyCode: 13 }, // enter
|
||||||
back: { keyCode: 27, builtin: true }, // escape
|
back: { keyCode: 27, builtin: true }, // escape
|
||||||
},
|
},
|
||||||
|
|
||||||
ingame: {
|
ingame: {
|
||||||
menuOpenShop: { keyCode: key("F") },
|
menuOpenShop: { keyCode: key("F") },
|
||||||
menuOpenStats: { keyCode: key("G") },
|
menuOpenStats: { keyCode: key("G") },
|
||||||
menuClose: { keyCode: key("Q") },
|
menuClose: { keyCode: key("Q") },
|
||||||
|
|
||||||
toggleHud: { keyCode: 113 }, // F2
|
toggleHud: { keyCode: 113 }, // F2
|
||||||
exportScreenshot: { keyCode: 114 }, // F3PS
|
exportScreenshot: { keyCode: 114 }, // F3PS
|
||||||
toggleFPSInfo: { keyCode: 115 }, // F4
|
toggleFPSInfo: { keyCode: 115 }, // F4
|
||||||
|
|
||||||
switchLayers: { keyCode: key("Y") },
|
switchLayers: { keyCode: key("Y") },
|
||||||
},
|
},
|
||||||
|
|
||||||
navigation: {
|
navigation: {
|
||||||
mapMoveUp: { keyCode: key("W") },
|
mapMoveUp: { keyCode: key("W") },
|
||||||
mapMoveRight: { keyCode: key("D") },
|
mapMoveRight: { keyCode: key("D") },
|
||||||
mapMoveDown: { keyCode: key("S") },
|
mapMoveDown: { keyCode: key("S") },
|
||||||
mapMoveLeft: { keyCode: key("A") },
|
mapMoveLeft: { keyCode: key("A") },
|
||||||
mapMoveFaster: { keyCode: 16 }, //shift
|
mapMoveFaster: { keyCode: 16 }, //shift
|
||||||
|
|
||||||
centerMap: { keyCode: 32 }, // SPACE
|
centerMap: { keyCode: 32 }, // SPACE
|
||||||
mapZoomIn: { keyCode: 187, repeated: true }, // "+"
|
mapZoomIn: { keyCode: 187, repeated: true }, // "+"
|
||||||
mapZoomOut: { keyCode: 189, repeated: true }, // "-"
|
mapZoomOut: { keyCode: 189, repeated: true }, // "-"
|
||||||
|
|
||||||
createMarker: { keyCode: key("M") },
|
createMarker: { keyCode: key("M") },
|
||||||
},
|
},
|
||||||
|
|
||||||
buildings: {
|
buildings: {
|
||||||
belt: { keyCode: key("1") },
|
belt: { keyCode: key("1") },
|
||||||
splitter: { keyCode: key("2") },
|
splitter: { keyCode: key("2") },
|
||||||
underground_belt: { keyCode: key("3") },
|
underground_belt: { keyCode: key("3") },
|
||||||
miner: { keyCode: key("4") },
|
miner: { keyCode: key("4") },
|
||||||
cutter: { keyCode: key("5") },
|
cutter: { keyCode: key("5") },
|
||||||
rotater: { keyCode: key("6") },
|
rotater: { keyCode: key("6") },
|
||||||
stacker: { keyCode: key("7") },
|
stacker: { keyCode: key("7") },
|
||||||
mixer: { keyCode: key("8") },
|
mixer: { keyCode: key("8") },
|
||||||
painter: { keyCode: key("9") },
|
painter: { keyCode: key("9") },
|
||||||
trash: { keyCode: key("0") },
|
trash: { keyCode: key("0") },
|
||||||
|
|
||||||
lever: { keyCode: key("L") },
|
lever: { keyCode: key("L") },
|
||||||
filter: { keyCode: key("B") },
|
filter: { keyCode: key("B") },
|
||||||
display: { keyCode: key("N") },
|
display: { keyCode: key("N") },
|
||||||
|
|
||||||
wire: { keyCode: key("1") },
|
wire: { keyCode: key("1") },
|
||||||
wire_tunnel: { keyCode: key("2") },
|
wire_tunnel: { keyCode: key("2") },
|
||||||
constant_signal: { keyCode: key("3") },
|
constant_signal: { keyCode: key("3") },
|
||||||
logic_gate: { keyCode: key("4") },
|
logic_gate: { keyCode: key("4") },
|
||||||
},
|
virtual_processor: { keyCode: key("5") },
|
||||||
|
},
|
||||||
placement: {
|
|
||||||
pipette: { keyCode: key("Q") },
|
placement: {
|
||||||
rotateWhilePlacing: { keyCode: key("R") },
|
pipette: { keyCode: key("Q") },
|
||||||
rotateInverseModifier: { keyCode: 16 }, // SHIFT
|
rotateWhilePlacing: { keyCode: key("R") },
|
||||||
cycleBuildingVariants: { keyCode: key("T") },
|
rotateInverseModifier: { keyCode: 16 }, // SHIFT
|
||||||
cycleBuildings: { keyCode: 9 }, // TAB
|
cycleBuildingVariants: { keyCode: key("T") },
|
||||||
switchDirectionLockSide: { keyCode: key("R") },
|
cycleBuildings: { keyCode: 9 }, // TAB
|
||||||
},
|
switchDirectionLockSide: { keyCode: key("R") },
|
||||||
|
},
|
||||||
massSelect: {
|
|
||||||
massSelectStart: { keyCode: 17 }, // CTRL
|
massSelect: {
|
||||||
massSelectSelectMultiple: { keyCode: 16 }, // SHIFT
|
massSelectStart: { keyCode: 17 }, // CTRL
|
||||||
massSelectCopy: { keyCode: key("C") },
|
massSelectSelectMultiple: { keyCode: 16 }, // SHIFT
|
||||||
massSelectCut: { keyCode: key("X") },
|
massSelectCopy: { keyCode: key("C") },
|
||||||
confirmMassDelete: { keyCode: 46 }, // DEL
|
massSelectCut: { keyCode: key("X") },
|
||||||
pasteLastBlueprint: { keyCode: key("V") },
|
confirmMassDelete: { keyCode: 46 }, // DEL
|
||||||
},
|
pasteLastBlueprint: { keyCode: key("V") },
|
||||||
|
},
|
||||||
placementModifiers: {
|
|
||||||
lockBeltDirection: { keyCode: 16 }, // SHIFT
|
placementModifiers: {
|
||||||
placementDisableAutoOrientation: { keyCode: 17 }, // CTRL
|
lockBeltDirection: { keyCode: 16 }, // SHIFT
|
||||||
placeMultiple: { keyCode: 16 }, // SHIFT
|
placementDisableAutoOrientation: { keyCode: 17 }, // CTRL
|
||||||
placeInverse: { keyCode: 18 }, // ALT
|
placeMultiple: { keyCode: 16 }, // SHIFT
|
||||||
},
|
placeInverse: { keyCode: 18 }, // ALT
|
||||||
};
|
},
|
||||||
|
};
|
||||||
// Assign ids
|
|
||||||
for (const categoryId in KEYMAPPINGS) {
|
// Assign ids
|
||||||
for (const mappingId in KEYMAPPINGS[categoryId]) {
|
for (const categoryId in KEYMAPPINGS) {
|
||||||
KEYMAPPINGS[categoryId][mappingId].id = mappingId;
|
for (const mappingId in KEYMAPPINGS[categoryId]) {
|
||||||
}
|
KEYMAPPINGS[categoryId][mappingId].id = mappingId;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
export const KEYCODE_LMB = 1;
|
|
||||||
export const KEYCODE_MMB = 2;
|
export const KEYCODE_LMB = 1;
|
||||||
export const KEYCODE_RMB = 3;
|
export const KEYCODE_MMB = 2;
|
||||||
|
export const KEYCODE_RMB = 3;
|
||||||
/**
|
|
||||||
* Returns a keycode -> string
|
/**
|
||||||
* @param {number} code
|
* Returns a keycode -> string
|
||||||
* @returns {string}
|
* @param {number} code
|
||||||
*/
|
* @returns {string}
|
||||||
export function getStringForKeyCode(code) {
|
*/
|
||||||
switch (code) {
|
export function getStringForKeyCode(code) {
|
||||||
case KEYCODE_LMB:
|
switch (code) {
|
||||||
return "LMB";
|
case KEYCODE_LMB:
|
||||||
case KEYCODE_MMB:
|
return "LMB";
|
||||||
return "MMB";
|
case KEYCODE_MMB:
|
||||||
case KEYCODE_RMB:
|
return "MMB";
|
||||||
return "RMB";
|
case KEYCODE_RMB:
|
||||||
case 4:
|
return "RMB";
|
||||||
return "MB4";
|
case 4:
|
||||||
case 5:
|
return "MB4";
|
||||||
return "MB5";
|
case 5:
|
||||||
case 8:
|
return "MB5";
|
||||||
return "⌫";
|
case 8:
|
||||||
case 9:
|
return "⌫";
|
||||||
return T.global.keys.tab;
|
case 9:
|
||||||
case 13:
|
return T.global.keys.tab;
|
||||||
return "⏎";
|
case 13:
|
||||||
case 16:
|
return "⏎";
|
||||||
return "⇪";
|
case 16:
|
||||||
case 17:
|
return "⇪";
|
||||||
return T.global.keys.control;
|
case 17:
|
||||||
case 18:
|
return T.global.keys.control;
|
||||||
return T.global.keys.alt;
|
case 18:
|
||||||
case 19:
|
return T.global.keys.alt;
|
||||||
return "PAUSE";
|
case 19:
|
||||||
case 20:
|
return "PAUSE";
|
||||||
return "CAPS";
|
case 20:
|
||||||
case 27:
|
return "CAPS";
|
||||||
return T.global.keys.escape;
|
case 27:
|
||||||
case 32:
|
return T.global.keys.escape;
|
||||||
return T.global.keys.space;
|
case 32:
|
||||||
case 33:
|
return T.global.keys.space;
|
||||||
return "PGUP";
|
case 33:
|
||||||
case 34:
|
return "PGUP";
|
||||||
return "PGDOWN";
|
case 34:
|
||||||
case 35:
|
return "PGDOWN";
|
||||||
return "END";
|
case 35:
|
||||||
case 36:
|
return "END";
|
||||||
return "HOME";
|
case 36:
|
||||||
case 37:
|
return "HOME";
|
||||||
return "⬅";
|
case 37:
|
||||||
case 38:
|
return "⬅";
|
||||||
return "⬆";
|
case 38:
|
||||||
case 39:
|
return "⬆";
|
||||||
return "➡";
|
case 39:
|
||||||
case 40:
|
return "➡";
|
||||||
return "⬇";
|
case 40:
|
||||||
case 44:
|
return "⬇";
|
||||||
return "PRNT";
|
case 44:
|
||||||
case 45:
|
return "PRNT";
|
||||||
return "INS";
|
case 45:
|
||||||
case 46:
|
return "INS";
|
||||||
return "DEL";
|
case 46:
|
||||||
case 93:
|
return "DEL";
|
||||||
return "SEL";
|
case 93:
|
||||||
case 96:
|
return "SEL";
|
||||||
return "NUM 0";
|
case 96:
|
||||||
case 97:
|
return "NUM 0";
|
||||||
return "NUM 1";
|
case 97:
|
||||||
case 98:
|
return "NUM 1";
|
||||||
return "NUM 2";
|
case 98:
|
||||||
case 99:
|
return "NUM 2";
|
||||||
return "NUM 3";
|
case 99:
|
||||||
case 100:
|
return "NUM 3";
|
||||||
return "NUM 4";
|
case 100:
|
||||||
case 101:
|
return "NUM 4";
|
||||||
return "NUM 5";
|
case 101:
|
||||||
case 102:
|
return "NUM 5";
|
||||||
return "NUM 6";
|
case 102:
|
||||||
case 103:
|
return "NUM 6";
|
||||||
return "NUM 7";
|
case 103:
|
||||||
case 104:
|
return "NUM 7";
|
||||||
return "NUM 8";
|
case 104:
|
||||||
case 105:
|
return "NUM 8";
|
||||||
return "NUM 9";
|
case 105:
|
||||||
case 106:
|
return "NUM 9";
|
||||||
return "*";
|
case 106:
|
||||||
case 107:
|
return "*";
|
||||||
return "+";
|
case 107:
|
||||||
case 109:
|
return "+";
|
||||||
return "-";
|
case 109:
|
||||||
case 110:
|
return "-";
|
||||||
return ".";
|
case 110:
|
||||||
case 111:
|
return ".";
|
||||||
return "/";
|
case 111:
|
||||||
case 112:
|
return "/";
|
||||||
return "F1";
|
case 112:
|
||||||
case 113:
|
return "F1";
|
||||||
return "F2";
|
case 113:
|
||||||
case 114:
|
return "F2";
|
||||||
return "F3";
|
case 114:
|
||||||
case 115:
|
return "F3";
|
||||||
return "F4";
|
case 115:
|
||||||
case 116:
|
return "F4";
|
||||||
return "F4";
|
case 116:
|
||||||
case 117:
|
return "F4";
|
||||||
return "F5";
|
case 117:
|
||||||
case 118:
|
return "F5";
|
||||||
return "F6";
|
case 118:
|
||||||
case 119:
|
return "F6";
|
||||||
return "F7";
|
case 119:
|
||||||
case 120:
|
return "F7";
|
||||||
return "F8";
|
case 120:
|
||||||
case 121:
|
return "F8";
|
||||||
return "F9";
|
case 121:
|
||||||
case 122:
|
return "F9";
|
||||||
return "F10";
|
case 122:
|
||||||
case 123:
|
return "F10";
|
||||||
return "F11";
|
case 123:
|
||||||
case 124:
|
return "F11";
|
||||||
return "F12";
|
case 124:
|
||||||
|
return "F12";
|
||||||
case 144:
|
|
||||||
return "NUMLOCK";
|
case 144:
|
||||||
case 145:
|
return "NUMLOCK";
|
||||||
return "SCRLOCK";
|
case 145:
|
||||||
case 182:
|
return "SCRLOCK";
|
||||||
return "COMP";
|
case 182:
|
||||||
case 183:
|
return "COMP";
|
||||||
return "CALC";
|
case 183:
|
||||||
case 186:
|
return "CALC";
|
||||||
return ";";
|
case 186:
|
||||||
case 187:
|
return ";";
|
||||||
return "+";
|
case 187:
|
||||||
case 188:
|
return "+";
|
||||||
return ",";
|
case 188:
|
||||||
case 189:
|
return ",";
|
||||||
return "-";
|
case 189:
|
||||||
case 191:
|
return "-";
|
||||||
return "/";
|
case 191:
|
||||||
case 219:
|
return "/";
|
||||||
return "[";
|
case 219:
|
||||||
case 220:
|
return "[";
|
||||||
return "\\";
|
case 220:
|
||||||
case 221:
|
return "\\";
|
||||||
return "]";
|
case 221:
|
||||||
case 222:
|
return "]";
|
||||||
return "'";
|
case 222:
|
||||||
}
|
return "'";
|
||||||
|
}
|
||||||
return String.fromCharCode(code);
|
|
||||||
}
|
return String.fromCharCode(code);
|
||||||
|
}
|
||||||
export class Keybinding {
|
|
||||||
/**
|
export class Keybinding {
|
||||||
*
|
/**
|
||||||
* @param {KeyActionMapper} keyMapper
|
*
|
||||||
* @param {Application} app
|
* @param {KeyActionMapper} keyMapper
|
||||||
* @param {object} param0
|
* @param {Application} app
|
||||||
* @param {number} param0.keyCode
|
* @param {object} param0
|
||||||
* @param {boolean=} param0.builtin
|
* @param {number} param0.keyCode
|
||||||
* @param {boolean=} param0.repeated
|
* @param {boolean=} param0.builtin
|
||||||
*/
|
* @param {boolean=} param0.repeated
|
||||||
constructor(keyMapper, app, { keyCode, builtin = false, repeated = false }) {
|
*/
|
||||||
assert(keyCode && Number.isInteger(keyCode), "Invalid key code: " + keyCode);
|
constructor(keyMapper, app, { keyCode, builtin = false, repeated = false }) {
|
||||||
this.keyMapper = keyMapper;
|
assert(keyCode && Number.isInteger(keyCode), "Invalid key code: " + keyCode);
|
||||||
this.app = app;
|
this.keyMapper = keyMapper;
|
||||||
this.keyCode = keyCode;
|
this.app = app;
|
||||||
this.builtin = builtin;
|
this.keyCode = keyCode;
|
||||||
this.repeated = repeated;
|
this.builtin = builtin;
|
||||||
|
this.repeated = repeated;
|
||||||
this.signal = new Signal();
|
|
||||||
this.toggled = new Signal();
|
this.signal = new Signal();
|
||||||
}
|
this.toggled = new Signal();
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Returns whether this binding is currently pressed
|
/**
|
||||||
* @returns {boolean}
|
* Returns whether this binding is currently pressed
|
||||||
*/
|
* @returns {boolean}
|
||||||
get pressed() {
|
*/
|
||||||
// Check if the key is down
|
get pressed() {
|
||||||
if (this.app.inputMgr.keysDown.has(this.keyCode)) {
|
// Check if the key is down
|
||||||
// Check if it is the top reciever
|
if (this.app.inputMgr.keysDown.has(this.keyCode)) {
|
||||||
const reciever = this.keyMapper.inputReceiver;
|
// Check if it is the top reciever
|
||||||
return this.app.inputMgr.getTopReciever() === reciever;
|
const reciever = this.keyMapper.inputReceiver;
|
||||||
}
|
return this.app.inputMgr.getTopReciever() === reciever;
|
||||||
return false;
|
}
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Adds an event listener
|
/**
|
||||||
* @param {function() : void} receiver
|
* Adds an event listener
|
||||||
* @param {object=} scope
|
* @param {function() : void} receiver
|
||||||
*/
|
* @param {object=} scope
|
||||||
add(receiver, scope = null) {
|
*/
|
||||||
this.signal.add(receiver, scope);
|
add(receiver, scope = null) {
|
||||||
}
|
this.signal.add(receiver, scope);
|
||||||
|
}
|
||||||
/**
|
|
||||||
* @param {Element} elem
|
/**
|
||||||
* @returns {HTMLElement} the created element, or null if the keybindings are not shown
|
* @param {Element} elem
|
||||||
* */
|
* @returns {HTMLElement} the created element, or null if the keybindings are not shown
|
||||||
appendLabelToElement(elem) {
|
* */
|
||||||
if (IS_MOBILE) {
|
appendLabelToElement(elem) {
|
||||||
return null;
|
if (IS_MOBILE) {
|
||||||
}
|
return null;
|
||||||
const spacer = document.createElement("code");
|
}
|
||||||
spacer.classList.add("keybinding");
|
const spacer = document.createElement("code");
|
||||||
spacer.innerHTML = getStringForKeyCode(this.keyCode);
|
spacer.classList.add("keybinding");
|
||||||
elem.appendChild(spacer);
|
spacer.innerHTML = getStringForKeyCode(this.keyCode);
|
||||||
return spacer;
|
elem.appendChild(spacer);
|
||||||
}
|
return spacer;
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Returns the key code as a nice string
|
/**
|
||||||
*/
|
* Returns the key code as a nice string
|
||||||
getKeyCodeString() {
|
*/
|
||||||
return getStringForKeyCode(this.keyCode);
|
getKeyCodeString() {
|
||||||
}
|
return getStringForKeyCode(this.keyCode);
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Remvoes all signal receivers
|
/**
|
||||||
*/
|
* Remvoes all signal receivers
|
||||||
clearSignalReceivers() {
|
*/
|
||||||
this.signal.removeAll();
|
clearSignalReceivers() {
|
||||||
}
|
this.signal.removeAll();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
export class KeyActionMapper {
|
|
||||||
/**
|
export class KeyActionMapper {
|
||||||
*
|
/**
|
||||||
* @param {GameRoot} root
|
*
|
||||||
* @param {InputReceiver} inputReciever
|
* @param {GameRoot} root
|
||||||
*/
|
* @param {InputReceiver} inputReciever
|
||||||
constructor(root, inputReciever) {
|
*/
|
||||||
this.root = root;
|
constructor(root, inputReciever) {
|
||||||
this.inputReceiver = inputReciever;
|
this.root = root;
|
||||||
|
this.inputReceiver = inputReciever;
|
||||||
inputReciever.keydown.add(this.handleKeydown, this);
|
|
||||||
inputReciever.keyup.add(this.handleKeyup, this);
|
inputReciever.keydown.add(this.handleKeydown, this);
|
||||||
|
inputReciever.keyup.add(this.handleKeyup, this);
|
||||||
/** @type {Object.<string, Keybinding>} */
|
|
||||||
this.keybindings = {};
|
/** @type {Object.<string, Keybinding>} */
|
||||||
|
this.keybindings = {};
|
||||||
const overrides = root.app.settings.getKeybindingOverrides();
|
|
||||||
|
const overrides = root.app.settings.getKeybindingOverrides();
|
||||||
for (const category in KEYMAPPINGS) {
|
|
||||||
for (const key in KEYMAPPINGS[category]) {
|
for (const category in KEYMAPPINGS) {
|
||||||
let payload = Object.assign({}, KEYMAPPINGS[category][key]);
|
for (const key in KEYMAPPINGS[category]) {
|
||||||
if (overrides[key]) {
|
let payload = Object.assign({}, KEYMAPPINGS[category][key]);
|
||||||
payload.keyCode = overrides[key];
|
if (overrides[key]) {
|
||||||
}
|
payload.keyCode = overrides[key];
|
||||||
|
}
|
||||||
this.keybindings[key] = new Keybinding(this, this.root.app, payload);
|
|
||||||
}
|
this.keybindings[key] = new Keybinding(this, this.root.app, payload);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
inputReciever.pageBlur.add(this.onPageBlur, this);
|
|
||||||
inputReciever.destroyed.add(this.cleanup, this);
|
inputReciever.pageBlur.add(this.onPageBlur, this);
|
||||||
}
|
inputReciever.destroyed.add(this.cleanup, this);
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Returns all keybindings starting with the given id
|
/**
|
||||||
* @param {string} pattern
|
* Returns all keybindings starting with the given id
|
||||||
* @returns {Array<Keybinding>}
|
* @param {string} pattern
|
||||||
*/
|
* @returns {Array<Keybinding>}
|
||||||
getKeybindingsStartingWith(pattern) {
|
*/
|
||||||
let result = [];
|
getKeybindingsStartingWith(pattern) {
|
||||||
for (const key in this.keybindings) {
|
let result = [];
|
||||||
if (key.startsWith(pattern)) {
|
for (const key in this.keybindings) {
|
||||||
result.push(this.keybindings[key]);
|
if (key.startsWith(pattern)) {
|
||||||
}
|
result.push(this.keybindings[key]);
|
||||||
}
|
}
|
||||||
return result;
|
}
|
||||||
}
|
return result;
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Forwards the given events to the other mapper (used in tooltips)
|
/**
|
||||||
* @param {KeyActionMapper} receiver
|
* Forwards the given events to the other mapper (used in tooltips)
|
||||||
* @param {Array<string>} bindings
|
* @param {KeyActionMapper} receiver
|
||||||
*/
|
* @param {Array<string>} bindings
|
||||||
forward(receiver, bindings) {
|
*/
|
||||||
for (let i = 0; i < bindings.length; ++i) {
|
forward(receiver, bindings) {
|
||||||
const key = bindings[i];
|
for (let i = 0; i < bindings.length; ++i) {
|
||||||
this.keybindings[key].signal.add((...args) => receiver.keybindings[key].signal.dispatch(...args));
|
const key = bindings[i];
|
||||||
}
|
this.keybindings[key].signal.add((...args) => receiver.keybindings[key].signal.dispatch(...args));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
cleanup() {
|
|
||||||
for (const key in this.keybindings) {
|
cleanup() {
|
||||||
this.keybindings[key].signal.removeAll();
|
for (const key in this.keybindings) {
|
||||||
}
|
this.keybindings[key].signal.removeAll();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
onPageBlur() {
|
|
||||||
// Reset all down states
|
onPageBlur() {
|
||||||
// Find mapping
|
// Reset all down states
|
||||||
for (const key in this.keybindings) {
|
// Find mapping
|
||||||
/** @type {Keybinding} */
|
for (const key in this.keybindings) {
|
||||||
const binding = this.keybindings[key];
|
/** @type {Keybinding} */
|
||||||
}
|
const binding = this.keybindings[key];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Internal keydown handler
|
/**
|
||||||
* @param {object} param0
|
* Internal keydown handler
|
||||||
* @param {number} param0.keyCode
|
* @param {object} param0
|
||||||
* @param {boolean} param0.shift
|
* @param {number} param0.keyCode
|
||||||
* @param {boolean} param0.alt
|
* @param {boolean} param0.shift
|
||||||
* @param {boolean=} param0.initial
|
* @param {boolean} param0.alt
|
||||||
*/
|
* @param {boolean=} param0.initial
|
||||||
handleKeydown({ keyCode, shift, alt, initial }) {
|
*/
|
||||||
let stop = false;
|
handleKeydown({ keyCode, shift, alt, initial }) {
|
||||||
|
let stop = false;
|
||||||
// Find mapping
|
|
||||||
for (const key in this.keybindings) {
|
// Find mapping
|
||||||
/** @type {Keybinding} */
|
for (const key in this.keybindings) {
|
||||||
const binding = this.keybindings[key];
|
/** @type {Keybinding} */
|
||||||
if (binding.keyCode === keyCode && (initial || binding.repeated)) {
|
const binding = this.keybindings[key];
|
||||||
/** @type {Signal} */
|
if (binding.keyCode === keyCode && (initial || binding.repeated)) {
|
||||||
const signal = this.keybindings[key].signal;
|
/** @type {Signal} */
|
||||||
if (signal.dispatch() === STOP_PROPAGATION) {
|
const signal = this.keybindings[key].signal;
|
||||||
return;
|
if (signal.dispatch() === STOP_PROPAGATION) {
|
||||||
}
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (stop) {
|
|
||||||
return STOP_PROPAGATION;
|
if (stop) {
|
||||||
}
|
return STOP_PROPAGATION;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Internal keyup handler
|
/**
|
||||||
* @param {object} param0
|
* Internal keyup handler
|
||||||
* @param {number} param0.keyCode
|
* @param {object} param0
|
||||||
* @param {boolean} param0.shift
|
* @param {number} param0.keyCode
|
||||||
* @param {boolean} param0.alt
|
* @param {boolean} param0.shift
|
||||||
*/
|
* @param {boolean} param0.alt
|
||||||
handleKeyup({ keyCode, shift, alt }) {
|
*/
|
||||||
// Empty
|
handleKeyup({ keyCode, shift, alt }) {
|
||||||
}
|
// Empty
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Returns a given keybinding
|
/**
|
||||||
* @param {{ keyCode: number }} binding
|
* Returns a given keybinding
|
||||||
* @returns {Keybinding}
|
* @param {{ keyCode: number }} binding
|
||||||
*/
|
* @returns {Keybinding}
|
||||||
getBinding(binding) {
|
*/
|
||||||
// @ts-ignore
|
getBinding(binding) {
|
||||||
const id = binding.id;
|
// @ts-ignore
|
||||||
assert(id, "Not a valid keybinding: " + JSON.stringify(binding));
|
const id = binding.id;
|
||||||
assert(this.keybindings[id], "Keybinding " + id + " not known!");
|
assert(id, "Not a valid keybinding: " + JSON.stringify(binding));
|
||||||
return this.keybindings[id];
|
assert(this.keybindings[id], "Keybinding " + id + " not known!");
|
||||||
}
|
return this.keybindings[id];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
@ -1,274 +1,281 @@
|
|||||||
import { globalConfig } from "../core/config";
|
import { globalConfig } from "../core/config";
|
||||||
import { DrawParameters } from "../core/draw_parameters";
|
import { DrawParameters } from "../core/draw_parameters";
|
||||||
import { getBuildingDataFromCode } from "./building_codes";
|
import { getBuildingDataFromCode } from "./building_codes";
|
||||||
import { Entity } from "./entity";
|
import { Entity } from "./entity";
|
||||||
import { MapChunk } from "./map_chunk";
|
import { MapChunk } from "./map_chunk";
|
||||||
import { GameRoot } from "./root";
|
import { GameRoot } from "./root";
|
||||||
import { THEME } from "./theme";
|
import { THEME } from "./theme";
|
||||||
import { drawSpriteClipped } from "../core/draw_utils";
|
import { drawSpriteClipped } from "../core/draw_utils";
|
||||||
|
|
||||||
export const CHUNK_OVERLAY_RES = 3;
|
export const CHUNK_OVERLAY_RES = 3;
|
||||||
|
|
||||||
export class MapChunkView extends MapChunk {
|
export class MapChunkView extends MapChunk {
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {GameRoot} root
|
* @param {GameRoot} root
|
||||||
* @param {number} x
|
* @param {number} x
|
||||||
* @param {number} y
|
* @param {number} y
|
||||||
*/
|
*/
|
||||||
constructor(root, x, y) {
|
constructor(root, x, y) {
|
||||||
super(root, x, y);
|
super(root, x, y);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whenever something changes, we increase this number - so we know we need to redraw
|
* Whenever something changes, we increase this number - so we know we need to redraw
|
||||||
*/
|
*/
|
||||||
this.renderIteration = 0;
|
this.renderIteration = 0;
|
||||||
|
|
||||||
this.markDirty();
|
this.markDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marks this chunk as dirty, rerendering all caches
|
* Marks this chunk as dirty, rerendering all caches
|
||||||
*/
|
*/
|
||||||
markDirty() {
|
markDirty() {
|
||||||
++this.renderIteration;
|
++this.renderIteration;
|
||||||
this.renderKey = this.x + "/" + this.y + "@" + this.renderIteration;
|
this.renderKey = this.x + "/" + this.y + "@" + this.renderIteration;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draws the background layer
|
* Draws the background layer
|
||||||
* @param {DrawParameters} parameters
|
* @param {DrawParameters} parameters
|
||||||
*/
|
*/
|
||||||
drawBackgroundLayer(parameters) {
|
drawBackgroundLayer(parameters) {
|
||||||
const systems = this.root.systemMgr.systems;
|
const systems = this.root.systemMgr.systems;
|
||||||
systems.mapResources.drawChunk(parameters, this);
|
systems.mapResources.drawChunk(parameters, this);
|
||||||
systems.beltUnderlays.drawChunk(parameters, this);
|
systems.beltUnderlays.drawChunk(parameters, this);
|
||||||
systems.belt.drawChunk(parameters, this);
|
systems.belt.drawChunk(parameters, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draws the foreground layer
|
* Draws the dynamic foreground layer
|
||||||
* @param {DrawParameters} parameters
|
* @param {DrawParameters} parameters
|
||||||
*/
|
*/
|
||||||
drawForegroundLayer(parameters) {
|
drawForegroundDynamicLayer(parameters) {
|
||||||
const systems = this.root.systemMgr.systems;
|
const systems = this.root.systemMgr.systems;
|
||||||
|
|
||||||
systems.itemEjector.drawChunk(parameters, this);
|
systems.itemEjector.drawChunk(parameters, this);
|
||||||
systems.itemAcceptor.drawChunk(parameters, this);
|
systems.itemAcceptor.drawChunk(parameters, this);
|
||||||
|
systems.miner.drawChunk(parameters, this);
|
||||||
systems.miner.drawChunk(parameters, this);
|
}
|
||||||
|
|
||||||
systems.staticMapEntities.drawChunk(parameters, this);
|
/**
|
||||||
systems.lever.drawChunk(parameters, this);
|
* Draws the static foreground layer
|
||||||
systems.display.drawChunk(parameters, this);
|
* @param {DrawParameters} parameters
|
||||||
systems.storage.drawChunk(parameters, this);
|
*/
|
||||||
}
|
drawForegroundStaticLayer(parameters) {
|
||||||
|
const systems = this.root.systemMgr.systems;
|
||||||
/**
|
|
||||||
* Overlay
|
systems.staticMapEntities.drawChunk(parameters, this);
|
||||||
* @param {DrawParameters} parameters
|
systems.lever.drawChunk(parameters, this);
|
||||||
*/
|
systems.display.drawChunk(parameters, this);
|
||||||
drawOverlay(parameters) {
|
systems.storage.drawChunk(parameters, this);
|
||||||
const overlaySize = globalConfig.mapChunkSize * CHUNK_OVERLAY_RES;
|
}
|
||||||
const sprite = this.root.buffers.getForKey({
|
|
||||||
key: "chunk@" + this.root.currentLayer,
|
/**
|
||||||
subKey: this.renderKey,
|
* Overlay
|
||||||
w: overlaySize,
|
* @param {DrawParameters} parameters
|
||||||
h: overlaySize,
|
*/
|
||||||
dpi: 1,
|
drawOverlay(parameters) {
|
||||||
redrawMethod: this.generateOverlayBuffer.bind(this),
|
const overlaySize = globalConfig.mapChunkSize * CHUNK_OVERLAY_RES;
|
||||||
});
|
const sprite = this.root.buffers.getForKey({
|
||||||
|
key: "chunk@" + this.root.currentLayer,
|
||||||
const dims = globalConfig.mapChunkWorldSize;
|
subKey: this.renderKey,
|
||||||
|
w: overlaySize,
|
||||||
// Draw chunk "pixel" art
|
h: overlaySize,
|
||||||
parameters.context.imageSmoothingEnabled = false;
|
dpi: 1,
|
||||||
drawSpriteClipped({
|
redrawMethod: this.generateOverlayBuffer.bind(this),
|
||||||
parameters,
|
});
|
||||||
sprite,
|
|
||||||
x: this.x * dims,
|
const dims = globalConfig.mapChunkWorldSize;
|
||||||
y: this.y * dims,
|
|
||||||
w: dims,
|
// Draw chunk "pixel" art
|
||||||
h: dims,
|
parameters.context.imageSmoothingEnabled = false;
|
||||||
originalW: overlaySize,
|
drawSpriteClipped({
|
||||||
originalH: overlaySize,
|
parameters,
|
||||||
});
|
sprite,
|
||||||
|
x: this.x * dims,
|
||||||
parameters.context.imageSmoothingEnabled = true;
|
y: this.y * dims,
|
||||||
|
w: dims,
|
||||||
// Draw patch items
|
h: dims,
|
||||||
if (this.root.currentLayer === "regular") {
|
originalW: overlaySize,
|
||||||
for (let i = 0; i < this.patches.length; ++i) {
|
originalH: overlaySize,
|
||||||
const patch = this.patches[i];
|
});
|
||||||
|
|
||||||
const destX = this.x * dims + patch.pos.x * globalConfig.tileSize;
|
parameters.context.imageSmoothingEnabled = true;
|
||||||
const destY = this.y * dims + patch.pos.y * globalConfig.tileSize;
|
|
||||||
const diameter = Math.min(80, 30 / parameters.zoomLevel);
|
// Draw patch items
|
||||||
|
if (this.root.currentLayer === "regular") {
|
||||||
patch.item.drawItemCenteredClipped(destX, destY, parameters, diameter);
|
for (let i = 0; i < this.patches.length; ++i) {
|
||||||
}
|
const patch = this.patches[i];
|
||||||
}
|
|
||||||
}
|
const destX = this.x * dims + patch.pos.x * globalConfig.tileSize;
|
||||||
|
const destY = this.y * dims + patch.pos.y * globalConfig.tileSize;
|
||||||
/**
|
const diameter = Math.min(80, 30 / parameters.zoomLevel);
|
||||||
*
|
|
||||||
* @param {HTMLCanvasElement} canvas
|
patch.item.drawItemCenteredClipped(destX, destY, parameters, diameter);
|
||||||
* @param {CanvasRenderingContext2D} context
|
}
|
||||||
* @param {number} w
|
}
|
||||||
* @param {number} h
|
}
|
||||||
* @param {number} dpi
|
|
||||||
*/
|
/**
|
||||||
generateOverlayBuffer(canvas, context, w, h, dpi) {
|
*
|
||||||
context.fillStyle =
|
* @param {HTMLCanvasElement} canvas
|
||||||
this.containedEntities.length > 0
|
* @param {CanvasRenderingContext2D} context
|
||||||
? THEME.map.chunkOverview.filled
|
* @param {number} w
|
||||||
: THEME.map.chunkOverview.empty;
|
* @param {number} h
|
||||||
context.fillRect(0, 0, w, h);
|
* @param {number} dpi
|
||||||
|
*/
|
||||||
for (let x = 0; x < globalConfig.mapChunkSize; ++x) {
|
generateOverlayBuffer(canvas, context, w, h, dpi) {
|
||||||
const lowerArray = this.lowerLayer[x];
|
context.fillStyle =
|
||||||
const upperArray = this.contents[x];
|
this.containedEntities.length > 0
|
||||||
for (let y = 0; y < globalConfig.mapChunkSize; ++y) {
|
? THEME.map.chunkOverview.filled
|
||||||
const upperContent = upperArray[y];
|
: THEME.map.chunkOverview.empty;
|
||||||
if (upperContent) {
|
context.fillRect(0, 0, w, h);
|
||||||
const staticComp = upperContent.components.StaticMapEntity;
|
|
||||||
const data = getBuildingDataFromCode(staticComp.code);
|
for (let x = 0; x < globalConfig.mapChunkSize; ++x) {
|
||||||
const metaBuilding = data.metaInstance;
|
const lowerArray = this.lowerLayer[x];
|
||||||
|
const upperArray = this.contents[x];
|
||||||
const overlayMatrix = metaBuilding.getSpecialOverlayRenderMatrix(
|
for (let y = 0; y < globalConfig.mapChunkSize; ++y) {
|
||||||
staticComp.rotation,
|
const upperContent = upperArray[y];
|
||||||
data.rotationVariant,
|
if (upperContent) {
|
||||||
data.variant,
|
const staticComp = upperContent.components.StaticMapEntity;
|
||||||
upperContent
|
const data = getBuildingDataFromCode(staticComp.code);
|
||||||
);
|
const metaBuilding = data.metaInstance;
|
||||||
|
|
||||||
if (overlayMatrix) {
|
const overlayMatrix = metaBuilding.getSpecialOverlayRenderMatrix(
|
||||||
// Draw lower content first since it "shines" through
|
staticComp.rotation,
|
||||||
const lowerContent = lowerArray[y];
|
data.rotationVariant,
|
||||||
if (lowerContent) {
|
data.variant,
|
||||||
context.fillStyle = lowerContent.getBackgroundColorAsResource();
|
upperContent
|
||||||
context.fillRect(
|
);
|
||||||
x * CHUNK_OVERLAY_RES,
|
|
||||||
y * CHUNK_OVERLAY_RES,
|
if (overlayMatrix) {
|
||||||
CHUNK_OVERLAY_RES,
|
// Draw lower content first since it "shines" through
|
||||||
CHUNK_OVERLAY_RES
|
const lowerContent = lowerArray[y];
|
||||||
);
|
if (lowerContent) {
|
||||||
}
|
context.fillStyle = lowerContent.getBackgroundColorAsResource();
|
||||||
|
context.fillRect(
|
||||||
context.fillStyle = metaBuilding.getSilhouetteColor();
|
x * CHUNK_OVERLAY_RES,
|
||||||
for (let dx = 0; dx < 3; ++dx) {
|
y * CHUNK_OVERLAY_RES,
|
||||||
for (let dy = 0; dy < 3; ++dy) {
|
CHUNK_OVERLAY_RES,
|
||||||
const isFilled = overlayMatrix[dx + dy * 3];
|
CHUNK_OVERLAY_RES
|
||||||
if (isFilled) {
|
);
|
||||||
context.fillRect(
|
}
|
||||||
x * CHUNK_OVERLAY_RES + dx,
|
|
||||||
y * CHUNK_OVERLAY_RES + dy,
|
context.fillStyle = metaBuilding.getSilhouetteColor();
|
||||||
1,
|
for (let dx = 0; dx < 3; ++dx) {
|
||||||
1
|
for (let dy = 0; dy < 3; ++dy) {
|
||||||
);
|
const isFilled = overlayMatrix[dx + dy * 3];
|
||||||
}
|
if (isFilled) {
|
||||||
}
|
context.fillRect(
|
||||||
}
|
x * CHUNK_OVERLAY_RES + dx,
|
||||||
|
y * CHUNK_OVERLAY_RES + dy,
|
||||||
continue;
|
1,
|
||||||
} else {
|
1
|
||||||
context.fillStyle = metaBuilding.getSilhouetteColor();
|
);
|
||||||
context.fillRect(
|
}
|
||||||
x * CHUNK_OVERLAY_RES,
|
}
|
||||||
y * CHUNK_OVERLAY_RES,
|
}
|
||||||
CHUNK_OVERLAY_RES,
|
|
||||||
CHUNK_OVERLAY_RES
|
continue;
|
||||||
);
|
} else {
|
||||||
|
context.fillStyle = metaBuilding.getSilhouetteColor();
|
||||||
continue;
|
context.fillRect(
|
||||||
}
|
x * CHUNK_OVERLAY_RES,
|
||||||
}
|
y * CHUNK_OVERLAY_RES,
|
||||||
|
CHUNK_OVERLAY_RES,
|
||||||
const lowerContent = lowerArray[y];
|
CHUNK_OVERLAY_RES
|
||||||
if (lowerContent) {
|
);
|
||||||
context.fillStyle = lowerContent.getBackgroundColorAsResource();
|
|
||||||
context.fillRect(
|
continue;
|
||||||
x * CHUNK_OVERLAY_RES,
|
}
|
||||||
y * CHUNK_OVERLAY_RES,
|
}
|
||||||
CHUNK_OVERLAY_RES,
|
|
||||||
CHUNK_OVERLAY_RES
|
const lowerContent = lowerArray[y];
|
||||||
);
|
if (lowerContent) {
|
||||||
}
|
context.fillStyle = lowerContent.getBackgroundColorAsResource();
|
||||||
}
|
context.fillRect(
|
||||||
}
|
x * CHUNK_OVERLAY_RES,
|
||||||
|
y * CHUNK_OVERLAY_RES,
|
||||||
if (this.root.currentLayer === "wires") {
|
CHUNK_OVERLAY_RES,
|
||||||
// Draw wires overlay
|
CHUNK_OVERLAY_RES
|
||||||
|
);
|
||||||
context.fillStyle = THEME.map.wires.overlayColor;
|
}
|
||||||
context.fillRect(0, 0, w, h);
|
}
|
||||||
|
}
|
||||||
for (let x = 0; x < globalConfig.mapChunkSize; ++x) {
|
|
||||||
const wiresArray = this.wireContents[x];
|
if (this.root.currentLayer === "wires") {
|
||||||
for (let y = 0; y < globalConfig.mapChunkSize; ++y) {
|
// Draw wires overlay
|
||||||
const content = wiresArray[y];
|
|
||||||
if (!content) {
|
context.fillStyle = THEME.map.wires.overlayColor;
|
||||||
continue;
|
context.fillRect(0, 0, w, h);
|
||||||
}
|
|
||||||
MapChunkView.drawSingleWiresOverviewTile({
|
for (let x = 0; x < globalConfig.mapChunkSize; ++x) {
|
||||||
context,
|
const wiresArray = this.wireContents[x];
|
||||||
x: x * CHUNK_OVERLAY_RES,
|
for (let y = 0; y < globalConfig.mapChunkSize; ++y) {
|
||||||
y: y * CHUNK_OVERLAY_RES,
|
const content = wiresArray[y];
|
||||||
entity: content,
|
if (!content) {
|
||||||
tileSizePixels: CHUNK_OVERLAY_RES,
|
continue;
|
||||||
});
|
}
|
||||||
}
|
MapChunkView.drawSingleWiresOverviewTile({
|
||||||
}
|
context,
|
||||||
}
|
x: x * CHUNK_OVERLAY_RES,
|
||||||
}
|
y: y * CHUNK_OVERLAY_RES,
|
||||||
|
entity: content,
|
||||||
/**
|
tileSizePixels: CHUNK_OVERLAY_RES,
|
||||||
* @param {object} param0
|
});
|
||||||
* @param {CanvasRenderingContext2D} param0.context
|
}
|
||||||
* @param {number} param0.x
|
}
|
||||||
* @param {number} param0.y
|
}
|
||||||
* @param {Entity} param0.entity
|
}
|
||||||
* @param {number} param0.tileSizePixels
|
|
||||||
* @param {string=} param0.overrideColor Optionally override the color to be rendered
|
/**
|
||||||
*/
|
* @param {object} param0
|
||||||
static drawSingleWiresOverviewTile({ context, x, y, entity, tileSizePixels, overrideColor = null }) {
|
* @param {CanvasRenderingContext2D} param0.context
|
||||||
const staticComp = entity.components.StaticMapEntity;
|
* @param {number} param0.x
|
||||||
const data = getBuildingDataFromCode(staticComp.code);
|
* @param {number} param0.y
|
||||||
const metaBuilding = data.metaInstance;
|
* @param {Entity} param0.entity
|
||||||
const overlayMatrix = metaBuilding.getSpecialOverlayRenderMatrix(
|
* @param {number} param0.tileSizePixels
|
||||||
staticComp.rotation,
|
* @param {string=} param0.overrideColor Optionally override the color to be rendered
|
||||||
data.rotationVariant,
|
*/
|
||||||
data.variant,
|
static drawSingleWiresOverviewTile({ context, x, y, entity, tileSizePixels, overrideColor = null }) {
|
||||||
entity
|
const staticComp = entity.components.StaticMapEntity;
|
||||||
);
|
const data = getBuildingDataFromCode(staticComp.code);
|
||||||
context.fillStyle = overrideColor || metaBuilding.getSilhouetteColor();
|
const metaBuilding = data.metaInstance;
|
||||||
if (overlayMatrix) {
|
const overlayMatrix = metaBuilding.getSpecialOverlayRenderMatrix(
|
||||||
for (let dx = 0; dx < 3; ++dx) {
|
staticComp.rotation,
|
||||||
for (let dy = 0; dy < 3; ++dy) {
|
data.rotationVariant,
|
||||||
const isFilled = overlayMatrix[dx + dy * 3];
|
data.variant,
|
||||||
if (isFilled) {
|
entity
|
||||||
context.fillRect(
|
);
|
||||||
x + (dx * tileSizePixels) / CHUNK_OVERLAY_RES,
|
context.fillStyle = overrideColor || metaBuilding.getSilhouetteColor();
|
||||||
y + (dy * tileSizePixels) / CHUNK_OVERLAY_RES,
|
if (overlayMatrix) {
|
||||||
tileSizePixels / CHUNK_OVERLAY_RES,
|
for (let dx = 0; dx < 3; ++dx) {
|
||||||
tileSizePixels / CHUNK_OVERLAY_RES
|
for (let dy = 0; dy < 3; ++dy) {
|
||||||
);
|
const isFilled = overlayMatrix[dx + dy * 3];
|
||||||
}
|
if (isFilled) {
|
||||||
}
|
context.fillRect(
|
||||||
}
|
x + (dx * tileSizePixels) / CHUNK_OVERLAY_RES,
|
||||||
} else {
|
y + (dy * tileSizePixels) / CHUNK_OVERLAY_RES,
|
||||||
context.fillRect(x, y, tileSizePixels, tileSizePixels);
|
tileSizePixels / CHUNK_OVERLAY_RES,
|
||||||
}
|
tileSizePixels / CHUNK_OVERLAY_RES
|
||||||
}
|
);
|
||||||
|
}
|
||||||
/**
|
}
|
||||||
* Draws the wires layer
|
}
|
||||||
* @param {DrawParameters} parameters
|
} else {
|
||||||
*/
|
context.fillRect(x, y, tileSizePixels, tileSizePixels);
|
||||||
drawWiresForegroundLayer(parameters) {
|
}
|
||||||
const systems = this.root.systemMgr.systems;
|
}
|
||||||
systems.wire.drawChunk(parameters, this);
|
|
||||||
systems.staticMapEntities.drawWiresChunk(parameters, this);
|
/**
|
||||||
systems.wiredPins.drawChunk(parameters, this);
|
* Draws the wires layer
|
||||||
}
|
* @param {DrawParameters} parameters
|
||||||
}
|
*/
|
||||||
|
drawWiresForegroundLayer(parameters) {
|
||||||
|
const systems = this.root.systemMgr.systems;
|
||||||
|
systems.wire.drawChunk(parameters, this);
|
||||||
|
systems.staticMapEntities.drawWiresChunk(parameters, this);
|
||||||
|
systems.wiredPins.drawChunk(parameters, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,252 +1,253 @@
|
|||||||
import { globalConfig } from "../core/config";
|
import { globalConfig } from "../core/config";
|
||||||
import { DrawParameters } from "../core/draw_parameters";
|
import { DrawParameters } from "../core/draw_parameters";
|
||||||
import { BaseMap } from "./map";
|
import { BaseMap } from "./map";
|
||||||
import { freeCanvas, makeOffscreenBuffer } from "../core/buffer_utils";
|
import { freeCanvas, makeOffscreenBuffer } from "../core/buffer_utils";
|
||||||
import { Entity } from "./entity";
|
import { Entity } from "./entity";
|
||||||
import { THEME } from "./theme";
|
import { THEME } from "./theme";
|
||||||
import { MapChunkView } from "./map_chunk_view";
|
import { MapChunkView } from "./map_chunk_view";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the view of the map, it extends the map which is the raw model and allows
|
* This is the view of the map, it extends the map which is the raw model and allows
|
||||||
* to draw it
|
* to draw it
|
||||||
*/
|
*/
|
||||||
export class MapView extends BaseMap {
|
export class MapView extends BaseMap {
|
||||||
constructor(root) {
|
constructor(root) {
|
||||||
super(root);
|
super(root);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DPI of the background cache images, required in some places
|
* DPI of the background cache images, required in some places
|
||||||
*/
|
*/
|
||||||
this.backgroundCacheDPI = 2;
|
this.backgroundCacheDPI = 2;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The cached background sprite, containing the flat background
|
* The cached background sprite, containing the flat background
|
||||||
* @type {HTMLCanvasElement} */
|
* @type {HTMLCanvasElement} */
|
||||||
this.cachedBackgroundCanvas = null;
|
this.cachedBackgroundCanvas = null;
|
||||||
|
|
||||||
/** @type {CanvasRenderingContext2D} */
|
/** @type {CanvasRenderingContext2D} */
|
||||||
this.cachedBackgroundContext = null;
|
this.cachedBackgroundContext = null;
|
||||||
/**
|
/**
|
||||||
* Cached pattern of the stripes background
|
* Cached pattern of the stripes background
|
||||||
* @type {CanvasPattern} */
|
* @type {CanvasPattern} */
|
||||||
this.cachedBackgroundPattern = null;
|
this.cachedBackgroundPattern = null;
|
||||||
|
|
||||||
this.internalInitializeCachedBackgroundCanvases();
|
this.internalInitializeCachedBackgroundCanvases();
|
||||||
this.root.signals.aboutToDestruct.add(this.cleanup, this);
|
this.root.signals.aboutToDestruct.add(this.cleanup, this);
|
||||||
|
|
||||||
this.root.signals.entityAdded.add(this.onEntityChanged, this);
|
this.root.signals.entityAdded.add(this.onEntityChanged, this);
|
||||||
this.root.signals.entityDestroyed.add(this.onEntityChanged, this);
|
this.root.signals.entityDestroyed.add(this.onEntityChanged, this);
|
||||||
this.root.signals.entityChanged.add(this.onEntityChanged, this);
|
this.root.signals.entityChanged.add(this.onEntityChanged, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanup() {
|
cleanup() {
|
||||||
freeCanvas(this.cachedBackgroundCanvas);
|
freeCanvas(this.cachedBackgroundCanvas);
|
||||||
this.cachedBackgroundCanvas = null;
|
this.cachedBackgroundCanvas = null;
|
||||||
this.cachedBackgroundPattern = null;
|
this.cachedBackgroundPattern = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when an entity was added, removed or changed
|
* Called when an entity was added, removed or changed
|
||||||
* @param {Entity} entity
|
* @param {Entity} entity
|
||||||
*/
|
*/
|
||||||
onEntityChanged(entity) {
|
onEntityChanged(entity) {
|
||||||
const staticComp = entity.components.StaticMapEntity;
|
const staticComp = entity.components.StaticMapEntity;
|
||||||
if (staticComp) {
|
if (staticComp) {
|
||||||
const rect = staticComp.getTileSpaceBounds();
|
const rect = staticComp.getTileSpaceBounds();
|
||||||
for (let x = rect.x; x <= rect.right(); ++x) {
|
for (let x = rect.x; x <= rect.right(); ++x) {
|
||||||
for (let y = rect.y; y <= rect.bottom(); ++y) {
|
for (let y = rect.y; y <= rect.bottom(); ++y) {
|
||||||
this.root.map.getOrCreateChunkAtTile(x, y).markDirty();
|
this.root.map.getOrCreateChunkAtTile(x, y).markDirty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draws all static entities like buildings etc.
|
* Draws all static entities like buildings etc.
|
||||||
* @param {DrawParameters} drawParameters
|
* @param {DrawParameters} drawParameters
|
||||||
*/
|
*/
|
||||||
drawStaticEntityDebugOverlays(drawParameters) {
|
drawStaticEntityDebugOverlays(drawParameters) {
|
||||||
const cullRange = drawParameters.visibleRect.toTileCullRectangle();
|
const cullRange = drawParameters.visibleRect.toTileCullRectangle();
|
||||||
const top = cullRange.top();
|
const top = cullRange.top();
|
||||||
const right = cullRange.right();
|
const right = cullRange.right();
|
||||||
const bottom = cullRange.bottom();
|
const bottom = cullRange.bottom();
|
||||||
const left = cullRange.left();
|
const left = cullRange.left();
|
||||||
|
|
||||||
const border = 1;
|
const border = 1;
|
||||||
|
|
||||||
const minY = top - border;
|
const minY = top - border;
|
||||||
const maxY = bottom + border;
|
const maxY = bottom + border;
|
||||||
const minX = left - border;
|
const minX = left - border;
|
||||||
const maxX = right + border - 1;
|
const maxX = right + border - 1;
|
||||||
|
|
||||||
// Render y from top down for proper blending
|
// Render y from top down for proper blending
|
||||||
for (let y = minY; y <= maxY; ++y) {
|
for (let y = minY; y <= maxY; ++y) {
|
||||||
for (let x = minX; x <= maxX; ++x) {
|
for (let x = minX; x <= maxX; ++x) {
|
||||||
// const content = this.tiles[x][y];
|
// const content = this.tiles[x][y];
|
||||||
const chunk = this.getChunkAtTileOrNull(x, y);
|
const chunk = this.getChunkAtTileOrNull(x, y);
|
||||||
if (!chunk) {
|
if (!chunk) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const content = chunk.getTileContentFromWorldCoords(x, y);
|
const content = chunk.getTileContentFromWorldCoords(x, y);
|
||||||
if (content) {
|
if (content) {
|
||||||
let isBorder = x <= left - 1 || x >= right + 1 || y <= top - 1 || y >= bottom + 1;
|
let isBorder = x <= left - 1 || x >= right + 1 || y <= top - 1 || y >= bottom + 1;
|
||||||
if (!isBorder) {
|
if (!isBorder) {
|
||||||
content.drawDebugOverlays(drawParameters);
|
content.drawDebugOverlays(drawParameters);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes all canvases used for background rendering
|
* Initializes all canvases used for background rendering
|
||||||
*/
|
*/
|
||||||
internalInitializeCachedBackgroundCanvases() {
|
internalInitializeCachedBackgroundCanvases() {
|
||||||
// Background canvas
|
// Background canvas
|
||||||
const dims = globalConfig.tileSize;
|
const dims = globalConfig.tileSize;
|
||||||
const dpi = this.backgroundCacheDPI;
|
const dpi = this.backgroundCacheDPI;
|
||||||
const [canvas, context] = makeOffscreenBuffer(dims * dpi, dims * dpi, {
|
const [canvas, context] = makeOffscreenBuffer(dims * dpi, dims * dpi, {
|
||||||
smooth: false,
|
smooth: false,
|
||||||
label: "map-cached-bg",
|
label: "map-cached-bg",
|
||||||
});
|
});
|
||||||
context.scale(dpi, dpi);
|
context.scale(dpi, dpi);
|
||||||
|
|
||||||
context.fillStyle = THEME.map.background;
|
context.fillStyle = THEME.map.background;
|
||||||
context.fillRect(0, 0, dims, dims);
|
context.fillRect(0, 0, dims, dims);
|
||||||
|
|
||||||
const borderWidth = THEME.map.gridLineWidth;
|
const borderWidth = THEME.map.gridLineWidth;
|
||||||
context.fillStyle = THEME.map.grid;
|
context.fillStyle = THEME.map.grid;
|
||||||
context.fillRect(0, 0, dims, borderWidth);
|
context.fillRect(0, 0, dims, borderWidth);
|
||||||
context.fillRect(0, borderWidth, borderWidth, dims);
|
context.fillRect(0, borderWidth, borderWidth, dims);
|
||||||
|
|
||||||
context.fillRect(dims - borderWidth, borderWidth, borderWidth, dims - 2 * borderWidth);
|
context.fillRect(dims - borderWidth, borderWidth, borderWidth, dims - 2 * borderWidth);
|
||||||
context.fillRect(borderWidth, dims - borderWidth, dims, borderWidth);
|
context.fillRect(borderWidth, dims - borderWidth, dims, borderWidth);
|
||||||
|
|
||||||
this.cachedBackgroundCanvas = canvas;
|
this.cachedBackgroundCanvas = canvas;
|
||||||
this.cachedBackgroundContext = context;
|
this.cachedBackgroundContext = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draws the maps foreground
|
* Draws the maps foreground
|
||||||
* @param {DrawParameters} parameters
|
* @param {DrawParameters} parameters
|
||||||
*/
|
*/
|
||||||
drawForeground(parameters) {
|
drawForeground(parameters) {
|
||||||
this.drawVisibleChunks(parameters, MapChunkView.prototype.drawForegroundLayer);
|
this.drawVisibleChunks(parameters, MapChunkView.prototype.drawForegroundDynamicLayer);
|
||||||
}
|
this.drawVisibleChunks(parameters, MapChunkView.prototype.drawForegroundStaticLayer);
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Calls a given method on all given chunks
|
/**
|
||||||
* @param {DrawParameters} parameters
|
* Calls a given method on all given chunks
|
||||||
* @param {function} method
|
* @param {DrawParameters} parameters
|
||||||
*/
|
* @param {function} method
|
||||||
drawVisibleChunks(parameters, method) {
|
*/
|
||||||
const cullRange = parameters.visibleRect.allScaled(1 / globalConfig.tileSize);
|
drawVisibleChunks(parameters, method) {
|
||||||
const top = cullRange.top();
|
const cullRange = parameters.visibleRect.allScaled(1 / globalConfig.tileSize);
|
||||||
const right = cullRange.right();
|
const top = cullRange.top();
|
||||||
const bottom = cullRange.bottom();
|
const right = cullRange.right();
|
||||||
const left = cullRange.left();
|
const bottom = cullRange.bottom();
|
||||||
|
const left = cullRange.left();
|
||||||
const border = 0;
|
|
||||||
const minY = top - border;
|
const border = 0;
|
||||||
const maxY = bottom + border;
|
const minY = top - border;
|
||||||
const minX = left - border;
|
const maxY = bottom + border;
|
||||||
const maxX = right + border;
|
const minX = left - border;
|
||||||
|
const maxX = right + border;
|
||||||
const chunkStartX = Math.floor(minX / globalConfig.mapChunkSize);
|
|
||||||
const chunkStartY = Math.floor(minY / globalConfig.mapChunkSize);
|
const chunkStartX = Math.floor(minX / globalConfig.mapChunkSize);
|
||||||
|
const chunkStartY = Math.floor(minY / globalConfig.mapChunkSize);
|
||||||
const chunkEndX = Math.floor(maxX / globalConfig.mapChunkSize);
|
|
||||||
const chunkEndY = Math.floor(maxY / globalConfig.mapChunkSize);
|
const chunkEndX = Math.floor(maxX / globalConfig.mapChunkSize);
|
||||||
|
const chunkEndY = Math.floor(maxY / globalConfig.mapChunkSize);
|
||||||
// Render y from top down for proper blending
|
|
||||||
for (let chunkX = chunkStartX; chunkX <= chunkEndX; ++chunkX) {
|
// Render y from top down for proper blending
|
||||||
for (let chunkY = chunkStartY; chunkY <= chunkEndY; ++chunkY) {
|
for (let chunkX = chunkStartX; chunkX <= chunkEndX; ++chunkX) {
|
||||||
const chunk = this.root.map.getChunk(chunkX, chunkY, true);
|
for (let chunkY = chunkStartY; chunkY <= chunkEndY; ++chunkY) {
|
||||||
method.call(chunk, parameters);
|
const chunk = this.root.map.getChunk(chunkX, chunkY, true);
|
||||||
}
|
method.call(chunk, parameters);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Draws the wires foreground
|
/**
|
||||||
* @param {DrawParameters} parameters
|
* Draws the wires foreground
|
||||||
*/
|
* @param {DrawParameters} parameters
|
||||||
drawWiresForegroundLayer(parameters) {
|
*/
|
||||||
this.drawVisibleChunks(parameters, MapChunkView.prototype.drawWiresForegroundLayer);
|
drawWiresForegroundLayer(parameters) {
|
||||||
}
|
this.drawVisibleChunks(parameters, MapChunkView.prototype.drawWiresForegroundLayer);
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Draws the map overlay
|
/**
|
||||||
* @param {DrawParameters} parameters
|
* Draws the map overlay
|
||||||
*/
|
* @param {DrawParameters} parameters
|
||||||
drawOverlay(parameters) {
|
*/
|
||||||
this.drawVisibleChunks(parameters, MapChunkView.prototype.drawOverlay);
|
drawOverlay(parameters) {
|
||||||
}
|
this.drawVisibleChunks(parameters, MapChunkView.prototype.drawOverlay);
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Draws the map background
|
/**
|
||||||
* @param {DrawParameters} parameters
|
* Draws the map background
|
||||||
*/
|
* @param {DrawParameters} parameters
|
||||||
drawBackground(parameters) {
|
*/
|
||||||
if (!this.cachedBackgroundPattern) {
|
drawBackground(parameters) {
|
||||||
this.cachedBackgroundPattern = parameters.context.createPattern(
|
if (!this.cachedBackgroundPattern) {
|
||||||
this.cachedBackgroundCanvas,
|
this.cachedBackgroundPattern = parameters.context.createPattern(
|
||||||
"repeat"
|
this.cachedBackgroundCanvas,
|
||||||
);
|
"repeat"
|
||||||
}
|
);
|
||||||
|
}
|
||||||
if (!this.root.app.settings.getAllSettings().disableTileGrid) {
|
|
||||||
const dpi = this.backgroundCacheDPI;
|
if (!this.root.app.settings.getAllSettings().disableTileGrid) {
|
||||||
parameters.context.scale(1 / dpi, 1 / dpi);
|
const dpi = this.backgroundCacheDPI;
|
||||||
|
parameters.context.scale(1 / dpi, 1 / dpi);
|
||||||
parameters.context.fillStyle = this.cachedBackgroundPattern;
|
|
||||||
parameters.context.fillRect(
|
parameters.context.fillStyle = this.cachedBackgroundPattern;
|
||||||
parameters.visibleRect.x * dpi,
|
parameters.context.fillRect(
|
||||||
parameters.visibleRect.y * dpi,
|
parameters.visibleRect.x * dpi,
|
||||||
parameters.visibleRect.w * dpi,
|
parameters.visibleRect.y * dpi,
|
||||||
parameters.visibleRect.h * dpi
|
parameters.visibleRect.w * dpi,
|
||||||
);
|
parameters.visibleRect.h * dpi
|
||||||
parameters.context.scale(dpi, dpi);
|
);
|
||||||
}
|
parameters.context.scale(dpi, dpi);
|
||||||
|
}
|
||||||
this.drawVisibleChunks(parameters, MapChunkView.prototype.drawBackgroundLayer);
|
|
||||||
|
this.drawVisibleChunks(parameters, MapChunkView.prototype.drawBackgroundLayer);
|
||||||
if (G_IS_DEV && globalConfig.debug.showChunkBorders) {
|
|
||||||
const cullRange = parameters.visibleRect.toTileCullRectangle();
|
if (G_IS_DEV && globalConfig.debug.showChunkBorders) {
|
||||||
const top = cullRange.top();
|
const cullRange = parameters.visibleRect.toTileCullRectangle();
|
||||||
const right = cullRange.right();
|
const top = cullRange.top();
|
||||||
const bottom = cullRange.bottom();
|
const right = cullRange.right();
|
||||||
const left = cullRange.left();
|
const bottom = cullRange.bottom();
|
||||||
|
const left = cullRange.left();
|
||||||
const border = 1;
|
|
||||||
const minY = top - border;
|
const border = 1;
|
||||||
const maxY = bottom + border;
|
const minY = top - border;
|
||||||
const minX = left - border;
|
const maxY = bottom + border;
|
||||||
const maxX = right + border - 1;
|
const minX = left - border;
|
||||||
|
const maxX = right + border - 1;
|
||||||
const chunkStartX = Math.floor(minX / globalConfig.mapChunkSize);
|
|
||||||
const chunkStartY = Math.floor(minY / globalConfig.mapChunkSize);
|
const chunkStartX = Math.floor(minX / globalConfig.mapChunkSize);
|
||||||
|
const chunkStartY = Math.floor(minY / globalConfig.mapChunkSize);
|
||||||
const chunkEndX = Math.ceil(maxX / globalConfig.mapChunkSize);
|
|
||||||
const chunkEndY = Math.ceil(maxY / globalConfig.mapChunkSize);
|
const chunkEndX = Math.ceil(maxX / globalConfig.mapChunkSize);
|
||||||
|
const chunkEndY = Math.ceil(maxY / globalConfig.mapChunkSize);
|
||||||
for (let chunkX = chunkStartX; chunkX <= chunkEndX; ++chunkX) {
|
|
||||||
for (let chunkY = chunkStartY; chunkY <= chunkEndY; ++chunkY) {
|
for (let chunkX = chunkStartX; chunkX <= chunkEndX; ++chunkX) {
|
||||||
parameters.context.fillStyle = "#ffaaaa";
|
for (let chunkY = chunkStartY; chunkY <= chunkEndY; ++chunkY) {
|
||||||
parameters.context.fillRect(
|
parameters.context.fillStyle = "#ffaaaa";
|
||||||
chunkX * globalConfig.mapChunkWorldSize,
|
parameters.context.fillRect(
|
||||||
chunkY * globalConfig.mapChunkWorldSize,
|
chunkX * globalConfig.mapChunkWorldSize,
|
||||||
globalConfig.mapChunkWorldSize,
|
chunkY * globalConfig.mapChunkWorldSize,
|
||||||
3
|
globalConfig.mapChunkWorldSize,
|
||||||
);
|
3
|
||||||
parameters.context.fillRect(
|
);
|
||||||
chunkX * globalConfig.mapChunkWorldSize,
|
parameters.context.fillRect(
|
||||||
chunkY * globalConfig.mapChunkWorldSize,
|
chunkX * globalConfig.mapChunkWorldSize,
|
||||||
3,
|
chunkY * globalConfig.mapChunkWorldSize,
|
||||||
globalConfig.mapChunkWorldSize
|
3,
|
||||||
);
|
globalConfig.mapChunkWorldSize
|
||||||
}
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
@ -1,162 +1,173 @@
|
|||||||
import { gMetaBuildingRegistry } from "../core/global_registries";
|
import { gMetaBuildingRegistry } from "../core/global_registries";
|
||||||
import { createLogger } from "../core/logging";
|
import { createLogger } from "../core/logging";
|
||||||
import { MetaBeltBuilding } from "./buildings/belt";
|
import { MetaBeltBuilding } from "./buildings/belt";
|
||||||
import { MetaBeltBaseBuilding } from "./buildings/belt_base";
|
import { MetaBeltBaseBuilding } from "./buildings/belt_base";
|
||||||
import { enumCutterVariants, MetaCutterBuilding } from "./buildings/cutter";
|
import { enumCutterVariants, MetaCutterBuilding } from "./buildings/cutter";
|
||||||
import { MetaHubBuilding } from "./buildings/hub";
|
import { MetaHubBuilding } from "./buildings/hub";
|
||||||
import { enumMinerVariants, MetaMinerBuilding } from "./buildings/miner";
|
import { enumMinerVariants, MetaMinerBuilding } from "./buildings/miner";
|
||||||
import { MetaMixerBuilding } from "./buildings/mixer";
|
import { MetaMixerBuilding } from "./buildings/mixer";
|
||||||
import { enumPainterVariants, MetaPainterBuilding } from "./buildings/painter";
|
import { enumPainterVariants, MetaPainterBuilding } from "./buildings/painter";
|
||||||
import { enumRotaterVariants, MetaRotaterBuilding } from "./buildings/rotater";
|
import { enumRotaterVariants, MetaRotaterBuilding } from "./buildings/rotater";
|
||||||
import { enumSplitterVariants, MetaSplitterBuilding } from "./buildings/splitter";
|
import { enumSplitterVariants, MetaSplitterBuilding } from "./buildings/splitter";
|
||||||
import { MetaStackerBuilding } from "./buildings/stacker";
|
import { MetaStackerBuilding } from "./buildings/stacker";
|
||||||
import { enumTrashVariants, MetaTrashBuilding } from "./buildings/trash";
|
import { enumTrashVariants, MetaTrashBuilding } from "./buildings/trash";
|
||||||
import { enumUndergroundBeltVariants, MetaUndergroundBeltBuilding } from "./buildings/underground_belt";
|
import { enumUndergroundBeltVariants, MetaUndergroundBeltBuilding } from "./buildings/underground_belt";
|
||||||
import { MetaWireBuilding } from "./buildings/wire";
|
import { MetaWireBuilding } from "./buildings/wire";
|
||||||
import { gBuildingVariants, registerBuildingVariant } from "./building_codes";
|
import { gBuildingVariants, registerBuildingVariant } from "./building_codes";
|
||||||
import { defaultBuildingVariant } from "./meta_building";
|
import { defaultBuildingVariant } from "./meta_building";
|
||||||
import { MetaConstantSignalBuilding } from "./buildings/constant_signal";
|
import { MetaConstantSignalBuilding } from "./buildings/constant_signal";
|
||||||
import { MetaLogicGateBuilding, enumLogicGateVariants } from "./buildings/logic_gate";
|
import { MetaLogicGateBuilding, enumLogicGateVariants } from "./buildings/logic_gate";
|
||||||
import { MetaLeverBuilding } from "./buildings/lever";
|
import { MetaLeverBuilding } from "./buildings/lever";
|
||||||
import { MetaFilterBuilding } from "./buildings/filter";
|
import { MetaFilterBuilding } from "./buildings/filter";
|
||||||
import { MetaWireTunnelBuilding, enumWireTunnelVariants } from "./buildings/wire_tunnel";
|
import { MetaWireTunnelBuilding, enumWireTunnelVariants } from "./buildings/wire_tunnel";
|
||||||
import { MetaDisplayBuilding } from "./buildings/display";
|
import { MetaDisplayBuilding } from "./buildings/display";
|
||||||
|
import { MetaVirtualProcessorBuilding, enumVirtualProcessorVariants } from "./buildings/virtual_processor";
|
||||||
const logger = createLogger("building_registry");
|
|
||||||
|
const logger = createLogger("building_registry");
|
||||||
export function initMetaBuildingRegistry() {
|
|
||||||
gMetaBuildingRegistry.register(MetaSplitterBuilding);
|
export function initMetaBuildingRegistry() {
|
||||||
gMetaBuildingRegistry.register(MetaMinerBuilding);
|
gMetaBuildingRegistry.register(MetaSplitterBuilding);
|
||||||
gMetaBuildingRegistry.register(MetaCutterBuilding);
|
gMetaBuildingRegistry.register(MetaMinerBuilding);
|
||||||
gMetaBuildingRegistry.register(MetaRotaterBuilding);
|
gMetaBuildingRegistry.register(MetaCutterBuilding);
|
||||||
gMetaBuildingRegistry.register(MetaStackerBuilding);
|
gMetaBuildingRegistry.register(MetaRotaterBuilding);
|
||||||
gMetaBuildingRegistry.register(MetaMixerBuilding);
|
gMetaBuildingRegistry.register(MetaStackerBuilding);
|
||||||
gMetaBuildingRegistry.register(MetaPainterBuilding);
|
gMetaBuildingRegistry.register(MetaMixerBuilding);
|
||||||
gMetaBuildingRegistry.register(MetaTrashBuilding);
|
gMetaBuildingRegistry.register(MetaPainterBuilding);
|
||||||
gMetaBuildingRegistry.register(MetaBeltBuilding);
|
gMetaBuildingRegistry.register(MetaTrashBuilding);
|
||||||
gMetaBuildingRegistry.register(MetaUndergroundBeltBuilding);
|
gMetaBuildingRegistry.register(MetaBeltBuilding);
|
||||||
gMetaBuildingRegistry.register(MetaHubBuilding);
|
gMetaBuildingRegistry.register(MetaUndergroundBeltBuilding);
|
||||||
gMetaBuildingRegistry.register(MetaWireBuilding);
|
gMetaBuildingRegistry.register(MetaHubBuilding);
|
||||||
gMetaBuildingRegistry.register(MetaConstantSignalBuilding);
|
gMetaBuildingRegistry.register(MetaWireBuilding);
|
||||||
gMetaBuildingRegistry.register(MetaLogicGateBuilding);
|
gMetaBuildingRegistry.register(MetaConstantSignalBuilding);
|
||||||
gMetaBuildingRegistry.register(MetaLeverBuilding);
|
gMetaBuildingRegistry.register(MetaLogicGateBuilding);
|
||||||
gMetaBuildingRegistry.register(MetaFilterBuilding);
|
gMetaBuildingRegistry.register(MetaLeverBuilding);
|
||||||
gMetaBuildingRegistry.register(MetaWireTunnelBuilding);
|
gMetaBuildingRegistry.register(MetaFilterBuilding);
|
||||||
gMetaBuildingRegistry.register(MetaDisplayBuilding);
|
gMetaBuildingRegistry.register(MetaWireTunnelBuilding);
|
||||||
|
gMetaBuildingRegistry.register(MetaDisplayBuilding);
|
||||||
// Belt
|
gMetaBuildingRegistry.register(MetaVirtualProcessorBuilding);
|
||||||
registerBuildingVariant(1, MetaBeltBaseBuilding, defaultBuildingVariant, 0);
|
|
||||||
registerBuildingVariant(2, MetaBeltBaseBuilding, defaultBuildingVariant, 1);
|
// Belt
|
||||||
registerBuildingVariant(3, MetaBeltBaseBuilding, defaultBuildingVariant, 2);
|
registerBuildingVariant(1, MetaBeltBaseBuilding, defaultBuildingVariant, 0);
|
||||||
|
registerBuildingVariant(2, MetaBeltBaseBuilding, defaultBuildingVariant, 1);
|
||||||
// Splitter
|
registerBuildingVariant(3, MetaBeltBaseBuilding, defaultBuildingVariant, 2);
|
||||||
registerBuildingVariant(4, MetaSplitterBuilding);
|
|
||||||
registerBuildingVariant(5, MetaSplitterBuilding, enumSplitterVariants.compact);
|
// Splitter
|
||||||
registerBuildingVariant(6, MetaSplitterBuilding, enumSplitterVariants.compactInverse);
|
registerBuildingVariant(4, MetaSplitterBuilding);
|
||||||
|
registerBuildingVariant(5, MetaSplitterBuilding, enumSplitterVariants.compact);
|
||||||
// Miner
|
registerBuildingVariant(6, MetaSplitterBuilding, enumSplitterVariants.compactInverse);
|
||||||
registerBuildingVariant(7, MetaMinerBuilding);
|
registerBuildingVariant(47, MetaSplitterBuilding, enumSplitterVariants.compactMerge);
|
||||||
registerBuildingVariant(8, MetaMinerBuilding, enumMinerVariants.chainable);
|
registerBuildingVariant(48, MetaSplitterBuilding, enumSplitterVariants.compactMergeInverse);
|
||||||
|
|
||||||
// Cutter
|
// Miner
|
||||||
registerBuildingVariant(9, MetaCutterBuilding);
|
registerBuildingVariant(7, MetaMinerBuilding);
|
||||||
registerBuildingVariant(10, MetaCutterBuilding, enumCutterVariants.quad);
|
registerBuildingVariant(8, MetaMinerBuilding, enumMinerVariants.chainable);
|
||||||
|
|
||||||
// Rotater
|
// Cutter
|
||||||
registerBuildingVariant(11, MetaRotaterBuilding);
|
registerBuildingVariant(9, MetaCutterBuilding);
|
||||||
registerBuildingVariant(12, MetaRotaterBuilding, enumRotaterVariants.ccw);
|
registerBuildingVariant(10, MetaCutterBuilding, enumCutterVariants.quad);
|
||||||
registerBuildingVariant(13, MetaRotaterBuilding, enumRotaterVariants.fl);
|
|
||||||
|
// Rotater
|
||||||
// Stacker
|
registerBuildingVariant(11, MetaRotaterBuilding);
|
||||||
registerBuildingVariant(14, MetaStackerBuilding);
|
registerBuildingVariant(12, MetaRotaterBuilding, enumRotaterVariants.ccw);
|
||||||
|
registerBuildingVariant(13, MetaRotaterBuilding, enumRotaterVariants.fl);
|
||||||
// Mixer
|
|
||||||
registerBuildingVariant(15, MetaMixerBuilding);
|
// Stacker
|
||||||
|
registerBuildingVariant(14, MetaStackerBuilding);
|
||||||
// Painter
|
|
||||||
registerBuildingVariant(16, MetaPainterBuilding);
|
// Mixer
|
||||||
registerBuildingVariant(17, MetaPainterBuilding, enumPainterVariants.mirrored);
|
registerBuildingVariant(15, MetaMixerBuilding);
|
||||||
registerBuildingVariant(18, MetaPainterBuilding, enumPainterVariants.double);
|
|
||||||
registerBuildingVariant(19, MetaPainterBuilding, enumPainterVariants.quad);
|
// Painter
|
||||||
|
registerBuildingVariant(16, MetaPainterBuilding);
|
||||||
// Trash
|
registerBuildingVariant(17, MetaPainterBuilding, enumPainterVariants.mirrored);
|
||||||
registerBuildingVariant(20, MetaTrashBuilding);
|
registerBuildingVariant(18, MetaPainterBuilding, enumPainterVariants.double);
|
||||||
registerBuildingVariant(21, MetaTrashBuilding, enumTrashVariants.storage);
|
registerBuildingVariant(19, MetaPainterBuilding, enumPainterVariants.quad);
|
||||||
|
|
||||||
// Underground belt
|
// Trash
|
||||||
registerBuildingVariant(22, MetaUndergroundBeltBuilding, defaultBuildingVariant, 0);
|
registerBuildingVariant(20, MetaTrashBuilding);
|
||||||
registerBuildingVariant(23, MetaUndergroundBeltBuilding, defaultBuildingVariant, 1);
|
registerBuildingVariant(21, MetaTrashBuilding, enumTrashVariants.storage);
|
||||||
registerBuildingVariant(24, MetaUndergroundBeltBuilding, enumUndergroundBeltVariants.tier2, 0);
|
|
||||||
registerBuildingVariant(25, MetaUndergroundBeltBuilding, enumUndergroundBeltVariants.tier2, 1);
|
// Underground belt
|
||||||
|
registerBuildingVariant(22, MetaUndergroundBeltBuilding, defaultBuildingVariant, 0);
|
||||||
// Hub
|
registerBuildingVariant(23, MetaUndergroundBeltBuilding, defaultBuildingVariant, 1);
|
||||||
registerBuildingVariant(26, MetaHubBuilding);
|
registerBuildingVariant(24, MetaUndergroundBeltBuilding, enumUndergroundBeltVariants.tier2, 0);
|
||||||
|
registerBuildingVariant(25, MetaUndergroundBeltBuilding, enumUndergroundBeltVariants.tier2, 1);
|
||||||
// Wire
|
|
||||||
registerBuildingVariant(27, MetaWireBuilding, defaultBuildingVariant, 0);
|
// Hub
|
||||||
registerBuildingVariant(28, MetaWireBuilding, defaultBuildingVariant, 1);
|
registerBuildingVariant(26, MetaHubBuilding);
|
||||||
registerBuildingVariant(29, MetaWireBuilding, defaultBuildingVariant, 2);
|
|
||||||
registerBuildingVariant(30, MetaWireBuilding, defaultBuildingVariant, 3);
|
// Wire
|
||||||
|
registerBuildingVariant(27, MetaWireBuilding, defaultBuildingVariant, 0);
|
||||||
// Constant signal
|
registerBuildingVariant(28, MetaWireBuilding, defaultBuildingVariant, 1);
|
||||||
registerBuildingVariant(31, MetaConstantSignalBuilding);
|
registerBuildingVariant(29, MetaWireBuilding, defaultBuildingVariant, 2);
|
||||||
|
registerBuildingVariant(30, MetaWireBuilding, defaultBuildingVariant, 3);
|
||||||
// Logic gate
|
|
||||||
registerBuildingVariant(32, MetaLogicGateBuilding);
|
// Constant signal
|
||||||
registerBuildingVariant(34, MetaLogicGateBuilding, enumLogicGateVariants.not);
|
registerBuildingVariant(31, MetaConstantSignalBuilding);
|
||||||
registerBuildingVariant(35, MetaLogicGateBuilding, enumLogicGateVariants.xor);
|
|
||||||
registerBuildingVariant(36, MetaLogicGateBuilding, enumLogicGateVariants.or);
|
// Logic gate
|
||||||
registerBuildingVariant(38, MetaLogicGateBuilding, enumLogicGateVariants.transistor);
|
registerBuildingVariant(32, MetaLogicGateBuilding);
|
||||||
|
registerBuildingVariant(34, MetaLogicGateBuilding, enumLogicGateVariants.not);
|
||||||
// Lever
|
registerBuildingVariant(35, MetaLogicGateBuilding, enumLogicGateVariants.xor);
|
||||||
registerBuildingVariant(33, MetaLeverBuilding);
|
registerBuildingVariant(36, MetaLogicGateBuilding, enumLogicGateVariants.or);
|
||||||
|
registerBuildingVariant(38, MetaLogicGateBuilding, enumLogicGateVariants.transistor);
|
||||||
// Filter
|
|
||||||
registerBuildingVariant(37, MetaFilterBuilding);
|
// Lever
|
||||||
|
registerBuildingVariant(33, MetaLeverBuilding);
|
||||||
// Wire tunnel
|
|
||||||
registerBuildingVariant(39, MetaWireTunnelBuilding);
|
// Filter
|
||||||
registerBuildingVariant(41, MetaWireTunnelBuilding, enumWireTunnelVariants.coating);
|
registerBuildingVariant(37, MetaFilterBuilding);
|
||||||
|
|
||||||
// Display
|
// Wire tunnel
|
||||||
registerBuildingVariant(40, MetaDisplayBuilding);
|
registerBuildingVariant(39, MetaWireTunnelBuilding);
|
||||||
|
registerBuildingVariant(41, MetaWireTunnelBuilding, enumWireTunnelVariants.coating);
|
||||||
// Propagate instances
|
|
||||||
for (const key in gBuildingVariants) {
|
// Display
|
||||||
gBuildingVariants[key].metaInstance = gMetaBuildingRegistry.findByClass(
|
registerBuildingVariant(40, MetaDisplayBuilding);
|
||||||
gBuildingVariants[key].metaClass
|
|
||||||
);
|
// Virtual Processor
|
||||||
}
|
registerBuildingVariant(42, MetaVirtualProcessorBuilding);
|
||||||
|
registerBuildingVariant(43, MetaVirtualProcessorBuilding, enumVirtualProcessorVariants.analyzer);
|
||||||
for (const key in gBuildingVariants) {
|
registerBuildingVariant(44, MetaVirtualProcessorBuilding, enumVirtualProcessorVariants.rotater);
|
||||||
const variant = gBuildingVariants[key];
|
registerBuildingVariant(45, MetaVirtualProcessorBuilding, enumVirtualProcessorVariants.unstacker);
|
||||||
assert(variant.metaClass, "Variant has no meta: " + key);
|
registerBuildingVariant(46, MetaVirtualProcessorBuilding, enumVirtualProcessorVariants.shapecompare);
|
||||||
|
|
||||||
if (typeof variant.rotationVariant === "undefined") {
|
// Propagate instances
|
||||||
variant.rotationVariant = 0;
|
for (const key in gBuildingVariants) {
|
||||||
}
|
gBuildingVariants[key].metaInstance = gMetaBuildingRegistry.findByClass(
|
||||||
if (typeof variant.variant === "undefined") {
|
gBuildingVariants[key].metaClass
|
||||||
variant.variant = defaultBuildingVariant;
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
for (const key in gBuildingVariants) {
|
||||||
logger.log("Registered", gMetaBuildingRegistry.getNumEntries(), "buildings");
|
const variant = gBuildingVariants[key];
|
||||||
logger.log("Registered", Object.keys(gBuildingVariants).length, "building codes");
|
assert(variant.metaClass, "Variant has no meta: " + key);
|
||||||
}
|
|
||||||
|
if (typeof variant.rotationVariant === "undefined") {
|
||||||
/**
|
variant.rotationVariant = 0;
|
||||||
* Once all sprites are loaded, propagates the cache
|
}
|
||||||
*/
|
if (typeof variant.variant === "undefined") {
|
||||||
export function initBuildingCodesAfterResourcesLoaded() {
|
variant.variant = defaultBuildingVariant;
|
||||||
logger.log("Propagating sprite cache");
|
}
|
||||||
for (const key in gBuildingVariants) {
|
}
|
||||||
const variant = gBuildingVariants[key];
|
|
||||||
|
logger.log("Registered", gMetaBuildingRegistry.getNumEntries(), "buildings");
|
||||||
variant.sprite = variant.metaInstance.getSprite(variant.rotationVariant, variant.variant);
|
logger.log("Registered", Object.keys(gBuildingVariants).length, "building codes");
|
||||||
variant.blueprintSprite = variant.metaInstance.getBlueprintSprite(
|
}
|
||||||
variant.rotationVariant,
|
|
||||||
variant.variant
|
/**
|
||||||
);
|
* Once all sprites are loaded, propagates the cache
|
||||||
variant.silhouetteColor = variant.metaInstance.getSilhouetteColor();
|
*/
|
||||||
}
|
export function initBuildingCodesAfterResourcesLoaded() {
|
||||||
}
|
logger.log("Propagating sprite cache");
|
||||||
|
for (const key in gBuildingVariants) {
|
||||||
|
const variant = gBuildingVariants[key];
|
||||||
|
|
||||||
|
variant.sprite = variant.metaInstance.getSprite(variant.rotationVariant, variant.variant);
|
||||||
|
variant.blueprintSprite = variant.metaInstance.getBlueprintSprite(
|
||||||
|
variant.rotationVariant,
|
||||||
|
variant.variant
|
||||||
|
);
|
||||||
|
variant.silhouetteColor = variant.metaInstance.getSilhouetteColor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -85,7 +85,7 @@ export class ShapeDefinition extends BasicSerializableObject {
|
|||||||
return errorCode;
|
return errorCode;
|
||||||
}
|
}
|
||||||
const definition = ShapeDefinition.fromShortKey(data);
|
const definition = ShapeDefinition.fromShortKey(data);
|
||||||
this.layers = definition.layers;
|
this.layers = /** @type {Array<ShapeLayer>} */ (definition.layers);
|
||||||
}
|
}
|
||||||
|
|
||||||
serialize() {
|
serialize() {
|
||||||
@ -102,7 +102,8 @@ export class ShapeDefinition extends BasicSerializableObject {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The layers from bottom to top
|
* The layers from bottom to top
|
||||||
* @type {Array<ShapeLayer>} */
|
* @type {Array<ShapeLayer>}
|
||||||
|
*/
|
||||||
this.layers = layers;
|
this.layers = layers;
|
||||||
|
|
||||||
/** @type {string} */
|
/** @type {string} */
|
||||||
@ -600,7 +601,7 @@ export class ShapeDefinition extends BasicSerializableObject {
|
|||||||
for (let quadrantIndex = 0; quadrantIndex < 4; ++quadrantIndex) {
|
for (let quadrantIndex = 0; quadrantIndex < 4; ++quadrantIndex) {
|
||||||
const item = quadrants[quadrantIndex];
|
const item = quadrants[quadrantIndex];
|
||||||
if (item) {
|
if (item) {
|
||||||
item.color = colors[quadrantIndex] || item.color;
|
item.color = colors[quadrantIndex] || null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,129 +1,135 @@
|
|||||||
import trim from "trim";
|
import trim from "trim";
|
||||||
import { DialogWithForm } from "../../core/modal_dialog_elements";
|
import { DialogWithForm } from "../../core/modal_dialog_elements";
|
||||||
import { FormElementInput } from "../../core/modal_dialog_forms";
|
import { FormElementInput } from "../../core/modal_dialog_forms";
|
||||||
import { BaseItem } from "../base_item";
|
import { BaseItem } from "../base_item";
|
||||||
import { enumColors } from "../colors";
|
import { enumColors } from "../colors";
|
||||||
import { ConstantSignalComponent } from "../components/constant_signal";
|
import { ConstantSignalComponent } from "../components/constant_signal";
|
||||||
import { Entity } from "../entity";
|
import { Entity } from "../entity";
|
||||||
import { GameSystemWithFilter } from "../game_system_with_filter";
|
import { GameSystemWithFilter } from "../game_system_with_filter";
|
||||||
import { BOOL_FALSE_SINGLETON, BOOL_TRUE_SINGLETON } from "../items/boolean_item";
|
import { BOOL_FALSE_SINGLETON, BOOL_TRUE_SINGLETON } from "../items/boolean_item";
|
||||||
import { COLOR_ITEM_SINGLETONS } from "../items/color_item";
|
import { COLOR_ITEM_SINGLETONS } from "../items/color_item";
|
||||||
import { ShapeDefinition } from "../shape_definition";
|
import { ShapeDefinition } from "../shape_definition";
|
||||||
|
|
||||||
export class ConstantSignalSystem extends GameSystemWithFilter {
|
export class ConstantSignalSystem extends GameSystemWithFilter {
|
||||||
constructor(root) {
|
constructor(root) {
|
||||||
super(root, [ConstantSignalComponent]);
|
super(root, [ConstantSignalComponent]);
|
||||||
|
|
||||||
this.root.signals.entityManuallyPlaced.add(this.querySigalValue, this);
|
this.root.signals.entityManuallyPlaced.add(this.querySigalValue, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
update() {
|
update() {
|
||||||
// Set signals
|
// Set signals
|
||||||
for (let i = 0; i < this.allEntities.length; ++i) {
|
for (let i = 0; i < this.allEntities.length; ++i) {
|
||||||
const entity = this.allEntities[i];
|
const entity = this.allEntities[i];
|
||||||
const pinsComp = entity.components.WiredPins;
|
const pinsComp = entity.components.WiredPins;
|
||||||
const signalComp = entity.components.ConstantSignal;
|
const signalComp = entity.components.ConstantSignal;
|
||||||
pinsComp.slots[0].value = signalComp.signal;
|
pinsComp.slots[0].value = signalComp.signal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Asks the entity to enter a valid signal code
|
* Asks the entity to enter a valid signal code
|
||||||
* @param {Entity} entity
|
* @param {Entity} entity
|
||||||
*/
|
*/
|
||||||
querySigalValue(entity) {
|
querySigalValue(entity) {
|
||||||
if (!entity.components.ConstantSignal) {
|
if (!entity.components.ConstantSignal) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ok, query, but also save the uid because it could get stale
|
// Ok, query, but also save the uid because it could get stale
|
||||||
const uid = entity.uid;
|
const uid = entity.uid;
|
||||||
|
|
||||||
const signalValueInput = new FormElementInput({
|
const signalValueInput = new FormElementInput({
|
||||||
id: "signalValue",
|
id: "signalValue",
|
||||||
label: null,
|
label: null,
|
||||||
placeholder: "",
|
placeholder: "",
|
||||||
defaultValue: "",
|
defaultValue: "",
|
||||||
validator: val => this.parseSignalCode(val),
|
validator: val => this.parseSignalCode(val),
|
||||||
});
|
});
|
||||||
const dialog = new DialogWithForm({
|
const dialog = new DialogWithForm({
|
||||||
app: this.root.app,
|
app: this.root.app,
|
||||||
title: "Set Signal",
|
title: "Set Signal",
|
||||||
desc: "Enter a shape code, color or '0' or '1'",
|
desc: "Enter a shape code, color or '0' or '1'",
|
||||||
formElements: [signalValueInput],
|
formElements: [signalValueInput],
|
||||||
buttons: ["cancel:bad:escape", "ok:good:enter"],
|
buttons: ["cancel:bad:escape", "ok:good:enter"],
|
||||||
});
|
closeButton: false,
|
||||||
this.root.hud.parts.dialogs.internalShowDialog(dialog);
|
});
|
||||||
|
this.root.hud.parts.dialogs.internalShowDialog(dialog);
|
||||||
// When confirmed, set the signal
|
|
||||||
dialog.buttonSignals.ok.add(() => {
|
// When confirmed, set the signal
|
||||||
if (!this.root || !this.root.entityMgr) {
|
dialog.buttonSignals.ok.add(() => {
|
||||||
// Game got stopped
|
if (!this.root || !this.root.entityMgr) {
|
||||||
return;
|
// Game got stopped
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
const entityRef = this.root.entityMgr.findByUid(uid, false);
|
|
||||||
if (!entityRef) {
|
const entityRef = this.root.entityMgr.findByUid(uid, false);
|
||||||
// outdated
|
if (!entityRef) {
|
||||||
return;
|
// outdated
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
const constantComp = entityRef.components.ConstantSignal;
|
|
||||||
if (!constantComp) {
|
const constantComp = entityRef.components.ConstantSignal;
|
||||||
// no longer interesting
|
if (!constantComp) {
|
||||||
return;
|
// no longer interesting
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
constantComp.signal = this.parseSignalCode(signalValueInput.getValue());
|
|
||||||
});
|
constantComp.signal = this.parseSignalCode(signalValueInput.getValue());
|
||||||
|
});
|
||||||
// When cancelled, destroy the entity again
|
|
||||||
dialog.buttonSignals.cancel.add(() => {
|
// When cancelled, destroy the entity again
|
||||||
if (!this.root || !this.root.entityMgr) {
|
dialog.buttonSignals.cancel.add(() => {
|
||||||
// Game got stopped
|
if (!this.root || !this.root.entityMgr) {
|
||||||
return;
|
// Game got stopped
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
const entityRef = this.root.entityMgr.findByUid(uid, false);
|
|
||||||
if (!entityRef) {
|
const entityRef = this.root.entityMgr.findByUid(uid, false);
|
||||||
// outdated
|
if (!entityRef) {
|
||||||
return;
|
// outdated
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
const constantComp = entityRef.components.ConstantSignal;
|
|
||||||
if (!constantComp) {
|
const constantComp = entityRef.components.ConstantSignal;
|
||||||
// no longer interesting
|
if (!constantComp) {
|
||||||
return;
|
// no longer interesting
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
this.root.logic.tryDeleteBuilding(entityRef);
|
|
||||||
});
|
this.root.logic.tryDeleteBuilding(entityRef);
|
||||||
}
|
});
|
||||||
|
}
|
||||||
/**
|
|
||||||
* Tries to parse a signal code
|
/**
|
||||||
* @param {string} code
|
* Tries to parse a signal code
|
||||||
* @returns {BaseItem}
|
* @param {string} code
|
||||||
*/
|
* @returns {BaseItem}
|
||||||
parseSignalCode(code) {
|
*/
|
||||||
code = trim(code);
|
parseSignalCode(code) {
|
||||||
const codeLower = code.toLowerCase();
|
if (!this.root || !this.root.shapeDefinitionMgr) {
|
||||||
|
// Stale reference
|
||||||
if (enumColors[codeLower]) {
|
return null;
|
||||||
return COLOR_ITEM_SINGLETONS[codeLower];
|
}
|
||||||
}
|
|
||||||
if (code === "1" || codeLower === "true") {
|
code = trim(code);
|
||||||
return BOOL_TRUE_SINGLETON;
|
const codeLower = code.toLowerCase();
|
||||||
}
|
|
||||||
|
if (enumColors[codeLower]) {
|
||||||
if (code === "0" || codeLower === "false") {
|
return COLOR_ITEM_SINGLETONS[codeLower];
|
||||||
return BOOL_FALSE_SINGLETON;
|
}
|
||||||
}
|
if (code === "1" || codeLower === "true") {
|
||||||
|
return BOOL_TRUE_SINGLETON;
|
||||||
if (ShapeDefinition.isValidShortKey(code)) {
|
}
|
||||||
return this.root.shapeDefinitionMgr.getShapeItemFromShortKey(code);
|
|
||||||
}
|
if (code === "0" || codeLower === "false") {
|
||||||
|
return BOOL_FALSE_SINGLETON;
|
||||||
return null;
|
}
|
||||||
}
|
|
||||||
}
|
if (ShapeDefinition.isValidShortKey(code)) {
|
||||||
|
return this.root.shapeDefinitionMgr.getShapeItemFromShortKey(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,51 +1,44 @@
|
|||||||
import { GameSystemWithFilter } from "../game_system_with_filter";
|
import { GameSystemWithFilter } from "../game_system_with_filter";
|
||||||
import { LeverComponent } from "../components/lever";
|
import { LeverComponent } from "../components/lever";
|
||||||
import { BOOL_TRUE_SINGLETON, BOOL_FALSE_SINGLETON } from "../items/boolean_item";
|
import { BOOL_TRUE_SINGLETON, BOOL_FALSE_SINGLETON } from "../items/boolean_item";
|
||||||
import { MapChunkView } from "../map_chunk_view";
|
import { MapChunkView } from "../map_chunk_view";
|
||||||
import { globalConfig } from "../../core/config";
|
import { globalConfig } from "../../core/config";
|
||||||
import { Loader } from "../../core/loader";
|
import { Loader } from "../../core/loader";
|
||||||
|
|
||||||
export class LeverSystem extends GameSystemWithFilter {
|
export class LeverSystem extends GameSystemWithFilter {
|
||||||
constructor(root) {
|
constructor(root) {
|
||||||
super(root, [LeverComponent]);
|
super(root, [LeverComponent]);
|
||||||
|
|
||||||
this.spriteOn = Loader.getSprite("sprites/wires/lever_on.png");
|
this.spriteOn = Loader.getSprite("sprites/wires/lever_on.png");
|
||||||
this.spriteOff = Loader.getSprite("sprites/buildings/lever.png");
|
this.spriteOff = Loader.getSprite("sprites/buildings/lever.png");
|
||||||
}
|
}
|
||||||
|
|
||||||
update() {
|
update() {
|
||||||
for (let i = 0; i < this.allEntities.length; ++i) {
|
for (let i = 0; i < this.allEntities.length; ++i) {
|
||||||
const entity = this.allEntities[i];
|
const entity = this.allEntities[i];
|
||||||
|
|
||||||
const leverComp = entity.components.Lever;
|
const leverComp = entity.components.Lever;
|
||||||
const pinsComp = entity.components.WiredPins;
|
const pinsComp = entity.components.WiredPins;
|
||||||
|
|
||||||
// Simply sync the status to the first slot
|
// Simply sync the status to the first slot
|
||||||
pinsComp.slots[0].value = leverComp.toggled ? BOOL_TRUE_SINGLETON : BOOL_FALSE_SINGLETON;
|
pinsComp.slots[0].value = leverComp.toggled ? BOOL_TRUE_SINGLETON : BOOL_FALSE_SINGLETON;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draws a given chunk
|
* Draws a given chunk
|
||||||
* @param {import("../../core/draw_utils").DrawParameters} parameters
|
* @param {import("../../core/draw_utils").DrawParameters} parameters
|
||||||
* @param {MapChunkView} chunk
|
* @param {MapChunkView} chunk
|
||||||
*/
|
*/
|
||||||
drawChunk(parameters, chunk) {
|
drawChunk(parameters, chunk) {
|
||||||
const contents = chunk.containedEntitiesByLayer.regular;
|
const contents = chunk.containedEntitiesByLayer.regular;
|
||||||
for (let i = 0; i < contents.length; ++i) {
|
for (let i = 0; i < contents.length; ++i) {
|
||||||
const entity = contents[i];
|
const entity = contents[i];
|
||||||
const leverComp = entity.components.Lever;
|
const leverComp = entity.components.Lever;
|
||||||
if (leverComp) {
|
if (leverComp) {
|
||||||
const sprite = leverComp.toggled ? this.spriteOn : this.spriteOff;
|
const sprite = leverComp.toggled ? this.spriteOn : this.spriteOff;
|
||||||
const origin = entity.components.StaticMapEntity.origin;
|
entity.components.StaticMapEntity.drawSpriteOnBoundsClipped(parameters, sprite);
|
||||||
sprite.drawCached(
|
}
|
||||||
parameters,
|
}
|
||||||
origin.x * globalConfig.tileSize,
|
}
|
||||||
origin.y * globalConfig.tileSize,
|
}
|
||||||
globalConfig.tileSize,
|
|
||||||
globalConfig.tileSize
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,180 +1,326 @@
|
|||||||
import { LogicGateComponent, enumLogicGateType } from "../components/logic_gate";
|
import { BaseItem } from "../base_item";
|
||||||
import { GameSystemWithFilter } from "../game_system_with_filter";
|
import { enumColors } from "../colors";
|
||||||
import { BaseItem } from "../base_item";
|
import { enumLogicGateType, LogicGateComponent } from "../components/logic_gate";
|
||||||
import { enumPinSlotType } from "../components/wired_pins";
|
import { enumPinSlotType } from "../components/wired_pins";
|
||||||
import { BOOL_TRUE_SINGLETON, BOOL_FALSE_SINGLETON, BooleanItem } from "../items/boolean_item";
|
import { GameSystemWithFilter } from "../game_system_with_filter";
|
||||||
import { enumItemProcessorTypes } from "../components/item_processor";
|
import { BOOL_FALSE_SINGLETON, BOOL_TRUE_SINGLETON, BooleanItem } from "../items/boolean_item";
|
||||||
|
import { COLOR_ITEM_SINGLETONS } from "../items/color_item";
|
||||||
export class LogicGateSystem extends GameSystemWithFilter {
|
import { ShapeDefinition } from "../shape_definition";
|
||||||
constructor(root) {
|
import { ShapeItem } from "../items/shape_item";
|
||||||
super(root, [LogicGateComponent]);
|
|
||||||
|
export class LogicGateSystem extends GameSystemWithFilter {
|
||||||
this.boundOperations = {
|
constructor(root) {
|
||||||
[enumLogicGateType.and]: this.compute_AND.bind(this),
|
super(root, [LogicGateComponent]);
|
||||||
[enumLogicGateType.not]: this.compute_NOT.bind(this),
|
|
||||||
[enumLogicGateType.xor]: this.compute_XOR.bind(this),
|
this.boundOperations = {
|
||||||
[enumLogicGateType.or]: this.compute_OR.bind(this),
|
[enumLogicGateType.and]: this.compute_AND.bind(this),
|
||||||
[enumLogicGateType.transistor]: this.compute_IF.bind(this),
|
[enumLogicGateType.not]: this.compute_NOT.bind(this),
|
||||||
};
|
[enumLogicGateType.xor]: this.compute_XOR.bind(this),
|
||||||
}
|
[enumLogicGateType.or]: this.compute_OR.bind(this),
|
||||||
|
[enumLogicGateType.transistor]: this.compute_IF.bind(this),
|
||||||
update() {
|
|
||||||
for (let i = 0; i < this.allEntities.length; ++i) {
|
[enumLogicGateType.rotater]: this.compute_ROTATE.bind(this),
|
||||||
const entity = this.allEntities[i];
|
[enumLogicGateType.analyzer]: this.compute_ANALYZE.bind(this),
|
||||||
const logicComp = entity.components.LogicGate;
|
[enumLogicGateType.cutter]: this.compute_CUT.bind(this),
|
||||||
const slotComp = entity.components.WiredPins;
|
[enumLogicGateType.unstacker]: this.compute_UNSTACK.bind(this),
|
||||||
|
[enumLogicGateType.shapecompare]: this.compute_SHAPECOMPARE.bind(this),
|
||||||
const slotValues = [];
|
};
|
||||||
|
}
|
||||||
for (let i = 0; i < slotComp.slots.length; ++i) {
|
|
||||||
const slot = slotComp.slots[i];
|
update() {
|
||||||
if (slot.type !== enumPinSlotType.logicalAcceptor) {
|
for (let i = 0; i < this.allEntities.length; ++i) {
|
||||||
continue;
|
const entity = this.allEntities[i];
|
||||||
}
|
const logicComp = entity.components.LogicGate;
|
||||||
if (slot.linkedNetwork) {
|
const slotComp = entity.components.WiredPins;
|
||||||
slotValues.push(slot.linkedNetwork.currentValue);
|
|
||||||
} else {
|
const slotValues = [];
|
||||||
slotValues.push(null);
|
|
||||||
}
|
for (let i = 0; i < slotComp.slots.length; ++i) {
|
||||||
}
|
const slot = slotComp.slots[i];
|
||||||
|
if (slot.type !== enumPinSlotType.logicalAcceptor) {
|
||||||
const result = this.boundOperations[logicComp.type](slotValues);
|
continue;
|
||||||
|
}
|
||||||
// @TODO: For now we hardcode the value to always be slot 0
|
if (slot.linkedNetwork) {
|
||||||
assert(
|
slotValues.push(slot.linkedNetwork.currentValue);
|
||||||
slotValues.length === slotComp.slots.length - 1,
|
} else {
|
||||||
"Bad slot config, should have N acceptor slots and 1 ejector"
|
slotValues.push(null);
|
||||||
);
|
}
|
||||||
assert(slotComp.slots[0].type === enumPinSlotType.logicalEjector, "Slot 0 should be ejector");
|
}
|
||||||
|
|
||||||
slotComp.slots[0].value = result;
|
const result = this.boundOperations[logicComp.type](slotValues);
|
||||||
}
|
|
||||||
}
|
if (Array.isArray(result)) {
|
||||||
|
let resultIndex = 0;
|
||||||
/**
|
for (let i = 0; i < slotComp.slots.length; ++i) {
|
||||||
* @param {Array<BaseItem|null>} parameters
|
const slot = slotComp.slots[i];
|
||||||
* @returns {BaseItem}
|
if (slot.type !== enumPinSlotType.logicalEjector) {
|
||||||
*/
|
continue;
|
||||||
compute_AND(parameters) {
|
}
|
||||||
assert(parameters.length === 2, "bad parameter count for AND");
|
slot.value = result[resultIndex++];
|
||||||
|
}
|
||||||
const param1 = parameters[0];
|
} else {
|
||||||
const param2 = parameters[1];
|
// @TODO: For now we hardcode the value to always be slot 0
|
||||||
if (!param1 || !param2) {
|
assert(
|
||||||
// Not enough params
|
slotValues.length === slotComp.slots.length - 1,
|
||||||
return BOOL_FALSE_SINGLETON;
|
"Bad slot config, should have N acceptor slots and 1 ejector"
|
||||||
}
|
);
|
||||||
|
assert(slotComp.slots[0].type === enumPinSlotType.logicalEjector, "Slot 0 should be ejector");
|
||||||
const itemType = param1.getItemType();
|
slotComp.slots[0].value = result;
|
||||||
|
}
|
||||||
if (itemType !== param2.getItemType()) {
|
}
|
||||||
// Differing type
|
}
|
||||||
return BOOL_FALSE_SINGLETON;
|
|
||||||
}
|
/**
|
||||||
|
* @param {Array<BaseItem|null>} parameters
|
||||||
if (itemType === "boolean") {
|
* @returns {BaseItem}
|
||||||
return /** @type {BooleanItem} */ (param1).value && /** @type {BooleanItem} */ (param2).value
|
*/
|
||||||
? BOOL_TRUE_SINGLETON
|
compute_AND(parameters) {
|
||||||
: BOOL_FALSE_SINGLETON;
|
assert(parameters.length === 2, "bad parameter count for AND");
|
||||||
}
|
|
||||||
|
const param1 = parameters[0];
|
||||||
return BOOL_FALSE_SINGLETON;
|
const param2 = parameters[1];
|
||||||
}
|
if (!param1 || !param2) {
|
||||||
|
// Not enough params
|
||||||
/**
|
return BOOL_FALSE_SINGLETON;
|
||||||
* @param {Array<BaseItem|null>} parameters
|
}
|
||||||
* @returns {BaseItem}
|
|
||||||
*/
|
const itemType = param1.getItemType();
|
||||||
compute_NOT(parameters) {
|
|
||||||
const item = parameters[0];
|
if (itemType !== param2.getItemType()) {
|
||||||
if (!item) {
|
// Differing type
|
||||||
return BOOL_TRUE_SINGLETON;
|
return BOOL_FALSE_SINGLETON;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.getItemType() !== "boolean") {
|
if (itemType === "boolean") {
|
||||||
// Not a boolean actually
|
return /** @type {BooleanItem} */ (param1).value && /** @type {BooleanItem} */ (param2).value
|
||||||
return BOOL_FALSE_SINGLETON;
|
? BOOL_TRUE_SINGLETON
|
||||||
}
|
: BOOL_FALSE_SINGLETON;
|
||||||
|
}
|
||||||
const value = /** @type {BooleanItem} */ (item).value;
|
|
||||||
return value ? BOOL_FALSE_SINGLETON : BOOL_TRUE_SINGLETON;
|
return BOOL_FALSE_SINGLETON;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Array<BaseItem|null>} parameters
|
* @param {Array<BaseItem|null>} parameters
|
||||||
* @returns {BaseItem}
|
* @returns {BaseItem}
|
||||||
*/
|
*/
|
||||||
compute_XOR(parameters) {
|
compute_NOT(parameters) {
|
||||||
assert(parameters.length === 2, "bad parameter count for XOR");
|
const item = parameters[0];
|
||||||
|
if (!item) {
|
||||||
const param1 = parameters[0];
|
return BOOL_TRUE_SINGLETON;
|
||||||
const param2 = parameters[1];
|
}
|
||||||
if (!param1 && !param2) {
|
|
||||||
// Not enough params
|
if (item.getItemType() !== "boolean") {
|
||||||
return BOOL_FALSE_SINGLETON;
|
// Not a boolean actually
|
||||||
}
|
return BOOL_FALSE_SINGLETON;
|
||||||
|
}
|
||||||
// Check for the right types
|
|
||||||
if (param1 && param1.getItemType() !== "boolean") {
|
const value = /** @type {BooleanItem} */ (item).value;
|
||||||
return BOOL_FALSE_SINGLETON;
|
return value ? BOOL_FALSE_SINGLETON : BOOL_TRUE_SINGLETON;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (param2 && param2.getItemType() !== "boolean") {
|
/**
|
||||||
return BOOL_FALSE_SINGLETON;
|
* @param {Array<BaseItem|null>} parameters
|
||||||
}
|
* @returns {BaseItem}
|
||||||
|
*/
|
||||||
const valueParam1 = param1 ? /** @type {BooleanItem} */ (param1).value : 0;
|
compute_XOR(parameters) {
|
||||||
const valueParam2 = param2 ? /** @type {BooleanItem} */ (param2).value : 0;
|
assert(parameters.length === 2, "bad parameter count for XOR");
|
||||||
|
|
||||||
return valueParam1 ^ valueParam2 ? BOOL_TRUE_SINGLETON : BOOL_FALSE_SINGLETON;
|
const param1 = parameters[0];
|
||||||
}
|
const param2 = parameters[1];
|
||||||
|
if (!param1 && !param2) {
|
||||||
/**
|
// Not enough params
|
||||||
* @param {Array<BaseItem|null>} parameters
|
return BOOL_FALSE_SINGLETON;
|
||||||
* @returns {BaseItem}
|
}
|
||||||
*/
|
|
||||||
compute_OR(parameters) {
|
// Check for the right types
|
||||||
assert(parameters.length === 2, "bad parameter count for OR");
|
if (param1 && param1.getItemType() !== "boolean") {
|
||||||
|
return BOOL_FALSE_SINGLETON;
|
||||||
const param1 = parameters[0];
|
}
|
||||||
const param2 = parameters[1];
|
|
||||||
if (!param1 && !param2) {
|
if (param2 && param2.getItemType() !== "boolean") {
|
||||||
// Not enough params
|
return BOOL_FALSE_SINGLETON;
|
||||||
return BOOL_FALSE_SINGLETON;
|
}
|
||||||
}
|
|
||||||
|
const valueParam1 = param1 ? /** @type {BooleanItem} */ (param1).value : 0;
|
||||||
const valueParam1 =
|
const valueParam2 = param2 ? /** @type {BooleanItem} */ (param2).value : 0;
|
||||||
param1 && param1.getItemType() === "boolean" ? /** @type {BooleanItem} */ (param1).value : 0;
|
|
||||||
const valueParam2 =
|
return valueParam1 ^ valueParam2 ? BOOL_TRUE_SINGLETON : BOOL_FALSE_SINGLETON;
|
||||||
param2 && param2.getItemType() === "boolean" ? /** @type {BooleanItem} */ (param2).value : 0;
|
}
|
||||||
|
|
||||||
return valueParam1 || valueParam2 ? BOOL_TRUE_SINGLETON : BOOL_FALSE_SINGLETON;
|
/**
|
||||||
}
|
* @param {Array<BaseItem|null>} parameters
|
||||||
|
* @returns {BaseItem}
|
||||||
/**
|
*/
|
||||||
* @param {Array<BaseItem|null>} parameters
|
compute_OR(parameters) {
|
||||||
* @returns {BaseItem}
|
assert(parameters.length === 2, "bad parameter count for OR");
|
||||||
*/
|
|
||||||
compute_IF(parameters) {
|
const param1 = parameters[0];
|
||||||
assert(parameters.length === 2, "bad parameter count for IF");
|
const param2 = parameters[1];
|
||||||
|
if (!param1 && !param2) {
|
||||||
const flag = parameters[0];
|
// Not enough params
|
||||||
const value = parameters[1];
|
return BOOL_FALSE_SINGLETON;
|
||||||
if (!flag || !value) {
|
}
|
||||||
// Not enough params
|
|
||||||
return null;
|
const valueParam1 =
|
||||||
}
|
param1 && param1.getItemType() === "boolean" ? /** @type {BooleanItem} */ (param1).value : 0;
|
||||||
|
const valueParam2 =
|
||||||
if (flag.getItemType() !== "boolean") {
|
param2 && param2.getItemType() === "boolean" ? /** @type {BooleanItem} */ (param2).value : 0;
|
||||||
// Flag is not a boolean
|
|
||||||
return null;
|
return valueParam1 || valueParam2 ? BOOL_TRUE_SINGLETON : BOOL_FALSE_SINGLETON;
|
||||||
}
|
}
|
||||||
|
|
||||||
// pass through item
|
/**
|
||||||
if (/** @type {BooleanItem} */ (flag).value) {
|
* @param {Array<BaseItem|null>} parameters
|
||||||
return value;
|
* @returns {BaseItem}
|
||||||
}
|
*/
|
||||||
|
compute_IF(parameters) {
|
||||||
return null;
|
assert(parameters.length === 2, "bad parameter count for IF");
|
||||||
}
|
|
||||||
}
|
const flag = parameters[0];
|
||||||
|
const value = parameters[1];
|
||||||
|
if (!flag || !value) {
|
||||||
|
// Not enough params
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flag.getItemType() !== "boolean") {
|
||||||
|
// Flag is not a boolean
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// pass through item
|
||||||
|
if (/** @type {BooleanItem} */ (flag).value) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Array<BaseItem|null>} parameters
|
||||||
|
* @returns {BaseItem}
|
||||||
|
*/
|
||||||
|
compute_ROTATE(parameters) {
|
||||||
|
const item = parameters[0];
|
||||||
|
if (!item || item.getItemType() !== "shape") {
|
||||||
|
// Not a shape
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const definition = /** @type {ShapeItem} */ (item).definition;
|
||||||
|
const rotatedDefinition = this.root.shapeDefinitionMgr.shapeActionRotateCW(definition);
|
||||||
|
return this.root.shapeDefinitionMgr.getShapeItemFromDefinition(rotatedDefinition);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Array<BaseItem|null>} parameters
|
||||||
|
* @returns {[BaseItem, BaseItem]}
|
||||||
|
*/
|
||||||
|
compute_ANALYZE(parameters) {
|
||||||
|
const item = parameters[0];
|
||||||
|
if (!item || item.getItemType() !== "shape") {
|
||||||
|
// Not a shape
|
||||||
|
return [null, null];
|
||||||
|
}
|
||||||
|
|
||||||
|
const definition = /** @type {ShapeItem} */ (item).definition;
|
||||||
|
const lowerLayer = /** @type {import("../shape_definition").ShapeLayer} */ (definition.layers[0]);
|
||||||
|
if (!lowerLayer) {
|
||||||
|
return [null, null];
|
||||||
|
}
|
||||||
|
|
||||||
|
const topRightContent = lowerLayer[0];
|
||||||
|
|
||||||
|
if (!topRightContent || topRightContent.subShape === null) {
|
||||||
|
return [null, null];
|
||||||
|
}
|
||||||
|
|
||||||
|
const newDefinition = new ShapeDefinition({
|
||||||
|
layers: [
|
||||||
|
[
|
||||||
|
{ subShape: topRightContent.subShape, color: enumColors.uncolored },
|
||||||
|
{ subShape: topRightContent.subShape, color: enumColors.uncolored },
|
||||||
|
{ subShape: topRightContent.subShape, color: enumColors.uncolored },
|
||||||
|
{ subShape: topRightContent.subShape, color: enumColors.uncolored },
|
||||||
|
],
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
return [
|
||||||
|
COLOR_ITEM_SINGLETONS[topRightContent.color],
|
||||||
|
this.root.shapeDefinitionMgr.getShapeItemFromDefinition(newDefinition),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Array<BaseItem|null>} parameters
|
||||||
|
* @returns {[BaseItem, BaseItem]}
|
||||||
|
*/
|
||||||
|
compute_CUT(parameters) {
|
||||||
|
const item = parameters[0];
|
||||||
|
if (!item || item.getItemType() !== "shape") {
|
||||||
|
// Not a shape
|
||||||
|
return [null, null];
|
||||||
|
}
|
||||||
|
|
||||||
|
const definition = /** @type {ShapeItem} */ (item).definition;
|
||||||
|
const result = this.root.shapeDefinitionMgr.shapeActionCutHalf(definition);
|
||||||
|
return [
|
||||||
|
result[0].isEntirelyEmpty()
|
||||||
|
? null
|
||||||
|
: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(result[0]),
|
||||||
|
result[1].isEntirelyEmpty()
|
||||||
|
? null
|
||||||
|
: this.root.shapeDefinitionMgr.getShapeItemFromDefinition(result[1]),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Array<BaseItem|null>} parameters
|
||||||
|
* @returns {[BaseItem, BaseItem]}
|
||||||
|
*/
|
||||||
|
compute_UNSTACK(parameters) {
|
||||||
|
const item = parameters[0];
|
||||||
|
if (!item || item.getItemType() !== "shape") {
|
||||||
|
// Not a shape
|
||||||
|
return [null, null];
|
||||||
|
}
|
||||||
|
|
||||||
|
const definition = /** @type {ShapeItem} */ (item).definition;
|
||||||
|
const layers = /** @type {Array<import("../shape_definition").ShapeLayer>} */ (definition.layers);
|
||||||
|
|
||||||
|
const upperLayerDefinition = new ShapeDefinition({
|
||||||
|
layers: [layers[layers.length - 1]],
|
||||||
|
});
|
||||||
|
|
||||||
|
const lowerLayers = layers.slice(0, layers.length - 1);
|
||||||
|
const lowerLayerDefinition =
|
||||||
|
lowerLayers.length > 0 ? new ShapeDefinition({ layers: lowerLayers }) : null;
|
||||||
|
|
||||||
|
return [
|
||||||
|
lowerLayerDefinition
|
||||||
|
? this.root.shapeDefinitionMgr.getShapeItemFromDefinition(lowerLayerDefinition)
|
||||||
|
: null,
|
||||||
|
this.root.shapeDefinitionMgr.getShapeItemFromDefinition(upperLayerDefinition),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Array<BaseItem|null>} parameters
|
||||||
|
* @returns {BaseItem}
|
||||||
|
*/
|
||||||
|
compute_SHAPECOMPARE(parameters) {
|
||||||
|
const itemA = parameters[0];
|
||||||
|
const itemB = parameters[1];
|
||||||
|
|
||||||
|
return itemA &&
|
||||||
|
itemB &&
|
||||||
|
itemA.getItemType() === "shape" &&
|
||||||
|
itemB.getItemType() === "shape" &&
|
||||||
|
/** @type {ShapeItem} */ (itemA).definition.getHash() ===
|
||||||
|
/** @type {ShapeItem} */ (itemB).definition.getHash()
|
||||||
|
? BOOL_TRUE_SINGLETON
|
||||||
|
: BOOL_FALSE_SINGLETON;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,179 +1,182 @@
|
|||||||
import { ShapeDefinition } from "./shape_definition";
|
import { ShapeDefinition } from "./shape_definition";
|
||||||
import { finalGameShape } from "./upgrades";
|
import { finalGameShape } from "./upgrades";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Don't forget to also update tutorial_goals_mappings.js as well as the translations!
|
* Don't forget to also update tutorial_goals_mappings.js as well as the translations!
|
||||||
* @enum {string}
|
* @enum {string}
|
||||||
*/
|
*/
|
||||||
export const enumHubGoalRewards = {
|
export const enumHubGoalRewards = {
|
||||||
reward_cutter_and_trash: "reward_cutter_and_trash",
|
reward_cutter_and_trash: "reward_cutter_and_trash",
|
||||||
reward_rotater: "reward_rotater",
|
reward_rotater: "reward_rotater",
|
||||||
reward_painter: "reward_painter",
|
reward_painter: "reward_painter",
|
||||||
reward_mixer: "reward_mixer",
|
reward_mixer: "reward_mixer",
|
||||||
reward_stacker: "reward_stacker",
|
reward_stacker: "reward_stacker",
|
||||||
reward_splitter: "reward_splitter",
|
reward_splitter: "reward_splitter",
|
||||||
reward_tunnel: "reward_tunnel",
|
reward_tunnel: "reward_tunnel",
|
||||||
|
|
||||||
reward_rotater_ccw: "reward_rotater_ccw",
|
reward_rotater_ccw: "reward_rotater_ccw",
|
||||||
reward_rotater_fl: "reward_rotater_fl",
|
reward_rotater_fl: "reward_rotater_fl",
|
||||||
reward_miner_chainable: "reward_miner_chainable",
|
reward_miner_chainable: "reward_miner_chainable",
|
||||||
reward_underground_belt_tier_2: "reward_underground_belt_tier_2",
|
reward_underground_belt_tier_2: "reward_underground_belt_tier_2",
|
||||||
reward_splitter_compact: "reward_splitter_compact",
|
reward_splitter_compact: "reward_splitter_compact",
|
||||||
reward_cutter_quad: "reward_cutter_quad",
|
reward_cutter_quad: "reward_cutter_quad",
|
||||||
reward_painter_double: "reward_painter_double",
|
reward_painter_double: "reward_painter_double",
|
||||||
reward_painter_quad: "reward_painter_quad",
|
reward_painter_quad: "reward_painter_quad",
|
||||||
reward_storage: "reward_storage",
|
reward_storage: "reward_storage",
|
||||||
|
|
||||||
reward_blueprints: "reward_blueprints",
|
// @todo: unlock
|
||||||
reward_freeplay: "reward_freeplay",
|
reward_merger_compact: "reward_compact_merger",
|
||||||
|
|
||||||
no_reward: "no_reward",
|
reward_blueprints: "reward_blueprints",
|
||||||
no_reward_freeplay: "no_reward_freeplay",
|
reward_freeplay: "reward_freeplay",
|
||||||
};
|
|
||||||
|
no_reward: "no_reward",
|
||||||
export const tutorialGoals = [
|
no_reward_freeplay: "no_reward_freeplay",
|
||||||
// 1
|
};
|
||||||
// Circle
|
|
||||||
{
|
export const tutorialGoals = [
|
||||||
shape: "CuCuCuCu", // belts t1
|
// 1
|
||||||
required: 40,
|
// Circle
|
||||||
reward: enumHubGoalRewards.reward_cutter_and_trash,
|
{
|
||||||
},
|
shape: "CuCuCuCu", // belts t1
|
||||||
|
required: 40,
|
||||||
// 2
|
reward: enumHubGoalRewards.reward_cutter_and_trash,
|
||||||
// Cutter
|
},
|
||||||
{
|
|
||||||
shape: "----CuCu", //
|
// 2
|
||||||
required: 40,
|
// Cutter
|
||||||
reward: enumHubGoalRewards.no_reward,
|
{
|
||||||
},
|
shape: "----CuCu", //
|
||||||
|
required: 40,
|
||||||
// 3
|
reward: enumHubGoalRewards.no_reward,
|
||||||
// Rectangle
|
},
|
||||||
{
|
|
||||||
shape: "RuRuRuRu", // miners t1
|
// 3
|
||||||
required: 100,
|
// Rectangle
|
||||||
reward: enumHubGoalRewards.reward_splitter,
|
{
|
||||||
},
|
shape: "RuRuRuRu", // miners t1
|
||||||
|
required: 100,
|
||||||
// 4
|
reward: enumHubGoalRewards.reward_splitter,
|
||||||
{
|
},
|
||||||
shape: "RuRu----", // processors t2
|
|
||||||
required: 120,
|
// 4
|
||||||
reward: enumHubGoalRewards.reward_rotater,
|
{
|
||||||
},
|
shape: "RuRu----", // processors t2
|
||||||
|
required: 120,
|
||||||
// 5
|
reward: enumHubGoalRewards.reward_rotater,
|
||||||
// Rotater
|
},
|
||||||
{
|
|
||||||
shape: "Cu----Cu", // belts t2
|
// 5
|
||||||
required: 200,
|
// Rotater
|
||||||
reward: enumHubGoalRewards.reward_tunnel,
|
{
|
||||||
},
|
shape: "Cu----Cu", // belts t2
|
||||||
|
required: 200,
|
||||||
// 6
|
reward: enumHubGoalRewards.reward_tunnel,
|
||||||
{
|
},
|
||||||
shape: "Cu------", // miners t2
|
|
||||||
required: 400,
|
// 6
|
||||||
reward: enumHubGoalRewards.reward_painter,
|
{
|
||||||
},
|
shape: "Cu------", // miners t2
|
||||||
|
required: 400,
|
||||||
// 7
|
reward: enumHubGoalRewards.reward_painter,
|
||||||
// Painter
|
},
|
||||||
{
|
|
||||||
shape: "CrCrCrCr", // unused
|
// 7
|
||||||
required: 800,
|
// Painter
|
||||||
reward: enumHubGoalRewards.reward_rotater_ccw,
|
{
|
||||||
},
|
shape: "CrCrCrCr", // unused
|
||||||
|
required: 800,
|
||||||
// 8
|
reward: enumHubGoalRewards.reward_rotater_ccw,
|
||||||
{
|
},
|
||||||
shape: "RbRb----", // painter t2
|
|
||||||
required: 1000,
|
// 8
|
||||||
reward: enumHubGoalRewards.reward_mixer,
|
{
|
||||||
},
|
shape: "RbRb----", // painter t2
|
||||||
|
required: 1000,
|
||||||
// 9
|
reward: enumHubGoalRewards.reward_mixer,
|
||||||
// Mixing (purple)
|
},
|
||||||
{
|
|
||||||
shape: "CpCpCpCp", // belts t3
|
// 9
|
||||||
required: 1400,
|
// Mixing (purple)
|
||||||
reward: enumHubGoalRewards.reward_splitter_compact,
|
{
|
||||||
},
|
shape: "CpCpCpCp", // belts t3
|
||||||
|
required: 1400,
|
||||||
// 10
|
reward: enumHubGoalRewards.reward_splitter_compact,
|
||||||
// Star shape + cyan
|
},
|
||||||
{
|
|
||||||
shape: "ScScScSc", // miners t3
|
// 10
|
||||||
required: 1600,
|
// Star shape + cyan
|
||||||
reward: enumHubGoalRewards.reward_stacker,
|
{
|
||||||
},
|
shape: "ScScScSc", // miners t3
|
||||||
|
required: 1600,
|
||||||
// 11
|
reward: enumHubGoalRewards.reward_stacker,
|
||||||
// Stacker
|
},
|
||||||
{
|
|
||||||
shape: "CgScScCg", // processors t3
|
// 11
|
||||||
required: 1800,
|
// Stacker
|
||||||
reward: enumHubGoalRewards.reward_miner_chainable,
|
{
|
||||||
},
|
shape: "CgScScCg", // processors t3
|
||||||
|
required: 1800,
|
||||||
// 12
|
reward: enumHubGoalRewards.reward_miner_chainable,
|
||||||
// Blueprints
|
},
|
||||||
{
|
|
||||||
shape: "CbCbCbRb:CwCwCwCw",
|
// 12
|
||||||
required: 2000,
|
// Blueprints
|
||||||
reward: enumHubGoalRewards.reward_blueprints,
|
{
|
||||||
},
|
shape: "CbCbCbRb:CwCwCwCw",
|
||||||
|
required: 2000,
|
||||||
// 13
|
reward: enumHubGoalRewards.reward_blueprints,
|
||||||
{
|
},
|
||||||
shape: "RpRpRpRp:CwCwCwCw", // painting t3
|
|
||||||
required: 12000,
|
// 13
|
||||||
reward: enumHubGoalRewards.reward_underground_belt_tier_2,
|
{
|
||||||
},
|
shape: "RpRpRpRp:CwCwCwCw", // painting t3
|
||||||
|
required: 12000,
|
||||||
// 14
|
reward: enumHubGoalRewards.reward_underground_belt_tier_2,
|
||||||
{
|
},
|
||||||
shape: "SrSrSrSr:CyCyCyCy", // unused
|
|
||||||
required: 16000,
|
// 14
|
||||||
reward: enumHubGoalRewards.reward_storage,
|
{
|
||||||
},
|
shape: "SrSrSrSr:CyCyCyCy", // unused
|
||||||
|
required: 16000,
|
||||||
// 15
|
reward: enumHubGoalRewards.reward_storage,
|
||||||
{
|
},
|
||||||
shape: "SrSrSrSr:CyCyCyCy:SwSwSwSw", // belts t4 (two variants)
|
|
||||||
required: 25000,
|
// 15
|
||||||
reward: enumHubGoalRewards.reward_cutter_quad,
|
{
|
||||||
},
|
shape: "SrSrSrSr:CyCyCyCy:SwSwSwSw", // belts t4 (two variants)
|
||||||
|
required: 25000,
|
||||||
// 16
|
reward: enumHubGoalRewards.reward_cutter_quad,
|
||||||
{
|
},
|
||||||
shape: "CbRbRbCb:CwCwCwCw:WbWbWbWb", // miner t4 (two variants)
|
|
||||||
required: 50000,
|
// 16
|
||||||
reward: enumHubGoalRewards.reward_painter_double,
|
{
|
||||||
},
|
shape: "CbRbRbCb:CwCwCwCw:WbWbWbWb", // miner t4 (two variants)
|
||||||
|
required: 50000,
|
||||||
// 17
|
reward: enumHubGoalRewards.reward_painter_double,
|
||||||
{
|
},
|
||||||
shape: "WrRgWrRg:CwCrCwCr:SgSgSgSg", // processors t4 (two variants)
|
|
||||||
required: 120000,
|
// 17
|
||||||
reward: enumHubGoalRewards.reward_painter_quad,
|
{
|
||||||
},
|
shape: "WrRgWrRg:CwCrCwCr:SgSgSgSg", // processors t4 (two variants)
|
||||||
|
required: 120000,
|
||||||
// 18
|
reward: enumHubGoalRewards.reward_painter_quad,
|
||||||
{
|
},
|
||||||
shape: finalGameShape,
|
|
||||||
required: 250000,
|
// 18
|
||||||
reward: enumHubGoalRewards.reward_freeplay,
|
{
|
||||||
},
|
shape: finalGameShape,
|
||||||
];
|
required: 250000,
|
||||||
|
reward: enumHubGoalRewards.reward_freeplay,
|
||||||
if (G_IS_DEV) {
|
},
|
||||||
tutorialGoals.forEach(({ shape }) => {
|
];
|
||||||
try {
|
|
||||||
ShapeDefinition.fromShortKey(shape);
|
if (G_IS_DEV) {
|
||||||
} catch (ex) {
|
tutorialGoals.forEach(({ shape }) => {
|
||||||
throw new Error("Invalid tutorial goal: '" + ex + "' for shape" + shape);
|
try {
|
||||||
}
|
ShapeDefinition.fromShortKey(shape);
|
||||||
});
|
} catch (ex) {
|
||||||
}
|
throw new Error("Invalid tutorial goal: '" + ex + "' for shape" + shape);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
@ -1,218 +1,225 @@
|
|||||||
import { ExplainedResult } from "../core/explained_result";
|
import { ExplainedResult } from "../core/explained_result";
|
||||||
import { createLogger } from "../core/logging";
|
import { createLogger } from "../core/logging";
|
||||||
import { ReadWriteProxy } from "../core/read_write_proxy";
|
import { ReadWriteProxy } from "../core/read_write_proxy";
|
||||||
import { globalConfig } from "../core/config";
|
import { globalConfig } from "../core/config";
|
||||||
import { Savegame } from "./savegame";
|
import { Savegame } from "./savegame";
|
||||||
const logger = createLogger("savegame_manager");
|
const logger = createLogger("savegame_manager");
|
||||||
|
|
||||||
const Rusha = require("rusha");
|
const Rusha = require("rusha");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {import("./savegame_typedefs").SavegamesData} SavegamesData
|
* @typedef {import("./savegame_typedefs").SavegamesData} SavegamesData
|
||||||
* @typedef {import("./savegame_typedefs").SavegameMetadata} SavegameMetadata
|
* @typedef {import("./savegame_typedefs").SavegameMetadata} SavegameMetadata
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** @enum {string} */
|
/** @enum {string} */
|
||||||
export const enumLocalSavegameStatus = {
|
export const enumLocalSavegameStatus = {
|
||||||
offline: "offline",
|
offline: "offline",
|
||||||
synced: "synced",
|
synced: "synced",
|
||||||
};
|
};
|
||||||
|
|
||||||
export class SavegameManager extends ReadWriteProxy {
|
export class SavegameManager extends ReadWriteProxy {
|
||||||
constructor(app) {
|
constructor(app) {
|
||||||
super(app, "savegames.bin");
|
super(app, "savegames.bin");
|
||||||
|
|
||||||
this.currentData = this.getDefaultData();
|
this.currentData = this.getDefaultData();
|
||||||
}
|
}
|
||||||
|
|
||||||
// RW Proxy Impl
|
// RW Proxy Impl
|
||||||
/**
|
/**
|
||||||
* @returns {SavegamesData}
|
* @returns {SavegamesData}
|
||||||
*/
|
*/
|
||||||
getDefaultData() {
|
getDefaultData() {
|
||||||
return {
|
return {
|
||||||
version: this.getCurrentVersion(),
|
version: this.getCurrentVersion(),
|
||||||
savegames: [],
|
savegames: [],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
getCurrentVersion() {
|
getCurrentVersion() {
|
||||||
return 1001;
|
return 1002;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns {SavegamesData}
|
* @returns {SavegamesData}
|
||||||
*/
|
*/
|
||||||
getCurrentData() {
|
getCurrentData() {
|
||||||
return super.getCurrentData();
|
return super.getCurrentData();
|
||||||
}
|
}
|
||||||
|
|
||||||
verify(data) {
|
verify(data) {
|
||||||
// TODO / FIXME!!!!
|
// TODO / FIXME!!!!
|
||||||
return ExplainedResult.good();
|
return ExplainedResult.good();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {SavegamesData} data
|
* @param {SavegamesData} data
|
||||||
*/
|
*/
|
||||||
migrate(data) {
|
migrate(data) {
|
||||||
if (data.version < 1001) {
|
if (data.version < 1001) {
|
||||||
data.savegames.forEach(savegame => {
|
data.savegames.forEach(savegame => {
|
||||||
savegame.level = 0;
|
savegame.level = 0;
|
||||||
});
|
});
|
||||||
data.version = 1001;
|
data.version = 1001;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ExplainedResult.good();
|
if (data.version < 1002) {
|
||||||
}
|
data.savegames.forEach(savegame => {
|
||||||
|
savegame.name = null;
|
||||||
// End rw proxy
|
});
|
||||||
|
data.version = 1002;
|
||||||
/**
|
}
|
||||||
* @returns {Array<SavegameMetadata>}
|
|
||||||
*/
|
return ExplainedResult.good();
|
||||||
getSavegamesMetaData() {
|
}
|
||||||
return this.currentData.savegames;
|
|
||||||
}
|
// End rw proxy
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* @returns {Array<SavegameMetadata>}
|
||||||
* @param {string} internalId
|
*/
|
||||||
* @returns {Savegame}
|
getSavegamesMetaData() {
|
||||||
*/
|
return this.currentData.savegames;
|
||||||
getSavegameById(internalId) {
|
}
|
||||||
const metadata = this.getGameMetaDataByInternalId(internalId);
|
|
||||||
if (!metadata) {
|
/**
|
||||||
return null;
|
*
|
||||||
}
|
* @param {string} internalId
|
||||||
return new Savegame(this.app, { internalId, metaDataRef: metadata });
|
* @returns {Savegame}
|
||||||
}
|
*/
|
||||||
|
getSavegameById(internalId) {
|
||||||
/**
|
const metadata = this.getGameMetaDataByInternalId(internalId);
|
||||||
* Deletes a savegame
|
if (!metadata) {
|
||||||
* @param {SavegameMetadata} game
|
return null;
|
||||||
*/
|
}
|
||||||
deleteSavegame(game) {
|
return new Savegame(this.app, { internalId, metaDataRef: metadata });
|
||||||
const handle = new Savegame(this.app, {
|
}
|
||||||
internalId: game.internalId,
|
|
||||||
metaDataRef: game,
|
/**
|
||||||
});
|
* Deletes a savegame
|
||||||
|
* @param {SavegameMetadata} game
|
||||||
return handle.deleteAsync().then(() => {
|
*/
|
||||||
for (let i = 0; i < this.currentData.savegames.length; ++i) {
|
deleteSavegame(game) {
|
||||||
const potentialGame = this.currentData.savegames[i];
|
const handle = new Savegame(this.app, {
|
||||||
if (potentialGame.internalId === handle.internalId) {
|
internalId: game.internalId,
|
||||||
this.currentData.savegames.splice(i, 1);
|
metaDataRef: game,
|
||||||
break;
|
});
|
||||||
}
|
|
||||||
}
|
return handle.deleteAsync().then(() => {
|
||||||
|
for (let i = 0; i < this.currentData.savegames.length; ++i) {
|
||||||
return this.writeAsync();
|
const potentialGame = this.currentData.savegames[i];
|
||||||
});
|
if (potentialGame.internalId === handle.internalId) {
|
||||||
}
|
this.currentData.savegames.splice(i, 1);
|
||||||
|
break;
|
||||||
/**
|
}
|
||||||
* Returns a given games metadata by id
|
}
|
||||||
* @param {string} id
|
|
||||||
* @returns {SavegameMetadata}
|
return this.writeAsync();
|
||||||
*/
|
});
|
||||||
getGameMetaDataByInternalId(id) {
|
}
|
||||||
for (let i = 0; i < this.currentData.savegames.length; ++i) {
|
|
||||||
const data = this.currentData.savegames[i];
|
/**
|
||||||
if (data.internalId === id) {
|
* Returns a given games metadata by id
|
||||||
return data;
|
* @param {string} id
|
||||||
}
|
* @returns {SavegameMetadata}
|
||||||
}
|
*/
|
||||||
logger.error("Savegame internal id not found:", id);
|
getGameMetaDataByInternalId(id) {
|
||||||
return null;
|
for (let i = 0; i < this.currentData.savegames.length; ++i) {
|
||||||
}
|
const data = this.currentData.savegames[i];
|
||||||
|
if (data.internalId === id) {
|
||||||
/**
|
return data;
|
||||||
* Creates a new savegame
|
}
|
||||||
* @returns {Savegame}
|
}
|
||||||
*/
|
logger.error("Savegame internal id not found:", id);
|
||||||
createNewSavegame() {
|
return null;
|
||||||
const id = this.generateInternalId();
|
}
|
||||||
|
|
||||||
const metaData = /** @type {SavegameMetadata} */ ({
|
/**
|
||||||
lastUpdate: Date.now(),
|
* Creates a new savegame
|
||||||
version: Savegame.getCurrentVersion(),
|
* @returns {Savegame}
|
||||||
internalId: id,
|
*/
|
||||||
});
|
createNewSavegame() {
|
||||||
|
const id = this.generateInternalId();
|
||||||
this.currentData.savegames.push(metaData);
|
|
||||||
this.sortSavegames();
|
const metaData = /** @type {SavegameMetadata} */ ({
|
||||||
|
lastUpdate: Date.now(),
|
||||||
return new Savegame(this.app, {
|
version: Savegame.getCurrentVersion(),
|
||||||
internalId: id,
|
internalId: id,
|
||||||
metaDataRef: metaData,
|
});
|
||||||
});
|
|
||||||
}
|
this.currentData.savegames.push(metaData);
|
||||||
|
this.sortSavegames();
|
||||||
importSavegame(data) {
|
|
||||||
const savegame = this.createNewSavegame();
|
return new Savegame(this.app, {
|
||||||
const migrationResult = savegame.migrate(data);
|
internalId: id,
|
||||||
if (migrationResult.isBad()) {
|
metaDataRef: metaData,
|
||||||
return Promise.reject("Failed to migrate: " + migrationResult.reason);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
savegame.currentData = data;
|
importSavegame(data) {
|
||||||
const verification = savegame.verify(data);
|
const savegame = this.createNewSavegame();
|
||||||
if (verification.isBad()) {
|
const migrationResult = savegame.migrate(data);
|
||||||
return Promise.reject("Verification failed: " + verification.result);
|
if (migrationResult.isBad()) {
|
||||||
}
|
return Promise.reject("Failed to migrate: " + migrationResult.reason);
|
||||||
|
}
|
||||||
return savegame.writeSavegameAndMetadata().then(() => this.sortSavegames());
|
|
||||||
}
|
savegame.currentData = data;
|
||||||
|
const verification = savegame.verify(data);
|
||||||
/**
|
if (verification.isBad()) {
|
||||||
* Sorts all savegames by their creation time descending
|
return Promise.reject("Verification failed: " + verification.result);
|
||||||
* @returns {Promise<any>}
|
}
|
||||||
*/
|
|
||||||
sortSavegames() {
|
return savegame.writeSavegameAndMetadata().then(() => this.sortSavegames());
|
||||||
this.currentData.savegames.sort((a, b) => b.lastUpdate - a.lastUpdate);
|
}
|
||||||
let promiseChain = Promise.resolve();
|
|
||||||
while (this.currentData.savegames.length > 30) {
|
/**
|
||||||
const toRemove = this.currentData.savegames.pop();
|
* Sorts all savegames by their creation time descending
|
||||||
|
* @returns {Promise<any>}
|
||||||
// Try to remove the savegame since its no longer available
|
*/
|
||||||
const game = new Savegame(this.app, {
|
sortSavegames() {
|
||||||
internalId: toRemove.internalId,
|
this.currentData.savegames.sort((a, b) => b.lastUpdate - a.lastUpdate);
|
||||||
metaDataRef: toRemove,
|
let promiseChain = Promise.resolve();
|
||||||
});
|
while (this.currentData.savegames.length > 30) {
|
||||||
promiseChain = promiseChain
|
const toRemove = this.currentData.savegames.pop();
|
||||||
.then(() => game.deleteAsync())
|
|
||||||
.then(
|
// Try to remove the savegame since its no longer available
|
||||||
() => {},
|
const game = new Savegame(this.app, {
|
||||||
err => {
|
internalId: toRemove.internalId,
|
||||||
logger.error(this, "Failed to remove old savegame:", toRemove, ":", err);
|
metaDataRef: toRemove,
|
||||||
}
|
});
|
||||||
);
|
promiseChain = promiseChain
|
||||||
}
|
.then(() => game.deleteAsync())
|
||||||
|
.then(
|
||||||
return promiseChain;
|
() => {},
|
||||||
}
|
err => {
|
||||||
|
logger.error(this, "Failed to remove old savegame:", toRemove, ":", err);
|
||||||
/**
|
}
|
||||||
* Helper method to generate a new internal savegame id
|
);
|
||||||
*/
|
}
|
||||||
generateInternalId() {
|
|
||||||
return Rusha.createHash()
|
return promiseChain;
|
||||||
.update(Date.now() + "/" + Math.random())
|
}
|
||||||
.digest("hex");
|
|
||||||
}
|
/**
|
||||||
|
* Helper method to generate a new internal savegame id
|
||||||
// End
|
*/
|
||||||
|
generateInternalId() {
|
||||||
initialize() {
|
return Rusha.createHash()
|
||||||
// First read, then directly write to ensure we have the latest data
|
.update(Date.now() + "/" + Math.random())
|
||||||
// @ts-ignore
|
.digest("hex");
|
||||||
return this.readAsync().then(() => {
|
}
|
||||||
if (G_IS_DEV && globalConfig.debug.disableSavegameWrite) {
|
|
||||||
return Promise.resolve();
|
// End
|
||||||
}
|
|
||||||
return this.sortSavegames().then(() => this.writeAsync());
|
initialize() {
|
||||||
});
|
// First read, then directly write to ensure we have the latest data
|
||||||
}
|
// @ts-ignore
|
||||||
}
|
return this.readAsync().then(() => {
|
||||||
|
if (G_IS_DEV && globalConfig.debug.disableSavegameWrite) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
return this.sortSavegames().then(() => this.writeAsync());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,38 +1,39 @@
|
|||||||
/**
|
/**
|
||||||
* @typedef {import("../game/entity").Entity} Entity
|
* @typedef {import("../game/entity").Entity} Entity
|
||||||
*
|
*
|
||||||
* @typedef {{}} SavegameStats
|
* @typedef {{}} SavegameStats
|
||||||
*
|
*
|
||||||
* @typedef {{
|
* @typedef {{
|
||||||
* camera: any,
|
* camera: any,
|
||||||
* time: any,
|
* time: any,
|
||||||
* entityMgr: any,
|
* entityMgr: any,
|
||||||
* map: any,
|
* map: any,
|
||||||
* hubGoals: any,
|
* hubGoals: any,
|
||||||
* pinnedShapes: any,
|
* pinnedShapes: any,
|
||||||
* waypoints: any,
|
* waypoints: any,
|
||||||
* entities: Array<Entity>,
|
* entities: Array<Entity>,
|
||||||
* beltPaths: Array<any>
|
* beltPaths: Array<any>
|
||||||
* }} SerializedGame
|
* }} SerializedGame
|
||||||
*
|
*
|
||||||
* @typedef {{
|
* @typedef {{
|
||||||
* version: number,
|
* version: number,
|
||||||
* dump: SerializedGame,
|
* dump: SerializedGame,
|
||||||
* stats: SavegameStats,
|
* stats: SavegameStats,
|
||||||
* lastUpdate: number,
|
* lastUpdate: number,
|
||||||
* }} SavegameData
|
* }} SavegameData
|
||||||
*
|
*
|
||||||
* @typedef {{
|
* @typedef {{
|
||||||
* lastUpdate: number,
|
* lastUpdate: number,
|
||||||
* version: number,
|
* version: number,
|
||||||
* internalId: string,
|
* internalId: string,
|
||||||
* level: number
|
* level: number
|
||||||
* }} SavegameMetadata
|
* name: string|null
|
||||||
*
|
* }} SavegameMetadata
|
||||||
* @typedef {{
|
*
|
||||||
* version: number,
|
* @typedef {{
|
||||||
* savegames: Array<SavegameMetadata>
|
* version: number,
|
||||||
* }} SavegamesData
|
* savegames: Array<SavegameMetadata>
|
||||||
*/
|
* }} SavegamesData
|
||||||
|
*/
|
||||||
export default {};
|
|
||||||
|
export default {};
|
||||||
|
@ -17,7 +17,7 @@ export class MobileWarningState extends GameState {
|
|||||||
There is also no estimate when this will change, but feel to make a contribution! It's
|
There is also no estimate when this will change, but feel to make a contribution! It's
|
||||||
<a href="https://github.com/tobspr/shapez.io" target="_blank">open source</a>!</p>
|
<a href="https://github.com/tobspr/shapez.io" target="_blank">open source</a>!</p>
|
||||||
|
|
||||||
<p>If you want to play on your computer, you can also get the standalone on steam:</p>
|
<p>If you want to play on your computer, you can also get the standalone on Steam:</p>
|
||||||
|
|
||||||
|
|
||||||
<a href="${
|
<a href="${
|
||||||
|
@ -25,7 +25,7 @@ steamPage:
|
|||||||
shortText: shapez.io és un joc que té com a objectiu construir i automatitzar fàbriques per tal de produir figures cada cop més complexes en un mapa infinit.
|
shortText: shapez.io és un joc que té com a objectiu construir i automatitzar fàbriques per tal de produir figures cada cop més complexes en un mapa infinit.
|
||||||
|
|
||||||
# This is the text shown above the Discord link
|
# This is the text shown above the Discord link
|
||||||
discordLink: Official Discord - Chat with me!
|
discordLink: Discord Oficial (en Anglès)
|
||||||
|
|
||||||
# This is the long description for the steam page - It is contained here so you can help to translate it, and I will regulary update the store page.
|
# This is the long description for the steam page - It is contained here so you can help to translate it, and I will regulary update the store page.
|
||||||
# NOTICE:
|
# NOTICE:
|
||||||
@ -34,50 +34,50 @@ steamPage:
|
|||||||
longText: >-
|
longText: >-
|
||||||
[img]{STEAM_APP_IMAGE}/extras/store_page_gif.gif[/img]
|
[img]{STEAM_APP_IMAGE}/extras/store_page_gif.gif[/img]
|
||||||
|
|
||||||
shapez.io is a game about building factories to automate the creation and processing of increasingly complex shapes across an infinitely expanding map.
|
shapez.io és un joc que té com a objectiu construir i automatitzar fàbriques per tal de produir figures cada cop més complexes en un mapa infinit.
|
||||||
|
|
||||||
Upon delivering the requested shapes you'll progress within the game and unlock upgrades to speed up your factory.
|
Al lliurar les formes geomètriques sol·licitades, progressaràs i desbloquejaràs millores per accelerar la teva fàbrica.
|
||||||
|
|
||||||
As the demand for shapes increases, you'll have to scale up your factory to meet the demand - Don't forget about resources though, you'll have to expand across the [b]infinite map[/b]!
|
Com que la demanda de figures va augmentant, hauràs de augmentar proporcionalment la teva fàbrica per satisfer la demanda. Però no t'oblidis dels recursos, ja que t'hauràs d'expandre a través del [b]mapa infinit[/b]!
|
||||||
|
|
||||||
Soon you'll have to mix colors and paint your shapes with them - Combine red, green and blue color resources to produce different colors and paint shapes with them to satisfy the demand.
|
Sovint, hauràs de començar a mesclar color i pintar les figures amb aquests; combina roig, verd i blau per produïr diferents colors i pintar les figures amb aquests per satisfer la demanda.
|
||||||
|
|
||||||
This game features 18 progressive levels (Which should already keep you busy for hours!) but I'm constantly adding new content - There's a lot planned!
|
Aquest joc presenta 18 nivells progressius (Els quals ja haurien de mantenir-te ocupat durant hores!) però estic constantment afegint nou contingut. Tenim moltes coses planejades!
|
||||||
|
|
||||||
Purchasing the game gives you access to the standalone version which has additional features, and you'll also receive access to newly developed features.
|
La compra del joc et dona accés a versió independent amb característiques adicionals, i també rebràs accés a les noves característiques que es desenvolupin.
|
||||||
|
|
||||||
[b]Standalone Advantages[/b]
|
[b]Ventatges de la Versió Independent[/b]
|
||||||
|
|
||||||
[list]
|
[list]
|
||||||
[*] Dark Mode
|
[*] Mode Oscur
|
||||||
[*] Unlimited Waypoints
|
[*] Punts d'interès il·limitats
|
||||||
[*] Unlimited Savegames
|
[*] Guardar partides il·limitades
|
||||||
[*] Additional settings
|
[*] Configuracions Adicionals
|
||||||
[*] Coming soon: Wires & Energy! Aiming for (roughly) end of July 2020.
|
[*] Pròximament: Cables i Energia! Objectiu (aproximadament) per finals de Juliol del 2020.
|
||||||
[*] Coming soon: More Levels
|
[*] Pròximament: Més Nivells
|
||||||
[*] Allows me to further develop shapez.io ❤️
|
[*] Em permet seguir desenvolupant shapez.io ❤️
|
||||||
[/list]
|
[/list]
|
||||||
|
|
||||||
[b]Future Updates[/b]
|
[b]Futures Actualitzacions[/b]
|
||||||
|
|
||||||
I am updating the game often and trying to push an update at least once every week!
|
Actualitzo el joc sovint i intent treure una actualització nova per setmana!
|
||||||
|
|
||||||
[list]
|
[list]
|
||||||
[*] Different maps and challenges (e.g. maps with obstacles)
|
[*] Mapes i reptes diferents (p.e. mapes amb obstacles)
|
||||||
[*] Puzzles (Deliver the requested shape with a restricted area / set of buildings)
|
[*] Trencaclosques (Entrega la figura amb una àrea/conjunt d'edificis delimitats)
|
||||||
[*] A story mode where buildings have a cost
|
[*] Un "Mode Història" on els edificis tenen un cost
|
||||||
[*] Configurable map generator (Configure resource/shape size/density, seed and more)
|
[*] Generador de mapes configurable (Configura el tamany/densitat de les formes/recursos, llavors i més)
|
||||||
[*] Additional types of shapes
|
[*] Formes geomètriques adicionals
|
||||||
[*] Performance improvements (The game already runs pretty well!)
|
[*] Millores de rendiment (El joc ja funciona força bé!)
|
||||||
[*] And much more!
|
[*] Moltes coses més!
|
||||||
[/list]
|
[/list]
|
||||||
|
|
||||||
[b]This game is open source![/b]
|
[b]Aquest joc és de codi obert![/b]
|
||||||
|
|
||||||
Anybody can contribute, I'm actively involved in the community and attempt to review all suggestions and take feedback into consideration where possible.
|
Qualsevol pot contribuir, estic involucrat activament en la comunitat i intento revisar tots els suggeriments i tenir en compte els comentaris sempre que sigui possible.
|
||||||
Be sure to check out my trello board for the full roadmap!
|
Assegureu-vos de consultar el meu tauler de trello per obtenir el full de ruta complet!
|
||||||
|
|
||||||
[b]Links[/b]
|
[b]Enllaços[/b]
|
||||||
|
|
||||||
[list]
|
[list]
|
||||||
[*] [url=https://discord.com/invite/HN7EVzV]Official Discord[/url]
|
[*] [url=https://discord.com/invite/HN7EVzV]Official Discord[/url]
|
||||||
@ -174,7 +174,7 @@ dialogs:
|
|||||||
importSavegameError:
|
importSavegameError:
|
||||||
title: Error en importar
|
title: Error en importar
|
||||||
text: >-
|
text: >-
|
||||||
Failed to import your savegame:
|
Ha ocurrit un error intentant importar la teva partida:
|
||||||
|
|
||||||
importSavegameSuccess:
|
importSavegameSuccess:
|
||||||
title: Importar
|
title: Importar
|
||||||
@ -184,7 +184,7 @@ dialogs:
|
|||||||
gameLoadFailure:
|
gameLoadFailure:
|
||||||
title: No es pot carregar la partida guardada
|
title: No es pot carregar la partida guardada
|
||||||
text: >-
|
text: >-
|
||||||
Failed to load your savegame:
|
Ha ocurrit un error al intentar carregar la teva partida:
|
||||||
|
|
||||||
confirmSavegameDelete:
|
confirmSavegameDelete:
|
||||||
title: Eliminar
|
title: Eliminar
|
||||||
@ -194,7 +194,7 @@ dialogs:
|
|||||||
savegameDeletionError:
|
savegameDeletionError:
|
||||||
title: Error en eliminar
|
title: Error en eliminar
|
||||||
text: >-
|
text: >-
|
||||||
Failed to delete the savegame:
|
Ha ocurrit un error al intentar eliminar la teva partida:
|
||||||
|
|
||||||
restartRequired:
|
restartRequired:
|
||||||
title: Reiniciar
|
title: Reiniciar
|
||||||
@ -240,12 +240,12 @@ dialogs:
|
|||||||
massCutConfirm:
|
massCutConfirm:
|
||||||
title: Tallar edificis
|
title: Tallar edificis
|
||||||
desc: >-
|
desc: >-
|
||||||
Estàs esborrant molts edificis de cop (<count> per ser exactes)! Estàs segur que vols seguir?
|
Estàs tallant molts edificis de cop (<count> per ser exactes)! Estàs segur que vols seguir?
|
||||||
|
|
||||||
massCutInsufficientConfirm:
|
massCutInsufficientConfirm:
|
||||||
title: Confirm cut
|
title: Confirmar tallar
|
||||||
desc: >-
|
desc: >-
|
||||||
You can not afford to paste this area! Are you sure you want to cut it?
|
No pots aferrar a aquesta zona! Estàs segur de que vols tallarla?
|
||||||
|
|
||||||
blueprintsNotUnlocked:
|
blueprintsNotUnlocked:
|
||||||
title: Encara no s'ha desbloquejat
|
title: Encara no s'ha desbloquejat
|
||||||
@ -271,7 +271,7 @@ dialogs:
|
|||||||
desc: En la Demo només pots crear dos marcadors, aconsegueix la versió completa per gaudir de l'experiència completa!
|
desc: En la Demo només pots crear dos marcadors, aconsegueix la versió completa per gaudir de l'experiència completa!
|
||||||
|
|
||||||
exportScreenshotWarning:
|
exportScreenshotWarning:
|
||||||
title: Export screenshot
|
title: Exportar Captura de Pantalla
|
||||||
desc: Has demanat exportar la teva/teua base com una captura de pantalla. Tin en conte que aquest procés pot ser molt lent i inclús crashear el teu joc!
|
desc: Has demanat exportar la teva/teua base com una captura de pantalla. Tin en conte que aquest procés pot ser molt lent i inclús crashear el teu joc!
|
||||||
|
|
||||||
ingame:
|
ingame:
|
||||||
@ -307,7 +307,7 @@ ingame:
|
|||||||
purple: Morat
|
purple: Morat
|
||||||
cyan: Cian
|
cyan: Cian
|
||||||
white: Blanc
|
white: Blanc
|
||||||
black: Black
|
black: Negre
|
||||||
uncolored: Sense color
|
uncolored: Sense color
|
||||||
|
|
||||||
# Everything related to placing buildings (I.e. as soon as you selected a building
|
# Everything related to placing buildings (I.e. as soon as you selected a building
|
||||||
@ -315,7 +315,7 @@ ingame:
|
|||||||
buildingPlacement:
|
buildingPlacement:
|
||||||
# Buildings can have different variants which are unlocked at later levels,
|
# Buildings can have different variants which are unlocked at later levels,
|
||||||
# and this is the hint shown when there are multiple variants available.
|
# and this is the hint shown when there are multiple variants available.
|
||||||
cycleBuildingVariants: Pulsa <key> per a ciclar entre variants.
|
cycleBuildingVariants: Prem <key> per a ciclar entre variants.
|
||||||
|
|
||||||
# Shows the hotkey in the ui, e.g. "Hotkey: Q"
|
# Shows the hotkey in the ui, e.g. "Hotkey: Q"
|
||||||
hotkeyLabel: >-
|
hotkeyLabel: >-
|
||||||
@ -687,139 +687,139 @@ settings:
|
|||||||
disabled: Desactivat
|
disabled: Desactivat
|
||||||
|
|
||||||
scrollWheelSensitivity:
|
scrollWheelSensitivity:
|
||||||
title: Zoom sensitivity
|
title: Sensitivitat del Zoom
|
||||||
description: >-
|
description: >-
|
||||||
Changes how sensitive the zoom is (Either mouse wheel or trackpad).
|
Canvia la sensitivitat del zoom (Roda del ratolí o del trackpad).
|
||||||
sensitivity:
|
sensitivity:
|
||||||
super_slow: Super slow
|
super_slow: Molt lent
|
||||||
slow: Slow
|
slow: Lent
|
||||||
regular: Regular
|
regular: Regular
|
||||||
fast: Fast
|
fast: Ràpid
|
||||||
super_fast: Super fast
|
super_fast: Molt Ràpid
|
||||||
|
|
||||||
movementSpeed:
|
movementSpeed:
|
||||||
title: Movement speed
|
title: Velocitat de Moviment
|
||||||
description: >-
|
description: >-
|
||||||
Changes how fast the view moves when using the keyboard.
|
Canvia la rapidesa amb la que la vista es mou mentres empres el teclat.
|
||||||
speeds:
|
speeds:
|
||||||
super_slow: Super slow
|
super_slow: Molt lent
|
||||||
slow: Slow
|
slow: Lent
|
||||||
regular: Regular
|
regular: Regular
|
||||||
fast: Fast
|
fast: Rápid
|
||||||
super_fast: Super Fast
|
super_fast: Molt Ràpid
|
||||||
extremely_fast: Extremely Fast
|
extremely_fast: Extremadament Ràpid
|
||||||
|
|
||||||
language:
|
language:
|
||||||
title: Language
|
title: Idioma
|
||||||
description: >-
|
description: >-
|
||||||
Change the language. All translations are user-contributed and might be incomplete!
|
Canvia l'idioma. Totes les traduccions són contribucions d'usuaris i poden estar incompletes!
|
||||||
|
|
||||||
enableColorBlindHelper:
|
enableColorBlindHelper:
|
||||||
title: Color Blind Mode
|
title: Mode per Daltònics
|
||||||
description: >-
|
description: >-
|
||||||
Enables various tools which allow you to play the game if you are color blind.
|
Habilita diverses eines que et permeten jugar si ets Daltònic.
|
||||||
|
|
||||||
fullscreen:
|
fullscreen:
|
||||||
title: Fullscreen
|
title: Pantalla Completa
|
||||||
description: >-
|
description: >-
|
||||||
It is recommended to play the game in fullscreen to get the best experience. Only available in the standalone.
|
Es recomana jugar en Pantalla Completa per aconseguir la millor experiència. Només disponible a la versió completa del joc.
|
||||||
|
|
||||||
soundsMuted:
|
soundsMuted:
|
||||||
title: Mute Sounds
|
title: Silencia els sons
|
||||||
description: >-
|
description: >-
|
||||||
If enabled, mutes all sound effects.
|
Si està activat, silencia tots els sons.
|
||||||
|
|
||||||
musicMuted:
|
musicMuted:
|
||||||
title: Mute Music
|
title: Silencia la música
|
||||||
description: >-
|
description: >-
|
||||||
If enabled, mutes all music.
|
Si està activat, silencia la música.
|
||||||
|
|
||||||
theme:
|
theme:
|
||||||
title: Game theme
|
title: Tema del joc (Visual)
|
||||||
description: >-
|
description: >-
|
||||||
Choose the game theme (light / dark).
|
Tria el tema visual (clar / oscur).
|
||||||
themes:
|
themes:
|
||||||
dark: Dark
|
dark: Oscur
|
||||||
light: Light
|
light: Clar
|
||||||
|
|
||||||
refreshRate:
|
refreshRate:
|
||||||
title: Simulation Target
|
title: Objectiu de Simulació
|
||||||
description: >-
|
description: >-
|
||||||
If you have a 144hz monitor, change the refresh rate here so the game will properly simulate at higher refresh rates. This might actually decrease the FPS if your computer is too slow.
|
Si tens un monitor de 144hz, canvia la tarifa de refresc aquí per que el joc es mostri de forma correcta a tarifes de refresc altes. Pot decrementar els FPS si el teu ordenador és massa lent.
|
||||||
|
|
||||||
alwaysMultiplace:
|
alwaysMultiplace:
|
||||||
title: Multiplace
|
title: Col·locació Múltiple
|
||||||
description: >-
|
description: >-
|
||||||
If enabled, all buildings will stay selected after placement until you cancel it. This is equivalent to holding SHIFT permanently.
|
Si s'activa, tots els edificis es mantindràn seleccionats després de col·locarlos fins que ho cancel·lis. Això és equivalent a mantenir SHIFT permanentment.
|
||||||
|
|
||||||
offerHints:
|
offerHints:
|
||||||
title: Hints & Tutorials
|
title: Pistes i Tutorials
|
||||||
description: >-
|
description: >-
|
||||||
Whether to offer hints and tutorials while playing. Also hides certain UI elements up to a given level to make it easier to get into the game.
|
Si s'activa, es mostraràn pistes i tutorials mentres es juga. També amaga certs elements visuals fins a un nivell per que sigui més fàcil aprendre a jugar.
|
||||||
|
|
||||||
enableTunnelSmartplace:
|
enableTunnelSmartplace:
|
||||||
title: Smart Tunnels
|
title: Túnels Intel·ligents
|
||||||
description: >-
|
description: >-
|
||||||
When enabled, placing tunnels will automatically remove unnecessary belts. This also enables you to drag tunnels and excess tunnels will get removed.
|
Si s'activa, al col·locar túnels s'eliminaràn les cintes transportadores innecessaris. També et permet arrastrar túnels i els túnels sobrants s'eliminaràn.
|
||||||
|
|
||||||
vignette:
|
vignette:
|
||||||
title: Vignette
|
title: Vinyeta
|
||||||
description: >-
|
description: >-
|
||||||
Enables the vignette, which darkens the screen corners and makes text easier to read.
|
Activa la vinyeta, que obscureix els cantons de la pantalla i facilita la lectura de texte.
|
||||||
|
|
||||||
rotationByBuilding:
|
rotationByBuilding:
|
||||||
title: Rotation by building type
|
title: Rotació segons el tipus d'edifici.
|
||||||
description: >-
|
description: >-
|
||||||
Each building type remembers the rotation you last set it to individually. This may be more comfortable if you frequently switch between placing different building types.
|
Cada tipus d'edifici recorda la rotació que vau definir per última vegada de manera individual. Això pot ser més còmode si canvies freqüentment entre edificis.
|
||||||
|
|
||||||
compactBuildingInfo:
|
compactBuildingInfo:
|
||||||
title: Compact Building Infos
|
title: Informació sobre Edificis Compactes
|
||||||
description: >-
|
description: >-
|
||||||
Shortens info boxes for buildings by only showing their ratios. Otherwise a description and image is shown.
|
Escurça els quadres d’informació dels edificis només mostrant les seves velocitats. En cas contrari, es mostra una descripció i una imatge.
|
||||||
|
|
||||||
disableCutDeleteWarnings:
|
disableCutDeleteWarnings:
|
||||||
title: Disable Cut/Delete Warnings
|
title: Desactiva els diàlegs de Talla/Borra
|
||||||
description: >-
|
description: >-
|
||||||
Disables the warning dialogs brought up when cutting/deleting more than 100 entities.
|
Desactiva els diàlegs d'advertència que es mostren en tallar / suprimir més de 100 entitats.
|
||||||
|
|
||||||
keybindings:
|
keybindings:
|
||||||
title: Keybindings
|
title: Combinacions de tecles
|
||||||
hint: >-
|
hint: >-
|
||||||
Tip: Be sure to make use of CTRL, SHIFT and ALT! They enable different placement options.
|
Tip: Assegura't d'emprar CTRL, SHIFT i ALT! Et permeten col·locar objectes de formes diferents.
|
||||||
|
|
||||||
resetKeybindings: Reset Keybindings
|
resetKeybindings: Resetejar les Combinacions de tecles
|
||||||
|
|
||||||
categoryLabels:
|
categoryLabels:
|
||||||
general: Application
|
general: Aplicació
|
||||||
ingame: Game
|
ingame: Joc
|
||||||
navigation: Navigating
|
navigation: Navegació
|
||||||
placement: Placement
|
placement: Col·locació
|
||||||
massSelect: Mass Select
|
massSelect: Sel·lecció Massiva
|
||||||
buildings: Building Shortcuts
|
buildings: Dreceres d'Edificis
|
||||||
placementModifiers: Placement Modifiers
|
placementModifiers: Modificadors de col·locació
|
||||||
|
|
||||||
mappings:
|
mappings:
|
||||||
confirm: Confirm
|
confirm: Confirmar
|
||||||
back: Back
|
back: Enrere
|
||||||
mapMoveUp: Move Up
|
mapMoveUp: Moure Amunt
|
||||||
mapMoveRight: Move Right
|
mapMoveRight: Moure Dreta
|
||||||
mapMoveDown: Move Down
|
mapMoveDown: Moure Avall
|
||||||
mapMoveLeft: Move Left
|
mapMoveLeft: Moure Esquerra
|
||||||
mapMoveFaster: Move Faster
|
mapMoveFaster: Moure més Ràpid
|
||||||
centerMap: Center Map
|
centerMap: Centrar Mapa
|
||||||
|
|
||||||
mapZoomIn: Zoom in
|
mapZoomIn: Apropar
|
||||||
mapZoomOut: Zoom out
|
mapZoomOut: Allunyar
|
||||||
createMarker: Create Marker
|
createMarker: Crea un Marcador
|
||||||
|
|
||||||
menuOpenShop: Upgrades
|
menuOpenShop: Millores
|
||||||
menuOpenStats: Statistics
|
menuOpenStats: Estadístiques
|
||||||
menuClose: Close Menu
|
menuClose: Tancar Menú
|
||||||
|
|
||||||
toggleHud: Toggle HUD
|
toggleHud: Commutar HUD
|
||||||
toggleFPSInfo: Toggle FPS and Debug Info
|
toggleFPSInfo: Commutar FPS i Informació de Depuració
|
||||||
switchLayers: Switch layers
|
switchLayers: Canviar capes
|
||||||
exportScreenshot: Export whole Base as Image
|
exportScreenshot: Exportar la Base com a Imatge
|
||||||
belt: *belt
|
belt: *belt
|
||||||
splitter: *splitter
|
splitter: *splitter
|
||||||
underground_belt: *underground_belt
|
underground_belt: *underground_belt
|
||||||
@ -834,49 +834,49 @@ keybindings:
|
|||||||
trash: *trash
|
trash: *trash
|
||||||
wire: *wire
|
wire: *wire
|
||||||
|
|
||||||
pipette: Pipette
|
pipette: Pipeta
|
||||||
rotateWhilePlacing: Rotate
|
rotateWhilePlacing: Rotar
|
||||||
rotateInverseModifier: >-
|
rotateInverseModifier: >-
|
||||||
Modifier: Rotate CCW instead
|
Modifier: Rotar en sentit antihorari
|
||||||
cycleBuildingVariants: Cycle Variants
|
cycleBuildingVariants: Rotar les Variants
|
||||||
confirmMassDelete: Delete area
|
confirmMassDelete: Eliminar àrea
|
||||||
pasteLastBlueprint: Paste last blueprint
|
pasteLastBlueprint: Afferar el darrer pla
|
||||||
cycleBuildings: Cycle Buildings
|
cycleBuildings: Rotar els Buildings
|
||||||
lockBeltDirection: Enable belt planner
|
lockBeltDirection: Habilitar el planificador de cintes transportadores
|
||||||
switchDirectionLockSide: >-
|
switchDirectionLockSide: >-
|
||||||
Planner: Switch side
|
Planner: Canviar costat
|
||||||
|
|
||||||
massSelectStart: Hold and drag to start
|
massSelectStart: Manteniu premut i arrossegueu per començar
|
||||||
massSelectSelectMultiple: Select multiple areas
|
massSelectSelectMultiple: Seleccionar múltiples àrees
|
||||||
massSelectCopy: Copy area
|
massSelectCopy: Copiar àrea
|
||||||
massSelectCut: Cut area
|
massSelectCut: Tallar àrea
|
||||||
|
|
||||||
placementDisableAutoOrientation: Disable automatic orientation
|
placementDisableAutoOrientation: Desactivar orientació automàtica
|
||||||
placeMultiple: Stay in placement mode
|
placeMultiple: Mantenir-se en mode de col·locació
|
||||||
placeInverse: Invert automatic belt orientation
|
placeInverse: Invertir orientació automàtica de les cintes transportadores
|
||||||
|
|
||||||
about:
|
about:
|
||||||
title: About this Game
|
title: Sobre aquest Joc
|
||||||
body: >-
|
body: >-
|
||||||
This game is open source and developed by <a href="https://github.com/tobspr" target="_blank">Tobias Springer</a> (this is me).<br><br>
|
Aquest joc és de codi obert i desenvolupat per <a href="https://github.com/tobspr" target="_blank">Tobias Springer</a> (sóc jo).<br><br>
|
||||||
|
|
||||||
If you want to contribute, check out <a href="<githublink>" target="_blank">shapez.io on GitHub</a>.<br><br>
|
Si vols contribuir, visita <a href="<githublink>" target="_blank">shapez.io a GitHub</a>.<br><br>
|
||||||
|
|
||||||
This game wouldn't have been possible without the great Discord community around my games - You should really join the <a href="<discordlink>" target="_blank">Discord server</a>!<br><br>
|
Aquest joc no hauria estat possible sense la gran comunitat de Discord al voltant dels meus jocs. Recoman unir-se al <a href="<discordlink>" target="_blank">servidor de Discord</a>!<br><br>
|
||||||
|
|
||||||
The soundtrack was made by <a href="https://soundcloud.com/pettersumelius" target="_blank">Peppsen</a> - He's awesome.<br><br>
|
Banda sonora creada per<a href="https://soundcloud.com/pettersumelius" target="_blank">Peppsen</a> - És increïble.<br><br>
|
||||||
|
|
||||||
Finally, huge thanks to my best friend <a href="https://github.com/niklas-dahl" target="_blank">Niklas</a> - Without our Factorio sessions, this game would never have existed.
|
Finalment, gràcies al meu millor amic <a href="https://github.com/niklas-dahl" target="_blank">Niklas</a>. Sense les nostres sessions de Factorio, aquest joc mai hauria existit.
|
||||||
|
|
||||||
changelog:
|
changelog:
|
||||||
title: Changelog
|
title: Registre de Canvis
|
||||||
|
|
||||||
demo:
|
demo:
|
||||||
features:
|
features:
|
||||||
restoringGames: Restoring savegames
|
restoringGames: Restaurar partides guardats
|
||||||
importingGames: Importing savegames
|
importingGames: Importar partides guardats
|
||||||
oneGameLimit: Limited to one savegame
|
oneGameLimit: Limitat a una partida guardada.
|
||||||
customizeKeybindings: Customizing Keybindings
|
customizeKeybindings: Personalitzar teclats
|
||||||
exportingBase: Exporting whole Base as Image
|
exportingBase: Exportar la base com a Imatge
|
||||||
|
|
||||||
settingNotAvailable: Not available in the demo.
|
settingNotAvailable: No disponible en la versió de demostració.
|
||||||
|
@ -234,8 +234,8 @@ dialogs:
|
|||||||
|
|
||||||
createMarker:
|
createMarker:
|
||||||
title: Nová značka
|
title: Nová značka
|
||||||
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: Pojmenuj jí nějak výstižně, též ji můžeš doplnit <strong>zkratkou</strong> pro tvar (Kterou si můžete vytvořit <a href="https://viewer.shapez.io" target="_blank">zde</a>)
|
||||||
titleEdit: Edit Marker
|
titleEdit: Upravit značku
|
||||||
|
|
||||||
markerDemoLimit:
|
markerDemoLimit:
|
||||||
desc: V ukázce můžete vytvořit pouze dvě značky. Získejte plnou verzi pro neomezený počet značek!
|
desc: V ukázce můžete vytvořit pouze dvě značky. Získejte plnou verzi pro neomezený počet značek!
|
||||||
@ -252,8 +252,8 @@ dialogs:
|
|||||||
může zejména u větších základen dlouho trvat, nebo dokonce shodit hru!
|
může zejména u větších základen dlouho trvat, nebo dokonce shodit hru!
|
||||||
|
|
||||||
massCutInsufficientConfirm:
|
massCutInsufficientConfirm:
|
||||||
title: Confirm cut
|
title: Potvrdit vyjmutí
|
||||||
desc: You can not afford to paste this area! Are you sure you want to cut it?
|
desc: Nemůžeš si dovolit vložení této oblasti! Skutečně ji chceš vyjmout?
|
||||||
|
|
||||||
ingame:
|
ingame:
|
||||||
# This is shown in the top left corner and displays useful keybindings in
|
# This is shown in the top left corner and displays useful keybindings in
|
||||||
@ -510,25 +510,25 @@ buildings:
|
|||||||
description: Skladuje věci navíc až do naplnění kapacity. Může být použit na skladování surovin navíc.
|
description: Skladuje věci navíc až do naplnění kapacity. Může být použit na skladování surovin navíc.
|
||||||
wire:
|
wire:
|
||||||
default:
|
default:
|
||||||
name: Energy Wire
|
name: Kabel
|
||||||
description: Allows you to transport energy.
|
description: Dovoluje přenos energie.
|
||||||
advanced_processor:
|
advanced_processor:
|
||||||
default:
|
default:
|
||||||
name: Color Inverter
|
name: Invertor barev
|
||||||
description: Accepts a color or shape and inverts it.
|
description: Přijme barvu či tvar a převrátí jej.
|
||||||
energy_generator:
|
energy_generator:
|
||||||
deliver: Deliver
|
deliver: Dodej
|
||||||
toGenerateEnergy: For
|
toGenerateEnergy: Pro
|
||||||
default:
|
default:
|
||||||
name: Energy Generator
|
name: Generátor energie
|
||||||
description: Generates energy by consuming shapes.
|
description: Vyrábí energii zpracováním tvarů.
|
||||||
wire_crossings:
|
wire_crossings:
|
||||||
default:
|
default:
|
||||||
name: Wire Splitter
|
name: Dělič kabelů
|
||||||
description: Splits a energy wire into two.
|
description: Rozdělí kabel na dva.
|
||||||
merger:
|
merger:
|
||||||
name: Wire Merger
|
name: Slučovač kabelů
|
||||||
description: Merges two energy wires into one.
|
description: Spojí dva kabely do jednoho.
|
||||||
|
|
||||||
storyRewards:
|
storyRewards:
|
||||||
# Those are the rewards gained from completing the store
|
# Those are the rewards gained from completing the store
|
||||||
@ -543,7 +543,7 @@ storyRewards:
|
|||||||
reward_painter:
|
reward_painter:
|
||||||
title: Barvení
|
title: Barvení
|
||||||
desc: >-
|
desc: >-
|
||||||
The <strong>painter</strong> has been unlocked - Extract some color veins (just as you do with shapes) and combine it with a shape in the painter to color them!<br><br>PS: If you are colorblind, there is a <strong>color blind mode</strong> in the settings!
|
<strong>Barvič</strong> byl právě odemčen - Vytěžte nějaká ložiska barev (podobně jako těžíte tvary) a spojte je s tvarem v barviči pro jeho obarvení!<br><br>PS: Pokud jste barvoslepý, v nastavení naleznete <strong>režim pro barvoslepé</strong>!
|
||||||
|
|
||||||
reward_mixer:
|
reward_mixer:
|
||||||
title: Míchání barev
|
title: Míchání barev
|
||||||
@ -587,7 +587,7 @@ storyRewards:
|
|||||||
desc: Odemknuli jste variantu <strong>barviče</strong> - Funguje stejně jako normální, ale nabarví <strong>dva tvary naráz</strong> pomocí jedné barvy!
|
desc: Odemknuli jste variantu <strong>barviče</strong> - Funguje stejně jako normální, ale nabarví <strong>dva tvary naráz</strong> pomocí jedné barvy!
|
||||||
|
|
||||||
reward_painter_quad:
|
reward_painter_quad:
|
||||||
title: Quad Painting
|
title: Čtyřstranné barvení
|
||||||
desc: Odemknuli jste variantu <strong>painter</strong> - Umožní vám nabarvit každou čtvrtinu tvaru jinou barvou!
|
desc: Odemknuli jste variantu <strong>painter</strong> - Umožní vám nabarvit každou čtvrtinu tvaru jinou barvou!
|
||||||
|
|
||||||
reward_storage:
|
reward_storage:
|
||||||
@ -616,9 +616,9 @@ storyRewards:
|
|||||||
settings:
|
settings:
|
||||||
title: Nastavení
|
title: Nastavení
|
||||||
categories:
|
categories:
|
||||||
general: General
|
general: Obecné
|
||||||
userInterface: User Interface
|
userInterface: Uživatelské rozhraní
|
||||||
advanced: Advanced
|
advanced: Rozšířené
|
||||||
|
|
||||||
versionBadges:
|
versionBadges:
|
||||||
dev: Vývojová verze
|
dev: Vývojová verze
|
||||||
|
@ -44,37 +44,38 @@ steamPage:
|
|||||||
|
|
||||||
Nutze dein gesammeltes Wissen über die Maschinen und lasse deine Fabriken die gewünschten Formen der 18 verschiedenen Level abliefern. Schalte mit jedem Level neue Arbeitsschritte oder Gebäude frei. Das sollte dich schon für Stunden beschäftigt halten! Danach werden im Freispielmodus zufällige Formen generiert, die du ebenfalls abliefern kannst. Ich füge regelmäßig neue Funktionen hinzu und davon sind eine ganze Menge geplant!
|
Nutze dein gesammeltes Wissen über die Maschinen und lasse deine Fabriken die gewünschten Formen der 18 verschiedenen Level abliefern. Schalte mit jedem Level neue Arbeitsschritte oder Gebäude frei. Das sollte dich schon für Stunden beschäftigt halten! Danach werden im Freispielmodus zufällige Formen generiert, die du ebenfalls abliefern kannst. Ich füge regelmäßig neue Funktionen hinzu und davon sind eine ganze Menge geplant!
|
||||||
|
|
||||||
|
|
||||||
Wenn du das Spiel erwirbst, erhälst du Zugriff auf die zusätzlichen Features der Standalone-Version. Das bedeutet, du kannst unter anderem die neuesten Updates zuerst spielen!
|
Wenn du das Spiel erwirbst, erhälst du Zugriff auf die zusätzlichen Features der Standalone-Version. Das bedeutet, du kannst unter anderem die neuesten Updates zuerst spielen!
|
||||||
|
|
||||||
[b]Vorteile der Standalone[/b]
|
[b]Vorteile der Standalone[/b]
|
||||||
|
|
||||||
[list]
|
[list]
|
||||||
[*] Dark-Mode
|
[*] Dark Mode
|
||||||
[*] Unbegrenzte Wegpunkte
|
[*] unbegrenzte Anzahl an Wegpunkten
|
||||||
[*] Unbegrenzte Anzahl von Spielständen
|
[*] unbegrenzte Anzahl an Speicherständen
|
||||||
[*] Weitere Einstellungen
|
[*] zusätzliche Einstellungen
|
||||||
[*] Es kommen: Mehr Levels
|
[*] Bald: Strom & Kabel! (Ungefähr) geplant für ende Juli 2020.
|
||||||
[*] Es kommen: Kabel & Energie! Voraussichtlich gegen Ende Juli 2020.
|
[*] Bald: mehr Level
|
||||||
[*] Unterstütze die Entwicklung von shapez.io ❤️
|
[*] Erlaubt es mir shapez.io weiter zu entwickeln ❤️
|
||||||
[/list]
|
[/list]
|
||||||
|
|
||||||
[b]Geplante Funktionen[/b]
|
[b]Zukünftige Updates:[/b]
|
||||||
|
|
||||||
Ich bin aktiv mit der Entwicklung beschäftigt und versuche jede Woche ein Update oder den aktuellen Stand der Entwicklung zu veröffentlichen.
|
Ich update das Spiel sehr oft, und versuche wöchentlich ein Update zu veröffentlichen!
|
||||||
|
|
||||||
[list]
|
[list]
|
||||||
[*] Verschiedene Karten und Herausforderungen (z.B. Karten mit Hindernissen)
|
[*] Verschiedene Karten und Herausforderungen (z.B. Karten mit Hindernissen)
|
||||||
[*] Puzzle (Liefere die gewünschten Formen mit begrenztem Platz / limitierten Gebäuden)
|
[*] Herausforderungen (liefere die geforderte Form mit einer beschränkten Karte / anzahl an Gebäuden ab)
|
||||||
[*] Story-Modus mit Gebäudekosten
|
[*] Eine Kampange, in der die Gebäude einen Preis haben.
|
||||||
[*] Einstellbare Kartengenerierung (Ändere die Grösse/Anzahl/Dichte der Ressourcenflecken, den Seed und mehr)
|
[*] einen konfigurierbaren Kartengenerator (bestimme Ressourcen / Größe / Dichte, den Seed und viel mehr)
|
||||||
[*] Mehr Formentypen
|
[*] zusätzliche Formen
|
||||||
[*] Performanceverbesserungen (Das Spiel läuft bereits ganz gut!)
|
[*] Performanceverbesserungen (Das Spiel läuft bereits sehr gut!)
|
||||||
[*] Und vieles mehr!
|
[*] Und vieles mehr!
|
||||||
[/list]
|
[/list]
|
||||||
|
|
||||||
[b] Dieses Spiel ist Open Source[/b]
|
[b]Das Spiel ist open source![/b]
|
||||||
|
|
||||||
Jeder kann dazu beitragen! Ich bin aktiv in die Community involviert, versuche alle Vorschläge zu lesen und beziehe so viel Feedback wie möglich mit in die Entwicklung ein.
|
Jeder kann dazu beitragen. Ich bin aktiv in der Communtiy involviert und versuche alle Vorschläge zu lesen und beziehe so viel Feedback wie möglich mit in die Entwicklung ein.
|
||||||
Die komplette Roadmap gibt es auf dem Trello-Board zum Nachlesen!
|
Die komplette Roadmap gibt es auf dem Trello-Board zum Nachlesen!
|
||||||
|
|
||||||
[b]Links [/b]
|
[b]Links [/b]
|
||||||
@ -83,6 +84,8 @@ steamPage:
|
|||||||
[*] [url=https://discord.com/invite/HN7EVzV]Offizieller Discord[/url]
|
[*] [url=https://discord.com/invite/HN7EVzV]Offizieller Discord[/url]
|
||||||
[*] [url=https://trello.com/b/ISQncpJP/shapezio]Roadmap[/url]
|
[*] [url=https://trello.com/b/ISQncpJP/shapezio]Roadmap[/url]
|
||||||
[*] [url=https://www.reddit.com/r/shapezio]Subreddit[/url]
|
[*] [url=https://www.reddit.com/r/shapezio]Subreddit[/url]
|
||||||
|
[*] [url=https://github.com/tobspr/shapez.io]Source code (GitHub)[/url]
|
||||||
|
[*] [url=https://github.com/tobspr/shapez.io/blob/master/translations/README.md]Hilf zu übersetzen[/url]
|
||||||
[*] [url=https://github.com/tobspr/shapez.io]Quelltext (GitHub)[/url]
|
[*] [url=https://github.com/tobspr/shapez.io]Quelltext (GitHub)[/url]
|
||||||
[*] [url=https://github.com/tobspr/shapez.io/blob/master/translations/README.md]Hilf beim Übersetzen[/url]
|
[*] [url=https://github.com/tobspr/shapez.io/blob/master/translations/README.md]Hilf beim Übersetzen[/url]
|
||||||
[/list]
|
[/list]
|
||||||
@ -105,7 +108,7 @@ global:
|
|||||||
trillions: T
|
trillions: T
|
||||||
|
|
||||||
# Shown for infinitely big numbers
|
# Shown for infinitely big numbers
|
||||||
infinite: unend
|
infinite: ∞
|
||||||
|
|
||||||
time:
|
time:
|
||||||
# Used for formatting past time dates
|
# Used for formatting past time dates
|
||||||
@ -141,9 +144,9 @@ demoBanners:
|
|||||||
|
|
||||||
mainMenu:
|
mainMenu:
|
||||||
play: Spielen
|
play: Spielen
|
||||||
|
changelog: Änderungsprotokoll
|
||||||
continue: Fortsetzen
|
continue: Fortsetzen
|
||||||
newGame: Neues Spiel
|
newGame: Neues Spiel
|
||||||
changelog: Änderungshistorie
|
|
||||||
subreddit: Reddit
|
subreddit: Reddit
|
||||||
importSavegame: Importieren
|
importSavegame: Importieren
|
||||||
openSourceHint: Dieses Spiel ist Open Source!
|
openSourceHint: Dieses Spiel ist Open Source!
|
||||||
@ -153,7 +156,7 @@ mainMenu:
|
|||||||
|
|
||||||
# This is shown when using firefox and other browsers which are not supported.
|
# This is shown when using firefox and other browsers which are not supported.
|
||||||
browserWarning: >-
|
browserWarning: >-
|
||||||
Sorry, aber das Spiel wird in deinem Browser langsam laufen! Erwerbe die Standalone-Version oder downloade Chrome für die beste Erfahrung!
|
Sorry, aber das Spiel wird in deinem Browser langsam laufen! Kaufe die Standalone-Version oder downloade Chrome für die beste Erfahrung!
|
||||||
|
|
||||||
savegameLevel: Level <x>
|
savegameLevel: Level <x>
|
||||||
savegameLevelUnknown: Unbekanntes Level
|
savegameLevelUnknown: Unbekanntes Level
|
||||||
@ -167,7 +170,7 @@ dialogs:
|
|||||||
restart: Neustart
|
restart: Neustart
|
||||||
reset: Zurücksetzen
|
reset: Zurücksetzen
|
||||||
getStandalone: Standalone Ansehen
|
getStandalone: Standalone Ansehen
|
||||||
deleteGame: Ich weiß, was ich tue
|
deleteGame: Ich weiß, was ich tue!
|
||||||
viewUpdate: Update anzeigen
|
viewUpdate: Update anzeigen
|
||||||
showUpgrades: Upgrades anzeigen
|
showUpgrades: Upgrades anzeigen
|
||||||
showKeybindings: Kürzel anzeigen
|
showKeybindings: Kürzel anzeigen
|
||||||
@ -200,15 +203,15 @@ dialogs:
|
|||||||
restartRequired:
|
restartRequired:
|
||||||
title: Neustart benötigt
|
title: Neustart benötigt
|
||||||
text: >-
|
text: >-
|
||||||
Du muss das Spiel neu starten, um die Einstellungen anzuwenden.
|
Du musst das Spiel neu starten, um die Einstellungen anzuwenden.
|
||||||
|
|
||||||
editKeybinding:
|
editKeybinding:
|
||||||
title: Tastenbelegung ändern
|
title: Tastenbelegung ändern
|
||||||
desc: Drücke die (Maus-)Taste, die du vergeben willst, oder ESC um abzubrechen.
|
desc: Drücke die (Maus-)Taste, die du belegen möchtest, oder ESC um abzubrechen.
|
||||||
|
|
||||||
resetKeybindingsConfirmation:
|
resetKeybindingsConfirmation:
|
||||||
title: Tastenbelegung zurücksetzen
|
title: Tastenbelegung zurücksetzen
|
||||||
desc: Das wird alle deine Tastenbelegungen auf den Standard zurücksetzen. Bist du dir sicher?
|
desc: Dies wird alle deine Tastenbelegungen auf den Standard zurücksetzen. Bist du dir sicher?
|
||||||
|
|
||||||
keybindingsResetOk:
|
keybindingsResetOk:
|
||||||
title: Tastenbelegung zurückgesetzt
|
title: Tastenbelegung zurückgesetzt
|
||||||
@ -225,13 +228,13 @@ dialogs:
|
|||||||
updateSummary:
|
updateSummary:
|
||||||
title: Neues Update!
|
title: Neues Update!
|
||||||
desc: >-
|
desc: >-
|
||||||
Hier sind die Änderungen, seit dem du das letzte Mal gespielt hast:
|
Hier sind die Änderungen, seitdem du das letzte Mal gespielt hast:
|
||||||
|
|
||||||
upgradesIntroduction:
|
upgradesIntroduction:
|
||||||
title: Upgrades Freischalten
|
title: Upgrades Freischalten
|
||||||
desc: >-
|
desc: >-
|
||||||
Viele deiner Formen können noch benutzt werden, um Upgrades freizuschalten - <strong>Zerstöre deine alten Fabriken nicht!</strong>
|
Viele deiner Formen können noch benutzt werden, um Upgrades freizuschalten - <strong>Zerstöre deine alten Fabriken nicht!</strong>
|
||||||
Den Upgrade-Tab kannst du oben rechts im Bildschirm finden.
|
Den Upgrade-Tab findest du oben rechts im Bildschirm.
|
||||||
|
|
||||||
massDeleteConfirm:
|
massDeleteConfirm:
|
||||||
title: Löschen bestätigen
|
title: Löschen bestätigen
|
||||||
@ -245,13 +248,12 @@ dialogs:
|
|||||||
|
|
||||||
massCutInsufficientConfirm:
|
massCutInsufficientConfirm:
|
||||||
title: Ausschneiden bestätigen
|
title: Ausschneiden bestätigen
|
||||||
desc: >-
|
desc: Du kannst dir das Einfügen nicht leisten! Bist du sicher, dass du trotzdem Ausschneiden möchtest?
|
||||||
Du hast aktuell nicht genug Blaupausenformen, um die Auswahl einzufügen! Möchtest du sie trotzdem ausschneiden?
|
|
||||||
|
|
||||||
blueprintsNotUnlocked:
|
blueprintsNotUnlocked:
|
||||||
title: Noch nicht freigeschaltet
|
title: Noch nicht freigeschaltet
|
||||||
desc: >-
|
desc: >-
|
||||||
Blueprints werden erst in Level 12 freigeschaltet!
|
Blaupausen werden erst in Level 12 freigeschaltet!
|
||||||
|
|
||||||
keybindingsIntroduction:
|
keybindingsIntroduction:
|
||||||
title: Nützliche Hotkeys
|
title: Nützliche Hotkeys
|
||||||
@ -264,15 +266,16 @@ dialogs:
|
|||||||
|
|
||||||
createMarker:
|
createMarker:
|
||||||
title: Neuer Marker
|
title: Neuer Marker
|
||||||
titleEdit: Edit Marker
|
|
||||||
desc: Gib ihm einen griffigen Namen. Du kannst sogar die <strong>Abkürzung</strong> einer Form eingeben (Diese kann <a href="https://viewer.shapez.io" target="_blank">hier</a> generiert werden).
|
desc: Gib ihm einen griffigen Namen. Du kannst sogar die <strong>Abkürzung</strong> einer Form eingeben (Diese kann <a href="https://viewer.shapez.io" target="_blank">hier</a> generiert werden).
|
||||||
|
titleEdit: Marker bearbeiten
|
||||||
|
|
||||||
markerDemoLimit:
|
markerDemoLimit:
|
||||||
desc: Du kannst nur 2 benutzerdefinierte Marker in der Demo benutzen. Hol dir die Standalone, um unendlich viele Marker zu erstellen!
|
desc: Du kannst nur 2 Marker in der Demo benutzen. Hol dir die Standalone, um unendlich viele Marker zu erstellen!
|
||||||
|
|
||||||
exportScreenshotWarning:
|
exportScreenshotWarning:
|
||||||
title: Bildschirmfoto exportieren
|
title: Bildschirmfoto exportieren
|
||||||
desc: Hier kannst du ein Bildschirmfoto von deiner ganzen Fabrik erstellen. Für extrem große Fabriken kann das jedoch sehr lange dauern und ggf. zum Spielabsturz führen!
|
desc: >-
|
||||||
|
Hier kannst du ein Bildschirmfoto von deiner ganzen Fabrik erstellen. Für extrem große Fabriken kann das jedoch sehr lange dauern und ggf. zum Spielabsturz führen!
|
||||||
|
|
||||||
ingame:
|
ingame:
|
||||||
# This is shown in the top left corner and displays useful keybindings in
|
# This is shown in the top left corner and displays useful keybindings in
|
||||||
@ -363,7 +366,7 @@ ingame:
|
|||||||
dataSources:
|
dataSources:
|
||||||
stored:
|
stored:
|
||||||
title: Gelagert
|
title: Gelagert
|
||||||
description: Zeigt die Menge an Formen, die im zentralen Gebäude gelagert sind.
|
description: Zeigt die Menge an Formen, die im Hub gelagert sind.
|
||||||
produced:
|
produced:
|
||||||
title: Produziert
|
title: Produziert
|
||||||
description: Zeigt die Menge an Formen, die deine gesamte Fabrik produziert (inkl. Zwischenprodukte).
|
description: Zeigt die Menge an Formen, die deine gesamte Fabrik produziert (inkl. Zwischenprodukte).
|
||||||
@ -400,7 +403,7 @@ ingame:
|
|||||||
# Map markers
|
# Map markers
|
||||||
waypoints:
|
waypoints:
|
||||||
waypoints: Markierungen
|
waypoints: Markierungen
|
||||||
hub: HUB
|
hub: Hub
|
||||||
description: Linksklick auf einen Marker, um dort hinzugelangen, Rechtsklick, um ihn zu löschen.<br><br>Drücke <keybinding> um einen Marker aus deinem Blickwinkel, oder <strong>rechtsklicke</strong>, um einen Marker auf der ausgewählten Position zu erschaffen.
|
description: Linksklick auf einen Marker, um dort hinzugelangen, Rechtsklick, um ihn zu löschen.<br><br>Drücke <keybinding> um einen Marker aus deinem Blickwinkel, oder <strong>rechtsklicke</strong>, um einen Marker auf der ausgewählten Position zu erschaffen.
|
||||||
creationSuccessNotification: Marker wurde erstellt.
|
creationSuccessNotification: Marker wurde erstellt.
|
||||||
|
|
||||||
@ -416,7 +419,7 @@ ingame:
|
|||||||
hints:
|
hints:
|
||||||
1_1_extractor: Platziere einen <strong>Extrahierer</strong> auf der <strong>Kreisform</strong> um sie zu extrahieren!
|
1_1_extractor: Platziere einen <strong>Extrahierer</strong> auf der <strong>Kreisform</strong> um sie zu extrahieren!
|
||||||
1_2_conveyor: >-
|
1_2_conveyor: >-
|
||||||
Verbinde den Extrahierer mit einem <strong>Förderband</strong> und schließe ihn am zentralen Gebäude an!<br><br>Tipp: <strong>Drück und ziehe</strong> das Förderband mit der Maus!
|
Verbinde den Extrahierer mit einem <strong>Förderband</strong> und schließe ihn am Hub an!<br><br>Tipp: <strong>Drück und ziehe</strong> das Förderband mit der Maus!
|
||||||
|
|
||||||
1_3_expand: >-
|
1_3_expand: >-
|
||||||
Dies ist <strong>KEIN</strong> Idle-Game! Baue mehr Extrahierer und Förderbänder, um das Ziel schneller zu erreichen.<br><br>Tipp: Halte <strong>UMSCH</strong>, um mehrere Gebäude zu platzieren und nutze <strong>R</strong> um sie zu rotieren.
|
Dies ist <strong>KEIN</strong> Idle-Game! Baue mehr Extrahierer und Förderbänder, um das Ziel schneller zu erreichen.<br><br>Tipp: Halte <strong>UMSCH</strong>, um mehrere Gebäude zu platzieren und nutze <strong>R</strong> um sie zu rotieren.
|
||||||
@ -493,14 +496,14 @@ buildings:
|
|||||||
|
|
||||||
rotater:
|
rotater:
|
||||||
default:
|
default:
|
||||||
name: &rotater Rotierer
|
name: &rotater Rotierer (-90°)
|
||||||
description: Rotiert Formen im Uhrzeigersinn um 90 Grad.
|
description: Rotiert Formen im Uhrzeigersinn um 90 Grad.
|
||||||
ccw:
|
ccw:
|
||||||
name: Rotierer (CCW)
|
name: Rotierer (+90°)
|
||||||
description: Rotiert Formen gegen den Uhrzeigersinn um 90 Grad.
|
description: Rotiert Formen gegen den Uhrzeigersinn um 90 Grad.
|
||||||
fl:
|
fl:
|
||||||
name: Rotierer (180)
|
name: Rotierer (+180°)
|
||||||
description: Rotiert Formen um 180 Grad.
|
description: Rotiert die Formen um 180 Grad.
|
||||||
|
|
||||||
stacker:
|
stacker:
|
||||||
default:
|
default:
|
||||||
@ -540,25 +543,25 @@ buildings:
|
|||||||
|
|
||||||
wire:
|
wire:
|
||||||
default:
|
default:
|
||||||
name: Energiekabel
|
name: Stromkabel
|
||||||
description: Transportiert Energie.
|
description: Erlaubt dir Strom zu transportieren.
|
||||||
advanced_processor:
|
advanced_processor:
|
||||||
default:
|
default:
|
||||||
name: Farbinvertierer
|
name: Farbinvertierer
|
||||||
description: Akzeptiert Farben und Formen und invertiert sie.
|
description: Invertiert die Farbe. Geht auch bei Formen.
|
||||||
energy_generator:
|
energy_generator:
|
||||||
deliver: Liefere
|
deliver: Liefere
|
||||||
toGenerateEnergy: für
|
toGenerateEnergy: für
|
||||||
default:
|
default:
|
||||||
name: Energiegenerator
|
name: Stromgenerator
|
||||||
description: Generiert Energie, indem die Formen verbraucht werden.
|
description: Erzeugt Strom, indem er Formen verbraucht.
|
||||||
wire_crossings:
|
wire_crossings:
|
||||||
default:
|
default:
|
||||||
name: Kabelverteiler
|
name: Kabelverteiler
|
||||||
description: Teilt ein Energiekabel in zwei Ausgänge.
|
description: Teilt ein Stromkabel in zwei auf.
|
||||||
merger:
|
merger:
|
||||||
name: Kabelverbinder
|
name: Kabelverbinder
|
||||||
description: Verbindet zwei Energiekabel zu einem.
|
description: Verbindet zwei Stromkabel zu einem.
|
||||||
|
|
||||||
storyRewards:
|
storyRewards:
|
||||||
# Those are the rewards gained from completing the store
|
# Those are the rewards gained from completing the store
|
||||||
@ -573,7 +576,7 @@ storyRewards:
|
|||||||
reward_painter:
|
reward_painter:
|
||||||
title: Färben
|
title: Färben
|
||||||
desc: >-
|
desc: >-
|
||||||
The <strong>painter</strong> has been unlocked - Extract some color veins (just as you do with shapes) and combine it with a shape in the painter to color them!<br><br>PS: If you are colorblind, there is a <strong>color blind mode</strong> in the settings!
|
Der <strong>Färber</strong> wurde freigeschaltet. Extrahiere ein paar Farben (genauso wie bei Formen) und färbe damit eine Form im Färber!<br><br>PS: Falls du Farbenblind bist, es gibt einen <strong>Modus für Farbenblinde</strong> in den Einstellungen!
|
||||||
|
|
||||||
reward_mixer:
|
reward_mixer:
|
||||||
title: Farben mischen
|
title: Farben mischen
|
||||||
@ -626,7 +629,7 @@ storyRewards:
|
|||||||
|
|
||||||
reward_freeplay:
|
reward_freeplay:
|
||||||
title: Freies Spiel
|
title: Freies Spiel
|
||||||
desc: Du hast es geschafft! Du hast den <strong>Freispielmodus</strong> freigeschaltet! Das heißt, dass abzuliefernde Formen jetzt zufällig generiert werden! (Keine Sorge, für die Standaloneversion ist noch mehr geplant!)
|
desc: Du hast es geschafft! Du bist im <strong>freien Spiel</strong> angekommen! Das heißt, dass abzuliefernde Formen jetzt zufällig generiert werden! (Keine Sorge, für die Standaloneversion ist noch mehr geplant!)
|
||||||
|
|
||||||
reward_blueprints:
|
reward_blueprints:
|
||||||
title: Blaupause
|
title: Blaupause
|
||||||
@ -636,7 +639,7 @@ storyRewards:
|
|||||||
no_reward:
|
no_reward:
|
||||||
title: Nächstes Level
|
title: Nächstes Level
|
||||||
desc: >-
|
desc: >-
|
||||||
Dieses Level hat dir keine Belohnung gegeben, aber das nächste schon! <br><br> PS: Denk daran, deine alten Fabriken nicht zu zerstören - Du wirst sie später <strong>alle</strong> noch brauchen, um <strong>Upgrades freizuschalten</strong>!
|
Dieses Level hat dir keine Belohnung gegeben, aber dafür das Nächste schon! <br><br> PS: Denk daran, deine alten Fabriken nicht zu zerstören - Du wirst sie später <strong>alle</strong> noch brauchen, um <strong>Upgrades freizuschalten</strong>!
|
||||||
|
|
||||||
no_reward_freeplay:
|
no_reward_freeplay:
|
||||||
title: Nächstes Level
|
title: Nächstes Level
|
||||||
@ -684,7 +687,7 @@ settings:
|
|||||||
scrollWheelSensitivity:
|
scrollWheelSensitivity:
|
||||||
title: Zoomempfindlichkeit
|
title: Zoomempfindlichkeit
|
||||||
description: >-
|
description: >-
|
||||||
Ändert die Sensitivität des Zooms (Sowohl Mausrad, als auch Trackpad).
|
Ändert die Empfindlichkeit des Zooms (Sowohl Mausrad, als auch Trackpad).
|
||||||
sensitivity:
|
sensitivity:
|
||||||
super_slow: Sehr langsam
|
super_slow: Sehr langsam
|
||||||
slow: Langsam
|
slow: Langsam
|
||||||
@ -722,30 +725,30 @@ settings:
|
|||||||
soundsMuted:
|
soundsMuted:
|
||||||
title: Geräusche stummschalten
|
title: Geräusche stummschalten
|
||||||
description: >-
|
description: >-
|
||||||
Bei Aktivierung werden alle Geräusche stummgeschaltet.
|
Bei der Aktivierung werden alle Geräusche stummgeschaltet.
|
||||||
|
|
||||||
musicMuted:
|
musicMuted:
|
||||||
title: Musik stummschalten
|
title: Musik stummschalten
|
||||||
description: >-
|
description: >-
|
||||||
Bei Aktivierung wird die Musik stummgeschaltet.
|
Bei der Aktivierung wird die Musik stummgeschaltet.
|
||||||
|
|
||||||
theme:
|
theme:
|
||||||
title: Farbmodus
|
title: Farbmodus
|
||||||
description: >-
|
description: >-
|
||||||
Wähle zwischen dunklem und hellem Farbmodus.
|
Wähle zwischen dem dunklen und dem hellen Farbmodus.
|
||||||
themes:
|
themes:
|
||||||
dark: Dunkel
|
dark: Dunkel
|
||||||
light: Hell
|
light: Hell
|
||||||
|
|
||||||
refreshRate:
|
refreshRate:
|
||||||
title: Zielbildwiederholrate
|
title: Tickrate
|
||||||
description: >-
|
description: >-
|
||||||
Für z.B. einen 144-Hz-Monitor kann die Bildwiederholrate hier korrekt eingestellt werden. Bei einem zu langsamen Computer kann dies die Leistung beeinträchtigen.
|
Das Spiel passt die Tickrate automatisch so an, dass sie immer zwischen diesem Wert und der hälfte bleibt. Zum Beispiel bei einer Tickrate von 60 Hz versucht das Spiel, diese zu halten. Falls dies zu viel ist, regelt der Computer diese runter bis zu einer Untergrenze von 30Hz.
|
||||||
|
|
||||||
alwaysMultiplace:
|
alwaysMultiplace:
|
||||||
title: Mehrfachplatzierung
|
title: Mehrfachplatzierung
|
||||||
description: >-
|
description: >-
|
||||||
Bei Aktivierung wird das platzierte Gebäude nicht abgewählt. Das hat den gleichen Effekt wie beim Platzieren permanent UMSCH gedrückt zu halten.
|
Bei Aktivierung wird das platzierte Gebäude nicht abgewählt. Das hat den gleichen Effekt wie beim Platzieren UMSCH gedrückt zu halten.
|
||||||
|
|
||||||
offerHints:
|
offerHints:
|
||||||
title: Hinweise & Tutorials
|
title: Hinweise & Tutorials
|
||||||
@ -762,11 +765,6 @@ settings:
|
|||||||
description: >-
|
description: >-
|
||||||
Aktiviert den Vignetteneffekt, der den Rand des Bildschirms zunehmend verdunkelt und das Lesen der Textfelder vereinfacht.
|
Aktiviert den Vignetteneffekt, der den Rand des Bildschirms zunehmend verdunkelt und das Lesen der Textfelder vereinfacht.
|
||||||
|
|
||||||
rotationByBuilding:
|
|
||||||
title: Rotation pro Gebäudetyp
|
|
||||||
description: >-
|
|
||||||
Jeder Gebäudetyp merkt sich einzeln, welche Rotation ausgewählt ist. Das fühlt sich möglicherweise besser an, wenn du häufig zwischen verschiedenen Gebäudetypen wechselst.
|
|
||||||
|
|
||||||
compactBuildingInfo:
|
compactBuildingInfo:
|
||||||
title: Kompakte Gebäudeinformationen
|
title: Kompakte Gebäudeinformationen
|
||||||
description: >-
|
description: >-
|
||||||
@ -777,6 +775,13 @@ settings:
|
|||||||
description: >-
|
description: >-
|
||||||
Deaktiviert die Warnung, die beim Löschen und Ausschneiden von mehr als 100 Feldern angezeigt wird.
|
Deaktiviert die Warnung, die beim Löschen und Ausschneiden von mehr als 100 Feldern angezeigt wird.
|
||||||
|
|
||||||
|
rotationByBuilding:
|
||||||
|
title: Rotation pro Gebäudetyp
|
||||||
|
description: >-
|
||||||
|
Jeder Gebäudetyp merkt sich einzeln, in welche Richtung er zeigt.
|
||||||
|
Das fühlt sich möglicherweise besser an, wenn du häufig zwischen verschiedenen
|
||||||
|
Gebäudetypen wechselst.
|
||||||
|
|
||||||
keybindings:
|
keybindings:
|
||||||
title: Tastenbelegung
|
title: Tastenbelegung
|
||||||
hint: >-
|
hint: >-
|
||||||
@ -785,7 +790,7 @@ keybindings:
|
|||||||
resetKeybindings: Tastenbelegung zurücksetzen.
|
resetKeybindings: Tastenbelegung zurücksetzen.
|
||||||
|
|
||||||
categoryLabels:
|
categoryLabels:
|
||||||
general: Applikation
|
general: Anwendung
|
||||||
ingame: Spiel
|
ingame: Spiel
|
||||||
navigation: Navigation
|
navigation: Navigation
|
||||||
placement: Platzierung
|
placement: Platzierung
|
||||||
@ -803,8 +808,8 @@ keybindings:
|
|||||||
mapMoveFaster: Schneller bewegen
|
mapMoveFaster: Schneller bewegen
|
||||||
centerMap: Karte zentrieren
|
centerMap: Karte zentrieren
|
||||||
|
|
||||||
mapZoomIn: Hineinzoomen
|
mapZoomIn: Reinzoomen
|
||||||
mapZoomOut: Herauszoomen
|
mapZoomOut: Rauszoomen
|
||||||
createMarker: Markierung erstellen
|
createMarker: Markierung erstellen
|
||||||
|
|
||||||
menuOpenShop: Upgrades
|
menuOpenShop: Upgrades
|
||||||
@ -832,7 +837,6 @@ keybindings:
|
|||||||
Modifikator: stattdessen gegen den UZS rotieren
|
Modifikator: stattdessen gegen den UZS rotieren
|
||||||
cycleBuildingVariants: Variante wählen
|
cycleBuildingVariants: Variante wählen
|
||||||
confirmMassDelete: Massenlöschung bestätigen
|
confirmMassDelete: Massenlöschung bestätigen
|
||||||
pasteLastBlueprint: Letzte Blaupause einfügen
|
|
||||||
cycleBuildings: Gebäude rotieren
|
cycleBuildings: Gebäude rotieren
|
||||||
lockBeltDirection: Bandplaner aktivieren
|
lockBeltDirection: Bandplaner aktivieren
|
||||||
switchDirectionLockSide: >-
|
switchDirectionLockSide: >-
|
||||||
@ -846,8 +850,10 @@ keybindings:
|
|||||||
placementDisableAutoOrientation: Automatische Orientierung deaktivieren
|
placementDisableAutoOrientation: Automatische Orientierung deaktivieren
|
||||||
placeMultiple: Im Platziermodus bleiben
|
placeMultiple: Im Platziermodus bleiben
|
||||||
placeInverse: Automatische Förderbandorientierung invertieren
|
placeInverse: Automatische Förderbandorientierung invertieren
|
||||||
advanced_processor: Farbnivertierer
|
pasteLastBlueprint: Letzte Blaupause einfügen
|
||||||
energy_generator: Energiegenerator
|
advanced_processor: Farbinvertierer
|
||||||
|
energy_generator: Stromgenerator
|
||||||
|
wire: Stromkabel
|
||||||
|
|
||||||
about:
|
about:
|
||||||
title: Über dieses Spiel
|
title: Über dieses Spiel
|
||||||
|
@ -488,8 +488,8 @@ buildings:
|
|||||||
name: Pivoteur inversé
|
name: Pivoteur inversé
|
||||||
description: Fait pivoter une forme de 90 degrés vers la gauche.
|
description: Fait pivoter une forme de 90 degrés vers la gauche.
|
||||||
fl:
|
fl:
|
||||||
name: Pivoteur (180)
|
name: Retourneur
|
||||||
description: Fait pivoter les formes de 90 degrés.
|
description: Tourne la forme de 180 degrés.
|
||||||
|
|
||||||
stacker:
|
stacker:
|
||||||
default:
|
default:
|
||||||
@ -635,9 +635,9 @@ storyRewards:
|
|||||||
settings:
|
settings:
|
||||||
title: Options
|
title: Options
|
||||||
categories:
|
categories:
|
||||||
general: General
|
general: Général
|
||||||
userInterface: User Interface
|
userInterface: Interface Utilisateur
|
||||||
advanced: Advanced
|
advanced: Avancé
|
||||||
|
|
||||||
versionBadges:
|
versionBadges:
|
||||||
dev: Développement
|
dev: Développement
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
---
|
---
|
||||||
steamPage:
|
steamPage:
|
||||||
# This is the short text appearing on the steam page
|
# This is the short text appearing on the steam page
|
||||||
shortText: shapez.io is a game about building factories to automate the creation and combination of increasingly complex shapes within an infinite map.
|
shortText: shapez.io giderek karmaşıklaşan şekillerin sonsuz bir harita üzerinde üretimi ve birleştirilmesi hakında bir oyundur.
|
||||||
|
|
||||||
# This is the long description for the steam page - It is contained here so you can help to translate it, and I will regulary update the store page.
|
# This is the long description for the steam page - It is contained here so you can help to translate it, and I will regulary update the store page.
|
||||||
# NOTICE:
|
# NOTICE:
|
||||||
@ -31,56 +31,57 @@ steamPage:
|
|||||||
longText: >-
|
longText: >-
|
||||||
[img]{STEAM_APP_IMAGE}/extras/store_page_gif.gif[/img]
|
[img]{STEAM_APP_IMAGE}/extras/store_page_gif.gif[/img]
|
||||||
|
|
||||||
shapez.io is a game about building factories to automate the creation and processing of increasingly complex shapes across an infinitely expanding map.
|
shapez.io giderek karmaşıklaşan şekillerin sonsuz bir harita üzerinde üretimi ve birleştirilmesi hakında bir oyundur.
|
||||||
Upon delivering the requested shapes you will progress within the game and unlock upgrades to Hız up your factory.
|
Talep edilen şekilleri verdikten sonra oyundaki geliştirmeleri açıp fabrikanızı hızlandırabilirsiniz.
|
||||||
|
|
||||||
As the demand for shapes increases, you will have to scale up your factory to meet the demand - Don't forget about resources though, you will have to expand across the [b]infinite map[/b]!
|
Şekiller için talep artınca fabrikanı büyütüp talebi karşılamalısın - Kaynakları unutma! Sonsuz [b]sonsuz haritada[/b] genişlemen gerekecek!
|
||||||
|
|
||||||
Soon you will have to mix colors and paint your shapes with them - Combine red, green and blue color resources to produce different colors and paint shapes with it to satisfy the demand.
|
Yakında renkleri karıştırman ve şekileri boyaman gerekecek - Talebi karşılamak için kırmızı, mavi ve yeşili karıştırıp şekilleri boyacaksın
|
||||||
|
|
||||||
This game features 18 progressive levels (Which should keep you busy for hours already!) but I'm constantly adding new content - There is a lot planned!
|
Bu oyun 18 kademeli seviye içerir (Seni saatlerce meşgul tutumalı) ama sürekli yeni şeyler ekliyorum - Ekleyecek çok şey var!
|
||||||
|
|
||||||
Purchasing the game gives you access to the standalone version which has additional features and you'll also receive access to newly developed features.
|
Bu oyunu satın almak indirilebilir versiyonuna (Ek özellikler var) ve yeni özeliklerine erişebileceksiniz.
|
||||||
|
|
||||||
[b]Standalone Advantages[/b]
|
[b]İndirebilir versiyonun avantajları[/b]
|
||||||
|
|
||||||
[list]
|
[list]
|
||||||
[*] Dark Mode
|
[*] Karanlık mod
|
||||||
[*] Unlimited Waypoints
|
[*] Sonsuz işaret
|
||||||
[*] Unlimited Savegames
|
[*] Sonsuz kayıt alanı
|
||||||
[*] Additional settings
|
[*] Ek ayarlar
|
||||||
[*] Coming soon: Wires & Energy! Aiming for (roughly) end of July 2020.
|
[*] Yakında geliyor: Kablo ve enerji! Temmuz sonunu hedefliyorum.
|
||||||
[*] Coming soon: More Levels
|
[*] Yakında geliyor: Daha fazla seviyeler
|
||||||
[*] Allows me to further develop shapez.io ❤️
|
[*] Shapez.io'yu geliştirmeme izin veriyor ❤️
|
||||||
[/list]
|
[/list]
|
||||||
|
|
||||||
[b]Future Updates[/b]
|
[b]Gelecek güncellemeler[/b]
|
||||||
|
|
||||||
I am updating the game very often and trying to push an update at least every week!
|
Oyunu sık sık güncelliyorum ve en az haftada bir güncellemeye çalışıyorum!
|
||||||
|
|
||||||
[list]
|
[list]
|
||||||
[*] Different maps and challenges (e.g. maps with obstacles)
|
[*] Farklı haritalar ve görevler (Örneğin engelli haritalar)
|
||||||
[*] Puzzles (Deliver the requested shape with a restricted area / set of buildings)
|
[*] Yapbozlar (İstenen şekilleri kısıtlı bir alanda / belli yapılar ile)
|
||||||
[*] A story mode where buildings have a cost
|
[*] Hikaye modu ile yapıların maliyeti olacak.
|
||||||
[*] Configurable map generator (Configure resource/shape size/density, seed and more)
|
[*] Ayarlanabilir harita yapımı(Ayarlanabilir kaynak/şekil boyut/sıklığı, seedler ve daha fazlası)
|
||||||
[*] Additional types of shapes
|
[*] Ek şekiller
|
||||||
[*] Performance improvements (The game already runs pretty well!)
|
[*] Performans optimizasyonları (oyun zaten iyi çalışıyor)
|
||||||
[*] And much more!
|
[*] Ve daha fazlası
|
||||||
[/list]
|
[/list]
|
||||||
|
|
||||||
[b]This game is open source![/b]
|
[b]Bu oyun açık kaynaklı![/b]
|
||||||
|
|
||||||
Anybody can contribute, I'm actively involved in the community and attempt to review all suggestions and take feedback into consideration where possible.
|
Bu oyuna herkes katkıda bulunabilir! Aktif olarak toplulukğa katkıda bulunuyorum ve bütün önerileri gözden geçirmeye çalışıyorum.
|
||||||
Be sure to check out my trello board for the full roadmap!
|
Yol planıma Trello'dan bakmayı unutmayın!
|
||||||
|
|
||||||
|
|
||||||
[b]Links[/b]
|
[b]Links[/b]
|
||||||
|
|
||||||
[list]
|
[list]
|
||||||
[*] [url=https://discord.com/invite/HN7EVzV]Official Discord[/url]
|
[*] [url=https://discord.com/invite/HN7EVzV]Dİscord'umuz[/url]
|
||||||
[*] [url=https://trello.com/b/ISQncpJP/shapezio]Roadmap[/url]
|
[*] [url=https://trello.com/b/ISQncpJP/shapezio]yol planı[/url]
|
||||||
[*] [url=https://www.reddit.com/r/shapezio]Subreddit[/url]
|
[*] [url=https://www.reddit.com/r/shapezio]Subreddit[/url]
|
||||||
[*] [url=https://github.com/tobspr/shapez.io]Source code (GitHub)[/url]
|
[*] [url=https://github.com/tobspr/shapez.io]KAynak kodu (GitHub)[/url]
|
||||||
[*] [url=https://github.com/tobspr/shapez.io/blob/master/translations/README.md]Help translate[/url]
|
[*] [url=https://github.com/tobspr/shapez.io/blob/master/translations/README.md]Çevirmeye yardımcı olun[/url]
|
||||||
[/list]
|
[/list]
|
||||||
|
|
||||||
discordLink: Official Discord - Chat with me!
|
discordLink: Official Discord - Chat with me!
|
||||||
|