mirror of
https://github.com/tobspr/shapez.io.git
synced 2024-10-27 20:34:29 +00:00
Add settings
This commit is contained in:
parent
a70a937302
commit
53386e1cd9
BIN
res/ui/icons/enum_selector.png
Normal file
BIN
res/ui/icons/enum_selector.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 576 B |
BIN
res/ui/icons/state_back_button.png
Normal file
BIN
res/ui/icons/state_back_button.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 513 B |
@ -519,14 +519,14 @@ canvas {
|
|||||||
.checkbox {
|
.checkbox {
|
||||||
$bgColor: darken($mainBgColor, 0);
|
$bgColor: darken($mainBgColor, 0);
|
||||||
background-color: $bgColor;
|
background-color: $bgColor;
|
||||||
@include S(width, 45px);
|
@include S(width, 35px);
|
||||||
@include S(height, 20px);
|
@include S(height, 17px);
|
||||||
display: flex;
|
display: flex;
|
||||||
@include S(padding, 3px);
|
@include S(padding, 3px);
|
||||||
box-sizing: content-box;
|
box-sizing: content-box;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
pointer-events: all;
|
pointer-events: all;
|
||||||
transition: opacity 0.2s ease-in-out, background-color 0.4s ease-in-out, box-shadow 0.4s ease-in-out !important;
|
transition: opacity 0.2s ease-in-out, background-color 0.3s ease-in-out, box-shadow 0.4s ease-in-out !important;
|
||||||
position: relative;
|
position: relative;
|
||||||
@include BorderRadius(20px);
|
@include BorderRadius(20px);
|
||||||
@include IncreasedClickArea(10px);
|
@include IncreasedClickArea(10px);
|
||||||
@ -535,9 +535,13 @@ canvas {
|
|||||||
opacity: 0.2;
|
opacity: 0.2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: darken($bgColor, 5);
|
||||||
|
}
|
||||||
|
|
||||||
.knob {
|
.knob {
|
||||||
@include S(width, 20px);
|
@include S(width, 20px);
|
||||||
@include S(height, 20px);
|
@include S(height, 17px);
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
transition: margin-left 0.4s ease-in-out !important;
|
transition: margin-left 0.4s ease-in-out !important;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
@ -550,7 +554,11 @@ canvas {
|
|||||||
background-color: $themeColor;
|
background-color: $themeColor;
|
||||||
@include BoxShadow3D($themeColor, $size: 2px);
|
@include BoxShadow3D($themeColor, $size: 2px);
|
||||||
.knob {
|
.knob {
|
||||||
@include S(margin-left, 25px);
|
@include S(margin-left, 15px);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: lighten($themeColor, 15);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,6 +49,20 @@
|
|||||||
@include S(padding, 12px);
|
@include S(padding, 12px);
|
||||||
pointer-events: all;
|
pointer-events: all;
|
||||||
|
|
||||||
|
&.optionChooserDialog {
|
||||||
|
.optionParent {
|
||||||
|
display: grid;
|
||||||
|
@include S(grid-gap, 5px);
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
.option {
|
||||||
|
pointer-events: all;
|
||||||
|
cursor: pointer;
|
||||||
|
@include S(padding, 10px);
|
||||||
|
background: #eee;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
> .title {
|
> .title {
|
||||||
@include Heading;
|
@include Heading;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
@import "states/preload";
|
@import "states/preload";
|
||||||
@import "states/main_menu";
|
@import "states/main_menu";
|
||||||
@import "states/ingame";
|
@import "states/ingame";
|
||||||
|
@import "states/settings";
|
||||||
|
|
||||||
@import "ingame_hud/buildings_toolbar";
|
@import "ingame_hud/buildings_toolbar";
|
||||||
@import "ingame_hud/building_placer";
|
@import "ingame_hud/building_placer";
|
||||||
|
57
src/css/states/settings.scss
Normal file
57
src/css/states/settings.scss
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
#state_SettingsState {
|
||||||
|
.content {
|
||||||
|
.categoryLabel {
|
||||||
|
display: block;
|
||||||
|
text-transform: uppercase;
|
||||||
|
@include S(margin-top, 15px);
|
||||||
|
@include S(margin-bottom, 15px);
|
||||||
|
@include Heading;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting {
|
||||||
|
@include S(padding, 10px);
|
||||||
|
background: #eee;
|
||||||
|
@include S(border-radius, 2px);
|
||||||
|
@include S(margin-bottom, 5px);
|
||||||
|
|
||||||
|
label {
|
||||||
|
text-transform: uppercase;
|
||||||
|
@include Text;
|
||||||
|
}
|
||||||
|
|
||||||
|
.desc {
|
||||||
|
@include S(margin-top, 5px);
|
||||||
|
@include SuperSmallText;
|
||||||
|
color: #aaadb2;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .row {
|
||||||
|
display: grid;
|
||||||
|
align-items: center;
|
||||||
|
grid-template-columns: 1fr auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.value.enum {
|
||||||
|
background: #fff;
|
||||||
|
@include PlainText;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
pointer-events: all;
|
||||||
|
cursor: pointer;
|
||||||
|
justify-content: center;
|
||||||
|
@include S(min-width, 100px);
|
||||||
|
@include S(border-radius, 2px);
|
||||||
|
@include S(padding, 4px);
|
||||||
|
@include S(padding-right, 15px);
|
||||||
|
|
||||||
|
background: #fff uiResource("icons/enum_selector.png") calc(100% - #{D(5px)})
|
||||||
|
calc(50% + #{D(1px)}) / #{D(15px)} no-repeat;
|
||||||
|
|
||||||
|
transition: background-color 0.12s ease-in-out;
|
||||||
|
&:hover {
|
||||||
|
background-color: #fafafa;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -5,263 +5,39 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
$padding: 15px;
|
$padding: 15px;
|
||||||
|
|
||||||
.bottomPoppingInNotification {
|
.headerBar,
|
||||||
position: absolute;
|
.content {
|
||||||
left: 50%;
|
@include S(width, 500px);
|
||||||
text-align: center;
|
|
||||||
@include BoxShadow3D(mix(lighten($mainBgColor, 12), $colorRedBright, 50%));
|
|
||||||
@include S(padding, 10px);
|
|
||||||
max-width: #{D(280px)};
|
|
||||||
@include S(bottom, 30px);
|
|
||||||
box-sizing: border-box;
|
|
||||||
width: 100%;
|
|
||||||
@include PlainText;
|
|
||||||
@include BorderRadius(4px);
|
|
||||||
|
|
||||||
$baseTransform: translateX(-50%);
|
|
||||||
transform-origin: 0% 100%;
|
|
||||||
transform: translateY(500%);
|
|
||||||
opacity: 0;
|
|
||||||
|
|
||||||
display: block;
|
|
||||||
@include InlineAnimation(5s ease-in-out) {
|
|
||||||
0% {
|
|
||||||
opacity: 0;
|
|
||||||
transform: scale(0) skew(5deg, 5deg) translateY(100%) $baseTransform;
|
|
||||||
}
|
|
||||||
8% {
|
|
||||||
transform: scale(1.05) translateY(-2%) $baseTransform;
|
|
||||||
}
|
|
||||||
12% {
|
|
||||||
transform: scale(1) $baseTransform;
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
97% {
|
|
||||||
transform: scale(1) $baseTransform;
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
opacity: 0;
|
|
||||||
transform: scale(0) skew(5deg, 5deg) translateY(100%) $baseTransform;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.widthKeeper {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
box-sizing: content-box;
|
|
||||||
@include S(max-width, 1000px);
|
|
||||||
|
|
||||||
@include StyleAtHeight(800px) {
|
|
||||||
@include S(padding-top, 30px);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.headerBar {
|
.headerBar {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
@include VerticalStyle {
|
|
||||||
// margin-top: 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
// margin-bottom: 15px;
|
|
||||||
padding: $padding;
|
|
||||||
|
|
||||||
$h: 25px;
|
|
||||||
@include S(min-height, $h);
|
|
||||||
@include S(max-height, $h);
|
|
||||||
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
|
||||||
position: relative;
|
|
||||||
z-index: 50;
|
|
||||||
background: transparent;
|
|
||||||
|
|
||||||
@include S(padding-top, $padding);
|
|
||||||
@include S(padding-left, $padding);
|
|
||||||
@include S(padding-right, $padding);
|
|
||||||
background-size: calc(100% - #{D(6px)}) 100%;
|
|
||||||
$paddingBottom: 20px;
|
|
||||||
|
|
||||||
@include S(padding-bottom, $paddingBottom);
|
|
||||||
@include S(margin-bottom, -$h - $padding - $paddingBottom);
|
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
// text-align: center;
|
|
||||||
cursor: pointer;
|
|
||||||
// transform-origin: 0px 50%;
|
|
||||||
pointer-events: all;
|
|
||||||
@include S(padding, 5px, 0px, 5px, 30px);
|
|
||||||
@include S(left, -2px);
|
|
||||||
@include S(min-width, 100px);
|
|
||||||
position: relative;
|
|
||||||
@include IncreasedClickArea(25px);
|
|
||||||
text-transform: uppercase;
|
|
||||||
// background: uiResource("back_arrow.png") center center no-repeat;
|
|
||||||
@include S(background-position-x, -3px);
|
|
||||||
@include S(background-size, 25px, 25px);
|
|
||||||
|
|
||||||
// Due to back button
|
|
||||||
color: $text3dColor;
|
|
||||||
@include TextShadow3D($borderColor: #18151d);
|
|
||||||
@include SuperHeading;
|
@include SuperHeading;
|
||||||
@include StyleBelowWidth(380px) {
|
|
||||||
@include Heading;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.grow {
|
|
||||||
flex-grow: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
text-align: left;
|
|
||||||
flex-direction: column;
|
|
||||||
pointer-events: all;
|
|
||||||
box-sizing: border-box;
|
|
||||||
z-index: 25;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
@include S(padding-left, 0px);
|
|
||||||
@include S(padding-right, 0px);
|
|
||||||
|
|
||||||
height: 100%;
|
|
||||||
@include SupportsAndroidNotchQuery {
|
|
||||||
height: calc(
|
|
||||||
100% - constant(safe-area-inset-top) - constant(safe-area-inset-bottom) -
|
|
||||||
var(--notch-inset-top) - var(--notch-inset-bottom)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@include SupportsiOsNotchQuery {
|
|
||||||
height: calc(
|
|
||||||
100% - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px) -
|
|
||||||
var(--notch-inset-top) - var(--notch-inset-bottom)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
.loadingIndicator {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.errorIndicator {
|
|
||||||
display: none;
|
|
||||||
flex-direction: column;
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
.errorInner {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
@include S(max-width, 350px);
|
|
||||||
|
|
||||||
strong {
|
|
||||||
$col: #ff4564;
|
|
||||||
@include TextShadow3D($col);
|
|
||||||
@include Heading;
|
|
||||||
}
|
|
||||||
|
|
||||||
i {
|
|
||||||
@include PlainText;
|
|
||||||
color: #888;
|
|
||||||
@include S(margin-top, 10px);
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.loadingIndicator,
|
|
||||||
.errorIndicator {
|
|
||||||
box-sizing: border-box;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
height: 100%;
|
|
||||||
@include S(padding, 30px);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loading state
|
|
||||||
&.loading {
|
|
||||||
.mainContent {
|
|
||||||
animation: none;
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
.loadingIndicator {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error state
|
|
||||||
&.errored {
|
|
||||||
.mainContent {
|
|
||||||
animation: none;
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.errorIndicator {
|
|
||||||
animation: none;
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.mainContent {
|
|
||||||
overflow-y: auto !important;
|
|
||||||
overflow-x: hidden;
|
|
||||||
@include S(padding, $padding);
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
box-sizing: border-box;
|
|
||||||
|
|
||||||
@include InlineAnimation(0.4s ease-in-out) {
|
|
||||||
0% {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.category_label {
|
|
||||||
display: block;
|
|
||||||
@include S(margin-top, 40px);
|
|
||||||
|
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
&:first-child {
|
color: #333438;
|
||||||
margin-top: 0 !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@include S(margin-bottom, 16px);
|
.backButton {
|
||||||
@include Heading;
|
@include S(width, 30px);
|
||||||
|
@include S(height, 30px);
|
||||||
@include TextShadow3D(#68a1bb, $borderColor: #141718);
|
@include S(margin-right, 10px);
|
||||||
|
@include S(margin-left, -5px);
|
||||||
|
background: uiResource("icons/state_back_button.png") center center / 70% no-repeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cardbox {
|
@include S(margin-bottom, 20px);
|
||||||
@include S(padding, 20px, 15px);
|
|
||||||
$cardBg: lighten($mainBgColor, 9);
|
|
||||||
background: $cardBg;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
@include S(margin-bottom, 15px);
|
|
||||||
@include BorderRadius(4px);
|
|
||||||
@include S(padding-bottom, 14px);
|
|
||||||
@include BoxShadow3D($cardBg);
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
border-bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&.hasTitle {
|
.content {
|
||||||
.mainContent {
|
background: #fff;
|
||||||
@include S(padding-top, 70px, $important: true);
|
@include S(border-radius, 2px);
|
||||||
}
|
@include S(padding, 10px);
|
||||||
|
max-height: calc(80vh - #{D(60px)});
|
||||||
|
overflow-y: auto;
|
||||||
|
box-sizing: border-box;
|
||||||
|
pointer-events: all;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
$globalBorderRadius: 0px;
|
||||||
|
|
||||||
// When to reduce control elements size for small devices
|
// When to reduce control elements size for small devices
|
||||||
$layoutExpandMinWidth: 340px;
|
$layoutExpandMinWidth: 340px;
|
||||||
|
|
||||||
|
@ -31,6 +31,7 @@ import { ShapezGameAnalytics } from "./platform/browser/game_analytics";
|
|||||||
import { queryParamOptions } from "./core/query_parameters";
|
import { queryParamOptions } from "./core/query_parameters";
|
||||||
import { NoGameAnalytics } from "./platform/browser/no_game_analytics";
|
import { NoGameAnalytics } from "./platform/browser/no_game_analytics";
|
||||||
import { StorageImplBrowserIndexedDB } from "./platform/browser/storage_indexed_db";
|
import { StorageImplBrowserIndexedDB } from "./platform/browser/storage_indexed_db";
|
||||||
|
import { SettingsState } from "./states/settings";
|
||||||
|
|
||||||
const logger = createLogger("application");
|
const logger = createLogger("application");
|
||||||
|
|
||||||
@ -142,7 +143,7 @@ export class Application {
|
|||||||
*/
|
*/
|
||||||
registerStates() {
|
registerStates() {
|
||||||
/** @type {Array<typeof GameState>} */
|
/** @type {Array<typeof GameState>} */
|
||||||
const states = [PreloadState, MainMenuState, InGameState];
|
const states = [PreloadState, MainMenuState, InGameState, SettingsState];
|
||||||
|
|
||||||
for (let i = 0; i < states.length; ++i) {
|
for (let i = 0; i < states.length; ++i) {
|
||||||
this.stateMgr.register(states[i]);
|
this.stateMgr.register(states[i]);
|
||||||
|
@ -90,7 +90,7 @@ export const globalConfig = {
|
|||||||
allBuildingsUnlocked: true,
|
allBuildingsUnlocked: true,
|
||||||
upgradesNoCost: true,
|
upgradesNoCost: true,
|
||||||
disableUnlockDialog: false,
|
disableUnlockDialog: false,
|
||||||
testTranslations: true,
|
// testTranslations: true,
|
||||||
/* dev:end */
|
/* dev:end */
|
||||||
},
|
},
|
||||||
|
|
||||||
|
153
src/js/core/textual_game_state.js
Normal file
153
src/js/core/textual_game_state.js
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
import { HUDModalDialogs } from "../game/hud/parts/modal_dialogs";
|
||||||
|
import { GameState } from "./game_state";
|
||||||
|
import { T } from "../translations";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Baseclass for all game states which are structured similary: A header with back button + some
|
||||||
|
* scrollable content.
|
||||||
|
*/
|
||||||
|
export class TextualGameState extends GameState {
|
||||||
|
///// INTERFACE ////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should return the states inner html. If not overriden, will create a scrollable container
|
||||||
|
* with the content of getMainContentHTML()
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
getInnerHTML() {
|
||||||
|
return `
|
||||||
|
<div class="content mainContent">
|
||||||
|
${this.getMainContentHTML()}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should return the states HTML content.
|
||||||
|
*/
|
||||||
|
getMainContentHTML() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should return the title of the game state. If null, no title and back button will
|
||||||
|
* get created
|
||||||
|
* @returns {string|null}
|
||||||
|
*/
|
||||||
|
getStateHeaderTitle() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Back button handler, can be overridden. Per default it goes back to the main menu,
|
||||||
|
* or if coming from the game it moves back to the game again.
|
||||||
|
*/
|
||||||
|
onBackButton() {
|
||||||
|
if (this.backToStateId) {
|
||||||
|
this.moveToState(this.backToStateId, this.backToStatePayload);
|
||||||
|
} else {
|
||||||
|
this.moveToState(this.getDefaultPreviousState());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the default state to go back to
|
||||||
|
*/
|
||||||
|
getDefaultPreviousState() {
|
||||||
|
return "MainMenuState";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Goes to a new state, telling him to go back to this state later
|
||||||
|
* @param {string} stateId
|
||||||
|
*/
|
||||||
|
moveToStateAddGoBack(stateId) {
|
||||||
|
this.moveToState(stateId, {
|
||||||
|
backToStateId: this.key,
|
||||||
|
backToStatePayload: {
|
||||||
|
backToStateId: this.backToStateId,
|
||||||
|
backToStatePayload: this.backToStatePayload,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all click detectors, except the one on the back button. Useful when regenerating
|
||||||
|
* content.
|
||||||
|
*/
|
||||||
|
clearClickDetectorsExceptHeader() {
|
||||||
|
for (let i = 0; i < this.clickDetectors.length; ++i) {
|
||||||
|
const detector = this.clickDetectors[i];
|
||||||
|
if (detector.element === this.headerElement) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
detector.cleanup();
|
||||||
|
this.clickDetectors.splice(i, 1);
|
||||||
|
i -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides the GameState implementation to provide our own html
|
||||||
|
*/
|
||||||
|
internalGetFullHtml() {
|
||||||
|
let headerHtml = "";
|
||||||
|
if (this.getStateHeaderTitle()) {
|
||||||
|
headerHtml = `
|
||||||
|
<div class="headerBar">
|
||||||
|
<button class="backButton"></button>
|
||||||
|
<h1>${this.getStateHeaderTitle()}</h1>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `
|
||||||
|
${headerHtml}
|
||||||
|
<div class="container">
|
||||||
|
${this.getInnerHTML()}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
//// INTERNALS /////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides the GameState leave callback to cleanup stuff
|
||||||
|
*/
|
||||||
|
internalLeaveCallback() {
|
||||||
|
super.internalLeaveCallback();
|
||||||
|
this.dialogs.cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides the GameState enter callback to setup required stuff
|
||||||
|
* @param {any} payload
|
||||||
|
*/
|
||||||
|
internalEnterCallback(payload) {
|
||||||
|
super.internalEnterCallback(payload, false);
|
||||||
|
if (payload.backToStateId) {
|
||||||
|
this.backToStateId = payload.backToStateId;
|
||||||
|
this.backToStatePayload = payload.backToStatePayload;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.htmlElement.classList.add("textualState");
|
||||||
|
if (this.getStateHeaderTitle()) {
|
||||||
|
this.htmlElement.classList.add("hasTitle");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.containerElement = this.htmlElement.querySelector(".widthKeeper .container");
|
||||||
|
this.headerElement = this.htmlElement.querySelector(".headerBar > .backButton");
|
||||||
|
|
||||||
|
if (this.headerElement) {
|
||||||
|
this.trackClicks(this.headerElement, this.onBackButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dialogs = new HUDModalDialogs(null, this.app);
|
||||||
|
const dialogsElement = document.body.querySelector(".modalDialogParent");
|
||||||
|
this.dialogs.initializeToElement(dialogsElement);
|
||||||
|
|
||||||
|
this.onEnter(payload);
|
||||||
|
}
|
||||||
|
}
|
@ -25,6 +25,10 @@ export class HUDSettingsMenu extends BaseHUDPart {
|
|||||||
title: T.ingame.settingsMenu.buttons.continue,
|
title: T.ingame.settingsMenu.buttons.continue,
|
||||||
action: () => this.close(),
|
action: () => this.close(),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: T.ingame.settingsMenu.buttons.settings,
|
||||||
|
action: () => this.goToSettings(),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: T.ingame.settingsMenu.buttons.menu,
|
title: T.ingame.settingsMenu.buttons.menu,
|
||||||
action: () => this.returnToMenu(),
|
action: () => this.returnToMenu(),
|
||||||
@ -47,6 +51,10 @@ export class HUDSettingsMenu extends BaseHUDPart {
|
|||||||
this.root.gameState.goBackToMenu();
|
this.root.gameState.goBackToMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
goToSettings() {
|
||||||
|
this.root.gameState.goToSettings();
|
||||||
|
}
|
||||||
|
|
||||||
shouldPauseGame() {
|
shouldPauseGame() {
|
||||||
return this.visible;
|
return this.visible;
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ import { Application } from "../application";
|
|||||||
/* typehints:end */
|
/* typehints:end */
|
||||||
|
|
||||||
import { createLogger } from "../core/logging";
|
import { createLogger } from "../core/logging";
|
||||||
|
import { T } from "../translations";
|
||||||
|
|
||||||
const logger = createLogger("setting_types");
|
const logger = createLogger("setting_types");
|
||||||
|
|
||||||
@ -63,8 +64,8 @@ export class BaseSetting {
|
|||||||
|
|
||||||
showRestartRequiredDialog() {
|
showRestartRequiredDialog() {
|
||||||
const { restart } = this.dialogs.showInfo(
|
const { restart } = this.dialogs.showInfo(
|
||||||
"Restart required",
|
T.dialogs.restartRequired.title,
|
||||||
"You need to restart the game to apply the settings.",
|
T.dialogs.restartRequired.text,
|
||||||
this.app.platformWrapper.getSupportsRestart() ? ["later:grey", "restart:misc"] : ["ok:good"]
|
this.app.platformWrapper.getSupportsRestart() ? ["later:grey", "restart:misc"] : ["ok:good"]
|
||||||
);
|
);
|
||||||
if (restart) {
|
if (restart) {
|
||||||
@ -113,11 +114,11 @@ export class EnumSetting extends BaseSetting {
|
|||||||
return `
|
return `
|
||||||
<div class="setting cardbox ${this.enabled ? "enabled" : "disabled"}">
|
<div class="setting cardbox ${this.enabled ? "enabled" : "disabled"}">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<label>TODO: SETTING TITLE</label>
|
<label>${T.settings.labels[this.id].title}</label>
|
||||||
<div class="value enum" data-setting="${this.id}"></div>
|
<div class="value enum" data-setting="${this.id}"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="desc">
|
<div class="desc">
|
||||||
TODO: SETTING DESC
|
${T.settings.labels[this.id].description}
|
||||||
</div>
|
</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
@ -153,7 +154,7 @@ export class EnumSetting extends BaseSetting {
|
|||||||
}
|
}
|
||||||
|
|
||||||
modify() {
|
modify() {
|
||||||
const { optionSelected } = this.dialogs.showOptionChooser("TODO: SETTING TITLE", {
|
const { optionSelected } = this.dialogs.showOptionChooser(T.settings.labels[this.id].title, {
|
||||||
active: this.app.settings.getSetting(this.id),
|
active: this.app.settings.getSetting(this.id),
|
||||||
options: this.options.map(option => ({
|
options: this.options.map(option => ({
|
||||||
value: this.valueGetter(option),
|
value: this.valueGetter(option),
|
||||||
@ -186,13 +187,13 @@ export class BoolSetting extends BaseSetting {
|
|||||||
return `
|
return `
|
||||||
<div class="setting cardbox ${this.enabled ? "enabled" : "disabled"}">
|
<div class="setting cardbox ${this.enabled ? "enabled" : "disabled"}">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<label>TODO: SETTING TITLE</label>
|
<label>${T.settings.labels[this.id].title}</label>
|
||||||
<div class="value checkbox checked" data-setting="${this.id}">
|
<div class="value checkbox checked" data-setting="${this.id}">
|
||||||
<span class="knob"></span>
|
<span class="knob"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="desc">
|
<div class="desc">
|
||||||
TODO: SETTING DESC
|
${T.settings.labels[this.id].description}
|
||||||
</div>
|
</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
@ -144,6 +144,13 @@ export class InGameState extends GameState {
|
|||||||
this.saveThenGoToState("MainMenuState");
|
this.saveThenGoToState("MainMenuState");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Goes back to the settings state
|
||||||
|
*/
|
||||||
|
goToSettings() {
|
||||||
|
this.saveThenGoToState("SettingsState");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Moves to a state outside of the game
|
* Moves to a state outside of the game
|
||||||
* @param {string} stateId
|
* @param {string} stateId
|
||||||
|
@ -285,6 +285,12 @@ export class MainMenuState extends GameState {
|
|||||||
const savegame = this.app.savegameMgr.createNewSavegame();
|
const savegame = this.app.savegameMgr.createNewSavegame();
|
||||||
|
|
||||||
this.app.analytics.trackUiClick("startgame");
|
this.app.analytics.trackUiClick("startgame");
|
||||||
|
|
||||||
|
if (G_IS_DEV) {
|
||||||
|
// TODO
|
||||||
|
this.moveToState("SettingsState");
|
||||||
|
}
|
||||||
|
|
||||||
this.moveToState("InGameState", {
|
this.moveToState("InGameState", {
|
||||||
savegame,
|
savegame,
|
||||||
});
|
});
|
||||||
|
127
src/js/states/settings.js
Normal file
127
src/js/states/settings.js
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
import { TextualGameState } from "../core/textual_game_state";
|
||||||
|
import { formatSecondsToTimeAgo } from "../core/utils";
|
||||||
|
import { allApplicationSettings } from "../profile/application_settings";
|
||||||
|
import { T } from "../translations";
|
||||||
|
|
||||||
|
export class SettingsState extends TextualGameState {
|
||||||
|
constructor() {
|
||||||
|
super("SettingsState");
|
||||||
|
}
|
||||||
|
|
||||||
|
getStateHeaderTitle() {
|
||||||
|
return T.settings.title;
|
||||||
|
}
|
||||||
|
|
||||||
|
getMainContentHTML() {
|
||||||
|
return `
|
||||||
|
|
||||||
|
<div class="upperLinks">
|
||||||
|
${
|
||||||
|
this.app.platformWrapper.getSupportsKeyboard()
|
||||||
|
? `
|
||||||
|
<button class="styledButton editKeybindings">KEYBINDING TODO</button>
|
||||||
|
`
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
<button class="styledButton changelog">CHANGELOG TODO</button>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
${this.getSettingsHtml()}
|
||||||
|
<div class="versionbar">
|
||||||
|
<div class="buildVersion">${T.global.loading} ...</div>
|
||||||
|
<button class="styledButton copyright">COPYRIGHT TODO</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
getSettingsHtml() {
|
||||||
|
let lastCategory = null;
|
||||||
|
let html = "";
|
||||||
|
for (let i = 0; i < allApplicationSettings.length; ++i) {
|
||||||
|
const setting = allApplicationSettings[i];
|
||||||
|
|
||||||
|
if (setting.categoryId !== lastCategory) {
|
||||||
|
lastCategory = setting.categoryId;
|
||||||
|
if (i !== 0) {
|
||||||
|
html += "</div>";
|
||||||
|
}
|
||||||
|
html += `<strong class="categoryLabel">${T.settings.categories[lastCategory]}</strong>`;
|
||||||
|
html += "<div class='settingsContainer'>";
|
||||||
|
}
|
||||||
|
|
||||||
|
html += setting.getHtml();
|
||||||
|
}
|
||||||
|
if (lastCategory) {
|
||||||
|
html += "</div>";
|
||||||
|
}
|
||||||
|
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderBuildText() {
|
||||||
|
const labelVersion = this.htmlElement.querySelector(".buildVersion");
|
||||||
|
const lastBuildMs = new Date().getTime() - G_BUILD_TIME;
|
||||||
|
const lastBuildText = formatSecondsToTimeAgo(lastBuildMs / 1000.0);
|
||||||
|
|
||||||
|
const version = T.settings.versionBadges[G_APP_ENVIRONMENT];
|
||||||
|
|
||||||
|
labelVersion.innerHTML = `
|
||||||
|
<span class='version'>
|
||||||
|
${G_BUILD_VERSION} @ ${version} @ ${G_BUILD_COMMIT_HASH}
|
||||||
|
</span>
|
||||||
|
<span class='buildTime'>
|
||||||
|
${T.settings.buildDate.replace("<at-date>", lastBuildText)}<br />
|
||||||
|
</span>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
onEnter(payload) {
|
||||||
|
this.renderBuildText();
|
||||||
|
this.trackClicks(this.htmlElement.querySelector(".copyright"), this.onCopyrightClicked, {
|
||||||
|
preventDefault: false,
|
||||||
|
});
|
||||||
|
this.trackClicks(this.htmlElement.querySelector(".changelog"), this.onChangelogClicked, {
|
||||||
|
preventDefault: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const keybindingsButton = this.htmlElement.querySelector(".editKeybindings");
|
||||||
|
|
||||||
|
if (keybindingsButton) {
|
||||||
|
this.trackClicks(keybindingsButton, this.onKeybindingsClicked, { preventDefault: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.initSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
initSettings() {
|
||||||
|
allApplicationSettings.forEach(setting => {
|
||||||
|
const element = this.htmlElement.querySelector("[data-setting='" + setting.id + "']");
|
||||||
|
setting.bind(this.app, element, this.dialogs);
|
||||||
|
setting.syncValueToElement();
|
||||||
|
this.trackClicks(
|
||||||
|
element,
|
||||||
|
() => {
|
||||||
|
setting.modify();
|
||||||
|
},
|
||||||
|
{ preventDefault: false }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onCopyrightClicked() {
|
||||||
|
// this.moveToStateAddGoBack("CopyrightState");
|
||||||
|
}
|
||||||
|
|
||||||
|
onChangelogClicked() {
|
||||||
|
// this.moveToStateAddGoBack("ChangelogState");
|
||||||
|
}
|
||||||
|
|
||||||
|
onKeybindingsClicked() {
|
||||||
|
// this.moveToStateAddGoBack("KeybindingsState");
|
||||||
|
}
|
||||||
|
}
|
@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
global:
|
global:
|
||||||
loading: Loading
|
loading: Loading
|
||||||
|
error: Error
|
||||||
|
|
||||||
# How big numbers are rendered, e.g. "10,000"
|
# How big numbers are rendered, e.g. "10,000"
|
||||||
thousandsDivider: ","
|
thousandsDivider: ","
|
||||||
@ -77,6 +78,8 @@ dialogs:
|
|||||||
ok: OK
|
ok: OK
|
||||||
delete: Delete
|
delete: Delete
|
||||||
cancel: Cancel
|
cancel: Cancel
|
||||||
|
later: Later
|
||||||
|
restart: Restart
|
||||||
|
|
||||||
importSavegameError:
|
importSavegameError:
|
||||||
title: Import Error
|
title: Import Error
|
||||||
@ -103,6 +106,11 @@ dialogs:
|
|||||||
text: >-
|
text: >-
|
||||||
Failed to delete the savegame:
|
Failed to delete the savegame:
|
||||||
|
|
||||||
|
restartRequired:
|
||||||
|
title: Restart required
|
||||||
|
text: >-
|
||||||
|
You need to restart the game to apply the settings.
|
||||||
|
|
||||||
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
|
||||||
# every situation
|
# every situation
|
||||||
@ -186,6 +194,7 @@ ingame:
|
|||||||
|
|
||||||
buttons:
|
buttons:
|
||||||
continue: Continue
|
continue: Continue
|
||||||
|
settings: Settings
|
||||||
menu: Return to menu
|
menu: Return to menu
|
||||||
|
|
||||||
# All shop upgrades
|
# All shop upgrades
|
||||||
@ -257,3 +266,41 @@ storyRewards:
|
|||||||
|
|
||||||
# Special reward, which is shown when there is no reward actually
|
# Special reward, which is shown when there is no reward actually
|
||||||
no_reward: Next level
|
no_reward: Next level
|
||||||
|
|
||||||
|
settings:
|
||||||
|
title: Settings
|
||||||
|
categories:
|
||||||
|
game: Game
|
||||||
|
app: Application
|
||||||
|
|
||||||
|
versionBadges:
|
||||||
|
dev: Development
|
||||||
|
staging: Staging
|
||||||
|
prod: Production
|
||||||
|
buildDate: Built <at-date>
|
||||||
|
|
||||||
|
labels:
|
||||||
|
uiScale:
|
||||||
|
title: Interface scale
|
||||||
|
description: >-
|
||||||
|
Changes the size of the user interface. The interface will still scale based on your device resolution, but this setting controls the amount of scale.
|
||||||
|
|
||||||
|
fullscreen:
|
||||||
|
title: Fullscreen
|
||||||
|
description: >-
|
||||||
|
It is recommended to play the game in fullscreen to get the best experience. Only available in the standalone.
|
||||||
|
|
||||||
|
theme:
|
||||||
|
title: Interface theme
|
||||||
|
description: >-
|
||||||
|
Choose the interface theme which also affects the game. Notice that everything except the default theme may lead to graphical issues.
|
||||||
|
|
||||||
|
soundsMuted:
|
||||||
|
title: Mute Sounds
|
||||||
|
description: >-
|
||||||
|
If enabled, mutes all sound effects.
|
||||||
|
|
||||||
|
musicMuted:
|
||||||
|
title: Mute Music
|
||||||
|
description: >-
|
||||||
|
If enabled, mutes all music.
|
||||||
|
Loading…
Reference in New Issue
Block a user