diff --git a/res/ui/icons/mods.png b/res/ui/icons/mods.png new file mode 100644 index 00000000..1b3cb477 Binary files /dev/null and b/res/ui/icons/mods.png differ diff --git a/res/ui/icons/mods_white.png b/res/ui/icons/mods_white.png new file mode 100644 index 00000000..1cb34f27 Binary files /dev/null and b/res/ui/icons/mods_white.png differ diff --git a/src/css/main.scss b/src/css/main.scss index 1ac4f537..4850e3df 100644 --- a/src/css/main.scss +++ b/src/css/main.scss @@ -31,6 +31,7 @@ @import "states/mobile_warning"; @import "states/changelog"; @import "states/puzzle_menu"; +@import "states/mods"; @import "ingame_hud/buildings_toolbar"; @import "ingame_hud/building_placer"; diff --git a/src/css/states/main_menu.scss b/src/css/states/main_menu.scss index 2c70f5d8..f6e0c776 100644 --- a/src/css/states/main_menu.scss +++ b/src/css/states/main_menu.scss @@ -185,11 +185,14 @@ .updateLabel { position: absolute; transform: translateX(50%) rotate(-5deg); - color: #300bff; - @include Heading; + color: #fff; + @include PlainText; font-weight: bold; @include S(right, 40px); @include S(bottom, 20px); + background: $modsColor; + @include S(border-radius, $globalBorderRadius); + @include S(padding, 0, 5px, 1px, 5px); @include InlineAnimation(1.3s ease-in-out infinite) { 50% { @@ -305,14 +308,36 @@ @include S(padding-bottom, 10px); @include S(border-radius, $globalBorderRadius); - h3 { + .header { + display: flex; + width: 100%; + align-items: center; @include S(margin-bottom, 10px); + + .editMods { + margin-left: auto; + @include S(width, 20px); + @include S(height, 20px); + padding: 0; + opacity: 0.5; + background: transparent center center/ 80% no-repeat; + & { + /* @load-async */ + background-image: uiResource("icons/edit_key.png") !important; + } + } + } + + h3 { @include Heading; + color: $modsColor; + margin: 0; } .dlcHint { @include SuperSmallText; @include S(margin-top, 10px); + @include S(width, 300px); display: grid; grid-template-columns: 1fr auto; @@ -326,6 +351,7 @@ @include S(border-radius, $globalBorderRadius); @include S(padding, 5px); box-sizing: border-box; + @include PlainText; @include S(margin-bottom, 5px); display: grid; grid-template-columns: 1fr auto auto; @@ -375,6 +401,7 @@ display: flex; flex-direction: column; align-items: center; + width: 100%; } .modeButtons { @@ -419,27 +446,33 @@ } } - .importButton { + .outer { @include S(margin-top, 15px); + } + + .importButton { @include IncreasedClickArea(0px); } .newGameButton { @include IncreasedClickArea(0px); - @include S(margin-top, 15px); @include S(margin-left, 15px); } - .playModeButton { + .modsButton { @include IncreasedClickArea(0px); - @include S(margin-top, 15px); @include S(margin-left, 15px); - } - .editModeButton { - @include IncreasedClickArea(0px); - @include S(margin-top, 15px); - @include S(margin-left, 15px); + @include S(width, 20px); + + & { + /* @load-async */ + background-image: uiResource("res/ui/icons/mods_white.png") !important; + } + background-position: center center; + background-size: D(15px); + background-color: $modsColor !important; + background-repeat: no-repeat; } .savegames { diff --git a/src/css/states/mods.scss b/src/css/states/mods.scss new file mode 100644 index 00000000..b0d0cb9b --- /dev/null +++ b/src/css/states/mods.scss @@ -0,0 +1,131 @@ +#state_ModsState { + .mainContent { + display: flex; + flex-direction: column; + } + + > .headerBar { + display: grid; + grid-template-columns: 1fr auto; + align-items: center; + + > h1 { + justify-self: start; + } + + .openModsFolder { + background-color: $modsColor; + } + } + + .noModSupport { + display: flex; + align-items: center; + justify-content: center; + height: 100%; + flex-direction: column; + + .steamLink { + @include S(height, 50px); + @include S(width, 220px); + background: #171a23 center center / contain no-repeat; + overflow: hidden; + display: block; + text-indent: -999em; + cursor: pointer; + @include S(margin-top, 30px); + pointer-events: all; + transition: all 0.12s ease-in; + transition-property: opacity, transform; + + @include S(border-radius, $globalBorderRadius); + + &:hover { + opacity: 0.9; + } + } + } + + .modsStats { + @include PlainText; + color: $accentColorDark; + + &.noMods { + @include S(width, 400px); + align-self: center; + justify-self: center; + text-align: center; + display: flex; + flex-direction: column; + align-items: center; + @include Text; + @include S(margin-top, 100px); + color: lighten($accentColorDark, 15); + + &::before { + @include S(margin-bottom, 15px); + content: ""; + @include S(width, 50px); + @include S(height, 50px); + background-position: center center; + background-size: contain; + opacity: 0.2; + } + &::before { + /* @load-async */ + background-image: uiResource("res/ui/icons/mods.png") !important; + } + } + } + + .modsList { + @include S(margin-top, 10px); + overflow-y: scroll; + pointer-events: all; + @include S(padding-right, 5px); + flex-grow: 1; + + .mod { + @include S(border-radius, $globalBorderRadius); + background: $accentColorBright; + @include S(margin-bottom, 4px); + @include S(padding, 7px); + @include S(grid-gap, 5px); + display: grid; + grid-template-columns: 1fr D(100px) D(100px) D(100px); + + .checkbox { + align-self: center; + justify-self: center; + } + + .mainInfo { + display: flex; + flex-direction: column; + + .description { + @include SuperSmallText; + @include S(margin-top, 5px); + color: $accentColorDark; + } + .website { + text-transform: uppercase; + align-self: start; + @include SuperSmallText; + @include S(margin-top, 5px); + } + } + + .version, + .author { + display: flex; + flex-direction: column; + strong { + text-transform: uppercase; + color: $accentColorDark; + @include SuperSmallText; + } + } + } + } +} diff --git a/src/css/states/settings.scss b/src/css/states/settings.scss index 5b36c677..75ef6b85 100644 --- a/src/css/states/settings.scss +++ b/src/css/states/settings.scss @@ -15,20 +15,21 @@ } .sidebar { - display: grid; + display: flex; @include S(min-width, 210px); @include S(max-width, 320px); - @include S(grid-gap, 3px); - grid-template-rows: auto auto auto auto auto 1fr; + flex-direction: column; @include StyleBelowWidth($layoutBreak) { - grid-template-rows: 1fr 1fr; - grid-template-columns: auto auto; + display: grid; + grid-template-columns: 1fr 1fr 1fr; + @include S(grid-gap, 5px); max-width: unset !important; } button { text-align: left; + @include S(margin-bottom, 3px); &::after { content: unset; } @@ -37,15 +38,26 @@ @include StyleBelowWidth($layoutBreak) { text-align: center; + height: D(30px) !important; + padding: D(5px) !important; } } .other { - @include S(margin-top, 10px); align-self: end; + margin-top: auto; @include StyleBelowWidth($layoutBreak) { margin-top: 0; + display: grid; + grid-template-columns: 1fr 1fr; + @include S(grid-gap, 5px); + max-width: unset !important; + grid-column: 1 / 3; + + button { + margin: 0 !important; + } } } @@ -69,6 +81,30 @@ } } + button.manageMods { + background-color: lighten($modsColor, 38); + color: $modsColor; + display: flex; + @include S(padding-right, 5px); + .newBadge { + color: #fff; + @include S(border-radius, $globalBorderRadius); + background: $modsColor; + margin-left: auto; + @include S(padding, 0, 3px, 0, 3px); + + @include InlineAnimation(1.3s ease-in-out infinite) { + 50% { + transform: rotate(0deg) scale(1.1); + } + } + } + + &.active { + background-color: $colorGreenBright; + } + } + button.privacy { @include S(margin-top, 4px); } diff --git a/src/css/variables.scss b/src/css/variables.scss index c7b7c17c..fe1fa864 100644 --- a/src/css/variables.scss +++ b/src/css/variables.scss @@ -38,6 +38,7 @@ $colorRedBright: #ef5072; $colorOrangeBright: #ef9d50; $themeColor: #393747; $ingameHudBg: rgba(#333438, 0.9); +$modsColor: rgb(214, 60, 228); $text3dColor: #f4ffff; diff --git a/src/js/application.js b/src/js/application.js index 67e9c353..44207e4d 100644 --- a/src/js/application.js +++ b/src/js/application.js @@ -37,6 +37,7 @@ import { LoginState } from "./states/login"; import { WegameSplashState } from "./states/wegame_splash"; import { MODS } from "./mods/modloader"; import { MOD_SIGNALS } from "./mods/mod_signals"; +import { ModsState } from "./states/mods"; /** * @typedef {import("./platform/achievement_provider").AchievementProviderInterface} AchievementProviderInterface @@ -171,6 +172,7 @@ export class Application { ChangelogState, PuzzleMenuState, LoginState, + ModsState, ]; for (let i = 0; i < states.length; ++i) { diff --git a/src/js/mods/demo_mod.nobuild/index.js b/src/js/mods/demo_mod.nobuild/index.js index b636dace..2188defc 100644 --- a/src/js/mods/demo_mod.nobuild/index.js +++ b/src/js/mods/demo_mod.nobuild/index.js @@ -16,11 +16,12 @@ registerMod(shapez => { super( app, { - authorContact: "tobias@tobspr.io", - authorName: "tobspr", + website: "https://tobspr.io", + author: "tobspr", name: "Demo Mod", version: "1", id: "demo-mod", + description: "A simple mod to demonstrate the capatibilities of the mod loader.", }, modLoader ); @@ -28,11 +29,11 @@ registerMod(shapez => { init() { // Add some custom css - this.modInterface.registerCss(` - * { - font-family: "Comic Sans", "Comic Sans MS", Tahoma !important; - } - `); + // this.modInterface.registerCss(` + // * { + // font-family: "Comic Sans", "Comic Sans MS", Tahoma !important; + // } + // `); // Replace a builtin sprite ["red", "green", "blue", "yellow", "purple", "cyan", "white"].forEach(color => { diff --git a/src/js/mods/mod.js b/src/js/mods/mod.js index b9018b68..4e14bfe2 100644 --- a/src/js/mods/mod.js +++ b/src/js/mods/mod.js @@ -12,8 +12,9 @@ export class Mod { * @param {object} metadata * @param {string} metadata.name * @param {string} metadata.version - * @param {string} metadata.authorName - * @param {string} metadata.authorContact + * @param {string} metadata.author + * @param {string} metadata.website + * @param {string} metadata.description * @param {string} metadata.id * * @param {ModLoader} modLoader diff --git a/src/js/states/main_menu.js b/src/js/states/main_menu.js index a6bb940a..91142c3b 100644 --- a/src/js/states/main_menu.js +++ b/src/js/states/main_menu.js @@ -155,36 +155,22 @@ export class MainMenuState extends GameState { ? `
${T.mods.noModSupport}
+ + Get the shapez.io standalone! + + +