/*: * @target MZ * @plugindesc Integrate your game with the Steamworks SDK - 1.00.01 * <pluginName:CycloneSteam> * @author Hudell * @url https://makerdevs.com/plugin/cyclone-steam * * @help * =========================================================================== * 88 * 88 * 88 * ,adPPYba, 8b d8 ,adPPYba, 88 ,adPPYba, 8b,dPPYba, ,adPPYba, * a8" "" `8b d8' a8" "" 88 a8" "8a 88P' `"8a a8P_____88 * 8b `8b d8' 8b 88 8b d8 88 88 8PP""""""" * "8a, ,aa `8b,d8' "8a, ,aa 88 "8a, ,a8" 88 88 "8b, ,aa * `"Ybbd8"' Y88' `"Ybbd8"' 88 `"YbbdP"' 88 88 `"Ybbd8"' * d8' * d8' * Steamworks Integration by Hudell * =========================================================================== * Terms of Use * =========================================================================== * 1. For support, feature requests or bug reports, you may contact me through * any of the following channels (in order of preference): * * 1.a. Opening an issue on the plugin's GitHub repository: * https://github.com/Hudell/cyclone-engine * 1.b. Tagging me on threads on Rpg Maker related Forums, such as: * rpgmakerweb.com (English) * centrorpg.com (Portuguese) * condadobraveheart.com (Portuguese) * 1.c. Opening threads on the plugin's itch.io page * 1.d. Tagging my user on Rpg Maker related sub-reddits, such as r/rpgmaker * * 2. Do not send me Direct Messages asking for support or bug reports. * You may only send me direct messages when none of the above platforms are * appropiate for it, or when you want to share pictures of cute dogs. * * 3. A special exception is created for patreon users who get access to my * priority support discord server. * * 4. Sending plugin related questions on channels related to any of my other * projects (such as my game's Discord server) may result in an immediate ban * from such platforms and I may also choose to ignore your future requests. * * 5. This plugin is released under the Apache License 2.0 (Apache-2.0). * * 6. You can send me your own changes to this plugin if you wish to see them * included in an update, by registering a Pull Request on the plugin's GitHub * repository. * * 7. This plugin is provided as is. While I'll often read feedback and offer * updates to my plugins, I am in no obligation to do so. * * 8. I'm not responsible for anything created with this plugin. * =========================================================================== */ (function () { 'use strict'; globalThis.CyclonePatcher=class{static initialize(t){this.pluginName=t,this.superClasses=new Map;}static _descriptorIsProperty(t){return t.get||t.set||!t.value||"function"!=typeof t.value}static _getAllClassDescriptors(t,e=!1){if(t===Object)return {};const r=Object.getOwnPropertyDescriptors(e?t.prototype:t);let s={};if(t.prototype){const r=Object.getPrototypeOf(t.prototype).constructor;r!==Object&&(s=this._getAllClassDescriptors(r,e));}return Object.assign({},s,r)}static _assignDescriptor(t,e,r,s,a=!1){if(this._descriptorIsProperty(r))r.get||r.set?Object.defineProperty(t,s,{get:r.get,set:r.set,enumerable:r.enumerable,configurable:r.configurable}):Object.defineProperty(t,s,{value:r.value,enumerable:r.enumerable,configurable:r.configurable});else {let r=s;if(a)for(;r in t;)r=`_${r}`;t[r]=e[s];}}static _applyPatch(t,e,r,s,a=!1){const n=this._getAllClassDescriptors(t,a),i=a?t.prototype:t,o=a?e.prototype:e,l=Object.getOwnPropertyDescriptors(o);let u=!1;for(const t in l){if(s.includes(t))continue;if(t in n){u=!0;const e=n[t];this._assignDescriptor(r,i,e,t,!0);}const e=l[t];this._assignDescriptor(i,o,e,t);}return u}static patchClass(t,e){const r=this.superClasses&&this.superClasses[t.name]||{},s={},a={},n=e(a,s);if("function"!=typeof n)throw new Error(`Invalid class patch for ${t.name}`);const i=Object.getOwnPropertyNames(class{}),o=Object.getOwnPropertyNames(class{}.prototype),l=this._applyPatch(t,n,r,i),u=this._applyPatch(t,n,s,o,!0);if(l){const t=Object.getOwnPropertyDescriptors(r);for(const e in t)this._assignDescriptor(a,r,t[e],e);u&&(a.$prototype=s);}else Object.assign(a,s);this.superClasses&&(this.superClasses[t.name]=a);}};const t=Object.freeze(["TRUE","ON","1","YES","T","V"]);class e extends CyclonePatcher{static initialize(t){super.initialize(t),this.fileName=void 0,this.params={},this.structs=new Map,this.eventListeners=new Map,this.structs.set("Dictionary",{name:{type:"string",defaultValue:""},value:{type:"string",defaultValue:""}});}static register(t={}){const e=this.loadAllParams();this.params=this.loadParamMap(t,e);}static loadAllParams(){for(const t of globalThis.$plugins){if(!t||!t.status)continue;if(!t.description||!t.description.includes(`<pluginName:${this.pluginName}`))continue;this.fileName=t.name;const e=new Map;for(const r in t.parameters)r&&!r.startsWith("-")&&e.set(r,t.parameters[r]);return e}}static loadParamMap(t,e){const r={};for(const s in t)if(t.hasOwnProperty(s))try{r[s]=this.parseParam(s,t,e);}catch(t){console.error(`CycloneEngine crashed while trying to parse a parameter value (${s}). Please report the following error to Hudell:`),console.log(t);}return r}static registerEvent(t,e){this.eventListeners.has(t)||this.eventListeners.set(t,new Set);this.eventListeners.get(t).add(e);}static removeEventListener(t,e){if(!this.eventListeners.has(t))return;this.eventListeners.get(t).delete(e);}static shouldReturnCallbackResult(t,{abortOnTrue:e,abortOnFalse:r,returnOnValue:s}){return !(!1!==t||!r)||(!(!0!==t||!e)||!(void 0===t||!s))}static runEvent(t,{abortOnTrue:e=!1,abortOnFalse:r=!1,returnOnValue:s=!1}={},...a){if(!this.eventListeners.has(t))return;const n=this.eventListeners.get(t);for(const t of n){if("number"==typeof t){this.runCommonEvent(t);continue}if("function"!=typeof t){console.error("CycloneEngine: Invalid callback type:"),console.log(t);continue}const n=t(...a);if(this.shouldReturnCallbackResult(n,{abortOnTrue:e,abortOnFalse:r,returnOnValue:s}))return n}}static runCommonEvent(t){const e=globalThis.$dataCommonEvents[t];if(!e)return;const r=new Game_Interpreter(1);if(r.setup(e.list,0),!this._interpreters){this._interpreters=new Set;const t=SceneManager.updateMain;SceneManager.updateMain=()=>{t.call(SceneManager),this.update();};}this._interpreters.add(r);}static update(){if(this._interpreters)for(const t of this._interpreters)t.update(),t.isRunning()||this._interpreters.delete(t);}static getPluginFileName(){return this.fileName??this.pluginName}static isTrue(e){return "string"!=typeof e?Boolean(e):t.includes(e.toUpperCase())}static isFalse(t){return !this.isTrue(t)}static getIntParam({value:t,defaultValue:e}){try{const r=parseInt(t);return isNaN(r)?e:r}catch(r){return ""!==t&&console.error(`Cyclone Engine plugin ${this.pluginName}: Param is expected to be an integer number, but the received value was '${t}'.`),e}}static getFloatParam({value:t,defaultValue:e}){try{const r=parseFloat(t.replace(",","."));return isNaN(r)?e:r}catch(r){return ""!==t&&console.error(`Cyclone Engine plugin ${this.pluginName}: Param is expected to be a number, but the received value was '${t}'.`),e}}static getIntListParam({value:t}){return this.parseArray((t??"").trim(),(t=>{try{return parseInt(t.trim())}catch(e){return ""!==t&&console.error(`Cyclone Engine plugin ${this.pluginName}: Param is expected to be a list of integer numbers, but one of the items was '${t}'.`),0}}))}static parseStructArrayParam({data:t,type:e}){const r=[];for(const s of t){const t=this.parseStructParam({value:s,defaultValue:"",type:e});t&&r.push(t);}return r}static getFloatListParam({value:t}){return this.parseArray((t||"").trim(),(t=>{try{return parseFloat(t.trim())}catch(e){return ""!==t&&console.error(`Cyclone Engine plugin ${this.pluginName}: Param ${name} is expected to be a list of numbers, but one of the items was '${t}'.`),0}}))}static getParam({value:t,defaultValue:e,type:r}){if(r.endsWith("[]"))return this.parseArrayParam({value:t,type:r});if(r.startsWith("struct<"))return this.parseStructParam({value:t,defaultValue:e,type:r});if(void 0===t)return e;switch(r){case"int":return this.getIntParam({value:t,defaultValue:e});case"float":return this.getFloatParam({value:t,defaultValue:e});case"boolean":return "boolean"==typeof t?t:this.isTrue(String(t).trim());default:return t}}static getPluginParam(t){return this.params.get(t)}static defaultValueForType(t){switch(t){case"int":return 0;case"boolean":return !1}return ""}static parseParam(t,e,r){let s=e[t];s&&"string"==typeof s&&(s={type:s,defaultValue:this.defaultValueForType(s)});const{name:a=t,type:n="string",defaultValue:i=""}=s;let o;if(r)o=r.get(a)??i;else {o=(this.getPluginParam(a)||{}).value??i;}return this.getParam({value:o,defaultValue:i,type:n})}static parseArrayParam({value:t,type:e}){const r=this.parseArray(t);if(!r||!r.length)return r;const s=e.substr(0,e.length-2),a=[];for(const t of r){const e=this.defaultValueForType(s);a.push(this.getParam({value:t,type:s,defaultValue:e}));}return a}static getRegexMatch(t,e,r){const s=t.match(e);if(s)return s[r]}static parseStructParam({value:t,defaultValue:e,type:r}){let s;if(t)try{s=JSON.parse(t);}catch(e){console.error("Cyclone Engine failed to parse param structure: ",t),console.error(e);}s||(s=JSON.parse(e));const a=this.getRegexMatch(r,/struct<(.*)>/i,1);if(!a)return console.error(`Unknown plugin param type: ${r}`),s;const n=this.structs.get(a);if(!n)return console.error(`Unknown param structure type: ${a}`),s;for(const t in n){if(!n.hasOwnProperty(t))continue;let e=n[t];"string"==typeof e&&(e={type:e,defaultValue:this.defaultValueForType(e)}),s[t]=this.getParam({value:s[t],defaultValue:e.defaultValue,type:e.type});}return s}static parseList(t,e){let r=t;r.startsWith("[")&&(r=r.substr(1)),r.endsWith("]")&&(r=r.substr(0,r.length-1));const s=r.split(",");return e?s.map((t=>e(t))):s}static parseArray(t,e){let r;try{r=JSON.parse(t);}catch(t){return []}return r&&r.length?e?r.map((t=>e(t))):r:[]}static registerCommand(t,e,r){return "function"==typeof e?PluginManager.registerCommand(this.getPluginFileName(),t,e):PluginManager.registerCommand(this.getPluginFileName(),t,(t=>{const s=new Map;for(const e in t)t.hasOwnProperty(e)&&s.set(e,t[e]);const a=this.loadParamMap(e,s);return Object.assign(t,a),r(t)}))}}globalThis.CyclonePlugin=e; class CycloneSteam extends CyclonePlugin { static register() { this.initialized = false; this.initialize('CycloneSteam'); super.register({}); if (typeof require !== 'function') { return; } try { this.greenworks = require('./greenworks'); } catch (e) { this.greenworks = false; console.error('Greenworks failed to load. Make sure you copied all files from the Steamworks SDK to the right folders;'); console.log('https://makerdevs.com/plugin/cyclone-steam'); console.error(e); return; } this.initialized = this.greenworks.initAPI(); if (!this.initialized) { console.error('Greenworks failed to initialize.'); return; } this.steam = this.greenworks.getSteamId(); } static get screenName() { if (!this.greenworks) { return 'Play Test'; } return (this.steam && this.steam.screenName) || ''; } static get uiLanguage() { if (!this.greenworks) { return 'english'; } return this.greenworks.getCurrentUILanguage(); } static get gameLanguage() { if (!this.greenworks) { return 'english'; } return this.greenworks.getCurrentGameLanguage(); } static get achievementCount() { if (!this.greenworks) { return 0; } return this.greenworks.getNumberOfAchievements(); } static get running() { return this.greenworks && this.greenworks.isSteamRunning(); } static get overlayEnabled() { return this.greenworks && this.greenworks.isGameOverlayEnabled(); } static get dlcCount() { if (!this.greenworks) { return 0; } return this.greenworks.getDLCCount(); } static get friendCount() { if (!this.greenworks) { return 0; } return this.greenworks.getFriendCount(this.greenworks.FriendFlags.Immediate); } static get cloudEnabled() { if (!this.greenworks) { return false; } return this.greenworks.isCloudEnabled(); } static get userCloudEnabled() { if (!this.greenworks) { return false; } return this.greenworks.isCloudEnabledForUser(); } static activateAchievement(achievementId) { if (!achievementId) { console.error('Achievement name not provided.'); return; } if (!this.greenworks) { console.log(`Activate Achievement ${ achievementId }`); return; } if (!this.running) { return; } this.greenworks.activateAchievement(achievementId, () => { console.log(`Achievement activated: ${ achievementId }`); }, () => { console.log(`Failed to activate achievement: ${ achievementId }`); }); } static getAchievement(achievementId) { if (!achievementId) { console.error('Achievement name not provided.'); return false; } if (!this.greenworks) { return false; } if (!this.running) { return; } return this.greenworks.getAchievement(achievementId, () => { // #ToDo }, () => { console.log(`Failed to check achievement: ${ achievementId }`); }); } static clearAchievement(achievementId) { if (!achievementId) { console.error('Achievement name not provided.'); return; } if (!this.greenworks) { console.log(`Clear achievement ${ achievementId }`); return; } if (!this.running) { return; } this.greenworks.clearAchievement(achievementId, () => { console.log(`Successfully cleared achievement: ${ achievementId }`); }, () => { console.log(`Failed to clear achievement: ${ achievementId }`); }); } static activateGameOverlay(option) { if (!this.running) { return false; } this.greenworks.activateGameOverlay(option); } static activateGameOverlayToWebPage(url) { if (!this.running) { return false; } this.greenworks.activateGameOverlayToWebPage(url); } static isDLCInstalled(dlcAppId) { if (!this.running) { return false; } return this.greenworks.isDLCInstalled(dlcAppId); } static installDLC(dlcAppId) { if (!this.running) { return; } return this.greenworks.installDLC(dlcAppId); } static uninstallDLC(dlcAppId) { if (!this.running) { return; } return this.greenworks.uninstallDLC(dlcAppId); } static getStatInt(name) { if (!this.running) { return 0; } return this.greenworks.getStatInt(name); } static getStatFloat(name) { if (!this.running) { return 0; } return this.greenworks.getStatFloat(name); } static setStat(name, value) { console.log('Change Stat', name, value); if (!this.running) { return 0; } this.greenworks.setStat(name, value); } static storeStats() { console.log('Store Stats'); if (!this.running) { return; } this.greenworks.storeStats(() => { console.log('Store stats: success'); }, () => { console.log('Store stats: failed'); }); } static isSubscribedApp(appId) { if (!this.running) { return false; } return this.greenworks.isSubscribedApp(appId); } } globalThis.CycloneSteam = CycloneSteam; CycloneSteam.register(); })();