1
0
mirror of https://github.com/tobspr/shapez.io.git synced 2026-03-02 03:39:21 +00:00

Merge steam-demo branch

This commit is contained in:
tobspr
2022-06-06 14:06:09 +02:00
parent 09551fa0e5
commit 222ea8c261
56 changed files with 792 additions and 1081 deletions

View File

@@ -52,6 +52,11 @@
}
}
.playtimeDisclaimer {
@include S(margin-bottom, 10px);
@include PlainText;
}
.steamLinkButton {
@include IncreasedClickArea(5px);
@include S(margin, 0);

View File

@@ -1,10 +1,16 @@
#ingame_HUD_CatMemes {
#ingame_HUD_SteamCapsule {
position: absolute;
@include S(width, 150px);
@include S(height, 150px);
background: transparent center center / contain no-repeat;
@include S(height, 119px);
background: transparent center center / cover no-repeat;
right: 0;
pointer-events: all;
overflow: hidden;
@include S(right, 10px);
border: D(2px) solid #000;
@include S(border-radius, $globalBorderRadius);
cursor: pointer;
@include S(bottom, 150px);
& {
@@ -12,6 +18,10 @@
background-image: uiResource("res/ui/memes/cat1.png") !important;
}
&:hover {
opacity: 0.95;
}
@include InlineAnimation(0.5s ease-in-out) {
0% {
transform: translateX(100%);

View File

@@ -57,7 +57,7 @@
@import "ingame_hud/shape_viewer";
@import "ingame_hud/sandbox_controller";
@import "ingame_hud/standalone_advantages";
@import "ingame_hud/cat_memes";
@import "ingame_hud/steam_capsule";
@import "ingame_hud/puzzle_back_to_menu";
@import "ingame_hud/puzzle_editor_review";
@import "ingame_hud/puzzle_dlc_logo";
@@ -105,6 +105,7 @@ ingame_HUD_Waypoints_Hint,
ingame_HUD_WatermarkClicker,
ingame_HUD_Watermark,
ingame_HUD_ColorBlindBelowTileHelper,
ingame_HUD_SteamCapsule,
ingame_HUD_SandboxController,
// Overlays
@@ -118,8 +119,7 @@ ingame_HUD_StandaloneAdvantages,
ingame_HUD_UnlockNotification,
ingame_HUD_PuzzleCompleteNotification,
ingame_HUD_SettingsMenu,
ingame_HUD_ModalDialogs,
ingame_HUD_CatMemes;
ingame_HUD_ModalDialogs;
$zindex: 100;
@@ -132,7 +132,7 @@ $zindex: 100;
}
body.uiHidden {
> div {
> div:not(.ingameDialog):not(#ingame_HUD_SettingsMenu):not(#ingame_HUD_ModalDialogs):not(#ingame_HUD_UnlockNotification):not(#ingame_HUD_PuzzleCompleteNotification) {
display: none !important;
}
}

View File

@@ -97,7 +97,7 @@
}
.standaloneBanner {
background: rgb(216, 79, 76);
background: rgba(12, 168, 93, 0.957);
@include S(border-radius, $globalBorderRadius);
box-sizing: border-box;
@include S(padding, 15px);
@@ -129,8 +129,18 @@
@include S(padding-left, 20px);
li {
@include Text;
color: #fff;
}
}
strong {
margin: 0;
}
.playtimeDisclaimer {
color: #fff;
@include S(margin-top, 15px);
@include SuperSmallText;
}
.steamLink {
align-self: center;
@@ -153,6 +163,12 @@
opacity: 0.9;
}
@include InlineAnimation(1s ease-in-out infinite) {
50% {
transform: scale(1.02, 1.03);
}
}
> .discount {
position: absolute;
@include S(top, -7px);
@@ -186,9 +202,8 @@
img {
@include S(width, 300px);
}
position: relative;
@include S(left, -22px);
@include S(left, -8px);
.updateLabel {
position: absolute;
@@ -677,25 +692,19 @@
}
.footer {
display: grid;
display: flex;
flex-grow: 1;
justify-content: center;
align-items: flex-end;
width: 100%;
grid-template-columns: auto auto auto 1fr;
@include S(padding, 10px);
box-sizing: border-box;
@include S(grid-gap, 4px);
&.noLinks {
grid-template-columns: auto 1fr;
}
&.wegameDisclaimer {
@include SuperSmallText;
display: grid;
justify-content: center;
grid-template-columns: 1fr auto 1fr;
text-align: center;
> .disclaimer {

View File

@@ -47,6 +47,10 @@
align-self: end;
margin-top: auto;
&.noabout {
align-self: start;
}
@include StyleBelowWidth($layoutBreak) {
margin-top: 0;
display: grid;

View File

@@ -41,7 +41,6 @@ import { ModsState } from "./states/mods";
/**
* @typedef {import("./platform/achievement_provider").AchievementProviderInterface} AchievementProviderInterface
* @typedef {import("./platform/game_analytics").GameAnalyticsInterface} GameAnalyticsInterface
* @typedef {import("./platform/sound").SoundInterface} SoundInterface
* @typedef {import("./platform/storage").StorageInterface} StorageInterface
*/
@@ -118,7 +117,7 @@ export class Application {
/** @type {AnalyticsInterface} */
this.analytics = null;
/** @type {GameAnalyticsInterface} */
/** @type {ShapezGameAnalytics} */
this.gameAnalytics = null;
this.initPlatformDependentInstances();
@@ -227,12 +226,10 @@ export class Application {
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));
}
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);

View File

@@ -17,6 +17,10 @@ export function getLogoSprite() {
return "logo_wegame.png";
}
if (G_IS_STEAM_DEMO) {
return "logo_demo.png";
}
if (G_CHINA_VERSION) {
return "logo_cn.png";
}

View File

@@ -117,13 +117,7 @@ export const globalConfig = {
rendering: {},
debug: require("./config.local").default,
currentDiscount: {
amount: 50,
from: Date.parse("May 23 2022 17:00 +2:00"),
until: Date.parse("May 30 2022 23:59 +2:00"),
active: false, // computed later
},
currentDiscount: 0,
// Secret vars
info: {
@@ -169,8 +163,3 @@ if (G_IS_DEV && globalConfig.debug.noArtificialDelays) {
globalConfig.warmupTimeSecondsFast = 0;
globalConfig.warmupTimeSecondsRegular = 0;
}
globalConfig.currentDiscount.active =
!G_IS_STANDALONE &&
new Date().getTime() < globalConfig.currentDiscount.until &&
new Date().getTime() > globalConfig.currentDiscount.from;

View File

@@ -19,12 +19,10 @@ export function setGlobalApp(app) {
export const BUILD_OPTIONS = {
HAVE_ASSERT: G_HAVE_ASSERT,
APP_ENVIRONMENT: G_APP_ENVIRONMENT,
TRACKING_ENDPOINT: G_TRACKING_ENDPOINT,
CHINA_VERSION: G_CHINA_VERSION,
WEGAME_VERSION: G_WEGAME_VERSION,
IS_DEV: G_IS_DEV,
IS_RELEASE: G_IS_RELEASE,
IS_MOBILE_APP: G_IS_MOBILE_APP,
IS_BROWSER: G_IS_BROWSER,
IS_STANDALONE: G_IS_STANDALONE,
BUILD_TIME: G_BUILD_TIME,

View File

@@ -2,7 +2,6 @@
import { Application } from "../application";
/* typehints:end */
import { ExplainedResult } from "./explained_result";
import { queryParamOptions } from "./query_parameters";
import { ReadWriteProxy } from "./read_write_proxy";
export class RestrictionManager extends ReadWriteProxy {
@@ -56,13 +55,12 @@ export class RestrictionManager extends ReadWriteProxy {
* @returns {boolean}
*/
isLimitedVersion() {
if (G_IS_STANDALONE) {
// Standalone is never limited
return false;
if (G_IS_STEAM_DEMO) {
return true;
}
if (queryParamOptions.embedProvider === "gamedistribution") {
// also full version on gamedistribution
if (G_IS_STANDALONE) {
// Standalone is never limited
return false;
}

View File

@@ -2,29 +2,6 @@ import { T } from "../translations";
const bigNumberSuffixTranslationKeys = ["thousands", "millions", "billions", "trillions"];
/**
* Returns if this platform is android
* @returns {boolean}
*/
export function isAndroid() {
if (!G_IS_MOBILE_APP) {
return false;
}
const platform = window.device.platform;
return platform === "Android" || platform === "amazon-fireos";
}
/**
* Returns if this platform is iOs
* @returns {boolean}
*/
export function isIos() {
if (!G_IS_MOBILE_APP) {
return false;
}
return window.device.platform === "iOS";
}
/**
* Returns a platform name
* @returns {"android" | "browser" | "ios" | "standalone" | "unknown"}
@@ -34,10 +11,6 @@ export function getPlatformName() {
return "standalone";
} else if (G_IS_BROWSER) {
return "browser";
} else if (G_IS_MOBILE_APP && isAndroid()) {
return "android";
} else if (G_IS_MOBILE_APP && isIos()) {
return "ios";
}
return "unknown";
}
@@ -456,7 +429,7 @@ export function isSupportedBrowser() {
// and if not iOS Chrome check
// so use the below updated condition
if (G_IS_MOBILE_APP || G_IS_STANDALONE) {
if (G_IS_STANDALONE) {
return true;
}

View File

@@ -1,21 +0,0 @@
import { makeDiv } from "../../../core/utils";
import { BaseHUDPart } from "../base_hud_part";
import { DynamicDomAttach } from "../dynamic_dom_attach";
const memeShowIntervalSeconds = 70 * 60;
const memeShowDuration = 5;
export class HUDCatMemes extends BaseHUDPart {
createElements(parent) {
this.element = makeDiv(parent, "ingame_HUD_CatMemes");
}
initialize() {
this.domAttach = new DynamicDomAttach(this.root, this.element);
}
update() {
const now = this.root.time.realtimeNow();
this.domAttach.update(now % memeShowIntervalSeconds > memeShowIntervalSeconds - memeShowDuration);
}
}

View File

@@ -125,7 +125,11 @@ export class HUDModalDialogs extends BaseHUDPart {
dialog.buttonSignals.getStandalone.add(() => {
this.app.analytics.trackUiClick("demo_dialog_click");
window.open(THIRDPARTY_URLS.stanaloneCampaignLink + "/shapez_demo_dialog");
window.open(
THIRDPARTY_URLS.stanaloneCampaignLink +
"/shapez_demo_dialog" +
(G_IS_STEAM_DEMO ? "_steamdemo" : "")
);
});
return dialog.buttonSignals;

View File

@@ -5,8 +5,6 @@ import { T } from "../../../translations";
import { BaseHUDPart } from "../base_hud_part";
import { DynamicDomAttach } from "../dynamic_dom_attach";
const showIntervalSeconds = 9 * 60;
export class HUDStandaloneAdvantages extends BaseHUDPart {
createElements(parent) {
this.background = makeDiv(parent, "ingame_HUD_StandaloneAdvantages", ["ingameDialog"]);
@@ -33,10 +31,11 @@ export class HUDStandaloneAdvantages extends BaseHUDPart {
</div>
<div class="lowerBar">
<div class="playtimeDisclaimer">${T.demoBanners.playtimeDisclaimer}</div>
<button class="steamLinkButton ${A_B_TESTING_LINK_TYPE}">
${
globalConfig.currentDiscount.active
? `<span class='discount'>${globalConfig.currentDiscount.amount}% off!</span>`
globalConfig.currentDiscount > 0
? `<span class='discount'>${globalConfig.currentDiscount}% off!</span>`
: ""
}
</button>
@@ -46,13 +45,15 @@ export class HUDStandaloneAdvantages extends BaseHUDPart {
);
this.trackClicks(this.contentDiv.querySelector("button.steamLinkButton"), () => {
const discount = globalConfig.currentDiscount.active
? "_discount" + globalConfig.currentDiscount.amount
: "";
const discount =
globalConfig.currentDiscount > 0 ? "_discount" + globalConfig.currentDiscount : "";
this.root.app.analytics.trackUiClick("standalone_advantage_visit_steam");
this.root.app.platformWrapper.openExternalLink(
THIRDPARTY_URLS.stanaloneCampaignLink + "/shapez_std_advg" + discount
THIRDPARTY_URLS.stanaloneCampaignLink +
"/shapez_std_advg" +
discount +
(G_IS_STEAM_DEMO ? "_steamdemo" : "")
);
this.close();
});
@@ -62,6 +63,22 @@ export class HUDStandaloneAdvantages extends BaseHUDPart {
});
}
get showIntervalSeconds() {
switch (this.root.app.gameAnalytics.abtVariant) {
case "0":
return 5 * 60;
case "1":
return 10 * 60;
case "2":
default:
return 15 * 60;
case "3":
return 20 * 60;
case "4":
return 1e14;
}
}
initialize() {
this.domAttach = new DynamicDomAttach(this.root, this.background, {
attachClass: "visible",
@@ -86,7 +103,7 @@ export class HUDStandaloneAdvantages extends BaseHUDPart {
}
update() {
if (!this.visible && this.root.time.now() - this.lastShown > showIntervalSeconds) {
if (!this.visible && this.root.time.now() - this.lastShown > this.showIntervalSeconds) {
this.show();
}

View File

@@ -0,0 +1,31 @@
import { globalConfig, THIRDPARTY_URLS } from "../../../core/config";
import { makeDiv } from "../../../core/utils";
import { BaseHUDPart } from "../base_hud_part";
import { DynamicDomAttach } from "../dynamic_dom_attach";
const showCapsuleAfter = 30 * 60;
export class HUDSteamCapsule extends BaseHUDPart {
createElements(parent) {
this.element = makeDiv(parent, "ingame_HUD_SteamCapsule");
}
initialize() {
const discount = globalConfig.currentDiscount > 0 ? "_discount" + globalConfig.currentDiscount : "";
this.domAttach = new DynamicDomAttach(this.root, this.element);
this.trackClicks(this.element, () => {
this.root.app.platformWrapper.openExternalLink(
THIRDPARTY_URLS.stanaloneCampaignLink +
"/shapez_steamcapsule" +
discount +
(G_IS_STEAM_DEMO ? "_steamdemo" : "")
);
});
}
update() {
this.domAttach.update(this.root.time.now() > showCapsuleAfter);
}
}

View File

@@ -75,7 +75,7 @@ export class HUDUnlockNotification extends BaseHUDPart {
<div class="rewardName">
${T.ingame.levelCompleteNotification.unlockText.replace("<reward>", rewardName)}
</div>
<div class="rewardDesc">
${T.storyRewards[reward].desc}
</div>
@@ -131,6 +131,13 @@ export class HUDUnlockNotification extends BaseHUDPart {
this.root.hud.signals.unlockNotificationFinished.dispatch();
if (
this.root.hubGoals.level === 7 &&
this.root.app.restrictionMgr.getIsStandaloneMarketingActive()
) {
this.root.hud.parts.standaloneAdvantages.show();
}
if (!this.root.app.settings.getAllSettings().offerHints) {
return;
}

View File

@@ -2,66 +2,35 @@ import { globalConfig, THIRDPARTY_URLS } from "../../../core/config";
import { makeDiv } from "../../../core/utils";
import { T } from "../../../translations";
import { BaseHUDPart } from "../base_hud_part";
import { DynamicDomAttach } from "../dynamic_dom_attach";
const watermarkShowIntervalSeconds = G_IS_DEV ? 120 : 7 * 60;
const watermarkShowDuration = 5;
export class HUDWatermark extends BaseHUDPart {
createElements(parent) {
this.element = makeDiv(
parent,
"ingame_HUD_Watermark",
[],
`
<strong>${T.ingame.watermark.title}</strong>
<p>${T.ingame.watermark.desc}
</p>
`
);
this.linkElement = makeDiv(
parent,
"ingame_HUD_WatermarkClicker",
globalConfig.currentDiscount.active ? ["withDiscount"] : [],
globalConfig.currentDiscount > 0 ? ["withDiscount"] : [],
T.ingame.watermark.get_on_steam +
(globalConfig.currentDiscount.active
? `<span class='discount'>${globalConfig.currentDiscount.amount}% off!</span>`
(globalConfig.currentDiscount > 0
? `<span class='discount'>${globalConfig.currentDiscount}% off!</span>`
: "")
);
this.trackClicks(this.linkElement, () => {
this.root.app.analytics.trackUiClick("watermark_click_2_direct");
const discount = globalConfig.currentDiscount.active
? "_discount" + globalConfig.currentDiscount.amount
: "";
const discount =
globalConfig.currentDiscount > 0 ? "_discount" + globalConfig.currentDiscount : "";
this.root.app.platformWrapper.openExternalLink(
THIRDPARTY_URLS.stanaloneCampaignLink + "/shapez_watermark" + discount
THIRDPARTY_URLS.stanaloneCampaignLink +
"/shapez_watermark" +
discount +
(G_IS_STEAM_DEMO ? "_steamdemo" : "")
);
});
}
initialize() {
this.trackClicks(this.element, this.onWatermarkClick);
initialize() {}
this.domAttach = new DynamicDomAttach(this.root, this.element, {
attachClass: "visible",
timeToKeepSeconds: 0.5,
});
}
update() {
this.domAttach.update(
this.root.time.realtimeNow() % watermarkShowIntervalSeconds < watermarkShowDuration
);
}
onWatermarkClick() {
this.root.app.analytics.trackUiClick("watermark_click_2_new");
this.root.hud.parts.standaloneAdvantages.show();
}
update() {}
/**
*
@@ -70,7 +39,7 @@ export class HUDWatermark extends BaseHUDPart {
drawOverlays(parameters) {
const w = this.root.gameWidth;
parameters.context.fillStyle = "rgba(230, 230, 230, 0.9)";
parameters.context.fillStyle = "rgba(20, 30, 40, 0.25)";
parameters.context.font = "bold " + this.root.app.getEffectiveUiScale() * 40 + "px GameFont";
parameters.context.textAlign = "center";
parameters.context.fillText(

View File

@@ -31,11 +31,9 @@ import { IS_MOBILE } from "../../core/config";
import { HUDKeybindingOverlay } from "../hud/parts/keybinding_overlay";
import { HUDWatermark } from "../hud/parts/watermark";
import { HUDStandaloneAdvantages } from "../hud/parts/standalone_advantages";
import { HUDCatMemes } from "../hud/parts/cat_memes";
import { HUDSteamCapsule } from "../hud/parts/steam_capsule";
import { HUDPartTutorialHints } from "../hud/parts/tutorial_hints";
import { HUDInteractiveTutorial } from "../hud/parts/interactive_tutorial";
import { HUDSandboxController } from "../hud/parts/sandbox_controller";
import { queryParamOptions } from "../../core/query_parameters";
import { MetaBlockBuilding } from "../buildings/block";
import { MetaItemProducerBuilding } from "../buildings/item_producer";
import { MOD_SIGNALS } from "../../mods/mod_signals";
@@ -584,7 +582,7 @@ export class RegularGameMode extends GameMode {
if (this.root.app.restrictionMgr.getIsStandaloneMarketingActive()) {
this.additionalHudParts.watermark = HUDWatermark;
this.additionalHudParts.standaloneAdvantages = HUDStandaloneAdvantages;
this.additionalHudParts.catMemes = HUDCatMemes;
this.additionalHudParts.catMemes = HUDSteamCapsule;
}
if (this.root.app.settings.getAllSettings().offerHints) {

3
src/js/globals.d.ts vendored
View File

@@ -10,11 +10,10 @@ declare const G_APP_ENVIRONMENT: string;
declare const G_HAVE_ASSERT: boolean;
declare const G_BUILD_TIME: number;
declare const G_IS_STANDALONE: boolean;
declare const G_IS_STEAM_DEMO: boolean;
declare const G_IS_BROWSER: boolean;
declare const G_IS_MOBILE_APP: boolean;
declare const G_BUILD_COMMIT_HASH: string;
declare const G_TRACKING_ENDPOINT: string;
declare const G_BUILD_VERSION: string;
declare const G_ALL_UI_IMAGES: Array<string>;
declare const G_IS_RELEASE: boolean;

View File

@@ -105,6 +105,10 @@ export class ModLoader {
}
exposeExports() {
if (G_IS_STEAM_DEMO) {
return;
}
if (G_IS_DEV || G_IS_STANDALONE) {
let exports = {};
const modules = require.context("../", true, /\.js$/);
@@ -136,6 +140,11 @@ export class ModLoader {
}
async initMods() {
if (G_IS_STEAM_DEMO) {
this.initialized = true;
return;
}
if (!G_IS_STANDALONE && !G_IS_DEV) {
this.initialized = true;
return;

View File

@@ -1,5 +1,6 @@
import { globalConfig } from "../../core/config";
import { createLogger } from "../../core/logging";
import { randomInt } from "../../core/utils";
import { BeltComponent } from "../../game/components/belt";
import { StaticMapEntityComponent } from "../../game/components/static_map_entity";
import { RegularGameMode } from "../../game/modes/regular";
@@ -13,16 +14,26 @@ const logger = createLogger("game_analytics");
const analyticsUrl = G_IS_DEV ? "http://localhost:8001" : "https://analytics.shapez.io";
// Be sure to increment the ID whenever it changes to make sure all
// users are tracked
const analyticsLocalFile = "shapez_token_123.bin";
// Be sure to increment the ID whenever it changes
const analyticsLocalFile = G_IS_STEAM_DEMO ? "shapez_token_steamdemo.bin" : "shapez_token_123.bin";
const currentABT = "abt_sa_si";
export class ShapezGameAnalytics extends GameAnalyticsInterface {
constructor(app) {
super(app);
this.abtVariant = "0";
}
get environment() {
if (G_IS_DEV) {
return "dev";
}
if (G_IS_STEAM_DEMO) {
return "steam-demo";
}
if (G_IS_STANDALONE) {
return "steam";
}
@@ -38,6 +49,22 @@ export class ShapezGameAnalytics extends GameAnalyticsInterface {
}
}
fetchABVariant() {
return this.app.storage.readFileAsync("shapez_" + currentABT + ".bin").then(
abt => {
this.abtVariant = abt;
logger.log("Got abtVariant:", abt);
},
err => {
if (err === FILE_NOT_FOUND) {
this.abtVariant = String(randomInt(0, 4));
logger.log("Determing abt variant to", this.abtVariant);
this.app.storage.writeFileAsync("shapez_" + currentABT + ".bin", this.abtVariant);
}
}
);
}
/**
* @returns {Promise<void>}
*/
@@ -48,46 +75,68 @@ export class ShapezGameAnalytics extends GameAnalyticsInterface {
return;
}
setInterval(() => this.sendTimePoints(), 60 * 1000);
// Retrieve sync key from player
return this.app.storage.readFileAsync(analyticsLocalFile).then(
syncKey => {
this.syncKey = syncKey;
logger.log("Player sync key read:", this.syncKey);
},
error => {
// File was not found, retrieve new key
if (error === FILE_NOT_FOUND) {
logger.log("Retrieving new player key");
return this.fetchABVariant().then(() => {
setInterval(() => this.sendTimePoints(), 60 * 1000);
// Perform call to get a new key from the API
this.sendToApi("/v1/register", {
environment: this.environment,
standalone:
G_IS_STANDALONE &&
this.app.achievementProvider instanceof SteamAchievementProvider,
commit: G_BUILD_COMMIT_HASH,
})
.then(res => {
// Try to read and parse the key from the api
if (res.key && typeof res.key === "string" && res.key.length === 40) {
this.syncKey = res.key;
logger.log("Key retrieved:", this.syncKey);
this.app.storage.writeFileAsync(analyticsLocalFile, res.key);
} else {
throw new Error("Bad response from analytics server: " + res);
}
})
.catch(err => {
logger.error("Failed to register on analytics api:", err);
});
} else {
logger.error("Failed to read ga key:", error);
}
return;
if (this.app.restrictionMgr.isLimitedVersion()) {
fetch(
analyticsUrl +
"/track/shapez_launch_" +
this.environment +
"_" +
currentABT +
"_" +
this.abtVariant,
{
method: "GET",
mode: "no-cors",
cache: "no-cache",
referrer: "no-referrer",
credentials: "omit",
}
).catch(err => {});
}
);
return this.app.storage.readFileAsync(analyticsLocalFile).then(
syncKey => {
this.syncKey = syncKey;
logger.log("Player sync key read:", this.syncKey);
},
error => {
// File was not found, retrieve new key
if (error === FILE_NOT_FOUND) {
logger.log("Retrieving new player key");
// Perform call to get a new key from the API
this.sendToApi("/v1/register", {
environment: this.environment,
standalone:
G_IS_STANDALONE &&
!G_IS_STEAM_DEMO &&
this.app.achievementProvider instanceof SteamAchievementProvider,
commit: G_BUILD_COMMIT_HASH,
})
.then(res => {
// Try to read and parse the key from the api
if (res.key && typeof res.key === "string" && res.key.length === 40) {
this.syncKey = res.key;
logger.log("Key retrieved:", this.syncKey);
this.app.storage.writeFileAsync(analyticsLocalFile, res.key);
} else {
throw new Error("Bad response from analytics server: " + res);
}
})
.catch(err => {
logger.error("Failed to register on analytics api:", err);
});
} else {
logger.error("Failed to read ga key:", error);
}
return;
}
);
});
}
/**

View File

@@ -1,7 +0,0 @@
import { GameAnalyticsInterface } from "../game_analytics";
export class NoGameAnalytics extends GameAnalyticsInterface {
initialize() {
return Promise.resolve();
}
}

View File

@@ -189,7 +189,7 @@ function initializeSettings() {
},
/**
* @param {Application} app
*/ app => app.restrictionMgr.getHasExtendedSettings()
*/ app => G_IS_STANDALONE
),
new BoolSetting(
@@ -514,7 +514,7 @@ export class ApplicationSettings extends ReadWriteProxy {
const settings = data.settings;
// MODS
if (!THEMES[settings.theme]) {
if (!THEMES[settings.theme] || !this.app.restrictionMgr.getHasExtendedSettings()) {
console.warn("Resetting theme because its no longer available: " + settings.theme);
settings.theme = "light";
}
@@ -700,7 +700,7 @@ export class ApplicationSettings extends ReadWriteProxy {
}
// MODS
if (!THEMES[data.settings.theme]) {
if (!THEMES[data.settings.theme] || !this.app.restrictionMgr.getHasExtendedSettings()) {
console.warn("Resetting theme because its no longer available: " + data.settings.theme);
data.settings.theme = "light";
}

View File

@@ -38,9 +38,8 @@ export class MainMenuState extends GameState {
getInnerHTML() {
const showLanguageIcon = !G_CHINA_VERSION && !G_WEGAME_VERSION;
const showExitAppButton = G_IS_STANDALONE;
const showUpdateLabel = !G_WEGAME_VERSION;
const showBrowserWarning = !G_IS_STANDALONE && !isSupportedBrowser();
const showPuzzleDLC = !G_WEGAME_VERSION && G_IS_STANDALONE;
const showPuzzleDLC = !G_WEGAME_VERSION && G_IS_STANDALONE && !G_IS_STEAM_DEMO;
const showWegameFooter = G_WEGAME_VERSION;
const hasMods = MODS.anyModsActive();
@@ -69,15 +68,17 @@ export class MainMenuState extends GameState {
const ownsPuzzleDLC =
G_IS_DEV ||
(G_IS_STANDALONE &&
!G_IS_STEAM_DEMO &&
/** @type { PlatformWrapperImplElectron}*/ (this.app.platformWrapper).dlcs.puzzle);
const bannerHtml = `
<h3>${T.demoBanners.title}</h3>
<p>${T.demoBanners.intro}</p>
<span class="playtimeDisclaimer">${T.demoBanners.playtimeDisclaimer}</span>
<a href="#" class="steamLink ${A_B_TESTING_LINK_TYPE}" target="_blank">
${
globalConfig.currentDiscount.active
? `<span class='discount'>${globalConfig.currentDiscount.amount}% off!</span>`
globalConfig.currentDiscount > 0
? `<span class='discount'>${globalConfig.currentDiscount}% off!</span>`
: ""
}
@@ -201,7 +202,7 @@ export class MainMenuState extends GameState {
<div class="footer ${showExternalLinks ? "" : "noLinks"} ">
${
showExternalLinks
showExternalLinks && !G_IS_STEAM_DEMO
? `
<a class="githubLink boxLink" target="_blank">
${T.mainMenu.openSourceHint}
@@ -453,11 +454,12 @@ export class MainMenuState extends GameState {
onSteamLinkClicked() {
this.app.analytics.trackUiClick("main_menu_steam_link_" + A_B_TESTING_LINK_TYPE);
const discount = globalConfig.currentDiscount.active
? "_discount" + globalConfig.currentDiscount.amount
: "";
const discount = globalConfig.currentDiscount > 0 ? "_discount" + globalConfig.currentDiscount : "";
this.app.platformWrapper.openExternalLink(
THIRDPARTY_URLS.stanaloneCampaignLink + "/shapez_mainmenu" + discount
THIRDPARTY_URLS.stanaloneCampaignLink +
"/shapez_mainmenu" +
discount +
(G_IS_STEAM_DEMO ? "_steamdemo" : "")
);
return false;
@@ -743,7 +745,9 @@ export class MainMenuState extends GameState {
getStandalone.add(() => {
this.app.analytics.trackUiClick("visit_steampage_from_slot_limit");
this.app.platformWrapper.openExternalLink(
THIRDPARTY_URLS.stanaloneCampaignLink + "/shapez_slotlimit"
THIRDPARTY_URLS.stanaloneCampaignLink +
"/shapez_slotlimit" +
(G_IS_STEAM_DEMO ? "_steamdemo" : "")
);
});
}

View File

@@ -3,6 +3,8 @@ import { TextualGameState } from "../core/textual_game_state";
import { MODS } from "../mods/modloader";
import { T } from "../translations";
const MODS_SUPPORTED = !G_IS_STEAM_DEMO && (G_IS_STANDALONE || G_IS_DEV);
export class ModsState extends TextualGameState {
constructor() {
super("ModsState");
@@ -19,12 +21,12 @@ export class ModsState extends TextualGameState {
<div class="actions">
${
(G_IS_STANDALONE || G_IS_DEV) && MODS.mods.length > 0
MODS_SUPPORTED && MODS.mods.length > 0
? `<button class="styledButton browseMods">${T.mods.browseMods}</button>`
: ""
}
${
G_IS_STANDALONE || G_IS_DEV
MODS_SUPPORTED
? `<button class="styledButton openModsFolder">${T.mods.openFolder}</button>`
: ""
}
@@ -41,7 +43,7 @@ export class ModsState extends TextualGameState {
}
getMainContentHTML() {
if (!G_IS_STANDALONE && !G_IS_DEV) {
if (!MODS_SUPPORTED) {
return `
<div class="noModSupport">
@@ -137,7 +139,9 @@ export class ModsState extends TextualGameState {
onSteamLinkClicked() {
this.app.analytics.trackUiClick("mods_steam_link");
this.app.platformWrapper.openExternalLink(
THIRDPARTY_URLS.stanaloneCampaignLink + "/shapez_modsettings"
THIRDPARTY_URLS.stanaloneCampaignLink +
"/shapez_modsettings" +
(G_IS_STEAM_DEMO ? "_steamdemo" : "")
);
return false;

View File

@@ -61,6 +61,26 @@ export class PreloadState extends GameState {
this.startLoading();
}
async fetchDiscounts() {
await Promise.race([
new Promise((resolve, reject) => {
setTimeout(() => {
reject("Failed to resolve steam discounts within timeout");
}, 2000);
}),
fetch("https://analytics.shapez.io/v1/discounts")
.then(res => res.json())
.then(data => {
globalConfig.currentDiscount = Number(
data["1318690"].data.price_overview.discount_percent
);
logger.log("Fetched current discount:", globalConfig.currentDiscount);
}),
]).catch(err => {
logger.warn("Failed to fetch current discount:", err);
});
}
onLeave() {
// this.dialogs.cleanup();
}
@@ -101,6 +121,9 @@ export class PreloadState extends GameState {
.then(() => this.app.analytics.initialize())
.then(() => this.app.gameAnalytics.initialize())
.then(() => this.setStatus("Connecting to api"))
.then(() => this.fetchDiscounts())
.then(() => this.setStatus("Initializing settings"))
.then(() => {
return this.app.settings.initialize();

View File

@@ -36,13 +36,11 @@ export class SettingsState extends TextualGameState {
: `
<button class="styledButton categoryButton manageMods">${T.mods.title}
<span class="newBadge">${T.settings.newBadge}</span>
</button>
`
</button>`
}
<div class="other">
<div class="other ${G_CHINA_VERSION || G_WEGAME_VERSION ? "noabout" : ""}">
${
G_CHINA_VERSION || G_WEGAME_VERSION