@ -64,7 +64,7 @@ This project is based on ES5. Some ES2015 features are used but most of them are
|
||||
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.
|
||||
7. Add the component in `src/js/game/component_registry.js`
|
||||
8. Add the componetn in `src/js/game/entity_components.js`
|
||||
8. Add the component in `src/js/game/entity_components.js`
|
||||
9. Done! You can use your component now
|
||||
|
||||
#### Adding a new building
|
||||
@ -96,6 +96,6 @@ This project is based on ES5. Some ES2015 features are used but most of them are
|
||||
|
||||
For most assets I use Adobe Photoshop, you can find them in `assets/`.
|
||||
|
||||
You will need a <a href="https://www.codeandweb.com/texturepacker" target="_blank">Texture Packer</a> license in order to regenerate the atlas. If you don't have one but want to contribute assets, let me know and I might compile it for you. I'm currently switching to an open source solution but I can't give an estimate when thats done.
|
||||
You will need a <a href="https://www.codeandweb.com/texturepacker" target="_blank">Texture Packer</a> license in order to regenerate the atlas. If you don't have one but want to contribute assets, let me know and I might compile it for you. I'm currently switching to an open source solution but I can't give an estimate when that's done.
|
||||
|
||||
<img src="https://i.imgur.com/W25Fkl0.png" alt="shapez.io Screenshot">
|
||||
|
@ -16,6 +16,12 @@ function gulptasksSounds($, gulp, buildFolder) {
|
||||
cacheDirName: "shapezio-precompiled-sounds",
|
||||
});
|
||||
|
||||
function getFileCacheValue(file) {
|
||||
const { _isVinyl, base, cwd, contents, history, stat, path } = file;
|
||||
const encodedContents = Buffer.from(contents).toString('base64');
|
||||
return { _isVinyl, base, cwd, contents: encodedContents, history, stat, path };
|
||||
}
|
||||
|
||||
// Encodes the game music
|
||||
gulp.task("sounds.music", () => {
|
||||
return gulp
|
||||
@ -34,6 +40,7 @@ function gulptasksSounds($, gulp, buildFolder) {
|
||||
{
|
||||
name: "music",
|
||||
fileCache,
|
||||
value: getFileCacheValue,
|
||||
}
|
||||
)
|
||||
)
|
||||
@ -58,6 +65,7 @@ function gulptasksSounds($, gulp, buildFolder) {
|
||||
{
|
||||
name: "music-high-quality",
|
||||
fileCache,
|
||||
value: getFileCacheValue,
|
||||
}
|
||||
)
|
||||
)
|
||||
|
BIN
res/ui/building_icons/balancer.png
Normal file
After Width: | Height: | Size: 7.2 KiB |
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 4.5 KiB |
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 9.7 KiB After Width: | Height: | Size: 8.9 KiB |
Before Width: | Height: | Size: 6.5 KiB After Width: | Height: | Size: 5.9 KiB |
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 5.2 KiB |
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.2 KiB |
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 6.3 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 7.0 KiB |
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 7.8 KiB After Width: | Height: | Size: 7.2 KiB |
Before Width: | Height: | Size: 9.0 KiB After Width: | Height: | Size: 7.7 KiB |
Before Width: | Height: | Size: 7.7 KiB |
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.4 KiB |
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 5.5 KiB |
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 7.1 KiB |
Before Width: | Height: | Size: 8.1 KiB After Width: | Height: | Size: 7.2 KiB |
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 3.8 KiB |
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 57 KiB |
BIN
res/ui/building_tutorials/balancer-splitter.png
Normal file
After Width: | Height: | Size: 79 KiB |
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 51 KiB |
Before Width: | Height: | Size: 57 KiB |
Before Width: | Height: | Size: 825 B After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 756 B After Width: | Height: | Size: 2.2 KiB |
BIN
res/ui/icons/enum_selector_white.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 731 B |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 728 B After Width: | Height: | Size: 385 B |
BIN
res/ui/icons/settings_menu_exit.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
res/ui/icons/settings_menu_play.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
res/ui/icons/settings_menu_settings.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
BIN
res/ui/icons/shop_active.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 927 B After Width: | Height: | Size: 546 B |
BIN
res/ui/icons/toggle_unit.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 1.3 MiB |
Before Width: | Height: | Size: 280 KiB After Width: | Height: | Size: 281 KiB |
Before Width: | Height: | Size: 677 KiB After Width: | Height: | Size: 700 KiB |
@ -197,7 +197,7 @@
|
||||
<key>scaleMode</key>
|
||||
<enum type="ScaleMode">Smooth</enum>
|
||||
<key>extrude</key>
|
||||
<uint>2</uint>
|
||||
<uint>3</uint>
|
||||
<key>trimThreshold</key>
|
||||
<uint>2</uint>
|
||||
<key>trimMargin</key>
|
||||
@ -269,7 +269,7 @@
|
||||
<key type="filename">sprites/blueprints/miner.png</key>
|
||||
<key type="filename">sprites/blueprints/reader.png</key>
|
||||
<key type="filename">sprites/blueprints/rotater-ccw.png</key>
|
||||
<key type="filename">sprites/blueprints/rotater-fl.png</key>
|
||||
<key type="filename">sprites/blueprints/rotater-rotate180.png</key>
|
||||
<key type="filename">sprites/blueprints/rotater.png</key>
|
||||
<key type="filename">sprites/blueprints/splitter-compact-inverse.png</key>
|
||||
<key type="filename">sprites/blueprints/splitter-compact-merge-inverse.png</key>
|
||||
@ -281,8 +281,10 @@
|
||||
<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/virtual_processor-analyzer.png</key>
|
||||
<key type="filename">sprites/blueprints/virtual_processor-painter.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-stacker.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>
|
||||
@ -298,7 +300,7 @@
|
||||
<key type="filename">sprites/buildings/miner-chainable.png</key>
|
||||
<key type="filename">sprites/buildings/reader.png</key>
|
||||
<key type="filename">sprites/buildings/rotater-ccw.png</key>
|
||||
<key type="filename">sprites/buildings/rotater-fl.png</key>
|
||||
<key type="filename">sprites/buildings/rotater-rotate180.png</key>
|
||||
<key type="filename">sprites/buildings/splitter-compact-inverse.png</key>
|
||||
<key type="filename">sprites/buildings/splitter-compact-merge-inverse.png</key>
|
||||
<key type="filename">sprites/buildings/splitter-compact-merge.png</key>
|
||||
@ -308,8 +310,10 @@
|
||||
<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/virtual_processor-analyzer.png</key>
|
||||
<key type="filename">sprites/buildings/virtual_processor-painter.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-stacker.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>
|
||||
@ -503,6 +507,28 @@
|
||||
<key>scale9FromFile</key>
|
||||
<false/>
|
||||
</struct>
|
||||
<key type="filename">sprites/colors/blue.png</key>
|
||||
<key type="filename">sprites/colors/cyan.png</key>
|
||||
<key type="filename">sprites/colors/green.png</key>
|
||||
<key type="filename">sprites/colors/purple.png</key>
|
||||
<key type="filename">sprites/colors/red.png</key>
|
||||
<key type="filename">sprites/colors/uncolored.png</key>
|
||||
<key type="filename">sprites/colors/white.png</key>
|
||||
<key type="filename">sprites/colors/yellow.png</key>
|
||||
<struct type="IndividualSpriteSettings">
|
||||
<key>pivotPoint</key>
|
||||
<point_f>0.5,0.5</point_f>
|
||||
<key>spriteScale</key>
|
||||
<double>1</double>
|
||||
<key>scale9Enabled</key>
|
||||
<false/>
|
||||
<key>scale9Borders</key>
|
||||
<rect>18,18,36,36</rect>
|
||||
<key>scale9Paddings</key>
|
||||
<rect>18,18,36,36</rect>
|
||||
<key>scale9FromFile</key>
|
||||
<false/>
|
||||
</struct>
|
||||
<key type="filename">sprites/debug/acceptor_slot.png</key>
|
||||
<key type="filename">sprites/debug/ejector_slot.png</key>
|
||||
<key type="filename">sprites/misc/hub_direction_indicator.png</key>
|
||||
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
BIN
res_raw/sprites/blueprints/balancer.png
Normal file
After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 7.8 KiB After Width: | Height: | Size: 7.4 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 47 KiB |
BIN
res_raw/sprites/blueprints/virtual_processor-painter.png
Normal file
After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 9.0 KiB After Width: | Height: | Size: 9.0 KiB |
Before Width: | Height: | Size: 9.0 KiB After Width: | Height: | Size: 9.0 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
BIN
res_raw/sprites/buildings/balancer.png
Normal file
After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 7.1 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 30 KiB |
BIN
res_raw/sprites/buildings/virtual_processor-painter.png
Normal file
After Width: | Height: | Size: 9.4 KiB |
Before Width: | Height: | Size: 9.8 KiB After Width: | Height: | Size: 11 KiB |
BIN
res_raw/sprites/colors/blue.png
Normal file
After Width: | Height: | Size: 4.0 KiB |
BIN
res_raw/sprites/colors/cyan.png
Normal file
After Width: | Height: | Size: 4.1 KiB |
BIN
res_raw/sprites/colors/green.png
Normal file
After Width: | Height: | Size: 4.0 KiB |
BIN
res_raw/sprites/colors/purple.png
Normal file
After Width: | Height: | Size: 4.1 KiB |
BIN
res_raw/sprites/colors/red.png
Normal file
After Width: | Height: | Size: 4.0 KiB |
BIN
res_raw/sprites/colors/uncolored.png
Normal file
After Width: | Height: | Size: 4.0 KiB |
BIN
res_raw/sprites/colors/white.png
Normal file
After Width: | Height: | Size: 4.1 KiB |
BIN
res_raw/sprites/colors/yellow.png
Normal file
After Width: | Height: | Size: 4.1 KiB |
@ -392,13 +392,18 @@ canvas {
|
||||
&::after {
|
||||
content: " ";
|
||||
background: uiResource("loading.svg") center center / contain no-repeat;
|
||||
@include S(width, 15px);
|
||||
@include S(height, 15px);
|
||||
@include S(margin-top, 1px);
|
||||
@include S(margin-left, 5px);
|
||||
@include S(width, 35px);
|
||||
@include S(height, 35px);
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
@include InlineAnimation(1.5s ease-in-out infinite) {
|
||||
50% {
|
||||
transform: scale(1.2) rotate(160deg);
|
||||
}
|
||||
}
|
||||
|
||||
@include DarkThemeOverride {
|
||||
color: #fff;
|
||||
}
|
||||
@ -463,18 +468,44 @@ canvas {
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
.loadingImage {
|
||||
background: uiResource("loading.svg") center center / #{D(60px)} no-repeat;
|
||||
background: uiResource("loading.svg") center center / #{D(40px)} no-repeat;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
|
||||
@include InlineAnimation(1.5s ease-in-out infinite) {
|
||||
50% {
|
||||
transform: scale(1.2) rotate(160deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.prefab_GameHint {
|
||||
position: absolute;
|
||||
@include S(left, 20px);
|
||||
@include S(right, 20px);
|
||||
@include S(bottom, 60px);
|
||||
@include Text;
|
||||
color: #666;
|
||||
|
||||
@include DarkThemeOverride() {
|
||||
color: lighten($darkModeGameBackground, 50);
|
||||
}
|
||||
}
|
||||
|
||||
.loadingStatus {
|
||||
position: absolute;
|
||||
@include S(left, 20px);
|
||||
@include S(right, 20px);
|
||||
@include S(bottom, 30px);
|
||||
@include Text;
|
||||
@include TextShadow3D(#aaa);
|
||||
@include PlainText;
|
||||
color: #aaa;
|
||||
|
||||
@include DarkThemeOverride {
|
||||
color: lighten($darkModeGameBackground, 20);
|
||||
}
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
@ -568,6 +599,13 @@ canvas {
|
||||
background-color: lighten($themeColor, 15);
|
||||
}
|
||||
}
|
||||
|
||||
@include DarkThemeOverride {
|
||||
background-color: $darkModeGameBackground !important;
|
||||
&.checked {
|
||||
background-color: $colorBlueBright !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.rangeInputContainer {
|
||||
@ -597,6 +635,16 @@ input.rangeInput {
|
||||
@include S(border-radius, 8px);
|
||||
}
|
||||
|
||||
@include DarkThemeOverride {
|
||||
&::-webkit-slider-runnable-track {
|
||||
background-color: $darkModeControlsBackground;
|
||||
}
|
||||
|
||||
&::-webkit-slider-thumb {
|
||||
box-shadow: inset 0 0 0 D(10px) #eee;
|
||||
}
|
||||
}
|
||||
|
||||
&::-webkit-slider-thumb {
|
||||
appearance: none;
|
||||
-webkit-appearance: none;
|
||||
|
@ -1,4 +1,4 @@
|
||||
$buildings: belt, cutter, miner, mixer, painter, rotater, splitter, stacker, trash, underground_belt, wire,
|
||||
$buildings: belt, cutter, miner, mixer, painter, rotater, balancer, stacker, trash, underground_belt, wire,
|
||||
constant_signal, logic_gate, lever, filter, wire_tunnel, display, virtual_processor, reader;
|
||||
|
||||
@each $building in $buildings {
|
||||
@ -7,7 +7,7 @@ $buildings: belt, cutter, miner, mixer, painter, rotater, splitter, stacker, tra
|
||||
}
|
||||
}
|
||||
|
||||
$buildingsAndVariants: belt, splitter, splitter-compact, splitter-compact-inverse, underground_belt,
|
||||
$buildingsAndVariants: belt, balancer, balancer-merger, balancer-splitter, underground_belt,
|
||||
underground_belt-tier2, miner, miner-chainable, cutter, cutter-quad, rotater, rotater-ccw, rotater-fl,
|
||||
stacker, mixer, painter, painter-double, painter-quad, trash, trash-storage;
|
||||
@each $building in $buildingsAndVariants {
|
||||
@ -16,10 +16,18 @@ $buildingsAndVariants: belt, splitter, splitter-compact, splitter-compact-invers
|
||||
}
|
||||
}
|
||||
|
||||
// Special case
|
||||
// @TODO: New buildings (balancer, wires, etc)
|
||||
|
||||
// Special cases for mirrored vairants
|
||||
[data-icon="building_tutorials/painter-mirrored.png"] {
|
||||
background-image: uiResource("res/ui/building_tutorials/painter.png") !important;
|
||||
}
|
||||
[data-icon="building_tutorials/balancer-merger-inverse.png"] {
|
||||
background-image: uiResource("res/ui/building_tutorials/balancer-merger.png") !important;
|
||||
}
|
||||
[data-icon="building_tutorials/balancer-splitter-inverse.png"] {
|
||||
background-image: uiResource("res/ui/building_tutorials/balancer-splitter.png") !important;
|
||||
}
|
||||
|
||||
$icons: notification_saved, notification_success, notification_upgrade;
|
||||
@each $icon in $icons {
|
||||
|
@ -1,107 +1,93 @@
|
||||
.ingame_buildingsToolbar {
|
||||
position: fixed;
|
||||
@include S(bottom, 0px);
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
|
||||
// NOTE: This flex rule may not be necessary. Need to find out intent.
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: transparent;
|
||||
border-bottom-width: 0;
|
||||
transition: transform 120ms ease-in-out;
|
||||
will-change: transform;
|
||||
|
||||
background-color: rgba(mix(#ddd, $colorBlueBright, 90%), 0.5);
|
||||
backdrop-filter: blur(D(3px));
|
||||
|
||||
@include DarkThemeOverride {
|
||||
background-color: #222428;
|
||||
}
|
||||
|
||||
&:not(.visible) {
|
||||
transform: translateX(-50%) translateY(#{D(100px)});
|
||||
}
|
||||
|
||||
@include S(border-top-left-radius, $globalBorderRadius);
|
||||
@include S(border-top-right-radius, $globalBorderRadius);
|
||||
|
||||
.buildings {
|
||||
display: grid;
|
||||
grid-auto-flow: column;
|
||||
|
||||
.building {
|
||||
color: $accentColorDark;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@include S(padding, 5px);
|
||||
@include S(padding-bottom, 1px);
|
||||
@include S(width, 35px);
|
||||
@include S(height, 40px);
|
||||
|
||||
background: center center / 65% no-repeat;
|
||||
|
||||
&:not(.unlocked) {
|
||||
@include S(width, 20px);
|
||||
opacity: 0.15;
|
||||
background-image: none !important;
|
||||
|
||||
&::before {
|
||||
content: " ";
|
||||
background: uiResource("locked_building.png") center center / #{D(20px)} #{D(20px)}
|
||||
no-repeat;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 4;
|
||||
}
|
||||
}
|
||||
|
||||
@include S(border-radius, $globalBorderRadius);
|
||||
|
||||
&.unlocked {
|
||||
pointer-events: all;
|
||||
transition: all 50ms ease-in-out;
|
||||
transition-property: background-color, transform;
|
||||
cursor: pointer;
|
||||
will-change: transform;
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background-color: $accentColorDark;
|
||||
opacity: 0;
|
||||
will-change: opacity;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
&::before {
|
||||
opacity: 0.1;
|
||||
}
|
||||
}
|
||||
|
||||
&.selected {
|
||||
transform: scale(1.05);
|
||||
|
||||
&::before {
|
||||
background-color: $colorBlueBright;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.keybinding {
|
||||
color: #111;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.ingame_buildingsToolbar {
|
||||
position: absolute;
|
||||
@include S(bottom, 5px);
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
|
||||
// NOTE: This flex rule may not be necessary. Need to find out intent.
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: transparent;
|
||||
transition: transform 120ms ease-in-out;
|
||||
will-change: transform;
|
||||
|
||||
backdrop-filter: blur(D(3px));
|
||||
background-color: rgba(0, 40, 80, 0.05);
|
||||
@include S(border-radius, $globalBorderRadius);
|
||||
|
||||
@include DarkThemeOverride {
|
||||
background-color: rgba(darken($darkModeGameBackground, 15), 0.4);
|
||||
|
||||
&#ingame_HUD_wires_toolbar {
|
||||
background-color: rgba(darken($darkModeGameBackground, 5), 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.visible) {
|
||||
transform: translateX(-50%) translateY(#{D(100px)});
|
||||
}
|
||||
|
||||
.buildings {
|
||||
display: grid;
|
||||
grid-auto-flow: column;
|
||||
|
||||
.building {
|
||||
color: $accentColorDark;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@include S(padding, 5px);
|
||||
@include S(padding-bottom, 1px);
|
||||
@include S(width, 35px);
|
||||
@include S(height, 40px);
|
||||
|
||||
background: center center / 70% no-repeat;
|
||||
|
||||
&:not(.unlocked) {
|
||||
@include S(width, 20px);
|
||||
opacity: 0.15;
|
||||
background-image: none !important;
|
||||
|
||||
&::before {
|
||||
content: " ";
|
||||
background: uiResource("locked_building.png") center center / #{D(20px)} #{D(20px)}
|
||||
no-repeat;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 4;
|
||||
}
|
||||
}
|
||||
|
||||
@include S(border-radius, $globalBorderRadius);
|
||||
|
||||
&.unlocked {
|
||||
pointer-events: all;
|
||||
transition: all 50ms ease-in-out;
|
||||
transition-property: background-color, transform;
|
||||
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background-color: rgba(30, 40, 90, 0.1);
|
||||
}
|
||||
|
||||
&.pressed {
|
||||
transform: scale(0.9) !important;
|
||||
}
|
||||
|
||||
&.selected {
|
||||
// transform: scale(1.05);
|
||||
background-color: rgba(lighten($colorBlueBright, 9), 0.4);
|
||||
|
||||
.keybinding {
|
||||
color: #111;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,231 +1,233 @@
|
||||
.ingameDialog {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
pointer-events: all;
|
||||
background: $modalDialogBg;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
@include InlineAnimation(0.12s ease-in-out) {
|
||||
0% {
|
||||
background-color: transparent;
|
||||
opacity: 0.5;
|
||||
}
|
||||
100% {
|
||||
background-color: $modalDialogBg;
|
||||
}
|
||||
}
|
||||
|
||||
$darkModeDialogBg: darken($darkModeGameBackground, 10);
|
||||
|
||||
@include DarkThemeOverride {
|
||||
background: rgba($darkModeDialogBg, 0.9);
|
||||
@include InlineAnimation(0.12s ease-in-out) {
|
||||
0% {
|
||||
background-color: transparent;
|
||||
opacity: 0.5;
|
||||
}
|
||||
100% {
|
||||
background-color: rgba($darkModeDialogBg, 0.9);
|
||||
}
|
||||
}
|
||||
|
||||
> .dialogInner.optionChooserDialog .optionParent {
|
||||
.option {
|
||||
background: #3d3f42;
|
||||
|
||||
&:hover {
|
||||
background-color: #424348;
|
||||
}
|
||||
|
||||
&.active {
|
||||
background: $colorBlueBright;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.visible {
|
||||
.dialogInner {
|
||||
opacity: 1;
|
||||
}
|
||||
backdrop-filter: blur(D(3px));
|
||||
}
|
||||
|
||||
.dialogInner {
|
||||
transition: opacity 0.2s ease-in-out;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&.loadingDialog {
|
||||
* {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
> .dialogInner {
|
||||
background: #fff;
|
||||
max-height: calc(100vh - #{D(40px)});
|
||||
@include S(border-radius, $globalBorderRadius);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@include S(padding, 12px);
|
||||
pointer-events: all;
|
||||
|
||||
@include DarkThemeOverride {
|
||||
background: #333438;
|
||||
}
|
||||
|
||||
&.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;
|
||||
transition: background-color 0.12s ease-in-out;
|
||||
|
||||
&:hover {
|
||||
background-color: #e7e7e7;
|
||||
}
|
||||
|
||||
&.active {
|
||||
background-color: $colorBlueBright;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .title {
|
||||
@include Heading;
|
||||
margin: 0;
|
||||
text-transform: uppercase;
|
||||
display: grid;
|
||||
align-items: center;
|
||||
grid-template-columns: 1fr auto;
|
||||
@include S(margin-bottom, 10px);
|
||||
|
||||
@include DarkThemeInvert();
|
||||
> .closeButton {
|
||||
opacity: 0.7;
|
||||
@include S(width, 20px);
|
||||
@include S(height, 20px);
|
||||
background: uiResource("icons/close.png") center center / 80% no-repeat;
|
||||
cursor: pointer;
|
||||
pointer-events: all;
|
||||
transition: opacity 0.2s ease-in-out;
|
||||
&:hover {
|
||||
opacity: 0.4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .content {
|
||||
@include PlainText;
|
||||
overflow-y: auto;
|
||||
pointer-events: all;
|
||||
@include S(width, 350px);
|
||||
|
||||
@include DarkThemeOverride {
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
a {
|
||||
color: $colorBlueBright;
|
||||
}
|
||||
|
||||
strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.keybinding {
|
||||
position: relative;
|
||||
background: #eee;
|
||||
@include PlainText;
|
||||
height: unset;
|
||||
margin: 1px 0;
|
||||
}
|
||||
|
||||
input {
|
||||
background: #eee;
|
||||
color: #333438;
|
||||
width: 100%;
|
||||
|
||||
&.errored {
|
||||
background-color: rgb(250, 206, 206);
|
||||
}
|
||||
}
|
||||
|
||||
ul.bucketList {
|
||||
padding-left: 30px;
|
||||
|
||||
li {
|
||||
display: list-item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .buttons {
|
||||
@include S(margin-top, 15px);
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
> button {
|
||||
@include S(margin-left, 8px);
|
||||
@include Text;
|
||||
@include S(min-width, 60px);
|
||||
@include S(padding, 5px, 15px);
|
||||
|
||||
transition: opacity 0.12s ease-in-out;
|
||||
&:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
&.good {
|
||||
background-color: $colorGreenBright;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
&.bad {
|
||||
background-color: $colorRedBright;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
&.timedButton {
|
||||
pointer-events: none;
|
||||
cursor: default;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
&::after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: unset;
|
||||
z-index: 5;
|
||||
content: " ";
|
||||
display: inline-block;
|
||||
background: rgba(#fff, 0.6);
|
||||
@include InlineAnimation(5s linear) {
|
||||
0% {
|
||||
width: 100%;
|
||||
}
|
||||
100% {
|
||||
width: 0%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.ingameDialog {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
pointer-events: all;
|
||||
background: $modalDialogBg;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
@include InlineAnimation(0.12s ease-in-out) {
|
||||
0% {
|
||||
background-color: transparent;
|
||||
opacity: 0.5;
|
||||
}
|
||||
100% {
|
||||
background-color: $modalDialogBg;
|
||||
}
|
||||
}
|
||||
|
||||
$darkModeDialogBg: darken($darkModeGameBackground, 5);
|
||||
|
||||
@include DarkThemeOverride {
|
||||
background: rgba($darkModeDialogBg, 0.9);
|
||||
@include InlineAnimation(0.12s ease-in-out) {
|
||||
0% {
|
||||
background-color: transparent;
|
||||
opacity: 0.5;
|
||||
}
|
||||
100% {
|
||||
background-color: rgba($darkModeDialogBg, 0.9);
|
||||
}
|
||||
}
|
||||
|
||||
> .dialogInner.optionChooserDialog .optionParent {
|
||||
.option {
|
||||
background: $darkModeControlsBackground;
|
||||
|
||||
&:hover {
|
||||
background-color: lighten($darkModeControlsBackground, 5);
|
||||
}
|
||||
|
||||
&.active {
|
||||
background: $colorBlueBright;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.visible {
|
||||
.dialogInner {
|
||||
opacity: 1;
|
||||
}
|
||||
backdrop-filter: blur(D(3px));
|
||||
}
|
||||
|
||||
.dialogInner {
|
||||
transition: opacity 0.2s ease-in-out;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&.loadingDialog {
|
||||
* {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
> .dialogInner {
|
||||
background: #fff;
|
||||
max-height: calc(100vh - #{D(40px)});
|
||||
@include S(border-radius, $globalBorderRadius);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@include S(padding, 12px);
|
||||
pointer-events: all;
|
||||
|
||||
@include DarkThemeOverride {
|
||||
background: darken($darkModeControlsBackground, 5);
|
||||
}
|
||||
|
||||
&.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;
|
||||
|
||||
transition: background-color 0.12s ease-in-out;
|
||||
|
||||
&:hover {
|
||||
background-color: #e7e7e7;
|
||||
}
|
||||
|
||||
&.active {
|
||||
background-color: $colorBlueBright;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .title {
|
||||
@include Heading;
|
||||
margin: 0;
|
||||
text-transform: uppercase;
|
||||
display: grid;
|
||||
align-items: center;
|
||||
grid-template-columns: 1fr auto;
|
||||
@include S(margin-bottom, 10px);
|
||||
|
||||
@include DarkThemeInvert();
|
||||
> .closeButton {
|
||||
opacity: 0.7;
|
||||
@include S(width, 20px);
|
||||
@include S(height, 20px);
|
||||
background: uiResource("icons/close.png") center center / 80% no-repeat;
|
||||
cursor: pointer;
|
||||
pointer-events: all;
|
||||
transition: opacity 0.2s ease-in-out;
|
||||
&:hover {
|
||||
opacity: 0.4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .content {
|
||||
@include PlainText;
|
||||
overflow-y: auto;
|
||||
pointer-events: all;
|
||||
@include S(width, 350px);
|
||||
|
||||
@include DarkThemeOverride {
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
a {
|
||||
color: $colorBlueBright;
|
||||
}
|
||||
|
||||
strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.keybinding {
|
||||
position: relative;
|
||||
background: #eee;
|
||||
@include PlainText;
|
||||
height: unset;
|
||||
margin: 1px 0;
|
||||
}
|
||||
|
||||
input {
|
||||
background: #eee;
|
||||
color: #333438;
|
||||
width: 100%;
|
||||
|
||||
&.errored {
|
||||
background-color: rgb(250, 206, 206);
|
||||
}
|
||||
}
|
||||
|
||||
ul.bucketList {
|
||||
padding-left: 30px;
|
||||
|
||||
li {
|
||||
display: list-item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .buttons {
|
||||
@include S(margin-top, 15px);
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
> button {
|
||||
@include S(margin-left, 8px);
|
||||
@include Text;
|
||||
@include S(min-width, 60px);
|
||||
@include S(padding, 5px, 15px);
|
||||
|
||||
transition: opacity 0.12s ease-in-out;
|
||||
&:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
&.good {
|
||||
background-color: $colorGreenBright;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
&.bad {
|
||||
background-color: $colorRedBright;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
&.timedButton {
|
||||
pointer-events: none;
|
||||
cursor: default;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
&::after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: unset;
|
||||
z-index: 5;
|
||||
content: " ";
|
||||
display: inline-block;
|
||||
background: rgba(#fff, 0.6);
|
||||
@include InlineAnimation(5s linear) {
|
||||
0% {
|
||||
width: 100%;
|
||||
}
|
||||
100% {
|
||||
width: 0%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,43 +1,79 @@
|
||||
#ingame_HUD_EntityDebugger {
|
||||
position: absolute;
|
||||
background: $ingameHudBg;
|
||||
@include S(padding, 5px);
|
||||
@include S(right, 30px);
|
||||
@include S(top, 200px);
|
||||
|
||||
font-size: 14px;
|
||||
line-height: 16px;
|
||||
color: #fff;
|
||||
background: rgba(0, 10, 20, 0.7);
|
||||
padding: 10px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
|
||||
@include SuperSmallText;
|
||||
color: #eee;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
> label {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.hint {
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
&,
|
||||
* {
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
.flag {
|
||||
display: inline-block;
|
||||
background: #333438;
|
||||
@include S(padding, 2px);
|
||||
@include S(margin-right, 2px);
|
||||
|
||||
u {
|
||||
opacity: 0.5;
|
||||
}
|
||||
.propertyTable {
|
||||
@include S(margin-top, 8px);
|
||||
}
|
||||
|
||||
.components {
|
||||
@include S(margin-top, 4px);
|
||||
.propertyTable,
|
||||
.entityComponents,
|
||||
.entityComponents .object > div {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
@include S(grid-gap, 3px);
|
||||
.component {
|
||||
@include S(padding, 2px);
|
||||
background: #333;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
grid-template-columns: 1fr auto;
|
||||
@include S(column-gap, 10px);
|
||||
}
|
||||
|
||||
.data {
|
||||
@include S(width, 150px);
|
||||
@include S(height, 130px);
|
||||
.entityComponents {
|
||||
grid-column: 1 / 3;
|
||||
@include S(margin-top, 5px);
|
||||
|
||||
font-family: "Roboto Mono", "Fira Code", monospace;
|
||||
font-size: 90%;
|
||||
@include S(letter-spacing, -0.5px);
|
||||
|
||||
label,
|
||||
span {
|
||||
line-height: 1.5em;
|
||||
|
||||
&:not(span) {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
&,
|
||||
* {
|
||||
@include SuperSmallText;
|
||||
@include S(font-size, 7px, $important: true);
|
||||
@include S(line-height, 12px, $important: true);
|
||||
}
|
||||
|
||||
.object {
|
||||
grid-column: 1 / 3;
|
||||
line-height: 1.5em;
|
||||
|
||||
> summary {
|
||||
transition: opacity 0.1s ease-in-out;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
> div {
|
||||
@include S(margin-left, 4px);
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,111 +1,95 @@
|
||||
#ingame_HUD_GameMenu {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
display: flex;
|
||||
grid-auto-flow: column;
|
||||
@include S(top, 10px);
|
||||
@include S(right, 10px);
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr 1fr;
|
||||
@include S(grid-gap, 6px);
|
||||
|
||||
> .menuButtons {
|
||||
backdrop-filter: blur(D(1px));
|
||||
|
||||
> button,
|
||||
> .button {
|
||||
@include PlainText;
|
||||
@include IncreasedClickArea(0px);
|
||||
background: green;
|
||||
@include S(width, 30px);
|
||||
@include S(height, 30px);
|
||||
|
||||
pointer-events: all;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
@include S(padding, 5px, 4px);
|
||||
justify-content: flex-end;
|
||||
@include S(margin-left, 20px);
|
||||
transition: all 0.12s ease-in-out;
|
||||
transition-property: opacity, transform;
|
||||
|
||||
> .button {
|
||||
@include S(width, 30px);
|
||||
@include S(height, 30px);
|
||||
display: inline-block;
|
||||
background: center center / 60% no-repeat;
|
||||
pointer-events: all;
|
||||
cursor: pointer;
|
||||
transition: all 0.12s ease-in-out;
|
||||
transition-property: opacity, transform;
|
||||
will-change: opacity;
|
||||
opacity: 0.9;
|
||||
@include S(margin-left, 5px);
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
background: center center / 70% no-repeat;
|
||||
grid-row: 1;
|
||||
|
||||
@include IncreasedClickArea(0px);
|
||||
&.pressed {
|
||||
transform: scale(0.9) !important;
|
||||
}
|
||||
|
||||
@include DarkThemeInvert;
|
||||
opacity: 0.7;
|
||||
&:hover {
|
||||
opacity: 0.9 !important;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
@include DarkThemeInvert;
|
||||
|
||||
&.save {
|
||||
background-image: uiResource("icons/save.png");
|
||||
@include MakeAnimationWrappedEvenOdd(0.5s ease-in-out) {
|
||||
0% {
|
||||
transform: scale(1, 1);
|
||||
}
|
||||
&.shop {
|
||||
background-image: uiResource("icons/shop.png");
|
||||
grid-column: 1;
|
||||
}
|
||||
|
||||
70% {
|
||||
transform: scale(1.5, 1.5) rotate(20deg);
|
||||
opacity: 0.2;
|
||||
}
|
||||
&.stats {
|
||||
background-image: uiResource("icons/statistics.png");
|
||||
grid-column: 2;
|
||||
}
|
||||
|
||||
85% {
|
||||
transform: scale(0.9, 0.9);
|
||||
opacity: 1;
|
||||
}
|
||||
&.save {
|
||||
background-image: uiResource("icons/save.png");
|
||||
grid-column: 3;
|
||||
@include MakeAnimationWrappedEvenOdd(0.5s ease-in-out) {
|
||||
0% {
|
||||
transform: scale(1, 1);
|
||||
}
|
||||
|
||||
90% {
|
||||
transform: scale(1.1, 1.1);
|
||||
}
|
||||
70% {
|
||||
transform: scale(1.5, 1.5) rotate(20deg);
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
85% {
|
||||
transform: scale(0.9, 0.9);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
90% {
|
||||
transform: scale(1.1, 1.1);
|
||||
}
|
||||
}
|
||||
|
||||
&.settings {
|
||||
background-image: uiResource("icons/settings.png");
|
||||
&.saving {
|
||||
@include InlineAnimation(0.4s ease-in-out infinite) {
|
||||
50% {
|
||||
opacity: 0.5;
|
||||
transform: scale(0.8);
|
||||
}
|
||||
}
|
||||
pointer-events: none;
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.buttonContainer button {
|
||||
@include PlainText;
|
||||
color: #fff;
|
||||
border-color: rgba(0, 0, 0, 0.1);
|
||||
@include S(padding, 5px, 5px, 5px);
|
||||
|
||||
@include S(padding-left, 30px);
|
||||
@include S(margin-right, 3px);
|
||||
@include IncreasedClickArea(0px);
|
||||
@include ButtonText;
|
||||
@include S(min-height, 40px);
|
||||
transition: all 0.12s ease-in-out;
|
||||
transition-property: opacity, transform;
|
||||
display: inline-flex;
|
||||
background: center #{D(13px)} / #{D(20px)} no-repeat;
|
||||
background-color: $colorGreenBright;
|
||||
|
||||
&[data-button-id="shop"] {
|
||||
background-color: rgb(93, 103, 250);
|
||||
background-image: uiResource("icons/shop.png");
|
||||
background-size: #{D(18px)};
|
||||
}
|
||||
&[data-button-id="stats"] {
|
||||
background-color: rgb(85, 199, 138);
|
||||
background-image: uiResource("icons/statistics.png");
|
||||
&.settings {
|
||||
background-image: uiResource("icons/settings_menu_settings.png");
|
||||
grid-column: 4;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.keybinding {
|
||||
border: 0;
|
||||
color: #fff;
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
bottom: unset;
|
||||
background: transparent;
|
||||
@include S(top, 0px);
|
||||
right: unset;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
&:not(.hasBadge) .badge {
|
||||
@ -113,34 +97,27 @@
|
||||
}
|
||||
|
||||
&.hasBadge {
|
||||
transform-origin: 50% 0%;
|
||||
@include InlineAnimation(1s ease-in-out infinite) {
|
||||
&.shop {
|
||||
filter: none;
|
||||
background-image: uiResource("icons/shop_active.png");
|
||||
opacity: 0.9;
|
||||
}
|
||||
transform-origin: 50% 50%;
|
||||
@include InlineAnimation(0.8s ease-in-out infinite) {
|
||||
50% {
|
||||
transform: scale(1.02);
|
||||
transform: scale(1.3) rotate(6deg);
|
||||
}
|
||||
}
|
||||
|
||||
.badge {
|
||||
position: absolute;
|
||||
@include S(bottom, -8px);
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
|
||||
background: #333;
|
||||
transform: translate(-50%, -50%);
|
||||
@include PlainText;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
@include S(min-width, 5px);
|
||||
@include S(height, 10px);
|
||||
@include S(padding, 1px, 3px, 2px);
|
||||
@include S(border-radius, $globalBorderRadius);
|
||||
border: #{D(1px)} solid #fff;
|
||||
@include InlineAnimation(1s ease-in-out infinite) {
|
||||
50% {
|
||||
transform: translateX(-50%) scale(1.05);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
color: #333438;
|
||||
backdrop-filter: blur(D(2px));
|
||||
backdrop-filter: blur(D(1px));
|
||||
padding: D(3px);
|
||||
|
||||
@include DarkThemeOverride {
|
||||
|
@ -1,138 +1,137 @@
|
||||
#ingame_HUD_PinnedShapes {
|
||||
position: absolute;
|
||||
@include S(left, 9px);
|
||||
@include S(top, 150px);
|
||||
@include PlainText;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start;
|
||||
|
||||
> .shape {
|
||||
position: relative;
|
||||
display: grid;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
grid-template-columns: auto 1fr;
|
||||
grid-template-rows: 1fr 1fr;
|
||||
@include S(margin-bottom, 4px);
|
||||
color: #333438;
|
||||
// text-shadow: #{D(1px)} #{D(1px)} 0 rgba(0, 10, 20, 0.2);
|
||||
filter: drop-shadow(#{D(1px)} #{D(1px)} 0 rgba(0, 10, 20, 0.2));
|
||||
|
||||
&.unpinable {
|
||||
> canvas {
|
||||
cursor: pointer;
|
||||
pointer-events: all;
|
||||
}
|
||||
}
|
||||
|
||||
> canvas {
|
||||
@include S(width, 25px);
|
||||
@include S(height, 25px);
|
||||
grid-column: 1 / 2;
|
||||
grid-row: 1 / 3;
|
||||
pointer-events: all;
|
||||
transition: transform 0.1s ease-in-out;
|
||||
transform-origin: D(2px) center;
|
||||
will-change: transform;
|
||||
position: relative;
|
||||
z-index: 20;
|
||||
&:hover {
|
||||
transform: scale(2);
|
||||
z-index: 21;
|
||||
}
|
||||
}
|
||||
|
||||
> .amountLabel,
|
||||
> .goalLabel {
|
||||
@include S(margin-left, 5px);
|
||||
@include SuperSmallText;
|
||||
font-weight: bold;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
grid-column: 2 / 3;
|
||||
@include S(height, 9px);
|
||||
|
||||
@include DarkThemeOverride {
|
||||
color: #eee;
|
||||
}
|
||||
}
|
||||
|
||||
> .goalLabel {
|
||||
@include S(font-size, 7px);
|
||||
opacity: 0.9;
|
||||
align-self: start;
|
||||
justify-self: start;
|
||||
font-weight: normal;
|
||||
grid-row: 2 / 3;
|
||||
}
|
||||
|
||||
> .amountLabel {
|
||||
align-self: end;
|
||||
justify-self: start;
|
||||
grid-row: 1 / 2;
|
||||
}
|
||||
|
||||
> .infoButton {
|
||||
@include S(width, 8px);
|
||||
@include S(height, 8px);
|
||||
background: uiResource("icons/info_button.png") center center / 95% no-repeat;
|
||||
position: absolute;
|
||||
opacity: 0.7;
|
||||
@include S(top, 13px);
|
||||
@include S(left, -7px);
|
||||
@include DarkThemeInvert;
|
||||
@include IncreasedClickArea(2px);
|
||||
transition: opacity 0.12s ease-in-out;
|
||||
z-index: 100;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
&.goal,
|
||||
&.blueprint {
|
||||
.amountLabel::after {
|
||||
content: " ";
|
||||
position: absolute;
|
||||
display: inline-block;
|
||||
@include S(width, 8px);
|
||||
@include S(height, 8px);
|
||||
@include S(top, 4px);
|
||||
@include S(left, -7px);
|
||||
background: center center / contain no-repeat;
|
||||
}
|
||||
|
||||
&.goal .amountLabel {
|
||||
&::after {
|
||||
background-image: uiResource("icons/current_goal_marker.png");
|
||||
background-size: 90%;
|
||||
}
|
||||
@include DarkThemeOverride {
|
||||
&::after {
|
||||
background-image: uiResource("icons/current_goal_marker_inverted.png") !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.blueprint .amountLabel {
|
||||
&::after {
|
||||
background-image: uiResource("icons/blueprint_marker.png");
|
||||
background-size: 90%;
|
||||
}
|
||||
@include DarkThemeOverride {
|
||||
&::after {
|
||||
background-image: uiResource("icons/blueprint_marker_inverted.png") !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.completed {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
#ingame_HUD_PinnedShapes {
|
||||
position: absolute;
|
||||
@include S(left, 9px);
|
||||
@include S(top, 150px);
|
||||
@include PlainText;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start;
|
||||
|
||||
> .shape {
|
||||
position: relative;
|
||||
display: grid;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
grid-template-columns: auto 1fr;
|
||||
grid-template-rows: 1fr 1fr;
|
||||
@include S(margin-bottom, 4px);
|
||||
color: #333438;
|
||||
// text-shadow: #{D(1px)} #{D(1px)} 0 rgba(0, 10, 20, 0.2);
|
||||
|
||||
&.unpinable {
|
||||
> canvas {
|
||||
cursor: pointer;
|
||||
pointer-events: all;
|
||||
}
|
||||
}
|
||||
|
||||
> canvas {
|
||||
@include S(width, 25px);
|
||||
@include S(height, 25px);
|
||||
grid-column: 1 / 2;
|
||||
grid-row: 1 / 3;
|
||||
pointer-events: all;
|
||||
transition: transform 0.1s ease-in-out;
|
||||
transform-origin: D(2px) center;
|
||||
will-change: transform;
|
||||
position: relative;
|
||||
z-index: 20;
|
||||
&:hover {
|
||||
transform: scale(2);
|
||||
z-index: 21;
|
||||
}
|
||||
}
|
||||
|
||||
> .amountLabel,
|
||||
> .goalLabel {
|
||||
@include S(margin-left, 5px);
|
||||
@include SuperSmallText;
|
||||
font-weight: bold;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
grid-column: 2 / 3;
|
||||
@include S(height, 9px);
|
||||
|
||||
@include DarkThemeOverride {
|
||||
color: #eee;
|
||||
}
|
||||
}
|
||||
|
||||
> .goalLabel {
|
||||
@include S(font-size, 7px);
|
||||
opacity: 0.9;
|
||||
align-self: start;
|
||||
justify-self: start;
|
||||
font-weight: normal;
|
||||
grid-row: 2 / 3;
|
||||
}
|
||||
|
||||
> .amountLabel {
|
||||
align-self: end;
|
||||
justify-self: start;
|
||||
grid-row: 1 / 2;
|
||||
}
|
||||
|
||||
> .infoButton {
|
||||
@include S(width, 8px);
|
||||
@include S(height, 8px);
|
||||
background: uiResource("icons/info_button.png") center center / 95% no-repeat;
|
||||
position: absolute;
|
||||
opacity: 0.7;
|
||||
@include S(top, 13px);
|
||||
@include S(left, -7px);
|
||||
@include DarkThemeInvert;
|
||||
@include IncreasedClickArea(2px);
|
||||
transition: opacity 0.12s ease-in-out;
|
||||
z-index: 100;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
&.goal,
|
||||
&.blueprint {
|
||||
.amountLabel::after {
|
||||
content: " ";
|
||||
position: absolute;
|
||||
display: inline-block;
|
||||
@include S(width, 8px);
|
||||
@include S(height, 8px);
|
||||
@include S(top, 4px);
|
||||
@include S(left, -7px);
|
||||
background: center center / contain no-repeat;
|
||||
}
|
||||
|
||||
&.goal .amountLabel {
|
||||
&::after {
|
||||
background-image: uiResource("icons/current_goal_marker.png");
|
||||
background-size: 90%;
|
||||
}
|
||||
@include DarkThemeOverride {
|
||||
&::after {
|
||||
background-image: uiResource("icons/current_goal_marker_inverted.png") !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.blueprint .amountLabel {
|
||||
&::after {
|
||||
background-image: uiResource("icons/blueprint_marker.png");
|
||||
background-size: 90%;
|
||||
}
|
||||
@include DarkThemeOverride {
|
||||
&::after {
|
||||
background-image: uiResource("icons/blueprint_marker_inverted.png") !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.completed {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,50 +1,50 @@
|
||||
#ingame_HUD_SandboxController {
|
||||
position: absolute;
|
||||
background: $ingameHudBg;
|
||||
@include S(padding, 5px);
|
||||
@include S(bottom, 10px);
|
||||
@include S(left, 10px);
|
||||
|
||||
@include SuperSmallText;
|
||||
color: #eee;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
> label {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.hint {
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
.plusMinus {
|
||||
@include S(margin-top, 4px);
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto auto;
|
||||
align-items: center;
|
||||
@include S(grid-gap, 4px);
|
||||
|
||||
button {
|
||||
@include PlainText;
|
||||
@include S(padding, 0);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@include S(width, 15px);
|
||||
@include S(height, 15px);
|
||||
@include IncreasedClickArea(0px);
|
||||
}
|
||||
}
|
||||
|
||||
.additionalOptions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@include S(margin-top, 10px);
|
||||
button {
|
||||
@include S(margin-bottom, 2px);
|
||||
@include IncreasedClickArea(0px);
|
||||
@include SuperSmallText;
|
||||
}
|
||||
}
|
||||
}
|
||||
#ingame_HUD_SandboxController {
|
||||
position: absolute;
|
||||
background: $ingameHudBg;
|
||||
@include S(padding, 5px);
|
||||
@include S(bottom, 10px);
|
||||
@include S(left, 10px);
|
||||
|
||||
@include SuperSmallText;
|
||||
color: #eee;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
> label {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.sandboxHint {
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
.plusMinus {
|
||||
@include S(margin-top, 4px);
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto auto;
|
||||
align-items: center;
|
||||
@include S(grid-gap, 4px);
|
||||
|
||||
button {
|
||||
@include PlainText;
|
||||
@include S(padding, 0);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@include S(width, 15px);
|
||||
@include S(height, 15px);
|
||||
@include IncreasedClickArea(0px);
|
||||
}
|
||||
}
|
||||
|
||||
.additionalOptions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@include S(margin-top, 10px);
|
||||
button {
|
||||
@include S(margin-bottom, 2px);
|
||||
@include IncreasedClickArea(0px);
|
||||
@include SuperSmallText;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,41 +1,61 @@
|
||||
#ingame_HUD_SettingsMenu {
|
||||
.statsElement {
|
||||
position: absolute;
|
||||
@include S(left, 30px);
|
||||
@include S(top, 30px);
|
||||
color: #fff;
|
||||
display: flex;
|
||||
grid-template-rows: 1fr auto;
|
||||
flex-direction: column;
|
||||
|
||||
strong {
|
||||
text-transform: uppercase;
|
||||
@include PlainText;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
span {
|
||||
@include S(margin-bottom, 25px);
|
||||
@include Heading;
|
||||
}
|
||||
}
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.buttons {
|
||||
display: grid;
|
||||
grid-auto-flow: row;
|
||||
@include S(grid-gap, 10px);
|
||||
background: rgba(0, 10, 20, 0.1);
|
||||
@include S(padding, 10px);
|
||||
@include S(border-radius, $globalBorderRadius);
|
||||
|
||||
button {
|
||||
background-color: #eee;
|
||||
color: #55585a;
|
||||
}
|
||||
}
|
||||
}
|
||||
#ingame_HUD_SettingsMenu {
|
||||
.statsElement {
|
||||
position: absolute;
|
||||
@include S(left, 30px);
|
||||
@include S(right, 30px);
|
||||
@include S(bottom, 30px);
|
||||
color: #fff;
|
||||
display: grid;
|
||||
grid-template-rows: auto auto;
|
||||
grid-auto-columns: 1fr;
|
||||
align-items: center;
|
||||
justify-items: center;
|
||||
|
||||
strong {
|
||||
text-transform: uppercase;
|
||||
@include PlainText;
|
||||
opacity: 0.5;
|
||||
grid-row: 1;
|
||||
}
|
||||
|
||||
span {
|
||||
@include Heading;
|
||||
grid-row: 2;
|
||||
}
|
||||
}
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.buttons {
|
||||
display: grid;
|
||||
grid-auto-flow: column;
|
||||
@include S(grid-gap, 50px);
|
||||
@include S(margin-top, -10px);
|
||||
|
||||
button {
|
||||
background: transparent;
|
||||
filter: invert(1);
|
||||
|
||||
background: uiResource("icons/settings_menu_play.png") center top / contain no-repeat;
|
||||
content: "";
|
||||
opacity: 0.8;
|
||||
@include S(width, 35px);
|
||||
@include S(height, 35px);
|
||||
|
||||
&.settings {
|
||||
background-image: uiResource("icons/settings_menu_settings.png");
|
||||
}
|
||||
|
||||
&.menu {
|
||||
background-image: uiResource("icons/settings_menu_exit.png");
|
||||
}
|
||||
|
||||
&:hover {
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,161 +1,154 @@
|
||||
#ingame_HUD_ShapeViewer {
|
||||
$dims: 170px;
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
@include S(width, $dims);
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
overflow-x: hidden;
|
||||
|
||||
&[data-layers="3"],
|
||||
&[data-layers="4"] {
|
||||
@include S(width, 2 * $dims);
|
||||
.renderArea {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
@include S(grid-row-gap, 15px);
|
||||
}
|
||||
}
|
||||
|
||||
.renderArea {
|
||||
display: grid;
|
||||
width: 100%;
|
||||
@include S(grid-row-gap, 10px);
|
||||
align-items: center;
|
||||
justify-items: center;
|
||||
}
|
||||
|
||||
.infoArea {
|
||||
align-self: flex-end;
|
||||
@include S(margin-top, 10px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
|
||||
button {
|
||||
@include S(margin, 0);
|
||||
@include PlainText;
|
||||
}
|
||||
}
|
||||
|
||||
.seperator {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.layer {
|
||||
position: relative;
|
||||
background: #eee;
|
||||
|
||||
@include DarkThemeOverride {
|
||||
background: rgba(0, 10, 20, 0.2);
|
||||
}
|
||||
@include S(width, 150px);
|
||||
@include S(height, 100px);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
> canvas {
|
||||
@include S(width, 50px);
|
||||
@include S(height, 50px);
|
||||
}
|
||||
|
||||
.quad {
|
||||
position: absolute;
|
||||
width: 50%;
|
||||
height: 50%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
|
||||
$arrowDims: 23px;
|
||||
$spacing: 9px;
|
||||
@include S(padding, 6px);
|
||||
|
||||
.colorLabel {
|
||||
text-transform: uppercase;
|
||||
@include SuperSmallText;
|
||||
@include S(font-size, 9px);
|
||||
}
|
||||
|
||||
.emptyLabel {
|
||||
text-transform: uppercase;
|
||||
@include SuperSmallText;
|
||||
@include S(font-size, 9px);
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: " ";
|
||||
background: rgba(0, 10, 20, 0.5);
|
||||
@include S(width, $arrowDims);
|
||||
@include S(height, 1px);
|
||||
position: absolute;
|
||||
transform: rotate(45deg);
|
||||
transform-origin: 50% 50%;
|
||||
}
|
||||
@include DarkThemeOverride {
|
||||
&::after {
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
&.quad-0 {
|
||||
right: 0;
|
||||
top: 0;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-end;
|
||||
|
||||
&::after {
|
||||
@include S(left, $spacing);
|
||||
@include S(bottom, $arrowDims / 2 + $spacing);
|
||||
transform: rotate(-45deg);
|
||||
}
|
||||
}
|
||||
&.quad-1 {
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
|
||||
align-items: flex-end;
|
||||
justify-content: flex-end;
|
||||
|
||||
&::after {
|
||||
@include S(left, $spacing);
|
||||
@include S(top, $arrowDims / 2 + $spacing);
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
}
|
||||
&.quad-2 {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
|
||||
align-items: flex-end;
|
||||
justify-content: flex-start;
|
||||
|
||||
&::after {
|
||||
@include S(right, $spacing);
|
||||
@include S(top, $arrowDims / 2 + $spacing);
|
||||
transform: rotate(135deg);
|
||||
}
|
||||
}
|
||||
&.quad-3 {
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start;
|
||||
|
||||
&::after {
|
||||
@include S(right, $spacing);
|
||||
@include S(bottom, $arrowDims / 2 + $spacing);
|
||||
transform: rotate(225deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#ingame_HUD_ShapeViewer {
|
||||
$dims: 170px;
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
@include S(width, $dims);
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
overflow-x: hidden;
|
||||
|
||||
&[data-layers="3"],
|
||||
&[data-layers="4"] {
|
||||
@include S(width, 2 * $dims);
|
||||
.renderArea {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
@include S(grid-row-gap, 15px);
|
||||
}
|
||||
}
|
||||
|
||||
.renderArea {
|
||||
display: grid;
|
||||
width: 100%;
|
||||
@include S(grid-row-gap, 10px);
|
||||
align-items: center;
|
||||
justify-items: center;
|
||||
}
|
||||
|
||||
.infoArea {
|
||||
align-self: flex-end;
|
||||
@include S(margin-top, 10px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
|
||||
button {
|
||||
@include S(margin, 0);
|
||||
@include PlainText;
|
||||
}
|
||||
}
|
||||
|
||||
.layer {
|
||||
position: relative;
|
||||
background: #eee;
|
||||
|
||||
@include DarkThemeOverride {
|
||||
background: rgba(0, 10, 20, 0.2);
|
||||
}
|
||||
@include S(width, 150px);
|
||||
@include S(height, 100px);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
> canvas {
|
||||
@include S(width, 50px);
|
||||
@include S(height, 50px);
|
||||
}
|
||||
|
||||
.quad {
|
||||
position: absolute;
|
||||
width: 50%;
|
||||
height: 50%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
|
||||
$arrowDims: 23px;
|
||||
$spacing: 9px;
|
||||
@include S(padding, 6px);
|
||||
|
||||
.colorLabel {
|
||||
text-transform: uppercase;
|
||||
@include SuperSmallText;
|
||||
@include S(font-size, 9px);
|
||||
}
|
||||
|
||||
.emptyLabel {
|
||||
text-transform: uppercase;
|
||||
@include SuperSmallText;
|
||||
@include S(font-size, 9px);
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: " ";
|
||||
background: rgba(0, 10, 20, 0.5);
|
||||
@include S(width, $arrowDims);
|
||||
@include S(height, 1px);
|
||||
position: absolute;
|
||||
transform: rotate(45deg);
|
||||
transform-origin: 50% 50%;
|
||||
}
|
||||
@include DarkThemeOverride {
|
||||
&::after {
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
&.quad-0 {
|
||||
right: 0;
|
||||
top: 0;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-end;
|
||||
|
||||
&::after {
|
||||
@include S(left, $spacing);
|
||||
@include S(bottom, $arrowDims / 2 + $spacing);
|
||||
transform: rotate(-45deg);
|
||||
}
|
||||
}
|
||||
&.quad-1 {
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
|
||||
align-items: flex-end;
|
||||
justify-content: flex-end;
|
||||
|
||||
&::after {
|
||||
@include S(left, $spacing);
|
||||
@include S(top, $arrowDims / 2 + $spacing);
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
}
|
||||
&.quad-2 {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
|
||||
align-items: flex-end;
|
||||
justify-content: flex-start;
|
||||
|
||||
&::after {
|
||||
@include S(right, $spacing);
|
||||
@include S(top, $arrowDims / 2 + $spacing);
|
||||
transform: rotate(135deg);
|
||||
}
|
||||
}
|
||||
&.quad-3 {
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start;
|
||||
|
||||
&::after {
|
||||
@include S(right, $spacing);
|
||||
@include S(bottom, $arrowDims / 2 + $spacing);
|
||||
transform: rotate(225deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,320 +1,320 @@
|
||||
#ingame_HUD_Shop {
|
||||
.content {
|
||||
@include S(padding-right, 10px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@include S(width, 500px);
|
||||
|
||||
.upgrade {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr auto;
|
||||
background: #eee;
|
||||
@include S(border-radius, $globalBorderRadius);
|
||||
@include S(margin-bottom, 4px);
|
||||
@include S(padding, 5px, 10px);
|
||||
@include S(grid-row-gap, 1px);
|
||||
@include S(height, 85px);
|
||||
grid-template-rows: #{D(20px)} auto;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
@include DarkThemeOverride {
|
||||
background: #55585a;
|
||||
}
|
||||
|
||||
.title {
|
||||
grid-column: 1 / 3;
|
||||
grid-row: 1 / 2;
|
||||
@include PlainText;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: row-reverse;
|
||||
justify-content: flex-end;
|
||||
|
||||
@include DarkThemeOverride {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.tier {
|
||||
@include S(margin-right, 9px);
|
||||
background: $colorGreenBright;
|
||||
@include S(border-radius, $globalBorderRadius);
|
||||
text-transform: uppercase;
|
||||
@include PlainText;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
@include S(min-width, 50px);
|
||||
@include S(padding, 0px, 5px);
|
||||
|
||||
&[data-tier="0"] {
|
||||
background-color: rgb(73, 186, 190);
|
||||
}
|
||||
&[data-tier="1"] {
|
||||
background-color: rgb(88, 110, 207);
|
||||
}
|
||||
&[data-tier="2"] {
|
||||
background-color: rgb(189, 100, 192);
|
||||
}
|
||||
&[data-tier="3"] {
|
||||
background-color: rgb(117, 192, 98);
|
||||
}
|
||||
&[data-tier="4"] {
|
||||
background-color: rgb(243, 77, 48);
|
||||
}
|
||||
&[data-tier="5"] {
|
||||
background-color: rgb(255, 209, 6);
|
||||
}
|
||||
&[data-tier="6"] {
|
||||
background-color: rgb(44, 41, 46);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
@include S(width, 40px);
|
||||
@include S(height, 40px);
|
||||
background: center center / 80% no-repeat;
|
||||
align-self: center;
|
||||
justify-self: center;
|
||||
grid-column: 1 / 2;
|
||||
grid-row: 2 / 4;
|
||||
@include S(margin-right, 30px);
|
||||
@include S(margin-left, 10px);
|
||||
opacity: 0.32;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.description {
|
||||
grid-column: 2 / 4;
|
||||
grid-row: 1 / 2;
|
||||
@include PlainText;
|
||||
color: #aaa;
|
||||
align-self: start;
|
||||
justify-self: end;
|
||||
}
|
||||
|
||||
.requirements {
|
||||
grid-column: 2 / 3;
|
||||
grid-row: 3 / 4;
|
||||
display: grid;
|
||||
grid-auto-flow: column;
|
||||
@include S(grid-gap, 9px);
|
||||
justify-content: start;
|
||||
|
||||
.requirement {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
@include S(width, 70px);
|
||||
overflow: hidden;
|
||||
|
||||
button.pin {
|
||||
@include S(width, 12px);
|
||||
@include S(height, 12px);
|
||||
background: uiResource("icons/pin.png") center center / 95% no-repeat;
|
||||
position: absolute;
|
||||
@include S(top, 2px);
|
||||
@include S(right, 2px);
|
||||
opacity: 0.6;
|
||||
cursor: pointer;
|
||||
pointer-events: all;
|
||||
@include IncreasedClickArea(5px);
|
||||
transition: opacity 0.12s ease-in-out;
|
||||
|
||||
@include DarkThemeInvert;
|
||||
|
||||
$disabledOpacity: 0.2;
|
||||
$enabledOpacity: 0.6;
|
||||
|
||||
&:hover {
|
||||
opacity: $enabledOpacity + 0.1;
|
||||
}
|
||||
|
||||
&.alreadyPinned {
|
||||
opacity: $disabledOpacity !important;
|
||||
|
||||
&:hover {
|
||||
opacity: $disabledOpacity + 0.1 !important;
|
||||
}
|
||||
}
|
||||
|
||||
&.isGoal {
|
||||
background: uiResource("icons/current_goal_marker.png") center center / 95%
|
||||
no-repeat;
|
||||
opacity: $disabledOpacity !important;
|
||||
cursor: default;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&.pinned {
|
||||
opacity: $disabledOpacity;
|
||||
@include InlineAnimation(0.3s ease-in-out) {
|
||||
0% {
|
||||
opacity: 1;
|
||||
transform: scale(0.8);
|
||||
}
|
||||
|
||||
30% {
|
||||
opacity: 1;
|
||||
transform: scale(1.2);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
opacity: $disabledOpacity + 0.1;
|
||||
}
|
||||
}
|
||||
|
||||
&.unpinned {
|
||||
opacity: $enabledOpacity;
|
||||
@include InlineAnimation(0.3s ease-in-out) {
|
||||
0% {
|
||||
opacity: 1;
|
||||
transform: scale(0.8);
|
||||
}
|
||||
|
||||
30% {
|
||||
opacity: 1;
|
||||
transform: scale(1.2);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
opacity: $enabledOpacity + 0.1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
button.showInfo {
|
||||
@include S(width, 11px);
|
||||
@include S(height, 11px);
|
||||
background: uiResource("icons/info_button.png") center center / 95% no-repeat;
|
||||
position: absolute;
|
||||
@include S(top, 17px);
|
||||
@include S(right, 2.5px);
|
||||
opacity: 0.5;
|
||||
cursor: pointer;
|
||||
pointer-events: all;
|
||||
@include IncreasedClickArea(5px);
|
||||
transition: opacity 0.12s ease-in-out;
|
||||
@include DarkThemeInvert;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
canvas {
|
||||
@include S(width, 40px);
|
||||
@include S(height, 40px);
|
||||
}
|
||||
|
||||
.amount {
|
||||
@include S(margin-top, 4px);
|
||||
z-index: 10;
|
||||
@include SuperSmallText;
|
||||
letter-spacing: 0;
|
||||
background: #e2e4e6;
|
||||
|
||||
@include S(line-height, 13px);
|
||||
@include S(border-radius, $globalBorderRadius);
|
||||
@include S(padding, 1px, 0px, 2px);
|
||||
position: relative;
|
||||
text-align: center;
|
||||
@include S(min-width, 50px);
|
||||
// @include S(max-width, 100px);
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
|
||||
@include DarkThemeOverride {
|
||||
background: #333438;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.progressBar {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
@include S(border-radius, $globalBorderRadius);
|
||||
position: absolute;
|
||||
display: inline-block;
|
||||
z-index: -1;
|
||||
transition: all 0.2s ease-in-out;
|
||||
transition-property: width, background-color;
|
||||
background: #bdbfca;
|
||||
|
||||
@include DarkThemeOverride {
|
||||
background: #8c8d96;
|
||||
}
|
||||
|
||||
&.complete {
|
||||
background-color: $colorGreenBright;
|
||||
|
||||
@include DarkThemeOverride {
|
||||
background-color: $colorGreenBright;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
button.buy {
|
||||
grid-column: 3 / 4;
|
||||
grid-row: 3 / 4;
|
||||
align-self: center;
|
||||
justify-self: end;
|
||||
// @include S(padding, 4px, 5px);
|
||||
// @include PlainText;
|
||||
background-color: $colorGreenBright;
|
||||
color: #fff;
|
||||
|
||||
transition: all 0.2s ease-in-out;
|
||||
transition-property: background-color, opacity;
|
||||
|
||||
&:not(.buyable) {
|
||||
background-color: #aaa;
|
||||
cursor: default;
|
||||
pointer-events: none;
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
&.buyable {
|
||||
@include InlineAnimation(1s ease-in-out infinite) {
|
||||
0% {
|
||||
}
|
||||
|
||||
50% {
|
||||
background-color: lighten($colorGreenBright, 10);
|
||||
}
|
||||
100% {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.maxLevel {
|
||||
button.buy {
|
||||
opacity: 0 !important;
|
||||
}
|
||||
.requirements {
|
||||
display: none;
|
||||
}
|
||||
.description {
|
||||
color: $colorGreenBright;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#ingame_HUD_Shop {
|
||||
.content {
|
||||
@include S(padding-right, 10px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@include S(width, 500px);
|
||||
|
||||
.upgrade {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr auto;
|
||||
background: #eee;
|
||||
@include S(border-radius, $globalBorderRadius);
|
||||
@include S(margin-bottom, 4px);
|
||||
@include S(padding, 5px, 10px);
|
||||
@include S(grid-row-gap, 1px);
|
||||
@include S(height, 85px);
|
||||
grid-template-rows: #{D(20px)} auto;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
@include DarkThemeOverride {
|
||||
background: $darkModeControlsBackground;
|
||||
}
|
||||
|
||||
.title {
|
||||
grid-column: 1 / 3;
|
||||
grid-row: 1 / 2;
|
||||
@include PlainText;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: row-reverse;
|
||||
justify-content: flex-end;
|
||||
|
||||
@include DarkThemeOverride {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.tier {
|
||||
@include S(margin-right, 9px);
|
||||
background: $colorGreenBright;
|
||||
@include S(border-radius, $globalBorderRadius);
|
||||
text-transform: uppercase;
|
||||
@include PlainText;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
@include S(min-width, 50px);
|
||||
@include S(padding, 0px, 5px);
|
||||
|
||||
&[data-tier="0"] {
|
||||
background-color: rgb(73, 186, 190);
|
||||
}
|
||||
&[data-tier="1"] {
|
||||
background-color: rgb(88, 110, 207);
|
||||
}
|
||||
&[data-tier="2"] {
|
||||
background-color: rgb(189, 100, 192);
|
||||
}
|
||||
&[data-tier="3"] {
|
||||
background-color: rgb(117, 192, 98);
|
||||
}
|
||||
&[data-tier="4"] {
|
||||
background-color: rgb(243, 77, 48);
|
||||
}
|
||||
&[data-tier="5"] {
|
||||
background-color: rgb(255, 209, 6);
|
||||
}
|
||||
&[data-tier="6"] {
|
||||
background-color: rgb(44, 41, 46);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
@include S(width, 40px);
|
||||
@include S(height, 40px);
|
||||
background: center center / 80% no-repeat;
|
||||
align-self: center;
|
||||
justify-self: center;
|
||||
grid-column: 1 / 2;
|
||||
grid-row: 2 / 4;
|
||||
@include S(margin-right, 30px);
|
||||
@include S(margin-left, 10px);
|
||||
opacity: 0.32;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.description {
|
||||
grid-column: 2 / 4;
|
||||
grid-row: 1 / 2;
|
||||
@include PlainText;
|
||||
color: #aaa;
|
||||
align-self: start;
|
||||
justify-self: end;
|
||||
}
|
||||
|
||||
.requirements {
|
||||
grid-column: 2 / 3;
|
||||
grid-row: 3 / 4;
|
||||
display: grid;
|
||||
grid-auto-flow: column;
|
||||
@include S(grid-gap, 9px);
|
||||
justify-content: start;
|
||||
|
||||
.requirement {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
@include S(width, 70px);
|
||||
overflow: hidden;
|
||||
|
||||
button.pin {
|
||||
@include S(width, 12px);
|
||||
@include S(height, 12px);
|
||||
background: uiResource("icons/pin.png") center center / 95% no-repeat;
|
||||
position: absolute;
|
||||
@include S(top, 2px);
|
||||
@include S(right, 2px);
|
||||
opacity: 0.6;
|
||||
cursor: pointer;
|
||||
pointer-events: all;
|
||||
@include IncreasedClickArea(5px);
|
||||
transition: opacity 0.12s ease-in-out;
|
||||
|
||||
@include DarkThemeInvert;
|
||||
|
||||
$disabledOpacity: 0.2;
|
||||
$enabledOpacity: 0.6;
|
||||
|
||||
&:hover {
|
||||
opacity: $enabledOpacity + 0.1;
|
||||
}
|
||||
|
||||
&.alreadyPinned {
|
||||
opacity: $disabledOpacity !important;
|
||||
|
||||
&:hover {
|
||||
opacity: $disabledOpacity + 0.1 !important;
|
||||
}
|
||||
}
|
||||
|
||||
&.isGoal {
|
||||
background: uiResource("icons/current_goal_marker.png") center center / 95%
|
||||
no-repeat;
|
||||
opacity: $disabledOpacity !important;
|
||||
cursor: default;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&.pinned {
|
||||
opacity: $disabledOpacity;
|
||||
@include InlineAnimation(0.3s ease-in-out) {
|
||||
0% {
|
||||
opacity: 1;
|
||||
transform: scale(0.8);
|
||||
}
|
||||
|
||||
30% {
|
||||
opacity: 1;
|
||||
transform: scale(1.2);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
opacity: $disabledOpacity + 0.1;
|
||||
}
|
||||
}
|
||||
|
||||
&.unpinned {
|
||||
opacity: $enabledOpacity;
|
||||
@include InlineAnimation(0.3s ease-in-out) {
|
||||
0% {
|
||||
opacity: 1;
|
||||
transform: scale(0.8);
|
||||
}
|
||||
|
||||
30% {
|
||||
opacity: 1;
|
||||
transform: scale(1.2);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
opacity: $enabledOpacity + 0.1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
button.showInfo {
|
||||
@include S(width, 11px);
|
||||
@include S(height, 11px);
|
||||
background: uiResource("icons/info_button.png") center center / 95% no-repeat;
|
||||
position: absolute;
|
||||
@include S(top, 17px);
|
||||
@include S(right, 2.5px);
|
||||
opacity: 0.5;
|
||||
cursor: pointer;
|
||||
pointer-events: all;
|
||||
@include IncreasedClickArea(5px);
|
||||
transition: opacity 0.12s ease-in-out;
|
||||
@include DarkThemeInvert;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
canvas {
|
||||
@include S(width, 40px);
|
||||
@include S(height, 40px);
|
||||
}
|
||||
|
||||
.amount {
|
||||
@include S(margin-top, 4px);
|
||||
z-index: 10;
|
||||
@include SuperSmallText;
|
||||
letter-spacing: 0;
|
||||
background: #e2e4e6;
|
||||
|
||||
@include S(line-height, 13px);
|
||||
@include S(border-radius, $globalBorderRadius);
|
||||
@include S(padding, 1px, 0px, 2px);
|
||||
position: relative;
|
||||
text-align: center;
|
||||
@include S(min-width, 50px);
|
||||
// @include S(max-width, 100px);
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
|
||||
@include DarkThemeOverride {
|
||||
background: #333438;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.progressBar {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
@include S(border-radius, $globalBorderRadius);
|
||||
position: absolute;
|
||||
display: inline-block;
|
||||
z-index: -1;
|
||||
transition: all 0.2s ease-in-out;
|
||||
transition-property: width, background-color;
|
||||
background: #bdbfca;
|
||||
|
||||
@include DarkThemeOverride {
|
||||
background: #8c8d96;
|
||||
}
|
||||
|
||||
&.complete {
|
||||
background-color: $colorGreenBright;
|
||||
|
||||
@include DarkThemeOverride {
|
||||
background-color: $colorGreenBright;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
button.buy {
|
||||
grid-column: 3 / 4;
|
||||
grid-row: 3 / 4;
|
||||
align-self: center;
|
||||
justify-self: end;
|
||||
// @include S(padding, 4px, 5px);
|
||||
// @include PlainText;
|
||||
background-color: $colorGreenBright;
|
||||
color: #fff;
|
||||
|
||||
transition: all 0.2s ease-in-out;
|
||||
transition-property: background-color, opacity;
|
||||
|
||||
&:not(.buyable) {
|
||||
background-color: #aaa;
|
||||
cursor: default;
|
||||
pointer-events: none;
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
&.buyable {
|
||||
@include InlineAnimation(1s ease-in-out infinite) {
|
||||
0% {
|
||||
}
|
||||
|
||||
50% {
|
||||
background-color: lighten($colorGreenBright, 10);
|
||||
}
|
||||
100% {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.maxLevel {
|
||||
button.buy {
|
||||
opacity: 0 !important;
|
||||
}
|
||||
.requirements {
|
||||
display: none;
|
||||
}
|
||||
.description {
|
||||
color: $colorGreenBright;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -33,22 +33,47 @@
|
||||
|
||||
&.displayIcons,
|
||||
&.displayDetailed,
|
||||
&.displaySorted,
|
||||
&.displayIterateUnit {
|
||||
background: transparent center center / #{D(15px)} no-repeat;
|
||||
}
|
||||
|
||||
&.displayDetailed {
|
||||
background-image: uiResource("icons/display_list.png");
|
||||
}
|
||||
|
||||
&.displayIcons {
|
||||
background-image: uiResource("icons/display_icons.png");
|
||||
background-size: #{D(11.5px)};
|
||||
}
|
||||
|
||||
&.displayDetailed {
|
||||
@include S(border-top-left-radius, $globalBorderRadius);
|
||||
@include S(border-bottom-left-radius, $globalBorderRadius);
|
||||
}
|
||||
|
||||
&.displaySorted {
|
||||
background: uiResource("icons/display_list.png") center center / #{D(15px)} no-repeat;
|
||||
&.displayIcons {
|
||||
background-image: uiResource("icons/display_icons.png");
|
||||
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-image: uiResource("icons/display_sorted.png");
|
||||
background-size: #{D(11.5px)};
|
||||
margin-right: 5px;
|
||||
@include S(border-top-right-radius, $globalBorderRadius);
|
||||
@include S(border-bottom-right-radius, $globalBorderRadius);
|
||||
|
||||
@include S(padding, 1px, 0);
|
||||
}
|
||||
|
||||
&.displayIterateUnit {
|
||||
background-image: uiResource("icons/toggle_unit.png");
|
||||
opacity: 0.8;
|
||||
@include S(padding, 1px, 0);
|
||||
}
|
||||
|
||||
background-color: #44484a !important;
|
||||
transition: opacity 0.2s ease-in-out;
|
||||
|
||||
@include DarkThemeOverride {
|
||||
background-color: lighten($darkModeControlsBackground, 10) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.filtersDataSource,
|
||||
@ -110,10 +135,10 @@
|
||||
}
|
||||
|
||||
@include DarkThemeOverride {
|
||||
background: #222428;
|
||||
background: $darkModeControlsBackground;
|
||||
|
||||
&.pinned {
|
||||
background: darken(#222428, 10);
|
||||
background: mix($darkModeControlsBackground, $colorBlueBright, 90%);
|
||||
}
|
||||
}
|
||||
|
||||
@ -159,6 +184,11 @@
|
||||
grid-column: 1 / 2;
|
||||
grid-row: 2 / 3;
|
||||
justify-self: end;
|
||||
color: #55595a;
|
||||
|
||||
@include DarkThemeOverride {
|
||||
color: #aaa;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -176,6 +206,10 @@
|
||||
align-self: center;
|
||||
text-align: right;
|
||||
color: #55595a;
|
||||
|
||||
@include DarkThemeOverride {
|
||||
color: #aaa;
|
||||
}
|
||||
}
|
||||
|
||||
canvas.graph {
|
||||
|
@ -1,37 +1,52 @@
|
||||
#state_InGameState {
|
||||
.gameLoadingOverlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 9999;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
pointer-events: all;
|
||||
display: flex;
|
||||
background: $mainBgColor;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#ingame_Canvas {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
#ingame_HUD_ModalDialogs {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
@include DarkThemeOverride {
|
||||
.gameLoadingOverlay {
|
||||
background: $darkModeGameBackground;
|
||||
}
|
||||
}
|
||||
}
|
||||
#state_InGameState {
|
||||
.gameLoadingOverlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 9999;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
pointer-events: all;
|
||||
display: flex;
|
||||
background: $mainBgColor;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.prefab_GameHint {
|
||||
position: absolute;
|
||||
@include S(bottom, 40px);
|
||||
@include S(left, 20px);
|
||||
@include S(right, 20px);
|
||||
@include PlainText;
|
||||
text-align: center;
|
||||
|
||||
color: #666;
|
||||
|
||||
@include DarkThemeOverride() {
|
||||
color: lighten($darkModeGameBackground, 50);
|
||||
}
|
||||
}
|
||||
|
||||
#ingame_Canvas {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
#ingame_HUD_ModalDialogs {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
@include DarkThemeOverride {
|
||||
.gameLoadingOverlay {
|
||||
background: $darkModeGameBackground;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,71 +1,71 @@
|
||||
#state_KeybindingsState {
|
||||
.content {
|
||||
.topEntries {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto;
|
||||
@include S(grid-gap, 5px);
|
||||
@include S(margin-bottom, 10px);
|
||||
}
|
||||
|
||||
.hint {
|
||||
display: block;
|
||||
background: #eee;
|
||||
@include S(padding, 4px);
|
||||
@include PlainText;
|
||||
}
|
||||
|
||||
.category {
|
||||
.entry {
|
||||
display: grid;
|
||||
@include S(margin-top, 2px);
|
||||
@include S(padding-top, 2px);
|
||||
@include S(grid-gap, 4px);
|
||||
grid-template-columns: 1fr #{D(100px)} auto auto;
|
||||
border-bottom: #{D(1px)} dotted #eee;
|
||||
color: #888c8f;
|
||||
.mapping {
|
||||
color: $colorBlueBright;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
button {
|
||||
@include S(height, 15px);
|
||||
@include S(width, 15px);
|
||||
@include IncreasedClickArea(0px);
|
||||
background: transparent center center / 40% no-repeat;
|
||||
opacity: 0.9;
|
||||
&.editKeybinding {
|
||||
background-image: uiResource("icons/edit_key.png");
|
||||
}
|
||||
|
||||
&.resetKeybinding {
|
||||
background-image: uiResource("icons/reset_key.png");
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
pointer-events: none;
|
||||
cursor: default;
|
||||
opacity: 0.1 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include DarkThemeOverride {
|
||||
.content {
|
||||
.hint {
|
||||
background: #3b3d40;
|
||||
}
|
||||
|
||||
.category .entry {
|
||||
color: #c0c4c8;
|
||||
border-bottom-color: #888;
|
||||
|
||||
button {
|
||||
filter: invert(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#state_KeybindingsState {
|
||||
.content {
|
||||
.topEntries {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto;
|
||||
@include S(grid-gap, 5px);
|
||||
@include S(margin-bottom, 10px);
|
||||
}
|
||||
|
||||
.hint {
|
||||
display: block;
|
||||
background: #eee;
|
||||
@include S(padding, 4px);
|
||||
@include PlainText;
|
||||
}
|
||||
|
||||
.category {
|
||||
.entry {
|
||||
display: grid;
|
||||
@include S(margin-top, 2px);
|
||||
@include S(padding-top, 2px);
|
||||
@include S(grid-gap, 4px);
|
||||
grid-template-columns: 1fr #{D(100px)} auto auto;
|
||||
border-bottom: #{D(1px)} dotted #eee;
|
||||
color: #888c8f;
|
||||
.mapping {
|
||||
color: $colorBlueBright;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
button {
|
||||
@include S(height, 15px);
|
||||
@include S(width, 15px);
|
||||
@include IncreasedClickArea(0px);
|
||||
background: transparent center center / 40% no-repeat;
|
||||
opacity: 0.9;
|
||||
&.editKeybinding {
|
||||
background-image: uiResource("icons/edit_key.png");
|
||||
}
|
||||
|
||||
&.resetKeybinding {
|
||||
background-image: uiResource("icons/reset_key.png");
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
pointer-events: none;
|
||||
cursor: default;
|
||||
opacity: 0.1 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include DarkThemeOverride {
|
||||
.content {
|
||||
.hint {
|
||||
background: darken($darkModeControlsBackground, 4);
|
||||
}
|
||||
|
||||
.category .entry {
|
||||
color: #c0c4c8;
|
||||
border-bottom-color: #888;
|
||||
|
||||
button {
|
||||
filter: invert(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,13 +25,15 @@
|
||||
background: uiResource("icons/main_menu_settings.png") center center / contain no-repeat;
|
||||
transition: opacity 0.12s ease-in-out;
|
||||
@include IncreasedClickArea(2px);
|
||||
opacity: 0.7;
|
||||
&:hover {
|
||||
opacity: 0.9;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.exitAppButton {
|
||||
background-image: uiResource("icons/main_menu_exit.png");
|
||||
background-size: 90%;
|
||||
}
|
||||
|
||||
.languageChoose {
|
||||
@ -40,6 +42,7 @@
|
||||
background-color: #fff;
|
||||
@include S(border-width, 2px);
|
||||
background-size: cover;
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
@ -57,7 +60,7 @@
|
||||
transform: translate(50%, 50%);
|
||||
filter: blur(D(3px));
|
||||
|
||||
$opacity: 0.2;
|
||||
$opacity: 0.07;
|
||||
&.loaded {
|
||||
display: block;
|
||||
opacity: $opacity;
|
||||
@ -121,10 +124,13 @@
|
||||
}
|
||||
|
||||
.steamLink {
|
||||
align-self: center;
|
||||
justify-self: center;
|
||||
width: 100%;
|
||||
@include S(height, 40px);
|
||||
@include S(width, 180px);
|
||||
|
||||
background: uiResource("get_on_steam.png") center center / contain no-repeat;
|
||||
background: #171a23 uiResource("get_on_steam.png") center center / contain no-repeat;
|
||||
overflow: hidden;
|
||||
display: block;
|
||||
text-indent: -999em;
|
||||
@ -134,8 +140,11 @@
|
||||
transition: all 0.12s ease-in;
|
||||
transition-property: opacity, transform;
|
||||
transform: skewX(-0.5deg);
|
||||
|
||||
@include S(border-radius, $globalBorderRadius);
|
||||
|
||||
&:hover {
|
||||
transform: skewX(-1deg) scale(1.02);
|
||||
transform: scale(1.02);
|
||||
opacity: 0.9;
|
||||
}
|
||||
}
|
||||
@ -332,25 +341,39 @@
|
||||
button.downloadGame {
|
||||
grid-column: 3 / 4;
|
||||
grid-row: 1 / 2;
|
||||
background-color: $colorBlueBright;
|
||||
background-color: transparent;
|
||||
background-image: uiResource("icons/download.png");
|
||||
@include S(width, 15px);
|
||||
@include IncreasedClickArea(0px);
|
||||
@include S(height, 15px);
|
||||
background-size: 60%;
|
||||
background-size: 80%;
|
||||
align-self: start;
|
||||
opacity: 0.4;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
@include DarkThemeInvert;
|
||||
}
|
||||
|
||||
button.deleteGame {
|
||||
grid-column: 3 / 4;
|
||||
grid-row: 2 / 3;
|
||||
background-color: $colorRedBright;
|
||||
background-color: transparent;
|
||||
@include IncreasedClickArea(0px);
|
||||
background-image: uiResource("icons/delete.png");
|
||||
@include S(width, 15px);
|
||||
@include S(height, 15px);
|
||||
align-self: end;
|
||||
background-size: 60%;
|
||||
background-size: 80%;
|
||||
opacity: 0.4;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
@include DarkThemeInvert;
|
||||
}
|
||||
|
||||
button.renameGame {
|
||||
@ -363,11 +386,11 @@
|
||||
justify-self: center;
|
||||
|
||||
background-size: 90%;
|
||||
opacity: 0.25;
|
||||
opacity: 0.4;
|
||||
@include S(margin-left, 4px);
|
||||
|
||||
&:hover {
|
||||
opacity: 0.35;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
@include DarkThemeInvert;
|
||||
@ -379,6 +402,11 @@
|
||||
margin: 0;
|
||||
@include S(width, 32px);
|
||||
height: 100%;
|
||||
@include S(margin-left, 4px);
|
||||
|
||||
@include DarkThemeOverride {
|
||||
background-color: lighten($darkModeControlsBackground, 10);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -407,13 +435,17 @@
|
||||
|
||||
@include S(padding, 15px);
|
||||
|
||||
$linkBg: #fdfdff;
|
||||
$linkBgHover: darken($linkBg, 2);
|
||||
$linkColor: #55586a;
|
||||
|
||||
> .boxLink {
|
||||
display: grid;
|
||||
align-items: center;
|
||||
grid-template-columns: 1fr auto;
|
||||
|
||||
justify-content: center;
|
||||
background: #fdfdfd uiResource("icons/link.png") top D(3px) right D(3px) / D(9px) no-repeat;
|
||||
background: $linkBg uiResource("icons/link.png") top D(3px) right D(3px) / D(9px) no-repeat;
|
||||
@include S(padding, 5px);
|
||||
@include S(padding-left, 10px);
|
||||
@include S(border-radius, $globalBorderRadius);
|
||||
@ -422,7 +454,7 @@
|
||||
font-weight: bold;
|
||||
box-sizing: border-box;
|
||||
text-transform: uppercase;
|
||||
color: #616266;
|
||||
color: $linkColor;
|
||||
|
||||
transition: background-color 0.12s ease-in-out;
|
||||
pointer-events: all;
|
||||
@ -431,7 +463,7 @@
|
||||
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background-color: #f0f6ff;
|
||||
background-color: $linkBgHover;
|
||||
}
|
||||
|
||||
.thirdpartyLogo {
|
||||
@ -458,12 +490,12 @@
|
||||
@include S(height, 60px);
|
||||
|
||||
> a {
|
||||
color: #616266;
|
||||
background: #fdfdfd;
|
||||
color: $linkColor;
|
||||
background: $linkBg;
|
||||
height: 100%;
|
||||
|
||||
&:hover {
|
||||
background-color: #f0f6ff;
|
||||
background-color: $linkBgHover;
|
||||
}
|
||||
@include SuperSmallText;
|
||||
text-transform: uppercase;
|
||||
@ -499,19 +531,11 @@
|
||||
@include DarkThemeOverride {
|
||||
background: $darkModeGameBackground center center / cover !important;
|
||||
|
||||
.topButtons {
|
||||
filter: invert(1);
|
||||
|
||||
.languageChoose {
|
||||
filter: invert(1);
|
||||
}
|
||||
}
|
||||
|
||||
.mainContainer {
|
||||
background: darken($darkModeGameBackground, 10);
|
||||
background: $darkModeControlsBackground;
|
||||
|
||||
.savegames .savegame {
|
||||
background: darken($darkModeGameBackground, 15);
|
||||
background: darken($darkModeControlsBackground, 5);
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
@ -519,11 +543,11 @@
|
||||
.footer {
|
||||
> a,
|
||||
.sidelinks > a {
|
||||
background-color: darken($darkModeGameBackground, 10);
|
||||
background-color: $darkModeControlsBackground;
|
||||
color: #eee;
|
||||
|
||||
&:hover {
|
||||
background-color: darken($darkModeGameBackground, 8);
|
||||
background-color: darken($darkModeControlsBackground, 5);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,145 +1,145 @@
|
||||
#state_PreloadState {
|
||||
&.failure {
|
||||
.loadingImage,
|
||||
.loadingStatus {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.changelogDialogEntry {
|
||||
margin-top: 10px;
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
text-align: left;
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
background: #eef1f4;
|
||||
|
||||
@include DarkThemeOverride {
|
||||
background: #424242;
|
||||
}
|
||||
|
||||
.version {
|
||||
@include Heading;
|
||||
}
|
||||
.date {
|
||||
@include PlainText;
|
||||
&::before {
|
||||
content: " | ";
|
||||
}
|
||||
color: #aaabaf;
|
||||
}
|
||||
|
||||
.changes {
|
||||
@include PlainText;
|
||||
@include S(padding-left, 15px);
|
||||
strong {
|
||||
background: $colorBlueBright;
|
||||
color: #fff;
|
||||
text-transform: uppercase;
|
||||
@include S(padding, 1px, 2px);
|
||||
@include S(margin-right, 3px);
|
||||
}
|
||||
a {
|
||||
color: $colorBlueBright;
|
||||
}
|
||||
li {
|
||||
@include SuperSmallText;
|
||||
@include S(margin-bottom, 10px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.failureBox {
|
||||
.logo {
|
||||
img {
|
||||
@include S(width, 240px);
|
||||
}
|
||||
|
||||
@include S(margin-bottom, 30px);
|
||||
}
|
||||
|
||||
@include InlineAnimation(0.3s ease-in-out) {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.failureInner {
|
||||
// background: darken($mainBgColor, 6);
|
||||
@include S(max-width, 350px);
|
||||
margin: 0 20px;
|
||||
text-align: left;
|
||||
|
||||
@include BoxShadow3D(#fff);
|
||||
@include S(padding, 15px);
|
||||
@include S(border-radius, $globalBorderRadius);
|
||||
@include DropShadow;
|
||||
|
||||
.errorHeader {
|
||||
color: #ef5072;
|
||||
}
|
||||
|
||||
.errorMessage {
|
||||
@include PlainText;
|
||||
display: block;
|
||||
color: #666;
|
||||
text-align: left;
|
||||
@include BreakText;
|
||||
hyphens: auto;
|
||||
// border: dotted #666;
|
||||
// @include S(border-width, 1px, 0);
|
||||
@include S(padding, 10px, 0);
|
||||
@include S(margin-top, 10px);
|
||||
}
|
||||
|
||||
.supportHelp {
|
||||
@include S(margin-top, 10px);
|
||||
@include PlainText;
|
||||
|
||||
.email {
|
||||
color: $themeColor;
|
||||
cursor: pointer;
|
||||
pointer-events: all;
|
||||
}
|
||||
}
|
||||
|
||||
.lower {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@include S(margin-top, 16px);
|
||||
|
||||
i {
|
||||
flex-grow: 1;
|
||||
text-align: right;
|
||||
color: #777;
|
||||
@include PlainText;
|
||||
}
|
||||
|
||||
button.resetApp {
|
||||
@include Button3D($colorRedBright);
|
||||
@include PlainText;
|
||||
@include S(padding, 5px, 8px, 4px);
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Animations */
|
||||
.status {
|
||||
transform: scale(0.7) $hardwareAcc;
|
||||
opacity: 0;
|
||||
@include StateAnim(transform, opacity);
|
||||
}
|
||||
|
||||
&.arrived {
|
||||
.status {
|
||||
opacity: 1;
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
#state_PreloadState {
|
||||
&.failure {
|
||||
.loadingImage,
|
||||
.loadingStatus {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.changelogDialogEntry {
|
||||
margin-top: 10px;
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
text-align: left;
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
background: #eef1f4;
|
||||
|
||||
@include DarkThemeOverride {
|
||||
background: #424242;
|
||||
}
|
||||
|
||||
.version {
|
||||
@include Heading;
|
||||
}
|
||||
.date {
|
||||
@include PlainText;
|
||||
&::before {
|
||||
content: " | ";
|
||||
}
|
||||
color: #aaabaf;
|
||||
}
|
||||
|
||||
.changes {
|
||||
@include PlainText;
|
||||
@include S(padding-left, 15px);
|
||||
strong {
|
||||
background: $colorBlueBright;
|
||||
color: #fff;
|
||||
text-transform: uppercase;
|
||||
@include S(padding, 1px, 2px);
|
||||
@include S(margin-right, 3px);
|
||||
}
|
||||
a {
|
||||
color: $colorBlueBright;
|
||||
}
|
||||
li {
|
||||
@include SuperSmallText;
|
||||
@include S(margin-bottom, 10px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.failureBox {
|
||||
.logo {
|
||||
img {
|
||||
@include S(width, 240px);
|
||||
}
|
||||
|
||||
@include S(margin-bottom, 30px);
|
||||
}
|
||||
|
||||
@include InlineAnimation(0.3s ease-in-out) {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.failureInner {
|
||||
// background: darken($mainBgColor, 6);
|
||||
@include S(max-width, 350px);
|
||||
margin: 0 20px;
|
||||
text-align: left;
|
||||
|
||||
@include BoxShadow3D(#fff);
|
||||
@include S(padding, 15px);
|
||||
@include S(border-radius, $globalBorderRadius);
|
||||
@include DropShadow;
|
||||
|
||||
.errorHeader {
|
||||
color: #ef5072;
|
||||
}
|
||||
|
||||
.errorMessage {
|
||||
@include PlainText;
|
||||
display: block;
|
||||
color: #666;
|
||||
text-align: left;
|
||||
@include BreakText;
|
||||
hyphens: auto;
|
||||
// border: dotted #666;
|
||||
// @include S(border-width, 1px, 0);
|
||||
@include S(padding, 10px, 0);
|
||||
@include S(margin-top, 10px);
|
||||
}
|
||||
|
||||
.supportHelp {
|
||||
@include S(margin-top, 10px);
|
||||
@include PlainText;
|
||||
|
||||
.email {
|
||||
color: $themeColor;
|
||||
cursor: pointer;
|
||||
pointer-events: all;
|
||||
}
|
||||
}
|
||||
|
||||
.lower {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@include S(margin-top, 16px);
|
||||
|
||||
i {
|
||||
flex-grow: 1;
|
||||
text-align: right;
|
||||
color: #777;
|
||||
@include PlainText;
|
||||
}
|
||||
|
||||
button.resetApp {
|
||||
@include Button3D($colorRedBright);
|
||||
@include PlainText;
|
||||
@include S(padding, 5px, 8px, 4px);
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Animations */
|
||||
.status {
|
||||
transform: scale(0.7) $hardwareAcc;
|
||||
opacity: 0;
|
||||
@include StateAnim(transform, opacity);
|
||||
}
|
||||
|
||||
&.arrived {
|
||||
.status {
|
||||
opacity: 1;
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,95 @@
|
||||
#state_SettingsState {
|
||||
$colorCategoryButton: #eee;
|
||||
$colorCategoryButtonSelected: #5f748b;
|
||||
$colorCategoryButton: #eeeff5;
|
||||
$colorCategoryButtonSelected: $colorBlueBright;
|
||||
|
||||
$layoutBreak: 1000px;
|
||||
|
||||
.container .content {
|
||||
display: flex;
|
||||
overflow-y: scroll;
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
@include S(grid-gap, 10px);
|
||||
|
||||
@include StyleBelowWidth($layoutBreak) {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
display: grid;
|
||||
@include S(min-width, 210px);
|
||||
@include S(max-width, 320px);
|
||||
@include S(grid-gap, 3px);
|
||||
grid-template-rows: auto auto auto auto auto 1fr;
|
||||
|
||||
@include StyleBelowWidth($layoutBreak) {
|
||||
grid-template-rows: 1fr 1fr;
|
||||
grid-template-columns: auto auto;
|
||||
max-width: unset !important;
|
||||
}
|
||||
|
||||
button {
|
||||
text-align: left;
|
||||
&::after {
|
||||
content: unset;
|
||||
}
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
|
||||
@include StyleBelowWidth($layoutBreak) {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.other {
|
||||
@include S(margin-top, 10px);
|
||||
align-self: end;
|
||||
|
||||
@include StyleBelowWidth($layoutBreak) {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
button.categoryButton,
|
||||
button.about {
|
||||
background-color: $colorCategoryButton;
|
||||
color: #777a7f;
|
||||
|
||||
&.active {
|
||||
background-color: $colorCategoryButtonSelected;
|
||||
color: #fff;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&.pressed {
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.versionbar {
|
||||
@include S(margin-top, 10px);
|
||||
|
||||
@include StyleBelowWidth($layoutBreak) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@include SuperSmallText;
|
||||
display: grid;
|
||||
align-items: center;
|
||||
grid-template-columns: 1fr auto;
|
||||
.buildVersion {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
color: #aaadaf;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.categoryContainer {
|
||||
width: 100%;
|
||||
overflow-y: scroll;
|
||||
pointer-events: all;
|
||||
@include S(padding-right, 10px);
|
||||
|
||||
.category {
|
||||
display: none;
|
||||
@ -88,65 +170,6 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@include S(min-width, 210px);
|
||||
@include S(max-width, 320px);
|
||||
width: 30%;
|
||||
height: 100%;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
@include S(margin-left, 20px);
|
||||
@include S(margin-right, 32px);
|
||||
|
||||
.other {
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
button {
|
||||
@include S(margin-top, 4px);
|
||||
width: calc(100% - #{D(20px)});
|
||||
text-align: start;
|
||||
|
||||
&::after {
|
||||
content: unset;
|
||||
}
|
||||
}
|
||||
|
||||
button.categoryButton,
|
||||
button.about {
|
||||
background-color: $colorCategoryButton;
|
||||
color: #777a7f;
|
||||
|
||||
&.active {
|
||||
background-color: $colorCategoryButtonSelected;
|
||||
color: #fff;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&.pressed {
|
||||
transform: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.versionbar {
|
||||
@include S(margin-top, 20px);
|
||||
@include SuperSmallText;
|
||||
display: grid;
|
||||
align-items: center;
|
||||
grid-template-columns: 1fr auto;
|
||||
.buildVersion {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
color: #aaadaf;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include DarkThemeOverride {
|
||||
@ -154,10 +177,12 @@
|
||||
.sidebar {
|
||||
button.categoryButton,
|
||||
button.about {
|
||||
background-color: #3f3f47;
|
||||
color: #ccc;
|
||||
background-color: darken($darkModeControlsBackground, 5);
|
||||
|
||||
&.active {
|
||||
background-color: $colorBlueBright;
|
||||
color: #fff;
|
||||
background-color: $colorCategoryButtonSelected;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -169,8 +194,13 @@
|
||||
|
||||
.value.enum {
|
||||
// dirty but works
|
||||
filter: invert(0.78) sepia(40%) hue-rotate(190deg);
|
||||
color: #222;
|
||||
// color: #222;
|
||||
background-color: $darkModeControlsBackground;
|
||||
background-image: uiResource("icons/enum_selector_white.png");
|
||||
color: #ddd;
|
||||
&:hover {
|
||||
background-color: darken($darkModeControlsBackground, 2);
|
||||
}
|
||||
}
|
||||
|
||||
.value.checkbox {
|
||||
|
@ -1,81 +1,81 @@
|
||||
.gameState.textualState {
|
||||
display: grid;
|
||||
grid-template-rows: auto 1fr;
|
||||
box-sizing: border-box;
|
||||
@include S(padding, 32px);
|
||||
height: 100vh;
|
||||
|
||||
.headerBar {
|
||||
display: flex;
|
||||
|
||||
h1 {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
align-items: center;
|
||||
pointer-events: all;
|
||||
cursor: pointer;
|
||||
@include SuperHeading;
|
||||
text-transform: uppercase;
|
||||
color: #333438;
|
||||
position: relative;
|
||||
@include IncreasedClickArea(10px);
|
||||
}
|
||||
|
||||
.backButton {
|
||||
@include S(width, 30px);
|
||||
@include S(height, 30px);
|
||||
@include S(margin-right, 10px);
|
||||
@include S(margin-left, -5px);
|
||||
background: uiResource("icons/state_back_button.png") center center / 70% no-repeat;
|
||||
}
|
||||
|
||||
@include S(margin-bottom, 20px);
|
||||
}
|
||||
|
||||
> .container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
overflow-y: auto;
|
||||
|
||||
> .content {
|
||||
width: 100%;
|
||||
background: #fff;
|
||||
@include S(border-radius, $globalBorderRadius);
|
||||
@include S(padding, 10px);
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
box-sizing: border-box;
|
||||
pointer-events: all;
|
||||
|
||||
a {
|
||||
color: $colorBlueBright;
|
||||
}
|
||||
|
||||
.categoryLabel {
|
||||
display: block;
|
||||
text-transform: uppercase;
|
||||
@include S(margin-top, 15px);
|
||||
@include S(margin-bottom, 15px);
|
||||
@include Heading;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include DarkThemeOverride {
|
||||
.headerBar {
|
||||
h1 {
|
||||
color: #e2e0db;
|
||||
}
|
||||
|
||||
.backButton {
|
||||
filter: invert(1);
|
||||
}
|
||||
}
|
||||
|
||||
> .container > .content {
|
||||
background: darken($darkModeGameBackground, 3);
|
||||
color: #eee;
|
||||
}
|
||||
}
|
||||
}
|
||||
.gameState.textualState {
|
||||
display: grid;
|
||||
grid-template-rows: auto 1fr;
|
||||
box-sizing: border-box;
|
||||
@include S(padding, 32px);
|
||||
height: 100vh;
|
||||
|
||||
.headerBar {
|
||||
display: flex;
|
||||
|
||||
h1 {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
align-items: center;
|
||||
pointer-events: all;
|
||||
cursor: pointer;
|
||||
@include SuperHeading;
|
||||
text-transform: uppercase;
|
||||
color: #333438;
|
||||
position: relative;
|
||||
@include IncreasedClickArea(10px);
|
||||
}
|
||||
|
||||
.backButton {
|
||||
@include S(width, 30px);
|
||||
@include S(height, 30px);
|
||||
@include S(margin-right, 10px);
|
||||
@include S(margin-left, -5px);
|
||||
background: uiResource("icons/state_back_button.png") center center / 70% no-repeat;
|
||||
}
|
||||
|
||||
@include S(margin-bottom, 20px);
|
||||
}
|
||||
|
||||
> .container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
overflow-y: auto;
|
||||
|
||||
> .content {
|
||||
width: 100%;
|
||||
background: #fff;
|
||||
@include S(border-radius, $globalBorderRadius);
|
||||
@include S(padding, 10px);
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
box-sizing: border-box;
|
||||
pointer-events: all;
|
||||
|
||||
a {
|
||||
color: $colorBlueBright;
|
||||
}
|
||||
|
||||
.categoryLabel {
|
||||
display: block;
|
||||
text-transform: uppercase;
|
||||
@include S(margin-top, 15px);
|
||||
@include S(margin-bottom, 15px);
|
||||
@include Heading;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include DarkThemeOverride {
|
||||
.headerBar {
|
||||
h1 {
|
||||
color: #e2e0db;
|
||||
}
|
||||
|
||||
.backButton {
|
||||
filter: invert(1);
|
||||
}
|
||||
}
|
||||
|
||||
> .container > .content {
|
||||
background: $darkModeControlsBackground;
|
||||
color: #eee;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,198 +1,199 @@
|
||||
$globalBorderRadius: 2px;
|
||||
|
||||
// When to reduce control elements size for small devices
|
||||
$layoutExpandMinWidth: 340px;
|
||||
|
||||
// Font sizes and line heights
|
||||
$superHeadingFontSize: 25px;
|
||||
$superHeadingLineHeight: 24px;
|
||||
|
||||
$breakTooltipShowStatsPx: 1023px;
|
||||
|
||||
$headingFontSize: 19px;
|
||||
$headingLineHeight: 21px;
|
||||
|
||||
$textFontSize: 16px;
|
||||
$textLineHeight: 21px;
|
||||
|
||||
$plainTextFontSize: 13px;
|
||||
$plainTextLineHeight: 17px;
|
||||
|
||||
$supersmallTextFontSize: 10px;
|
||||
$supersmallTextLineHeight: 13px;
|
||||
$buttonFontSize: 14px;
|
||||
$buttonLineHeight: 18px;
|
||||
|
||||
// Main background color
|
||||
$mainBgColor: #dee1ea;
|
||||
|
||||
// Accent colors
|
||||
|
||||
$accentColorBright: #e1e4ed;
|
||||
$accentColorDark: #7d808a;
|
||||
$colorGreenBright: #66bb6a;
|
||||
$colorBlueBright: rgb(74, 163, 223);
|
||||
$colorRedBright: #ef5072;
|
||||
$themeColor: #393747;
|
||||
$ingameHudBg: rgba(#333438, 0.9);
|
||||
|
||||
$text3dColor: #f4ffff;
|
||||
|
||||
$darkModeGameBackground: #5c606c;
|
||||
|
||||
// Dialog properties
|
||||
$modalDialogBg: rgba(160, 165, 180, 0.8);
|
||||
$dialogBgColor: lighten($mainBgColor, 10);
|
||||
|
||||
$lightFontWeight: normal;
|
||||
$boldFontWeight: 600;
|
||||
|
||||
$iconSizeSmall: 30px;
|
||||
$iconSizeMedium: 40px;
|
||||
$iconSizeLarge: 60px;
|
||||
|
||||
// Poppins 500
|
||||
// Rubik 400
|
||||
// Cairo 400
|
||||
// Viga 400
|
||||
// Sniglet 400
|
||||
|
||||
$mainFont: "GameFont", sans-serif;
|
||||
// $mainFont: "DK Canoodle";
|
||||
// $mainFont: "MADE Florence Sans";
|
||||
$numberFont: $mainFont;
|
||||
$textFont: $mainFont;
|
||||
|
||||
$mainFontWeight: 400;
|
||||
$mainFontSpacing: 0.04em;
|
||||
$mainFontScale: 1;
|
||||
|
||||
@mixin DebugText($color) {
|
||||
// font-size: 3px;
|
||||
// &,
|
||||
// * {
|
||||
// color: $color !important;
|
||||
// }
|
||||
}
|
||||
|
||||
@mixin SuperSmallText {
|
||||
@include ScaleFont($supersmallTextFontSize, $supersmallTextLineHeight);
|
||||
font-weight: $mainFontWeight;
|
||||
font-family: $mainFont;
|
||||
letter-spacing: $mainFontSpacing;
|
||||
@include DebugText(green);
|
||||
}
|
||||
|
||||
@mixin PlainText {
|
||||
@include ScaleFont($plainTextFontSize, $plainTextLineHeight);
|
||||
font-weight: $mainFontWeight;
|
||||
font-family: $mainFont;
|
||||
letter-spacing: $mainFontSpacing;
|
||||
|
||||
@include DebugText(red);
|
||||
}
|
||||
|
||||
@mixin Text {
|
||||
@include ScaleFont($textFontSize, $textLineHeight);
|
||||
font-weight: $mainFontWeight;
|
||||
font-family: $mainFont;
|
||||
|
||||
letter-spacing: $mainFontSpacing;
|
||||
|
||||
@include DebugText(blue);
|
||||
}
|
||||
|
||||
@mixin Heading {
|
||||
@include ScaleFont($headingFontSize, $headingLineHeight);
|
||||
font-weight: $mainFontWeight;
|
||||
font-family: $mainFont;
|
||||
letter-spacing: $mainFontSpacing;
|
||||
|
||||
@include DebugText(yellow);
|
||||
}
|
||||
|
||||
@mixin SuperHeading {
|
||||
@include ScaleFont($superHeadingFontSize, $superHeadingLineHeight);
|
||||
font-weight: $mainFontWeight;
|
||||
font-family: $mainFont;
|
||||
letter-spacing: $mainFontSpacing;
|
||||
|
||||
@include DebugText(orange);
|
||||
}
|
||||
|
||||
@mixin ButtonText {
|
||||
@include ScaleFont($buttonFontSize, $buttonLineHeight);
|
||||
font-weight: $mainFontWeight;
|
||||
font-family: $mainFont;
|
||||
letter-spacing: $mainFontSpacing;
|
||||
@include DebugText(purple);
|
||||
}
|
||||
|
||||
@function str-split($string, $separator) {
|
||||
// empty array/list
|
||||
$split-arr: ();
|
||||
// first index of separator in string
|
||||
$index: str-index($string, $separator);
|
||||
// loop through string
|
||||
@while $index != null {
|
||||
// get the substring from the first character to the separator
|
||||
$item: str-slice($string, 1, $index - 1);
|
||||
// push item to array
|
||||
$split-arr: append($split-arr, $item);
|
||||
// remove item and separator from string
|
||||
$string: str-slice($string, $index + 1);
|
||||
// find new index of separator
|
||||
$index: str-index($string, $separator);
|
||||
}
|
||||
// add the remaining string to list (the last item)
|
||||
$split-arr: append($split-arr, $string);
|
||||
|
||||
@return $split-arr;
|
||||
}
|
||||
|
||||
@function _first-index($string, $direction: "left") {
|
||||
@for $i from 1 through str-length($string) {
|
||||
$index: if($direction == "left", $i, -$i);
|
||||
|
||||
@if str-slice($string, $index, $index) != " " {
|
||||
@return $index;
|
||||
}
|
||||
}
|
||||
|
||||
@return 0;
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
||||
$globalBorderRadius: 2px;
|
||||
|
||||
// When to reduce control elements size for small devices
|
||||
$layoutExpandMinWidth: 340px;
|
||||
|
||||
// Font sizes and line heights
|
||||
$superHeadingFontSize: 25px;
|
||||
$superHeadingLineHeight: 24px;
|
||||
|
||||
$breakTooltipShowStatsPx: 1023px;
|
||||
|
||||
$headingFontSize: 19px;
|
||||
$headingLineHeight: 21px;
|
||||
|
||||
$textFontSize: 16px;
|
||||
$textLineHeight: 21px;
|
||||
|
||||
$plainTextFontSize: 13px;
|
||||
$plainTextLineHeight: 17px;
|
||||
|
||||
$supersmallTextFontSize: 10px;
|
||||
$supersmallTextLineHeight: 13px;
|
||||
$buttonFontSize: 14px;
|
||||
$buttonLineHeight: 18px;
|
||||
|
||||
// Main background color
|
||||
$mainBgColor: #dee1ea;
|
||||
|
||||
// Accent colors
|
||||
|
||||
$accentColorBright: #e1e4ed;
|
||||
$accentColorDark: #7d808a;
|
||||
$colorGreenBright: #66bb6a;
|
||||
$colorBlueBright: rgb(74, 151, 223);
|
||||
$colorRedBright: #ef5072;
|
||||
$themeColor: #393747;
|
||||
$ingameHudBg: rgba(#333438, 0.9);
|
||||
|
||||
$text3dColor: #f4ffff;
|
||||
|
||||
$darkModeGameBackground: #535866;
|
||||
$darkModeControlsBackground: darken($darkModeGameBackground, 5);
|
||||
|
||||
// Dialog properties
|
||||
$modalDialogBg: rgba(160, 165, 180, 0.8);
|
||||
$dialogBgColor: lighten($mainBgColor, 10);
|
||||
|
||||
$lightFontWeight: normal;
|
||||
$boldFontWeight: 600;
|
||||
|
||||
$iconSizeSmall: 30px;
|
||||
$iconSizeMedium: 40px;
|
||||
$iconSizeLarge: 60px;
|
||||
|
||||
// Poppins 500
|
||||
// Rubik 400
|
||||
// Cairo 400
|
||||
// Viga 400
|
||||
// Sniglet 400
|
||||
|
||||
$mainFont: "GameFont", sans-serif;
|
||||
// $mainFont: "DK Canoodle";
|
||||
// $mainFont: "MADE Florence Sans";
|
||||
$numberFont: $mainFont;
|
||||
$textFont: $mainFont;
|
||||
|
||||
$mainFontWeight: 400;
|
||||
$mainFontSpacing: 0.04em;
|
||||
$mainFontScale: 1;
|
||||
|
||||
@mixin DebugText($color) {
|
||||
// font-size: 3px;
|
||||
// &,
|
||||
// * {
|
||||
// color: $color !important;
|
||||
// }
|
||||
}
|
||||
|
||||
@mixin SuperSmallText {
|
||||
@include ScaleFont($supersmallTextFontSize, $supersmallTextLineHeight);
|
||||
font-weight: $mainFontWeight;
|
||||
font-family: $mainFont;
|
||||
letter-spacing: $mainFontSpacing;
|
||||
@include DebugText(green);
|
||||
}
|
||||
|
||||
@mixin PlainText {
|
||||
@include ScaleFont($plainTextFontSize, $plainTextLineHeight);
|
||||
font-weight: $mainFontWeight;
|
||||
font-family: $mainFont;
|
||||
letter-spacing: $mainFontSpacing;
|
||||
|
||||
@include DebugText(red);
|
||||
}
|
||||
|
||||
@mixin Text {
|
||||
@include ScaleFont($textFontSize, $textLineHeight);
|
||||
font-weight: $mainFontWeight;
|
||||
font-family: $mainFont;
|
||||
|
||||
letter-spacing: $mainFontSpacing;
|
||||
|
||||
@include DebugText(blue);
|
||||
}
|
||||
|
||||
@mixin Heading {
|
||||
@include ScaleFont($headingFontSize, $headingLineHeight);
|
||||
font-weight: $mainFontWeight;
|
||||
font-family: $mainFont;
|
||||
letter-spacing: $mainFontSpacing;
|
||||
|
||||
@include DebugText(yellow);
|
||||
}
|
||||
|
||||
@mixin SuperHeading {
|
||||
@include ScaleFont($superHeadingFontSize, $superHeadingLineHeight);
|
||||
font-weight: $mainFontWeight;
|
||||
font-family: $mainFont;
|
||||
letter-spacing: $mainFontSpacing;
|
||||
|
||||
@include DebugText(orange);
|
||||
}
|
||||
|
||||
@mixin ButtonText {
|
||||
@include ScaleFont($buttonFontSize, $buttonLineHeight);
|
||||
font-weight: $mainFontWeight;
|
||||
font-family: $mainFont;
|
||||
letter-spacing: $mainFontSpacing;
|
||||
@include DebugText(purple);
|
||||
}
|
||||
|
||||
@function str-split($string, $separator) {
|
||||
// empty array/list
|
||||
$split-arr: ();
|
||||
// first index of separator in string
|
||||
$index: str-index($string, $separator);
|
||||
// loop through string
|
||||
@while $index != null {
|
||||
// get the substring from the first character to the separator
|
||||
$item: str-slice($string, 1, $index - 1);
|
||||
// push item to array
|
||||
$split-arr: append($split-arr, $item);
|
||||
// remove item and separator from string
|
||||
$string: str-slice($string, $index + 1);
|
||||
// find new index of separator
|
||||
$index: str-index($string, $separator);
|
||||
}
|
||||
// add the remaining string to list (the last item)
|
||||
$split-arr: append($split-arr, $string);
|
||||
|
||||
@return $split-arr;
|
||||
}
|
||||
|
||||
@function _first-index($string, $direction: "left") {
|
||||
@for $i from 1 through str-length($string) {
|
||||
$index: if($direction == "left", $i, -$i);
|
||||
|
||||
@if str-slice($string, $index, $index) != " " {
|
||||
@return $index;
|
||||
}
|
||||
}
|
||||
|
||||
@return 0;
|
||||
}
|
||||
|
||||
@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,408 +1,411 @@
|
||||
import { AnimationFrame } from "./core/animation_frame";
|
||||
import { BackgroundResourcesLoader } from "./core/background_resources_loader";
|
||||
import { IS_MOBILE } from "./core/config";
|
||||
import { GameState } from "./core/game_state";
|
||||
import { GLOBAL_APP, setGlobalApp } from "./core/globals";
|
||||
import { InputDistributor } from "./core/input_distributor";
|
||||
import { Loader } from "./core/loader";
|
||||
import { createLogger, logSection } from "./core/logging";
|
||||
import { StateManager } from "./core/state_manager";
|
||||
import { TrackedState } from "./core/tracked_state";
|
||||
import { getPlatformName, waitNextFrame } from "./core/utils";
|
||||
import { Vector } from "./core/vector";
|
||||
import { AdProviderInterface } from "./platform/ad_provider";
|
||||
import { NoAdProvider } from "./platform/ad_providers/no_ad_provider";
|
||||
import { AnalyticsInterface } from "./platform/analytics";
|
||||
import { GoogleAnalyticsImpl } from "./platform/browser/google_analytics";
|
||||
import { SoundImplBrowser } from "./platform/browser/sound";
|
||||
import { PlatformWrapperImplBrowser } from "./platform/browser/wrapper";
|
||||
import { PlatformWrapperImplElectron } from "./platform/electron/wrapper";
|
||||
import { PlatformWrapperInterface } from "./platform/wrapper";
|
||||
import { ApplicationSettings } from "./profile/application_settings";
|
||||
import { SavegameManager } from "./savegame/savegame_manager";
|
||||
import { AboutState } from "./states/about";
|
||||
import { ChangelogState } from "./states/changelog";
|
||||
import { InGameState } from "./states/ingame";
|
||||
import { KeybindingsState } from "./states/keybindings";
|
||||
import { MainMenuState } from "./states/main_menu";
|
||||
import { MobileWarningState } from "./states/mobile_warning";
|
||||
import { PreloadState } from "./states/preload";
|
||||
import { SettingsState } from "./states/settings";
|
||||
import { ShapezGameAnalytics } from "./platform/browser/game_analytics";
|
||||
|
||||
/**
|
||||
* @typedef {import("./platform/game_analytics").GameAnalyticsInterface} GameAnalyticsInterface
|
||||
* @typedef {import("./platform/sound").SoundInterface} SoundInterface
|
||||
* @typedef {import("./platform/storage").StorageInterface} StorageInterface
|
||||
*/
|
||||
|
||||
const logger = createLogger("application");
|
||||
|
||||
// Set the name of the hidden property and the change event for visibility
|
||||
let pageHiddenPropName, pageVisibilityEventName;
|
||||
if (typeof document.hidden !== "undefined") {
|
||||
// Opera 12.10 and Firefox 18 and later support
|
||||
pageHiddenPropName = "hidden";
|
||||
pageVisibilityEventName = "visibilitychange";
|
||||
// @ts-ignore
|
||||
} else if (typeof document.msHidden !== "undefined") {
|
||||
pageHiddenPropName = "msHidden";
|
||||
pageVisibilityEventName = "msvisibilitychange";
|
||||
// @ts-ignore
|
||||
} else if (typeof document.webkitHidden !== "undefined") {
|
||||
pageHiddenPropName = "webkitHidden";
|
||||
pageVisibilityEventName = "webkitvisibilitychange";
|
||||
}
|
||||
|
||||
export class Application {
|
||||
constructor() {
|
||||
assert(!GLOBAL_APP, "Tried to construct application twice");
|
||||
logger.log("Creating application, platform =", getPlatformName());
|
||||
setGlobalApp(this);
|
||||
|
||||
this.unloaded = false;
|
||||
|
||||
// Global stuff
|
||||
this.settings = new ApplicationSettings(this);
|
||||
this.ticker = new AnimationFrame();
|
||||
this.stateMgr = new StateManager(this);
|
||||
this.savegameMgr = new SavegameManager(this);
|
||||
this.inputMgr = new InputDistributor(this);
|
||||
this.backgroundResourceLoader = new BackgroundResourcesLoader(this);
|
||||
|
||||
// Platform dependent stuff
|
||||
|
||||
/** @type {StorageInterface} */
|
||||
this.storage = null;
|
||||
|
||||
/** @type {SoundInterface} */
|
||||
this.sound = null;
|
||||
|
||||
/** @type {PlatformWrapperInterface} */
|
||||
this.platformWrapper = null;
|
||||
|
||||
/** @type {AdProviderInterface} */
|
||||
this.adProvider = null;
|
||||
|
||||
/** @type {AnalyticsInterface} */
|
||||
this.analytics = null;
|
||||
|
||||
/** @type {GameAnalyticsInterface} */
|
||||
this.gameAnalytics = null;
|
||||
|
||||
this.initPlatformDependentInstances();
|
||||
|
||||
// Track if the window is focused (only relevant for browser)
|
||||
this.focused = true;
|
||||
|
||||
// Track if the window is visible
|
||||
this.pageVisible = true;
|
||||
|
||||
// Track if the app is paused (cordova)
|
||||
this.applicationPaused = false;
|
||||
|
||||
/** @type {TypedTrackedState<boolean>} */
|
||||
this.trackedIsRenderable = new TrackedState(this.onAppRenderableStateChanged, this);
|
||||
|
||||
// Dimensions
|
||||
this.screenWidth = 0;
|
||||
this.screenHeight = 0;
|
||||
|
||||
// Store the timestamp where we last checked for a screen resize, since orientationchange is unreliable with cordova
|
||||
this.lastResizeCheck = null;
|
||||
|
||||
// Store the mouse position, or null if not available
|
||||
/** @type {Vector|null} */
|
||||
this.mousePosition = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes all platform instances
|
||||
*/
|
||||
initPlatformDependentInstances() {
|
||||
logger.log("Creating platform dependent instances (standalone=", G_IS_STANDALONE, ")");
|
||||
|
||||
if (G_IS_STANDALONE) {
|
||||
this.platformWrapper = new PlatformWrapperImplElectron(this);
|
||||
} else {
|
||||
this.platformWrapper = new PlatformWrapperImplBrowser(this);
|
||||
}
|
||||
|
||||
// Start with empty ad provider
|
||||
this.adProvider = new NoAdProvider(this);
|
||||
this.sound = new SoundImplBrowser(this);
|
||||
this.analytics = new GoogleAnalyticsImpl(this);
|
||||
this.gameAnalytics = new ShapezGameAnalytics(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers all game states
|
||||
*/
|
||||
registerStates() {
|
||||
/** @type {Array<typeof GameState>} */
|
||||
const states = [
|
||||
PreloadState,
|
||||
MobileWarningState,
|
||||
MainMenuState,
|
||||
InGameState,
|
||||
SettingsState,
|
||||
KeybindingsState,
|
||||
AboutState,
|
||||
ChangelogState,
|
||||
];
|
||||
|
||||
for (let i = 0; i < states.length; ++i) {
|
||||
this.stateMgr.register(states[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers all event listeners
|
||||
*/
|
||||
registerEventListeners() {
|
||||
window.addEventListener("focus", this.onFocus.bind(this));
|
||||
window.addEventListener("blur", this.onBlur.bind(this));
|
||||
|
||||
window.addEventListener("resize", () => this.checkResize(), true);
|
||||
window.addEventListener("orientationchange", () => this.checkResize(), true);
|
||||
|
||||
if (!G_IS_MOBILE_APP && !IS_MOBILE) {
|
||||
window.addEventListener("mousemove", this.handleMousemove.bind(this));
|
||||
}
|
||||
|
||||
// Unload events
|
||||
window.addEventListener("beforeunload", this.onBeforeUnload.bind(this), true);
|
||||
window.addEventListener("unload", this.onUnload.bind(this), true);
|
||||
|
||||
document.addEventListener(pageVisibilityEventName, this.handleVisibilityChange.bind(this), false);
|
||||
|
||||
// Track touches so we can update the focus appropriately
|
||||
document.addEventListener("touchstart", this.updateFocusAfterUserInteraction.bind(this), true);
|
||||
document.addEventListener("touchend", this.updateFocusAfterUserInteraction.bind(this), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the focus after a touch
|
||||
* @param {TouchEvent} event
|
||||
*/
|
||||
updateFocusAfterUserInteraction(event) {
|
||||
const target = /** @type {HTMLElement} */ (event.target);
|
||||
if (!target || !target.tagName) {
|
||||
// Safety check
|
||||
logger.warn("Invalid touchstart/touchend event:", event);
|
||||
return;
|
||||
}
|
||||
|
||||
// When clicking an element which is not the currently focused one, defocus it
|
||||
if (target !== document.activeElement) {
|
||||
// @ts-ignore
|
||||
if (document.activeElement.blur) {
|
||||
// @ts-ignore
|
||||
document.activeElement.blur();
|
||||
}
|
||||
}
|
||||
|
||||
// If we click an input field, focus it now
|
||||
if (target.tagName.toLowerCase() === "input") {
|
||||
// We *really* need the focus
|
||||
waitNextFrame().then(() => target.focus());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a page visibility change event
|
||||
* @param {Event} event
|
||||
*/
|
||||
handleVisibilityChange(event) {
|
||||
window.focus();
|
||||
const pageVisible = !document[pageHiddenPropName];
|
||||
if (pageVisible !== this.pageVisible) {
|
||||
this.pageVisible = pageVisible;
|
||||
logger.log("Visibility changed:", this.pageVisible);
|
||||
this.trackedIsRenderable.set(this.isRenderable());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a mouse move event
|
||||
* @param {MouseEvent} event
|
||||
*/
|
||||
handleMousemove(event) {
|
||||
this.mousePosition = new Vector(event.clientX, event.clientY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal on focus handler
|
||||
*/
|
||||
onFocus() {
|
||||
this.focused = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal blur handler
|
||||
*/
|
||||
onBlur() {
|
||||
this.focused = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the app is currently visible
|
||||
*/
|
||||
isRenderable() {
|
||||
return !this.applicationPaused && this.pageVisible;
|
||||
}
|
||||
|
||||
onAppRenderableStateChanged(renderable) {
|
||||
logger.log("Application renderable:", renderable);
|
||||
window.focus();
|
||||
const currentState = this.stateMgr.getCurrentState();
|
||||
if (!renderable) {
|
||||
if (currentState) {
|
||||
currentState.onAppPause();
|
||||
}
|
||||
} else {
|
||||
if (currentState) {
|
||||
currentState.onAppResume();
|
||||
}
|
||||
this.checkResize();
|
||||
}
|
||||
|
||||
this.sound.onPageRenderableStateChanged(renderable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal unload handler
|
||||
*/
|
||||
onUnload(event) {
|
||||
if (!this.unloaded) {
|
||||
logSection("UNLOAD HANDLER", "#f77");
|
||||
this.unloaded = true;
|
||||
const currentState = this.stateMgr.getCurrentState();
|
||||
if (currentState) {
|
||||
currentState.onBeforeExit();
|
||||
}
|
||||
this.deinitialize();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal before-unload handler
|
||||
*/
|
||||
onBeforeUnload(event) {
|
||||
logSection("BEFORE UNLOAD HANDLER", "#f77");
|
||||
const currentState = this.stateMgr.getCurrentState();
|
||||
|
||||
if (!G_IS_DEV && currentState && currentState.getHasUnloadConfirmation()) {
|
||||
if (!G_IS_STANDALONE) {
|
||||
// Need to show a "Are you sure you want to exit"
|
||||
event.preventDefault();
|
||||
event.returnValue = "Are you sure you want to exit?";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Boots the application
|
||||
*/
|
||||
boot() {
|
||||
console.log("Booting ...");
|
||||
this.registerStates();
|
||||
this.registerEventListeners();
|
||||
|
||||
Loader.linkAppAfterBoot(this);
|
||||
|
||||
// Check for mobile
|
||||
if (IS_MOBILE) {
|
||||
this.stateMgr.moveToState("MobileWarningState");
|
||||
} else {
|
||||
this.stateMgr.moveToState("PreloadState");
|
||||
}
|
||||
|
||||
// Starting rendering
|
||||
this.ticker.frameEmitted.add(this.onFrameEmitted, this);
|
||||
this.ticker.bgFrameEmitted.add(this.onBackgroundFrame, this);
|
||||
this.ticker.start();
|
||||
|
||||
window.focus();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deinitializes the application
|
||||
*/
|
||||
deinitialize() {
|
||||
return this.sound.deinitialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Background frame update callback
|
||||
* @param {number} dt
|
||||
*/
|
||||
onBackgroundFrame(dt) {
|
||||
if (this.isRenderable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentState = this.stateMgr.getCurrentState();
|
||||
if (currentState) {
|
||||
currentState.onBackgroundTick(dt);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Frame update callback
|
||||
* @param {number} dt
|
||||
*/
|
||||
onFrameEmitted(dt) {
|
||||
if (!this.isRenderable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const time = performance.now();
|
||||
|
||||
// Periodically check for resizes, this is expensive (takes 2-3ms so only do it once in a while!)
|
||||
if (!this.lastResizeCheck || time - this.lastResizeCheck > 1000) {
|
||||
this.checkResize();
|
||||
this.lastResizeCheck = time;
|
||||
}
|
||||
|
||||
const currentState = this.stateMgr.getCurrentState();
|
||||
if (currentState) {
|
||||
currentState.onRender(dt);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the app resized. Only does this once in a while
|
||||
* @param {boolean} forceUpdate Forced update of the dimensions
|
||||
*/
|
||||
checkResize(forceUpdate = false) {
|
||||
const w = window.innerWidth;
|
||||
const h = window.innerHeight;
|
||||
if (this.screenWidth !== w || this.screenHeight !== h || forceUpdate) {
|
||||
this.screenWidth = w;
|
||||
this.screenHeight = h;
|
||||
const currentState = this.stateMgr.getCurrentState();
|
||||
if (currentState) {
|
||||
currentState.onResized(this.screenWidth, this.screenHeight);
|
||||
}
|
||||
|
||||
const scale = this.getEffectiveUiScale();
|
||||
waitNextFrame().then(() => document.documentElement.style.setProperty("--ui-scale", `${scale}`));
|
||||
window.focus();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the effective ui sclae
|
||||
*/
|
||||
getEffectiveUiScale() {
|
||||
return this.platformWrapper.getUiScale() * this.settings.getInterfaceScaleValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback after ui scale has changed
|
||||
*/
|
||||
updateAfterUiScaleChanged() {
|
||||
this.checkResize(true);
|
||||
}
|
||||
}
|
||||
import { AnimationFrame } from "./core/animation_frame";
|
||||
import { BackgroundResourcesLoader } from "./core/background_resources_loader";
|
||||
import { IS_MOBILE } from "./core/config";
|
||||
import { GameState } from "./core/game_state";
|
||||
import { GLOBAL_APP, setGlobalApp } from "./core/globals";
|
||||
import { InputDistributor } from "./core/input_distributor";
|
||||
import { Loader } from "./core/loader";
|
||||
import { createLogger, logSection } from "./core/logging";
|
||||
import { StateManager } from "./core/state_manager";
|
||||
import { TrackedState } from "./core/tracked_state";
|
||||
import { getPlatformName, waitNextFrame } from "./core/utils";
|
||||
import { Vector } from "./core/vector";
|
||||
import { AdProviderInterface } from "./platform/ad_provider";
|
||||
import { NoAdProvider } from "./platform/ad_providers/no_ad_provider";
|
||||
import { AnalyticsInterface } from "./platform/analytics";
|
||||
import { GoogleAnalyticsImpl } from "./platform/browser/google_analytics";
|
||||
import { SoundImplBrowser } from "./platform/browser/sound";
|
||||
import { PlatformWrapperImplBrowser } from "./platform/browser/wrapper";
|
||||
import { PlatformWrapperImplElectron } from "./platform/electron/wrapper";
|
||||
import { PlatformWrapperInterface } from "./platform/wrapper";
|
||||
import { ApplicationSettings } from "./profile/application_settings";
|
||||
import { SavegameManager } from "./savegame/savegame_manager";
|
||||
import { AboutState } from "./states/about";
|
||||
import { ChangelogState } from "./states/changelog";
|
||||
import { InGameState } from "./states/ingame";
|
||||
import { KeybindingsState } from "./states/keybindings";
|
||||
import { MainMenuState } from "./states/main_menu";
|
||||
import { MobileWarningState } from "./states/mobile_warning";
|
||||
import { PreloadState } from "./states/preload";
|
||||
import { SettingsState } from "./states/settings";
|
||||
import { ShapezGameAnalytics } from "./platform/browser/game_analytics";
|
||||
|
||||
/**
|
||||
* @typedef {import("./platform/game_analytics").GameAnalyticsInterface} GameAnalyticsInterface
|
||||
* @typedef {import("./platform/sound").SoundInterface} SoundInterface
|
||||
* @typedef {import("./platform/storage").StorageInterface} StorageInterface
|
||||
*/
|
||||
|
||||
const logger = createLogger("application");
|
||||
|
||||
// Set the name of the hidden property and the change event for visibility
|
||||
let pageHiddenPropName, pageVisibilityEventName;
|
||||
if (typeof document.hidden !== "undefined") {
|
||||
// Opera 12.10 and Firefox 18 and later support
|
||||
pageHiddenPropName = "hidden";
|
||||
pageVisibilityEventName = "visibilitychange";
|
||||
// @ts-ignore
|
||||
} else if (typeof document.msHidden !== "undefined") {
|
||||
pageHiddenPropName = "msHidden";
|
||||
pageVisibilityEventName = "msvisibilitychange";
|
||||
// @ts-ignore
|
||||
} else if (typeof document.webkitHidden !== "undefined") {
|
||||
pageHiddenPropName = "webkitHidden";
|
||||
pageVisibilityEventName = "webkitvisibilitychange";
|
||||
}
|
||||
|
||||
export class Application {
|
||||
constructor() {
|
||||
assert(!GLOBAL_APP, "Tried to construct application twice");
|
||||
logger.log("Creating application, platform =", getPlatformName());
|
||||
setGlobalApp(this);
|
||||
|
||||
this.unloaded = false;
|
||||
|
||||
// Global stuff
|
||||
this.settings = new ApplicationSettings(this);
|
||||
this.ticker = new AnimationFrame();
|
||||
this.stateMgr = new StateManager(this);
|
||||
this.savegameMgr = new SavegameManager(this);
|
||||
this.inputMgr = new InputDistributor(this);
|
||||
this.backgroundResourceLoader = new BackgroundResourcesLoader(this);
|
||||
|
||||
// Platform dependent stuff
|
||||
|
||||
/** @type {StorageInterface} */
|
||||
this.storage = null;
|
||||
|
||||
/** @type {SoundInterface} */
|
||||
this.sound = null;
|
||||
|
||||
/** @type {PlatformWrapperInterface} */
|
||||
this.platformWrapper = null;
|
||||
|
||||
/** @type {AdProviderInterface} */
|
||||
this.adProvider = null;
|
||||
|
||||
/** @type {AnalyticsInterface} */
|
||||
this.analytics = null;
|
||||
|
||||
/** @type {GameAnalyticsInterface} */
|
||||
this.gameAnalytics = null;
|
||||
|
||||
this.initPlatformDependentInstances();
|
||||
|
||||
// Track if the window is focused (only relevant for browser)
|
||||
this.focused = true;
|
||||
|
||||
// Track if the window is visible
|
||||
this.pageVisible = true;
|
||||
|
||||
// Track if the app is paused (cordova)
|
||||
this.applicationPaused = false;
|
||||
|
||||
/** @type {TypedTrackedState<boolean>} */
|
||||
this.trackedIsRenderable = new TrackedState(this.onAppRenderableStateChanged, this);
|
||||
|
||||
// Dimensions
|
||||
this.screenWidth = 0;
|
||||
this.screenHeight = 0;
|
||||
|
||||
// Store the timestamp where we last checked for a screen resize, since orientationchange is unreliable with cordova
|
||||
this.lastResizeCheck = null;
|
||||
|
||||
// Store the mouse position, or null if not available
|
||||
/** @type {Vector|null} */
|
||||
this.mousePosition = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes all platform instances
|
||||
*/
|
||||
initPlatformDependentInstances() {
|
||||
logger.log("Creating platform dependent instances (standalone=", G_IS_STANDALONE, ")");
|
||||
|
||||
if (G_IS_STANDALONE) {
|
||||
this.platformWrapper = new PlatformWrapperImplElectron(this);
|
||||
} else {
|
||||
this.platformWrapper = new PlatformWrapperImplBrowser(this);
|
||||
}
|
||||
|
||||
// Start with empty ad provider
|
||||
this.adProvider = new NoAdProvider(this);
|
||||
this.sound = new SoundImplBrowser(this);
|
||||
this.analytics = new GoogleAnalyticsImpl(this);
|
||||
this.gameAnalytics = new ShapezGameAnalytics(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers all game states
|
||||
*/
|
||||
registerStates() {
|
||||
/** @type {Array<typeof GameState>} */
|
||||
const states = [
|
||||
PreloadState,
|
||||
MobileWarningState,
|
||||
MainMenuState,
|
||||
InGameState,
|
||||
SettingsState,
|
||||
KeybindingsState,
|
||||
AboutState,
|
||||
ChangelogState,
|
||||
];
|
||||
|
||||
for (let i = 0; i < states.length; ++i) {
|
||||
this.stateMgr.register(states[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers all event listeners
|
||||
*/
|
||||
registerEventListeners() {
|
||||
window.addEventListener("focus", this.onFocus.bind(this));
|
||||
window.addEventListener("blur", this.onBlur.bind(this));
|
||||
|
||||
window.addEventListener("resize", () => this.checkResize(), true);
|
||||
window.addEventListener("orientationchange", () => this.checkResize(), true);
|
||||
|
||||
if (!G_IS_MOBILE_APP && !IS_MOBILE) {
|
||||
window.addEventListener("mousemove", this.handleMousemove.bind(this));
|
||||
window.addEventListener("mouseout", this.handleMousemove.bind(this));
|
||||
window.addEventListener("mouseover", this.handleMousemove.bind(this));
|
||||
window.addEventListener("mouseleave", this.handleMousemove.bind(this));
|
||||
}
|
||||
|
||||
// Unload events
|
||||
window.addEventListener("beforeunload", this.onBeforeUnload.bind(this), true);
|
||||
window.addEventListener("unload", this.onUnload.bind(this), true);
|
||||
|
||||
document.addEventListener(pageVisibilityEventName, this.handleVisibilityChange.bind(this), false);
|
||||
|
||||
// Track touches so we can update the focus appropriately
|
||||
document.addEventListener("touchstart", this.updateFocusAfterUserInteraction.bind(this), true);
|
||||
document.addEventListener("touchend", this.updateFocusAfterUserInteraction.bind(this), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the focus after a touch
|
||||
* @param {TouchEvent} event
|
||||
*/
|
||||
updateFocusAfterUserInteraction(event) {
|
||||
const target = /** @type {HTMLElement} */ (event.target);
|
||||
if (!target || !target.tagName) {
|
||||
// Safety check
|
||||
logger.warn("Invalid touchstart/touchend event:", event);
|
||||
return;
|
||||
}
|
||||
|
||||
// When clicking an element which is not the currently focused one, defocus it
|
||||
if (target !== document.activeElement) {
|
||||
// @ts-ignore
|
||||
if (document.activeElement.blur) {
|
||||
// @ts-ignore
|
||||
document.activeElement.blur();
|
||||
}
|
||||
}
|
||||
|
||||
// If we click an input field, focus it now
|
||||
if (target.tagName.toLowerCase() === "input") {
|
||||
// We *really* need the focus
|
||||
waitNextFrame().then(() => target.focus());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a page visibility change event
|
||||
* @param {Event} event
|
||||
*/
|
||||
handleVisibilityChange(event) {
|
||||
window.focus();
|
||||
const pageVisible = !document[pageHiddenPropName];
|
||||
if (pageVisible !== this.pageVisible) {
|
||||
this.pageVisible = pageVisible;
|
||||
logger.log("Visibility changed:", this.pageVisible);
|
||||
this.trackedIsRenderable.set(this.isRenderable());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a mouse move event
|
||||
* @param {MouseEvent} event
|
||||
*/
|
||||
handleMousemove(event) {
|
||||
this.mousePosition = new Vector(event.clientX, event.clientY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal on focus handler
|
||||
*/
|
||||
onFocus() {
|
||||
this.focused = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal blur handler
|
||||
*/
|
||||
onBlur() {
|
||||
this.focused = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the app is currently visible
|
||||
*/
|
||||
isRenderable() {
|
||||
return !this.applicationPaused && this.pageVisible;
|
||||
}
|
||||
|
||||
onAppRenderableStateChanged(renderable) {
|
||||
logger.log("Application renderable:", renderable);
|
||||
window.focus();
|
||||
const currentState = this.stateMgr.getCurrentState();
|
||||
if (!renderable) {
|
||||
if (currentState) {
|
||||
currentState.onAppPause();
|
||||
}
|
||||
} else {
|
||||
if (currentState) {
|
||||
currentState.onAppResume();
|
||||
}
|
||||
this.checkResize();
|
||||
}
|
||||
|
||||
this.sound.onPageRenderableStateChanged(renderable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal unload handler
|
||||
*/
|
||||
onUnload(event) {
|
||||
if (!this.unloaded) {
|
||||
logSection("UNLOAD HANDLER", "#f77");
|
||||
this.unloaded = true;
|
||||
const currentState = this.stateMgr.getCurrentState();
|
||||
if (currentState) {
|
||||
currentState.onBeforeExit();
|
||||
}
|
||||
this.deinitialize();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal before-unload handler
|
||||
*/
|
||||
onBeforeUnload(event) {
|
||||
logSection("BEFORE UNLOAD HANDLER", "#f77");
|
||||
const currentState = this.stateMgr.getCurrentState();
|
||||
|
||||
if (!G_IS_DEV && currentState && currentState.getHasUnloadConfirmation()) {
|
||||
if (!G_IS_STANDALONE) {
|
||||
// Need to show a "Are you sure you want to exit"
|
||||
event.preventDefault();
|
||||
event.returnValue = "Are you sure you want to exit?";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Boots the application
|
||||
*/
|
||||
boot() {
|
||||
console.log("Booting ...");
|
||||
this.registerStates();
|
||||
this.registerEventListeners();
|
||||
|
||||
Loader.linkAppAfterBoot(this);
|
||||
|
||||
// Check for mobile
|
||||
if (IS_MOBILE) {
|
||||
this.stateMgr.moveToState("MobileWarningState");
|
||||
} else {
|
||||
this.stateMgr.moveToState("PreloadState");
|
||||
}
|
||||
|
||||
// Starting rendering
|
||||
this.ticker.frameEmitted.add(this.onFrameEmitted, this);
|
||||
this.ticker.bgFrameEmitted.add(this.onBackgroundFrame, this);
|
||||
this.ticker.start();
|
||||
|
||||
window.focus();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deinitializes the application
|
||||
*/
|
||||
deinitialize() {
|
||||
return this.sound.deinitialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Background frame update callback
|
||||
* @param {number} dt
|
||||
*/
|
||||
onBackgroundFrame(dt) {
|
||||
if (this.isRenderable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentState = this.stateMgr.getCurrentState();
|
||||
if (currentState) {
|
||||
currentState.onBackgroundTick(dt);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Frame update callback
|
||||
* @param {number} dt
|
||||
*/
|
||||
onFrameEmitted(dt) {
|
||||
if (!this.isRenderable()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const time = performance.now();
|
||||
|
||||
// Periodically check for resizes, this is expensive (takes 2-3ms so only do it once in a while!)
|
||||
if (!this.lastResizeCheck || time - this.lastResizeCheck > 1000) {
|
||||
this.checkResize();
|
||||
this.lastResizeCheck = time;
|
||||
}
|
||||
|
||||
const currentState = this.stateMgr.getCurrentState();
|
||||
if (currentState) {
|
||||
currentState.onRender(dt);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the app resized. Only does this once in a while
|
||||
* @param {boolean} forceUpdate Forced update of the dimensions
|
||||
*/
|
||||
checkResize(forceUpdate = false) {
|
||||
const w = window.innerWidth;
|
||||
const h = window.innerHeight;
|
||||
if (this.screenWidth !== w || this.screenHeight !== h || forceUpdate) {
|
||||
this.screenWidth = w;
|
||||
this.screenHeight = h;
|
||||
const currentState = this.stateMgr.getCurrentState();
|
||||
if (currentState) {
|
||||
currentState.onResized(this.screenWidth, this.screenHeight);
|
||||
}
|
||||
|
||||
const scale = this.getEffectiveUiScale();
|
||||
waitNextFrame().then(() => document.documentElement.style.setProperty("--ui-scale", `${scale}`));
|
||||
window.focus();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the effective ui sclae
|
||||
*/
|
||||
getEffectiveUiScale() {
|
||||
return this.platformWrapper.getUiScale() * this.settings.getInterfaceScaleValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback after ui scale has changed
|
||||
*/
|
||||
updateAfterUiScaleChanged() {
|
||||
this.checkResize(true);
|
||||
}
|
||||
}
|
||||
|
@ -24,13 +24,17 @@ export const CHANGELOG = [
|
||||
"Mark pinned shapes in statistics dialog and show them first (inspired by davidburhans)",
|
||||
"Added setting to show chunk borders",
|
||||
"Quad painters have been reworked! They now are integrated with the wires, and only paint the shape when the value is 1 (inspired by dengr1605)",
|
||||
"There are now compact 1x1 splitters available to be unlocked!",
|
||||
"There are now compact 1x1 balancers available to be unlocked!",
|
||||
"Replaced level completion sound to be less distracting",
|
||||
"Allow editing waypoints (by isaisstillalive)",
|
||||
"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 belt planner placing the belt when a dialog opens in the meantime",
|
||||
"Added confirmation when deleting a savegame",
|
||||
"Fixed tunnels entrances connecting to exits sometimes when they shouldn't",
|
||||
"You can now pan the map with your mouse by moving the cursor to the edges of the screen!",
|
||||
"Added setting to auto select the extractor when pipetting a resource patch (by Exund)",
|
||||
"You can now change the unit (seconds / minutes / hours) in the statistics dialog",
|
||||
"The initial belt planner direction is now based on the cursor movement (by MizardX)",
|
||||
"Fix preferred variant not getting saved when clicking on the hud (by Danacus)",
|
||||
],
|
||||
@ -109,7 +113,7 @@ export const CHANGELOG = [
|
||||
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)",
|
||||
"Added continue button to main menu and add separate 'New game' button (by jaysc)",
|
||||
"Added setting to disable smart tunnel placement introduced with the last update",
|
||||
"Added setting to disable vignette",
|
||||
"Update translations",
|
||||
|
@ -13,7 +13,7 @@ import { round1Digit } from "./utils";
|
||||
|
||||
const logger = createLogger("buffers");
|
||||
|
||||
const bufferGcDurationSeconds = 5;
|
||||
const bufferGcDurationSeconds = 0.5;
|
||||
|
||||
export class BufferMaintainer {
|
||||
/**
|
||||
@ -86,27 +86,29 @@ export class BufferMaintainer {
|
||||
// Make sure our backlog never gets too big
|
||||
clearBufferBacklog();
|
||||
|
||||
const bufferStats = getBufferStats();
|
||||
const mbUsed = round1Digit(bufferStats.vramUsage / (1024 * 1024));
|
||||
logger.log(
|
||||
"GC: Remove",
|
||||
(deletedKeys + "").padStart(4),
|
||||
", Remain",
|
||||
(totalKeys + "").padStart(4),
|
||||
"(",
|
||||
(bufferStats.bufferCount + "").padStart(4),
|
||||
"total",
|
||||
")",
|
||||
// if (G_IS_DEV) {
|
||||
// const bufferStats = getBufferStats();
|
||||
// const mbUsed = round1Digit(bufferStats.vramUsage / (1024 * 1024));
|
||||
// logger.log(
|
||||
// "GC: Remove",
|
||||
// (deletedKeys + "").padStart(4),
|
||||
// ", Remain",
|
||||
// (totalKeys + "").padStart(4),
|
||||
// "(",
|
||||
// (bufferStats.bufferCount + "").padStart(4),
|
||||
// "total",
|
||||
// ")",
|
||||
|
||||
"(",
|
||||
(bufferStats.backlog + "").padStart(4),
|
||||
"backlog",
|
||||
")",
|
||||
// "(",
|
||||
// (bufferStats.backlogSize + "").padStart(4),
|
||||
// "backlog",
|
||||
// ")",
|
||||
|
||||
"VRAM:",
|
||||
mbUsed,
|
||||
"MB"
|
||||
);
|
||||
// "VRAM:",
|
||||
// mbUsed,
|
||||
// "MB"
|
||||
// );
|
||||
// }
|
||||
|
||||
++this.iterationIndex;
|
||||
}
|
||||
|
@ -25,17 +25,43 @@ export function disableImageSmoothing(context) {
|
||||
context.webkitImageSmoothingEnabled = false;
|
||||
}
|
||||
|
||||
const registeredCanvas = [];
|
||||
const freeCanvasList = [];
|
||||
/**
|
||||
* @typedef {{
|
||||
* canvas: HTMLCanvasElement,
|
||||
* context: CanvasRenderingContext2D
|
||||
* }} CanvasCacheEntry
|
||||
*/
|
||||
|
||||
let vramUsage = 0;
|
||||
let bufferCount = 0;
|
||||
/**
|
||||
* @type {Array<CanvasCacheEntry>}
|
||||
*/
|
||||
const registeredCanvas = [];
|
||||
|
||||
/**
|
||||
* Buckets for each width * height combination
|
||||
* @type {Map<number, Array<CanvasCacheEntry>>}
|
||||
*/
|
||||
const freeCanvasBuckets = new Map();
|
||||
|
||||
/**
|
||||
* Track statistics
|
||||
*/
|
||||
const stats = {
|
||||
vramUsage: 0,
|
||||
backlogVramUsage: 0,
|
||||
bufferCount: 0,
|
||||
numReused: 0,
|
||||
numCreated: 0,
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {HTMLCanvasElement} canvas
|
||||
*/
|
||||
export function getBufferVramUsageBytes(canvas) {
|
||||
assert(canvas, "no canvas given");
|
||||
assert(Number.isFinite(canvas.width), "bad canvas width: " + canvas.width);
|
||||
assert(Number.isFinite(canvas.height), "bad canvas height" + canvas.height);
|
||||
return canvas.width * canvas.height * 4;
|
||||
}
|
||||
|
||||
@ -43,17 +69,31 @@ export function getBufferVramUsageBytes(canvas) {
|
||||
* Returns stats on the allocated buffers
|
||||
*/
|
||||
export function getBufferStats() {
|
||||
let numBuffersFree = 0;
|
||||
freeCanvasBuckets.forEach(bucket => {
|
||||
numBuffersFree += bucket.length;
|
||||
});
|
||||
|
||||
return {
|
||||
vramUsage,
|
||||
bufferCount,
|
||||
backlog: freeCanvasList.length,
|
||||
...stats,
|
||||
backlogKeys: freeCanvasBuckets.size,
|
||||
backlogSize: numBuffersFree,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the backlog buffers if they grew too much
|
||||
*/
|
||||
export function clearBufferBacklog() {
|
||||
while (freeCanvasList.length > 50) {
|
||||
freeCanvasList.pop();
|
||||
}
|
||||
freeCanvasBuckets.forEach(bucket => {
|
||||
while (bucket.length > 500) {
|
||||
const entry = bucket[bucket.length - 1];
|
||||
stats.backlogVramUsage -= getBufferVramUsageBytes(entry.canvas);
|
||||
delete entry.canvas;
|
||||
delete entry.context;
|
||||
bucket.pop();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -84,53 +124,29 @@ export function makeOffscreenBuffer(w, h, { smooth = true, reusable = true, labe
|
||||
let canvas = null;
|
||||
let context = null;
|
||||
|
||||
let bestMatchingOne = null;
|
||||
let bestMatchingPixelsDiff = 1e50;
|
||||
|
||||
const currentPixels = w * h;
|
||||
|
||||
// Ok, search in cache first
|
||||
for (let i = 0; i < freeCanvasList.length; ++i) {
|
||||
const { canvas: useableCanvas, context: useableContext } = freeCanvasList[i];
|
||||
const bucket = freeCanvasBuckets.get(w * h) || [];
|
||||
|
||||
for (let i = 0; i < bucket.length; ++i) {
|
||||
const { canvas: useableCanvas, context: useableContext } = bucket[i];
|
||||
if (useableCanvas.width === w && useableCanvas.height === h) {
|
||||
// Ok we found one
|
||||
canvas = useableCanvas;
|
||||
context = useableContext;
|
||||
|
||||
fastArrayDelete(freeCanvasList, i);
|
||||
// Restore past state
|
||||
context.restore();
|
||||
context.save();
|
||||
context.clearRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
delete canvas.style.width;
|
||||
delete canvas.style.height;
|
||||
|
||||
stats.numReused++;
|
||||
stats.backlogVramUsage -= getBufferVramUsageBytes(canvas);
|
||||
fastArrayDelete(bucket, i);
|
||||
break;
|
||||
}
|
||||
|
||||
const otherPixels = useableCanvas.width * useableCanvas.height;
|
||||
const diff = Math.abs(otherPixels - currentPixels);
|
||||
if (diff < bestMatchingPixelsDiff) {
|
||||
bestMatchingPixelsDiff = diff;
|
||||
bestMatchingOne = {
|
||||
canvas: useableCanvas,
|
||||
context: useableContext,
|
||||
index: i,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Ok none matching, reuse one though
|
||||
if (!canvas && bestMatchingOne) {
|
||||
canvas = bestMatchingOne.canvas;
|
||||
context = bestMatchingOne.context;
|
||||
canvas.width = w;
|
||||
canvas.height = h;
|
||||
fastArrayDelete(freeCanvasList, bestMatchingOne.index);
|
||||
}
|
||||
|
||||
// Reset context
|
||||
if (context) {
|
||||
// Restore past state
|
||||
context.restore();
|
||||
context.save();
|
||||
context.clearRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
delete canvas.style.width;
|
||||
delete canvas.style.height;
|
||||
}
|
||||
|
||||
// None found , create new one
|
||||
@ -138,6 +154,8 @@ export function makeOffscreenBuffer(w, h, { smooth = true, reusable = true, labe
|
||||
canvas = document.createElement("canvas");
|
||||
context = canvas.getContext("2d" /*, { alpha } */);
|
||||
|
||||
stats.numCreated++;
|
||||
|
||||
canvas.width = w;
|
||||
canvas.height = h;
|
||||
|
||||
@ -145,6 +163,7 @@ export function makeOffscreenBuffer(w, h, { smooth = true, reusable = true, labe
|
||||
context.save();
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
canvas.label = label;
|
||||
|
||||
if (smooth) {
|
||||
@ -167,8 +186,9 @@ export function makeOffscreenBuffer(w, h, { smooth = true, reusable = true, labe
|
||||
export function registerCanvas(canvas, context) {
|
||||
registeredCanvas.push({ canvas, context });
|
||||
|
||||
bufferCount += 1;
|
||||
vramUsage += getBufferVramUsageBytes(canvas);
|
||||
stats.bufferCount += 1;
|
||||
const bytesUsed = getBufferVramUsageBytes(canvas);
|
||||
stats.vramUsage += bytesUsed;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -180,6 +200,7 @@ export function freeCanvas(canvas) {
|
||||
|
||||
let index = -1;
|
||||
let data = null;
|
||||
|
||||
for (let i = 0; i < registeredCanvas.length; ++i) {
|
||||
if (registeredCanvas[i].canvas === canvas) {
|
||||
index = i;
|
||||
@ -193,8 +214,18 @@ export function freeCanvas(canvas) {
|
||||
return;
|
||||
}
|
||||
fastArrayDelete(registeredCanvas, index);
|
||||
freeCanvasList.push(data);
|
||||
|
||||
bufferCount -= 1;
|
||||
vramUsage -= getBufferVramUsageBytes(canvas);
|
||||
const key = canvas.width * canvas.height;
|
||||
const bucket = freeCanvasBuckets.get(key);
|
||||
if (bucket) {
|
||||
bucket.push(data);
|
||||
} else {
|
||||
freeCanvasBuckets.set(key, [data]);
|
||||
}
|
||||
|
||||
stats.bufferCount -= 1;
|
||||
|
||||
const bytesUsed = getBufferVramUsageBytes(canvas);
|
||||
stats.vramUsage -= bytesUsed;
|
||||
stats.backlogVramUsage += bytesUsed;
|
||||
}
|
||||
|