idk
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"name": "Exfiltration Template",
|
||||
"short_name": "Exfiltration Template",
|
||||
"description": "",
|
||||
"start_url": "index.html",
|
||||
"display": "fullscreen",
|
||||
"orientation": "landscape",
|
||||
"background_color": "#ffffff",
|
||||
"icons": [
|
||||
{
|
||||
"src": "icons/icon-16.png",
|
||||
"sizes": "16x16",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "icons/icon-32.png",
|
||||
"sizes": "32x32",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "icons/icon-64.png",
|
||||
"sizes": "64x64",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "icons/icon-128.png",
|
||||
"sizes": "128x128",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "icons/icon-256.png",
|
||||
"sizes": "256x256",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "icons/icon-512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 915 B |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 6.9 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 676 B |
@@ -0,0 +1,41 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Exfiltration Template</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
|
||||
|
||||
<meta name="generator" content="Scirra Construct">
|
||||
|
||||
<link rel="manifest" href="appmanifest.json">
|
||||
<link rel="apple-touch-icon" sizes="128x128" href="icons/icon-128.png">
|
||||
<link rel="apple-touch-icon" sizes="256x256" href="icons/icon-256.png">
|
||||
<link rel="apple-touch-icon" sizes="512x512" href="icons/icon-512.png">
|
||||
<link rel="icon" type="image/png" href="icons/icon-512.png">
|
||||
|
||||
<link rel="stylesheet" href="style.css">
|
||||
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script>
|
||||
if (location.protocol.substr(0, 4) === "file")
|
||||
{
|
||||
alert("Web exports won't work until you upload them. (When running on the file: protocol, browsers block many features from working for security reasons.)");
|
||||
}
|
||||
</script>
|
||||
|
||||
<noscript>
|
||||
<div id="notSupportedWrap">
|
||||
<h2 id="notSupportedTitle">This content requires JavaScript</h2>
|
||||
<p class="notSupportedMessage">JavaScript appears to be disabled. Please enable it to view this content.</p>
|
||||
</div>
|
||||
</noscript>
|
||||
<script src="scripts/supportcheck.js"></script>
|
||||
<script src="scripts/offlineclient.js" type="module"></script>
|
||||
<script src="scripts/main.js" type="module"></script>
|
||||
<script src="scripts/register-sw.js" type="module"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1 @@
|
||||
{"version":1704387809121,"fileList":["scripts/c3runtime.js","data.json","style.css","scripts/offlineclient.js","images/shared-0-sheet1.webp","images/shared-0-sheet0.webp","images/shared-0-sheet3.webp","images/shared-0-sheet2.webp","images/ground-sheet0.webp","images/ceiling-sheet0.webp","scripts/main.js","scripts/dispatchworker.js","scripts/jobworker.js","scripts/supportcheck.js","scripts/project/main.js","icons/icon-32.png","icons/icon-64.png","icons/icon-16.png","icons/icon-256.png","icons/icon-512.png","icons/loading-logo.png","icons/icon-128.png","scripts/register-sw.js"]}
|
||||
@@ -0,0 +1,10 @@
|
||||
'use strict';self.inputPort=null;self.jobQueue=[];self.jobWorkers=[];self.sentBlobs=[];self.sentBuffers=[];self.importedScripts=[];self.lastBroadcasts=new Map;
|
||||
class JobWorker{constructor(port,number){this._port=port;this._number=number;this._isReady=false;this._isBusy=false;this._port.onmessage=e=>this._OnMessage(e.data)}ImportScripts(scripts){this._port.postMessage({"type":"_import_scripts","scripts":scripts})}SendBlob(blob,id){this._port.postMessage({"type":"_send_blob","blob":blob,"id":id})}SendBuffer(buffer,id){this._port.postMessage({"type":"_send_buffer","buffer":buffer,"id":id})}SendJob(job){if(this._isBusy||!this._isReady)throw new Error("cannot take job");
|
||||
this._isBusy=true;this._port.postMessage(job,job["transferables"])}_InitBroadcast(job){this._port.postMessage(job,job["transferables"])}SendReady(){this._port.postMessage({"type":"_ready"})}IsReady(){return this._isReady}_OnReady(){this._isReady=true;this.MaybeStartNextJob()}IsBusy(){return this._isBusy}GetNumber(){return this._number}_OnMessage(msg){const type=msg["type"];switch(type){case "ready":this._OnReady();return;case "done":this._OnJobDone();return;default:console.error("unknown message from worker '"+
|
||||
type+"'");return}}_OnJobDone(){this._isBusy=false;this.MaybeStartNextJob()}MaybeStartNextJob(){if(this._isBusy||!this._isReady)return;const i=this._FindAvailableJob();if(i===-1)return;const job=self.jobQueue[i];const isBroadcast=job["isBroadcast"];if(isBroadcast){job["doneFlags"][this._number]=true;if(job["doneFlags"].every(x=>x))self.jobQueue.splice(i,1)}else self.jobQueue.splice(i,1);this.SendJob(job)}_FindAvailableJob(){for(let i=0,len=self.jobQueue.length;i<len;++i){const job=self.jobQueue[i];
|
||||
if(typeof job["maxWorkerNum"]==="number"&&this._number>=job["maxWorkerNum"])continue;if(!job["isBroadcast"]||this._number<job["doneFlags"].length&&!job["doneFlags"][this._number])return i}return-1}}let number=0;
|
||||
function AddJobWorker(port){const jobWorker=new JobWorker(port,number++);self.jobWorkers.push(jobWorker);for(const [blob,id]of self.sentBlobs)jobWorker.SendBlob(blob,id);for(const [buffer,id]of self.sentBuffers)jobWorker.SendBuffer(buffer,id);for(const scripts of self.importedScripts)jobWorker.ImportScripts(scripts);for(const broadcastJob of self.lastBroadcasts.values())jobWorker._InitBroadcast(broadcastJob);jobWorker.SendReady()}
|
||||
function CancelJob(jobId){for(let i=0,len=self.jobQueue.length;i<len;++i)if(self.jobQueue[i].jobId===jobId){self.jobQueue.splice(i,1);return}}self.addEventListener("message",e=>{const msg=e.data;const type=msg["type"];if(type==="_init"){self.inputPort=msg["in-port"];self.inputPort.onmessage=OnInputPortMessage}else if(type==="_addJobWorker")AddJobWorker(msg["port"])});
|
||||
function OnInputPortMessage(e){const msg=e.data;const type=msg["type"];if(type==="_cancel"){CancelJob(msg.jobId);return}else if(type==="_import_scripts"){const scripts=msg["scripts"];for(const w of self.jobWorkers)w.ImportScripts(scripts);self.importedScripts.push(scripts);return}else if(type==="_send_blob"){const blob=msg["blob"];const id=msg["id"];for(const w of self.jobWorkers)w.SendBlob(blob,id);self.sentBlobs.push([blob,id]);return}else if(type==="_send_buffer"){const buffer=msg["buffer"];const id=
|
||||
msg["id"];for(const w of self.jobWorkers)w.SendBuffer(buffer,id);self.sentBuffers.push([buffer,id]);return}else if(type==="_no_more_workers"){self.sentBlobs.length=0;self.sentBuffers.length=0;self.importedScripts.length=0;self.lastBroadcasts.clear();return}self.jobQueue.push(msg);if(msg["isBroadcast"]){const maxWorkerNum=msg["maxWorkerNum"];const curWorkerCount=self.jobWorkers.length;const useWorkerCount=typeof maxWorkerNum==="number"?Math.min(maxWorkerNum,curWorkerCount):curWorkerCount;msg["doneFlags"]=
|
||||
(new Array(useWorkerCount)).fill(false);msg["transferables"]=[];self.lastBroadcasts.delete(msg["type"]);self.lastBroadcasts.set(msg["type"],msg)}for(const w of self.jobWorkers)w.MaybeStartNextJob()};
|
||||
@@ -0,0 +1,9 @@
|
||||
'use strict';self.dispatchPort=null;self.outputPort=null;self.workerNumber=-1;self.activeJobId=null;self.sentBlobs=new Map;self.sentBuffers=new Map;self.JobHandlers={};
|
||||
function FlipImageData(data,width,height){const stride=width*4;const tempRow=new Uint8Array(stride);const imageBuffer=data.buffer;for(let topY=0,len=Math.floor(height/2);topY<len;++topY){const bottomY=height-topY-1;const topRow=new Uint8Array(imageBuffer,topY*stride,stride);const bottomRow=new Uint8Array(imageBuffer,bottomY*stride,stride);tempRow.set(topRow);topRow.set(bottomRow);bottomRow.set(tempRow)}}
|
||||
function UnpremultiplyImageData(data){for(let ptr=0,len=data.length;ptr<len;ptr+=4){const a=data[ptr+3];if(a===255)continue;const scale=255/a;data[ptr]*=scale;data[ptr+1]*=scale;data[ptr+2]*=scale}}
|
||||
self.JobHandlers["ProcessImageData"]=function(params){const buffer=params["buffer"];const data=new Uint8Array(buffer);const width=params["width"];const height=params["height"];if(params["flipY"])FlipImageData(data,width,height);if(params["unpremultiply"])UnpremultiplyImageData(data);return{result:buffer,transferables:[buffer]}};
|
||||
self.addEventListener("message",e=>{const msg=e.data;const type=msg["type"];switch(type){case "init":self.workerNumber=msg["number"];self.dispatchPort=msg["dispatch-port"];self.dispatchPort.onmessage=OnDispatchWorkerMessage;self.outputPort=msg["output-port"];return;case "terminate":self.close();return;default:console.error("unknown message '"+type+"'");return}});function SendReady(){self.dispatchPort.postMessage({"type":"ready"});self.outputPort.postMessage({"type":"ready"})}
|
||||
function SendError(isBroadcast,e){if(!isBroadcast)self.outputPort.postMessage({"type":"error","jobId":self.activeJobId,"error":e.toString()});SendDone()}function SendResult(isBroadcast,ret){if(!isBroadcast){const transferables=ret.transferables||[];self.outputPort.postMessage({"type":"result","jobId":self.activeJobId,"result":ret.result},transferables)}SendDone()}function SendDone(){self.activeJobId=null;self.dispatchPort.postMessage({"type":"done"})}
|
||||
function SendProgress(val){self.outputPort.postMessage({"type":"progress","jobId":self.activeJobId,"progress":val})}
|
||||
function OnDispatchWorkerMessage(e){const msg=e.data;const type=msg["type"];if(type==="_import_scripts"){importScripts(...msg["scripts"]);return}else if(type==="_send_blob"){self.sentBlobs.set(msg["id"],msg["blob"]);return}else if(type==="_send_buffer"){self.sentBuffers.set(msg["id"],msg["buffer"]);return}else if(type==="_ready"){SendReady();return}const jobId=msg["jobId"];const isBroadcast=msg["isBroadcast"];const params=msg["params"];let ret;self.activeJobId=jobId;if(!self.JobHandlers.hasOwnProperty(type)){console.error(`no handler for message type '${type}'`);
|
||||
return}try{ret=self.JobHandlers[type](params)}catch(e){SendError(isBroadcast,"Exception in job handler: "+e);return}if(ret&&ret.then)ret.then(asyncRet=>SendResult(isBroadcast,asyncRet)).catch(err=>SendError(isBroadcast,"Rejection in job handler: "+err));else SendResult(isBroadcast,ret)};
|
||||
@@ -0,0 +1,138 @@
|
||||
'use strict';{window.DOMHandler=class DOMHandler{constructor(iRuntime,componentId){this._iRuntime=iRuntime;this._componentId=componentId;this._hasTickCallback=false;this._tickCallback=()=>this.Tick()}Attach(){}PostToRuntime(handler,data,dispatchOpts,transferables){this._iRuntime.PostToRuntimeComponent(this._componentId,handler,data,dispatchOpts,transferables)}PostToRuntimeAsync(handler,data,dispatchOpts,transferables){return this._iRuntime.PostToRuntimeComponentAsync(this._componentId,handler,data,
|
||||
dispatchOpts,transferables)}_PostToRuntimeMaybeSync(name,data,dispatchOpts){if(this._iRuntime.UsesWorker())this.PostToRuntime(name,data,dispatchOpts);else this._iRuntime._GetLocalRuntime()["_OnMessageFromDOM"]({"type":"event","component":this._componentId,"handler":name,"dispatchOpts":dispatchOpts||null,"data":data,"responseId":null})}AddRuntimeMessageHandler(handler,func){this._iRuntime.AddRuntimeComponentMessageHandler(this._componentId,handler,func)}AddRuntimeMessageHandlers(list){for(const [handler,
|
||||
func]of list)this.AddRuntimeMessageHandler(handler,func)}GetRuntimeInterface(){return this._iRuntime}GetComponentID(){return this._componentId}_StartTicking(){if(this._hasTickCallback)return;this._iRuntime._AddRAFCallback(this._tickCallback);this._hasTickCallback=true}_StopTicking(){if(!this._hasTickCallback)return;this._iRuntime._RemoveRAFCallback(this._tickCallback);this._hasTickCallback=false}Tick(){}};window.RateLimiter=class RateLimiter{constructor(callback,interval){this._callback=callback;
|
||||
this._interval=interval;this._timerId=-1;this._lastCallTime=-Infinity;this._timerCallFunc=()=>this._OnTimer();this._ignoreReset=false;this._canRunImmediate=false}SetCanRunImmediate(c){this._canRunImmediate=!!c}Call(){if(this._timerId!==-1)return;const nowTime=Date.now();const timeSinceLastCall=nowTime-this._lastCallTime;const interval=this._interval;if(timeSinceLastCall>=interval&&this._canRunImmediate){this._lastCallTime=nowTime;this._RunCallback()}else this._timerId=self.setTimeout(this._timerCallFunc,
|
||||
Math.max(interval-timeSinceLastCall,4))}_RunCallback(){this._ignoreReset=true;this._callback();this._ignoreReset=false}Reset(){if(this._ignoreReset)return;this._CancelTimer();this._lastCallTime=Date.now()}_OnTimer(){this._timerId=-1;this._lastCallTime=Date.now();this._RunCallback()}_CancelTimer(){if(this._timerId!==-1){self.clearTimeout(this._timerId);this._timerId=-1}}Release(){this._CancelTimer();this._callback=null;this._timerCallFunc=null}}};
|
||||
|
||||
|
||||
'use strict';{class ElementState{constructor(elem){this._elem=elem;this._hadFirstUpdate=false;this._isVisibleFlag=true}SetVisibleFlag(f){this._isVisibleFlag=!!f}GetVisibleFlag(){return this._isVisibleFlag}HadFirstUpdate(){return this._hadFirstUpdate}SetHadFirstUpdate(){this._hadFirstUpdate=true}GetElement(){return this._elem}}window.DOMElementHandler=class DOMElementHandler extends self.DOMHandler{constructor(iRuntime,componentId){super(iRuntime,componentId);this._elementMap=new Map;this._autoAttach=
|
||||
true;this.AddRuntimeMessageHandlers([["create",e=>this._OnCreate(e)],["destroy",e=>this._OnDestroy(e)],["set-visible",e=>this._OnSetVisible(e)],["update-position",e=>this._OnUpdatePosition(e)],["update-state",e=>this._OnUpdateState(e)],["focus",e=>this._OnSetFocus(e)],["set-css-style",e=>this._OnSetCssStyle(e)],["set-attribute",e=>this._OnSetAttribute(e)],["remove-attribute",e=>this._OnRemoveAttribute(e)]]);this.AddDOMElementMessageHandler("get-element",elem=>elem)}SetAutoAttach(e){this._autoAttach=
|
||||
!!e}AddDOMElementMessageHandler(handler,func){this.AddRuntimeMessageHandler(handler,e=>{const elementId=e["elementId"];const elem=this.GetElementById(elementId);return func(elem,e)})}_OnCreate(e){const elementId=e["elementId"];const elem=this.CreateElement(elementId,e);const elementState=new ElementState(elem);this._elementMap.set(elementId,elementState);elem.style.boxSizing="border-box";elem.style.display="none";elementState.SetVisibleFlag(e["isVisible"]);const focusElem=this._GetFocusElement(elem);
|
||||
focusElem.addEventListener("focus",e=>this._OnFocus(elementId));focusElem.addEventListener("blur",e=>this._OnBlur(elementId));if(this._autoAttach)document.body.appendChild(elem)}CreateElement(elementId,e){throw new Error("required override");}DestroyElement(elem){}_OnDestroy(e){const elementId=e["elementId"];const elem=this.GetElementById(elementId);this.DestroyElement(elem);if(this._autoAttach)elem.parentElement.removeChild(elem);this._elementMap.delete(elementId)}PostToRuntimeElement(handler,elementId,
|
||||
data){if(!data)data={};data["elementId"]=elementId;this.PostToRuntime(handler,data)}_PostToRuntimeElementMaybeSync(handler,elementId,data){if(!data)data={};data["elementId"]=elementId;this._PostToRuntimeMaybeSync(handler,data)}_OnSetVisible(e){if(!this._autoAttach)return;const elemState=this._elementMap.get(e["elementId"]);const elem=elemState.GetElement();if(elemState.HadFirstUpdate())elem.style.display=e["isVisible"]?"":"none";else elemState.SetVisibleFlag(e["isVisible"])}_OnUpdatePosition(e){if(!this._autoAttach)return;
|
||||
const elemState=this._elementMap.get(e["elementId"]);const elem=elemState.GetElement();elem.style.left=e["left"]+"px";elem.style.top=e["top"]+"px";elem.style.width=e["width"]+"px";elem.style.height=e["height"]+"px";const fontSize=e["fontSize"];if(fontSize!==null)elem.style.fontSize=fontSize+"em";if(!elemState.HadFirstUpdate()){elemState.SetHadFirstUpdate();if(elemState.GetVisibleFlag())elem.style.display=""}}_OnUpdateState(e){const elem=this.GetElementById(e["elementId"]);this.UpdateState(elem,e)}UpdateState(elem,
|
||||
e){throw new Error("required override");}_GetFocusElement(elem){return elem}_OnFocus(elementId){this.PostToRuntimeElement("elem-focused",elementId)}_OnBlur(elementId){this.PostToRuntimeElement("elem-blurred",elementId)}_OnSetFocus(e){const elem=this._GetFocusElement(this.GetElementById(e["elementId"]));if(e["focus"])elem.focus();else elem.blur()}_OnSetCssStyle(e){const elem=this.GetElementById(e["elementId"]);const prop=e["prop"];const val=e["val"];if(prop.startsWith("--"))elem.style.setProperty(prop,
|
||||
val);else elem.style[prop]=val}_OnSetAttribute(e){const elem=this.GetElementById(e["elementId"]);elem.setAttribute(e["name"],e["val"])}_OnRemoveAttribute(e){const elem=this.GetElementById(e["elementId"]);elem.removeAttribute(e["name"])}GetElementById(elementId){const elementState=this._elementMap.get(elementId);if(!elementState)throw new Error(`no element with id ${elementId}`);return elementState.GetElement()}}};
|
||||
|
||||
|
||||
'use strict';{const isiOSLike=/(iphone|ipod|ipad|macos|macintosh|mac os x)/i.test(navigator.userAgent);const isAndroid=/android/i.test(navigator.userAgent);const isSafari=/safari/i.test(navigator.userAgent)&&!/(chrome|chromium|edg\/|OPR\/|nwjs)/i.test(navigator.userAgent);let resolveCounter=0;function AddScript(url){const elem=document.createElement("script");elem.async=false;elem.type="module";if(url.isStringSrc)return new Promise(resolve=>{const resolveName="c3_resolve_"+resolveCounter;++resolveCounter;
|
||||
self[resolveName]=resolve;elem.textContent=url.str+`\n\nself["${resolveName}"]();`;document.head.appendChild(elem)});else return new Promise((resolve,reject)=>{elem.onload=resolve;elem.onerror=reject;elem.src=url;document.head.appendChild(elem)})}let didCheckWorkerModuleSupport=false;let isWorkerModuleSupported=false;function SupportsWorkerTypeModule(){if(!didCheckWorkerModuleSupport){try{new Worker("blob://",{get type(){isWorkerModuleSupported=true}})}catch(e){}didCheckWorkerModuleSupport=true}return isWorkerModuleSupported}
|
||||
let tmpAudio=new Audio;const supportedAudioFormats={"audio/webm; codecs=opus":!!tmpAudio.canPlayType("audio/webm; codecs=opus"),"audio/ogg; codecs=opus":!!tmpAudio.canPlayType("audio/ogg; codecs=opus"),"audio/webm; codecs=vorbis":!!tmpAudio.canPlayType("audio/webm; codecs=vorbis"),"audio/ogg; codecs=vorbis":!!tmpAudio.canPlayType("audio/ogg; codecs=vorbis"),"audio/mp4":!!tmpAudio.canPlayType("audio/mp4"),"audio/mpeg":!!tmpAudio.canPlayType("audio/mpeg")};tmpAudio=null;async function BlobToString(blob){const arrayBuffer=
|
||||
await BlobToArrayBuffer(blob);const textDecoder=new TextDecoder("utf-8");return textDecoder.decode(arrayBuffer)}function BlobToArrayBuffer(blob){return new Promise((resolve,reject)=>{const fileReader=new FileReader;fileReader.onload=e=>resolve(e.target.result);fileReader.onerror=err=>reject(err);fileReader.readAsArrayBuffer(blob)})}const queuedArrayBufferReads=[];let activeArrayBufferReads=0;const MAX_ARRAYBUFFER_READS=8;window["RealFile"]=window["File"];const domHandlerClasses=[];const runtimeEventHandlers=
|
||||
new Map;const pendingResponsePromises=new Map;let nextResponseId=0;const runOnStartupFunctions=[];self.runOnStartup=function runOnStartup(f){if(typeof f!=="function")throw new Error("runOnStartup called without a function");runOnStartupFunctions.push(f)};const WEBVIEW_EXPORT_TYPES=new Set(["cordova","playable-ad","instant-games"]);function IsWebViewExportType(exportType){return WEBVIEW_EXPORT_TYPES.has(exportType)}let isWrapperFullscreen=false;window.RuntimeInterface=class RuntimeInterface{constructor(opts){this._useWorker=
|
||||
opts.useWorker;this._messageChannelPort=null;this._runtimeBaseUrl="";this._scriptFolder=opts.scriptFolder;this._workerScriptURLs={};this._worker=null;this._localRuntime=null;this._domHandlers=[];this._runtimeDomHandler=null;this._canvas=null;this._isExportingToVideo=false;this._exportToVideoDuration=0;this._jobScheduler=null;this._rafId=-1;this._rafFunc=()=>this._OnRAFCallback();this._rafCallbacks=[];this._wrapperInitResolve=null;this._wrapperComponentIds=[];this._exportType=opts.exportType;this._isFileProtocol=
|
||||
location.protocol.substr(0,4)==="file";if(this._useWorker&&(typeof OffscreenCanvas==="undefined"||!navigator["userActivation"]||!SupportsWorkerTypeModule()))this._useWorker=false;if(this._useWorker&&isSafari)this._useWorker=false;if(this._exportType==="playable-ad"||this._exportType==="instant-games")this._useWorker=false;if(this._exportType==="cordova"&&this._useWorker)if(isAndroid){const chromeVer=/Chrome\/(\d+)/i.exec(navigator.userAgent);if(!chromeVer||!(parseInt(chromeVer[1],10)>=90))this._useWorker=
|
||||
false}else this._useWorker=false;if(this.IsWebView2Wrapper())self["chrome"]["webview"].addEventListener("message",e=>this._OnWrapperMessage(e.data));else if(this._exportType==="macos-wkwebview")self["C3WrapperOnMessage"]=msg=>this._OnWrapperMessage(msg);this._localFileBlobs=null;this._localFileStrings=null;if(this._exportType==="html5"&&!window.isSecureContext)console.warn("[Construct] Warning: the browser indicates this is not a secure context. Some features may be unavailable. Use secure (HTTPS) hosting to ensure all features are available.");
|
||||
this.AddRuntimeComponentMessageHandler("runtime","cordova-fetch-local-file",e=>this._OnCordovaFetchLocalFile(e));this.AddRuntimeComponentMessageHandler("runtime","create-job-worker",e=>this._OnCreateJobWorker(e));this.AddRuntimeComponentMessageHandler("runtime","send-wrapper-extension-message",e=>this._OnSendWrapperExtensionMessage(e));if(this._exportType==="cordova")document.addEventListener("deviceready",()=>this._Init(opts));else this._Init(opts)}Release(){this._CancelAnimationFrame();if(this._messageChannelPort){this._messageChannelPort.onmessage=
|
||||
null;this._messageChannelPort=null}if(this._worker){this._worker.terminate();this._worker=null}if(this._localRuntime){this._localRuntime.Release();this._localRuntime=null}if(this._canvas){this._canvas.parentElement.removeChild(this._canvas);this._canvas=null}}GetCanvas(){return this._canvas}GetRuntimeBaseURL(){return this._runtimeBaseUrl}UsesWorker(){return this._useWorker}GetExportType(){return this._exportType}IsFileProtocol(){return this._isFileProtocol}GetScriptFolder(){return this._scriptFolder}IsiOSCordova(){return isiOSLike&&
|
||||
this._exportType==="cordova"}IsiOSWebView(){const ua=navigator.userAgent;return isiOSLike&&IsWebViewExportType(this._exportType)||navigator["standalone"]||/crios\/|fxios\/|edgios\//i.test(ua)}IsAndroid(){return isAndroid}IsAndroidWebView(){return isAndroid&&IsWebViewExportType(this._exportType)}IsWebView2Wrapper(){return this._exportType==="windows-webview2"||!!(this._exportType==="preview"&&window["chrome"]&&window["chrome"]["webview"]&&window["chrome"]["webview"]["postMessage"])}async _Init(opts){if(this._exportType===
|
||||
"macos-wkwebview")this._SendWrapperMessage({"type":"ready"});else if(this.IsWebView2Wrapper()){this._SetupWebView2Polyfills();const result=await this._InitWrapper();this._wrapperComponentIds=result["registeredComponentIds"]}if(this._exportType==="playable-ad"){this._localFileBlobs=self["c3_base64files"];this._localFileStrings={};await this._ConvertDataUrisToBlobs();for(let i=0,len=opts.engineScripts.length;i<len;++i){const src=opts.engineScripts[i];if(this._localFileStrings.hasOwnProperty(src))opts.engineScripts[i]=
|
||||
{isStringSrc:true,str:this._localFileStrings[src]};else if(this._localFileBlobs.hasOwnProperty(src))opts.engineScripts[i]=URL.createObjectURL(this._localFileBlobs[src])}opts.workerDependencyScripts=[]}if(this._exportType==="nwjs"&&self["nw"]&&self["nw"]["App"]["manifest"]["c3-steam-mode"]){let frameNum=0;this._AddRAFCallback(()=>{frameNum++;document.body.style.opacity=frameNum%2===0?"1":"0.999"})}if(opts.runtimeBaseUrl)this._runtimeBaseUrl=opts.runtimeBaseUrl;else{const origin=location.origin;this._runtimeBaseUrl=
|
||||
(origin==="null"?"file:///":origin)+location.pathname;const i=this._runtimeBaseUrl.lastIndexOf("/");if(i!==-1)this._runtimeBaseUrl=this._runtimeBaseUrl.substr(0,i+1)}if(opts.workerScripts)this._workerScriptURLs=opts.workerScripts;const messageChannel=new MessageChannel;this._messageChannelPort=messageChannel.port1;this._messageChannelPort.onmessage=e=>this["_OnMessageFromRuntime"](e.data);if(window["c3_addPortMessageHandler"])window["c3_addPortMessageHandler"](e=>this._OnMessageFromDebugger(e));this._jobScheduler=
|
||||
new self.JobSchedulerDOM(this);await this._jobScheduler.Init();if(typeof window["StatusBar"]==="object")window["StatusBar"]["hide"]();if(typeof window["AndroidFullScreen"]==="object")try{await new Promise((resolve,reject)=>{window["AndroidFullScreen"]["immersiveMode"](resolve,reject)})}catch(err){console.error("Failed to enter Android immersive mode: ",err)}if(this._useWorker)await this._InitWorker(opts,messageChannel.port2);else await this._InitDOM(opts,messageChannel.port2)}_GetWorkerURL(url){let ret;
|
||||
if(this._workerScriptURLs.hasOwnProperty(url))ret=this._workerScriptURLs[url];else if(url.endsWith("/workermain.js")&&this._workerScriptURLs.hasOwnProperty("workermain.js"))ret=this._workerScriptURLs["workermain.js"];else if(this._exportType==="playable-ad"&&this._localFileBlobs.hasOwnProperty(url))ret=this._localFileBlobs[url];else ret=url;if(ret instanceof Blob)ret=URL.createObjectURL(ret);return ret}async CreateWorker(url,baseUrl,workerOpts){if(url.startsWith("blob:"))return new Worker(url,workerOpts);
|
||||
if(this._exportType==="cordova"&&this._isFileProtocol){let filePath="";if(workerOpts.isC3MainWorker)filePath=url;else filePath=this._scriptFolder+url;const arrayBuffer=await this.CordovaFetchLocalFileAsArrayBuffer(filePath);const blob=new Blob([arrayBuffer],{type:"application/javascript"});return new Worker(URL.createObjectURL(blob),workerOpts)}const absUrl=new URL(url,baseUrl);const isCrossOrigin=location.origin!==absUrl.origin;if(isCrossOrigin){const response=await fetch(absUrl);if(!response.ok)throw new Error("failed to fetch worker script");
|
||||
const blob=await response.blob();return new Worker(URL.createObjectURL(blob),workerOpts)}else return new Worker(absUrl,workerOpts)}_GetWindowInnerWidth(){return Math.max(window.innerWidth,1)}_GetWindowInnerHeight(){return Math.max(window.innerHeight,1)}_GetCommonRuntimeOptions(opts){return{"runtimeBaseUrl":this._runtimeBaseUrl,"previewUrl":location.href,"windowInnerWidth":this._GetWindowInnerWidth(),"windowInnerHeight":this._GetWindowInnerHeight(),"devicePixelRatio":window.devicePixelRatio,"isFullscreen":RuntimeInterface.IsDocumentFullscreen(),
|
||||
"projectData":opts.projectData,"previewImageBlobs":window["cr_previewImageBlobs"]||this._localFileBlobs,"previewProjectFileBlobs":window["cr_previewProjectFileBlobs"],"previewProjectFileSWUrls":window["cr_previewProjectFiles"],"swClientId":window["cr_swClientId"]||"","exportType":opts.exportType,"isDebug":(new URLSearchParams(self.location.search)).has("debug"),"ife":!!self.ife,"jobScheduler":this._jobScheduler.GetPortData(),"supportedAudioFormats":supportedAudioFormats,"opusWasmScriptUrl":window["cr_opusWasmScriptUrl"]||
|
||||
this._scriptFolder+"opus.wasm.js","opusWasmBinaryUrl":window["cr_opusWasmBinaryUrl"]||this._scriptFolder+"opus.wasm.wasm","isFileProtocol":this._isFileProtocol,"isiOSCordova":this.IsiOSCordova(),"isiOSWebView":this.IsiOSWebView(),"isWebView2Wrapper":this.IsWebView2Wrapper(),"wrapperComponentIds":this._wrapperComponentIds,"isFBInstantAvailable":typeof self["FBInstant"]!=="undefined"}}async _InitWorker(opts,port2){const workerMainUrl=this._GetWorkerURL(opts.workerMainUrl);if(this._exportType==="preview"){this._worker=
|
||||
new Worker("previewworker.js",{type:"module",name:"Runtime"});await new Promise((resolve,reject)=>{const messageHandler=e=>{this._worker.removeEventListener("message",messageHandler);if(e.data&&e.data["type"]==="ok")resolve();else reject()};this._worker.addEventListener("message",messageHandler);this._worker.postMessage({"type":"construct-worker-init","import":(new URL(workerMainUrl,this._runtimeBaseUrl)).toString()})})}else this._worker=await this.CreateWorker(workerMainUrl,this._runtimeBaseUrl,
|
||||
{type:"module",name:"Runtime",isC3MainWorker:true});this._canvas=document.createElement("canvas");this._canvas.style.display="none";const offscreenCanvas=this._canvas["transferControlToOffscreen"]();document.body.appendChild(this._canvas);window["c3canvas"]=this._canvas;if(self["C3_InsertHTMLPlaceholders"])self["C3_InsertHTMLPlaceholders"]();let workerDependencyScripts=opts.workerDependencyScripts||[];let engineScripts=opts.engineScripts;workerDependencyScripts=await Promise.all(workerDependencyScripts.map(url=>
|
||||
this._MaybeGetCordovaScriptURL(url)));engineScripts=await Promise.all(engineScripts.map(url=>this._MaybeGetCordovaScriptURL(url)));if(this._exportType==="cordova")for(let i=0,len=opts.projectScripts.length;i<len;++i){const info=opts.projectScripts[i];const originalUrl=info[0];if(originalUrl===opts.mainProjectScript||(originalUrl==="scriptsInEvents.js"||originalUrl.endsWith("/scriptsInEvents.js")))info[1]=await this._MaybeGetCordovaScriptURL(originalUrl)}this._worker.postMessage(Object.assign(this._GetCommonRuntimeOptions(opts),
|
||||
{"type":"init-runtime","isInWorker":true,"messagePort":port2,"canvas":offscreenCanvas,"workerDependencyScripts":workerDependencyScripts,"engineScripts":engineScripts,"projectScripts":opts.projectScripts,"mainProjectScript":opts.mainProjectScript,"projectScriptsStatus":self["C3_ProjectScriptsStatus"]}),[port2,offscreenCanvas,...this._jobScheduler.GetPortTransferables()]);this._domHandlers=domHandlerClasses.map(C=>new C(this));this._FindRuntimeDOMHandler();this._runtimeDomHandler._EnableWindowResizeEvent();
|
||||
self["c3_callFunction"]=(name,params)=>this._runtimeDomHandler._InvokeFunctionFromJS(name,params);if(this._exportType==="preview")self["goToLastErrorScript"]=()=>this.PostToRuntimeComponent("runtime","go-to-last-error-script")}async _InitDOM(opts,port2){this._canvas=document.createElement("canvas");this._canvas.style.display="none";document.body.appendChild(this._canvas);window["c3canvas"]=this._canvas;if(self["C3_InsertHTMLPlaceholders"])self["C3_InsertHTMLPlaceholders"]();this._domHandlers=domHandlerClasses.map(C=>
|
||||
new C(this));this._FindRuntimeDOMHandler();let engineScripts=opts.engineScripts.map(url=>typeof url==="string"?(new URL(url,this._runtimeBaseUrl)).toString():url);if(Array.isArray(opts.workerDependencyScripts)){const workerDependencyScripts=[...opts.workerDependencyScripts].map(s=>s instanceof Blob?URL.createObjectURL(s):s);engineScripts.unshift(...workerDependencyScripts)}engineScripts=await Promise.all(engineScripts.map(url=>this._MaybeGetCordovaScriptURL(url)));await Promise.all(engineScripts.map(url=>
|
||||
AddScript(url)));const scriptsStatus=self["C3_ProjectScriptsStatus"];const mainProjectScript=opts.mainProjectScript;const allProjectScripts=opts.projectScripts;for(let [originalUrl,loadUrl]of allProjectScripts){if(!loadUrl)loadUrl=originalUrl;if(originalUrl===mainProjectScript)try{loadUrl=await this._MaybeGetCordovaScriptURL(loadUrl);await AddScript(loadUrl);if(this._exportType==="preview"&&!scriptsStatus[originalUrl])this._ReportProjectMainScriptError(originalUrl,"main script did not run to completion")}catch(err){this._ReportProjectMainScriptError(originalUrl,
|
||||
err)}else if(originalUrl==="scriptsInEvents.js"||originalUrl.endsWith("/scriptsInEvents.js")){loadUrl=await this._MaybeGetCordovaScriptURL(loadUrl);await AddScript(loadUrl)}}if(this._exportType==="preview"&&typeof self.C3.ScriptsInEvents!=="object"){this._RemoveLoadingMessage();const msg="Failed to load JavaScript code used in events. Check all your JavaScript code has valid syntax.";console.error("[C3 runtime] "+msg);alert(msg);return}const runtimeOpts=Object.assign(this._GetCommonRuntimeOptions(opts),
|
||||
{"isInWorker":false,"messagePort":port2,"canvas":this._canvas,"runOnStartupFunctions":runOnStartupFunctions});this._runtimeDomHandler._EnableWindowResizeEvent();this._OnBeforeCreateRuntime();this._localRuntime=self["C3_CreateRuntime"](runtimeOpts);await self["C3_InitRuntime"](this._localRuntime,runtimeOpts)}_ReportProjectMainScriptError(url,err){this._RemoveLoadingMessage();console.error(`[Preview] Failed to load project main script (${url}): `,err);alert(`Failed to load project main script (${url}). Check all your JavaScript code has valid syntax. Press F12 and check the console for error details.`)}_OnBeforeCreateRuntime(){this._RemoveLoadingMessage()}_RemoveLoadingMessage(){const loadingElem=
|
||||
window["cr_previewLoadingElem"];if(loadingElem){loadingElem.parentElement.removeChild(loadingElem);window["cr_previewLoadingElem"]=null}}async _OnCreateJobWorker(e){const outputPort=await this._jobScheduler._CreateJobWorker();return{"outputPort":outputPort,"transferables":[outputPort]}}_GetLocalRuntime(){if(this._useWorker)throw new Error("not available in worker mode");return this._localRuntime}PostToRuntimeComponent(component,handler,data,dispatchOpts,transferables){this._messageChannelPort.postMessage({"type":"event",
|
||||
"component":component,"handler":handler,"dispatchOpts":dispatchOpts||null,"data":data,"responseId":null},transferables)}PostToRuntimeComponentAsync(component,handler,data,dispatchOpts,transferables){const responseId=nextResponseId++;const ret=new Promise((resolve,reject)=>{pendingResponsePromises.set(responseId,{resolve,reject})});this._messageChannelPort.postMessage({"type":"event","component":component,"handler":handler,"dispatchOpts":dispatchOpts||null,"data":data,"responseId":responseId},transferables);
|
||||
return ret}["_OnMessageFromRuntime"](data){const type=data["type"];if(type==="event")return this._OnEventFromRuntime(data);else if(type==="result")this._OnResultFromRuntime(data);else if(type==="runtime-ready")this._OnRuntimeReady();else if(type==="alert-error"){this._RemoveLoadingMessage();alert(data["message"])}else if(type==="creating-runtime")this._OnBeforeCreateRuntime();else throw new Error(`unknown message '${type}'`);}_OnEventFromRuntime(e){const component=e["component"];const handler=e["handler"];
|
||||
const data=e["data"];const responseId=e["responseId"];const handlerMap=runtimeEventHandlers.get(component);if(!handlerMap){console.warn(`[DOM] No event handlers for component '${component}'`);return}const func=handlerMap.get(handler);if(!func){console.warn(`[DOM] No handler '${handler}' for component '${component}'`);return}let ret=null;try{ret=func(data)}catch(err){console.error(`Exception in '${component}' handler '${handler}':`,err);if(responseId!==null)this._PostResultToRuntime(responseId,false,
|
||||
""+err);return}if(responseId===null)return ret;else if(ret&&ret.then)ret.then(result=>this._PostResultToRuntime(responseId,true,result)).catch(err=>{console.error(`Rejection from '${component}' handler '${handler}':`,err);this._PostResultToRuntime(responseId,false,""+err)});else this._PostResultToRuntime(responseId,true,ret)}_PostResultToRuntime(responseId,isOk,result){let transferables;if(result&&result["transferables"])transferables=result["transferables"];this._messageChannelPort.postMessage({"type":"result",
|
||||
"responseId":responseId,"isOk":isOk,"result":result},transferables)}_OnResultFromRuntime(data){const responseId=data["responseId"];const isOk=data["isOk"];const result=data["result"];const pendingPromise=pendingResponsePromises.get(responseId);if(isOk)pendingPromise.resolve(result);else pendingPromise.reject(result);pendingResponsePromises.delete(responseId)}AddRuntimeComponentMessageHandler(component,handler,func){let handlerMap=runtimeEventHandlers.get(component);if(!handlerMap){handlerMap=new Map;
|
||||
runtimeEventHandlers.set(component,handlerMap)}if(handlerMap.has(handler))throw new Error(`[DOM] Component '${component}' already has handler '${handler}'`);handlerMap.set(handler,func)}static AddDOMHandlerClass(Class){if(domHandlerClasses.includes(Class))throw new Error("DOM handler already added");domHandlerClasses.push(Class)}_FindRuntimeDOMHandler(){for(const dh of this._domHandlers)if(dh.GetComponentID()==="runtime"){this._runtimeDomHandler=dh;return}throw new Error("cannot find runtime DOM handler");
|
||||
}_OnMessageFromDebugger(e){this.PostToRuntimeComponent("debugger","message",e)}_OnRuntimeReady(){for(const h of this._domHandlers)h.Attach()}static IsDocumentFullscreen(){return!!(document["fullscreenElement"]||document["webkitFullscreenElement"]||document["mozFullScreenElement"]||isWrapperFullscreen)}static _SetWrapperIsFullscreenFlag(f){isWrapperFullscreen=!!f}async GetRemotePreviewStatusInfo(){return await this.PostToRuntimeComponentAsync("runtime","get-remote-preview-status-info")}_AddRAFCallback(f){this._rafCallbacks.push(f);
|
||||
this._RequestAnimationFrame()}_RemoveRAFCallback(f){const i=this._rafCallbacks.indexOf(f);if(i===-1)throw new Error("invalid callback");this._rafCallbacks.splice(i,1);if(!this._rafCallbacks.length)this._CancelAnimationFrame()}_RequestAnimationFrame(){if(this._rafId===-1&&this._rafCallbacks.length)this._rafId=requestAnimationFrame(this._rafFunc)}_CancelAnimationFrame(){if(this._rafId!==-1){cancelAnimationFrame(this._rafId);this._rafId=-1}}_OnRAFCallback(){this._rafId=-1;for(const f of this._rafCallbacks)f();
|
||||
this._RequestAnimationFrame()}TryPlayMedia(mediaElem){this._runtimeDomHandler.TryPlayMedia(mediaElem)}RemovePendingPlay(mediaElem){this._runtimeDomHandler.RemovePendingPlay(mediaElem)}_PlayPendingMedia(){this._runtimeDomHandler._PlayPendingMedia()}SetSilent(s){this._runtimeDomHandler.SetSilent(s)}IsAudioFormatSupported(typeStr){return!!supportedAudioFormats[typeStr]}async _WasmDecodeWebMOpus(arrayBuffer){const result=await this.PostToRuntimeComponentAsync("runtime","opus-decode",{"arrayBuffer":arrayBuffer},
|
||||
null,[arrayBuffer]);return new Float32Array(result)}SetIsExportingToVideo(duration){this._isExportingToVideo=true;this._exportToVideoDuration=duration}IsExportingToVideo(){return this._isExportingToVideo}GetExportToVideoDuration(){return this._exportToVideoDuration}IsAbsoluteURL(url){return/^(?:[a-z\-]+:)?\/\//.test(url)||url.substr(0,5)==="data:"||url.substr(0,5)==="blob:"}IsRelativeURL(url){return!this.IsAbsoluteURL(url)}async _MaybeGetCordovaScriptURL(url){if(this._exportType==="cordova"&&(url.startsWith("file:")||
|
||||
this._isFileProtocol&&this.IsRelativeURL(url))){let filename=url;if(filename.startsWith(this._runtimeBaseUrl))filename=filename.substr(this._runtimeBaseUrl.length);const arrayBuffer=await this.CordovaFetchLocalFileAsArrayBuffer(filename);const blob=new Blob([arrayBuffer],{type:"application/javascript"});return URL.createObjectURL(blob)}else return url}async _OnCordovaFetchLocalFile(e){const filename=e["filename"];switch(e["as"]){case "text":return await this.CordovaFetchLocalFileAsText(filename);
|
||||
case "buffer":return await this.CordovaFetchLocalFileAsArrayBuffer(filename);default:throw new Error("unsupported type");}}_GetPermissionAPI(){const api=window["cordova"]&&window["cordova"]["plugins"]&&window["cordova"]["plugins"]["permissions"];if(typeof api!=="object")throw new Error("Permission API is not loaded");return api}_MapPermissionID(api,permission){const permissionID=api[permission];if(typeof permissionID!=="string")throw new Error("Invalid permission name");return permissionID}_HasPermission(id){const api=
|
||||
this._GetPermissionAPI();return new Promise((resolve,reject)=>api["checkPermission"](this._MapPermissionID(api,id),status=>resolve(!!status["hasPermission"]),reject))}_RequestPermission(id){const api=this._GetPermissionAPI();return new Promise((resolve,reject)=>api["requestPermission"](this._MapPermissionID(api,id),status=>resolve(!!status["hasPermission"]),reject))}async RequestPermissions(permissions){if(this.GetExportType()!=="cordova")return true;if(this.IsiOSCordova())return true;for(const id of permissions){const alreadyGranted=
|
||||
await this._HasPermission(id);if(alreadyGranted)continue;const granted=await this._RequestPermission(id);if(granted===false)return false}return true}async RequirePermissions(...permissions){if(await this.RequestPermissions(permissions)===false)throw new Error("Permission not granted");}CordovaFetchLocalFile(filename){const path=window["cordova"]["file"]["applicationDirectory"]+"www/"+filename;return new Promise((resolve,reject)=>{window["resolveLocalFileSystemURL"](path,entry=>{entry["file"](resolve,
|
||||
reject)},reject)})}async CordovaFetchLocalFileAsText(filename){const file=await this.CordovaFetchLocalFile(filename);return await BlobToString(file)}_CordovaMaybeStartNextArrayBufferRead(){if(!queuedArrayBufferReads.length)return;if(activeArrayBufferReads>=MAX_ARRAYBUFFER_READS)return;activeArrayBufferReads++;const job=queuedArrayBufferReads.shift();this._CordovaDoFetchLocalFileAsAsArrayBuffer(job.filename,job.successCallback,job.errorCallback)}CordovaFetchLocalFileAsArrayBuffer(filename){return new Promise((resolve,
|
||||
reject)=>{queuedArrayBufferReads.push({filename:filename,successCallback:result=>{activeArrayBufferReads--;this._CordovaMaybeStartNextArrayBufferRead();resolve(result)},errorCallback:err=>{activeArrayBufferReads--;this._CordovaMaybeStartNextArrayBufferRead();reject(err)}});this._CordovaMaybeStartNextArrayBufferRead()})}async _CordovaDoFetchLocalFileAsAsArrayBuffer(filename,successCallback,errorCallback){try{const file=await this.CordovaFetchLocalFile(filename);const arrayBuffer=await BlobToArrayBuffer(file);
|
||||
successCallback(arrayBuffer)}catch(err){errorCallback(err)}}_OnWrapperMessage(msg){if(msg==="entered-fullscreen"){RuntimeInterface._SetWrapperIsFullscreenFlag(true);this._runtimeDomHandler._OnFullscreenChange()}else if(msg==="exited-fullscreen"){RuntimeInterface._SetWrapperIsFullscreenFlag(false);this._runtimeDomHandler._OnFullscreenChange()}else if(typeof msg==="object"){const type=msg["type"];if(type==="wrapper-init-response"){this._wrapperInitResolve(msg);this._wrapperInitResolve=null}else if(type===
|
||||
"extension-message")this.PostToRuntimeComponent("runtime","wrapper-extension-message",msg);else console.warn("Unknown wrapper message: ",msg)}else console.warn("Unknown wrapper message: ",msg)}_OnSendWrapperExtensionMessage(data){this._SendWrapperMessage({"type":"extension-message","componentId":data["componentId"],"messageId":data["messageId"],"params":data["params"]||[],"asyncId":data["asyncId"]})}_SendWrapperMessage(o){if(this.IsWebView2Wrapper())window["chrome"]["webview"]["postMessage"](JSON.stringify(o));
|
||||
else if(this._exportType==="macos-wkwebview")window["webkit"]["messageHandlers"]["C3Wrapper"]["postMessage"](JSON.stringify(o));else;}_SetupWebView2Polyfills(){window.moveTo=(x,y)=>{this._SendWrapperMessage({"type":"set-window-position","windowX":Math.ceil(x),"windowY":Math.ceil(y)})};window.resizeTo=(w,h)=>{this._SendWrapperMessage({"type":"set-window-size","windowWidth":Math.ceil(w),"windowHeight":Math.ceil(h)})}}_InitWrapper(){if(!this.IsWebView2Wrapper())return Promise.resolve();return new Promise(resolve=>
|
||||
{this._wrapperInitResolve=resolve;this._SendWrapperMessage({"type":"wrapper-init"})})}async _ConvertDataUrisToBlobs(){const promises=[];for(const [filename,data]of Object.entries(this._localFileBlobs))promises.push(this._ConvertDataUriToBlobs(filename,data));await Promise.all(promises)}async _ConvertDataUriToBlobs(filename,data){if(typeof data==="object"){this._localFileBlobs[filename]=new Blob([data["str"]],{"type":data["type"]});this._localFileStrings[filename]=data["str"]}else{let blob=await this._FetchDataUri(data);
|
||||
if(!blob)blob=this._DataURIToBinaryBlobSync(data);this._localFileBlobs[filename]=blob}}async _FetchDataUri(dataUri){try{const response=await fetch(dataUri);return await response.blob()}catch(err){console.warn("Failed to fetch a data: URI. Falling back to a slower workaround. This is probably because the Content Security Policy unnecessarily blocked it. Allow data: URIs in your CSP to avoid this.",err);return null}}_DataURIToBinaryBlobSync(datauri){const o=this._ParseDataURI(datauri);return this._BinaryStringToBlob(o.data,
|
||||
o.mime_type)}_ParseDataURI(datauri){const comma=datauri.indexOf(",");if(comma<0)throw new URIError("expected comma in data: uri");const typepart=datauri.substring(5,comma);const datapart=datauri.substring(comma+1);const typearr=typepart.split(";");const mimetype=typearr[0]||"";const encoding1=typearr[1];const encoding2=typearr[2];let decodeddata;if(encoding1==="base64"||encoding2==="base64")decodeddata=atob(datapart);else decodeddata=decodeURIComponent(datapart);return{mime_type:mimetype,data:decodeddata}}_BinaryStringToBlob(binstr,
|
||||
mime_type){let len=binstr.length;let len32=len>>2;let a8=new Uint8Array(len);let a32=new Uint32Array(a8.buffer,0,len32);let i,j;for(i=0,j=0;i<len32;++i)a32[i]=binstr.charCodeAt(j++)|binstr.charCodeAt(j++)<<8|binstr.charCodeAt(j++)<<16|binstr.charCodeAt(j++)<<24;let tailLength=len&3;while(tailLength--){a8[j]=binstr.charCodeAt(j);++j}return new Blob([a8],{"type":mime_type})}}};
|
||||
|
||||
|
||||
'use strict';{const RuntimeInterface=self.RuntimeInterface;function IsCompatibilityMouseEvent(e){return e["sourceCapabilities"]&&e["sourceCapabilities"]["firesTouchEvents"]||e["originalEvent"]&&e["originalEvent"]["sourceCapabilities"]&&e["originalEvent"]["sourceCapabilities"]["firesTouchEvents"]}const KEY_CODE_ALIASES=new Map([["OSLeft","MetaLeft"],["OSRight","MetaRight"]]);const DISPATCH_RUNTIME_AND_SCRIPT={"dispatchRuntimeEvent":true,"dispatchUserScriptEvent":true};const DISPATCH_SCRIPT_ONLY={"dispatchUserScriptEvent":true};
|
||||
const DISPATCH_RUNTIME_ONLY={"dispatchRuntimeEvent":true};function AddStyleSheet(cssUrl){return new Promise((resolve,reject)=>{const styleLink=document.createElement("link");styleLink.onload=()=>resolve(styleLink);styleLink.onerror=err=>reject(err);styleLink.rel="stylesheet";styleLink.href=cssUrl;document.head.appendChild(styleLink)})}function FetchImage(url){return new Promise((resolve,reject)=>{const img=new Image;img.onload=()=>resolve(img);img.onerror=err=>reject(err);img.src=url})}async function BlobToImage(blob){const blobUrl=
|
||||
URL.createObjectURL(blob);try{return await FetchImage(blobUrl)}finally{URL.revokeObjectURL(blobUrl)}}function BlobToString(blob){return new Promise((resolve,reject)=>{let fileReader=new FileReader;fileReader.onload=e=>resolve(e.target.result);fileReader.onerror=err=>reject(err);fileReader.readAsText(blob)})}async function BlobToSvgImage(blob,width,height){if(!/firefox/i.test(navigator.userAgent))return await BlobToImage(blob);let str=await BlobToString(blob);const parser=new DOMParser;const doc=parser.parseFromString(str,
|
||||
"image/svg+xml");const rootElem=doc.documentElement;if(rootElem.hasAttribute("width")&&rootElem.hasAttribute("height")){const widthStr=rootElem.getAttribute("width");const heightStr=rootElem.getAttribute("height");if(!widthStr.includes("%")&&!heightStr.includes("%"))return await BlobToImage(blob)}rootElem.setAttribute("width",width+"px");rootElem.setAttribute("height",height+"px");const serializer=new XMLSerializer;str=serializer.serializeToString(doc);blob=new Blob([str],{type:"image/svg+xml"});
|
||||
return await BlobToImage(blob)}function IsInContentEditable(el){do{if(el.parentNode&&el.hasAttribute("contenteditable"))return true;el=el.parentNode}while(el);return false}const keyboardInputElementTagNames=new Set(["input","textarea","datalist","select"]);function IsKeyboardInputElement(elem){return keyboardInputElementTagNames.has(elem.tagName.toLowerCase())||IsInContentEditable(elem)}const canvasOrDocTags=new Set(["canvas","body","html"]);function PreventDefaultOnCanvasOrDoc(e){if(!e.target.tagName)return;
|
||||
const tagName=e.target.tagName.toLowerCase();if(canvasOrDocTags.has(tagName))e.preventDefault()}function BlockWheelZoom(e){if(e.metaKey||e.ctrlKey)e.preventDefault()}self["C3_GetSvgImageSize"]=async function(blob){const img=await BlobToImage(blob);if(img.width>0&&img.height>0)return[img.width,img.height];else{img.style.position="absolute";img.style.left="0px";img.style.top="0px";img.style.visibility="hidden";document.body.appendChild(img);const rc=img.getBoundingClientRect();document.body.removeChild(img);
|
||||
return[rc.width,rc.height]}};self["C3_RasterSvgImageBlob"]=async function(blob,imageWidth,imageHeight,surfaceWidth,surfaceHeight){const img=await BlobToSvgImage(blob,imageWidth,imageHeight);const canvas=document.createElement("canvas");canvas.width=surfaceWidth;canvas.height=surfaceHeight;const ctx=canvas.getContext("2d");ctx.drawImage(img,0,0,imageWidth,imageHeight);return canvas};let isCordovaPaused=false;document.addEventListener("pause",()=>isCordovaPaused=true);document.addEventListener("resume",
|
||||
()=>isCordovaPaused=false);function ParentHasFocus(){try{return window.parent&&window.parent.document.hasFocus()}catch(err){return false}}function KeyboardIsVisible(){const elem=document.activeElement;if(!elem)return false;const tagName=elem.tagName.toLowerCase();const inputTypes=new Set(["email","number","password","search","tel","text","url"]);if(tagName==="textarea")return true;if(tagName==="input")return inputTypes.has(elem.type.toLowerCase()||"text");return IsInContentEditable(elem)}const DOM_COMPONENT_ID=
|
||||
"runtime";const HANDLER_CLASS=class RuntimeDOMHandler extends self.DOMHandler{constructor(iRuntime){super(iRuntime,DOM_COMPONENT_ID);this._isFirstSizeUpdate=true;this._enableWindowResizeEvent=false;this._simulatedResizeTimerId=-1;this._targetOrientation="any";this._attachedDeviceOrientationEvent=false;this._attachedDeviceMotionEvent=false;this._pageVisibilityIsHidden=false;this._screenReaderTextWrap=document.createElement("div");this._screenReaderTextWrap.className="c3-screen-reader-text";this._screenReaderTextWrap.setAttribute("aria-live",
|
||||
"polite");document.body.appendChild(this._screenReaderTextWrap);this._debugHighlightElem=null;this._isExportToVideo=false;this._exportVideoProgressMessage="";this._exportVideoUpdateTimerId=-1;this._enableAndroidVKDetection=false;this._lastWindowWidth=iRuntime._GetWindowInnerWidth();this._lastWindowHeight=iRuntime._GetWindowInnerHeight();this._virtualKeyboardHeight=0;this._vkTranslateYOffset=0;iRuntime.AddRuntimeComponentMessageHandler("canvas","update-size",e=>this._OnUpdateCanvasSize(e));iRuntime.AddRuntimeComponentMessageHandler("runtime",
|
||||
"invoke-download",e=>this._OnInvokeDownload(e));iRuntime.AddRuntimeComponentMessageHandler("runtime","load-webfonts",e=>this._OnLoadWebFonts(e));iRuntime.AddRuntimeComponentMessageHandler("runtime","raster-svg-image",e=>this._OnRasterSvgImage(e));iRuntime.AddRuntimeComponentMessageHandler("runtime","get-svg-image-size",e=>this._OnGetSvgImageSize(e));iRuntime.AddRuntimeComponentMessageHandler("runtime","set-target-orientation",e=>this._OnSetTargetOrientation(e));iRuntime.AddRuntimeComponentMessageHandler("runtime",
|
||||
"register-sw",()=>this._OnRegisterSW());iRuntime.AddRuntimeComponentMessageHandler("runtime","post-to-debugger",e=>this._OnPostToDebugger(e));iRuntime.AddRuntimeComponentMessageHandler("runtime","go-to-script",e=>this._OnPostToDebugger(e));iRuntime.AddRuntimeComponentMessageHandler("runtime","before-start-ticking",()=>this._OnBeforeStartTicking());iRuntime.AddRuntimeComponentMessageHandler("runtime","debug-highlight",e=>this._OnDebugHighlight(e));iRuntime.AddRuntimeComponentMessageHandler("runtime",
|
||||
"enable-device-orientation",()=>this._AttachDeviceOrientationEvent());iRuntime.AddRuntimeComponentMessageHandler("runtime","enable-device-motion",()=>this._AttachDeviceMotionEvent());iRuntime.AddRuntimeComponentMessageHandler("runtime","add-stylesheet",e=>this._OnAddStylesheet(e));iRuntime.AddRuntimeComponentMessageHandler("runtime","script-create-worker",e=>this._OnScriptCreateWorker(e));iRuntime.AddRuntimeComponentMessageHandler("runtime","alert",e=>this._OnAlert(e));iRuntime.AddRuntimeComponentMessageHandler("runtime",
|
||||
"screen-reader-text",e=>this._OnScreenReaderTextEvent(e));iRuntime.AddRuntimeComponentMessageHandler("runtime","hide-cordova-splash",()=>this._OnHideCordovaSplash());iRuntime.AddRuntimeComponentMessageHandler("runtime","set-exporting-to-video",e=>this._SetExportingToVideo(e));iRuntime.AddRuntimeComponentMessageHandler("runtime","export-to-video-progress",e=>this._OnExportVideoProgress(e));iRuntime.AddRuntimeComponentMessageHandler("runtime","exported-to-video",e=>this._OnExportedToVideo(e));iRuntime.AddRuntimeComponentMessageHandler("runtime",
|
||||
"exported-to-image-sequence",e=>this._OnExportedToImageSequence(e));const allowDefaultContextMenuTagNames=new Set(["input","textarea","datalist"]);window.addEventListener("contextmenu",e=>{const t=e.target;const name=t.tagName.toLowerCase();if(!allowDefaultContextMenuTagNames.has(name)&&!IsInContentEditable(t))e.preventDefault()});const canvas=iRuntime.GetCanvas();window.addEventListener("selectstart",PreventDefaultOnCanvasOrDoc);window.addEventListener("gesturehold",PreventDefaultOnCanvasOrDoc);
|
||||
canvas.addEventListener("selectstart",PreventDefaultOnCanvasOrDoc);canvas.addEventListener("gesturehold",PreventDefaultOnCanvasOrDoc);window.addEventListener("touchstart",PreventDefaultOnCanvasOrDoc,{"passive":false});if(typeof PointerEvent!=="undefined"){window.addEventListener("pointerdown",PreventDefaultOnCanvasOrDoc,{"passive":false});canvas.addEventListener("pointerdown",PreventDefaultOnCanvasOrDoc)}else canvas.addEventListener("touchstart",PreventDefaultOnCanvasOrDoc);this._mousePointerLastButtons=
|
||||
0;window.addEventListener("mousedown",e=>{if(e.button===1)e.preventDefault()});window.addEventListener("mousewheel",BlockWheelZoom,{"passive":false});window.addEventListener("wheel",BlockWheelZoom,{"passive":false});window.addEventListener("resize",()=>this._OnWindowResize());window.addEventListener("fullscreenchange",()=>this._OnFullscreenChange());window.addEventListener("webkitfullscreenchange",()=>this._OnFullscreenChange());window.addEventListener("mozfullscreenchange",()=>this._OnFullscreenChange());
|
||||
window.addEventListener("fullscreenerror",e=>this._OnFullscreenError(e));window.addEventListener("webkitfullscreenerror",e=>this._OnFullscreenError(e));window.addEventListener("mozfullscreenerror",e=>this._OnFullscreenError(e));if(iRuntime.IsiOSWebView())if(window["visualViewport"]){let lastVisualViewportHeight=Infinity;window["visualViewport"].addEventListener("resize",()=>{const curVisualViewportHeight=window["visualViewport"].height;if(curVisualViewportHeight>lastVisualViewportHeight)document.scrollingElement.scrollTop=
|
||||
0;lastVisualViewportHeight=curVisualViewportHeight})}else window.addEventListener("focusout",()=>{if(!KeyboardIsVisible())document.scrollingElement.scrollTop=0});this._mediaPendingPlay=new Set;this._mediaRemovedPendingPlay=new WeakSet;this._isSilent=false}_OnBeforeStartTicking(){self.setTimeout(()=>{this._enableAndroidVKDetection=true},1E3);if(this._iRuntime.GetExportType()==="cordova"){document.addEventListener("pause",()=>this._OnVisibilityChange(true));document.addEventListener("resume",()=>this._OnVisibilityChange(false))}else document.addEventListener("visibilitychange",
|
||||
()=>this._OnVisibilityChange(document.visibilityState==="hidden"));this._pageVisibilityIsHidden=!!(document.visibilityState==="hidden"||isCordovaPaused);return{"isSuspended":this._pageVisibilityIsHidden}}Attach(){window.addEventListener("focus",()=>this._PostRuntimeEvent("window-focus"));window.addEventListener("blur",()=>{this._PostRuntimeEvent("window-blur",{"parentHasFocus":ParentHasFocus()});this._mousePointerLastButtons=0});window.addEventListener("focusin",e=>{if(IsKeyboardInputElement(e.target))this._PostRuntimeEvent("keyboard-blur")});
|
||||
window.addEventListener("keydown",e=>this._OnKeyEvent("keydown",e));window.addEventListener("keyup",e=>this._OnKeyEvent("keyup",e));window.addEventListener("dblclick",e=>this._OnMouseEvent("dblclick",e,DISPATCH_RUNTIME_AND_SCRIPT));window.addEventListener("wheel",e=>this._OnMouseWheelEvent("wheel",e));if(typeof PointerEvent!=="undefined"){window.addEventListener("pointerdown",e=>{this._HandlePointerDownFocus(e);this._OnPointerEvent("pointerdown",e)});if(this._iRuntime.UsesWorker()&&typeof window["onpointerrawupdate"]!==
|
||||
"undefined"&&self===self.top)window.addEventListener("pointerrawupdate",e=>this._OnPointerRawUpdate(e));else window.addEventListener("pointermove",e=>this._OnPointerEvent("pointermove",e));window.addEventListener("pointerup",e=>this._OnPointerEvent("pointerup",e));window.addEventListener("pointercancel",e=>this._OnPointerEvent("pointercancel",e))}else{window.addEventListener("mousedown",e=>{this._HandlePointerDownFocus(e);this._OnMouseEventAsPointer("pointerdown",e)});window.addEventListener("mousemove",
|
||||
e=>this._OnMouseEventAsPointer("pointermove",e));window.addEventListener("mouseup",e=>this._OnMouseEventAsPointer("pointerup",e));window.addEventListener("touchstart",e=>{this._HandlePointerDownFocus(e);this._OnTouchEvent("pointerdown",e)});window.addEventListener("touchmove",e=>this._OnTouchEvent("pointermove",e));window.addEventListener("touchend",e=>this._OnTouchEvent("pointerup",e));window.addEventListener("touchcancel",e=>this._OnTouchEvent("pointercancel",e))}const playFunc=()=>this._PlayPendingMedia();
|
||||
window.addEventListener("pointerup",playFunc,true);window.addEventListener("touchend",playFunc,true);window.addEventListener("click",playFunc,true);window.addEventListener("keydown",playFunc,true);window.addEventListener("gamepadconnected",playFunc,true);if(this._iRuntime.IsAndroid()&&!this._iRuntime.IsAndroidWebView()&&navigator["virtualKeyboard"]){navigator["virtualKeyboard"]["overlaysContent"]=true;navigator["virtualKeyboard"].addEventListener("geometrychange",()=>{this._OnAndroidVirtualKeyboardChange(this._GetWindowInnerHeight(),
|
||||
navigator["virtualKeyboard"]["boundingRect"]["height"])})}}_OnAndroidVirtualKeyboardChange(windowHeight,vkHeight){document.body.style.transform="";this._vkTranslateYOffset=0;if(vkHeight>0){const activeElement=document.activeElement;if(activeElement){const rc=activeElement.getBoundingClientRect();const rcMidY=(rc.top+rc.bottom)/2;const targetY=(windowHeight-vkHeight)/2;let shiftY=rcMidY-targetY;if(shiftY>vkHeight)shiftY=vkHeight;if(shiftY<0)shiftY=0;if(shiftY>0){document.body.style.transform=`translateY(${-shiftY}px)`;
|
||||
this._vkTranslateYOffset=shiftY}}}}_PostRuntimeEvent(name,data){this.PostToRuntime(name,data||null,DISPATCH_RUNTIME_ONLY)}_GetWindowInnerWidth(){return this._iRuntime._GetWindowInnerWidth()}_GetWindowInnerHeight(){return this._iRuntime._GetWindowInnerHeight()}_EnableWindowResizeEvent(){this._enableWindowResizeEvent=true;this._lastWindowWidth=this._iRuntime._GetWindowInnerWidth();this._lastWindowHeight=this._iRuntime._GetWindowInnerHeight()}_OnWindowResize(){if(this._isExportToVideo)return;if(!this._enableWindowResizeEvent)return;
|
||||
const width=this._GetWindowInnerWidth();const height=this._GetWindowInnerHeight();if(this._iRuntime.IsAndroidWebView())if(this._enableAndroidVKDetection)if(this._lastWindowWidth===width&&height<this._lastWindowHeight){this._virtualKeyboardHeight=this._lastWindowHeight-height;this._OnAndroidVirtualKeyboardChange(this._lastWindowHeight,this._virtualKeyboardHeight);return}else{if(this._virtualKeyboardHeight>0){this._virtualKeyboardHeight=0;this._OnAndroidVirtualKeyboardChange(height,this._virtualKeyboardHeight)}this._lastWindowWidth=
|
||||
width;this._lastWindowHeight=height}else{this._lastWindowWidth=width;this._lastWindowHeight=height}this.PostToRuntime("window-resize",{"innerWidth":width,"innerHeight":height,"devicePixelRatio":window.devicePixelRatio,"isFullscreen":RuntimeInterface.IsDocumentFullscreen()});if(this._iRuntime.IsiOSWebView()){if(this._simulatedResizeTimerId!==-1)clearTimeout(this._simulatedResizeTimerId);this._OnSimulatedResize(width,height,0)}}_ScheduleSimulatedResize(width,height,count){if(this._simulatedResizeTimerId!==
|
||||
-1)clearTimeout(this._simulatedResizeTimerId);this._simulatedResizeTimerId=setTimeout(()=>this._OnSimulatedResize(width,height,count),48)}_OnSimulatedResize(originalWidth,originalHeight,count){const width=this._GetWindowInnerWidth();const height=this._GetWindowInnerHeight();this._simulatedResizeTimerId=-1;if(width!=originalWidth||height!=originalHeight)this.PostToRuntime("window-resize",{"innerWidth":width,"innerHeight":height,"devicePixelRatio":window.devicePixelRatio,"isFullscreen":RuntimeInterface.IsDocumentFullscreen()});
|
||||
else if(count<10)this._ScheduleSimulatedResize(width,height,count+1)}_OnSetTargetOrientation(e){this._targetOrientation=e["targetOrientation"]}_TrySetTargetOrientation(){const orientation=this._targetOrientation;if(screen["orientation"]&&screen["orientation"]["lock"])screen["orientation"]["lock"](orientation).catch(err=>console.warn("[Construct] Failed to lock orientation: ",err));else try{let result=false;if(screen["lockOrientation"])result=screen["lockOrientation"](orientation);else if(screen["webkitLockOrientation"])result=
|
||||
screen["webkitLockOrientation"](orientation);else if(screen["mozLockOrientation"])result=screen["mozLockOrientation"](orientation);else if(screen["msLockOrientation"])result=screen["msLockOrientation"](orientation);if(!result)console.warn("[Construct] Failed to lock orientation")}catch(err){console.warn("[Construct] Failed to lock orientation: ",err)}}_OnFullscreenChange(){if(this._isExportToVideo)return;const isDocFullscreen=RuntimeInterface.IsDocumentFullscreen();if(isDocFullscreen&&this._targetOrientation!==
|
||||
"any")this._TrySetTargetOrientation();this.PostToRuntime("fullscreenchange",{"isFullscreen":isDocFullscreen,"innerWidth":this._GetWindowInnerWidth(),"innerHeight":this._GetWindowInnerHeight()})}_OnFullscreenError(e){console.warn("[Construct] Fullscreen request failed: ",e);this.PostToRuntime("fullscreenerror",{"isFullscreen":RuntimeInterface.IsDocumentFullscreen(),"innerWidth":this._GetWindowInnerWidth(),"innerHeight":this._GetWindowInnerHeight()})}_OnVisibilityChange(isHidden){if(this._pageVisibilityIsHidden===
|
||||
isHidden)return;this._pageVisibilityIsHidden=isHidden;if(isHidden)this._iRuntime._CancelAnimationFrame();else this._iRuntime._RequestAnimationFrame();this.PostToRuntime("visibilitychange",{"hidden":isHidden})}_OnKeyEvent(name,e){if(e.key==="Backspace")PreventDefaultOnCanvasOrDoc(e);if(this._isExportToVideo)return;const code=KEY_CODE_ALIASES.get(e.code)||e.code;this._PostToRuntimeMaybeSync(name,{"code":code,"key":e.key,"which":e.which,"repeat":e.repeat,"altKey":e.altKey,"ctrlKey":e.ctrlKey,"metaKey":e.metaKey,
|
||||
"shiftKey":e.shiftKey,"timeStamp":e.timeStamp},DISPATCH_RUNTIME_AND_SCRIPT)}_OnMouseWheelEvent(name,e){if(this._isExportToVideo)return;this.PostToRuntime(name,{"clientX":e.clientX,"clientY":e.clientY+this._vkTranslateYOffset,"pageX":e.pageX,"pageY":e.pageY+this._vkTranslateYOffset,"deltaX":e.deltaX,"deltaY":e.deltaY,"deltaZ":e.deltaZ,"deltaMode":e.deltaMode,"timeStamp":e.timeStamp},DISPATCH_RUNTIME_AND_SCRIPT)}_OnMouseEvent(name,e,opts){if(this._isExportToVideo)return;if(IsCompatibilityMouseEvent(e))return;
|
||||
this._PostToRuntimeMaybeSync(name,{"button":e.button,"buttons":e.buttons,"clientX":e.clientX,"clientY":e.clientY+this._vkTranslateYOffset,"pageX":e.pageX,"pageY":e.pageY+this._vkTranslateYOffset,"movementX":e.movementX||0,"movementY":e.movementY||0,"timeStamp":e.timeStamp},opts)}_OnMouseEventAsPointer(name,e){if(this._isExportToVideo)return;if(IsCompatibilityMouseEvent(e))return;const pointerId=1;const lastButtons=this._mousePointerLastButtons;if(name==="pointerdown"&&lastButtons!==0)name="pointermove";
|
||||
else if(name==="pointerup"&&e.buttons!==0)name="pointermove";this._PostToRuntimeMaybeSync(name,{"pointerId":pointerId,"pointerType":"mouse","button":e.button,"buttons":e.buttons,"lastButtons":lastButtons,"clientX":e.clientX,"clientY":e.clientY+this._vkTranslateYOffset,"pageX":e.pageX,"pageY":e.pageY+this._vkTranslateYOffset,"movementX":e.movementX||0,"movementY":e.movementY||0,"width":0,"height":0,"pressure":0,"tangentialPressure":0,"tiltX":0,"tiltY":0,"twist":0,"timeStamp":e.timeStamp},DISPATCH_RUNTIME_AND_SCRIPT);
|
||||
this._mousePointerLastButtons=e.buttons;this._OnMouseEvent(e.type,e,DISPATCH_SCRIPT_ONLY)}_OnPointerEvent(name,e){if(this._isExportToVideo)return;let lastButtons=0;if(e.pointerType==="mouse")lastButtons=this._mousePointerLastButtons;this._PostToRuntimeMaybeSync(name,{"pointerId":e.pointerId,"pointerType":e.pointerType,"button":e.button,"buttons":e.buttons,"lastButtons":lastButtons,"clientX":e.clientX,"clientY":e.clientY+this._vkTranslateYOffset,"pageX":e.pageX,"pageY":e.pageY+this._vkTranslateYOffset,
|
||||
"movementX":e.movementX||0,"movementY":e.movementY||0,"width":e.width||0,"height":e.height||0,"pressure":e.pressure||0,"tangentialPressure":e["tangentialPressure"]||0,"tiltX":e.tiltX||0,"tiltY":e.tiltY||0,"twist":e["twist"]||0,"timeStamp":e.timeStamp},DISPATCH_RUNTIME_AND_SCRIPT);if(e.pointerType==="mouse"){let mouseEventName="mousemove";if(name==="pointerdown")mouseEventName="mousedown";else if(name==="pointerup")mouseEventName="mouseup";this._OnMouseEvent(mouseEventName,e,DISPATCH_SCRIPT_ONLY);
|
||||
this._mousePointerLastButtons=e.buttons}}_OnPointerRawUpdate(e){this._OnPointerEvent("pointermove",e)}_OnTouchEvent(fireName,e){if(this._isExportToVideo)return;for(let i=0,len=e.changedTouches.length;i<len;++i){const t=e.changedTouches[i];this._PostToRuntimeMaybeSync(fireName,{"pointerId":t.identifier,"pointerType":"touch","button":0,"buttons":0,"lastButtons":0,"clientX":t.clientX,"clientY":t.clientY+this._vkTranslateYOffset,"pageX":t.pageX,"pageY":t.pageY+this._vkTranslateYOffset,"movementX":e.movementX||
|
||||
0,"movementY":e.movementY||0,"width":(t["radiusX"]||t["webkitRadiusX"]||0)*2,"height":(t["radiusY"]||t["webkitRadiusY"]||0)*2,"pressure":t["force"]||t["webkitForce"]||0,"tangentialPressure":0,"tiltX":0,"tiltY":0,"twist":t["rotationAngle"]||0,"timeStamp":e.timeStamp},DISPATCH_RUNTIME_AND_SCRIPT)}}_HandlePointerDownFocus(e){if(window!==window.top)window.focus();if(this._IsElementCanvasOrDocument(e.target)&&document.activeElement&&!this._IsElementCanvasOrDocument(document.activeElement))document.activeElement.blur()}_IsElementCanvasOrDocument(elem){return!elem||
|
||||
elem===document||elem===window||elem===document.body||elem.tagName.toLowerCase()==="canvas"}_AttachDeviceOrientationEvent(){if(this._attachedDeviceOrientationEvent)return;this._attachedDeviceOrientationEvent=true;window.addEventListener("deviceorientation",e=>this._OnDeviceOrientation(e));window.addEventListener("deviceorientationabsolute",e=>this._OnDeviceOrientationAbsolute(e))}_AttachDeviceMotionEvent(){if(this._attachedDeviceMotionEvent)return;this._attachedDeviceMotionEvent=true;window.addEventListener("devicemotion",
|
||||
e=>this._OnDeviceMotion(e))}_OnDeviceOrientation(e){if(this._isExportToVideo)return;this.PostToRuntime("deviceorientation",{"absolute":!!e["absolute"],"alpha":e["alpha"]||0,"beta":e["beta"]||0,"gamma":e["gamma"]||0,"timeStamp":e.timeStamp,"webkitCompassHeading":e["webkitCompassHeading"],"webkitCompassAccuracy":e["webkitCompassAccuracy"]},DISPATCH_RUNTIME_AND_SCRIPT)}_OnDeviceOrientationAbsolute(e){if(this._isExportToVideo)return;this.PostToRuntime("deviceorientationabsolute",{"absolute":!!e["absolute"],
|
||||
"alpha":e["alpha"]||0,"beta":e["beta"]||0,"gamma":e["gamma"]||0,"timeStamp":e.timeStamp},DISPATCH_RUNTIME_AND_SCRIPT)}_OnDeviceMotion(e){if(this._isExportToVideo)return;let accProp=null;const acc=e["acceleration"];if(acc)accProp={"x":acc["x"]||0,"y":acc["y"]||0,"z":acc["z"]||0};let withGProp=null;const withG=e["accelerationIncludingGravity"];if(withG)withGProp={"x":withG["x"]||0,"y":withG["y"]||0,"z":withG["z"]||0};let rotationRateProp=null;const rotationRate=e["rotationRate"];if(rotationRate)rotationRateProp=
|
||||
{"alpha":rotationRate["alpha"]||0,"beta":rotationRate["beta"]||0,"gamma":rotationRate["gamma"]||0};this.PostToRuntime("devicemotion",{"acceleration":accProp,"accelerationIncludingGravity":withGProp,"rotationRate":rotationRateProp,"interval":e["interval"],"timeStamp":e.timeStamp},DISPATCH_RUNTIME_AND_SCRIPT)}_OnUpdateCanvasSize(e){const runtimeInterface=this.GetRuntimeInterface();if(runtimeInterface.IsExportingToVideo())return;const canvas=runtimeInterface.GetCanvas();canvas.style.width=e["styleWidth"]+
|
||||
"px";canvas.style.height=e["styleHeight"]+"px";canvas.style.marginLeft=e["marginLeft"]+"px";canvas.style.marginTop=e["marginTop"]+"px";document.documentElement.style.setProperty("--construct-scale",e["displayScale"]);if(this._isFirstSizeUpdate){canvas.style.display="";this._isFirstSizeUpdate=false}}_OnInvokeDownload(e){const url=e["url"];const filename=e["filename"];const a=document.createElement("a");const body=document.body;a.textContent=filename;a.href=url;a.download=filename;body.appendChild(a);
|
||||
a.click();body.removeChild(a)}async _OnLoadWebFonts(e){const webfonts=e["webfonts"];await Promise.all(webfonts.map(async info=>{const fontFace=new FontFace(info.name,`url('${info.url}')`);document.fonts.add(fontFace);await fontFace.load()}))}async _OnRasterSvgImage(e){const blob=e["blob"];const imageWidth=e["imageWidth"];const imageHeight=e["imageHeight"];const surfaceWidth=e["surfaceWidth"];const surfaceHeight=e["surfaceHeight"];const imageBitmapOpts=e["imageBitmapOpts"];const canvas=await self["C3_RasterSvgImageBlob"](blob,
|
||||
imageWidth,imageHeight,surfaceWidth,surfaceHeight);let ret;if(imageBitmapOpts)ret=await createImageBitmap(canvas,imageBitmapOpts);else ret=await createImageBitmap(canvas);return{"imageBitmap":ret,"transferables":[ret]}}async _OnGetSvgImageSize(e){return await self["C3_GetSvgImageSize"](e["blob"])}async _OnAddStylesheet(e){await AddStyleSheet(e["url"])}_PlayPendingMedia(){const mediaToTryPlay=[...this._mediaPendingPlay];this._mediaPendingPlay.clear();if(!this._isSilent)for(const mediaElem of mediaToTryPlay){const playRet=
|
||||
mediaElem.play();if(playRet)playRet.catch(err=>{if(!this._mediaRemovedPendingPlay.has(mediaElem))this._mediaPendingPlay.add(mediaElem)})}}TryPlayMedia(mediaElem){if(typeof mediaElem.play!=="function")throw new Error("missing play function");this._mediaRemovedPendingPlay.delete(mediaElem);let playRet;try{playRet=mediaElem.play()}catch(err){this._mediaPendingPlay.add(mediaElem);return}if(playRet)playRet.catch(err=>{if(!this._mediaRemovedPendingPlay.has(mediaElem))this._mediaPendingPlay.add(mediaElem)})}RemovePendingPlay(mediaElem){this._mediaPendingPlay.delete(mediaElem);
|
||||
this._mediaRemovedPendingPlay.add(mediaElem)}SetSilent(s){this._isSilent=!!s}_OnHideCordovaSplash(){if(navigator["splashscreen"]&&navigator["splashscreen"]["hide"])navigator["splashscreen"]["hide"]()}_OnDebugHighlight(e){const show=e["show"];if(!show){if(this._debugHighlightElem)this._debugHighlightElem.style.display="none";return}if(!this._debugHighlightElem){this._debugHighlightElem=document.createElement("div");this._debugHighlightElem.id="inspectOutline";document.body.appendChild(this._debugHighlightElem)}const elem=
|
||||
this._debugHighlightElem;elem.style.display="";elem.style.left=e["left"]-1+"px";elem.style.top=e["top"]-1+"px";elem.style.width=e["width"]+2+"px";elem.style.height=e["height"]+2+"px";elem.textContent=e["name"]}_OnRegisterSW(){if(window["C3_RegisterSW"])window["C3_RegisterSW"]()}_OnPostToDebugger(data){if(!window["c3_postToMessagePort"])return;data["from"]="runtime";window["c3_postToMessagePort"](data)}_InvokeFunctionFromJS(name,params){return this.PostToRuntimeAsync("js-invoke-function",{"name":name,
|
||||
"params":params})}_OnScriptCreateWorker(e){const url=e["url"];const opts=e["opts"];const port2=e["port2"];const worker=new Worker(url,opts);worker.postMessage({"type":"construct-worker-init","port2":port2},[port2])}_OnAlert(e){alert(e["message"])}_OnScreenReaderTextEvent(e){const type=e["type"];if(type==="create"){const p=document.createElement("p");p.id="c3-sr-"+e["id"];p.textContent=e["text"];this._screenReaderTextWrap.appendChild(p)}else if(type==="update"){const p=document.getElementById("c3-sr-"+
|
||||
e["id"]);if(p)p.textContent=e["text"];else console.warn(`[Construct] Missing screen reader text with id ${e["id"]}`)}else if(type==="release"){const p=document.getElementById("c3-sr-"+e["id"]);if(p)p.remove();else console.warn(`[Construct] Missing screen reader text with id ${e["id"]}`)}else console.warn(`[Construct] Unknown screen reader text update '${type}'`)}_SetExportingToVideo(e){this._isExportToVideo=true;const headerElem=document.createElement("h1");headerElem.id="exportToVideoMessage";headerElem.textContent=
|
||||
e["message"];document.body.prepend(headerElem);document.body.classList.add("exportingToVideo");this.GetRuntimeInterface().GetCanvas().style.display="";this._iRuntime.SetIsExportingToVideo(e["duration"])}_OnExportVideoProgress(e){this._exportVideoProgressMessage=e["message"];if(this._exportVideoUpdateTimerId===-1)this._exportVideoUpdateTimerId=setTimeout(()=>this._DoUpdateExportVideoProgressMessage(),250)}_DoUpdateExportVideoProgressMessage(){this._exportVideoUpdateTimerId=-1;const headerElem=document.getElementById("exportToVideoMessage");
|
||||
if(headerElem)headerElem.textContent=this._exportVideoProgressMessage}_OnExportedToVideo(e){window.c3_postToMessagePort({"type":"exported-video","arrayBuffer":e["arrayBuffer"],"contentType":e["contentType"],"time":e["time"]})}_OnExportedToImageSequence(e){window.c3_postToMessagePort({"type":"exported-image-sequence","blobArr":e["blobArr"],"time":e["time"],"gif":e["gif"]})}};RuntimeInterface.AddDOMHandlerClass(HANDLER_CLASS)};
|
||||
|
||||
|
||||
'use strict';{const DISPATCH_WORKER_SCRIPT_NAME="dispatchworker.js";const JOB_WORKER_SCRIPT_NAME="jobworker.js";self.JobSchedulerDOM=class JobSchedulerDOM{constructor(runtimeInterface){this._runtimeInterface=runtimeInterface;this._baseUrl=runtimeInterface.GetRuntimeBaseURL();if(runtimeInterface.GetExportType()==="preview")this._baseUrl+="workers/";else this._baseUrl+=runtimeInterface.GetScriptFolder();this._maxNumWorkers=Math.min(navigator.hardwareConcurrency||2,16);this._dispatchWorker=null;this._jobWorkers=
|
||||
[];this._inputPort=null;this._outputPort=null}_GetWorkerScriptFolder(){if(this._runtimeInterface.GetExportType()==="playable-ad")return this._runtimeInterface.GetScriptFolder();else return""}async Init(){if(this._hasInitialised)throw new Error("already initialised");this._hasInitialised=true;const dispatchWorkerScriptUrl=this._runtimeInterface._GetWorkerURL(this._GetWorkerScriptFolder()+DISPATCH_WORKER_SCRIPT_NAME);this._dispatchWorker=await this._runtimeInterface.CreateWorker(dispatchWorkerScriptUrl,
|
||||
this._baseUrl,{name:"DispatchWorker"});const messageChannel=new MessageChannel;this._inputPort=messageChannel.port1;this._dispatchWorker.postMessage({"type":"_init","in-port":messageChannel.port2},[messageChannel.port2]);this._outputPort=await this._CreateJobWorker()}async _CreateJobWorker(){const number=this._jobWorkers.length;const jobWorkerScriptUrl=this._runtimeInterface._GetWorkerURL(this._GetWorkerScriptFolder()+JOB_WORKER_SCRIPT_NAME);const jobWorker=await this._runtimeInterface.CreateWorker(jobWorkerScriptUrl,
|
||||
this._baseUrl,{name:"JobWorker"+number});const dispatchChannel=new MessageChannel;const outputChannel=new MessageChannel;this._dispatchWorker.postMessage({"type":"_addJobWorker","port":dispatchChannel.port1},[dispatchChannel.port1]);jobWorker.postMessage({"type":"init","number":number,"dispatch-port":dispatchChannel.port2,"output-port":outputChannel.port2},[dispatchChannel.port2,outputChannel.port2]);this._jobWorkers.push(jobWorker);return outputChannel.port1}GetPortData(){return{"inputPort":this._inputPort,
|
||||
"outputPort":this._outputPort,"maxNumWorkers":this._maxNumWorkers}}GetPortTransferables(){return[this._inputPort,this._outputPort]}}};
|
||||
|
||||
|
||||
'use strict';{if(window["C3_IsSupported"]){const enableWorker=false;window["c3_runtimeInterface"]=new self.RuntimeInterface({useWorker:enableWorker,workerMainUrl:"workermain.js",engineScripts:["scripts/c3runtime.js"],projectScripts:[["scripts/project/main.js"]],mainProjectScript:"scripts/project/main.js",scriptFolder:"scripts/",workerDependencyScripts:[],exportType:"html5"})}};
|
||||
'use strict';{const DOM_COMPONENT_ID="mouse";const HANDLER_CLASS=class MouseDOMHandler extends self.DOMHandler{constructor(iRuntime){super(iRuntime,DOM_COMPONENT_ID);this.AddRuntimeMessageHandlers([["cursor",e=>this._OnChangeCursorStyle(e)],["request-pointer-lock",()=>this._OnRequestPointerLock()],["release-pointer-lock",()=>this._OnReleasePointerLock()]]);document.addEventListener("pointerlockchange",e=>this._OnPointerLockChange());document.addEventListener("pointerlockerror",e=>this._OnPointerLockError())}_OnChangeCursorStyle(e){document.documentElement.style.cursor=
|
||||
e}_OnRequestPointerLock(){this._iRuntime.GetCanvas().requestPointerLock()}_OnReleasePointerLock(){document.exitPointerLock()}_OnPointerLockChange(){this.PostToRuntime("pointer-lock-change",{"has-pointer-lock":!!document.pointerLockElement})}_OnPointerLockError(){this.PostToRuntime("pointer-lock-error",{"has-pointer-lock":!!document.pointerLockElement})}};self.RuntimeInterface.AddDOMHandlerClass(HANDLER_CLASS)};
|
||||
@@ -0,0 +1,2 @@
|
||||
'use strict';{class OfflineClient{constructor(){this._broadcastChannel=typeof BroadcastChannel==="undefined"?null:new BroadcastChannel("offline");this._queuedMessages=[];this._onMessageCallback=null;if(this._broadcastChannel)this._broadcastChannel.onmessage=e=>this._OnBroadcastChannelMessage(e)}_OnBroadcastChannelMessage(e){if(this._onMessageCallback){this._onMessageCallback(e);return}this._queuedMessages.push(e)}SetMessageCallback(f){this._onMessageCallback=f;for(let e of this._queuedMessages)this._onMessageCallback(e);
|
||||
this._queuedMessages.length=0}}window.OfflineClientInfo=new OfflineClient};
|
||||
@@ -0,0 +1,407 @@
|
||||
/*
|
||||
* Made by Viridino Studios (@ViridinoStudios)
|
||||
*
|
||||
* Felipe Vaiano Calderan - Programmer
|
||||
* Twitter: @fvcalderan
|
||||
* E-mail: [email protected]
|
||||
*
|
||||
* Wesley Andrade - Artist
|
||||
* Twitter: @andrart7
|
||||
* E-mail: [email protected]
|
||||
*
|
||||
* Made with the support of patrons on https://www.patreon.com/viridinostudios
|
||||
*/
|
||||
|
||||
//=============================================================================
|
||||
|
||||
// Camera view variables
|
||||
let camLookTheta;
|
||||
let camLookPhi;
|
||||
let camLookX;
|
||||
let camLookY;
|
||||
let camLookZ;
|
||||
|
||||
// These variables store object instances that are referenced later.
|
||||
let player;
|
||||
let playerSpawn;
|
||||
let playerEscapeZone;
|
||||
let hudRadar;
|
||||
let hudTextSuccess;
|
||||
let hudTextFail;
|
||||
let hudTextTutorial;
|
||||
let hudFader;
|
||||
|
||||
// List of instances
|
||||
let turrets;
|
||||
let hudDots;
|
||||
let playerSpotters;
|
||||
let playerSafeZones;
|
||||
|
||||
// Global objects
|
||||
let camera;
|
||||
let mouse;
|
||||
let keyboard;
|
||||
|
||||
// Special objects
|
||||
let mouseLocker; // Canvas to request mouse lock
|
||||
|
||||
// Gameplay variables
|
||||
let playerCrouching; // Is the player crouching?
|
||||
let inputsDisabled; // Is the player frozen?
|
||||
|
||||
// Settings
|
||||
const MOUSESENSITIVITY = 0.0025; // Mouse sensitivity
|
||||
const CROUCHSPEED = 50; // Player crouch speed
|
||||
const WALKSPEED = 100; // Player waking speed
|
||||
const NUMTURRETS = 3; // Number of Turrets
|
||||
const RADARDISTFACTOR = 0.05; // Radar distance factor
|
||||
const RADARMAXDIST = 19; // Radar maximum scan distance
|
||||
const TURRETANGLEFIX = 3.92699; // Fix turret angle
|
||||
const RESETFADETIME = 3.25; // Time to fade-out-in the game
|
||||
const TEXTBLINKSPEED = 0.25; // Success/Fail text blink speed
|
||||
|
||||
runOnStartup(async runtime => {
|
||||
// Code to run on the loading screen.
|
||||
|
||||
runtime.addEventListener(
|
||||
"beforeprojectstart", () => onBeforeProjectStart(runtime)
|
||||
);
|
||||
});
|
||||
|
||||
async function onBeforeProjectStart(runtime)
|
||||
{
|
||||
// Code to run just before 'On start of layout'
|
||||
|
||||
// Get instances
|
||||
player = runtime.objects.Player.getFirstInstance();
|
||||
playerSpawn = runtime.objects.PlayerSpawn.getFirstInstance();
|
||||
playerEscapeZone = runtime.objects.PlayerEscapeZone.getFirstInstance();
|
||||
hudRadar = runtime.objects.HUDRadar.getFirstInstance();
|
||||
hudTextSuccess = runtime.objects.HUDTextSuccess.getFirstInstance();
|
||||
hudTextFail = runtime.objects.HUDTextFail.getFirstInstance();
|
||||
hudTextTutorial = runtime.objects.HUDTextTutorial.getFirstInstance();
|
||||
hudFader = runtime.objects.HUDFader.getFirstInstance();
|
||||
|
||||
// Get list of instances
|
||||
turrets = runtime.objects.TurretBody.getAllInstances();
|
||||
playerSpotters = runtime.objects.PlayerSpotter.getAllInstances();
|
||||
playerSafeZones = runtime.objects.PlayerSafeZone.getAllInstances();
|
||||
|
||||
// Get global objects
|
||||
camera = runtime.objects.Camera3D;
|
||||
mouse = runtime.mouse;
|
||||
keyboard = runtime.keyboard;
|
||||
|
||||
// Configure initial parameters for the game
|
||||
setupGame(runtime);
|
||||
|
||||
// Remove mouse cursor
|
||||
runtime.mouse.setCursorStyle("None");
|
||||
|
||||
// Create a canvas to request mouse lock
|
||||
mouseLocker = document.createElement("canvas");
|
||||
document.body.appendChild(mouseLocker);
|
||||
|
||||
// Mouse events
|
||||
runtime.addEventListener("mousedown", e => onMouseDown(e));
|
||||
runtime.addEventListener("mousemove", e => onMouseMove(e));
|
||||
|
||||
// Keyboard events
|
||||
runtime.addEventListener("keydown", e => onKeyDown(e));
|
||||
|
||||
// Start ticking
|
||||
runtime.addEventListener("tick", () => onTick(runtime));
|
||||
}
|
||||
|
||||
|
||||
function setupGame(runtime) {
|
||||
// Configure initial parameters for the game
|
||||
|
||||
// Disable player's input and show tutorial
|
||||
inputsDisabled = true;
|
||||
hudTextTutorial.behaviors.Tween.startTween(
|
||||
"opacity", 1, 0.5, "in-out-sine"
|
||||
);
|
||||
|
||||
// Set player spawn position
|
||||
player.x = playerSpawn.x;
|
||||
player.y = playerSpawn.y;
|
||||
|
||||
// Starting camera's spherical coordinates
|
||||
camLookTheta = 0;
|
||||
camLookPhi = Math.PI/2;
|
||||
|
||||
// Spherical coordinates converted to cartesian coordinates
|
||||
setCameraCartesian(camLookTheta, camLookPhi);
|
||||
|
||||
// Set starting look position
|
||||
camera.lookAtPosition(
|
||||
player.x, player.y, player.zElevation + player.zHeight - 2,
|
||||
camLookX, camLookY, camLookZ, 0, 0, 1
|
||||
);
|
||||
|
||||
// Setup player crouching state (false at the start)
|
||||
playerCrouching = false;
|
||||
|
||||
// Setup radar dots
|
||||
hudDots = new Array(NUMTURRETS).fill(null);
|
||||
|
||||
for (const dot of runtime.objects.HUDDot.getAllInstances()) {
|
||||
dot.destroy();
|
||||
}
|
||||
|
||||
for (const turret of turrets) {
|
||||
hudDots[turret.instVars.id] = runtime.objects.HUDDot.createInstance(
|
||||
"HUD", hudRadar.x, hudRadar.y
|
||||
);
|
||||
}
|
||||
|
||||
// Setup mission success text
|
||||
hudTextSuccess.isVisible = false;
|
||||
hudTextSuccess.behaviors.Flash.flash(TEXTBLINKSPEED, TEXTBLINKSPEED, 0);
|
||||
|
||||
// Setup mission failed text
|
||||
hudTextFail.isVisible = false;
|
||||
hudTextFail.behaviors.Flash.flash(TEXTBLINKSPEED, TEXTBLINKSPEED, 0);
|
||||
|
||||
// Setup fader
|
||||
hudFader.behaviors.Tween.startTween(
|
||||
"opacity", 0, RESETFADETIME/2, "in-out-sine"
|
||||
);
|
||||
|
||||
// Reset view bobbing speed
|
||||
setViewBobbing(8, 4);
|
||||
}
|
||||
|
||||
function onMouseDown(_) {
|
||||
// If mouse is not locket yet, lock it
|
||||
|
||||
if (document.pointerLockElement !== mouseLocker) {
|
||||
mouseLocker.requestPointerLock();
|
||||
}
|
||||
}
|
||||
|
||||
function onMouseMove(e) {
|
||||
// Process mouse movement
|
||||
|
||||
// Set camera's spherical coordinates according to mouse movement
|
||||
camLookTheta += e.movementX * MOUSESENSITIVITY;
|
||||
camLookPhi = Math.max(
|
||||
Math.min(
|
||||
camLookPhi - e.movementY * MOUSESENSITIVITY, Math.PI - 0.1
|
||||
), 0.1
|
||||
);
|
||||
}
|
||||
|
||||
function onKeyDown(e) {
|
||||
// Check if a key has been pressed
|
||||
|
||||
// [Space] hides the tutorial and enables input
|
||||
if (e.key == " " && inputsDisabled) {
|
||||
inputsDisabled = false;
|
||||
hudTextTutorial.behaviors.Tween.startTween(
|
||||
"opacity", 0, 0.5, "in-out-sine"
|
||||
);
|
||||
}
|
||||
|
||||
// [C] toggles player's crouching state
|
||||
if ((e.key == "c" || e.key == "C") && !inputsDisabled) {
|
||||
playerCrouching = !playerCrouching;
|
||||
}
|
||||
}
|
||||
|
||||
function onTick(runtime) {
|
||||
// Code to run every tick
|
||||
|
||||
setCamera3D(runtime);
|
||||
getKeyboardInputs();
|
||||
updateRadar();
|
||||
spotPlayer(runtime);
|
||||
playerEscape(runtime);
|
||||
}
|
||||
|
||||
function setCamera3D(runtime) {
|
||||
// Set camera position and rotation
|
||||
|
||||
const camX = player.x; // Camera X
|
||||
const camY = player.y; // Camera Y
|
||||
const camZ = lerp(
|
||||
camera.getCameraPosition()[2],
|
||||
player.zElevation + player.zHeight - (playerCrouching ? 16 : 2),
|
||||
0.2 * 60 * runtime.dt
|
||||
); // Camera Z (lerp is for smooth crouching/standing)
|
||||
|
||||
// Spherical coordinates converted to cartesian coordinates
|
||||
setCameraCartesian(camLookTheta, camLookPhi);
|
||||
|
||||
// Apply camera settings
|
||||
camera.lookAtPosition(
|
||||
camX, camY, camZ,
|
||||
camX + camLookX,camY + camLookY, camZ + camLookZ,
|
||||
0, 0, 1
|
||||
);
|
||||
}
|
||||
|
||||
function getKeyboardInputs() {
|
||||
// Get player's inputs
|
||||
|
||||
// If the tutorial is being displayed, ignore everything
|
||||
if (inputsDisabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Shorthand for player's 8 Direction behavior
|
||||
const eightd = player.behaviors.EightDirection;
|
||||
|
||||
// Is the player crouching?
|
||||
eightd.maxSpeed = playerCrouching ? CROUCHSPEED : WALKSPEED;
|
||||
|
||||
// Directional inputs
|
||||
const inputRight = keyboard.isKeyDown("KeyD");
|
||||
const inputLeft = keyboard.isKeyDown("KeyA");
|
||||
const inputUp = keyboard.isKeyDown("KeyW");
|
||||
const inputDown = keyboard.isKeyDown("KeyS");
|
||||
|
||||
// Compute axes
|
||||
const horizontalAxis = inputRight - inputLeft;
|
||||
const verticalAxis = inputUp - inputDown;
|
||||
|
||||
// Move the player's object and increase bobbing if an axis is active
|
||||
if (horizontalAxis != 0 || verticalAxis != 0) {
|
||||
// Get the angle to move
|
||||
const axisAngle = Math.atan2(horizontalAxis, verticalAxis);
|
||||
|
||||
// Set 8Direction movement vector
|
||||
eightd.setVector(
|
||||
Math.cos(camLookTheta + axisAngle) * eightd.maxSpeed,
|
||||
Math.sin(camLookTheta + axisAngle) * eightd.maxSpeed,
|
||||
)
|
||||
|
||||
// Increase view bobbing speed
|
||||
setViewBobbing(2, 1);
|
||||
|
||||
// Otherwise make the player stationary and view bobbing slow
|
||||
} else {
|
||||
eightd.setVector(0, 0);
|
||||
setViewBobbing(8, 4);
|
||||
}
|
||||
}
|
||||
|
||||
function updateRadar() {
|
||||
// Update radar dots
|
||||
|
||||
for (const turret of turrets) {
|
||||
// Get angle and distance between player and turret
|
||||
const params = [player.x, player.y, turret.x, turret.y]
|
||||
const a = angleBetween(...params) - camLookTheta - Math.PI/2;
|
||||
const d = distanceBetween(...params) * RADARDISTFACTOR;
|
||||
|
||||
// Update dots on the radar according to new angle and distance
|
||||
const dot = hudDots[turret.instVars.id];
|
||||
dot.x = hudRadar.x + Math.cos(a) * d;
|
||||
dot.y = hudRadar.y + Math.sin(a) * d;
|
||||
dot.angle = turret.angle - camLookTheta - Math.PI/2 - TURRETANGLEFIX;
|
||||
dot.isVisible = d < RADARMAXDIST;
|
||||
}
|
||||
}
|
||||
|
||||
function spotPlayer(runtime) {
|
||||
// Turrets spot player if they have them in sight
|
||||
|
||||
// For each spotter
|
||||
for (const ps of playerSpotters) {
|
||||
|
||||
// Check if the player is overlapping this spotter
|
||||
if (
|
||||
player.testOverlap(ps) &&
|
||||
!hudTextFail.behaviors.Flash.isFlashing
|
||||
) {
|
||||
// Assume that the player is not safe
|
||||
let playerSafe = false;
|
||||
|
||||
// Check if the player is inside a safe zone
|
||||
for (const sz of playerSafeZones) {
|
||||
if (player.testOverlap(sz)) {
|
||||
// Deal with player crouching state
|
||||
playerSafe = (sz.instVars.crouchingOnly && playerCrouching)
|
||||
|| !sz.instVars.crouchingOnly;
|
||||
}
|
||||
}
|
||||
|
||||
// If the player is not safe, then it is game over
|
||||
if (!playerSafe) {
|
||||
gameOver(runtime, hudTextFail);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function playerEscape(runtime) {
|
||||
// Player escaped successfully
|
||||
|
||||
if (
|
||||
player.testOverlap(playerEscapeZone) &&
|
||||
!hudTextSuccess.behaviors.Flash.isFlashing
|
||||
) {
|
||||
gameOver(runtime, hudTextSuccess);
|
||||
}
|
||||
}
|
||||
|
||||
function gameOver(runtime, activeText) {
|
||||
// Start game over procedures
|
||||
|
||||
// Show fail / success text
|
||||
activeText.isVisible = true;
|
||||
|
||||
// Start flashing the text
|
||||
activeText.behaviors.Flash.flash(0.33, 0.33, Number.MAX_SAFE_INTEGER);
|
||||
hudFader.behaviors.Tween.startTween(
|
||||
"opacity", 1, RESETFADETIME/2, "in-out-sine"
|
||||
);
|
||||
|
||||
// Fix Z-Ordering
|
||||
hudFader.moveToTop();
|
||||
activeText.moveToTop();
|
||||
|
||||
// Disable player movement
|
||||
inputsDisabled = true;
|
||||
|
||||
// Decrease view bobbing speed
|
||||
setViewBobbing(8, 4);
|
||||
|
||||
// Call restart
|
||||
setTimeout(() => setupGame(runtime), RESETFADETIME*1000);
|
||||
}
|
||||
|
||||
function setCameraCartesian(theta, phi) {
|
||||
// Convert spherical to cartesian and set camLooks
|
||||
|
||||
camLookX = Math.cos(theta) * Math.sin(phi);
|
||||
camLookY = Math.sin(theta) * Math.sin(phi);
|
||||
camLookZ = -Math.cos(phi);
|
||||
}
|
||||
|
||||
function setViewBobbing(h, v) {
|
||||
// Set view bobbing
|
||||
|
||||
hudRadar.behaviors.SineH.period = h;
|
||||
hudRadar.behaviors.SineV.period = v;
|
||||
}
|
||||
|
||||
function lerp(start, end, amt) {
|
||||
// Simple helper function for linear interpolation
|
||||
|
||||
return (1 - amt) * start + amt * end;
|
||||
}
|
||||
|
||||
function distanceBetween(x1, y1, x2, y2) {
|
||||
// Calculate 2D distance between two coordinates
|
||||
|
||||
return Math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2);
|
||||
}
|
||||
|
||||
function angleBetween(x1, y1, x2, y2) {
|
||||
// Calculate 2D angle between two coordinates
|
||||
|
||||
return Math.atan2(y2 - y1, x2 - x1);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
'use strict';{window.C3_RegisterSW=async function C3_RegisterSW(){if(!navigator.serviceWorker)return;try{const reg=await navigator.serviceWorker.register("sw.js",{scope:"./"});console.info("Registered service worker on "+reg.scope)}catch(err){console.warn("Failed to register service worker: ",err)}}};
|
||||
@@ -0,0 +1,5 @@
|
||||
'use strict';(function(){var isKasperskyScriptInjected=!!document.querySelector('script[src*="kaspersky"]');var tmpCanvas=document.createElement("canvas");var hasWebGL=!!tmpCanvas.getContext("webgl");var missingFeatures=[];if(!hasWebGL)missingFeatures.push("WebGL");if(typeof WebAssembly==="undefined")missingFeatures.push("WebAssembly");if(!("noModule"in HTMLScriptElement.prototype))missingFeatures.push("JavaScript Modules");if(missingFeatures.length===0&&!isKasperskyScriptInjected)window["C3_IsSupported"]=
|
||||
true;else{var msgWrap=document.createElement("div");msgWrap.id="notSupportedWrap";document.body.appendChild(msgWrap);var msgTitle=document.createElement("h2");msgTitle.id="notSupportedTitle";if(isKasperskyScriptInjected)msgTitle.textContent="Kaspersky Internet Security broke this export";else msgTitle.textContent="Software update needed";msgWrap.appendChild(msgTitle);var msgBody=document.createElement("p");msgBody.className="notSupportedMessage";var msgText="This content is not supported because your device's software appears to be out-of-date. ";
|
||||
var ua=navigator.userAgent;if(/android/i.test(ua))msgText+='<br><br>On Android, fix this by making sure the <a href="https://play.google.com/store/apps/details?id=com.google.android.webview">Android System Webview</a> app has updates enabled and is up-to-date.';else if(/iphone|ipad|ipod/i.test(ua)){msgText+="Alternatively if <strong>Lockdown mode</strong> is enabled, try turning it off to view this content.";msgText+="<br><br>Note: using the <strong>iOS simulator</strong> requires <strong>Xcode 12+</strong>. Otherwise try testing on a real device instead."}else if(/msie/i.test(ua)||
|
||||
/trident/i.test(ua)||/edge\//i.test(ua))msgText+="<br><br>Note: <strong>Internet Explorer</strong> and the <strong>legacy Edge browser</strong> are not supported. Try using <a href='https://www.google.com/chrome'>Chrome</a> or <a href='https://www.mozilla.org/firefox'>Firefox</a> instead.";else if(isKasperskyScriptInjected)msgText="It appears a script was added to this export by Kaspersky software. This prevents the exported project from working. Try disabling Kaspersky and exporting again.";else msgText+=
|
||||
"Try installing any available software updates. Alternatively try on a different device.";msgText+="<br><br><em>Missing features: "+missingFeatures.join(", ")+"<br>User agent: "+navigator.userAgent+"</em>";msgBody.innerHTML=msgText;msgWrap.appendChild(msgBody)}})();
|
||||
@@ -0,0 +1,80 @@
|
||||
html, body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
|
||||
background: #000000;
|
||||
color: white;
|
||||
}
|
||||
|
||||
html, body, canvas {
|
||||
touch-action: none;
|
||||
touch-action-delay: none;
|
||||
}
|
||||
|
||||
#notSupportedWrap {
|
||||
margin: 2em auto 1em auto;
|
||||
width: 75%;
|
||||
max-width: 45em;
|
||||
border: 2px solid #aaa;
|
||||
border-radius: 1em;
|
||||
padding: 2em;
|
||||
background-color: #f0f0f0;
|
||||
font-family: "Segoe UI", Frutiger, "Frutiger Linotype", "Dejavu Sans", "Helvetica Neue", Arial, sans-serif;
|
||||
color: black;
|
||||
}
|
||||
|
||||
#notSupportedTitle {
|
||||
font-size: 1.8em;
|
||||
}
|
||||
|
||||
.notSupportedMessage {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.notSupportedMessage em {
|
||||
color: #888;
|
||||
}
|
||||
|
||||
/* bbcode styles */
|
||||
.bbCodeH1 {
|
||||
font-size: 2em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.bbCodeH2 {
|
||||
font-size: 1.5em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.bbCodeH3 {
|
||||
font-size: 1.25em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.bbCodeH4 {
|
||||
font-size: 1.1em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.bbCodeItem::before {
|
||||
content: " • ";
|
||||
}
|
||||
|
||||
/* For text icons converted to HTML: size the height to the line height
|
||||
preserving the aspect ratio. Also add position: relative as that allows
|
||||
just adding a 'top' style for the iconoffsety style. */
|
||||
.c3-text-icon {
|
||||
height: 1em;
|
||||
width: auto;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* screen reader text */
|
||||
.c3-screen-reader-text {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
clip: rect(1px, 1px, 1px, 1px);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
'use strict';const OFFLINE_DATA_FILE="offline.json";const CACHE_NAME_PREFIX="c3offline";const BROADCASTCHANNEL_NAME="offline";const CONSOLE_PREFIX="[SW] ";const LAZYLOAD_KEYNAME="";const broadcastChannel=typeof BroadcastChannel==="undefined"?null:new BroadcastChannel(BROADCASTCHANNEL_NAME);function PostBroadcastMessage(o){if(!broadcastChannel)return;setTimeout(()=>broadcastChannel.postMessage(o),3E3)}function Broadcast(type){PostBroadcastMessage({"type":type})}
|
||||
function BroadcastDownloadingUpdate(version){PostBroadcastMessage({"type":"downloading-update","version":version})}function BroadcastUpdateReady(version){PostBroadcastMessage({"type":"update-ready","version":version})}function IsUrlInLazyLoadList(url,lazyLoadList){if(!lazyLoadList)return false;try{for(const lazyLoadRegex of lazyLoadList)if((new RegExp(lazyLoadRegex)).test(url))return true}catch(err){console.error(CONSOLE_PREFIX+"Error matching in lazy-load list: ",err)}return false}
|
||||
function WriteLazyLoadListToStorage(lazyLoadList){if(typeof localforage==="undefined")return Promise.resolve();else return localforage.setItem(LAZYLOAD_KEYNAME,lazyLoadList)}function ReadLazyLoadListFromStorage(){if(typeof localforage==="undefined")return Promise.resolve([]);else return localforage.getItem(LAZYLOAD_KEYNAME)}function GetCacheBaseName(){return CACHE_NAME_PREFIX+"-"+self.registration.scope}function GetCacheVersionName(version){return GetCacheBaseName()+"-v"+version}
|
||||
async function GetAvailableCacheNames(){const cacheNames=await caches.keys();const cacheBaseName=GetCacheBaseName();return cacheNames.filter(n=>n.startsWith(cacheBaseName))}async function IsUpdatePending(){const availableCacheNames=await GetAvailableCacheNames();return availableCacheNames.length>=2}
|
||||
async function GetMainPageUrl(){const allClients=await clients.matchAll({includeUncontrolled:true,type:"window"});for(const c of allClients){let url=c.url;if(url.startsWith(self.registration.scope))url=url.substring(self.registration.scope.length);if(url&&url!=="/"){if(url.startsWith("?"))url="/"+url;return url}}return""}
|
||||
function fetchWithBypass(request,bypassCache){if(typeof request==="string")request=new Request(request);if(bypassCache)return fetch(request.url,{headers:request.headers,mode:request.mode,credentials:request.credentials,redirect:request.redirect,cache:"no-store"});else return fetch(request)}
|
||||
async function CreateCacheFromFileList(cacheName,fileList,bypassCache){const responses=await Promise.all(fileList.map(url=>fetchWithBypass(url,bypassCache)));let allOk=true;for(const response of responses)if(!response.ok){allOk=false;console.error(CONSOLE_PREFIX+"Error fetching '"+response.url+"' ("+response.status+" "+response.statusText+")")}if(!allOk)throw new Error("not all resources were fetched successfully");const cache=await caches.open(cacheName);try{return await Promise.all(responses.map((response,
|
||||
i)=>cache.put(fileList[i],response)))}catch(err){console.error(CONSOLE_PREFIX+"Error writing cache entries: ",err);caches.delete(cacheName);throw err;}}
|
||||
async function UpdateCheck(isFirst){try{const response=await fetchWithBypass(OFFLINE_DATA_FILE,true);if(!response.ok)throw new Error(OFFLINE_DATA_FILE+" responded with "+response.status+" "+response.statusText);const data=await response.json();const version=data.version;const fileList=data.fileList;const lazyLoadList=data.lazyLoad;const currentCacheName=GetCacheVersionName(version);const cacheExists=await caches.has(currentCacheName);if(cacheExists){const isUpdatePending=await IsUpdatePending();if(isUpdatePending){console.log(CONSOLE_PREFIX+
|
||||
"Update pending");Broadcast("update-pending")}else{console.log(CONSOLE_PREFIX+"Up to date");Broadcast("up-to-date")}return}const mainPageUrl=await GetMainPageUrl();fileList.unshift("./");if(mainPageUrl&&fileList.indexOf(mainPageUrl)===-1)fileList.unshift(mainPageUrl);console.log(CONSOLE_PREFIX+"Caching "+fileList.length+" files for offline use");if(isFirst)Broadcast("downloading");else BroadcastDownloadingUpdate(version);if(lazyLoadList)await WriteLazyLoadListToStorage(lazyLoadList);await CreateCacheFromFileList(currentCacheName,
|
||||
fileList,!isFirst);const isUpdatePending=await IsUpdatePending();if(isUpdatePending){console.log(CONSOLE_PREFIX+"All resources saved, update ready");BroadcastUpdateReady(version)}else{console.log(CONSOLE_PREFIX+"All resources saved, offline support ready");Broadcast("offline-ready")}}catch(err){console.warn(CONSOLE_PREFIX+"Update check failed: ",err)}}self.addEventListener("install",event=>{event.waitUntil(UpdateCheck(true).catch(()=>null))});
|
||||
async function GetCacheNameToUse(availableCacheNames,doUpdateCheck){if(availableCacheNames.length===1||!doUpdateCheck)return availableCacheNames[0];const allClients=await clients.matchAll();if(allClients.length>1)return availableCacheNames[0];const latestCacheName=availableCacheNames[availableCacheNames.length-1];console.log(CONSOLE_PREFIX+"Updating to new version");await Promise.all(availableCacheNames.slice(0,-1).map(c=>caches.delete(c)));return latestCacheName}
|
||||
async function HandleFetch(event,doUpdateCheck){const availableCacheNames=await GetAvailableCacheNames();if(!availableCacheNames.length)return fetch(event.request);const useCacheName=await GetCacheNameToUse(availableCacheNames,doUpdateCheck);const cache=await caches.open(useCacheName);const cachedResponse=await cache.match(event.request);if(cachedResponse)return cachedResponse;const result=await Promise.all([fetch(event.request),ReadLazyLoadListFromStorage()]);const fetchResponse=result[0];const lazyLoadList=
|
||||
result[1];if(IsUrlInLazyLoadList(event.request.url,lazyLoadList))try{await cache.put(event.request,fetchResponse.clone())}catch(err){console.warn(CONSOLE_PREFIX+"Error caching '"+event.request.url+"': ",err)}return fetchResponse}
|
||||
self.addEventListener("fetch",event=>{if((new URL(event.request.url)).origin!==location.origin)return;const doUpdateCheck=event.request.mode==="navigate";const responsePromise=HandleFetch(event,doUpdateCheck);if(doUpdateCheck)event.waitUntil(responsePromise.then(()=>UpdateCheck(false)));event.respondWith(responsePromise)});
|
||||