mirror of
https://github.com/IvarK/AntimatterDimensionsSourceCode.git
synced 2024-11-10 06:02:13 +00:00
Restructure Steam and Electron code
This commit is contained in:
parent
7ec351dd2b
commit
c33e34d50d
@ -1,4 +1,4 @@
|
||||
public/**/*.js
|
||||
src/components/SliderComponent.vue
|
||||
javascripts/supported-browsers.js
|
||||
src/steam/PlayFabClientApi.js
|
||||
src/steam/bindings/PlayFabClientApi.js
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { SteamRuntime } from "@/steam";
|
||||
import { GameMechanicState } from "../game-mechanics/index";
|
||||
|
||||
class AchievementState extends GameMechanicState {
|
||||
@ -70,7 +71,7 @@ class AchievementState extends GameMechanicState {
|
||||
GameUI.notify.reality(`Automatically unlocked: ${this.name}`);
|
||||
} else {
|
||||
GameUI.notify.success(`Achievement: ${this.name}`);
|
||||
SteamFunctions.GiveAchievement(this.id);
|
||||
SteamRuntime.activateAchievement(this.id);
|
||||
}
|
||||
if (player.speedrun.isActive && !player.speedrun.achievementTimes[this.id]) {
|
||||
// This stores a lot of data in the savefile and seems particularly suceptible to floating-point rounding issues
|
||||
@ -173,6 +174,12 @@ export const Achievements = {
|
||||
get power() {
|
||||
if (Pelle.isDisabled("achievementMult")) return 1;
|
||||
return Achievements._power.value;
|
||||
},
|
||||
|
||||
updateSteamStatus() {
|
||||
for (const achievement in Achievements.all.filter(x => x.isUnlocked)) {
|
||||
SteamRuntime.activateAchievement(achievement.id);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { ElectronRuntime } from "@/steam";
|
||||
import { sha512_256 } from "js-sha512";
|
||||
|
||||
import { DEV } from "@/env";
|
||||
@ -18,7 +19,7 @@ export class GameOptions {
|
||||
// This is needed because .s-base--dark is on newUI/normal but not on oldUI/normal
|
||||
// So the classes on body need to be updated
|
||||
Themes.find(Theme.currentName()).set();
|
||||
SteamFunctions.UIZoom()
|
||||
ElectronRuntime.updateZoom();
|
||||
GameStorage.save();
|
||||
}
|
||||
|
||||
|
@ -46,3 +46,7 @@ window.onerror = (event, source) => {
|
||||
if (!source.endsWith(".js")) return;
|
||||
GlobalErrorHandler.onerror(event);
|
||||
};
|
||||
|
||||
window.addEventListener("unhandledrejection", event => {
|
||||
GlobalErrorHandler.onerror(event);
|
||||
});
|
||||
|
@ -1,14 +1,15 @@
|
||||
import { discordRichPresence } from "./secret-formula/discord-rich-presence";
|
||||
|
||||
export const RichPresenceInfo = {
|
||||
get gameStage() {
|
||||
const stageDB = GameDatabase.discordRichPresence.stages;
|
||||
const stageDB = discordRichPresence.stages;
|
||||
for (let stage = stageDB.length - 1; stage >= 0; stage--) {
|
||||
if (stageDB[stage].hasReached()) return stageDB[stage];
|
||||
}
|
||||
throw Error("No valid progress stage found");
|
||||
},
|
||||
get challengeState() {
|
||||
const challDB = GameDatabase.discordRichPresence.challenges;
|
||||
const challDB = discordRichPresence.challenges;
|
||||
for (let index = 0; index < challDB.length; index++) {
|
||||
const chall = challDB[index];
|
||||
if (chall.activityToken()) return chall;
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { ElectronRuntime } from "@/steam";
|
||||
import Mousetrap from "mousetrap";
|
||||
|
||||
import { GameKeyboard } from "./keyboard";
|
||||
@ -280,28 +281,28 @@ export const shortcuts = [
|
||||
name: "Zoom In",
|
||||
keys: ["ctrl", "="],
|
||||
type: "bind",
|
||||
function: () => {SteamFunctions.SetZoomLevel("Increase")},
|
||||
function: () => ElectronRuntime.increaseZoom(),
|
||||
visible: () => false
|
||||
},
|
||||
{
|
||||
name: "Zoom In",
|
||||
keys: ["ctrl", "+"],
|
||||
type: "bind",
|
||||
function: () => {SteamFunctions.SetZoomLevel("Increase")},
|
||||
function: () => ElectronRuntime.increaseZoom(),
|
||||
visible: () => false
|
||||
},
|
||||
{
|
||||
name: "Zoom Out",
|
||||
keys: ["ctrl", "-"],
|
||||
type: "bind",
|
||||
function: () => {SteamFunctions.SetZoomLevel("Decrease")},
|
||||
function: () => ElectronRuntime.decreaseZoom(),
|
||||
visible: () => false
|
||||
},
|
||||
{
|
||||
name: "Reset Zoom",
|
||||
keys: ["ctrl", "0"],
|
||||
type: "bind",
|
||||
function: () => {SteamFunctions.ResetZoom()},
|
||||
function: () => ElectronRuntime.resetZoom(),
|
||||
visible: () => false
|
||||
},
|
||||
];
|
||||
|
@ -90,7 +90,7 @@ const Payments = {
|
||||
},
|
||||
|
||||
// Sends a request to purchase a STD upgrade, returning true if successful (and syncs data), false if not
|
||||
async buyUpgrade(upgradeKey) {
|
||||
async buyUpgrade(upgradeKey, cosmeticName) {
|
||||
if (!Cloud.loggedIn) return false;
|
||||
let res;
|
||||
try {
|
||||
@ -103,7 +103,7 @@ const Payments = {
|
||||
user: Cloud.user.id,
|
||||
upgrade: upgradeKey,
|
||||
extraData: {
|
||||
requestedSet: GlyphAppearanceHandler.chosenFromModal?.id,
|
||||
requestedSet: cosmeticName,
|
||||
fullCompletions: player.records.fullGameCompletions
|
||||
}
|
||||
})
|
||||
|
@ -1,42 +0,0 @@
|
||||
import { PlayFab } from "@/steam/PlayFabClientApi";
|
||||
|
||||
export function playFabLogin() {
|
||||
try {
|
||||
Steam.getAuthSessionTicket(ticket => {
|
||||
const SteamTicket = ticket.ticket.toString("hex");
|
||||
PlayFab.settings.titleId = "59813";
|
||||
const requestData = {
|
||||
TitleId: PlayFab.settings.titleId,
|
||||
SteamTicket,
|
||||
CreateAccount: true
|
||||
};
|
||||
try {
|
||||
PlayFab.ClientApi.LoginWithSteam(requestData, playFabLoginCallback);
|
||||
} catch (e) {
|
||||
console.log("Unable to send login request to PlayFab.");
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
|
||||
PlayFab.settings.titleId = "59813";
|
||||
let playFabId = -1;
|
||||
|
||||
function playFabLoginCallback(data, error) {
|
||||
if (error) {
|
||||
console.log(error.errorMessage);
|
||||
GameUI.notify.error("Couldn't log in to PlayFab Cloud.");
|
||||
return;
|
||||
}
|
||||
if (data) {
|
||||
playFabId = data.data.PlayFabId;
|
||||
PlayFab.PlayFabID = playFabId;
|
||||
GameUI.notify.info("Logged in to PlayFab Cloud");
|
||||
PlayFab.ClientApi.UpdateUserTitleDisplayName({ "DisplayName": Steam.getSteamId().screenName });
|
||||
|
||||
console.log("Logged in to playFab");
|
||||
SteamFunctions.autoLogin();
|
||||
}
|
||||
}
|
@ -21,7 +21,7 @@ function formatMachines(realPart, imagPart) {
|
||||
|
||||
// This is used for Discord Rich Presence, the information which shows up on a person's profile badge in Discord if
|
||||
// they are playing a game on Steam which has integration that pushes the info to Discord
|
||||
GameDatabase.discordRichPresence = {
|
||||
export const discordRichPresence = {
|
||||
/**
|
||||
* List of all challenges to display within DRP, checked from the first entry and iterating forward. It will only
|
||||
* show the first one it finds for space reasons, but this also has the desirable effect of hiding key challenges
|
||||
@ -261,3 +261,5 @@ GameDatabase.discordRichPresence = {
|
||||
},
|
||||
]
|
||||
};
|
||||
|
||||
GameDatabase.discordRichPresence = discordRichPresence;
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { SteamRuntime } from "@/steam";
|
||||
import { RebuyableMechanicState } from "./game-mechanics/index";
|
||||
|
||||
import Payments from "./payments";
|
||||
@ -177,8 +178,14 @@ class ShopPurchaseState extends RebuyableMechanicState {
|
||||
if (GameEnd.creditsEverClosed) return false;
|
||||
if (this.config.instantPurchase && ui.$viewModel.modal.progressBar) return false;
|
||||
|
||||
// Contact the firebase server to verify the purchase
|
||||
const success = true//await Payments.buyUpgrade(this.config.key);
|
||||
const cosmeticName = this.config.key === "singleCosmeticSet"
|
||||
? GlyphAppearanceHandler.chosenFromModal?.name
|
||||
: undefined;
|
||||
|
||||
// Contact the purchase provider to verify the purchase
|
||||
const success = SteamRuntime.isActive
|
||||
? await SteamRuntime.purchaseShopItem(this.cost, this.config.key, cosmeticName)
|
||||
: await Payments.buyUpgrade(this.config.key, cosmeticName);
|
||||
if (!success) return false;
|
||||
|
||||
if (player.IAP.enabled) Speedrun.setSTDUse(true);
|
||||
|
@ -1,4 +1,5 @@
|
||||
/* eslint-disable import/extensions */
|
||||
import { SteamRuntime } from "@/steam";
|
||||
import pako from "pako/dist/pako.esm.mjs";
|
||||
/* eslint-enable import/extensions */
|
||||
|
||||
@ -52,6 +53,29 @@ export const Cloud = {
|
||||
}
|
||||
},
|
||||
|
||||
async loginWithSteam(accountId, staticAccountId, screenName) {
|
||||
if (this.loggedIn) {
|
||||
Cloud.user.displayName = screenName;
|
||||
return true;
|
||||
}
|
||||
|
||||
const email = `${accountId}@ad.com`;
|
||||
const pass = staticAccountId;
|
||||
try {
|
||||
await Cloud.manualCloudCreate(email, pass);
|
||||
} catch {
|
||||
try {
|
||||
await Cloud.manualCloudLogin(email, pass);
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`Firebase Login Error: ${error}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Cloud.user.displayName = screenName;
|
||||
return true;
|
||||
},
|
||||
|
||||
async manualCloudLogin(EmailAddress,Password) {
|
||||
//try{
|
||||
@ -234,16 +258,15 @@ export const Cloud = {
|
||||
if (user) {
|
||||
this.user = {
|
||||
id: user.uid,
|
||||
displayName: steamOn ? Steam.getSteamId().screenName : "",//user.displayName,
|
||||
displayName: SteamRuntime.isActive
|
||||
? SteamRuntime.screenName
|
||||
: user.displayName,
|
||||
email: user.email,
|
||||
};
|
||||
SteamFunctions.SyncPlayFabSTD()
|
||||
SteamRuntime.syncIap();
|
||||
} else {
|
||||
this.user = null;
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
Cloud.init();
|
||||
//this.user.displayName = Steam.getSteamId().screenName
|
||||
|
@ -1,8 +1,10 @@
|
||||
import { SteamRuntime } from "@/steam";
|
||||
import * as ADNotations from "@antimatter-dimensions/notations";
|
||||
|
||||
import { DEV } from "@/env";
|
||||
|
||||
import { deepmergeAll } from "@/utility/deepmerge";
|
||||
import { Achievement } from "../achievements/normal-achievement";
|
||||
|
||||
export const GameStorage = {
|
||||
currentSlot: 0,
|
||||
@ -30,7 +32,7 @@ export const GameStorage = {
|
||||
const root = GameSaveSerializer.deserialize(save);
|
||||
|
||||
this.loadRoot(root);
|
||||
SteamFunctions.BackfillAchievements()
|
||||
Achievements.updateSteamStatus();
|
||||
},
|
||||
|
||||
loadRoot(root) {
|
||||
@ -66,7 +68,7 @@ export const GameStorage = {
|
||||
Tabs.all.find(t => t.id === player.options.lastOpenTab).show(true);
|
||||
Cloud.resetTempState();
|
||||
GameUI.notify.info("Game loaded");
|
||||
SteamFunctions.BackfillAchievements()
|
||||
Achievements.updateSteamStatus();
|
||||
},
|
||||
|
||||
import(saveData) {
|
||||
@ -93,7 +95,7 @@ export const GameStorage = {
|
||||
// is showing
|
||||
Tab.options.subtabs[0].show();
|
||||
GameUI.notify.info("Game imported");
|
||||
SteamFunctions.BackfillAchievements()
|
||||
Achievements.updateSteamStatus();
|
||||
},
|
||||
|
||||
importAsFile() {
|
||||
|
@ -1,10 +1,11 @@
|
||||
import { ElectronRuntime, SteamRuntime } from "@/steam";
|
||||
import TWEEN from "tween.js";
|
||||
|
||||
import { DC } from "./core/constants";
|
||||
import { deepmergeAll } from "@/utility/deepmerge";
|
||||
import { playFabLogin } from "./core/playfab";
|
||||
import { DEV } from "@/env";
|
||||
import { SpeedrunMilestones } from "./core/speedrun";
|
||||
import { Cloud } from "./core/storage";
|
||||
import { supportedBrowsers } from "./supported-browsers";
|
||||
|
||||
import Payments from "./core/payments";
|
||||
@ -1022,12 +1023,6 @@ window.onload = function() {
|
||||
GameUI.initialized = supportedBrowser;
|
||||
ui.view.initialized = supportedBrowser;
|
||||
setTimeout(() => {
|
||||
if(Steam){
|
||||
if(Steam.initAPI()){
|
||||
playFabLogin();
|
||||
if(SteamFunctions.discordOn){SteamFunctions.SetRichPresence()};
|
||||
}
|
||||
}
|
||||
document.getElementById("loading").style.display = "none";
|
||||
}, 500);
|
||||
if (!supportedBrowser) {
|
||||
@ -1064,15 +1059,11 @@ export function init() {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log("👨💻 Development Mode 👩💻");
|
||||
}
|
||||
ElectronRuntime.initialize();
|
||||
SteamRuntime.initialize();
|
||||
Cloud.init();
|
||||
GameStorage.load();
|
||||
Tabs.all.find(t => t.config.id === player.options.lastOpenTab).show(true);
|
||||
if(steamOn){
|
||||
SteamFunctions.UIZoom();
|
||||
if(!Cloud.loggedIn){
|
||||
SteamFunctions.autoLogin()
|
||||
}
|
||||
}
|
||||
//shop.init();
|
||||
Payments.init();
|
||||
}
|
||||
|
||||
|
@ -1,282 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
|
||||
const SteamFunctions = {
|
||||
purchaseChecker: [],
|
||||
purchasesInitiated: true,
|
||||
macUser: false,
|
||||
macInterval: 0,
|
||||
discordInterval: 0,
|
||||
richPresenceInterval: 0,
|
||||
discordOn: false,
|
||||
macIntervalOn: false,
|
||||
zoomLevel: 1,
|
||||
SteamInitialize() {
|
||||
this.forceRefresh();
|
||||
this.EventHandlers();
|
||||
if (window.navigator.platform === "MacIntel") {
|
||||
SteamFunctions.macUser = true;
|
||||
}else{
|
||||
Steam.initDiscordAPI("1057439416819396689",1399720)
|
||||
SteamFunctions.discordOn = true
|
||||
SteamFunctions.richPresenceInterval = setInterval(SteamFunctions.SetRichPresence,8000)
|
||||
SteamFunctions.discordInterval = setInterval(Steam.runDiscordCallbacks, 4000);
|
||||
}
|
||||
this.GetZoom()
|
||||
},
|
||||
forceRefresh() { // Canvas workaround to enable overlay
|
||||
const canvas = document.getElementById("forceRefreshCanvas");
|
||||
const ctx = canvas.getContext("2d");
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
window.requestAnimationFrame(SteamFunctions.forceRefresh);
|
||||
},
|
||||
SetRichPresence(){
|
||||
if(SteamFunctions.discordOn && RichPresenceInfo){
|
||||
Steam.setDiscordActivity({
|
||||
smallImage: "icon",
|
||||
largeImage: "icon",
|
||||
state: RichPresenceInfo.state,
|
||||
details: RichPresenceInfo.details
|
||||
})
|
||||
}
|
||||
},
|
||||
UIZoom() {
|
||||
if (nodeOn) {
|
||||
const setSize = 1020;
|
||||
const SizeDiff = window.outerHeight / setSize;
|
||||
require("electron").webFrame.setZoomFactor(SizeDiff*SteamFunctions.zoomLevel);
|
||||
}
|
||||
},
|
||||
SetZoomLevel(ZoomType){
|
||||
const zoomMAXed = SteamFunctions.zoomLevel >= 1.5 && ZoomType==="Increase"
|
||||
const zoomMINed = SteamFunctions.zoomLevel <= 0.5 && ZoomType==="Decrease"
|
||||
const zoomPossible = !(zoomMAXed || zoomMINed)
|
||||
if(zoomPossible){
|
||||
ZoomType==="Increase" && !zoomMAXed ? SteamFunctions.zoomLevel += .1 : SteamFunctions.zoomLevel = SteamFunctions.zoomLevel
|
||||
ZoomType==="Decrease" && !zoomMINed ? SteamFunctions.zoomLevel -= .1 : SteamFunctions.zoomLevel = SteamFunctions.zoomLevel
|
||||
SteamFunctions.zoomLevel = Math.round(SteamFunctions.zoomLevel * 10) / 10
|
||||
localStorage.setItem("Zoom",SteamFunctions.zoomLevel)
|
||||
SteamFunctions.UIZoom()
|
||||
GameUI.notify.info(`Size changed to ${Math.round(SteamFunctions.zoomLevel*100)}%`)
|
||||
}else{
|
||||
zoomMAXed ? GameUI.notify.info(`Zoom Level is at Maximum`) : GameUI.notify.info(`Zoom Level is at Minimum`)
|
||||
}
|
||||
},
|
||||
GetZoom(){
|
||||
let zoomGet;
|
||||
if(localStorage.getItem("Zoom")){
|
||||
zoomGet = Number(localStorage.getItem("Zoom"))
|
||||
}else{
|
||||
localStorage.setItem("Zoom",1)
|
||||
zoomGet = 1
|
||||
}
|
||||
SteamFunctions.zoomLevel = zoomGet
|
||||
},
|
||||
ResetZoom(){
|
||||
localStorage.setItem("Zoom",1)
|
||||
SteamFunctions.zoomLevel = 1
|
||||
SteamFunctions.UIZoom()
|
||||
GameUI.notify.info(`Size reverted to 100%`)
|
||||
},
|
||||
EventHandlers() {
|
||||
Steam.on("micro-txn-authorization-response", (data, ordered, orderstate) => {
|
||||
if (orderstate === true) {
|
||||
SteamFunctions.PurchaseValidation();
|
||||
}
|
||||
});
|
||||
},
|
||||
GiveAchievement(AchieveID) {
|
||||
if (Steam && steamOn) {
|
||||
if (Steam.activateAchievement) {
|
||||
Steam.activateAchievement(`Achievement${AchieveID}`,
|
||||
() => console.log(`Successfully Achieved Achievement${AchieveID}`),
|
||||
err => console.log(`Achievement Error: ${err}`)
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
BackfillAchievements() {
|
||||
if (Steam && steamOn) {
|
||||
if (Steam.activateAchievement) {
|
||||
const achAchieved = []
|
||||
const achErrored = []
|
||||
for (const ach in Achievements.all) {
|
||||
if (Achievements.all[ach].isUnlocked) {
|
||||
Steam.activateAchievement(`Achievement${Achievements.all[ach].id}`,
|
||||
() => achAchieved.push(Achievements.all[ach].id),
|
||||
err => achErrored.push(Achievements.all[ach].id)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
async autoLogin(){
|
||||
if(!Cloud.loggedIn){
|
||||
const AutoEmail = `${Steam.getSteamId().accountId}@ad.com`
|
||||
const AutoPass = Steam.getSteamId().staticAccountId
|
||||
try{
|
||||
await Cloud.manualCloudCreate(AutoEmail,AutoPass);
|
||||
}catch(e){
|
||||
try{
|
||||
await Cloud.manualCloudLogin(AutoEmail,AutoPass)
|
||||
}catch(LoginError){
|
||||
this.error = true;
|
||||
this.errorMessage = "Unable to login, please recheck email/password";
|
||||
console.log(`Login Error, Retrying (${LoginError})`)
|
||||
return;
|
||||
}
|
||||
}
|
||||
Cloud.user.displayName = Steam.getSteamId().screenName
|
||||
}
|
||||
SteamFunctions.SyncPlayFabSTD()
|
||||
},
|
||||
PurchaseIAP(STD, kreds) {
|
||||
if (!steamOn) return;
|
||||
const TheItem = { ItemId: `${STD}STD`, Quantity: 1, Annotation: "Purchased via in-game store" };
|
||||
PlayFab.settings.titleId = "59813";
|
||||
const loginRequest = {
|
||||
Items: [TheItem]
|
||||
};
|
||||
PlayFab.ClientApi.StartPurchase(loginRequest, (result, error) => {
|
||||
console.log(result, error);
|
||||
if (result !== null) {
|
||||
const TheOrder = result.data.OrderId;
|
||||
const PurchaseRequest = {
|
||||
OrderId: TheOrder,
|
||||
Currency: "RM",
|
||||
ProviderName: "Steam"
|
||||
};
|
||||
PlayFab.ClientApi.PayForPurchase(PurchaseRequest, (purchaseResult, purchaseError) => {
|
||||
if (purchaseResult !== null) {
|
||||
const txnID = purchaseResult.data.ProviderData;
|
||||
SteamFunctions.purchaseChecker.push(purchaseResult.data.OrderId);
|
||||
if (window.navigator.platform === "MacIntel") {
|
||||
shell.openExternal("https://store.steampowered.com/checkout/approvetxn/" + txnID + "/?returnurl=steam");
|
||||
SteamFunctions.macInterval = setInterval(async()=>{
|
||||
SteamFunctions.PurchaseValidation()
|
||||
},2000)
|
||||
SteamFunctions.macIntervalOn = true
|
||||
setTimeout(()=>{clearInterval(SteamFunctions.macInterval);SteamFunctions.macIntervalOn = false},300000)
|
||||
}
|
||||
} else if (purchaseError !== null) {
|
||||
console.log(purchaseError);
|
||||
}
|
||||
});
|
||||
} else if (error !== null) {
|
||||
console.log(error);
|
||||
}
|
||||
});
|
||||
},
|
||||
ConfirmSteamPurchase(OrderIdentifier) {
|
||||
PlayFab.ClientApi.ConfirmPurchase({ OrderId: OrderIdentifier }, (result, error) => {
|
||||
if (result !== null && result.data.Items != null) {
|
||||
const PurchaseName = result.data.Items[0].ItemId;
|
||||
const PurchaseInstance = result.data.Items[0].ItemInstanceId;
|
||||
PlayFab.ClientApi.ConsumeItem({ ItemInstanceId: PurchaseInstance, ConsumeCount: 1 },
|
||||
(consumeResult, consumeError) => {
|
||||
if (consumeResult !== null) {
|
||||
const stdsBought = Number(PurchaseName.replace("STD", ""));
|
||||
const currencyAddRequest = {Amount: stdsBought,VirtualCurrency: "ST"}
|
||||
PlayFab.ClientApi.AddUserVirtualCurrency(currencyAddRequest, (result, error) => {
|
||||
if (result !== null) {
|
||||
SteamFunctions.SyncPlayFabSTD()
|
||||
} else if (error !== null) {
|
||||
console.log(error);
|
||||
}
|
||||
})
|
||||
SteamFunctions.purchaseChecker = SteamFunctions.purchaseChecker.filter(item => item !== OrderIdentifier);
|
||||
GameUI.notify.info(`${stdsBought} STDs Obtained!`);
|
||||
} else if (consumeError !== null) {
|
||||
console.log(consumeError);
|
||||
}
|
||||
});
|
||||
} else if (error !== null) {
|
||||
console.log("Awaiting Payment Confirmation");
|
||||
}
|
||||
});
|
||||
},
|
||||
PurchaseValidation() {
|
||||
if (SteamFunctions.purchaseChecker.length > 0) {
|
||||
SteamFunctions.purchaseChecker.forEach(
|
||||
anOrder => SteamFunctions.ConfirmSteamPurchase(anOrder)
|
||||
);
|
||||
}
|
||||
},
|
||||
SyncPlayFabSTD(){
|
||||
if(PlayFab.ClientApi.IsClientLoggedIn()){
|
||||
PlayFab.ClientApi.GetUserInventory({PlayFabId: PlayFab.PlayFabId}, (result, error) => {
|
||||
if (result !== null) {
|
||||
const CurrentSTD = result.data.VirtualCurrency.ST
|
||||
const Inventory = result.data.Inventory
|
||||
ShopPurchaseData.totalSTD = CurrentSTD
|
||||
const inventoryData = {}
|
||||
Inventory.forEach(
|
||||
ShopItem => inventoryData[ShopItem.ItemId] = ShopItem.RemainingUses
|
||||
);
|
||||
for (const key of Object.keys(GameDatabase.shopPurchases)) ShopPurchaseData[key] = inventoryData[key] ?? 0;
|
||||
GameUI.update();
|
||||
SteamFunctions.GetCosmetics()
|
||||
} else if (error !== null) {
|
||||
console.log(error);
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
PurchaseShopItem(itemCost,itemKey,itemConfig,chosenSet){
|
||||
const itemPurchaseRequest = {
|
||||
ItemId: itemKey,
|
||||
Price: typeof itemCost === "function" ? itemCost() : itemCost,
|
||||
VirtualCurrency: "ST"
|
||||
}
|
||||
PlayFab.ClientApi.PurchaseItem(itemPurchaseRequest, (result, error) => {
|
||||
if (result !== null) {
|
||||
if (itemConfig.instantPurchase) itemConfig.onPurchase();
|
||||
if (itemKey === "singleCosmeticSet") this.StoreCosmetics(chosenSet)
|
||||
SteamFunctions.SyncPlayFabSTD();
|
||||
} else if (error !== null) {
|
||||
console.log(error);
|
||||
GameUI.notify.error(error.errorMessage)
|
||||
}
|
||||
})
|
||||
},
|
||||
StoreCosmetics(Cosmetic){
|
||||
const CosmeticID = Object.entries(GameDatabase.reality.glyphCosmeticSets).filter(item => item[1]["name"]===Cosmetic)[0][0]
|
||||
var CosmeticList = [CosmeticID]
|
||||
PlayFab.ClientApi.GetUserData({PlayFabId: PlayFab.PlayFabId}, (result, error) => {
|
||||
if (result !== null) {
|
||||
if(result.data.Data["Cosmetics"]){
|
||||
CosmeticList = CosmeticList.concat(result.data.Data["Cosmetics"].Value.split(","))
|
||||
}
|
||||
const updatedCosmetics = [...new Set(CosmeticList)]
|
||||
const UpdateRequest = {
|
||||
Data: {
|
||||
Cosmetics: updatedCosmetics.join(",")
|
||||
}
|
||||
}
|
||||
PlayFab.ClientApi.UpdateUserData(UpdateRequest, (result, error) => {
|
||||
if (result !== null) {
|
||||
console.log("Cosmetics Updated on Server");
|
||||
ShopPurchaseData.unlockedCosmetics = updatedCosmetics
|
||||
GameUI.update();
|
||||
} else if (error !== null) {
|
||||
console.log(error);
|
||||
}
|
||||
})
|
||||
} else if (error !== null) {
|
||||
console.log("Error Getting User Data");
|
||||
}
|
||||
})
|
||||
},
|
||||
GetCosmetics(){
|
||||
PlayFab.ClientApi.GetUserData({PlayFabId: PlayFab.PlayFabId}, (result, error) => {
|
||||
if (result !== null) {
|
||||
const currentCosmetics = result.data.Data["Cosmetics"] ? result.data.Data["Cosmetics"].Value.split(",") : []
|
||||
ShopPurchaseData.unlockedCosmetics = currentCosmetics
|
||||
GameUI.update();
|
||||
} else if (error !== null) {
|
||||
console.log(error);
|
||||
}
|
||||
})
|
||||
}
|
||||
};
|
@ -44,37 +44,6 @@
|
||||
</video>
|
||||
</div>
|
||||
<div id="performance-stats" class="c-performance-stats" style="display: none;"></div>
|
||||
|
||||
<script type="text/javascript" src="Steam/steam.js"></script>
|
||||
<canvas id="forceRefreshCanvas" style="position: fixed; right: 0px; bottom: 0px; width: 100vw; height: 100%; opacity: 0.01; pointer-events: none; z-index: -20;"></canvas>
|
||||
<script>
|
||||
/*--Post GameLoad Steam Initialization--*/
|
||||
var nodeOn = window.require!==undefined ? true : false
|
||||
steamOn=false
|
||||
if(nodeOn){
|
||||
var JQ = require('jquery');
|
||||
var Steam = require('greenworks')
|
||||
var shell = require('electron').shell;
|
||||
if (Steam) {
|
||||
if (Steam.initAPI()) {
|
||||
console.log("Steam is Functional!")
|
||||
steamOn = true
|
||||
} else {
|
||||
console.log("Steam API not activated. Are you sure you opened the game in Steam?")
|
||||
}
|
||||
}
|
||||
JQ(window).resize(function(){
|
||||
SteamFunctions.UIZoom()
|
||||
});
|
||||
if(Steam){
|
||||
if(Steam.initAPI()){
|
||||
SteamFunctions.SteamInitialize()
|
||||
}else{
|
||||
}
|
||||
}
|
||||
//console.log("SteamOn is "+steamOn)
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
<script>
|
||||
// We use an IE only document variable to check here to force it to show the browser warning.
|
||||
|
@ -31,6 +31,17 @@ html {
|
||||
background: white;
|
||||
}
|
||||
|
||||
._steam-refresh-canvas {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: fixed;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: -20;
|
||||
opacity: 0.01;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
* {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
|
@ -1,4 +1,6 @@
|
||||
<script>
|
||||
import { openExternalLink } from "@/utility/open-external-link";
|
||||
|
||||
export default {
|
||||
name: "InformationModalButton",
|
||||
props: {
|
||||
@ -25,9 +27,7 @@ export default {
|
||||
openAssociatedModal() {
|
||||
Modal[this.showModal].show();
|
||||
},
|
||||
openExternalLink(){
|
||||
shell.openExternal(this.link);
|
||||
}
|
||||
openExternalLink
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
@ -1,6 +1,7 @@
|
||||
<script>
|
||||
import ModalWrapper from "@/components/modals/ModalWrapper";
|
||||
import StdStoreRow from "@/components/modals/StdStoreRow";
|
||||
import { SteamRuntime } from "@/steam";
|
||||
|
||||
export default {
|
||||
name: "StdStoreModal",
|
||||
@ -15,10 +16,10 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
update() {
|
||||
this.macPurchaser = SteamFunctions.macUser && SteamFunctions.purchaseChecker.length > 0;
|
||||
this.macPurchaser = SteamRuntime.hasPendingPurchaseConfirmations;
|
||||
},
|
||||
macConfirm() {
|
||||
SteamFunctions.PurchaseValidation();
|
||||
SteamRuntime.validatePurchases();
|
||||
}
|
||||
},
|
||||
};
|
||||
@ -29,8 +30,8 @@ export default {
|
||||
<template #header>
|
||||
Support The Developer - coins
|
||||
</template>
|
||||
<span id='MacConfirm' v-if="macPurchaser">
|
||||
<button class='o-shop-button-button' @click="macConfirm()">Confirm Purchase to Receive STDs</button>
|
||||
<span v-if="macPurchaser">
|
||||
<button class="o-shop-button-button" @click="macConfirm()">Confirm Purchase to Receive STDs</button>
|
||||
<br><span>(Required on Mac)</span><br>
|
||||
</span>
|
||||
<div class="l-modal-store-content">
|
||||
|
@ -1,4 +1,5 @@
|
||||
<script>
|
||||
import { SteamRuntime } from "@/steam";
|
||||
import Payments from "../../../javascripts/core/payments";
|
||||
|
||||
export default {
|
||||
@ -15,7 +16,7 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
purchase() {
|
||||
SteamFunctions.PurchaseIAP(this.amount, this.cost);
|
||||
SteamRuntime.purchaseIap(this.amount);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
<script>
|
||||
import { ElectronRuntime } from "@/steam";
|
||||
import SliderComponent from "@/components/SliderComponent";
|
||||
|
||||
export default {
|
||||
@ -8,7 +9,7 @@ export default {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
updatedZoom: 0
|
||||
zoomLevel: 0
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@ -24,14 +25,11 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
update() {
|
||||
this.updatedZoom = Math.round(SteamFunctions.zoomLevel*100);
|
||||
this.zoomLevel = Math.round(ElectronRuntime.zoomFactor * 100);
|
||||
},
|
||||
adjustSliderValue(value) {
|
||||
this.updatedZoom = value;
|
||||
SteamFunctions.zoomLevel = Math.round(value/10)/10
|
||||
localStorage.setItem("Zoom",SteamFunctions.zoomLevel)
|
||||
SteamFunctions.UIZoom()
|
||||
GameUI.notify.info(`Size changed to ${Math.round(SteamFunctions.zoomLevel*100)}%`);
|
||||
this.zoomLevel = value;
|
||||
ElectronRuntime.zoomLevel = Math.round(value / 10) / 10;
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -39,11 +37,11 @@ export default {
|
||||
|
||||
<template>
|
||||
<div class="o-primary-btn o-primary-btn--option o-primary-btn--slider l-options-grid__button">
|
||||
<b>Zoom Level: {{ formatInt(updatedZoom) }}%</b>
|
||||
<b>Zoom Level: {{ formatInt(zoomLevel) }}%</b>
|
||||
<SliderComponent
|
||||
class="o-primary-btn--slider__slider"
|
||||
v-bind="sliderProps"
|
||||
:value="updatedZoom"
|
||||
:value="zoomLevel"
|
||||
@input="adjustSliderValue($event)"
|
||||
/>
|
||||
</div>
|
||||
|
@ -1,5 +1,4 @@
|
||||
<script>
|
||||
import { purchase } from 'vue-gtag';
|
||||
export default {
|
||||
name: "ShopButton",
|
||||
props: {
|
||||
@ -45,10 +44,11 @@ export default {
|
||||
openSelectionModal() {
|
||||
Modal.cosmeticSetChoice.show();
|
||||
},
|
||||
SteamPurchase(){
|
||||
if(!this.isSingleCosmeticSet || this.hasChosen){
|
||||
SteamFunctions.PurchaseShopItem(this.purchase.cost,this.purchase.config.key,this.purchase.config,this.chosenSet)
|
||||
performPurchase() {
|
||||
if (this.isSingleCosmeticSet && !this.hasChosen) {
|
||||
return;
|
||||
}
|
||||
this.purchase.purchase();
|
||||
},
|
||||
purchaseButtonObject() {
|
||||
return {
|
||||
@ -105,7 +105,7 @@ export default {
|
||||
</div>
|
||||
<button
|
||||
:class="purchaseButtonObject()"
|
||||
@click="SteamPurchase()"
|
||||
@click="performPurchase"
|
||||
>
|
||||
Cost: {{ cost }}
|
||||
<img
|
||||
|
@ -1,5 +1,6 @@
|
||||
<script>
|
||||
import "vue-loading-overlay/dist/vue-loading.css";
|
||||
import { STEAM } from "@/env";
|
||||
|
||||
import Loading from "vue-loading-overlay";
|
||||
|
||||
@ -33,7 +34,7 @@ export default {
|
||||
return ShopPurchase.all;
|
||||
},
|
||||
buySTDText() {
|
||||
return steamOn ? "Buy More" : "Play Online on Steam to buy STDs";
|
||||
return STEAM ? "Buy More" : "Play Online on Steam to buy STDs";
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@ -67,7 +68,7 @@ export default {
|
||||
}
|
||||
},
|
||||
showStore() {
|
||||
if (!steamOn) return;
|
||||
if (!STEAM) return;
|
||||
if (this.creditsClosed) return;
|
||||
SecretAchievement(33).unlock();
|
||||
if (this.loggedIn) Modal.shop.show();
|
||||
|
@ -1,4 +1,7 @@
|
||||
<script>
|
||||
import { openExternalLink } from "@/utility/open-external-link";
|
||||
import { STEAM } from "@/env";
|
||||
|
||||
export default {
|
||||
name: "NewsTicker",
|
||||
data() {
|
||||
@ -72,7 +75,14 @@ export default {
|
||||
this.currentNews.reset();
|
||||
}
|
||||
|
||||
line.innerHTML = this.currentNews.text.replace(/href='/g, `onClick='shell.openExternal("`).replace(/' target='_blank'/g, `")'`);
|
||||
let text = this.currentNews.text;
|
||||
if (STEAM) {
|
||||
window.openNewsLink = openExternalLink;
|
||||
text = text
|
||||
.replace(/href='/gu, `onClick='window.openNewsLink("`)
|
||||
.replace(/' target='_blank'/gu, `")'`);
|
||||
}
|
||||
line.innerHTML = text;
|
||||
|
||||
line.style["transition-duration"] = "0ms";
|
||||
if (this.currentNews?.id === "a244" || this.currentNews?.id === "ai63") {
|
||||
|
@ -1,2 +1,3 @@
|
||||
export const DEV = process.env.VUE_APP_DEV === "true";
|
||||
export const STEAM = process.env.VUE_APP_STEAM === "true";
|
||||
export const MAC = window.navigator.platform === "MacIntel";
|
||||
|
@ -1303,7 +1303,7 @@ PlayFab.ClientApi = {
|
||||
|
||||
};
|
||||
|
||||
export var PlayFabClientSDK = PlayFab.ClientApi;
|
||||
var PlayFabClientSDK = PlayFab.ClientApi;
|
||||
|
||||
PlayFab.RegisterWithPhaser = function() {
|
||||
if ( typeof Phaser === "undefined" || typeof Phaser.Plugin === "undefined" )
|
24
src/steam/bindings/electron.js
Normal file
24
src/steam/bindings/electron.js
Normal file
@ -0,0 +1,24 @@
|
||||
import { NodeModule } from "../node-module";
|
||||
|
||||
/**
|
||||
* @type {NodeModule<any>}
|
||||
*/
|
||||
const module = new NodeModule("electron");
|
||||
|
||||
export function isModuleLoaded() {
|
||||
return module.isLoaded;
|
||||
}
|
||||
|
||||
export function setZoomFactor(zoomFactor) {
|
||||
const setSize = 1020;
|
||||
const sizeDiff = window.outerHeight / setSize;
|
||||
return module.safeCall(
|
||||
x => x.webFrame.setZoomFactor(sizeDiff * zoomFactor)
|
||||
);
|
||||
}
|
||||
|
||||
export function openExternal(url) {
|
||||
return module.safeCall(
|
||||
x => x.shell.openExternal(url)
|
||||
);
|
||||
}
|
72
src/steam/bindings/greenworks.js
Normal file
72
src/steam/bindings/greenworks.js
Normal file
@ -0,0 +1,72 @@
|
||||
import { NodeModule } from "../node-module";
|
||||
|
||||
/**
|
||||
* @type {NodeModule<Greenworks.NodeModule>}
|
||||
*/
|
||||
const module = new NodeModule("greenworks");
|
||||
|
||||
export function isModuleLoaded() {
|
||||
return module.isLoaded;
|
||||
}
|
||||
|
||||
export function initAPI() {
|
||||
return module.safeCall(
|
||||
x => x.initAPI(),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
export function getSteamId() {
|
||||
return module.safeCall(
|
||||
x => x.getSteamId()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Promise<Greenworks.SteamAuthTicket>}
|
||||
*/
|
||||
export function getAuthSessionTicket() {
|
||||
return module.makePromise(
|
||||
(x, resolve, reject) => x.getAuthSessionTicket(resolve, reject)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export function activateAchievement(id) {
|
||||
return module.makePromise(
|
||||
(x, resolve, reject) => x.activateAchievement(id, resolve, reject)
|
||||
);
|
||||
}
|
||||
|
||||
// Mako, please rename the second parameter. I have no idea what it is.
|
||||
export function initDiscordAPI(clientId, steamGameId) {
|
||||
return module.safeCall(
|
||||
x => x.initDiscordAPI(clientId, steamGameId)
|
||||
);
|
||||
}
|
||||
|
||||
export function runDiscordCallbacks() {
|
||||
return module.safeCall(
|
||||
x => x.runDiscordCallbacks()
|
||||
);
|
||||
}
|
||||
|
||||
export function on(event, callback) {
|
||||
return module.safeCall(
|
||||
x => x.on(event, callback)
|
||||
);
|
||||
}
|
||||
|
||||
export function setDiscordActivity(state, details) {
|
||||
return module.safeCall(
|
||||
x => x.setDiscordActivity({
|
||||
smallImage: "icon",
|
||||
largeImage: "icon",
|
||||
state,
|
||||
details
|
||||
})
|
||||
);
|
||||
}
|
||||
|
118
src/steam/bindings/playfab.js
Normal file
118
src/steam/bindings/playfab.js
Normal file
@ -0,0 +1,118 @@
|
||||
import { PlayFab } from "@/steam/bindings/PlayFabClientApi";
|
||||
|
||||
/**
|
||||
* @type {PlayFabClientModule.IPlayFabClient}
|
||||
*/
|
||||
const clientApi = PlayFab.ClientApi;
|
||||
PlayFab.settings.titleId = "59813";
|
||||
|
||||
export function LoginWithSteam(ticket) {
|
||||
return makePromise(clientApi.LoginWithSteam, {
|
||||
SteamTicket: ticket,
|
||||
CreateAccount: true
|
||||
});
|
||||
}
|
||||
|
||||
export function UpdateUserTitleDisplayName(displayName) {
|
||||
makeAuthorizedPromise(clientApi.UpdateUserTitleDisplayName, {
|
||||
DisplayName: displayName
|
||||
});
|
||||
}
|
||||
|
||||
export function GetUserData() {
|
||||
return makeAuthorizedPromise(clientApi.GetUserData);
|
||||
}
|
||||
|
||||
export function UpdateUserData(data) {
|
||||
return makeAuthorizedPromise(clientApi.UpdateUserData, {
|
||||
Data: data
|
||||
});
|
||||
}
|
||||
|
||||
export function GetUserInventory() {
|
||||
return makeAuthorizedPromise(clientApi.GetUserInventory);
|
||||
}
|
||||
|
||||
export function PurchaseItem(id, price, currency) {
|
||||
return makeAuthorizedPromise(clientApi.PurchaseItem, {
|
||||
ItemId: id,
|
||||
Price: price,
|
||||
VirtualCurrency: currency
|
||||
});
|
||||
}
|
||||
|
||||
export function StartPurchase(itemId, quantity, annotation) {
|
||||
return makeAuthorizedPromise(clientApi.StartPurchase, {
|
||||
Items: [{
|
||||
ItemId: itemId,
|
||||
Quantity: quantity,
|
||||
Annotation: annotation
|
||||
}]
|
||||
});
|
||||
}
|
||||
|
||||
export function PayForPurchase(orderId, currency, providerName) {
|
||||
return makeAuthorizedPromise(clientApi.PayForPurchase, {
|
||||
OrderId: orderId,
|
||||
Currency: currency,
|
||||
ProviderName: providerName
|
||||
});
|
||||
}
|
||||
|
||||
export function ConfirmPurchase(orderId) {
|
||||
return makeAuthorizedPromise(clientApi.ConfirmPurchase, {
|
||||
OrderId: orderId
|
||||
});
|
||||
}
|
||||
|
||||
export function ConsumeItem(itemInstanceId, consumeCount) {
|
||||
return makeAuthorizedPromise(clientApi.ConsumeItem, {
|
||||
ItemInstanceId: itemInstanceId,
|
||||
ConsumeCount: consumeCount
|
||||
});
|
||||
}
|
||||
|
||||
export function AddUserVirtualCurrency(amount, virtualCurrency) {
|
||||
return makeAuthorizedPromise(clientApi.ConfirmPurchase, {
|
||||
Amount: amount,
|
||||
VirtualCurrency: virtualCurrency
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @template TRequest
|
||||
* @template TResponse
|
||||
* @param {(request: TRequest, callback: PlayFabModule.ApiCallback<TResponse>) => any} playFabFunction
|
||||
* @param {TRequest} [request]
|
||||
* @returns {Promise<TResponse>}
|
||||
*/
|
||||
function makeAuthorizedPromise(playFabFunction, request) {
|
||||
if (!clientApi.IsClientLoggedIn()) {
|
||||
return Promise.reject("PlayFab Client is not logged in.");
|
||||
}
|
||||
|
||||
return makePromise(playFabFunction, request);
|
||||
}
|
||||
|
||||
/**
|
||||
* So, apparently, PlayFab Web SDK is so bad, the promises they are
|
||||
* returning are not the actual promises for the api calls
|
||||
* (just take a look inside PlayFabClient.js). This wrapper
|
||||
* creates proper promises based on the callbacks.
|
||||
* @template TRequest
|
||||
* @template TResponse
|
||||
* @param {(request: TRequest, callback: PlayFabModule.ApiCallback<TResponse>) => any} playFabFunction
|
||||
* @param {TRequest} [request]
|
||||
* @returns {Promise<TResponse>}
|
||||
*/
|
||||
function makePromise(playFabFunction, request) {
|
||||
return new Promise((resolve, reject) => {
|
||||
playFabFunction(request ?? {}, (data, error) => {
|
||||
if (!error && data?.data) {
|
||||
resolve(data.data);
|
||||
} else {
|
||||
reject(error ?? data);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
75
src/steam/electron-runtime.js
Normal file
75
src/steam/electron-runtime.js
Normal file
@ -0,0 +1,75 @@
|
||||
import * as Electron from "./bindings/electron";
|
||||
|
||||
const MIN_ZOOM = 0.5;
|
||||
const MAX_ZOOM = 1.5;
|
||||
|
||||
let zoomFactor = 1;
|
||||
|
||||
export const ElectronRuntime = {
|
||||
initialize() {
|
||||
if (!this.isActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
zoomFactor = Number(localStorage.getItem("Zoom"));
|
||||
zoomFactor = Number.isFinite(zoomFactor) ? zoomFactor : 1;
|
||||
window.addEventListener("resize", () => this.updateZoom());
|
||||
},
|
||||
|
||||
get isActive() {
|
||||
return Electron.isModuleLoaded();
|
||||
},
|
||||
|
||||
increaseZoom() {
|
||||
if (!this.isActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (zoomFactor > MAX_ZOOM) {
|
||||
GameUI.notify.info("Zoom Level is at Maximum");
|
||||
return;
|
||||
}
|
||||
|
||||
this.zoomFactor = Math.round((zoomFactor + 0.1) * 10) / 10;
|
||||
},
|
||||
|
||||
decreaseZoom() {
|
||||
if (!this.isActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (zoomFactor < MIN_ZOOM) {
|
||||
GameUI.notify.info("Zoom Level is at Minimum");
|
||||
return;
|
||||
}
|
||||
|
||||
this.zoomFactor = Math.round((zoomFactor - 0.1) * 10) / 10;
|
||||
},
|
||||
|
||||
resetZoom() {
|
||||
if (!this.isActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.zoomFactor = 1;
|
||||
},
|
||||
|
||||
get zoomFactor() {
|
||||
return zoomFactor;
|
||||
},
|
||||
|
||||
set zoomFactor(value) {
|
||||
zoomFactor = value;
|
||||
localStorage.setItem("Zoom", zoomFactor.toString());
|
||||
this.updateZoom();
|
||||
GameUI.notify.info(`Size changed to ${Math.round(zoomFactor * 100)}%`);
|
||||
},
|
||||
|
||||
updateZoom() {
|
||||
if (!this.isActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
Electron.setZoomFactor(zoomFactor);
|
||||
}
|
||||
};
|
2
src/steam/index.js
Normal file
2
src/steam/index.js
Normal file
@ -0,0 +1,2 @@
|
||||
export { ElectronRuntime } from "./electron-runtime";
|
||||
export { SteamRuntime } from "./steam-runtime";
|
60
src/steam/node-module.js
Normal file
60
src/steam/node-module.js
Normal file
@ -0,0 +1,60 @@
|
||||
/* eslint-disable no-console */
|
||||
|
||||
import { STEAM } from "@/env";
|
||||
|
||||
/**
|
||||
* @template TModule
|
||||
*/
|
||||
export class NodeModule {
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
*/
|
||||
constructor(name) {
|
||||
this.name = name;
|
||||
/**
|
||||
* @type {TModule}
|
||||
* @private
|
||||
*/
|
||||
this.__module__ = STEAM && window.require !== undefined ? window.require(name) : undefined;
|
||||
}
|
||||
|
||||
get isLoaded() {
|
||||
return this.__module__ !== undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* @template TResult
|
||||
* @param {(module: TModule, resolve: (value?: TResult) => void, reject: (reason?: any) => void) => void} executor
|
||||
* @returns {Promise<TResult>}
|
||||
*/
|
||||
makePromise(executor) {
|
||||
if (!this.isLoaded) {
|
||||
throw Error(`Node module "${this.name}" is not loaded.`);
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
executor(this.__module__, resolve, reject);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @template TResult
|
||||
* @param {(module: TModule) => TResult} call
|
||||
* @param {TResult} [defaultValue]
|
||||
* @returns {TResult}
|
||||
*/
|
||||
safeCall(call, defaultValue) {
|
||||
if (!this.isLoaded) {
|
||||
throw Error(`Node module "${this.name}" is not loaded.`);
|
||||
}
|
||||
|
||||
try {
|
||||
return call(this.__module__);
|
||||
} catch (e) {
|
||||
console.error(`Failed to make a call to node module "${this.name}".`);
|
||||
console.error(e);
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
}
|
99
src/steam/steam-purchases.js
Normal file
99
src/steam/steam-purchases.js
Normal file
@ -0,0 +1,99 @@
|
||||
import { MAC } from "@/env";
|
||||
import { openExternalLink } from "@/utility/open-external-link";
|
||||
import * as PlayFab from "./bindings/playfab";
|
||||
|
||||
let pendingConfirmations = [];
|
||||
|
||||
export function hasPendingPurchaseConfirmations() {
|
||||
return MAC && pendingConfirmations.length > 0;
|
||||
}
|
||||
|
||||
export async function purchaseIap(std) {
|
||||
const itemId = `${std}STD`;
|
||||
const quantity = 1;
|
||||
const annotation = "Purchased via in-game store";
|
||||
const order = await PlayFab.StartPurchase(itemId, quantity, annotation);
|
||||
|
||||
const orderId = order.OrderId;
|
||||
const currency = "RM";
|
||||
const providerName = "Steam";
|
||||
const result = await PlayFab.PayForPurchase(orderId, currency, providerName);
|
||||
|
||||
pendingConfirmations.push(result.OrderId);
|
||||
|
||||
if (MAC) {
|
||||
const txnId = result.ProviderData;
|
||||
openExternalLink(`https://store.steampowered.com/checkout/approvetxn/${txnId}/?returnurl=steam`);
|
||||
|
||||
const macInterval = setInterval(() => {
|
||||
validatePurchases();
|
||||
}, 2000);
|
||||
|
||||
setTimeout(() => {
|
||||
clearInterval(macInterval);
|
||||
}, 300000);
|
||||
}
|
||||
}
|
||||
|
||||
export function validatePurchases() {
|
||||
for (const order of pendingConfirmations) {
|
||||
validatePurchase(order);
|
||||
}
|
||||
}
|
||||
|
||||
async function validatePurchase(orderId) {
|
||||
const confirm = await PlayFab.ConfirmPurchase(orderId);
|
||||
const purchaseName = confirm.Items[0].ItemId;
|
||||
const purchaseInstance = confirm.Items[0].ItemInstanceId;
|
||||
|
||||
await PlayFab.ConsumeItem(purchaseInstance, 1);
|
||||
const stdsBought = Number(purchaseName.replace("STD", ""));
|
||||
pendingConfirmations = pendingConfirmations.filter(item => item !== orderId);
|
||||
await PlayFab.AddUserVirtualCurrency(stdsBought, "ST");
|
||||
GameUI.notify.info(`${stdsBought} STDs Obtained!`);
|
||||
syncIap();
|
||||
}
|
||||
|
||||
export async function syncIap() {
|
||||
const userInventory = await PlayFab.GetUserInventory();
|
||||
ShopPurchaseData.totalSTD = userInventory.VirtualCurrency?.ST ?? 0;
|
||||
for (const key of Object.keys(GameDatabase.shopPurchases)) {
|
||||
const item = userInventory.Inventory.find(x => x.ItemId === key);
|
||||
ShopPurchaseData[key] = item?.RemainingUses ?? 0;
|
||||
}
|
||||
GameUI.update();
|
||||
|
||||
const userData = await PlayFab.GetUserData();
|
||||
ShopPurchaseData.unlockedCosmetics = userData.Data.Cosmetics?.Value?.split(",") ?? [];
|
||||
GameUI.update();
|
||||
}
|
||||
|
||||
export async function purchaseShopItem(key, cost, cosmeticName) {
|
||||
await PlayFab.PurchaseItem(key, cost, "ST");
|
||||
await storeCosmetic(cosmeticName);
|
||||
syncIap();
|
||||
}
|
||||
|
||||
async function storeCosmetic(name) {
|
||||
if (name === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const cosmetic = Object.values(GameDatabase.reality.glyphCosmeticSets)
|
||||
.find(x => x.name === name);
|
||||
if (cosmetic === undefined) {
|
||||
GameUI.notify.error(`Failed to store cosmetic "${name}"`);
|
||||
return;
|
||||
}
|
||||
|
||||
const userData = await PlayFab.GetUserData();
|
||||
const cosmetics = new Set(userData.Data?.Cosmetics?.Value?.split(",") ?? []);
|
||||
cosmetics.add(cosmetic.id);
|
||||
const updatedCosmetics = [...cosmetics];
|
||||
await PlayFab.UpdateUserData({
|
||||
Cosmetics: updatedCosmetics.join(",")
|
||||
});
|
||||
|
||||
ShopPurchaseData.unlockedCosmetics = updatedCosmetics;
|
||||
GameUI.update();
|
||||
}
|
178
src/steam/steam-runtime.js
Normal file
178
src/steam/steam-runtime.js
Normal file
@ -0,0 +1,178 @@
|
||||
/* eslint-disable no-console */
|
||||
import { RichPresenceInfo } from "../../javascripts/core/discord-parser";
|
||||
|
||||
import {
|
||||
hasPendingPurchaseConfirmations,
|
||||
purchaseIap,
|
||||
purchaseShopItem,
|
||||
syncIap,
|
||||
validatePurchases
|
||||
} from "./steam-purchases";
|
||||
|
||||
import * as Greenworks from "./bindings/greenworks";
|
||||
import * as PlayFab from "./bindings/playfab";
|
||||
|
||||
import { MAC, STEAM } from "@/env";
|
||||
|
||||
let isInitialized = false;
|
||||
let isActive = false;
|
||||
|
||||
export const SteamRuntime = {
|
||||
initialize() {
|
||||
if (isInitialized) {
|
||||
throw Error("Steam Runtime was initialized already.");
|
||||
}
|
||||
|
||||
isInitialized = true;
|
||||
|
||||
if (!STEAM || !Greenworks.isModuleLoaded() || !Greenworks.initAPI()) {
|
||||
return;
|
||||
}
|
||||
|
||||
isActive = true;
|
||||
|
||||
const steamId = Greenworks.getSteamId();
|
||||
loginPlayFab(steamId);
|
||||
loginFirebase(steamId);
|
||||
|
||||
Greenworks.on("micro-txn-authorization-response", (data, ordered, orderstate) => {
|
||||
if (orderstate === true) {
|
||||
validatePurchases();
|
||||
}
|
||||
});
|
||||
|
||||
if (!MAC) {
|
||||
initializeDiscord();
|
||||
}
|
||||
|
||||
createForceRefreshCanvas();
|
||||
},
|
||||
|
||||
get isActive() {
|
||||
if (!isInitialized) {
|
||||
throw Error("Steam Runtime was called before init.");
|
||||
}
|
||||
|
||||
return isActive;
|
||||
},
|
||||
|
||||
get screenName() {
|
||||
if (!this.isActive) {
|
||||
return "Non-Steam user";
|
||||
}
|
||||
|
||||
return Greenworks.getSteamId()?.screenName ?? "Steam user";
|
||||
},
|
||||
|
||||
activateAchievement(id) {
|
||||
if (!this.isActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
Greenworks.activateAchievement(`Achievement${id}`);
|
||||
},
|
||||
|
||||
async purchaseIap(std) {
|
||||
if (!this.isActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
await purchaseIap(std);
|
||||
},
|
||||
|
||||
validatePurchases() {
|
||||
if (!this.isActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
validatePurchases();
|
||||
},
|
||||
|
||||
async syncIap() {
|
||||
if (!this.isActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
await syncIap();
|
||||
},
|
||||
|
||||
async purchaseShopItem(key, cost, cosmeticName) {
|
||||
if (!this.isActive) {
|
||||
GameUI.notify.error("Shop purchases are not available.");
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
await purchaseShopItem(key, cost, cosmeticName);
|
||||
return true;
|
||||
} catch (e) {
|
||||
GameUI.notify.error(e.errorMessage ?? e);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
get hasPendingPurchaseConfirmations() {
|
||||
if (!this.isActive) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return hasPendingPurchaseConfirmations();
|
||||
}
|
||||
};
|
||||
|
||||
async function loginPlayFab(steamId) {
|
||||
try {
|
||||
const screenName = steamId.screenName;
|
||||
const ticket = await Greenworks.getAuthSessionTicket();
|
||||
await PlayFab.LoginWithSteam(ticket.ticket.toString("hex"), screenName);
|
||||
PlayFab.UpdateUserTitleDisplayName(screenName);
|
||||
GameUI.notify.info("Logged in to PlayFab Cloud");
|
||||
} catch (error) {
|
||||
GameUI.notify.error("Couldn't log in to PlayFab Cloud.");
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async function loginFirebase(steamId) {
|
||||
const accountId = steamId.accountId;
|
||||
const staticAccountId = steamId.staticAccountId;
|
||||
const screenName = steamId.screenName;
|
||||
if (await Cloud.loginWithSteam(accountId, staticAccountId, screenName)) {
|
||||
syncIap();
|
||||
}
|
||||
}
|
||||
|
||||
function initializeDiscord() {
|
||||
Greenworks.initDiscordAPI("1057439416819396689", 1399720);
|
||||
setDiscordActivity();
|
||||
Greenworks.runDiscordCallbacks();
|
||||
setInterval(setDiscordActivity, 8000);
|
||||
setInterval(Greenworks.runDiscordCallbacks, 4000);
|
||||
}
|
||||
|
||||
function setDiscordActivity() {
|
||||
Greenworks.setDiscordActivity(RichPresenceInfo.state, RichPresenceInfo.details);
|
||||
}
|
||||
|
||||
function createForceRefreshCanvas() {
|
||||
// This canvas is required for Steam overlay to properly work in Electron.
|
||||
// Makopaz:
|
||||
// "essentially it makes the overlay have a refresh rate, otherwise it only
|
||||
// updates based on parts of the screen which change, so without it the small
|
||||
// areas of the screen where antimatter and such increment would be the only
|
||||
// small portions of the overlay showing."
|
||||
// There should be a less expensive approach. Please create a new issue or
|
||||
// PR on GitHub if you know one, the planet will say thank you for saving
|
||||
// megawatts of electricity spent on this canvas.
|
||||
const canvas = document.createElement("canvas");
|
||||
canvas.classList.add("_steam-refresh-canvas");
|
||||
document.body.appendChild(canvas);
|
||||
const ctx = canvas.getContext("2d");
|
||||
|
||||
function forceRefresh() {
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
window.requestAnimationFrame(forceRefresh);
|
||||
}
|
||||
|
||||
forceRefresh();
|
||||
}
|
5731
src/typings/PlayFabClientApi.d.ts
vendored
Normal file
5731
src/typings/PlayFabClientApi.d.ts
vendored
Normal file
File diff suppressed because it is too large
Load Diff
82
src/typings/Playfab.d.ts
vendored
Normal file
82
src/typings/Playfab.d.ts
vendored
Normal file
@ -0,0 +1,82 @@
|
||||
/// <reference path="PlayFabAdminApi.d.ts" />
|
||||
/// <reference path="PlayFabClientApi.d.ts" />
|
||||
/// <reference path="PlayFabMatchmakerApi.d.ts" />
|
||||
/// <reference path="PlayFabServerApi.d.ts" />
|
||||
/// <reference path="PlayFabAuthenticationApi.d.ts" />
|
||||
/// <reference path="PlayFabCloudScriptApi.d.ts" />
|
||||
/// <reference path="PlayFabDataApi.d.ts" />
|
||||
/// <reference path="PlayFabEconomyApi.d.ts" />
|
||||
/// <reference path="PlayFabEventsApi.d.ts" />
|
||||
/// <reference path="PlayFabExperimentationApi.d.ts" />
|
||||
/// <reference path="PlayFabInsightsApi.d.ts" />
|
||||
/// <reference path="PlayFabGroupsApi.d.ts" />
|
||||
/// <reference path="PlayFabLocalizationApi.d.ts" />
|
||||
/// <reference path="PlayFabMultiplayerApi.d.ts" />
|
||||
/// <reference path="PlayFabProfilesApi.d.ts" />
|
||||
|
||||
declare module PlayFabModule {
|
||||
export interface ISettings {
|
||||
titleId: string;
|
||||
developerSecretKey: string;
|
||||
GlobalHeaderInjection?: { [key: string]: string };
|
||||
productionServerUrl: string;
|
||||
}
|
||||
export interface IPlayFabRequestCommon { }
|
||||
export interface IPlayFabError {
|
||||
code: number;
|
||||
status: string;
|
||||
error: string;
|
||||
errorCode: number;
|
||||
errorMessage: string;
|
||||
errorDetails?: { [key: string]: string[] };
|
||||
request?: any;
|
||||
customData?: any;
|
||||
retryAfterSeconds?: number;
|
||||
}
|
||||
export interface SuccessContainer<TResult extends IPlayFabResultCommon> extends IPlayFabError {
|
||||
data: TResult;
|
||||
}
|
||||
export interface IPlayFabResultCommon extends IPlayFabError { }
|
||||
|
||||
export interface ApiCallback<TResult extends IPlayFabResultCommon> { (result: SuccessContainer<TResult>, error: IPlayFabError): void }
|
||||
}
|
||||
|
||||
declare var PlayFab: {
|
||||
buildIdentifier: string;
|
||||
sdkVersion: string;
|
||||
GenerateErrorReport(IPlayFabError): string;
|
||||
settings: PlayFabModule.ISettings;
|
||||
AdminApi: PlayFabAdminModule.IPlayFabAdmin;
|
||||
ClientApi: PlayFabClientModule.IPlayFabClient;
|
||||
MatchmakerApi: PlayFabMatchmakerModule.IPlayFabMatchmaker;
|
||||
ServerApi: PlayFabServerModule.IPlayFabServer;
|
||||
AuthenticationApi: PlayFabAuthenticationModule.IPlayFabAuthentication;
|
||||
CloudScriptApi: PlayFabCloudScriptModule.IPlayFabCloudScript;
|
||||
DataApi: PlayFabDataModule.IPlayFabData;
|
||||
EconomyApi: PlayFabEconomyModule.IPlayFabEconomy;
|
||||
EventsApi: PlayFabEventsModule.IPlayFabEvents;
|
||||
ExperimentationApi: PlayFabExperimentationModule.IPlayFabExperimentation;
|
||||
InsightsApi: PlayFabInsightsModule.IPlayFabInsights;
|
||||
GroupsApi: PlayFabGroupsModule.IPlayFabGroups;
|
||||
LocalizationApi: PlayFabLocalizationModule.IPlayFabLocalization;
|
||||
MultiplayerApi: PlayFabMultiplayerModule.IPlayFabMultiplayer;
|
||||
ProfilesApi: PlayFabProfilesModule.IPlayFabProfiles;
|
||||
|
||||
};
|
||||
// Continue to support older usage
|
||||
declare var PlayFabAdminSDK: PlayFabAdminModule.IPlayFabAdmin;
|
||||
declare var PlayFabClientSDK: PlayFabClientModule.IPlayFabClient;
|
||||
declare var PlayFabMatchmakerSDK: PlayFabMatchmakerModule.IPlayFabMatchmaker;
|
||||
declare var PlayFabServerSDK: PlayFabServerModule.IPlayFabServer;
|
||||
declare var PlayFabAuthenticationSDK: PlayFabAuthenticationModule.IPlayFabAuthentication;
|
||||
declare var PlayFabCloudScriptSDK: PlayFabCloudScriptModule.IPlayFabCloudScript;
|
||||
declare var PlayFabDataSDK: PlayFabDataModule.IPlayFabData;
|
||||
declare var PlayFabEconomySDK: PlayFabEconomyModule.IPlayFabEconomy;
|
||||
declare var PlayFabEventsSDK: PlayFabEventsModule.IPlayFabEvents;
|
||||
declare var PlayFabExperimentationSDK: PlayFabExperimentationModule.IPlayFabExperimentation;
|
||||
declare var PlayFabInsightsSDK: PlayFabInsightsModule.IPlayFabInsights;
|
||||
declare var PlayFabGroupsSDK: PlayFabGroupsModule.IPlayFabGroups;
|
||||
declare var PlayFabLocalizationSDK: PlayFabLocalizationModule.IPlayFabLocalization;
|
||||
declare var PlayFabMultiplayerSDK: PlayFabMultiplayerModule.IPlayFabMultiplayer;
|
||||
declare var PlayFabProfilesSDK: PlayFabProfilesModule.IPlayFabProfiles;
|
||||
|
30
src/typings/greenworks.d.ts
vendored
Normal file
30
src/typings/greenworks.d.ts
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
export declare module Greenworks {
|
||||
export interface NodeModule {
|
||||
initAPI(): boolean;
|
||||
getSteamId(): SteamId;
|
||||
getAuthSessionTicket(resolve: (ticket: SteamAuthTicket) => void, reject: (error: any) => void): void;
|
||||
activateAchievement(id: string, resolve: () => void, reject: (error: any) => void): void;
|
||||
initDiscordAPI(clientId: string, steamGameId: number): void;
|
||||
runDiscordCallbacks(): void;
|
||||
setDiscordActivity(info: DiscordActivityInfo): void;
|
||||
on(event: string, callback: Function): void;
|
||||
}
|
||||
|
||||
export interface SteamId {
|
||||
screenName: string;
|
||||
accountId: string;
|
||||
staticAccountId: string;
|
||||
}
|
||||
|
||||
export interface SteamAuthTicket {
|
||||
ticket: Buffer;
|
||||
}
|
||||
|
||||
export interface DiscordActivityInfo {
|
||||
largeImage: string;
|
||||
smallImage: string;
|
||||
details: string;
|
||||
state: string;
|
||||
}
|
||||
}
|
||||
|
9
src/utility/open-external-link.js
Normal file
9
src/utility/open-external-link.js
Normal file
@ -0,0 +1,9 @@
|
||||
import * as Electron from "@/steam/bindings/electron";
|
||||
|
||||
export function openExternalLink(url) {
|
||||
if (Electron.isModuleLoaded()) {
|
||||
Electron.openExternal(url);
|
||||
} else {
|
||||
window.open(url, "_blank").focus();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user