Prepare for the release on gamedistribution
BIN
artwork/gamedistribution/1280x550.jpg
Normal file
After Width: | Height: | Size: 367 KiB |
BIN
artwork/gamedistribution/1280x550.png
Normal file
After Width: | Height: | Size: 663 KiB |
BIN
artwork/gamedistribution/1280x720.jpg
Normal file
After Width: | Height: | Size: 569 KiB |
BIN
artwork/gamedistribution/1280x720.png
Normal file
After Width: | Height: | Size: 972 KiB |
BIN
artwork/gamedistribution/1280x720.psd
Normal file
BIN
artwork/gamedistribution/512x340.jpg
Normal file
After Width: | Height: | Size: 129 KiB |
BIN
artwork/gamedistribution/512x340.png
Normal file
After Width: | Height: | Size: 214 KiB |
BIN
artwork/gamedistribution/512x384.jpg
Normal file
After Width: | Height: | Size: 181 KiB |
BIN
artwork/gamedistribution/512x384.png
Normal file
After Width: | Height: | Size: 265 KiB |
BIN
artwork/gamedistribution/512x384.psd
Normal file
BIN
artwork/gamedistribution/512x512.jpg
Normal file
After Width: | Height: | Size: 214 KiB |
BIN
artwork/gamedistribution/512x512.png
Normal file
After Width: | Height: | Size: 353 KiB |
BIN
artwork/gamedistribution/iframe/iframe.zip
Normal file
85
artwork/gamedistribution/iframe/index.html
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>shapez.io</title>
|
||||||
|
<script type="text/javascript">
|
||||||
|
function postToGameFrame(msg) {
|
||||||
|
var handle = document.getElementById("gameframe");
|
||||||
|
if (handle) {
|
||||||
|
handle.contentWindow.postMessage(msg, "*");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener("message", function (event) {
|
||||||
|
if (event.data === "shapezio://gd.game_loaded") {
|
||||||
|
console.log("Game loaded");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.data === "shapezio://gd.show_ad") {
|
||||||
|
console.log("Got ad message");
|
||||||
|
gdsdk.showAd();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
window["GD_OPTIONS"] = {
|
||||||
|
gameId: "ac8e6fc04a6f46f990ac6a317bb4d74e",
|
||||||
|
onEvent: function (event) {
|
||||||
|
switch (event.name) {
|
||||||
|
case "SDK_GAME_START":
|
||||||
|
console.log("GDSDK: ad finished");
|
||||||
|
postToGameFrame("shapezio://gd.ad_finished");
|
||||||
|
setTimeout(function () {
|
||||||
|
document.getElementById("gameframe").focus();
|
||||||
|
console.log("(GD-PARENT_FRAME) successfully focused frame");
|
||||||
|
}, 500);
|
||||||
|
break;
|
||||||
|
case "SDK_GAME_PAUSE":
|
||||||
|
// pause game logic / mute audio
|
||||||
|
console.log("GDSDK: ad started");
|
||||||
|
postToGameFrame("shapezio://gd.ad_started");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
(function (d, s, id) {
|
||||||
|
var js,
|
||||||
|
fjs = d.getElementsByTagName(s)[0];
|
||||||
|
if (d.getElementById(id)) return;
|
||||||
|
js = d.createElement(s);
|
||||||
|
js.id = id;
|
||||||
|
js.src = "https://html5.api.gamedistribution.com/main.min.js";
|
||||||
|
fjs.parentNode.insertBefore(js, fjs);
|
||||||
|
})(document, "script", "gamedistribution-jssdk");
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
background: #222428;
|
||||||
|
text-align: center;
|
||||||
|
font-family: Arial;
|
||||||
|
color: #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
#gameframe {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border: 0;
|
||||||
|
display: block;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body style="background: #333;">
|
||||||
|
<iframe
|
||||||
|
onclick="this.focus()"
|
||||||
|
src="https://beta.shapez.io?embed=gamedistribution"
|
||||||
|
id="gameframe"
|
||||||
|
allowfullscreen
|
||||||
|
></iframe>
|
||||||
|
</body>
|
||||||
|
</html>
|
8
artwork/gamedistribution/iframe/manifest.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"name": "shapez.io",
|
||||||
|
"short_name": "shapez.io",
|
||||||
|
"start_url": "index.html",
|
||||||
|
"display": "standalone",
|
||||||
|
"background_color": "#222428",
|
||||||
|
"description": "shapez.io"
|
||||||
|
}
|
@ -72,6 +72,33 @@ body {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@include Text;
|
@include Text;
|
||||||
|
|
||||||
|
&.externalAdOpen {
|
||||||
|
&::before {
|
||||||
|
text-transform: uppercase;
|
||||||
|
@include SuperSmallText;
|
||||||
|
content: "Loading Advertisement...";
|
||||||
|
color: #333;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
pointer-events: all;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: rgba(50, 60, 70, 0.8);
|
||||||
|
z-index: 9999;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
color: #fff;
|
||||||
|
|
||||||
|
@include InlineAnimation(1s ease-in-out infinite) {
|
||||||
|
50% {
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// For recording the bg video
|
// For recording the bg video
|
||||||
|
|
||||||
// filter: blur(5px);
|
// filter: blur(5px);
|
||||||
|
@ -96,6 +96,7 @@ body.uiHidden {
|
|||||||
}
|
}
|
||||||
|
|
||||||
body.modalDialogActive,
|
body.modalDialogActive,
|
||||||
|
body.externalAdOpen,
|
||||||
body.ingameDialogOpen {
|
body.ingameDialogOpen {
|
||||||
> *:not(.ingameDialog):not(.modalDialogParent):not(.loadingDialog):not(.gameLoadingOverlay):not(#ingame_HUD_ModalDialogs) {
|
> *:not(.ingameDialog):not(.modalDialogParent):not(.loadingDialog):not(.gameLoadingOverlay):not(#ingame_HUD_ModalDialogs) {
|
||||||
filter: blur(5px) !important;
|
filter: blur(5px) !important;
|
||||||
|
@ -38,6 +38,7 @@ export let clickDetectorGlobals = {
|
|||||||
* targetOnly?: boolean,
|
* targetOnly?: boolean,
|
||||||
* maxDistance?: number,
|
* maxDistance?: number,
|
||||||
* clickSound?: string,
|
* clickSound?: string,
|
||||||
|
* preventClick?: boolean,
|
||||||
* }} ClickDetectorConstructorArgs
|
* }} ClickDetectorConstructorArgs
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -55,6 +56,7 @@ export class ClickDetector {
|
|||||||
* @param {boolean=} param1.targetOnly Whether to also accept clicks on child elements (e.target !== element)
|
* @param {boolean=} param1.targetOnly Whether to also accept clicks on child elements (e.target !== element)
|
||||||
* @param {number=} param1.maxDistance The maximum distance in pixels to accept clicks
|
* @param {number=} param1.maxDistance The maximum distance in pixels to accept clicks
|
||||||
* @param {string=} param1.clickSound Sound key to play on touchdown
|
* @param {string=} param1.clickSound Sound key to play on touchdown
|
||||||
|
* @param {boolean=} param1.preventClick Whether to prevent click events
|
||||||
*/
|
*/
|
||||||
constructor(
|
constructor(
|
||||||
element,
|
element,
|
||||||
@ -66,6 +68,7 @@ export class ClickDetector {
|
|||||||
targetOnly = false,
|
targetOnly = false,
|
||||||
maxDistance = MAX_MOVE_DISTANCE_PX,
|
maxDistance = MAX_MOVE_DISTANCE_PX,
|
||||||
clickSound = SOUNDS.uiClick,
|
clickSound = SOUNDS.uiClick,
|
||||||
|
preventClick = false,
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
assert(element, "No element given!");
|
assert(element, "No element given!");
|
||||||
@ -78,6 +81,7 @@ export class ClickDetector {
|
|||||||
this.targetOnly = targetOnly;
|
this.targetOnly = targetOnly;
|
||||||
this.clickSound = clickSound;
|
this.clickSound = clickSound;
|
||||||
this.maxDistance = maxDistance;
|
this.maxDistance = maxDistance;
|
||||||
|
this.preventClick = preventClick;
|
||||||
|
|
||||||
// Signals
|
// Signals
|
||||||
this.click = new Signal();
|
this.click = new Signal();
|
||||||
@ -128,6 +132,10 @@ export class ClickDetector {
|
|||||||
this.element.removeEventListener("mousemove", this.handlerTouchMove, options);
|
this.element.removeEventListener("mousemove", this.handlerTouchMove, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.preventClick) {
|
||||||
|
this.element.removeEventListener("click", this.handlerPreventClick, options);
|
||||||
|
}
|
||||||
|
|
||||||
this.click.removeAll();
|
this.click.removeAll();
|
||||||
this.touchstart.removeAll();
|
this.touchstart.removeAll();
|
||||||
this.touchmove.removeAll();
|
this.touchmove.removeAll();
|
||||||
@ -142,6 +150,14 @@ export class ClickDetector {
|
|||||||
|
|
||||||
// INTERNAL METHODS
|
// INTERNAL METHODS
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {Event} event
|
||||||
|
*/
|
||||||
|
internalPreventClick(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal method to get the options to pass to an event listener
|
* Internal method to get the options to pass to an event listener
|
||||||
*/
|
*/
|
||||||
@ -164,6 +180,11 @@ export class ClickDetector {
|
|||||||
this.handlerTouchMove = this.internalOnPointerMove.bind(this);
|
this.handlerTouchMove = this.internalOnPointerMove.bind(this);
|
||||||
this.handlerTouchCancel = this.internalOnTouchCancel.bind(this);
|
this.handlerTouchCancel = this.internalOnTouchCancel.bind(this);
|
||||||
|
|
||||||
|
if (this.preventClick) {
|
||||||
|
this.handlerPreventClick = this.internalPreventClick.bind(this);
|
||||||
|
element.addEventListener("click", this.handlerPreventClick, options);
|
||||||
|
}
|
||||||
|
|
||||||
element.addEventListener("touchstart", this.handlerTouchStart, options);
|
element.addEventListener("touchstart", this.handlerTouchStart, options);
|
||||||
element.addEventListener("touchend", this.handlerTouchEnd, options);
|
element.addEventListener("touchend", this.handlerTouchEnd, options);
|
||||||
element.addEventListener("touchcancel", this.handlerTouchCancel, options);
|
element.addEventListener("touchcancel", this.handlerTouchCancel, options);
|
||||||
|
@ -101,6 +101,7 @@ export const globalConfig = {
|
|||||||
// framePausesBetweenTicks: 40,
|
// framePausesBetweenTicks: 40,
|
||||||
// testTranslations: true,
|
// testTranslations: true,
|
||||||
// enableEntityInspector: true,
|
// enableEntityInspector: true,
|
||||||
|
testAds: true,
|
||||||
/* dev:end */
|
/* dev:end */
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ export class HUDUnlockNotification extends BaseHUDPart {
|
|||||||
this.btnClose.innerText = "Next level";
|
this.btnClose.innerText = "Next level";
|
||||||
dialog.appendChild(this.btnClose);
|
dialog.appendChild(this.btnClose);
|
||||||
|
|
||||||
this.trackClicks(this.btnClose, this.close);
|
this.trackClicks(this.btnClose, this.requestClose);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -119,6 +119,12 @@ export class HUDUnlockNotification extends BaseHUDPart {
|
|||||||
this.root.soundProxy.playUi(SOUNDS.levelComplete);
|
this.root.soundProxy.playUi(SOUNDS.levelComplete);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
requestClose() {
|
||||||
|
this.root.app.adProvider.showVideoAd().then(() => {
|
||||||
|
this.close();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
close() {
|
close() {
|
||||||
this.visible = false;
|
this.visible = false;
|
||||||
}
|
}
|
||||||
|
125
src/js/platform/ad_providers/gamedistribution.js
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
/* typehints:start */
|
||||||
|
import { Application } from "../../application";
|
||||||
|
/* typehints:end */
|
||||||
|
|
||||||
|
import { AdProviderInterface } from "../ad_provider";
|
||||||
|
import { performanceNow } from "../../core/builtins";
|
||||||
|
import { createLogger } from "../../core/logging";
|
||||||
|
|
||||||
|
const minimumTimeBetweenVideoAdsMs = G_IS_DEV ? 1 : 5 * 60 * 1000;
|
||||||
|
|
||||||
|
const logger = createLogger("gamedistribution");
|
||||||
|
|
||||||
|
export class GamedistributionAdProvider extends AdProviderInterface {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {Application} app
|
||||||
|
*/
|
||||||
|
constructor(app) {
|
||||||
|
super(app);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The resolve function to finish the current video ad. Null if none is currently running
|
||||||
|
* @type {Function}
|
||||||
|
*/
|
||||||
|
this.videoAdResolveFunction = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current timer which will timeout the resolve
|
||||||
|
*/
|
||||||
|
this.videoAdResolveTimer = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When we showed the last video ad
|
||||||
|
*/
|
||||||
|
this.lastVideoAdShowTime = -1e20;
|
||||||
|
|
||||||
|
console.error("X");
|
||||||
|
}
|
||||||
|
|
||||||
|
getHasAds() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
getCanShowVideoAd() {
|
||||||
|
return (
|
||||||
|
this.getHasAds() &&
|
||||||
|
!this.videoAdResolveFunction &&
|
||||||
|
performanceNow() - this.lastVideoAdShowTime > minimumTimeBetweenVideoAdsMs
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize() {
|
||||||
|
// No point to initialize everything if ads are not supported
|
||||||
|
if (!this.getHasAds()) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.log("🎬 Initializing gamedistribution ads");
|
||||||
|
|
||||||
|
try {
|
||||||
|
parent.postMessage("shapezio://gd.game_loaded", "*");
|
||||||
|
} catch (ex) {
|
||||||
|
return Promise.reject("Frame communication not allowed");
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener(
|
||||||
|
"message",
|
||||||
|
event => {
|
||||||
|
if (event.data === "shapezio://gd.ad_started") {
|
||||||
|
console.log("🎬 Got ad started callback");
|
||||||
|
} else if (event.data === "shapezio://gd.ad_finished") {
|
||||||
|
console.log("🎬 Got ad finished callback");
|
||||||
|
if (this.videoAdResolveFunction) {
|
||||||
|
this.videoAdResolveFunction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
showVideoAd() {
|
||||||
|
assert(this.getHasAds(), "Called showVideoAd but ads are not supported!");
|
||||||
|
assert(!this.videoAdResolveFunction, "Video ad still running, can not show again!");
|
||||||
|
this.lastVideoAdShowTime = performanceNow();
|
||||||
|
|
||||||
|
console.log("🎬 Gamedistribution: Start ad");
|
||||||
|
try {
|
||||||
|
parent.postMessage("shapezio://gd.show_ad", "*");
|
||||||
|
} catch (ex) {
|
||||||
|
logger.warn("🎬 Failed to send message for gd ad:", ex);
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
document.body.classList.add("externalAdOpen");
|
||||||
|
|
||||||
|
return new Promise(resolve => {
|
||||||
|
// So, wait for the remove call but also remove after N seconds
|
||||||
|
this.videoAdResolveFunction = () => {
|
||||||
|
this.videoAdResolveFunction = null;
|
||||||
|
clearTimeout(this.videoAdResolveTimer);
|
||||||
|
this.videoAdResolveTimer = null;
|
||||||
|
|
||||||
|
// When the ad closed, also set the time
|
||||||
|
this.lastVideoAdShowTime = performanceNow();
|
||||||
|
resolve();
|
||||||
|
};
|
||||||
|
|
||||||
|
this.videoAdResolveTimer = setTimeout(() => {
|
||||||
|
logger.warn("Automatically closing ad after not receiving callback");
|
||||||
|
if (this.videoAdResolveFunction) {
|
||||||
|
this.videoAdResolveFunction();
|
||||||
|
}
|
||||||
|
}, 35000);
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
logger.error(this, "Error while resolving video ad:", err);
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
document.body.classList.remove("externalAdOpen");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -1,47 +0,0 @@
|
|||||||
import { AdProviderInterface } from "../ad_provider";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stores information about where we are iframed
|
|
||||||
*/
|
|
||||||
export class EmbedProvider {
|
|
||||||
/**
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
getId() {
|
|
||||||
abstract;
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether this provider supports ads
|
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
getSupportsAds() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the ad provider
|
|
||||||
* @returns {typeof AdProviderInterface}
|
|
||||||
*/
|
|
||||||
getAdProvider() {
|
|
||||||
abstract;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whetherexternal links are supported
|
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
getSupportsExternalLinks() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether this provider is iframed
|
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
getIsIframed() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
import { AdinplayAdProvider } from "../../ad_providers/adinplay";
|
|
||||||
import { ShapezioWebsiteEmbedProvider } from "./shapezio_website";
|
|
||||||
|
|
||||||
export class ArmorgamesEmbedProvider extends ShapezioWebsiteEmbedProvider {
|
|
||||||
getId() {
|
|
||||||
return "armorgames";
|
|
||||||
}
|
|
||||||
|
|
||||||
getAdProvider() {
|
|
||||||
return AdinplayAdProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
getIsIframed() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
import { ShapezioWebsiteEmbedProvider } from "./shapezio_website";
|
|
||||||
|
|
||||||
export class CrazygamesEmbedProvider extends ShapezioWebsiteEmbedProvider {
|
|
||||||
getId() {
|
|
||||||
return "crazygames";
|
|
||||||
}
|
|
||||||
|
|
||||||
getIsIframed() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
import { AdinplayAdProvider } from "../../ad_providers/adinplay";
|
|
||||||
import { ShapezioWebsiteEmbedProvider } from "./shapezio_website";
|
|
||||||
|
|
||||||
export class GamedistributionEmbedProvider extends ShapezioWebsiteEmbedProvider {
|
|
||||||
getId() {
|
|
||||||
return "gamedistribution";
|
|
||||||
}
|
|
||||||
|
|
||||||
getAdProvider() {
|
|
||||||
return AdinplayAdProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
getSupportsExternalLinks() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
getIsIframed() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
import { ShapezioWebsiteEmbedProvider } from "./shapezio_website";
|
|
||||||
|
|
||||||
export class IogamesSpaceEmbedProvider extends ShapezioWebsiteEmbedProvider {
|
|
||||||
getId() {
|
|
||||||
return "iogames.space";
|
|
||||||
}
|
|
||||||
|
|
||||||
getShowUpvoteHints() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
getIsIframed() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
import { NoAdProvider } from "../../ad_providers/no_ad_provider";
|
|
||||||
import { EmbedProvider } from "../embed_provider";
|
|
||||||
|
|
||||||
export class KongregateEmbedProvider extends EmbedProvider {
|
|
||||||
getId() {
|
|
||||||
return "kongregate";
|
|
||||||
}
|
|
||||||
|
|
||||||
getSupportsAds() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
getAdProvider() {
|
|
||||||
return NoAdProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
getSupportsExternalLinks() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
getIsIframed() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
import { ShapezioWebsiteEmbedProvider } from "./shapezio_website";
|
|
||||||
|
|
||||||
export class MiniclipEmbedProvider extends ShapezioWebsiteEmbedProvider {
|
|
||||||
getId() {
|
|
||||||
return "miniclip";
|
|
||||||
}
|
|
||||||
|
|
||||||
getIsIframed() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
import { EmbedProvider } from "../embed_provider";
|
|
||||||
import { AdinplayAdProvider } from "../../ad_providers/adinplay";
|
|
||||||
|
|
||||||
export class ShapezioWebsiteEmbedProvider extends EmbedProvider {
|
|
||||||
getId() {
|
|
||||||
return "shapezio";
|
|
||||||
}
|
|
||||||
|
|
||||||
getSupportsAds() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
getAdProvider() {
|
|
||||||
return AdinplayAdProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
getIsIframed() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
getSupportsExternalLinks() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,18 +1,11 @@
|
|||||||
import { Math_min } from "../../core/builtins";
|
import { Math_min } from "../../core/builtins";
|
||||||
|
import { globalConfig, IS_MOBILE } from "../../core/config";
|
||||||
import { createLogger } from "../../core/logging";
|
import { createLogger } from "../../core/logging";
|
||||||
import { queryParamOptions } from "../../core/query_parameters";
|
import { queryParamOptions } from "../../core/query_parameters";
|
||||||
import { clamp } from "../../core/utils";
|
import { clamp } from "../../core/utils";
|
||||||
import { globalConfig, IS_MOBILE } from "../../core/config";
|
|
||||||
import { NoAdProvider } from "../ad_providers/no_ad_provider";
|
import { NoAdProvider } from "../ad_providers/no_ad_provider";
|
||||||
import { PlatformWrapperInterface } from "../wrapper";
|
import { PlatformWrapperInterface } from "../wrapper";
|
||||||
import { ShapezioWebsiteEmbedProvider } from "./embed_providers/shapezio_website";
|
import { GamedistributionAdProvider } from "../ad_providers/gamedistribution";
|
||||||
import { ArmorgamesEmbedProvider } from "./embed_providers/armorgames";
|
|
||||||
import { IogamesSpaceEmbedProvider } from "./embed_providers/iogames_space";
|
|
||||||
import { MiniclipEmbedProvider } from "./embed_providers/miniclip";
|
|
||||||
import { GamedistributionEmbedProvider } from "./embed_providers/gamedistribution";
|
|
||||||
import { KongregateEmbedProvider } from "./embed_providers/kongregate";
|
|
||||||
import { CrazygamesEmbedProvider } from "./embed_providers/crazygames";
|
|
||||||
import { EmbedProvider } from "./embed_provider";
|
|
||||||
|
|
||||||
const logger = createLogger("platform/browser");
|
const logger = createLogger("platform/browser");
|
||||||
|
|
||||||
@ -20,39 +13,50 @@ export class PlatformWrapperImplBrowser extends PlatformWrapperInterface {
|
|||||||
initialize() {
|
initialize() {
|
||||||
this.recaptchaTokenCallback = null;
|
this.recaptchaTokenCallback = null;
|
||||||
|
|
||||||
this.embedProvider = new ShapezioWebsiteEmbedProvider();
|
this.embedProvider = {
|
||||||
|
id: "shapezio-website",
|
||||||
|
adProvider: NoAdProvider,
|
||||||
|
iframed: false,
|
||||||
|
externalLinks: true,
|
||||||
|
iogLink: true,
|
||||||
|
};
|
||||||
|
|
||||||
if (!G_IS_STANDALONE && queryParamOptions.embedProvider) {
|
if (!G_IS_STANDALONE && queryParamOptions.embedProvider) {
|
||||||
const providerId = queryParamOptions.embedProvider;
|
const providerId = queryParamOptions.embedProvider;
|
||||||
|
this.embedProvider.iframed = true;
|
||||||
|
this.embedProvider.iogLink = false;
|
||||||
|
|
||||||
switch (providerId) {
|
switch (providerId) {
|
||||||
case "armorgames": {
|
case "armorgames": {
|
||||||
this.embedProvider = new ArmorgamesEmbedProvider();
|
this.embedProvider.id = "armorgames";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case "iogames.space": {
|
case "iogames.space": {
|
||||||
this.embedProvider = new IogamesSpaceEmbedProvider();
|
this.embedProvider.id = "iogames.space";
|
||||||
|
this.embedProvider.iogLink = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case "miniclip": {
|
case "miniclip": {
|
||||||
this.embedProvider = new MiniclipEmbedProvider();
|
this.embedProvider.id = "miniclip";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case "gamedistribution": {
|
case "gamedistribution": {
|
||||||
this.embedProvider = new GamedistributionEmbedProvider();
|
this.embedProvider.id = "gamedistribution";
|
||||||
|
this.embedProvider.externalLinks = false;
|
||||||
|
this.embedProvider.adProvider = GamedistributionAdProvider;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case "kongregate": {
|
case "kongregate": {
|
||||||
this.embedProvider = new KongregateEmbedProvider();
|
this.embedProvider.id = "kongregate";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case "crazygames": {
|
case "crazygames": {
|
||||||
this.embedProvider = new CrazygamesEmbedProvider();
|
this.embedProvider.id = "crazygames";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,27 +66,9 @@ export class PlatformWrapperImplBrowser extends PlatformWrapperInterface {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.log("Embed provider:", this.embedProvider.getId());
|
logger.log("Embed provider:", this.embedProvider.id);
|
||||||
|
|
||||||
return super.initialize().then(() => {
|
return super.initialize().then(() => this.initializeAdProvider());
|
||||||
// SENTRY
|
|
||||||
if (!G_IS_DEV && false) {
|
|
||||||
logger.log(this, "Loading sentry");
|
|
||||||
const sentryTag = document.createElement("script");
|
|
||||||
sentryTag.src = "https://browser.sentry-cdn.com/5.7.1/bundle.min.js";
|
|
||||||
sentryTag.setAttribute("integrity", "TODO_SENTRY");
|
|
||||||
sentryTag.setAttribute("crossorigin", "anonymous");
|
|
||||||
sentryTag.addEventListener("load", this.onSentryLoaded.bind(this));
|
|
||||||
document.head.appendChild(sentryTag);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {EmbedProvider}
|
|
||||||
*/
|
|
||||||
getEmbedProvider() {
|
|
||||||
return this.embedProvider;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onSentryLoaded() {
|
onSentryLoaded() {
|
||||||
@ -151,7 +137,7 @@ export class PlatformWrapperImplBrowser extends PlatformWrapperInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getId() {
|
getId() {
|
||||||
return "browser@" + this.embedProvider.getId();
|
return "browser@" + this.embedProvider.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
getUiScale() {
|
getUiScale() {
|
||||||
@ -173,16 +159,15 @@ export class PlatformWrapperImplBrowser extends PlatformWrapperInterface {
|
|||||||
|
|
||||||
openExternalLink(url, force = false) {
|
openExternalLink(url, force = false) {
|
||||||
logger.log("Opening external:", url);
|
logger.log("Opening external:", url);
|
||||||
// if (force || this.embedProvider.getSupportsExternalLinks()) {
|
if (force || this.embedProvider.externalLinks) {
|
||||||
window.open(url);
|
window.open(url);
|
||||||
// } else {
|
} else {
|
||||||
// // Do nothing
|
// Do nothing
|
||||||
// alert("This platform does not allow opening external links. You can play on the website directly to open them.");
|
alert(
|
||||||
// }
|
"This platform does not allow opening external links. You can play on https://shapez.io directly to open them.\n\nClicked Link: " +
|
||||||
}
|
url
|
||||||
|
);
|
||||||
getSupportsAds() {
|
}
|
||||||
return this.embedProvider.getSupportsAds();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
performRestart() {
|
performRestart() {
|
||||||
@ -218,16 +203,18 @@ export class PlatformWrapperImplBrowser extends PlatformWrapperInterface {
|
|||||||
|
|
||||||
initializeAdProvider() {
|
initializeAdProvider() {
|
||||||
if (G_IS_DEV && !globalConfig.debug.testAds) {
|
if (G_IS_DEV && !globalConfig.debug.testAds) {
|
||||||
|
logger.log("Ads disabled in local environment");
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
// First, detect adblocker
|
// First, detect adblocker
|
||||||
return this.detectAdblock().then(hasAdblocker => {
|
return this.detectAdblock().then(hasAdblocker => {
|
||||||
if (hasAdblocker) {
|
if (hasAdblocker) {
|
||||||
|
logger.log("Adblock detected");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const adProvider = this.embedProvider.getAdProvider();
|
const adProvider = this.embedProvider.adProvider;
|
||||||
this.app.adProvider = new adProvider(this.app);
|
this.app.adProvider = new adProvider(this.app);
|
||||||
return this.app.adProvider.initialize().catch(err => {
|
return this.app.adProvider.initialize().catch(err => {
|
||||||
logger.error("Failed to initialize ad provider, disabling ads:", err);
|
logger.error("Failed to initialize ad provider, disabling ads:", err);
|
||||||
|
@ -28,7 +28,16 @@ export class AboutState extends TextualGameState {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
onEnter() {}
|
onEnter() {
|
||||||
|
const links = this.htmlElement.querySelectorAll("a[href]");
|
||||||
|
links.forEach(link => {
|
||||||
|
this.trackClicks(
|
||||||
|
link,
|
||||||
|
() => this.app.platformWrapper.openExternalLink(link.getAttribute("href")),
|
||||||
|
{ preventClick: true }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
getDefaultPreviousState() {
|
getDefaultPreviousState() {
|
||||||
return "SettingsState";
|
return "SettingsState";
|
||||||
|
@ -11,6 +11,7 @@ import {
|
|||||||
import { ReadWriteProxy } from "../core/read_write_proxy";
|
import { ReadWriteProxy } from "../core/read_write_proxy";
|
||||||
import { HUDModalDialogs } from "../game/hud/parts/modal_dialogs";
|
import { HUDModalDialogs } from "../game/hud/parts/modal_dialogs";
|
||||||
import { T } from "../translations";
|
import { T } from "../translations";
|
||||||
|
import { PlatformWrapperImplBrowser } from "../platform/browser/wrapper";
|
||||||
|
|
||||||
export class MainMenuState extends GameState {
|
export class MainMenuState extends GameState {
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -76,23 +77,25 @@ export class MainMenuState extends GameState {
|
|||||||
|
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
|
|
||||||
<a href="${THIRDPARTY_URLS.github}" target="_blank">
|
<a class="githubLink" target="_blank">
|
||||||
${T.mainMenu.openSourceHint}
|
${T.mainMenu.openSourceHint}
|
||||||
<span class="thirdpartyLogo githubLogo"></span>
|
<span class="thirdpartyLogo githubLogo"></span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a href="${THIRDPARTY_URLS.discord}" target="_blank">
|
<a class="discordLink" target="_blank">
|
||||||
${T.mainMenu.discordLink}
|
${T.mainMenu.discordLink}
|
||||||
<span class="thirdpartyLogo discordLogo"></span>
|
<span class="thirdpartyLogo discordLogo"></span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
${
|
${
|
||||||
G_IS_BROWSER
|
G_IS_BROWSER &&
|
||||||
|
this.app.platformWrapper instanceof PlatformWrapperImplBrowser &&
|
||||||
|
this.app.platformWrapper.embedProvider.iogLink
|
||||||
? `<a class="iogLink" target="_blank" href="https://iogames.space">More .io games</a>`
|
? `<a class="iogLink" target="_blank" href="https://iogames.space">More .io games</a>`
|
||||||
: ""
|
: ""
|
||||||
}
|
}
|
||||||
|
|
||||||
<div class="author">Made by <a href="https://tobspr.com" target="_blank">Tobias Springer</a></div>
|
<div class="author">Made by <a class="producerLink" target="_blank">Tobias Springer</a></div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
@ -100,6 +103,7 @@ export class MainMenuState extends GameState {
|
|||||||
|
|
||||||
requestImportSavegame() {
|
requestImportSavegame() {
|
||||||
if (IS_DEMO && this.app.savegameMgr.getSavegamesMetaData().length > 0) {
|
if (IS_DEMO && this.app.savegameMgr.getSavegamesMetaData().length > 0) {
|
||||||
|
this.app.analytics.trackUiClick("importgame_slot_limit_show");
|
||||||
this.dialogs.showWarning(T.dialogs.oneSavegameLimit.title, T.dialogs.oneSavegameLimit.desc);
|
this.dialogs.showWarning(T.dialogs.oneSavegameLimit.title, T.dialogs.oneSavegameLimit.desc);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -210,16 +214,36 @@ export class MainMenuState extends GameState {
|
|||||||
|
|
||||||
this.renderSavegames();
|
this.renderSavegames();
|
||||||
|
|
||||||
const steamLinks = this.htmlElement.querySelectorAll(".steamLink");
|
const steamLink = this.htmlElement.querySelector(".steamLink");
|
||||||
steamLinks.forEach(steamLink => {
|
if (steamLink) {
|
||||||
steamLink.addEventListener("click", this.onSteamLinkClicked.bind(this));
|
this.trackClicks(steamLink, () => this.onSteamLinkClicked(), { preventClick: true });
|
||||||
});
|
}
|
||||||
|
|
||||||
|
const discordLink = this.htmlElement.querySelector(".discordLink");
|
||||||
|
this.trackClicks(
|
||||||
|
discordLink,
|
||||||
|
() => this.app.platformWrapper.openExternalLink(THIRDPARTY_URLS.discord),
|
||||||
|
{ preventClick: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
const githubLink = this.htmlElement.querySelector(".githubLink");
|
||||||
|
this.trackClicks(
|
||||||
|
githubLink,
|
||||||
|
() => this.app.platformWrapper.openExternalLink(THIRDPARTY_URLS.github),
|
||||||
|
{ preventClick: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
const producerLink = this.htmlElement.querySelector(".producerLink");
|
||||||
|
this.trackClicks(
|
||||||
|
producerLink,
|
||||||
|
() => this.app.platformWrapper.openExternalLink("https://tobspr.com"),
|
||||||
|
{ preventClick: true }
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
onSteamLinkClicked(event) {
|
onSteamLinkClicked() {
|
||||||
this.app.analytics.trackUiClick("main_menu_steam_link");
|
this.app.analytics.trackUiClick("main_menu_steam_link");
|
||||||
window.open(THIRDPARTY_URLS.standaloneStorePage);
|
this.app.platformWrapper.openExternalLink(THIRDPARTY_URLS.standaloneStorePage);
|
||||||
event.preventDefault();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -272,15 +296,13 @@ export class MainMenuState extends GameState {
|
|||||||
resumeGame(game) {
|
resumeGame(game) {
|
||||||
this.app.analytics.trackUiClick("resume_game");
|
this.app.analytics.trackUiClick("resume_game");
|
||||||
|
|
||||||
// if (IS_DEMO) {
|
this.app.adProvider.showVideoAd().then(() => {
|
||||||
// this.dialogs.showFeatureRestrictionInfo(T.demo.features.restoringGames);
|
this.app.analytics.trackUiClick("resume_game_adcomplete");
|
||||||
// return;
|
const savegame = this.app.savegameMgr.getSavegameById(game.internalId);
|
||||||
// }
|
savegame.readAsync().then(() => {
|
||||||
|
this.moveToState("InGameState", {
|
||||||
const savegame = this.app.savegameMgr.getSavegameById(game.internalId);
|
savegame,
|
||||||
savegame.readAsync().then(() => {
|
});
|
||||||
this.moveToState("InGameState", {
|
|
||||||
savegame,
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -289,6 +311,8 @@ export class MainMenuState extends GameState {
|
|||||||
* @param {object} game
|
* @param {object} game
|
||||||
*/
|
*/
|
||||||
deleteGame(game) {
|
deleteGame(game) {
|
||||||
|
this.app.analytics.trackUiClick("delete_game");
|
||||||
|
|
||||||
const signals = this.dialogs.showWarning(
|
const signals = this.dialogs.showWarning(
|
||||||
T.dialogs.confirmSavegameDelete.title,
|
T.dialogs.confirmSavegameDelete.title,
|
||||||
T.dialogs.confirmSavegameDelete.text,
|
T.dialogs.confirmSavegameDelete.text,
|
||||||
@ -329,20 +353,26 @@ export class MainMenuState extends GameState {
|
|||||||
|
|
||||||
doStartNewGame() {
|
doStartNewGame() {
|
||||||
this.app.analytics.trackUiClick("startgame");
|
this.app.analytics.trackUiClick("startgame");
|
||||||
const savegame = this.app.savegameMgr.createNewSavegame();
|
|
||||||
|
|
||||||
this.moveToState("InGameState", {
|
this.app.adProvider.showVideoAd().then(() => {
|
||||||
savegame,
|
const savegame = this.app.savegameMgr.createNewSavegame();
|
||||||
|
|
||||||
|
this.moveToState("InGameState", {
|
||||||
|
savegame,
|
||||||
|
});
|
||||||
|
this.app.analytics.trackUiClick("startgame_adcomplete");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onPlayButtonClicked() {
|
onPlayButtonClicked() {
|
||||||
if (IS_DEMO && this.app.savegameMgr.getSavegamesMetaData().length > 0) {
|
if (IS_DEMO && this.app.savegameMgr.getSavegamesMetaData().length > 0) {
|
||||||
|
this.app.analytics.trackUiClick("startgame_slot_limit_show");
|
||||||
this.dialogs.showWarning(T.dialogs.oneSavegameLimit.title, T.dialogs.oneSavegameLimit.desc);
|
this.dialogs.showWarning(T.dialogs.oneSavegameLimit.title, T.dialogs.oneSavegameLimit.desc);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IS_DEMO) {
|
if (IS_DEMO) {
|
||||||
|
this.app.analytics.trackUiClick("startgame_pre_show");
|
||||||
const { ok } = this.dialogs.showWarning(
|
const { ok } = this.dialogs.showWarning(
|
||||||
T.dialogs.demoExplanation.title,
|
T.dialogs.demoExplanation.title,
|
||||||
T.dialogs.demoExplanation.desc
|
T.dialogs.demoExplanation.desc
|
||||||
|