v1.5.5 - Rework tutorial and polishing
This commit is contained in:
parent
482a4990ba
commit
8c5e593ceb
Binary file not shown.
After Width: | Height: | Size: 4.0 KiB |
Binary file not shown.
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 24 KiB |
Binary file not shown.
Binary file not shown.
|
@ -55,6 +55,35 @@
|
|||
position: relative;
|
||||
@include S(height, 40px);
|
||||
|
||||
@at-root html[data-tutorial-step="1_1_extractor"] &[data-id="miner"]:not(.selected),
|
||||
html[data-tutorial-step="1_2_conveyor"] &[data-id="belt"]:not(.selected),
|
||||
html[data-tutorial-step="2_1_place_cutter"] &[data-id="cutter"]:not(.selected),
|
||||
html[data-tutorial-step="2_2_place_trash"] &[data-id="trash"]:not(.selected) {
|
||||
&::before {
|
||||
content: "";
|
||||
|
||||
& {
|
||||
/* load-async */
|
||||
background: uiResource("icons/tutorial_arrow.png") center center / contain no-repeat;
|
||||
}
|
||||
|
||||
@include S(width, 25px);
|
||||
@include S(height, 25px);
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
bottom: 100%;
|
||||
transform: translateX(-50%);
|
||||
@include InlineAnimation(1s ease-in-out infinite) {
|
||||
50% {
|
||||
transform: translateX(-50%) translateY(20%);
|
||||
}
|
||||
}
|
||||
}
|
||||
@include S(border-radius, $globalBorderRadius);
|
||||
box-shadow: 0 0 D(10px) D(5px) rgba(74, 237, 134, 0.5) !important;
|
||||
background: rgba(74, 237, 134, 0.5) !important;
|
||||
}
|
||||
|
||||
.icon {
|
||||
color: $accentColorDark;
|
||||
display: flex;
|
||||
|
|
|
@ -32,12 +32,6 @@
|
|||
pointer-events: all;
|
||||
|
||||
transition: opacity 0.1s ease-out;
|
||||
&.hovered {
|
||||
opacity: 10%;
|
||||
.helperGif {
|
||||
opacity: 0%;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
color: #fff;
|
||||
|
|
|
@ -126,6 +126,7 @@
|
|||
@include S(height, 40px);
|
||||
background: #171a23 center center / contain no-repeat;
|
||||
|
||||
box-shadow: 0 D(3px) D(10px) rgba(96, 163, 136, 0.5);
|
||||
overflow: visible;
|
||||
@include S(border-radius, $globalBorderRadius);
|
||||
|
||||
|
@ -135,6 +136,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
opacity: 0.94 !important;
|
||||
}
|
||||
|
||||
> .discount {
|
||||
position: absolute;
|
||||
@include S(top, -7px);
|
||||
|
|
|
@ -102,30 +102,6 @@ button,
|
|||
opacity: 1;
|
||||
}
|
||||
|
||||
// ----------------------------------------
|
||||
/* Define a style which is only applied in horizontal mode */
|
||||
@mixin HorizontalStyle {
|
||||
@include AppendGlobal(".h") {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------
|
||||
/* Define a style which is only applied in vertical mode */
|
||||
@mixin VerticalStyle {
|
||||
@include AppendGlobal(".v") {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------
|
||||
/* Define a style which is only while the hardware keyboard is open */
|
||||
@mixin AndroidHwKeyboardOpen {
|
||||
@include AppendGlobal(".kb") {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------
|
||||
/* Automatically transforms the game state if a hardware keyboard is open */
|
||||
@mixin TransformToMatchKeyboard {
|
||||
|
|
|
@ -61,7 +61,7 @@
|
|||
opacity: 0;
|
||||
display: none;
|
||||
transform: translate(50%, 50%);
|
||||
filter: blur(D(7px));
|
||||
filter: blur(D(15px));
|
||||
|
||||
$opacity: 0.4;
|
||||
&.loaded {
|
||||
|
@ -81,34 +81,41 @@
|
|||
|
||||
.mainWrapper {
|
||||
@include S(padding, 0, 10px);
|
||||
@include S(margin-top, 15px);
|
||||
align-items: start;
|
||||
justify-items: center;
|
||||
|
||||
@include S(grid-column-gap, 10px);
|
||||
display: grid;
|
||||
|
||||
grid-template-rows: D(31px) 1fr D(93px);
|
||||
|
||||
&[data-columns="1"] {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
&[data-columns="2"] {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-template-columns: D(290px) 1fr;
|
||||
}
|
||||
|
||||
.standaloneBanner {
|
||||
background: rgba(12, 168, 93, 0.957);
|
||||
background: transparent;
|
||||
@include S(border-radius, $globalBorderRadius);
|
||||
// box-shadow: 0 D(5px) D(15px) rgba(#000, 0.2);
|
||||
@include S(width, 380px);
|
||||
box-sizing: border-box;
|
||||
@include S(padding, 15px);
|
||||
box-shadow: 0 D(5px) D(15px) rgba(#000, 0.2);
|
||||
@include S(padding, 0, 15px);
|
||||
// backdrop-filter: blur(10px);
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 0;
|
||||
|
||||
strong {
|
||||
font-weight: 700 !important;
|
||||
}
|
||||
|
||||
.onlinePlayerCount {
|
||||
color: #fff;
|
||||
color: #333;
|
||||
@include S(margin-top, 15px);
|
||||
@include SuperSmallText;
|
||||
@include S(height, 15px);
|
||||
|
@ -118,15 +125,14 @@
|
|||
h3 {
|
||||
@include Heading;
|
||||
font-weight: bold;
|
||||
@include S(margin-bottom, 20px);
|
||||
display: none;
|
||||
text-transform: uppercase;
|
||||
color: #fff;
|
||||
@include S(margin-bottom, 10px);
|
||||
text-align: center;
|
||||
color: #44484f;
|
||||
}
|
||||
|
||||
p {
|
||||
@include Text;
|
||||
color: #fff;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
ul {
|
||||
|
@ -139,7 +145,7 @@
|
|||
}
|
||||
|
||||
.playtimeDisclaimer {
|
||||
color: #fff;
|
||||
color: #333;
|
||||
@include S(margin-top, 15px);
|
||||
@include SuperSmallText;
|
||||
}
|
||||
|
@ -162,6 +168,7 @@
|
|||
@include S(border-radius, $globalBorderRadius);
|
||||
color: transparent;
|
||||
|
||||
box-shadow: 0 D(3px) D(10px) rgba(96, 163, 136, 0.5);
|
||||
&:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
@ -190,6 +197,151 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.points {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
width: 100%;
|
||||
@include S(grid-gap, 5px);
|
||||
}
|
||||
|
||||
.point {
|
||||
display: grid;
|
||||
grid-template-columns: #{D(27px)} auto;
|
||||
grid-template-rows: D(11px) D(10px);
|
||||
background: #fff #{D(10px)} center / #{D(17px)} no-repeat;
|
||||
@include S(grid-row-gap, 3px);
|
||||
align-items: center;
|
||||
@include S(padding, 6px);
|
||||
@include S(border-radius, $globalBorderRadius);
|
||||
|
||||
box-shadow: 0 D(5px) D(10px) rgba(#000, 0.2);
|
||||
|
||||
> strong {
|
||||
grid-column: 2 / 3;
|
||||
grid-row: 1 / 2;
|
||||
@include PlainText;
|
||||
@include S(font-size, 12px);
|
||||
line-height: 0.8em;
|
||||
white-space: nowrap;
|
||||
text-transform: uppercase;
|
||||
font-weight: bold;
|
||||
align-self: end;
|
||||
}
|
||||
|
||||
> p {
|
||||
grid-column: 2 / 3;
|
||||
grid-row: 2 / 3;
|
||||
@include SuperSmallText;
|
||||
white-space: nowrap;
|
||||
@include BreakText;
|
||||
@include S(font-size, 8px);
|
||||
line-height: 1em;
|
||||
align-self: start;
|
||||
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
&.levels {
|
||||
& {
|
||||
/* @load-async */
|
||||
background-image: uiResource("res/ui/icons/advantage_new_levels.png");
|
||||
}
|
||||
> strong {
|
||||
color: #f13555;
|
||||
}
|
||||
}
|
||||
|
||||
&.upgrades {
|
||||
& {
|
||||
/* @load-async */
|
||||
background-image: uiResource("res/ui/icons/advantage_upgrades.png");
|
||||
}
|
||||
> strong {
|
||||
color: #8a00ff;
|
||||
}
|
||||
}
|
||||
|
||||
&.buildings {
|
||||
& {
|
||||
/* @load-async */
|
||||
background-image: uiResource("res/ui/icons/advantage_buildings.png");
|
||||
}
|
||||
> strong {
|
||||
color: #3fce8b;
|
||||
}
|
||||
}
|
||||
|
||||
&.wires {
|
||||
& {
|
||||
/* @load-async */
|
||||
background-image: uiResource("res/ui/icons/advantage_wires.png");
|
||||
}
|
||||
> strong {
|
||||
color: #ef2fdb;
|
||||
}
|
||||
}
|
||||
|
||||
&.markers {
|
||||
& {
|
||||
/* @load-async */
|
||||
background-image: uiResource("res/ui/icons/advantage_markers.png");
|
||||
}
|
||||
> strong {
|
||||
color: #4294ff;
|
||||
}
|
||||
}
|
||||
|
||||
&.mods {
|
||||
& {
|
||||
/* @load-async */
|
||||
background-image: uiResource("res/ui/icons/advantage_mods.png");
|
||||
}
|
||||
> strong {
|
||||
color: #8a00ff;
|
||||
}
|
||||
}
|
||||
|
||||
&.savegames {
|
||||
& {
|
||||
/* @load-async */
|
||||
background-image: uiResource("res/ui/icons/advantage_savegames.png");
|
||||
}
|
||||
> strong {
|
||||
color: #ff9500;
|
||||
}
|
||||
}
|
||||
|
||||
&.darkmode {
|
||||
& {
|
||||
/* @load-async */
|
||||
background-image: uiResource("res/ui/icons/advantage_dark_mode.png");
|
||||
}
|
||||
> strong {
|
||||
color: #292c32;
|
||||
}
|
||||
}
|
||||
|
||||
&.support {
|
||||
& {
|
||||
/* @load-async */
|
||||
background-image: uiResource("res/ui/icons/advantage_support.png");
|
||||
}
|
||||
> strong {
|
||||
color: #e72d2d;
|
||||
}
|
||||
}
|
||||
|
||||
&.achievements {
|
||||
& {
|
||||
/* @load-async */
|
||||
background-image: uiResource("res/ui/icons/advantage_achievements.png");
|
||||
}
|
||||
> strong {
|
||||
color: #ffac0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -203,8 +355,8 @@
|
|||
@include S(padding-top, 20px);
|
||||
|
||||
img {
|
||||
@include S(width, 710px / 2.2);
|
||||
@include S(height, 180 / 2.2px);
|
||||
@include S(width, 710px / 2.5);
|
||||
@include S(height, 180px / 2.5);
|
||||
}
|
||||
position: relative;
|
||||
@include S(left, -8px);
|
||||
|
@ -243,11 +395,12 @@
|
|||
.sideContainer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@include S(width, 300px);
|
||||
width: 100%;
|
||||
grid-row: 1 / 4;
|
||||
grid-column: 2 / 3;
|
||||
|
||||
.standaloneBanner {
|
||||
flex-grow: 1;
|
||||
@include S(margin-bottom, 10px);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -258,7 +411,6 @@
|
|||
flex-direction: column;
|
||||
background: $colorBlueBright;
|
||||
grid-row: 1 / 2;
|
||||
grid-column: 2 / 3;
|
||||
position: relative;
|
||||
@include S(padding, 20px);
|
||||
@include S(border-radius, $globalBorderRadius);
|
||||
|
@ -324,7 +476,6 @@
|
|||
flex-direction: column;
|
||||
background: #fff;
|
||||
grid-row: 1 / 2;
|
||||
grid-column: 2 / 3;
|
||||
position: relative;
|
||||
text-align: left;
|
||||
align-items: flex-start;
|
||||
|
@ -418,18 +569,24 @@
|
|||
.mainContainer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
grid-row: 1 / 2;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
background: #fafafa;
|
||||
@include S(padding, 20px);
|
||||
background: rgba(#fff, 0.9);
|
||||
@include S(padding, 15px);
|
||||
@include S(border-radius, $globalBorderRadius);
|
||||
// border: #{D(2px)} solid rgba(0, 10, 20, 0.1);
|
||||
|
||||
box-shadow: 0 D(5px) D(15px) rgba(#000, 0.2);
|
||||
height: 100%;
|
||||
box-shadow: 0 D(5px) D(15px) rgba(#000, 0.2);
|
||||
width: 100%;
|
||||
position: relative;
|
||||
align-self: center;
|
||||
justify-self: center;
|
||||
grid-row: 1 / 4;
|
||||
grid-column: 1 / 2;
|
||||
|
||||
// &[data-savegames="0"] {
|
||||
// grid-row: 2 / 3;
|
||||
// }
|
||||
|
||||
box-sizing: border-box;
|
||||
|
||||
.buttons {
|
||||
|
@ -483,6 +640,16 @@
|
|||
|
||||
.outer {
|
||||
@include S(margin-top, 15px);
|
||||
display: grid;
|
||||
grid-auto-flow: column;
|
||||
grid-auto-columns: 1fr;
|
||||
|
||||
@include S(grid-gap, 5px);
|
||||
width: 100%;
|
||||
|
||||
> button {
|
||||
@include S(padding, 3px, 6px);
|
||||
}
|
||||
}
|
||||
|
||||
.importButton {
|
||||
|
@ -491,14 +658,10 @@
|
|||
|
||||
.newGameButton {
|
||||
@include IncreasedClickArea(0px);
|
||||
@include S(margin-left, 10px);
|
||||
}
|
||||
|
||||
.modsButton {
|
||||
@include IncreasedClickArea(0px);
|
||||
@include S(margin-left, 10px);
|
||||
|
||||
// @include S(width, 20px);
|
||||
|
||||
background-position: center center;
|
||||
background-size: D(15px);
|
||||
|
@ -509,9 +672,11 @@
|
|||
.savegames {
|
||||
@include S(max-height, 105px);
|
||||
overflow-y: auto;
|
||||
@include S(width, 250px);
|
||||
@include S(min-width, 230px);
|
||||
width: 100%;
|
||||
pointer-events: all;
|
||||
@include S(padding-right, 5px);
|
||||
margin-right: D(-5px);
|
||||
display: grid;
|
||||
grid-auto-flow: row;
|
||||
@include S(grid-gap, 5px);
|
||||
|
@ -586,6 +751,7 @@
|
|||
@include S(height, 15px);
|
||||
background-size: 80%;
|
||||
align-self: start;
|
||||
border-radius: 0;
|
||||
opacity: 0.4;
|
||||
|
||||
&:hover {
|
||||
|
@ -608,6 +774,7 @@
|
|||
@include S(height, 15px);
|
||||
align-self: end;
|
||||
background-size: 80%;
|
||||
border-radius: 0;
|
||||
opacity: 0.4;
|
||||
|
||||
&:hover {
|
||||
|
@ -628,7 +795,7 @@
|
|||
@include S(height, 10px);
|
||||
align-self: center;
|
||||
justify-self: center;
|
||||
|
||||
border-radius: 0;
|
||||
background-size: 90%;
|
||||
opacity: 0.4;
|
||||
@include S(margin-left, 4px);
|
||||
|
@ -725,7 +892,7 @@
|
|||
|
||||
a {
|
||||
&:hover img {
|
||||
opacity: 0.8;
|
||||
opacity: 0.85;
|
||||
}
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
@ -736,7 +903,7 @@
|
|||
@include S(width, 82px);
|
||||
@include S(height, 25px);
|
||||
filter: invert(100%);
|
||||
opacity: 0.6;
|
||||
opacity: 0.75;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -744,7 +911,7 @@
|
|||
@include S(padding, 15px);
|
||||
|
||||
$linkBg: rgba(#fdfdff, 0.5);
|
||||
$linkBgHover: darken($linkBg, 5);
|
||||
$linkBgHover: rgba(#fff, 0.7);
|
||||
$linkColor: #55586a;
|
||||
|
||||
> .boxLink {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#state_MobileWarningState {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: #333438 !important;
|
||||
background: #555b75 !important;
|
||||
@include S(padding, 20px);
|
||||
box-sizing: border-box;
|
||||
justify-content: center;
|
||||
|
@ -14,7 +14,7 @@
|
|||
}
|
||||
|
||||
p {
|
||||
color: #aaacaf;
|
||||
color: rgba(#fff, 0.5);
|
||||
display: block;
|
||||
margin-bottom: 13px;
|
||||
font-size: 16px;
|
||||
|
@ -28,12 +28,11 @@
|
|||
|
||||
.standaloneLink {
|
||||
width: 200px;
|
||||
height: 80px;
|
||||
height: 48px;
|
||||
min-height: 40px;
|
||||
& {
|
||||
background: uiResource("steam_link_btn/0.png") center center / contain no-repeat;
|
||||
background: #000 uiResource("steam_link_btn/0.png") center center / contain no-repeat;
|
||||
}
|
||||
overflow: hidden;
|
||||
display: block;
|
||||
text-indent: -999em;
|
||||
cursor: pointer;
|
||||
|
@ -41,7 +40,9 @@
|
|||
pointer-events: all;
|
||||
transition: all 0.12s ease-in;
|
||||
transition-property: opacity, transform;
|
||||
transform: skewX(-0.5deg);
|
||||
@include S(border-radius, $globalBorderRadius);
|
||||
overflow: hidden;
|
||||
|
||||
&:hover {
|
||||
transform: skewX(-1deg) scale(1.02);
|
||||
opacity: 0.9;
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
transition: all 0.12s ease-in;
|
||||
transition-property: opacity, transform;
|
||||
|
||||
box-shadow: 0 D(3px) D(10px) rgba(96, 163, 136, 0.5);
|
||||
@include S(border-radius, $globalBorderRadius);
|
||||
|
||||
&:hover {
|
||||
|
|
|
@ -178,34 +178,3 @@ $mainFontScale: 1;
|
|||
@function trim($string) {
|
||||
@return str-slice($string, _first-index($string, "left"), _first-index($string, "right"));
|
||||
}
|
||||
|
||||
@mixin AppendGlobal($prefix) {
|
||||
$strSelector: quote(&);
|
||||
$selectors: str-split($strSelector, ",");
|
||||
|
||||
$builtSelector: null;
|
||||
|
||||
@if (& == null) {
|
||||
$builtSelector: "html" + $prefix;
|
||||
} @else {
|
||||
$builtSelector: ();
|
||||
// @debug ($strSelector, "->>>", $selectors);
|
||||
@each $srcSelector in $selectors {
|
||||
$srcSelector: trim($srcSelector);
|
||||
// @debug ("___", $srcSelector);
|
||||
$selector: "html" + $prefix + " " + $srcSelector;
|
||||
@if str-index($srcSelector, "html.") {
|
||||
$selector: "html" +
|
||||
$prefix +
|
||||
"." +
|
||||
str-slice($srcSelector, str-index($srcSelector, "html.") + 5);
|
||||
}
|
||||
// @debug ("_______", $selector);
|
||||
$builtSelector: append($builtSelector, $selector, comma);
|
||||
}
|
||||
}
|
||||
|
||||
@at-root #{$builtSelector} {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,13 @@
|
|||
export const CHANGELOG = [
|
||||
{
|
||||
version: "1.5.5",
|
||||
date: "20.06.2022",
|
||||
entries: [
|
||||
"Reworked the tutorial to be simpler and more interactive",
|
||||
"General polishing",
|
||||
"Updated translations",
|
||||
],
|
||||
},
|
||||
{
|
||||
version: "1.5.3",
|
||||
date: "05.06.2022",
|
||||
|
|
|
@ -183,7 +183,7 @@ export class BeltPath extends BasicSerializableObject {
|
|||
* Recomputes cache variables once the path was changed
|
||||
*/
|
||||
onPathChanged() {
|
||||
this.boundAcceptor = this.computeAcceptingEntityAndSlot();
|
||||
this.boundAcceptor = this.computeAcceptingEntityAndSlot().acceptor;
|
||||
|
||||
/**
|
||||
* How many items past the first item are compressed
|
||||
|
@ -201,7 +201,7 @@ export class BeltPath extends BasicSerializableObject {
|
|||
/**
|
||||
* Finds the entity which accepts our items
|
||||
* @param {boolean=} debug_Silent Whether debug output should be silent
|
||||
* @return { (BaseItem, number?) => boolean }
|
||||
* @return { { acceptor?: (BaseItem, number?) => boolean, entity?: Entity } }
|
||||
*/
|
||||
computeAcceptingEntityAndSlot(debug_Silent = false) {
|
||||
DEBUG && !debug_Silent && logger.log("Recomputing acceptor target");
|
||||
|
@ -224,7 +224,7 @@ export class BeltPath extends BasicSerializableObject {
|
|||
);
|
||||
|
||||
if (!targetEntity) {
|
||||
return;
|
||||
return {};
|
||||
}
|
||||
|
||||
const noSimplifiedBelts = !this.root.app.settings.getAllSettings().simplifiedBelts;
|
||||
|
@ -247,10 +247,13 @@ export class BeltPath extends BasicSerializableObject {
|
|||
targetStaticComp.rotation
|
||||
);
|
||||
if (ejectSlotWsDirection === beltAcceptingDirection) {
|
||||
return item => {
|
||||
const path = targetBeltComp.assignedPath;
|
||||
assert(path, "belt has no path");
|
||||
return path.tryAcceptItem(item);
|
||||
return {
|
||||
entity: targetEntity,
|
||||
acceptor: item => {
|
||||
const path = targetBeltComp.assignedPath;
|
||||
assert(path, "belt has no path");
|
||||
return path.tryAcceptItem(item);
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -259,7 +262,7 @@ export class BeltPath extends BasicSerializableObject {
|
|||
const targetAcceptorComp = targetEntity.components.ItemAcceptor;
|
||||
if (!targetAcceptorComp) {
|
||||
// Entity doesn't accept items
|
||||
return;
|
||||
return {};
|
||||
}
|
||||
|
||||
const ejectingDirection = targetStaticComp.worldDirectionToLocal(ejectSlotWsDirection);
|
||||
|
@ -270,38 +273,41 @@ export class BeltPath extends BasicSerializableObject {
|
|||
|
||||
if (!matchingSlot) {
|
||||
// No matching slot found
|
||||
return;
|
||||
return {};
|
||||
}
|
||||
|
||||
const matchingSlotIndex = matchingSlot.index;
|
||||
const passOver = this.computePassOverFunctionWithoutBelts(targetEntity, matchingSlotIndex);
|
||||
if (!passOver) {
|
||||
return;
|
||||
return {};
|
||||
}
|
||||
|
||||
const matchingDirection = enumInvertedDirections[ejectingDirection];
|
||||
const filter = matchingSlot.slot.filter;
|
||||
|
||||
return function (item, remainingProgress = 0.0) {
|
||||
// Check if the acceptor has a filter
|
||||
if (filter && item._type !== filter) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Try to pass over
|
||||
if (passOver(item, matchingSlotIndex)) {
|
||||
// Trigger animation on the acceptor comp
|
||||
if (noSimplifiedBelts) {
|
||||
targetAcceptorComp.onItemAccepted(
|
||||
matchingSlotIndex,
|
||||
matchingDirection,
|
||||
item,
|
||||
remainingProgress
|
||||
);
|
||||
return {
|
||||
entity: targetEntity,
|
||||
acceptor: function (item, remainingProgress = 0.0) {
|
||||
// Check if the acceptor has a filter
|
||||
if (filter && item._type !== filter) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
// Try to pass over
|
||||
if (passOver(item, matchingSlotIndex)) {
|
||||
// Trigger animation on the acceptor comp
|
||||
if (noSimplifiedBelts) {
|
||||
targetAcceptorComp.onItemAccepted(
|
||||
matchingSlotIndex,
|
||||
matchingDirection,
|
||||
item,
|
||||
remainingProgress
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -494,7 +500,7 @@ export class BeltPath extends BasicSerializableObject {
|
|||
}
|
||||
|
||||
// Check acceptor
|
||||
const acceptor = this.computeAcceptingEntityAndSlot(true);
|
||||
const acceptor = this.computeAcceptingEntityAndSlot(true).acceptor;
|
||||
if (!!acceptor !== !!this.boundAcceptor) {
|
||||
return fail("Acceptor target mismatch, acceptor", !!acceptor, "vs stored", !!this.boundAcceptor);
|
||||
}
|
||||
|
|
|
@ -168,7 +168,7 @@ export class Camera extends BasicSerializableObject {
|
|||
* Finds a good initial zoom level
|
||||
*/
|
||||
findInitialZoom() {
|
||||
const desiredWorldSpaceWidth = 15 * globalConfig.tileSize;
|
||||
const desiredWorldSpaceWidth = 18 * globalConfig.tileSize;
|
||||
const zoomLevelX = this.root.gameWidth / desiredWorldSpaceWidth;
|
||||
const zoomLevelY = this.root.gameHeight / desiredWorldSpaceWidth;
|
||||
|
||||
|
|
|
@ -189,6 +189,7 @@ export class GameCore {
|
|||
});
|
||||
this.root.map.placeStaticEntity(hub);
|
||||
this.root.entityMgr.registerEntity(hub);
|
||||
this.root.camera.center = new Vector(-5, 2).multiplyScalar(globalConfig.tileSize);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -287,6 +287,7 @@ export class HubGoals extends BasicSerializableObject {
|
|||
* @param {string} upgradeId
|
||||
*/
|
||||
canUnlockUpgrade(upgradeId) {
|
||||
return true;
|
||||
const tiers = this.root.gameMode.getUpgrades()[upgradeId];
|
||||
const currentLevel = this.getUpgradeLevel(upgradeId);
|
||||
|
||||
|
|
|
@ -198,6 +198,7 @@ export class GameHUD {
|
|||
"changesDebugger",
|
||||
"minerHighlight",
|
||||
"shapeTooltip",
|
||||
"interactiveTutorial",
|
||||
];
|
||||
|
||||
for (let i = 0; i < partsOrder.length; ++i) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { BaseHUDPart } from "../base_hud_part";
|
||||
import { makeDiv } from "../../../core/utils";
|
||||
import { clamp, makeDiv, smoothPulse } from "../../../core/utils";
|
||||
import { GameRoot } from "../../root";
|
||||
import { MinerComponent } from "../../components/miner";
|
||||
import { DynamicDomAttach } from "../dynamic_dom_attach";
|
||||
|
@ -10,6 +10,15 @@ import { enumItemProcessorTypes, ItemProcessorComponent } from "../../components
|
|||
import { ShapeItem } from "../../items/shape_item";
|
||||
import { WireComponent } from "../../components/wire";
|
||||
import { LeverComponent } from "../../components/lever";
|
||||
import { DrawParameters } from "../../../core/draw_parameters";
|
||||
import { globalConfig } from "../../../core/config";
|
||||
import { Vector } from "../../../core/vector";
|
||||
import { MetaMinerBuilding } from "../../buildings/miner";
|
||||
import { gMetaBuildingRegistry } from "../../../core/global_registries";
|
||||
import { MetaBeltBuilding } from "../../buildings/belt";
|
||||
import { BeltComponent } from "../../components/belt";
|
||||
import { MetaTrashBuilding } from "../../buildings/trash";
|
||||
import { SOUNDS } from "../../../platform/sound";
|
||||
|
||||
// @todo: Make dictionary
|
||||
const tutorialsByLevel = [
|
||||
|
@ -24,12 +33,30 @@ const tutorialsByLevel = [
|
|||
// 1.2. connect to hub
|
||||
{
|
||||
id: "1_2_conveyor",
|
||||
condition: /** @param {GameRoot} root */ root => root.hubGoals.getCurrentGoalDelivered() === 0,
|
||||
condition: /** @param {GameRoot} root */ root => {
|
||||
const paths = root.systemMgr.systems.belt.beltPaths;
|
||||
const miners = root.entityMgr.getAllWithComponent(MinerComponent);
|
||||
for (let i = 0; i < paths.length; i++) {
|
||||
const path = paths[i];
|
||||
const acceptingEntity = path.computeAcceptingEntityAndSlot().entity;
|
||||
if (!acceptingEntity || !acceptingEntity.components.Hub) {
|
||||
continue;
|
||||
}
|
||||
// Find a miner which delivers to this belt path
|
||||
for (let k = 0; k < miners.length; ++k) {
|
||||
const miner = miners[k];
|
||||
if (miner.components.ItemEjector.slots[0].cachedBeltPath === path) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
},
|
||||
// 1.3 wait for completion
|
||||
{
|
||||
id: "1_3_expand",
|
||||
condition: () => true,
|
||||
condition: /** @param {GameRoot} root */ root => true,
|
||||
},
|
||||
],
|
||||
// Level 2
|
||||
|
@ -55,11 +82,7 @@ const tutorialsByLevel = [
|
|||
// 2.3 place more cutters
|
||||
{
|
||||
id: "2_3_more_cutters",
|
||||
condition: /** @param {GameRoot} root */ root =>
|
||||
root.entityMgr
|
||||
.getAllWithComponent(ItemProcessorComponent)
|
||||
.filter(e => e.components.ItemProcessor.type === enumItemProcessorTypes.cutter).length <
|
||||
3,
|
||||
condition: /** @param {GameRoot} root */ root => true,
|
||||
},
|
||||
],
|
||||
|
||||
|
@ -158,7 +181,7 @@ export class HUDInteractiveTutorial extends BaseHUDPart {
|
|||
|
||||
onHintChanged(hintId) {
|
||||
this.elementDescription.innerHTML = T.ingame.interactiveTutorial.hints[hintId];
|
||||
|
||||
document.documentElement.setAttribute("data-tutorial-step", hintId);
|
||||
const folder = G_WEGAME_VERSION
|
||||
? "interactive_tutorial.cn.noinline"
|
||||
: "interactive_tutorial.noinline";
|
||||
|
@ -167,6 +190,9 @@ export class HUDInteractiveTutorial extends BaseHUDPart {
|
|||
"url('" + cachebust("res/ui/" + folder + "/" + hintId + ".gif") + "')";
|
||||
this.element.classList.toggle("animEven");
|
||||
this.element.classList.toggle("animOdd");
|
||||
if (hintId) {
|
||||
this.root.app.sound.playUiSound(SOUNDS.tutorialStep);
|
||||
}
|
||||
}
|
||||
|
||||
update() {
|
||||
|
@ -187,4 +213,226 @@ export class HUDInteractiveTutorial extends BaseHUDPart {
|
|||
this.currentHintId.set(targetHintId);
|
||||
this.domAttach.update(!!targetHintId);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {DrawParameters} parameters
|
||||
*/
|
||||
draw(parameters) {
|
||||
const animation = smoothPulse(this.root.time.now());
|
||||
const currentBuilding = this.root.hud.parts.buildingPlacer.currentMetaBuilding.get();
|
||||
|
||||
if (["1_1_extractor"].includes(this.currentHintId.get())) {
|
||||
if (
|
||||
currentBuilding &&
|
||||
currentBuilding.getId() === gMetaBuildingRegistry.findByClass(MetaMinerBuilding).getId()
|
||||
) {
|
||||
// Find closest circle patch to hub
|
||||
|
||||
let closest = null;
|
||||
let closestDistance = 1e10;
|
||||
|
||||
for (let i = 0; i > -globalConfig.mapChunkSize; --i) {
|
||||
for (let j = 0; j < globalConfig.mapChunkSize; ++j) {
|
||||
const resourceItem = this.root.map.getLowerLayerContentXY(i, j);
|
||||
if (
|
||||
resourceItem instanceof ShapeItem &&
|
||||
resourceItem.definition.getHash() === "CuCuCuCu"
|
||||
) {
|
||||
let distance = Math.hypot(i, j);
|
||||
if (!closest || distance < closestDistance) {
|
||||
const tile = new Vector(i, j);
|
||||
if (!this.root.map.getTileContent(tile, "regular")) {
|
||||
closest = tile;
|
||||
closestDistance = distance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (closest) {
|
||||
parameters.context.fillStyle = "rgba(74, 237, 134, " + (0.5 - animation * 0.2) + ")";
|
||||
parameters.context.strokeStyle = "rgb(74, 237, 134)";
|
||||
parameters.context.lineWidth = 2;
|
||||
parameters.context.beginRoundedRect(
|
||||
closest.x * globalConfig.tileSize - 2 * animation,
|
||||
closest.y * globalConfig.tileSize - 2 * animation,
|
||||
globalConfig.tileSize + 4 * animation,
|
||||
globalConfig.tileSize + 4 * animation,
|
||||
3
|
||||
);
|
||||
parameters.context.fill();
|
||||
parameters.context.stroke();
|
||||
parameters.context.globalAlpha = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.currentHintId.get() === "1_2_conveyor") {
|
||||
if (
|
||||
currentBuilding &&
|
||||
currentBuilding.getId() === gMetaBuildingRegistry.findByClass(MetaBeltBuilding).getId()
|
||||
) {
|
||||
// Find closest miner
|
||||
const miners = this.root.entityMgr.getAllWithComponent(MinerComponent);
|
||||
|
||||
let closest = null;
|
||||
let closestDistance = 1e10;
|
||||
|
||||
for (let i = 0; i < miners.length; i++) {
|
||||
const miner = miners[i];
|
||||
const distance = miner.components.StaticMapEntity.origin.lengthSquare();
|
||||
|
||||
if (![0, 90].includes(miner.components.StaticMapEntity.rotation)) {
|
||||
continue;
|
||||
}
|
||||
if (!closest || distance < closestDistance) {
|
||||
closest = miner;
|
||||
}
|
||||
}
|
||||
|
||||
if (closest) {
|
||||
// draw line from miner to hub -> But respect orientation
|
||||
|
||||
const staticComp = closest.components.StaticMapEntity;
|
||||
|
||||
const offset = staticComp.rotation === 0 ? new Vector(0.5, 0) : new Vector(1, 0.5);
|
||||
|
||||
const anchor =
|
||||
staticComp.rotation === 0
|
||||
? new Vector(staticComp.origin.x + 0.5, 0.5)
|
||||
: new Vector(-0.5, staticComp.origin.y + 0.5);
|
||||
|
||||
const target = staticComp.rotation === 0 ? new Vector(-2.1, 0.5) : new Vector(-0.5, 2.1);
|
||||
|
||||
parameters.context.globalAlpha = 0.1 + animation * 0.1;
|
||||
parameters.context.strokeStyle = "rgb(74, 237, 134)";
|
||||
parameters.context.lineWidth = globalConfig.tileSize / 2;
|
||||
parameters.context.beginPath();
|
||||
parameters.context.moveTo(
|
||||
(staticComp.origin.x + offset.x) * globalConfig.tileSize,
|
||||
(staticComp.origin.y + offset.y) * globalConfig.tileSize
|
||||
);
|
||||
parameters.context.lineTo(
|
||||
anchor.x * globalConfig.tileSize,
|
||||
anchor.y * globalConfig.tileSize
|
||||
);
|
||||
parameters.context.lineTo(
|
||||
target.x * globalConfig.tileSize,
|
||||
target.y * globalConfig.tileSize
|
||||
);
|
||||
parameters.context.stroke();
|
||||
parameters.context.globalAlpha = 1;
|
||||
|
||||
const arrowSprite = this.root.hud.parts.buildingPlacer.lockIndicatorSprites.regular;
|
||||
|
||||
let arrows = [];
|
||||
|
||||
let pos = staticComp.origin.add(offset);
|
||||
let delta = anchor.sub(pos).normalize();
|
||||
let maxIter = 999;
|
||||
|
||||
while (pos.distanceSquare(anchor) > 1 && maxIter-- > 0) {
|
||||
pos = pos.add(delta);
|
||||
arrows.push({
|
||||
pos: pos.sub(offset),
|
||||
rotation: staticComp.rotation,
|
||||
});
|
||||
}
|
||||
|
||||
pos = anchor.copy();
|
||||
delta = target.sub(pos).normalize();
|
||||
const localDelta =
|
||||
staticComp.rotation === 0 ? new Vector(-1.5, -0.5) : new Vector(-0.5, 0.5);
|
||||
while (pos.distanceSquare(target) > 1 && maxIter-- > 0) {
|
||||
pos = pos.add(delta);
|
||||
arrows.push({
|
||||
pos: pos.add(localDelta),
|
||||
rotation: 90 - staticComp.rotation,
|
||||
});
|
||||
}
|
||||
|
||||
for (let i = 0; i < arrows.length; i++) {
|
||||
const { pos, rotation } = arrows[i];
|
||||
const worldPos = pos.toWorldSpaceCenterOfTile();
|
||||
const angle = Math.radians(rotation);
|
||||
|
||||
parameters.context.translate(worldPos.x, worldPos.y);
|
||||
parameters.context.rotate(angle);
|
||||
parameters.context.drawImage(
|
||||
arrowSprite,
|
||||
-6,
|
||||
-globalConfig.halfTileSize -
|
||||
clamp((this.root.time.realtimeNow() * 1.5) % 1.0, 0, 1) *
|
||||
1 *
|
||||
globalConfig.tileSize +
|
||||
globalConfig.halfTileSize -
|
||||
6,
|
||||
12,
|
||||
12
|
||||
);
|
||||
parameters.context.rotate(-angle);
|
||||
parameters.context.translate(-worldPos.x, -worldPos.y);
|
||||
}
|
||||
|
||||
parameters.context.fillStyle = "rgb(30, 40, 60)";
|
||||
parameters.context.font = "15px GameFont";
|
||||
|
||||
if (staticComp.rotation === 0) {
|
||||
const pos = staticComp.origin.toWorldSpace().subScalars(2, 10);
|
||||
parameters.context.translate(pos.x, pos.y);
|
||||
parameters.context.rotate(-Math.radians(90));
|
||||
parameters.context.fillText(
|
||||
T.ingame.interactiveTutorial.hints["1_2_hold_and_drag"],
|
||||
0,
|
||||
0
|
||||
);
|
||||
parameters.context.rotate(Math.radians(90));
|
||||
parameters.context.translate(-pos.x, -pos.y);
|
||||
} else {
|
||||
const pos = staticComp.origin.toWorldSpace().addScalars(40, 50);
|
||||
parameters.context.fillText(
|
||||
T.ingame.interactiveTutorial.hints["1_2_hold_and_drag"],
|
||||
pos.x,
|
||||
pos.y
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.currentHintId.get() === "2_2_place_trash") {
|
||||
// Find cutters
|
||||
if (
|
||||
currentBuilding &&
|
||||
currentBuilding.getId() === gMetaBuildingRegistry.findByClass(MetaTrashBuilding).getId()
|
||||
) {
|
||||
const entities = this.root.entityMgr.getAllWithComponent(ItemProcessorComponent);
|
||||
for (let i = 0; i < entities.length; i++) {
|
||||
const entity = entities[i];
|
||||
if (entity.components.ItemProcessor.type !== enumItemProcessorTypes.cutter) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const slot = entity.components.StaticMapEntity.localTileToWorld(
|
||||
new Vector(1, -1)
|
||||
).toWorldSpace();
|
||||
parameters.context.fillStyle = "rgba(74, 237, 134, " + (0.5 - animation * 0.2) + ")";
|
||||
parameters.context.strokeStyle = "rgb(74, 237, 134)";
|
||||
parameters.context.lineWidth = 2;
|
||||
parameters.context.beginRoundedRect(
|
||||
slot.x - 2 * animation,
|
||||
slot.y - 2 * animation,
|
||||
globalConfig.tileSize + 4 * animation,
|
||||
globalConfig.tileSize + 4 * animation,
|
||||
3
|
||||
);
|
||||
parameters.context.fill();
|
||||
parameters.context.stroke();
|
||||
parameters.context.globalAlpha = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import { BaseHUDPart } from "../base_hud_part";
|
|||
import { DynamicDomAttach } from "../dynamic_dom_attach";
|
||||
import { T } from "../../../translations";
|
||||
|
||||
const tutorialVideos = [2, 3, 4, 5, 6, 7, 9, 10, 11];
|
||||
const tutorialVideos = [3, 4, 5, 6, 7, 9, 10, 11];
|
||||
|
||||
export class HUDPartTutorialHints extends BaseHUDPart {
|
||||
createElements(parent) {
|
||||
|
|
|
@ -110,13 +110,6 @@ export class InGameState extends GameState {
|
|||
return "";
|
||||
}
|
||||
|
||||
getThemeMusic() {
|
||||
if (this.creationPayload.gameModeId && this.creationPayload.gameModeId.includes("puzzle")) {
|
||||
return MUSIC.puzzle;
|
||||
}
|
||||
return MUSIC.theme;
|
||||
}
|
||||
|
||||
onAppPause() {
|
||||
// if (this.stage === stages.s10_gameRunning) {
|
||||
// logger.log("Saving because app got paused");
|
||||
|
@ -241,6 +234,15 @@ export class InGameState extends GameState {
|
|||
|
||||
this.app.backgroundResourceLoader.getIngamePromise().then(
|
||||
() => {
|
||||
if (
|
||||
this.creationPayload.gameModeId &&
|
||||
this.creationPayload.gameModeId.includes("puzzle")
|
||||
) {
|
||||
this.app.sound.playThemeMusic(MUSIC.puzzle);
|
||||
} else {
|
||||
this.app.sound.playThemeMusic(MUSIC.theme);
|
||||
}
|
||||
|
||||
this.loadingOverlay.loadingIndicator.innerText = "";
|
||||
this.app.backgroundResourceLoader.resourceStateChangedSignal.removeAll();
|
||||
|
||||
|
|
|
@ -39,8 +39,6 @@ export class MainMenuState extends GameState {
|
|||
getInnerHTML() {
|
||||
const showLanguageIcon = !G_CHINA_VERSION && !G_WEGAME_VERSION;
|
||||
const showExitAppButton = G_IS_STANDALONE;
|
||||
// const showBrowserWarning = !G_IS_STANDALONE && !isSupportedBrowser();
|
||||
const showBrowserWarning = false;
|
||||
const showPuzzleDLC = !G_WEGAME_VERSION && G_IS_STANDALONE && !G_IS_STEAM_DEMO;
|
||||
const showWegameFooter = G_WEGAME_VERSION;
|
||||
const hasMods = MODS.anyModsActive();
|
||||
|
@ -73,8 +71,23 @@ export class MainMenuState extends GameState {
|
|||
/** @type { PlatformWrapperImplElectron}*/ (this.app.platformWrapper).dlcs.puzzle);
|
||||
|
||||
const bannerHtml = `
|
||||
<h3>${T.demoBanners.title}</h3>
|
||||
<p>${T.demoBanners.intro}</p>
|
||||
<h3>${T.demoBanners.titleV2}</h3>
|
||||
|
||||
|
||||
<div class="points">
|
||||
${Object.entries(T.ingame.standaloneAdvantages.points)
|
||||
.map(
|
||||
([key, trans]) => `
|
||||
<div class="point ${key}">
|
||||
<strong>${trans.title}</strong>
|
||||
<p>${trans.desc}</p>
|
||||
</div>`
|
||||
)
|
||||
.join("")}
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
${
|
||||
G_IS_STEAM_DEMO
|
||||
? `<span class="playtimeDisclaimer">${T.demoBanners.playtimeDisclaimer}</span>`
|
||||
|
@ -110,21 +123,21 @@ export class MainMenuState extends GameState {
|
|||
|
||||
<div class="logo">
|
||||
<img src="${cachebust("res/" + getLogoSprite())}" alt="shapez.io Logo"
|
||||
width="${Math.round((710 / 2.2) * this.app.getEffectiveUiScale())}"
|
||||
height="${Math.round((180 / 2.2) * this.app.getEffectiveUiScale())}"
|
||||
width="${Math.round((710 / 2.5) * this.app.getEffectiveUiScale())}"
|
||||
height="${Math.round((180 / 2.5) * this.app.getEffectiveUiScale())}"
|
||||
>
|
||||
${/*showUpdateLabel ? `<span class="updateLabel">MODS UPDATE!</span>` : ""*/ ""}
|
||||
</div>
|
||||
|
||||
<div class="mainWrapper" data-columns="${showDemoAdvertisement || showPuzzleDLC ? 2 : 1}">
|
||||
<div class="mainContainer">
|
||||
<div class="buttons"></div>
|
||||
</div>
|
||||
|
||||
<div class="sideContainer">
|
||||
${showDemoAdvertisement ? `<div class="standaloneBanner">${bannerHtml}</div>` : ""}
|
||||
</div>
|
||||
|
||||
<div class="mainContainer">
|
||||
${showBrowserWarning ? `<div class="browserWarning">${T.mainMenu.browserWarning}</div>` : ""}
|
||||
<div class="buttons"></div>
|
||||
</div>
|
||||
|
||||
${
|
||||
showPuzzleDLC && ownsPuzzleDLC && !hasMods
|
||||
|
@ -425,6 +438,10 @@ export class MainMenuState extends GameState {
|
|||
);
|
||||
}
|
||||
|
||||
this.htmlElement
|
||||
.querySelector(".mainContainer")
|
||||
.setAttribute("data-savegames", String(this.savedGames.length));
|
||||
|
||||
// Mods
|
||||
this.trackClicks(
|
||||
makeButton(outerDiv, ["modsButton", "styledButton"], T.mods.title),
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { cachebust } from "../core/cachebust";
|
||||
import { GameState } from "../core/game_state";
|
||||
import { getLogoSprite } from "../core/utils";
|
||||
|
||||
export class MobileWarningState extends GameState {
|
||||
constructor() {
|
||||
|
@ -10,14 +9,10 @@ export class MobileWarningState extends GameState {
|
|||
getInnerHTML() {
|
||||
return `
|
||||
|
||||
<img class="logo" src="${cachebust("res/" + getLogoSprite())}" alt="shapez.io Logo">
|
||||
<img class="logo" src="${cachebust("res/logo.png")}" alt="shapez.io Logo">
|
||||
|
||||
<p>
|
||||
I'm sorry, but shapez.io is not available on mobile devices yet!
|
||||
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>
|
||||
|
||||
<p>If you want to play on your computer, you can also get the game on Steam:</p>
|
||||
<p>I'm sorry, but shapez.io is not available on mobile devices yet!</p>
|
||||
<p>If you have a desktop device, you can get shapez on Steam:</p>
|
||||
|
||||
|
||||
<a href="https://get.shapez.io/shapez_mobile" class="standaloneLink" target="_blank">Play on Steam!</a>
|
||||
|
|
|
@ -57,6 +57,8 @@ global:
|
|||
loadingResources: Lade zusätzliche Ressourcen (<percentage> %)
|
||||
demoBanners:
|
||||
title: Demoversion
|
||||
titleV2: >-
|
||||
Spiele jetzt die Vollversion für:
|
||||
intro: |-
|
||||
Kaufe die Vollversion <strong>jetzt</strong> für:<ul>
|
||||
<li>Alle 26 Level + unendlich Freeplay</li>
|
||||
|
@ -77,7 +79,7 @@ mainMenu:
|
|||
newGame: Neues Spiel
|
||||
changelog: Änderungshistorie
|
||||
subreddit: Reddit
|
||||
importSavegame: Importieren
|
||||
importSavegame: Import
|
||||
openSourceHint: Dieses Spiel ist quelloffen!
|
||||
discordLink: Offizieller Discord Server
|
||||
helpTranslate: Hilf beim Übersetzen!
|
||||
|
@ -497,6 +499,7 @@ ingame:
|
|||
Signal</strong> ausgibt und den Färber aktiviert.<br><br> PS: Du
|
||||
musst nicht alle Eingänge verbinden! Probiere es auch mal mit
|
||||
zwei."
|
||||
1_2_hold_and_drag: Drücke und ziehe
|
||||
connectedMiners:
|
||||
one_miner: Ein Extraktor
|
||||
n_miners: <amount> Extraktoren
|
||||
|
|
|
@ -97,6 +97,9 @@ global:
|
|||
demoBanners:
|
||||
# This is the "advertisement" shown in the main menu and other various places
|
||||
title: Demo
|
||||
titleV2: >-
|
||||
Play the full version now for:
|
||||
|
||||
intro: >-
|
||||
Get the full game <strong>now</strong> to unlock:<ul>
|
||||
<li>All 26 levels + infinite Freeplay</li>
|
||||
|
@ -641,6 +644,8 @@ ingame:
|
|||
Press the switch to make it <strong>emit a truthy signal</strong> and thus activate the painter.<br><br>
|
||||
PS: You don't have to connect all inputs! Try wiring only two.
|
||||
|
||||
1_2_hold_and_drag: Hold and drag
|
||||
|
||||
# Connected miners
|
||||
connectedMiners:
|
||||
one_miner: 1 Extractor
|
||||
|
@ -655,7 +660,7 @@ ingame:
|
|||
|
||||
standaloneAdvantages:
|
||||
titleV2: >-
|
||||
Get the full version now on Steam to unlock:
|
||||
Play the full version now on Steam to unlock:
|
||||
titleExpiredV2: Demo completed!
|
||||
titleEnjoyingDemo: Enjoying the demo?
|
||||
|
||||
|
|
Loading…
Reference in New Issue