handler-playable-sdk 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Handler
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,26 @@
1
+ # @handler/playable-sdk
2
+
3
+ Contract-aligned Handler Playable SDK v0.1. Surface is fixed: `init`, `getRoot`, `on`, `off`, `start`, `finish`, `install`, `emit`. Provides a single root sandbox, canonical event envelope, and ID requirements for publish builds.
4
+
5
+ ## API (v0.1)
6
+
7
+ - `Handler.init({ root?, ids, profile, consent })`: creates or adopts a single root. `ids` must include mechanic_id, variant_id, deployment_id, export_id, profile_id, instance_id (instance default “default” only allowed in dev). Consent/telemetry pulled from export profile.
8
+ - `Handler.getRoot()`: returns the sandbox root element.
9
+ - `Handler.on(name, cb)`, `Handler.off(name, cb)`: subscribe/unsubscribe to canonical events or custom namespaced ones.
10
+ - `Handler.start()`, `Handler.finish()`: lifecycle markers.
11
+ - `Handler.install(url?)`: emits `cta_click` and, if profile policy demands, `conversion`. Direct network open is deferred to capability policy.
12
+ - `Handler.emit(name, payload)`: emits canonical or namespaced custom events. Reserved envelope keys are always added/overwritten.
13
+
14
+ Canonical event mapping from Smoud: `interaction -> engagement`, `finish -> complete`, `install -> cta_click (+conversion policy)`, `pause/resume/resize/volume` mapped 1:1 inside the envelope. Custom events must use `custom.<mechanic_id>.<event>`.
15
+
16
+ ## Build
17
+
18
+ ```bash
19
+ npm run build
20
+ ```
21
+
22
+ Outputs ESM/CJS + d.ts to `dist/`.
23
+
24
+ ## License
25
+
26
+ MIT
@@ -0,0 +1,17 @@
1
+ var u={};function I(e,t,n=!1){u[e]||(u[e]=[]),u[e].push({fn:t,once:n})}function V(e,t){if(u[e]){if(!t){delete u[e];return}u[e]=u[e].filter(n=>n.fn!==t)}}function P(e,...t){let n=u[e];if(n)for(let i of[...n])i.fn(...t),i.once&&V(e,i.fn)}function a(e,t){I(e,t,!0)}var R={name:"handler-playable-sdk",version:"0.1.0",description:"Handler Playable SDK v0.1 with contract-aligned surface (root sandbox, canonical event envelope).",main:"dist/index.js",module:"dist/index.mjs",types:"dist/index.d.ts",exports:{".":{types:"./dist/index.d.ts",import:"./dist/index.mjs",require:"./dist/index.js"},"./pixi":{types:"./dist/pixi/index.d.ts",import:"./dist/pixi/index.mjs",require:"./dist/pixi/index.js"}},scripts:{build:"tsup src/index.ts src/pixi/index.ts --format cjs,esm --dts --clean --minify",lint:"eslint 'src/**/*.{ts,tsx}'",typecheck:"tsc --noEmit",prepublishOnly:"npm run build"},author:"Handler",license:"MIT",publishConfig:{access:"public"},repository:{type:"git",url:"https://github.com/HandlerAIGames/handler-playable-sdk.git"},files:["dist","LICENSE","README.md"],peerDependencies:{"pixi.js":"^8.0.0"},peerDependenciesMeta:{"pixi.js":{optional:!0}},devDependencies:{eslint:"^8.57.1","pixi.js":"^8.8.1","ts-node":"^10.9.2",tsup:"^8.4.0",typescript:"^5.7.2"}};var s=0,Se=s++,Z=s++,ee=s++,te=s++,ne=s++,ie=s++,oe=s++,re=s++,ae=s++,se=s++,de=s++,le=s++,r=Se;function ce(){return r===Z}function ue(){return r===ee}function fe(){return r===te}function pe(){return r===ne}function g(){return r===ie}function y(){return r===oe}function me(){return r===re}function ge(){return r===ae}function ye(){return r===se}function H(){return r===de}function F(){return r===le}function we(){let e=typeof AD_PROTOCOL!="undefined"?AD_PROTOCOL:"none",t=typeof AD_NETWORK!="undefined"?AD_NETWORK:"web_embed";if(e==="mraid")try{mraid.getState(),r=Z;return}catch{}else if(e==="dapi")try{dapi.isReady(),r=ee;return}catch{}if(t==="facebook")try{typeof FbPlayableAd!="undefined"&&(r=te)}catch{}else if(t==="google")try{typeof ExitApi!="undefined"&&(r=ne)}catch{}else if(t==="mintegral")window.gameReady&&(r=ie);else if(t==="tapjoy")window.TJ_API&&(r=oe);else if(t==="tiktok")window.openAppStore&&(r=re);else if(t==="smadex")try{window.smxTracking&&(r=ae)}catch{}else if(t==="snapchat")try{window.ScPlayableAd&&(r=se)}catch{}else t==="vungle"?r=de:(e==="nucleo"||t==="nucleo")&&(r=le)}var Ee={mechanic_id:"TODO_mechanic_id",variant_id:"TODO_variant_id",deployment_id:"TODO_deployment_id",export_id:"TODO_export_id",profile_id:"TODO_profile_id",instance_id:"default"},Ce=Math.random().toString(36).slice(2),w=null,A={...Ee},Ae="web_embed",De={},G=!1,E=!1,T=!1,ke=!1,B=1,W=0,z=!1,d=!1,j="",b=Math.floor(window.innerWidth),h=Math.floor(window.innerHeight),K=b>h,l=!1,k=!1,be=!1,he=!1,U=!1,M=null;function _e(){if(w)return w;let e=document.createElement("div");return e.id="handler-root",e.setAttribute("data-handler-root","true"),document.body.appendChild(e),w=e,e}function N(e){switch(e){case"interaction":return"engagement";case"finish":return"complete";case"install":return"cta_click";default:return e}}function Te(e,t){return{event_name:e,ts:Date.now(),session_id:Ce,deployment_id:A.deployment_id,variant_id:A.variant_id,export_profile_id:A.profile_id,instance_id:A.instance_id||"default",env:Ae==="mraid"?"mraid":"web",payload:t}}function o(e,t){let n=N(e),i=Te(n,t);P(n,i),n!==e&&P(e,i)}function O(){M&&(M(b,h),M=null)}function _(e){B=e,o("volume",e)}function v(e){e&&(ke=!0),!T&&(T=!0,o("pause"),_(0))}function S(e){!e&&ke||T&&(T=!1,o("resume"),_(B))}function p(e,t){b=Math.floor(e||window.innerWidth),h=Math.floor(t||window.innerHeight),K=b>h,o("resize",{width:b,height:h})}function Le(){if(ce())try{let e=mraid.getMaxSize();p(e.width,e.height);let t=()=>{mraid.isViewable()&&mraid.getState()!=="hidden"?S():v()};if(mraid.addEventListener("viewableChange",t),mraid.addEventListener("stateChange",t),mraid.addEventListener("sizeChange",()=>{let n=mraid.getMaxSize();p(n.width,n.height)}),mraid.getAudioVolume){let n=mraid.getAudioVolume();_(n?1:0)}if(mraid.addEventListener("audioVolumeChange",n=>{n!==null&&_(n>0?1:0)}),mraid.addEventListener("error",(n,i)=>{console.warn("mraid error:",n,"action:",i)}),z=!0,mraid.isViewable()&&mraid.getState()!=="hidden")l=!0,o("boot"),o("view"),o("ready"),d=!0,O();else{let n=()=>{l=!0,o("boot"),o("view"),o("ready"),d=!0,O()};mraid.addEventListener("ready",n)}}catch(e){console.warn("MRAID hook skipped",e)}}function Ie(){if(ue())try{let e=dapi.getScreenSize();p(e.width,e.height),dapi.addEventListener("viewableChange",n=>{n.isViewable?S():v()}),dapi.addEventListener("adResized",n=>{let i=dapi.getScreenSize();p(n.width||i.width,n.height||i.height)});let t=dapi.getAudioVolume();if(_(t?1:0),dapi.addEventListener("audioVolumeChange",n=>_(n?1:0)),z=!0,dapi.isViewable())l=!0,o("boot"),o("view"),o("ready"),d=!0,O();else{let n=()=>{l=!0,o("boot"),o("view"),o("ready"),d=!0,O()};dapi.addEventListener("ready",n)}}catch(e){console.warn("DAPI hook skipped",e)}}function ve(){let e=()=>{l||document.visibilityState==="visible"&&(document.readyState==="complete"||document.readyState==="interactive")&&(l=!0,o("boot"),o("view"),o("ready"),d=!0,O(),k&&(k=!1,f.start()))};window.addEventListener("resize",()=>p()),document.addEventListener("visibilitychange",()=>{document.visibilityState==="visible"?(S(),e()):v()}),document.readyState==="complete"||document.readyState==="interactive"?e():window.addEventListener("load",e),z=!0}function Pe(){let e=t=>{typeof TouchEvent!="undefined"&&t instanceof TouchEvent&&(be=!0),!(be&&t instanceof MouseEvent)&&(W+=1,o("interaction",W))};document.addEventListener("mousedown",e),document.addEventListener("touchstart",e)}function Re(e){var i,c,x,C,J,D,q,L,$,X,Y,Q;let t=typeof AD_PROTOCOL!="undefined"?AD_PROTOCOL:"none";if((typeof AD_NETWORK!="undefined"?AD_NETWORK:"web_embed")==="google")try{(i=window.ExitApi)==null||i.exit();return}catch{}if(t==="mraid"&&typeof mraid!="undefined")mraid.open(e||"");else if(t==="dapi"&&typeof dapi!="undefined")dapi.openStoreUrl();else if(y())(x=(c=window.TJ_API)==null?void 0:c.click)==null||x.call(c);else if(fe())(J=(C=window.FbPlayableAd)==null?void 0:C.onCTAClick)==null||J.call(C);else if(ye())(q=(D=window.ScPlayableAd)==null?void 0:D.onCTAClick)==null||q.call(D);else if(ge())try{($=(L=window.smxTracking)==null?void 0:L.redirect)==null||$.call(L)}catch(m){console.warn("Smadex redirect failed",m)}else if(pe()){let m=window.ExitApi;m&&typeof m.exit=="function"?m.exit(e||j||""):e&&window.open(e)}else g()?(X=window.install)==null||X.call(window):me()?(Y=window.openAppStore)==null||Y.call(window):H()?(Q=parent==null?void 0:parent.postMessage)==null||Q.call(parent,"download","*"):e&&window.open(e)}function Me(){let e=typeof AD_NETWORK!="undefined"?AD_NETWORK:"web_embed",t=n=>{if(!n)return;let i=new Image;i.src=n};if(e==="bigabid"){let n=window.BIGABID_BIDTIMEMACROS;if(!n)return;a("view",()=>t(n.mraid_viewable)),a("start",()=>t(n.game_viewable)),a("engagement",()=>t(n.engagement));let i=()=>t(n.complete);a("complete",i),I("engagement",c=>{var x;((x=c==null?void 0:c.payload)==null?void 0:x.count)>3&&i()}),a("cta_click",()=>t(n.click))}else if(e==="inmobi"){let n=window.INMOBI_DSPMACROS;if(!n)return;a("view",()=>t(n.Ad_Load_Start)),a("start",()=>t(n.Ad_Viewable)),a("engagement",()=>t(n.First_Engagement)),a("complete",()=>t(n.Gameplay_Complete)),a("cta_click",()=>t(n.DSP_Click)),a("start",()=>{[5,10,15,20,25,30].forEach(i=>setTimeout(()=>t(n[`Spent_${i}_Seconds`]),i*1e3))})}}function Ne(){if(!y())return;let e=window.TJ_API;e&&e.setPlayableAPI&&e.setPlayableAPI({skipAd:()=>{try{f.finish()}catch(t){console.warn("Tapjoy skip failed",t)}}})}function xe(){var t,n,i;let e=window.TJ_API;(t=e==null?void 0:e.objectiveComplete)==null||t.call(e),(n=e==null?void 0:e.playableFinished)==null||n.call(e),(i=e==null?void 0:e.gameplayFinished)==null||i.call(e)}function je(){g()&&(window.mintGameStart=()=>{S(!0),p()},window.mintGameClose=()=>{v(!0)})}function ze(){if(!F())return;let e=window.NUC;!e||!e.trigger||(f.on("cta_click",()=>{var t,n;return(n=(t=e.trigger).convert)==null?void 0:n.call(t,j)}),f.on("complete",()=>{var t,n;return(n=(t=e.trigger).tryAgain)==null?void 0:n.call(t)}))}var f={init(e={},t){if(Ae=e.profile||"web_embed",De=e.consent||{},A={...Ee,...e.ids||{}},w=e.rootEl||w,j=e.destinationUrl||(/android/i.test(navigator.userAgent)?"https://play.google.com/store":"https://www.apple.com/app-store/"),t&&(M=t),o("init"),document.body.oncontextmenu=()=>!1,_e(),Ve(w),we(),Le(),Ie(),!z){if(document.readyState==="complete")ve();else if(!he){he=!0;let n=()=>{ve(),window.removeEventListener("load",n),document.removeEventListener("DOMContentLoaded",n)};window.addEventListener("load",n),document.addEventListener("DOMContentLoaded",n)}}Pe(),Me(),Ne(),je(),ze(),console.log(`%c @handler/playable-sdk %c v${R.version||"0.0.0"} `,"background: #007acc; color: #fff; font-size: 14px; padding: 4px 8px; border-top-left-radius: 4px; border-bottom-left-radius: 4px;","background: #e1e4e8; color: #333; font-size: 14px; padding: 4px 8px; border-top-right-radius: 4px; border-bottom-right-radius: 4px;"),l&&!d&&(o("boot"),o("view"),o("ready"),k&&(k=!1,f.start()),d=!0),d=l},getRoot(){return _e()},get version(){return R.version||"0.0.0"},get maxWidth(){return b},get maxHeight(){return h},get isLandscape(){return K},get isReady(){return d},get isStarted(){return G},get isPaused(){return T},get isFinished(){return E},get volume(){return B},get interactions(){return W},on(e,t){I(N(e),t)},off(e,t){V(N(e),t)},start(){var e,t;if(!G){if(!l){k=!0;return}if(G=!0,o("start"),p(),g())v(),(e=window.gameReady)==null||e.call(window);else if(y()){let n=window.TJ_API;(t=n==null?void 0:n.setPlayableBuild)==null||t.call(n,{orientation:K?"landscape":"portrait",buildID:R.version||"dev"})}}},finish(){var e,t;E||(E=!0,o("complete"),g()?(e=window.gameEnd)==null||e.call(window):H()?(t=parent==null?void 0:parent.postMessage)==null||t.call(parent,"complete","*"):y()&&xe())},install(e){if(!E){E=!0,y()?(xe(),setTimeout(()=>f.install(e),300)):(o("complete"),setTimeout(()=>f.install(e),0));return}U||(U=!0,setTimeout(()=>U=!1,500),o("cta_click"),o("conversion"),Re(e||j))},emit(e,t){let n=N(e);if(!["view","boot","start","engagement","complete","cta_click","conversion","retry","pause","resume","resize","volume","error"].includes(n)&&!n.startsWith("custom."))throw new Error(`Event ${e} must be canonical or namespaced as custom.<mechanic_id>.<event>`);let i=Te(n,t);P(n,i)},retry(){var e,t,n;if(g())(e=window.gameRetry)==null||e.call(window);else if(F()){let i=window.NUC;(n=(t=i==null?void 0:i.trigger)==null?void 0:t.tryAgain)==null||n.call(t)}o("engagement",{action:"retry"})},pause(){v(!0)},resume(){S(!0)},resize(e,t){p(e,t)}},Be=f;function Ve(e){let t=document.createElement("script");t.type="text/javascript",t.textContent=`
2
+ (function(){
3
+ var events = ['touchstart','touchend','mousedown','keydown'];
4
+ function unlock(){
5
+ if(window.AudioContext && AudioContext.prototype.resume){
6
+ if(!window.__handler_audio_ctx){
7
+ window.__handler_audio_ctx = new AudioContext();
8
+ }
9
+ if(window.__handler_audio_ctx.state === 'suspended'){
10
+ window.__handler_audio_ctx.resume();
11
+ }
12
+ }
13
+ events.forEach(function(e){ document.removeEventListener(e, unlock); });
14
+ }
15
+ events.forEach(function(e){ document.addEventListener(e, unlock, false); });
16
+ })();
17
+ `,e.appendChild(t)}export{f as a,Be as b};
@@ -0,0 +1,51 @@
1
+ declare global {
2
+ const mraid: any;
3
+ const dapi: any;
4
+ const AD_PROTOCOL: string;
5
+ const AD_NETWORK: string;
6
+ }
7
+ type IDs = {
8
+ mechanic_id: string;
9
+ variant_id: string;
10
+ deployment_id: string;
11
+ export_id: string;
12
+ profile_id: string;
13
+ instance_id: string;
14
+ };
15
+ type Consent = {
16
+ analytics?: boolean;
17
+ personalization?: boolean;
18
+ };
19
+ type InitOptions = {
20
+ rootEl?: HTMLElement;
21
+ ids?: Partial<IDs>;
22
+ profile?: 'web_embed' | 'mraid' | string;
23
+ consent?: Consent;
24
+ destinationUrl?: string;
25
+ };
26
+ declare const Handler: {
27
+ init(options?: InitOptions, callback?: (w: number, h: number) => void): void;
28
+ getRoot(): HTMLElement;
29
+ readonly version: any;
30
+ readonly maxWidth: number;
31
+ readonly maxHeight: number;
32
+ readonly isLandscape: boolean;
33
+ readonly isReady: boolean;
34
+ readonly isStarted: boolean;
35
+ readonly isPaused: boolean;
36
+ readonly isFinished: boolean;
37
+ readonly volume: number;
38
+ readonly interactions: number;
39
+ on(event: string, fn: (...args: any[]) => void): void;
40
+ off(event: string, fn?: (...args: any[]) => void): void;
41
+ start(): void;
42
+ finish(): void;
43
+ install(url?: string): void;
44
+ emit(name: string, payload?: any): void;
45
+ retry(): void;
46
+ pause(): void;
47
+ resume(): void;
48
+ resize(width?: number, height?: number): void;
49
+ };
50
+
51
+ export { Handler, Handler as default };
@@ -0,0 +1,51 @@
1
+ declare global {
2
+ const mraid: any;
3
+ const dapi: any;
4
+ const AD_PROTOCOL: string;
5
+ const AD_NETWORK: string;
6
+ }
7
+ type IDs = {
8
+ mechanic_id: string;
9
+ variant_id: string;
10
+ deployment_id: string;
11
+ export_id: string;
12
+ profile_id: string;
13
+ instance_id: string;
14
+ };
15
+ type Consent = {
16
+ analytics?: boolean;
17
+ personalization?: boolean;
18
+ };
19
+ type InitOptions = {
20
+ rootEl?: HTMLElement;
21
+ ids?: Partial<IDs>;
22
+ profile?: 'web_embed' | 'mraid' | string;
23
+ consent?: Consent;
24
+ destinationUrl?: string;
25
+ };
26
+ declare const Handler: {
27
+ init(options?: InitOptions, callback?: (w: number, h: number) => void): void;
28
+ getRoot(): HTMLElement;
29
+ readonly version: any;
30
+ readonly maxWidth: number;
31
+ readonly maxHeight: number;
32
+ readonly isLandscape: boolean;
33
+ readonly isReady: boolean;
34
+ readonly isStarted: boolean;
35
+ readonly isPaused: boolean;
36
+ readonly isFinished: boolean;
37
+ readonly volume: number;
38
+ readonly interactions: number;
39
+ on(event: string, fn: (...args: any[]) => void): void;
40
+ off(event: string, fn?: (...args: any[]) => void): void;
41
+ start(): void;
42
+ finish(): void;
43
+ install(url?: string): void;
44
+ emit(name: string, payload?: any): void;
45
+ retry(): void;
46
+ pause(): void;
47
+ resume(): void;
48
+ resize(width?: number, height?: number): void;
49
+ };
50
+
51
+ export { Handler, Handler as default };
package/dist/index.js ADDED
@@ -0,0 +1,17 @@
1
+ "use strict";var V=Object.defineProperty;var Se=Object.getOwnPropertyDescriptor;var Ce=Object.getOwnPropertyNames;var De=Object.prototype.hasOwnProperty;var Le=(e,t)=>{for(var n in t)V(e,n,{get:t[n],enumerable:!0})},Ie=(e,t,n,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of Ce(t))!De.call(e,a)&&a!==n&&V(e,a,{get:()=>t[a],enumerable:!(i=Se(t,a))||i.enumerable});return e};var Pe=e=>Ie(V({},"__esModule",{value:!0}),e);var qe={};Le(qe,{Handler:()=>u,default:()=>Be});module.exports=Pe(qe);var f={};function I(e,t,n=!1){f[e]||(f[e]=[]),f[e].push({fn:t,once:n})}function H(e,t){if(f[e]){if(!t){delete f[e];return}f[e]=f[e].filter(n=>n.fn!==t)}}function P(e,...t){let n=f[e];if(n)for(let i of[...n])i.fn(...t),i.once&&H(e,i.fn)}function s(e,t){I(e,t,!0)}var R={name:"handler-playable-sdk",version:"0.1.0",description:"Handler Playable SDK v0.1 with contract-aligned surface (root sandbox, canonical event envelope).",main:"dist/index.js",module:"dist/index.mjs",types:"dist/index.d.ts",exports:{".":{types:"./dist/index.d.ts",import:"./dist/index.mjs",require:"./dist/index.js"},"./pixi":{types:"./dist/pixi/index.d.ts",import:"./dist/pixi/index.mjs",require:"./dist/pixi/index.js"}},scripts:{build:"tsup src/index.ts src/pixi/index.ts --format cjs,esm --dts --clean --minify",lint:"eslint 'src/**/*.{ts,tsx}'",typecheck:"tsc --noEmit",prepublishOnly:"npm run build"},author:"Handler",license:"MIT",publishConfig:{access:"public"},repository:{type:"git",url:"https://github.com/HandlerAIGames/handler-playable-sdk.git"},files:["dist","LICENSE","README.md"],peerDependencies:{"pixi.js":"^8.0.0"},peerDependenciesMeta:{"pixi.js":{optional:!0}},devDependencies:{eslint:"^8.57.1","pixi.js":"^8.8.1","ts-node":"^10.9.2",tsup:"^8.4.0",typescript:"^5.7.2"}};var d=0,Me=d++,ee=d++,te=d++,ne=d++,ie=d++,oe=d++,re=d++,ae=d++,se=d++,de=d++,le=d++,ce=d++,r=Me;function ue(){return r===ee}function fe(){return r===te}function pe(){return r===ne}function me(){return r===ie}function g(){return r===oe}function y(){return r===re}function ge(){return r===ae}function ye(){return r===se}function we(){return r===de}function F(){return r===le}function G(){return r===ce}function be(){let e=typeof AD_PROTOCOL!="undefined"?AD_PROTOCOL:"none",t=typeof AD_NETWORK!="undefined"?AD_NETWORK:"web_embed";if(e==="mraid")try{mraid.getState(),r=ee;return}catch{}else if(e==="dapi")try{dapi.isReady(),r=te;return}catch{}if(t==="facebook")try{typeof FbPlayableAd!="undefined"&&(r=ne)}catch{}else if(t==="google")try{typeof ExitApi!="undefined"&&(r=ie)}catch{}else if(t==="mintegral")window.gameReady&&(r=oe);else if(t==="tapjoy")window.TJ_API&&(r=re);else if(t==="tiktok")window.openAppStore&&(r=ae);else if(t==="smadex")try{window.smxTracking&&(r=se)}catch{}else if(t==="snapchat")try{window.ScPlayableAd&&(r=de)}catch{}else t==="vungle"?r=le:(e==="nucleo"||t==="nucleo")&&(r=ce)}var Ae={mechanic_id:"TODO_mechanic_id",variant_id:"TODO_variant_id",deployment_id:"TODO_deployment_id",export_id:"TODO_export_id",profile_id:"TODO_profile_id",instance_id:"default"},Ne=Math.random().toString(36).slice(2),w=null,A={...Ae},ke="web_embed",je={},U=!1,E=!1,T=!1,Te=!1,J=1,K=0,z=!1,l=!1,j="",b=Math.floor(window.innerWidth),h=Math.floor(window.innerHeight),B=b>h,c=!1,k=!1,he=!1,_e=!1,W=!1,M=null;function ve(){if(w)return w;let e=document.createElement("div");return e.id="handler-root",e.setAttribute("data-handler-root","true"),document.body.appendChild(e),w=e,e}function N(e){switch(e){case"interaction":return"engagement";case"finish":return"complete";case"install":return"cta_click";default:return e}}function Oe(e,t){return{event_name:e,ts:Date.now(),session_id:Ne,deployment_id:A.deployment_id,variant_id:A.variant_id,export_profile_id:A.profile_id,instance_id:A.instance_id||"default",env:ke==="mraid"?"mraid":"web",payload:t}}function o(e,t){let n=N(e),i=Oe(n,t);P(n,i),n!==e&&P(e,i)}function O(){M&&(M(b,h),M=null)}function _(e){J=e,o("volume",e)}function v(e){e&&(Te=!0),!T&&(T=!0,o("pause"),_(0))}function S(e){!e&&Te||T&&(T=!1,o("resume"),_(J))}function p(e,t){b=Math.floor(e||window.innerWidth),h=Math.floor(t||window.innerHeight),B=b>h,o("resize",{width:b,height:h})}function ze(){if(ue())try{let e=mraid.getMaxSize();p(e.width,e.height);let t=()=>{mraid.isViewable()&&mraid.getState()!=="hidden"?S():v()};if(mraid.addEventListener("viewableChange",t),mraid.addEventListener("stateChange",t),mraid.addEventListener("sizeChange",()=>{let n=mraid.getMaxSize();p(n.width,n.height)}),mraid.getAudioVolume){let n=mraid.getAudioVolume();_(n?1:0)}if(mraid.addEventListener("audioVolumeChange",n=>{n!==null&&_(n>0?1:0)}),mraid.addEventListener("error",(n,i)=>{console.warn("mraid error:",n,"action:",i)}),z=!0,mraid.isViewable()&&mraid.getState()!=="hidden")c=!0,o("boot"),o("view"),o("ready"),l=!0,O();else{let n=()=>{c=!0,o("boot"),o("view"),o("ready"),l=!0,O()};mraid.addEventListener("ready",n)}}catch(e){console.warn("MRAID hook skipped",e)}}function Ve(){if(fe())try{let e=dapi.getScreenSize();p(e.width,e.height),dapi.addEventListener("viewableChange",n=>{n.isViewable?S():v()}),dapi.addEventListener("adResized",n=>{let i=dapi.getScreenSize();p(n.width||i.width,n.height||i.height)});let t=dapi.getAudioVolume();if(_(t?1:0),dapi.addEventListener("audioVolumeChange",n=>_(n?1:0)),z=!0,dapi.isViewable())c=!0,o("boot"),o("view"),o("ready"),l=!0,O();else{let n=()=>{c=!0,o("boot"),o("view"),o("ready"),l=!0,O()};dapi.addEventListener("ready",n)}}catch(e){console.warn("DAPI hook skipped",e)}}function xe(){let e=()=>{c||document.visibilityState==="visible"&&(document.readyState==="complete"||document.readyState==="interactive")&&(c=!0,o("boot"),o("view"),o("ready"),l=!0,O(),k&&(k=!1,u.start()))};window.addEventListener("resize",()=>p()),document.addEventListener("visibilitychange",()=>{document.visibilityState==="visible"?(S(),e()):v()}),document.readyState==="complete"||document.readyState==="interactive"?e():window.addEventListener("load",e),z=!0}function He(){let e=t=>{typeof TouchEvent!="undefined"&&t instanceof TouchEvent&&(he=!0),!(he&&t instanceof MouseEvent)&&(K+=1,o("interaction",K))};document.addEventListener("mousedown",e),document.addEventListener("touchstart",e)}function Fe(e){var i,a,x,C,q,D,$,L,X,Y,Q,Z;let t=typeof AD_PROTOCOL!="undefined"?AD_PROTOCOL:"none";if((typeof AD_NETWORK!="undefined"?AD_NETWORK:"web_embed")==="google")try{(i=window.ExitApi)==null||i.exit();return}catch{}if(t==="mraid"&&typeof mraid!="undefined")mraid.open(e||"");else if(t==="dapi"&&typeof dapi!="undefined")dapi.openStoreUrl();else if(y())(x=(a=window.TJ_API)==null?void 0:a.click)==null||x.call(a);else if(pe())(q=(C=window.FbPlayableAd)==null?void 0:C.onCTAClick)==null||q.call(C);else if(we())($=(D=window.ScPlayableAd)==null?void 0:D.onCTAClick)==null||$.call(D);else if(ye())try{(X=(L=window.smxTracking)==null?void 0:L.redirect)==null||X.call(L)}catch(m){console.warn("Smadex redirect failed",m)}else if(me()){let m=window.ExitApi;m&&typeof m.exit=="function"?m.exit(e||j||""):e&&window.open(e)}else g()?(Y=window.install)==null||Y.call(window):ge()?(Q=window.openAppStore)==null||Q.call(window):F()?(Z=parent==null?void 0:parent.postMessage)==null||Z.call(parent,"download","*"):e&&window.open(e)}function Ge(){let e=typeof AD_NETWORK!="undefined"?AD_NETWORK:"web_embed",t=n=>{if(!n)return;let i=new Image;i.src=n};if(e==="bigabid"){let n=window.BIGABID_BIDTIMEMACROS;if(!n)return;s("view",()=>t(n.mraid_viewable)),s("start",()=>t(n.game_viewable)),s("engagement",()=>t(n.engagement));let i=()=>t(n.complete);s("complete",i),I("engagement",a=>{var x;((x=a==null?void 0:a.payload)==null?void 0:x.count)>3&&i()}),s("cta_click",()=>t(n.click))}else if(e==="inmobi"){let n=window.INMOBI_DSPMACROS;if(!n)return;s("view",()=>t(n.Ad_Load_Start)),s("start",()=>t(n.Ad_Viewable)),s("engagement",()=>t(n.First_Engagement)),s("complete",()=>t(n.Gameplay_Complete)),s("cta_click",()=>t(n.DSP_Click)),s("start",()=>{[5,10,15,20,25,30].forEach(i=>setTimeout(()=>t(n[`Spent_${i}_Seconds`]),i*1e3))})}}function Ue(){if(!y())return;let e=window.TJ_API;e&&e.setPlayableAPI&&e.setPlayableAPI({skipAd:()=>{try{u.finish()}catch(t){console.warn("Tapjoy skip failed",t)}}})}function Ee(){var t,n,i;let e=window.TJ_API;(t=e==null?void 0:e.objectiveComplete)==null||t.call(e),(n=e==null?void 0:e.playableFinished)==null||n.call(e),(i=e==null?void 0:e.gameplayFinished)==null||i.call(e)}function We(){g()&&(window.mintGameStart=()=>{S(!0),p()},window.mintGameClose=()=>{v(!0)})}function Ke(){if(!G())return;let e=window.NUC;!e||!e.trigger||(u.on("cta_click",()=>{var t,n;return(n=(t=e.trigger).convert)==null?void 0:n.call(t,j)}),u.on("complete",()=>{var t,n;return(n=(t=e.trigger).tryAgain)==null?void 0:n.call(t)}))}var u={init(e={},t){if(ke=e.profile||"web_embed",je=e.consent||{},A={...Ae,...e.ids||{}},w=e.rootEl||w,j=e.destinationUrl||(/android/i.test(navigator.userAgent)?"https://play.google.com/store":"https://www.apple.com/app-store/"),t&&(M=t),o("init"),document.body.oncontextmenu=()=>!1,ve(),Je(w),be(),ze(),Ve(),!z){if(document.readyState==="complete")xe();else if(!_e){_e=!0;let n=()=>{xe(),window.removeEventListener("load",n),document.removeEventListener("DOMContentLoaded",n)};window.addEventListener("load",n),document.addEventListener("DOMContentLoaded",n)}}He(),Ge(),Ue(),We(),Ke(),console.log(`%c @handler/playable-sdk %c v${R.version||"0.0.0"} `,"background: #007acc; color: #fff; font-size: 14px; padding: 4px 8px; border-top-left-radius: 4px; border-bottom-left-radius: 4px;","background: #e1e4e8; color: #333; font-size: 14px; padding: 4px 8px; border-top-right-radius: 4px; border-bottom-right-radius: 4px;"),c&&!l&&(o("boot"),o("view"),o("ready"),k&&(k=!1,u.start()),l=!0),l=c},getRoot(){return ve()},get version(){return R.version||"0.0.0"},get maxWidth(){return b},get maxHeight(){return h},get isLandscape(){return B},get isReady(){return l},get isStarted(){return U},get isPaused(){return T},get isFinished(){return E},get volume(){return J},get interactions(){return K},on(e,t){I(N(e),t)},off(e,t){H(N(e),t)},start(){var e,t;if(!U){if(!c){k=!0;return}if(U=!0,o("start"),p(),g())v(),(e=window.gameReady)==null||e.call(window);else if(y()){let n=window.TJ_API;(t=n==null?void 0:n.setPlayableBuild)==null||t.call(n,{orientation:B?"landscape":"portrait",buildID:R.version||"dev"})}}},finish(){var e,t;E||(E=!0,o("complete"),g()?(e=window.gameEnd)==null||e.call(window):F()?(t=parent==null?void 0:parent.postMessage)==null||t.call(parent,"complete","*"):y()&&Ee())},install(e){if(!E){E=!0,y()?(Ee(),setTimeout(()=>u.install(e),300)):(o("complete"),setTimeout(()=>u.install(e),0));return}W||(W=!0,setTimeout(()=>W=!1,500),o("cta_click"),o("conversion"),Fe(e||j))},emit(e,t){let n=N(e);if(!["view","boot","start","engagement","complete","cta_click","conversion","retry","pause","resume","resize","volume","error"].includes(n)&&!n.startsWith("custom."))throw new Error(`Event ${e} must be canonical or namespaced as custom.<mechanic_id>.<event>`);let i=Oe(n,t);P(n,i)},retry(){var e,t,n;if(g())(e=window.gameRetry)==null||e.call(window);else if(G()){let i=window.NUC;(n=(t=i==null?void 0:i.trigger)==null?void 0:t.tryAgain)==null||n.call(t)}o("engagement",{action:"retry"})},pause(){v(!0)},resume(){S(!0)},resize(e,t){p(e,t)}},Be=u;function Je(e){let t=document.createElement("script");t.type="text/javascript",t.textContent=`
2
+ (function(){
3
+ var events = ['touchstart','touchend','mousedown','keydown'];
4
+ function unlock(){
5
+ if(window.AudioContext && AudioContext.prototype.resume){
6
+ if(!window.__handler_audio_ctx){
7
+ window.__handler_audio_ctx = new AudioContext();
8
+ }
9
+ if(window.__handler_audio_ctx.state === 'suspended'){
10
+ window.__handler_audio_ctx.resume();
11
+ }
12
+ }
13
+ events.forEach(function(e){ document.removeEventListener(e, unlock); });
14
+ }
15
+ events.forEach(function(e){ document.addEventListener(e, unlock, false); });
16
+ })();
17
+ `,e.appendChild(t)}0&&(module.exports={Handler});
package/dist/index.mjs ADDED
@@ -0,0 +1 @@
1
+ import{a,b}from"./chunk-CJIYY5BP.mjs";export{a as Handler,b as default};
@@ -0,0 +1,454 @@
1
+ import { Application, Container, Texture, Sprite, Text, Graphics, Point } from 'pixi.js';
2
+
3
+ /**
4
+ * Handler SDK - PixiJS Base Context
5
+ *
6
+ * Creates the base PixiJS application with Handler SDK lifecycle integration.
7
+ * Handles resize, pause/resume, and provides the overlay for UI elements.
8
+ *
9
+ * DO NOT EDIT - Handler SDK Core
10
+ */
11
+
12
+ /**
13
+ * Theme configuration for PixiJS base
14
+ */
15
+ type PixiTheme = {
16
+ background?: string;
17
+ text?: string;
18
+ text_muted?: string;
19
+ primary?: string;
20
+ cta_text?: string;
21
+ surface_shadow?: string;
22
+ };
23
+ /**
24
+ * Context returned by createPixiBase
25
+ */
26
+ type PixiBaseContext = {
27
+ app: Application;
28
+ stage: Application['stage'];
29
+ overlay: HTMLElement;
30
+ applySize: (size?: {
31
+ width?: number;
32
+ height?: number;
33
+ }) => void;
34
+ };
35
+ /**
36
+ * Create the base PixiJS context with Handler SDK integration
37
+ *
38
+ * @param root - The root HTML element to render into (from Handler.getRoot())
39
+ * @param theme - Optional theme configuration
40
+ * @returns PixiJS application context with overlay
41
+ *
42
+ * @example
43
+ * ```typescript
44
+ * const root = Handler.getRoot();
45
+ * const { app, stage, overlay } = await createPixiBase(root, {
46
+ * background: '#160a17',
47
+ * text: '#fef4dd',
48
+ * primary: '#ffb43b'
49
+ * });
50
+ * ```
51
+ */
52
+ declare function createPixiBase(root: HTMLElement, theme?: PixiTheme): Promise<PixiBaseContext>;
53
+
54
+ /**
55
+ * Handler SDK - GameObject System
56
+ *
57
+ * Unity-style component-based architecture for PixiJS objects.
58
+ * Automatically syncs config values to PixiJS objects and enables hot-reload.
59
+ *
60
+ * DO NOT EDIT - Handler SDK Core
61
+ */
62
+
63
+ /**
64
+ * Transform Component - Position, scale, rotation
65
+ */
66
+ declare class Transform {
67
+ position: {
68
+ x: number;
69
+ y: number;
70
+ };
71
+ scale: number;
72
+ rotation: number;
73
+ constructor(config?: {
74
+ position?: {
75
+ x: number;
76
+ y: number;
77
+ };
78
+ scale?: number;
79
+ rotation?: number;
80
+ });
81
+ update(config: {
82
+ position?: {
83
+ x: number;
84
+ y: number;
85
+ };
86
+ scale?: number;
87
+ rotation?: number;
88
+ }): void;
89
+ syncToPixi(pixiObject: Container | any): void;
90
+ }
91
+ /**
92
+ * Renderer Component - Visual properties
93
+ */
94
+ declare class Renderer {
95
+ z_index: number;
96
+ alpha: number;
97
+ visible: boolean;
98
+ tint: number | null;
99
+ constructor(config?: {
100
+ z_index?: number;
101
+ alpha?: number;
102
+ visible?: boolean;
103
+ tint?: number | null;
104
+ });
105
+ update(config: {
106
+ z_index?: number;
107
+ alpha?: number;
108
+ visible?: boolean;
109
+ tint?: number | null;
110
+ }): void;
111
+ syncToPixi(pixiObject: Container | any): void;
112
+ }
113
+ /**
114
+ * Base GameObject class - Unity-style entity with components
115
+ *
116
+ * @example
117
+ * ```typescript
118
+ * const characterGO = gameObjectManager.create('character_1', characterSprite);
119
+ * characterGO.setPosition(100, 200);
120
+ * characterGO.setAlpha(0.8);
121
+ * ```
122
+ */
123
+ declare class GameObject {
124
+ instanceId: string;
125
+ objectConfig: string;
126
+ pixiObject: Container | any;
127
+ transform: Transform;
128
+ renderer: Renderer;
129
+ private _config;
130
+ constructor(instanceId: string, objectConfig: string, pixiObject: Container | any, config: any);
131
+ /**
132
+ * Sync all components to PixiJS object
133
+ */
134
+ sync(): void;
135
+ /**
136
+ * Update config and sync (for hot-reload)
137
+ */
138
+ updateConfig(newConfig: any): void;
139
+ /**
140
+ * Get component value
141
+ */
142
+ getComponent<T>(componentName: string): T | undefined;
143
+ /**
144
+ * Get transform position
145
+ */
146
+ getPosition(): {
147
+ x: number;
148
+ y: number;
149
+ };
150
+ /**
151
+ * Set transform position
152
+ */
153
+ setPosition(x: number, y: number): void;
154
+ /**
155
+ * Get renderer alpha
156
+ */
157
+ getAlpha(): number;
158
+ /**
159
+ * Set renderer alpha
160
+ */
161
+ setAlpha(alpha: number): void;
162
+ /**
163
+ * Get renderer visible
164
+ */
165
+ getVisible(): boolean;
166
+ /**
167
+ * Set renderer visible
168
+ */
169
+ setVisible(visible: boolean): void;
170
+ }
171
+ /**
172
+ * GameObject Manager - Manages all game objects and enables hot-reload
173
+ *
174
+ * @example
175
+ * ```typescript
176
+ * const gameObjectManager = new GameObjectManager(config);
177
+ * const characterGO = gameObjectManager.create('character_1', characterSprite);
178
+ * ```
179
+ */
180
+ declare class GameObjectManager {
181
+ private objects;
182
+ private config;
183
+ constructor(config: any);
184
+ /**
185
+ * Create a GameObject from config
186
+ */
187
+ create(instanceId: string, pixiObject: Container | any): GameObject;
188
+ /**
189
+ * Get GameObject by instance ID
190
+ */
191
+ get(instanceId: string): GameObject | undefined;
192
+ /**
193
+ * Update config and sync all objects (for hot-reload)
194
+ */
195
+ updateConfig(newConfig: any): void;
196
+ /**
197
+ * Get all GameObjects
198
+ */
199
+ getAll(): GameObject[];
200
+ /**
201
+ * Remove GameObject
202
+ */
203
+ remove(instanceId: string): void;
204
+ /**
205
+ * Clear all GameObjects
206
+ */
207
+ clear(): void;
208
+ }
209
+
210
+ /**
211
+ * Handler SDK - Font Registry
212
+ *
213
+ * Maps font IDs to actual font families/paths.
214
+ * UI config references fonts by ID only (e.g., "brand.primary").
215
+ * This registry resolves IDs to actual font values.
216
+ *
217
+ * Font paths and platform differences are handled here, NOT in config.
218
+ *
219
+ * DO NOT EDIT - Handler SDK Core
220
+ */
221
+ type FontDefinition = {
222
+ family: string;
223
+ weight?: string | number;
224
+ style?: string;
225
+ };
226
+ /**
227
+ * Resolve font ID to font family string
228
+ * @param fontId Font ID from UI config (e.g., "brand.primary")
229
+ * @returns CSS font-family string
230
+ */
231
+ declare function resolveFont(fontId: string): string;
232
+ /**
233
+ * Resolve font ID to font weight
234
+ * @param fontId Font ID from UI config
235
+ * @returns Font weight (string or number)
236
+ */
237
+ declare function resolveFontWeight(fontId: string): string | number;
238
+ /**
239
+ * Register a new font (for engine initialization)
240
+ * @param fontId Font ID
241
+ * @param definition Font definition
242
+ */
243
+ declare function registerFont(fontId: string, definition: FontDefinition): void;
244
+ /**
245
+ * Get all registered font IDs
246
+ */
247
+ declare function getRegisteredFontIds(): string[];
248
+
249
+ /**
250
+ * Handler SDK - Type Definitions for PixiJS Integration
251
+ *
252
+ * DO NOT EDIT - Handler SDK Core
253
+ */
254
+ /**
255
+ * Object configuration from JSON files
256
+ */
257
+ interface ObjectConfig {
258
+ object_config?: string;
259
+ transform?: {
260
+ position?: {
261
+ x: number;
262
+ y: number;
263
+ };
264
+ scale?: number;
265
+ rotation?: number;
266
+ offset?: {
267
+ x?: number;
268
+ y?: number;
269
+ };
270
+ };
271
+ render?: {
272
+ z_index?: number;
273
+ alpha?: number;
274
+ visible?: boolean;
275
+ tint?: number | string | null;
276
+ background_color?: string;
277
+ background_alpha?: number;
278
+ border_color?: string;
279
+ };
280
+ gameplay?: {
281
+ rules?: Record<string, unknown>;
282
+ tuning?: Record<string, unknown>;
283
+ };
284
+ ui?: {
285
+ text?: string;
286
+ font?: string;
287
+ fontSize?: number;
288
+ letterSpacing?: number;
289
+ align?: string;
290
+ };
291
+ effects?: Record<string, unknown>;
292
+ audio?: Record<string, unknown>;
293
+ physics?: Record<string, unknown>;
294
+ interaction?: Record<string, unknown>;
295
+ identity?: Record<string, unknown>;
296
+ visibility?: Record<string, unknown>;
297
+ }
298
+ /**
299
+ * Engine configuration
300
+ */
301
+ interface EngineConfig {
302
+ assets?: Record<string, string>;
303
+ runtime?: {
304
+ ui?: Record<string, unknown>;
305
+ theme?: Record<string, unknown>;
306
+ ui_styles?: Record<string, unknown>;
307
+ timeline?: Record<string, number>;
308
+ };
309
+ }
310
+ /**
311
+ * Theme configuration
312
+ */
313
+ interface ThemeConfig {
314
+ background_color?: string;
315
+ text_color?: string;
316
+ square_color?: string;
317
+ cta_background?: string;
318
+ cta_text?: string;
319
+ melt_color?: string;
320
+ danger_color?: string;
321
+ brush_color?: string;
322
+ success_color?: string;
323
+ }
324
+ /**
325
+ * Gameplay configuration
326
+ */
327
+ interface GameplayConfig {
328
+ [key: string]: unknown;
329
+ }
330
+ /**
331
+ * Object-centric configuration
332
+ * This is the main config type that students work with
333
+ */
334
+ interface ObjectCentricConfig {
335
+ /** Map of object instance IDs to their configurations */
336
+ objects: Map<string, ObjectConfig>;
337
+ /** Engine configuration (assets, runtime settings) */
338
+ engine: EngineConfig;
339
+ /** Theme configuration */
340
+ theme: ThemeConfig;
341
+ /** Gameplay configuration */
342
+ gameplay: GameplayConfig;
343
+ }
344
+
345
+ /**
346
+ * Handler SDK - EndGame UI Panel
347
+ *
348
+ * Success screen with logo, text, CTA button, and tutorial hand.
349
+ * Fully configurable via UI config system.
350
+ *
351
+ * DO NOT EDIT - Handler SDK Core
352
+ */
353
+
354
+ interface EndGamePanelElements {
355
+ panel: Container;
356
+ logo: Sprite;
357
+ title: Text;
358
+ subtitle: Text;
359
+ footer: Text;
360
+ ctaButton: Graphics;
361
+ ctaText: Text;
362
+ ctaHint: Text;
363
+ hand: Sprite;
364
+ }
365
+ /**
366
+ * Create EndGame success panel with all UI elements
367
+ */
368
+ declare function createEndGamePanel(config: ObjectCentricConfig, handTexture: Texture | null, logoTexture: Texture | null): EndGamePanelElements;
369
+ /**
370
+ * Animate panel entrance with staggered animations
371
+ */
372
+ declare function animatePanelEntrance(elements: EndGamePanelElements, config: ObjectCentricConfig, onComplete?: () => void): void;
373
+ /**
374
+ * Animate tutorial hand clicking CTA button
375
+ */
376
+ declare function animateHandClick(hand: Sprite, ctaButton: Graphics, config: ObjectCentricConfig, onClick: () => void): void;
377
+
378
+ /**
379
+ * Handler SDK - Tutorial UI Components
380
+ *
381
+ * Tutorial hand and label components for game tutorials.
382
+ * Config requirement: All UI properties from object-centric config.
383
+ *
384
+ * DO NOT EDIT - Handler SDK Core
385
+ */
386
+
387
+ /**
388
+ * Create hand tutorial sprite
389
+ * Hand properties from object-centric config (hand_tutorial_1)
390
+ *
391
+ * @param handTexture - Texture for the hand sprite
392
+ * @param config - Object-centric configuration
393
+ * @param baseCharacterPos - Base position of the character for animation reference
394
+ */
395
+ declare function createHandTutorial(handTexture: Texture, config: ObjectCentricConfig, baseCharacterPos: Point): Sprite;
396
+ /**
397
+ * Create tutorial label text
398
+ * Uses ui.tutorial config object with UI component schema
399
+ *
400
+ * @param config - Object-centric configuration
401
+ */
402
+ declare function createTutorialLabel(config: ObjectCentricConfig): Text;
403
+ /**
404
+ * Update hand animation
405
+ * Animation parameters from object-centric config (hand_tutorial_1)
406
+ *
407
+ * @param hand - The hand sprite
408
+ * @param baseCharacterPos - Base character position
409
+ * @param time - Current time for animation
410
+ * @param isDragging - Whether the user is currently dragging
411
+ * @param config - Object-centric configuration
412
+ */
413
+ declare function updateHandAnimation(hand: Sprite, baseCharacterPos: Point, time: number, isDragging: boolean, config: ObjectCentricConfig): void;
414
+
415
+ /**
416
+ * Handler SDK - Asset Loader Utilities
417
+ *
418
+ * Asset loading and texture creation utilities for PixiJS.
419
+ * Config requirement: All asset paths from config.assets, colors from config.theme.
420
+ *
421
+ * DO NOT EDIT - Handler SDK Core
422
+ */
423
+
424
+ /**
425
+ * Load an asset with fallback placeholder
426
+ * Asset path comes from config.assets
427
+ *
428
+ * @param path - Asset path
429
+ * @param app - PixiJS application instance
430
+ * @param fallbackColor - Color for fallback placeholder
431
+ * @param isChar - Whether this is a character sprite (affects placeholder shape)
432
+ */
433
+ declare function loadAsset(path: string, app: any, fallbackColor?: number, isChar?: boolean): Promise<Texture>;
434
+ /**
435
+ * Load character frames from sprite sheet
436
+ * Uses config.assets.character_sprite for path
437
+ * Character sheet dimensions come from config (cols: 3, rows: 2)
438
+ *
439
+ * @param path - Path to the sprite sheet
440
+ * @param app - PixiJS application instance
441
+ * @param config - Object-centric configuration
442
+ */
443
+ declare function loadCharacterFrames(path: string, app: any, config: ObjectCentricConfig): Promise<Texture[]>;
444
+ /**
445
+ * Create brush texture
446
+ * Uses config.theme.brush_color for color
447
+ * Brush size comes from config.gameplay.brush
448
+ *
449
+ * @param app - PixiJS application instance
450
+ * @param config - Object-centric configuration
451
+ */
452
+ declare function createBrushTexture(app: any, config: ObjectCentricConfig): Texture;
453
+
454
+ export { type EndGamePanelElements, GameObject, GameObjectManager, type ObjectCentricConfig, type PixiBaseContext, type PixiTheme, Renderer, Transform, animateHandClick, animatePanelEntrance, createBrushTexture, createEndGamePanel, createHandTutorial, createPixiBase, createTutorialLabel, getRegisteredFontIds, loadAsset, loadCharacterFrames, registerFont, resolveFont, resolveFontWeight, updateHandAnimation };
@@ -0,0 +1,454 @@
1
+ import { Application, Container, Texture, Sprite, Text, Graphics, Point } from 'pixi.js';
2
+
3
+ /**
4
+ * Handler SDK - PixiJS Base Context
5
+ *
6
+ * Creates the base PixiJS application with Handler SDK lifecycle integration.
7
+ * Handles resize, pause/resume, and provides the overlay for UI elements.
8
+ *
9
+ * DO NOT EDIT - Handler SDK Core
10
+ */
11
+
12
+ /**
13
+ * Theme configuration for PixiJS base
14
+ */
15
+ type PixiTheme = {
16
+ background?: string;
17
+ text?: string;
18
+ text_muted?: string;
19
+ primary?: string;
20
+ cta_text?: string;
21
+ surface_shadow?: string;
22
+ };
23
+ /**
24
+ * Context returned by createPixiBase
25
+ */
26
+ type PixiBaseContext = {
27
+ app: Application;
28
+ stage: Application['stage'];
29
+ overlay: HTMLElement;
30
+ applySize: (size?: {
31
+ width?: number;
32
+ height?: number;
33
+ }) => void;
34
+ };
35
+ /**
36
+ * Create the base PixiJS context with Handler SDK integration
37
+ *
38
+ * @param root - The root HTML element to render into (from Handler.getRoot())
39
+ * @param theme - Optional theme configuration
40
+ * @returns PixiJS application context with overlay
41
+ *
42
+ * @example
43
+ * ```typescript
44
+ * const root = Handler.getRoot();
45
+ * const { app, stage, overlay } = await createPixiBase(root, {
46
+ * background: '#160a17',
47
+ * text: '#fef4dd',
48
+ * primary: '#ffb43b'
49
+ * });
50
+ * ```
51
+ */
52
+ declare function createPixiBase(root: HTMLElement, theme?: PixiTheme): Promise<PixiBaseContext>;
53
+
54
+ /**
55
+ * Handler SDK - GameObject System
56
+ *
57
+ * Unity-style component-based architecture for PixiJS objects.
58
+ * Automatically syncs config values to PixiJS objects and enables hot-reload.
59
+ *
60
+ * DO NOT EDIT - Handler SDK Core
61
+ */
62
+
63
+ /**
64
+ * Transform Component - Position, scale, rotation
65
+ */
66
+ declare class Transform {
67
+ position: {
68
+ x: number;
69
+ y: number;
70
+ };
71
+ scale: number;
72
+ rotation: number;
73
+ constructor(config?: {
74
+ position?: {
75
+ x: number;
76
+ y: number;
77
+ };
78
+ scale?: number;
79
+ rotation?: number;
80
+ });
81
+ update(config: {
82
+ position?: {
83
+ x: number;
84
+ y: number;
85
+ };
86
+ scale?: number;
87
+ rotation?: number;
88
+ }): void;
89
+ syncToPixi(pixiObject: Container | any): void;
90
+ }
91
+ /**
92
+ * Renderer Component - Visual properties
93
+ */
94
+ declare class Renderer {
95
+ z_index: number;
96
+ alpha: number;
97
+ visible: boolean;
98
+ tint: number | null;
99
+ constructor(config?: {
100
+ z_index?: number;
101
+ alpha?: number;
102
+ visible?: boolean;
103
+ tint?: number | null;
104
+ });
105
+ update(config: {
106
+ z_index?: number;
107
+ alpha?: number;
108
+ visible?: boolean;
109
+ tint?: number | null;
110
+ }): void;
111
+ syncToPixi(pixiObject: Container | any): void;
112
+ }
113
+ /**
114
+ * Base GameObject class - Unity-style entity with components
115
+ *
116
+ * @example
117
+ * ```typescript
118
+ * const characterGO = gameObjectManager.create('character_1', characterSprite);
119
+ * characterGO.setPosition(100, 200);
120
+ * characterGO.setAlpha(0.8);
121
+ * ```
122
+ */
123
+ declare class GameObject {
124
+ instanceId: string;
125
+ objectConfig: string;
126
+ pixiObject: Container | any;
127
+ transform: Transform;
128
+ renderer: Renderer;
129
+ private _config;
130
+ constructor(instanceId: string, objectConfig: string, pixiObject: Container | any, config: any);
131
+ /**
132
+ * Sync all components to PixiJS object
133
+ */
134
+ sync(): void;
135
+ /**
136
+ * Update config and sync (for hot-reload)
137
+ */
138
+ updateConfig(newConfig: any): void;
139
+ /**
140
+ * Get component value
141
+ */
142
+ getComponent<T>(componentName: string): T | undefined;
143
+ /**
144
+ * Get transform position
145
+ */
146
+ getPosition(): {
147
+ x: number;
148
+ y: number;
149
+ };
150
+ /**
151
+ * Set transform position
152
+ */
153
+ setPosition(x: number, y: number): void;
154
+ /**
155
+ * Get renderer alpha
156
+ */
157
+ getAlpha(): number;
158
+ /**
159
+ * Set renderer alpha
160
+ */
161
+ setAlpha(alpha: number): void;
162
+ /**
163
+ * Get renderer visible
164
+ */
165
+ getVisible(): boolean;
166
+ /**
167
+ * Set renderer visible
168
+ */
169
+ setVisible(visible: boolean): void;
170
+ }
171
+ /**
172
+ * GameObject Manager - Manages all game objects and enables hot-reload
173
+ *
174
+ * @example
175
+ * ```typescript
176
+ * const gameObjectManager = new GameObjectManager(config);
177
+ * const characterGO = gameObjectManager.create('character_1', characterSprite);
178
+ * ```
179
+ */
180
+ declare class GameObjectManager {
181
+ private objects;
182
+ private config;
183
+ constructor(config: any);
184
+ /**
185
+ * Create a GameObject from config
186
+ */
187
+ create(instanceId: string, pixiObject: Container | any): GameObject;
188
+ /**
189
+ * Get GameObject by instance ID
190
+ */
191
+ get(instanceId: string): GameObject | undefined;
192
+ /**
193
+ * Update config and sync all objects (for hot-reload)
194
+ */
195
+ updateConfig(newConfig: any): void;
196
+ /**
197
+ * Get all GameObjects
198
+ */
199
+ getAll(): GameObject[];
200
+ /**
201
+ * Remove GameObject
202
+ */
203
+ remove(instanceId: string): void;
204
+ /**
205
+ * Clear all GameObjects
206
+ */
207
+ clear(): void;
208
+ }
209
+
210
+ /**
211
+ * Handler SDK - Font Registry
212
+ *
213
+ * Maps font IDs to actual font families/paths.
214
+ * UI config references fonts by ID only (e.g., "brand.primary").
215
+ * This registry resolves IDs to actual font values.
216
+ *
217
+ * Font paths and platform differences are handled here, NOT in config.
218
+ *
219
+ * DO NOT EDIT - Handler SDK Core
220
+ */
221
+ type FontDefinition = {
222
+ family: string;
223
+ weight?: string | number;
224
+ style?: string;
225
+ };
226
+ /**
227
+ * Resolve font ID to font family string
228
+ * @param fontId Font ID from UI config (e.g., "brand.primary")
229
+ * @returns CSS font-family string
230
+ */
231
+ declare function resolveFont(fontId: string): string;
232
+ /**
233
+ * Resolve font ID to font weight
234
+ * @param fontId Font ID from UI config
235
+ * @returns Font weight (string or number)
236
+ */
237
+ declare function resolveFontWeight(fontId: string): string | number;
238
+ /**
239
+ * Register a new font (for engine initialization)
240
+ * @param fontId Font ID
241
+ * @param definition Font definition
242
+ */
243
+ declare function registerFont(fontId: string, definition: FontDefinition): void;
244
+ /**
245
+ * Get all registered font IDs
246
+ */
247
+ declare function getRegisteredFontIds(): string[];
248
+
249
+ /**
250
+ * Handler SDK - Type Definitions for PixiJS Integration
251
+ *
252
+ * DO NOT EDIT - Handler SDK Core
253
+ */
254
+ /**
255
+ * Object configuration from JSON files
256
+ */
257
+ interface ObjectConfig {
258
+ object_config?: string;
259
+ transform?: {
260
+ position?: {
261
+ x: number;
262
+ y: number;
263
+ };
264
+ scale?: number;
265
+ rotation?: number;
266
+ offset?: {
267
+ x?: number;
268
+ y?: number;
269
+ };
270
+ };
271
+ render?: {
272
+ z_index?: number;
273
+ alpha?: number;
274
+ visible?: boolean;
275
+ tint?: number | string | null;
276
+ background_color?: string;
277
+ background_alpha?: number;
278
+ border_color?: string;
279
+ };
280
+ gameplay?: {
281
+ rules?: Record<string, unknown>;
282
+ tuning?: Record<string, unknown>;
283
+ };
284
+ ui?: {
285
+ text?: string;
286
+ font?: string;
287
+ fontSize?: number;
288
+ letterSpacing?: number;
289
+ align?: string;
290
+ };
291
+ effects?: Record<string, unknown>;
292
+ audio?: Record<string, unknown>;
293
+ physics?: Record<string, unknown>;
294
+ interaction?: Record<string, unknown>;
295
+ identity?: Record<string, unknown>;
296
+ visibility?: Record<string, unknown>;
297
+ }
298
+ /**
299
+ * Engine configuration
300
+ */
301
+ interface EngineConfig {
302
+ assets?: Record<string, string>;
303
+ runtime?: {
304
+ ui?: Record<string, unknown>;
305
+ theme?: Record<string, unknown>;
306
+ ui_styles?: Record<string, unknown>;
307
+ timeline?: Record<string, number>;
308
+ };
309
+ }
310
+ /**
311
+ * Theme configuration
312
+ */
313
+ interface ThemeConfig {
314
+ background_color?: string;
315
+ text_color?: string;
316
+ square_color?: string;
317
+ cta_background?: string;
318
+ cta_text?: string;
319
+ melt_color?: string;
320
+ danger_color?: string;
321
+ brush_color?: string;
322
+ success_color?: string;
323
+ }
324
+ /**
325
+ * Gameplay configuration
326
+ */
327
+ interface GameplayConfig {
328
+ [key: string]: unknown;
329
+ }
330
+ /**
331
+ * Object-centric configuration
332
+ * This is the main config type that students work with
333
+ */
334
+ interface ObjectCentricConfig {
335
+ /** Map of object instance IDs to their configurations */
336
+ objects: Map<string, ObjectConfig>;
337
+ /** Engine configuration (assets, runtime settings) */
338
+ engine: EngineConfig;
339
+ /** Theme configuration */
340
+ theme: ThemeConfig;
341
+ /** Gameplay configuration */
342
+ gameplay: GameplayConfig;
343
+ }
344
+
345
+ /**
346
+ * Handler SDK - EndGame UI Panel
347
+ *
348
+ * Success screen with logo, text, CTA button, and tutorial hand.
349
+ * Fully configurable via UI config system.
350
+ *
351
+ * DO NOT EDIT - Handler SDK Core
352
+ */
353
+
354
+ interface EndGamePanelElements {
355
+ panel: Container;
356
+ logo: Sprite;
357
+ title: Text;
358
+ subtitle: Text;
359
+ footer: Text;
360
+ ctaButton: Graphics;
361
+ ctaText: Text;
362
+ ctaHint: Text;
363
+ hand: Sprite;
364
+ }
365
+ /**
366
+ * Create EndGame success panel with all UI elements
367
+ */
368
+ declare function createEndGamePanel(config: ObjectCentricConfig, handTexture: Texture | null, logoTexture: Texture | null): EndGamePanelElements;
369
+ /**
370
+ * Animate panel entrance with staggered animations
371
+ */
372
+ declare function animatePanelEntrance(elements: EndGamePanelElements, config: ObjectCentricConfig, onComplete?: () => void): void;
373
+ /**
374
+ * Animate tutorial hand clicking CTA button
375
+ */
376
+ declare function animateHandClick(hand: Sprite, ctaButton: Graphics, config: ObjectCentricConfig, onClick: () => void): void;
377
+
378
+ /**
379
+ * Handler SDK - Tutorial UI Components
380
+ *
381
+ * Tutorial hand and label components for game tutorials.
382
+ * Config requirement: All UI properties from object-centric config.
383
+ *
384
+ * DO NOT EDIT - Handler SDK Core
385
+ */
386
+
387
+ /**
388
+ * Create hand tutorial sprite
389
+ * Hand properties from object-centric config (hand_tutorial_1)
390
+ *
391
+ * @param handTexture - Texture for the hand sprite
392
+ * @param config - Object-centric configuration
393
+ * @param baseCharacterPos - Base position of the character for animation reference
394
+ */
395
+ declare function createHandTutorial(handTexture: Texture, config: ObjectCentricConfig, baseCharacterPos: Point): Sprite;
396
+ /**
397
+ * Create tutorial label text
398
+ * Uses ui.tutorial config object with UI component schema
399
+ *
400
+ * @param config - Object-centric configuration
401
+ */
402
+ declare function createTutorialLabel(config: ObjectCentricConfig): Text;
403
+ /**
404
+ * Update hand animation
405
+ * Animation parameters from object-centric config (hand_tutorial_1)
406
+ *
407
+ * @param hand - The hand sprite
408
+ * @param baseCharacterPos - Base character position
409
+ * @param time - Current time for animation
410
+ * @param isDragging - Whether the user is currently dragging
411
+ * @param config - Object-centric configuration
412
+ */
413
+ declare function updateHandAnimation(hand: Sprite, baseCharacterPos: Point, time: number, isDragging: boolean, config: ObjectCentricConfig): void;
414
+
415
+ /**
416
+ * Handler SDK - Asset Loader Utilities
417
+ *
418
+ * Asset loading and texture creation utilities for PixiJS.
419
+ * Config requirement: All asset paths from config.assets, colors from config.theme.
420
+ *
421
+ * DO NOT EDIT - Handler SDK Core
422
+ */
423
+
424
+ /**
425
+ * Load an asset with fallback placeholder
426
+ * Asset path comes from config.assets
427
+ *
428
+ * @param path - Asset path
429
+ * @param app - PixiJS application instance
430
+ * @param fallbackColor - Color for fallback placeholder
431
+ * @param isChar - Whether this is a character sprite (affects placeholder shape)
432
+ */
433
+ declare function loadAsset(path: string, app: any, fallbackColor?: number, isChar?: boolean): Promise<Texture>;
434
+ /**
435
+ * Load character frames from sprite sheet
436
+ * Uses config.assets.character_sprite for path
437
+ * Character sheet dimensions come from config (cols: 3, rows: 2)
438
+ *
439
+ * @param path - Path to the sprite sheet
440
+ * @param app - PixiJS application instance
441
+ * @param config - Object-centric configuration
442
+ */
443
+ declare function loadCharacterFrames(path: string, app: any, config: ObjectCentricConfig): Promise<Texture[]>;
444
+ /**
445
+ * Create brush texture
446
+ * Uses config.theme.brush_color for color
447
+ * Brush size comes from config.gameplay.brush
448
+ *
449
+ * @param app - PixiJS application instance
450
+ * @param config - Object-centric configuration
451
+ */
452
+ declare function createBrushTexture(app: any, config: ObjectCentricConfig): Texture;
453
+
454
+ export { type EndGamePanelElements, GameObject, GameObjectManager, type ObjectCentricConfig, type PixiBaseContext, type PixiTheme, Renderer, Transform, animateHandClick, animatePanelEntrance, createBrushTexture, createEndGamePanel, createHandTutorial, createPixiBase, createTutorialLabel, getRegisteredFontIds, loadAsset, loadCharacterFrames, registerFont, resolveFont, resolveFontWeight, updateHandAnimation };
@@ -0,0 +1,17 @@
1
+ "use strict";var Ke=Object.defineProperty;var Ka=Object.getOwnPropertyDescriptor;var $a=Object.getOwnPropertyNames;var Ja=Object.prototype.hasOwnProperty;var Qa=(e,t)=>{for(var n in t)Ke(e,n,{get:t[n],enumerable:!0})},Za=(e,t,n,a)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of $a(t))!Ja.call(e,o)&&o!==n&&Ke(e,o,{get:()=>t[o],enumerable:!(a=Ka(t,o))||a.enumerable});return e};var Ua=e=>Za(Ke({},"__esModule",{value:!0}),e);var bi={};Qa(bi,{GameObject:()=>Ae,GameObjectManager:()=>Ie,Renderer:()=>Ce,Transform:()=>Se,animateHandClick:()=>ia,animatePanelEntrance:()=>aa,createBrushTexture:()=>ca,createEndGamePanel:()=>na,createHandTutorial:()=>oa,createPixiBase:()=>Un,createTutorialLabel:()=>sa,getRegisteredFontIds:()=>ta,loadAsset:()=>at,loadCharacterFrames:()=>la,registerFont:()=>ea,resolveFont:()=>G,resolveFontWeight:()=>j,updateHandAnimation:()=>ra});module.exports=Ua(bi);var Zn=require("pixi.js");var ee={};function Re(e,t,n=!1){ee[e]||(ee[e]=[]),ee[e].push({fn:t,once:n})}function $e(e,t){if(ee[e]){if(!t){delete ee[e];return}ee[e]=ee[e].filter(n=>n.fn!==t)}}function ze(e,...t){let n=ee[e];if(n)for(let a of[...n])a.fn(...t),a.once&&$e(e,a.fn)}function W(e,t){Re(e,t,!0)}var He={name:"handler-playable-sdk",version:"0.1.0",description:"Handler Playable SDK v0.1 with contract-aligned surface (root sandbox, canonical event envelope).",main:"dist/index.js",module:"dist/index.mjs",types:"dist/index.d.ts",exports:{".":{types:"./dist/index.d.ts",import:"./dist/index.mjs",require:"./dist/index.js"},"./pixi":{types:"./dist/pixi/index.d.ts",import:"./dist/pixi/index.mjs",require:"./dist/pixi/index.js"}},scripts:{build:"tsup src/index.ts src/pixi/index.ts --format cjs,esm --dts --clean --minify",lint:"eslint 'src/**/*.{ts,tsx}'",typecheck:"tsc --noEmit",prepublishOnly:"npm run build"},author:"Handler",license:"MIT",publishConfig:{access:"public"},repository:{type:"git",url:"https://github.com/HandlerAIGames/handler-playable-sdk.git"},files:["dist","LICENSE","README.md"],peerDependencies:{"pixi.js":"^8.0.0"},peerDependenciesMeta:{"pixi.js":{optional:!0}},devDependencies:{eslint:"^8.57.1","pixi.js":"^8.8.1","ts-node":"^10.9.2",tsup:"^8.4.0",typescript:"^5.7.2"}};var N=0,ti=N++,Sn=N++,Cn=N++,An=N++,En=N++,kn=N++,Dn=N++,Pn=N++,Mn=N++,Fn=N++,Ln=N++,Rn=N++,_=ti;function zn(){return _===Sn}function Hn(){return _===Cn}function On(){return _===An}function Wn(){return _===En}function de(){return _===kn}function ue(){return _===Dn}function Nn(){return _===Pn}function Yn(){return _===Mn}function Bn(){return _===Fn}function Je(){return _===Ln}function Qe(){return _===Rn}function In(){let e=typeof AD_PROTOCOL!="undefined"?AD_PROTOCOL:"none",t=typeof AD_NETWORK!="undefined"?AD_NETWORK:"web_embed";if(e==="mraid")try{mraid.getState(),_=Sn;return}catch{}else if(e==="dapi")try{dapi.isReady(),_=Cn;return}catch{}if(t==="facebook")try{typeof FbPlayableAd!="undefined"&&(_=An)}catch{}else if(t==="google")try{typeof ExitApi!="undefined"&&(_=En)}catch{}else if(t==="mintegral")window.gameReady&&(_=kn);else if(t==="tapjoy")window.TJ_API&&(_=Dn);else if(t==="tiktok")window.openAppStore&&(_=Pn);else if(t==="smadex")try{window.smxTracking&&(_=Mn)}catch{}else if(t==="snapchat")try{window.ScPlayableAd&&(_=Fn)}catch{}else t==="vungle"?_=Ln:(e==="nucleo"||t==="nucleo")&&(_=Rn)}var Kn={mechanic_id:"TODO_mechanic_id",variant_id:"TODO_variant_id",deployment_id:"TODO_deployment_id",export_id:"TODO_export_id",profile_id:"TODO_profile_id",instance_id:"default"},ni=Math.random().toString(36).slice(2),pe=null,be={...Kn},$n="web_embed",ai={},Ze=!1,_e=!1,xe=!1,Jn=!1,nt=1,et=0,Ye=!1,J=!1,Ne="",fe=Math.floor(window.innerWidth),me=Math.floor(window.innerHeight),tt=fe>me,Q=!1,we=!1,Gn=!1,jn=!1,Ue=!1,Oe=null;function Vn(){if(pe)return pe;let e=document.createElement("div");return e.id="handler-root",e.setAttribute("data-handler-root","true"),document.body.appendChild(e),pe=e,e}function We(e){switch(e){case"interaction":return"engagement";case"finish":return"complete";case"install":return"cta_click";default:return e}}function Qn(e,t){return{event_name:e,ts:Date.now(),session_id:ni,deployment_id:be.deployment_id,variant_id:be.variant_id,export_profile_id:be.profile_id,instance_id:be.instance_id||"default",env:$n==="mraid"?"mraid":"web",payload:t}}function y(e,t){let n=We(e),a=Qn(n,t);ze(n,a),n!==e&&ze(e,a)}function Te(){Oe&&(Oe(fe,me),Oe=null)}function he(e){nt=e,y("volume",e)}function ye(e){e&&(Jn=!0),!xe&&(xe=!0,y("pause"),he(0))}function ve(e){!e&&Jn||xe&&(xe=!1,y("resume"),he(nt))}function ne(e,t){fe=Math.floor(e||window.innerWidth),me=Math.floor(t||window.innerHeight),tt=fe>me,y("resize",{width:fe,height:me})}function ii(){if(zn())try{let e=mraid.getMaxSize();ne(e.width,e.height);let t=()=>{mraid.isViewable()&&mraid.getState()!=="hidden"?ve():ye()};if(mraid.addEventListener("viewableChange",t),mraid.addEventListener("stateChange",t),mraid.addEventListener("sizeChange",()=>{let n=mraid.getMaxSize();ne(n.width,n.height)}),mraid.getAudioVolume){let n=mraid.getAudioVolume();he(n?1:0)}if(mraid.addEventListener("audioVolumeChange",n=>{n!==null&&he(n>0?1:0)}),mraid.addEventListener("error",(n,a)=>{console.warn("mraid error:",n,"action:",a)}),Ye=!0,mraid.isViewable()&&mraid.getState()!=="hidden")Q=!0,y("boot"),y("view"),y("ready"),J=!0,Te();else{let n=()=>{Q=!0,y("boot"),y("view"),y("ready"),J=!0,Te()};mraid.addEventListener("ready",n)}}catch(e){console.warn("MRAID hook skipped",e)}}function oi(){if(Hn())try{let e=dapi.getScreenSize();ne(e.width,e.height),dapi.addEventListener("viewableChange",n=>{n.isViewable?ve():ye()}),dapi.addEventListener("adResized",n=>{let a=dapi.getScreenSize();ne(n.width||a.width,n.height||a.height)});let t=dapi.getAudioVolume();if(he(t?1:0),dapi.addEventListener("audioVolumeChange",n=>he(n?1:0)),Ye=!0,dapi.isViewable())Q=!0,y("boot"),y("view"),y("ready"),J=!0,Te();else{let n=()=>{Q=!0,y("boot"),y("view"),y("ready"),J=!0,Te()};dapi.addEventListener("ready",n)}}catch(e){console.warn("DAPI hook skipped",e)}}function qn(){let e=()=>{Q||document.visibilityState==="visible"&&(document.readyState==="complete"||document.readyState==="interactive")&&(Q=!0,y("boot"),y("view"),y("ready"),J=!0,Te(),we&&(we=!1,te.start()))};window.addEventListener("resize",()=>ne()),document.addEventListener("visibilitychange",()=>{document.visibilityState==="visible"?(ve(),e()):ye()}),document.readyState==="complete"||document.readyState==="interactive"?e():window.addEventListener("load",e),Ye=!0}function si(){let e=t=>{typeof TouchEvent!="undefined"&&t instanceof TouchEvent&&(Gn=!0),!(Gn&&t instanceof MouseEvent)&&(et+=1,y("interaction",et))};document.addEventListener("mousedown",e),document.addEventListener("touchstart",e)}function ri(e){var a,o,i,s,r,l,d,p,f,u,c,h;let t=typeof AD_PROTOCOL!="undefined"?AD_PROTOCOL:"none";if((typeof AD_NETWORK!="undefined"?AD_NETWORK:"web_embed")==="google")try{(a=window.ExitApi)==null||a.exit();return}catch{}if(t==="mraid"&&typeof mraid!="undefined")mraid.open(e||"");else if(t==="dapi"&&typeof dapi!="undefined")dapi.openStoreUrl();else if(ue())(i=(o=window.TJ_API)==null?void 0:o.click)==null||i.call(o);else if(On())(r=(s=window.FbPlayableAd)==null?void 0:s.onCTAClick)==null||r.call(s);else if(Bn())(d=(l=window.ScPlayableAd)==null?void 0:l.onCTAClick)==null||d.call(l);else if(Yn())try{(f=(p=window.smxTracking)==null?void 0:p.redirect)==null||f.call(p)}catch(m){console.warn("Smadex redirect failed",m)}else if(Wn()){let m=window.ExitApi;m&&typeof m.exit=="function"?m.exit(e||Ne||""):e&&window.open(e)}else de()?(u=window.install)==null||u.call(window):Nn()?(c=window.openAppStore)==null||c.call(window):Je()?(h=parent==null?void 0:parent.postMessage)==null||h.call(parent,"download","*"):e&&window.open(e)}function li(){let e=typeof AD_NETWORK!="undefined"?AD_NETWORK:"web_embed",t=n=>{if(!n)return;let a=new Image;a.src=n};if(e==="bigabid"){let n=window.BIGABID_BIDTIMEMACROS;if(!n)return;W("view",()=>t(n.mraid_viewable)),W("start",()=>t(n.game_viewable)),W("engagement",()=>t(n.engagement));let a=()=>t(n.complete);W("complete",a),Re("engagement",o=>{var i;((i=o==null?void 0:o.payload)==null?void 0:i.count)>3&&a()}),W("cta_click",()=>t(n.click))}else if(e==="inmobi"){let n=window.INMOBI_DSPMACROS;if(!n)return;W("view",()=>t(n.Ad_Load_Start)),W("start",()=>t(n.Ad_Viewable)),W("engagement",()=>t(n.First_Engagement)),W("complete",()=>t(n.Gameplay_Complete)),W("cta_click",()=>t(n.DSP_Click)),W("start",()=>{[5,10,15,20,25,30].forEach(a=>setTimeout(()=>t(n[`Spent_${a}_Seconds`]),a*1e3))})}}function ci(){if(!ue())return;let e=window.TJ_API;e&&e.setPlayableAPI&&e.setPlayableAPI({skipAd:()=>{try{te.finish()}catch(t){console.warn("Tapjoy skip failed",t)}}})}function Xn(){var t,n,a;let e=window.TJ_API;(t=e==null?void 0:e.objectiveComplete)==null||t.call(e),(n=e==null?void 0:e.playableFinished)==null||n.call(e),(a=e==null?void 0:e.gameplayFinished)==null||a.call(e)}function di(){de()&&(window.mintGameStart=()=>{ve(!0),ne()},window.mintGameClose=()=>{ye(!0)})}function ui(){if(!Qe())return;let e=window.NUC;!e||!e.trigger||(te.on("cta_click",()=>{var t,n;return(n=(t=e.trigger).convert)==null?void 0:n.call(t,Ne)}),te.on("complete",()=>{var t,n;return(n=(t=e.trigger).tryAgain)==null?void 0:n.call(t)}))}var te={init(e={},t){if($n=e.profile||"web_embed",ai=e.consent||{},be={...Kn,...e.ids||{}},pe=e.rootEl||pe,Ne=e.destinationUrl||(/android/i.test(navigator.userAgent)?"https://play.google.com/store":"https://www.apple.com/app-store/"),t&&(Oe=t),y("init"),document.body.oncontextmenu=()=>!1,Vn(),pi(pe),In(),ii(),oi(),!Ye){if(document.readyState==="complete")qn();else if(!jn){jn=!0;let n=()=>{qn(),window.removeEventListener("load",n),document.removeEventListener("DOMContentLoaded",n)};window.addEventListener("load",n),document.addEventListener("DOMContentLoaded",n)}}si(),li(),ci(),di(),ui(),console.log(`%c @handler/playable-sdk %c v${He.version||"0.0.0"} `,"background: #007acc; color: #fff; font-size: 14px; padding: 4px 8px; border-top-left-radius: 4px; border-bottom-left-radius: 4px;","background: #e1e4e8; color: #333; font-size: 14px; padding: 4px 8px; border-top-right-radius: 4px; border-bottom-right-radius: 4px;"),Q&&!J&&(y("boot"),y("view"),y("ready"),we&&(we=!1,te.start()),J=!0),J=Q},getRoot(){return Vn()},get version(){return He.version||"0.0.0"},get maxWidth(){return fe},get maxHeight(){return me},get isLandscape(){return tt},get isReady(){return J},get isStarted(){return Ze},get isPaused(){return xe},get isFinished(){return _e},get volume(){return nt},get interactions(){return et},on(e,t){Re(We(e),t)},off(e,t){$e(We(e),t)},start(){var e,t;if(!Ze){if(!Q){we=!0;return}if(Ze=!0,y("start"),ne(),de())ye(),(e=window.gameReady)==null||e.call(window);else if(ue()){let n=window.TJ_API;(t=n==null?void 0:n.setPlayableBuild)==null||t.call(n,{orientation:tt?"landscape":"portrait",buildID:He.version||"dev"})}}},finish(){var e,t;_e||(_e=!0,y("complete"),de()?(e=window.gameEnd)==null||e.call(window):Je()?(t=parent==null?void 0:parent.postMessage)==null||t.call(parent,"complete","*"):ue()&&Xn())},install(e){if(!_e){_e=!0,ue()?(Xn(),setTimeout(()=>te.install(e),300)):(y("complete"),setTimeout(()=>te.install(e),0));return}Ue||(Ue=!0,setTimeout(()=>Ue=!1,500),y("cta_click"),y("conversion"),ri(e||Ne))},emit(e,t){let n=We(e);if(!["view","boot","start","engagement","complete","cta_click","conversion","retry","pause","resume","resize","volume","error"].includes(n)&&!n.startsWith("custom."))throw new Error(`Event ${e} must be canonical or namespaced as custom.<mechanic_id>.<event>`);let a=Qn(n,t);ze(n,a)},retry(){var e,t,n;if(de())(e=window.gameRetry)==null||e.call(window);else if(Qe()){let a=window.NUC;(n=(t=a==null?void 0:a.trigger)==null?void 0:t.tryAgain)==null||n.call(t)}y("engagement",{action:"retry"})},pause(){ye(!0)},resume(){ve(!0)},resize(e,t){ne(e,t)}},Be=te;function pi(e){let t=document.createElement("script");t.type="text/javascript",t.textContent=`
2
+ (function(){
3
+ var events = ['touchstart','touchend','mousedown','keydown'];
4
+ function unlock(){
5
+ if(window.AudioContext && AudioContext.prototype.resume){
6
+ if(!window.__handler_audio_ctx){
7
+ window.__handler_audio_ctx = new AudioContext();
8
+ }
9
+ if(window.__handler_audio_ctx.state === 'suspended'){
10
+ window.__handler_audio_ctx.resume();
11
+ }
12
+ }
13
+ events.forEach(function(e){ document.removeEventListener(e, unlock); });
14
+ }
15
+ events.forEach(function(e){ document.addEventListener(e, unlock, false); });
16
+ })();
17
+ `,e.appendChild(t)}async function Un(e,t={}){let n=new Zn.Application;await n.init({resizeTo:e,autoDensity:!0,resolution:window.devicePixelRatio||1,antialias:!0,backgroundAlpha:0});let a=i=>{var l,d,p,f,u,c;let s=(p=(d=(l=i==null?void 0:i.width)!=null?l:e.clientWidth)!=null?d:window.innerWidth)!=null?p:320,r=(c=(u=(f=i==null?void 0:i.height)!=null?f:e.clientHeight)!=null?u:window.innerHeight)!=null?c:480;n.renderer.resize(s,r)};e.innerHTML="",e.style.position="relative",e.style.display="flex",e.style.flexDirection="column",e.style.alignItems="stretch",e.style.width="100%",e.style.height="100dvh",e.style.minHeight="100vh",e.style.maxWidth="100%",e.style.maxHeight="100%",e.style.boxSizing="border-box",e.style.overflow="hidden",t.background&&(e.style.background=t.background),e.appendChild(n.canvas);let o=document.createElement("div");return o.setAttribute("style",["position:absolute","inset:0","display:flex","flex-direction:column","align-items:center","justify-content:flex-start","gap:12px","pointer-events:none","font-family:Arial,sans-serif","padding:20px 16px","padding-bottom:clamp(24px, 6vh, 96px)","box-sizing:border-box","z-index:2"].join(";")),e.appendChild(o),Be.on("pause",()=>{n.ticker.stop(),document.querySelectorAll("audio").forEach(s=>s.pause())}),Be.on("resume",()=>{n.ticker.start(),document.querySelectorAll("audio").forEach(s=>{s.currentTime>0&&!s.ended&&s.play().catch(()=>{})})}),Be.on("resize",({payload:i})=>{a({width:i==null?void 0:i.width,height:i==null?void 0:i.height})}),window.addEventListener("resize",()=>a()),requestAnimationFrame(()=>a()),{app:n,stage:n.stage,overlay:o,applySize:a}}var Se=class{constructor(t){var n,a;this.position=(t==null?void 0:t.position)||{x:0,y:0},this.scale=(n=t==null?void 0:t.scale)!=null?n:1,this.rotation=(a=t==null?void 0:t.rotation)!=null?a:0}update(t){t.position!==void 0&&(this.position={...t.position}),t.scale!==void 0&&(this.scale=t.scale),t.rotation!==void 0&&(this.rotation=t.rotation)}syncToPixi(t){t.position.set(this.position.x,this.position.y),t.scale.set(this.scale),t.rotation=this.rotation}},Ce=class{constructor(t){var n,a,o,i;this.z_index=(n=t==null?void 0:t.z_index)!=null?n:0,this.alpha=(a=t==null?void 0:t.alpha)!=null?a:1,this.visible=(o=t==null?void 0:t.visible)!=null?o:!0,this.tint=(i=t==null?void 0:t.tint)!=null?i:null}update(t){t.z_index!==void 0&&(this.z_index=t.z_index),t.alpha!==void 0&&(this.alpha=t.alpha),t.visible!==void 0&&(this.visible=t.visible),t.tint!==void 0&&(this.tint=t.tint)}syncToPixi(t){t.zIndex=this.z_index,t.alpha=this.alpha,t.visible=this.visible,this.tint!==null&&"tint"in t&&(t.tint=this.tint)}},Ae=class{constructor(t,n,a,o){this.instanceId=t,this.objectConfig=n,this.pixiObject=a,this._config=o,this.transform=new Se(o.transform),this.renderer=new Ce(o.render),this.sync()}sync(){this.transform.syncToPixi(this.pixiObject),this.renderer.syncToPixi(this.pixiObject)}updateConfig(t){this._config=t,t.transform&&this.transform.update(t.transform),t.render&&this.renderer.update(t.render),this.sync()}getComponent(t){return this._config[t]}getPosition(){return{...this.transform.position}}setPosition(t,n){this.transform.position={x:t,y:n},this.transform.syncToPixi(this.pixiObject)}getAlpha(){return this.renderer.alpha}setAlpha(t){this.renderer.alpha=t,this.renderer.syncToPixi(this.pixiObject)}getVisible(){return this.renderer.visible}setVisible(t){this.renderer.visible=t,this.renderer.syncToPixi(this.pixiObject)}},Ie=class{constructor(t){this.objects=new Map;this.config=t}create(t,n){let a=this.config.objects.get(t);if(!a)throw new Error(`Object config not found: ${t}`);let o=new Ae(t,a.object_config||t,n,a);return this.objects.set(t,o),o}get(t){return this.objects.get(t)}updateConfig(t){this.config=t;for(let[n,a]of this.objects.entries()){let o=t.objects.get(n);o&&a.updateConfig(o)}}getAll(){return Array.from(this.objects.values())}remove(t){this.objects.delete(t)}clear(){this.objects.clear()}};var Ge={"brand.primary":{family:"Arial, sans-serif",weight:"400"},"brand.warning":{family:"Arial Black, Arial, sans-serif",weight:"900"},"brand.heading":{family:"Arial Black, Arial, sans-serif",weight:"800"},"brand.body":{family:"Arial, sans-serif",weight:"400"}};function G(e){let t=Ge[e];return t?t.family:(console.warn(`Font ID "${e}" not found in registry, using fallback`),"Arial, sans-serif")}function j(e){var n;let t=Ge[e];return t&&(n=t.weight)!=null?n:"400"}function ea(e,t){Ge[e]=t}function ta(){return Object.keys(Ge)}var w=require("pixi.js");function na(e,t,n){var ht,yt,gt,_t,bt,wt,xt,Tt,vt,St,Ct,At,Et,kt,Dt,Pt,Mt,Ft,Lt,Rt,zt,Ht,Ot,Wt,Nt,Yt,Bt,It,Gt,jt,Vt,qt,Xt,Kt,$t,Jt,Qt,Zt,Ut,en,tn,nn,an,on,sn,rn,ln,cn,dn,un,pn,fn,mn,hn,yn,gn,_n,bn,wn,xn;let a=e.objects.get("ui_endgame_1"),o=e.objects.get("ui_endgame_logo_1"),i=e.objects.get("ui_endgame_title_1"),s=e.objects.get("ui_endgame_subtitle_1"),r=e.objects.get("ui_endgame_footer_1"),l=e.objects.get("ui_endgame_cta_1"),d=e.objects.get("ui_endgame_cta_hint_1"),p=e.objects.get("ui_endgame_hand_1"),f=e.engine.runtime||{},u=new w.Container;u.alpha=0,u.visible=!1;let c=((ht=a==null?void 0:a.gameplay)==null?void 0:ht.tuning)||{},h=(a==null?void 0:a.render)||{},m=(yt=c.panel_width)!=null?yt:320,g=(gt=c.panel_height)!=null?gt:400,x=(_t=c.panel_padding)!=null?_t:32,T=(bt=c.panel_radius)!=null?bt:24,A=h.background_color||c.panel_bg_color||"#1a0a0a",v=h.background_alpha!==void 0&&h.background_alpha!==null?h.background_alpha:(wt=c.panel_bg_alpha)!=null?wt:.98,E=h.border_color||c.panel_border_color||"#ffffff",k=(xt=c.panel_border_width)!=null?xt:2,S=(Tt=c.panel_border_alpha)!=null?Tt:.3,C=new w.Graphics;C.roundRect(-m/2,-g/2,m,g,T);let b=A.replace("#","");C.fill({color:parseInt(b,16),alpha:v});let D=E.replace("#","");C.stroke({color:parseInt(D,16),width:k,alpha:S});let L=new w.Graphics;L.roundRect(-m/2+2,-g/2+2,m-4,g-4,T-2),L.stroke({color:16777215,width:1,alpha:.1}),C.addChild(L),u.addChild(C);let V=((vt=o==null?void 0:o.gameplay)==null?void 0:vt.tuning)||{},Z=(o==null?void 0:o.transform)||{},Y=(St=V.logo_size)!=null?St:56,q=(Ct=Z.scale)!=null?Ct:1,ae=(Et=(At=Z.offset)==null?void 0:At.y)!=null?Et:-130,da=(kt=V.logo_alpha)!=null?kt:1,M=new w.Sprite(n||w.Texture.EMPTY);M.anchor.set(.5);let oe=0,se=0;if(n&&n!==w.Texture.EMPTY){let F=n.frame,$=n.baseTexture;F&&F.width&&F.height?(oe=F.width,se=F.height):$&&$.width&&$.height?(oe=$.width,se=$.height):n.width&&n.height&&(oe=n.width,se=n.height)}if(oe>0&&se>0){let F=oe/se,$,Xe;F>1?($=Y,Xe=Y/F):(Xe=Y,$=Y*F);let Tn=$/oe,vn=Xe/se;Tn*=q,vn*=q,M.scale.set(Tn,vn)}else{let F=Y/100*q;M.scale.set(F)}M.position.set(0,ae),M.alpha=0,M.visible=!0,M.targetAlpha=da,M.baseScaleX=M.scale.x,M.baseScaleY=M.scale.y,u.addChild(M);let R=i==null?void 0:i.ui,B=((Dt=i==null?void 0:i.gameplay)==null?void 0:Dt.tuning)||{},ua=(R==null?void 0:R.text)||"MISSION ACCOMPLISHED",it=(R==null?void 0:R.font)||"brand.heading",pa=(R==null?void 0:R.fontSize)||24,fa=(Pt=R==null?void 0:R.letterSpacing)!=null?Pt:1,Ee=(Mt=i==null?void 0:i.render)==null?void 0:Mt.tint,ma=typeof Ee=="string"?Ee:Ee?`#${Ee.toString(16).padStart(6,"0")}`:"#00FF88",ot=m-x*2-30,ha=B.font_weight_override?B.font_weight_override:j(it),U=new w.Text({text:ua,style:{fontFamily:G(it),fontSize:pa,fontWeight:ha,fill:ma,align:"center",letterSpacing:fa,wordWrap:!1,stroke:{color:B.stroke_color||"#000000",width:(Ft=B.stroke_width)!=null?Ft:2},dropShadow:{color:B.shadow_color||"#000000",alpha:(Lt=B.shadow_alpha)!=null?Lt:.9,angle:(Rt=B.shadow_angle)!=null?Rt:0,blur:(zt=B.shadow_blur)!=null?zt:8,distance:(Ht=B.shadow_distance)!=null?Ht:4}}});if(U.width>ot){let F=ot/U.width;U.scale.set(F)}U.anchor.set(.5);let st=(Ot=c.title_offset_y)!=null?Ot:30,ya=st+((Wt=B.animation_start_y_offset)!=null?Wt:-20);U.position.set(0,ya),U.alpha=0,u.addChild(U);let z=s==null?void 0:s.ui,rt=((Nt=s==null?void 0:s.gameplay)==null?void 0:Nt.tuning)||{},ga=(z==null?void 0:z.text)||"RESCUE COMPLETE",lt=(z==null?void 0:z.font)||"brand.body",_a=(z==null?void 0:z.fontSize)||11,ba=(Yt=z==null?void 0:z.letterSpacing)!=null?Yt:.5,ke=(Bt=s==null?void 0:s.render)==null?void 0:Bt.tint,wa=typeof ke=="string"?ke:ke?`#${ke.toString(16).padStart(6,"0")}`:"#B0B0B0",re=new w.Text({text:ga,style:{fontFamily:G(lt),fontSize:_a,fontWeight:j(lt),fill:wa,align:"center",letterSpacing:ba}});re.anchor.set(.5);let xa=st+20+((It=rt.animation_start_y_offset)!=null?It:-10);re.position.set(0,xa),re.alpha=0,re.animationStartYOffset=(Gt=rt.animation_start_y_offset)!=null?Gt:-10,u.addChild(re);let X=r==null?void 0:r.ui,ct=((jt=r==null?void 0:r.gameplay)==null?void 0:jt.tuning)||{},Ta=(X==null?void 0:X.text)||((Vt=f.ui)==null?void 0:Vt.cta_hint)||"Continue the adventure!",dt=(X==null?void 0:X.font)||"brand.body",va=(X==null?void 0:X.fontSize)||16,De=(qt=r==null?void 0:r.render)==null?void 0:qt.tint,Sa=typeof De=="string"?De:De?`#${De.toString(16).padStart(6,"0")}`:"#FFFFFF",Ca=(Xt=ct.max_width)!=null?Xt:m-x*2,Aa=(Kt=ct.line_height)!=null?Kt:1.4,le=new w.Text({text:Ta,style:{fontFamily:G(dt),fontSize:va,fontWeight:j(dt),fill:Sa,align:"center",wordWrap:!0,wordWrapWidth:Ca,lineHeight:Aa}});le.anchor.set(.5);let Ea=($t=c.footer_offset_y)!=null?$t:120;le.position.set(0,Ea);let ka=((Jt=r==null?void 0:r.render)==null?void 0:Jt.alpha)!==void 0&&((Qt=r==null?void 0:r.render)==null?void 0:Qt.alpha)!==null?r.render.alpha:1;le.alpha=0,le.targetAlpha=ka,u.addChild(le);let K=((Zt=l==null?void 0:l.gameplay)==null?void 0:Zt.tuning)||{},H=l==null?void 0:l.ui,Pe=(Ut=K.button_width)!=null?Ut:260,Me=(en=K.button_height)!=null?en:56,ut=(tn=K.button_radius)!=null?tn:16,Da=K.button_bg_color||((nn=f.theme)==null?void 0:nn.cta_background)||"#ffb43b",Pa=(an=K.button_bg_alpha)!=null?an:1,Ma=K.button_border_color||"#ffffff",Fa=(on=K.button_border_width)!=null?on:2,La=(sn=K.button_border_alpha)!=null?sn:.2,Fe=(rn=c.cta_offset_y)!=null?rn:160,I=new w.Graphics;I.roundRect(-Pe/2,-Me/2,Pe,Me,ut);let Ra=Da.replace("#","");I.fill({color:parseInt(Ra,16),alpha:Pa});let za=Ma.replace("#","");I.stroke({color:parseInt(za,16),width:Fa,alpha:La});let Ve=new w.Graphics;Ve.roundRect(-Pe/2+2,-Me/2+2,Pe-4,Me-4,ut-2),Ve.stroke({color:0,width:1,alpha:.15}),I.addChild(Ve),I.position.set(0,Fe),I.alpha=0,I.eventMode="static",I.cursor="pointer",u.addChild(I);let Ha=(H==null?void 0:H.text)||((ln=f.ui)==null?void 0:ln.cta_label_end)||"DOWNLOAD",pt=(H==null?void 0:H.font)||"brand.heading",Oa=(H==null?void 0:H.fontSize)||20,Wa=K.button_text_color||((cn=f.theme)==null?void 0:cn.cta_text)||"#1a0a0a",ge=new w.Text({text:Ha,style:{fontFamily:G(pt),fontSize:Oa,fontWeight:j(pt),fill:Wa,align:"center",letterSpacing:(dn=H==null?void 0:H.letterSpacing)!=null?dn:1}});ge.anchor.set(.5),ge.position.set(0,Fe),ge.alpha=0,u.addChild(ge);let O=d==null?void 0:d.ui,ft=((un=d==null?void 0:d.gameplay)==null?void 0:un.tuning)||{},Na=(O==null?void 0:O.text)||"Play the full game",mt=(O==null?void 0:O.font)||"brand.body",Ya=(O==null?void 0:O.fontSize)||11,Ba=(pn=O==null?void 0:O.letterSpacing)!=null?pn:0,Le=(fn=d==null?void 0:d.render)==null?void 0:fn.tint,Ia=typeof Le=="string"?Le:Le?`#${Le.toString(16).padStart(6,"0")}`:"#CCCCCC",ce=new w.Text({text:Na,style:{fontFamily:G(mt),fontSize:Ya,fontWeight:j(mt),fill:Ia,align:"center",letterSpacing:Ba}});ce.anchor.set(.5);let Ga=Fe+35+((mn=ft.animation_start_y_offset)!=null?mn:5);ce.position.set(0,Ga),ce.alpha=0,ce.animationStartYOffset=(hn=ft.animation_start_y_offset)!=null?hn:5,u.addChild(ce);let qe=((yn=p==null?void 0:p.gameplay)==null?void 0:yn.tuning)||{},ja=(gn=qe.hand_offset_x)!=null?gn:50,Va=(_n=qe.hand_offset_y)!=null?_n:50,qa=(wn=(bn=p==null?void 0:p.transform)==null?void 0:bn.scale)!=null?wn:.1875,Xa=(xn=qe.hand_scale_multiplier)!=null?xn:2.5,ie=new w.Sprite(t||w.Texture.EMPTY);return ie.anchor.set(.5),ie.scale.set(qa*Xa),ie.position.set(ja,Fe+Va),ie.alpha=0,ie.visible=!1,u.addChild(ie),{panel:u,logo:M,title:U,subtitle:re,footer:le,ctaButton:I,ctaText:ge,ctaHint:ce,hand:ie}}function aa(e,t,n){var k,S,C,b,D,L,V,Z;let a=t.objects.get("ui_endgame_1"),o=((k=a==null?void 0:a.gameplay)==null?void 0:k.tuning)||{},i=(S=o.animation_duration_ms)!=null?S:800,s=(C=o.fade_in_duration_ms)!=null?C:600,r=(b=o.scale_animation_intensity)!=null?b:.1,l=(D=o.logo_animation_delay_ms)!=null?D:100,d=(L=o.title_animation_delay_ms)!=null?L:200,p=(V=o.footer_animation_delay_ms)!=null?V:300,f=(Z=o.cta_animation_delay_ms)!=null?Z:400,{panel:u,logo:c,title:h,subtitle:m,footer:g,ctaButton:x,ctaText:T,ctaHint:A}=e;u.visible=!0;let v=Date.now(),E=()=>{let Y=Date.now()-v,q=Math.min(Y/s,1),ae=1-Math.pow(1-q,3);u.alpha=ae,u.scale.set(1+r*(1-ae)),q<1?requestAnimationFrame(E):(setTimeout(()=>fi(c,i,t),l),setTimeout(()=>{mi(h,i),setTimeout(()=>hi(m,i),50)},d),setTimeout(()=>yi(g,i),p),setTimeout(()=>{gi(x,T,i),setTimeout(()=>_i(A,i),50),n&&n()},f))};E()}function fi(e,t,n){var u,c,h,m,g;let a=n==null?void 0:n.objects.get("ui_endgame_logo_1"),i=(c=(((u=a==null?void 0:a.gameplay)==null?void 0:u.tuning)||{}).animation_start_scale)!=null?c:1.2,s=Date.now(),r=e.alpha,l=(h=e.targetAlpha)!=null?h:1,d=(m=e.baseScaleX)!=null?m:e.scale.x,p=(g=e.baseScaleY)!=null?g:e.scale.y,f=()=>{let x=Date.now()-s,T=Math.min(x/t,1),A=1-Math.pow(1-T,2);e.alpha=r+(l-r)*A;let v=i+(1-i)*A;e.scale.set(d*v,p*v),T<1&&requestAnimationFrame(f)};f()}function mi(e,t){let n=Date.now(),a=e.alpha,o=e.y,i=()=>{let s=Date.now()-n,r=Math.min(s/t,1),l=1-Math.pow(1-r,2);e.alpha=a+(1-a)*l,e.y=o-20*(1-l),r<1&&requestAnimationFrame(i)};i()}function hi(e,t){var r;let n=Date.now(),a=e.alpha,o=e.position.y,i=o-((r=e.animationStartYOffset)!=null?r:-10),s=()=>{let l=Date.now()-n,d=Math.min(l/t,1),p=1-Math.pow(1-d,2);e.alpha=a+(1-a)*p,e.position.y=o+(i-o)*p,d<1&&requestAnimationFrame(s)};s()}function yi(e,t){var s;let n=Date.now(),a=e.alpha,o=(s=e.targetAlpha)!=null?s:1,i=()=>{let r=Date.now()-n,l=Math.min(r/t,1),d=1-Math.pow(1-l,2);e.alpha=a+(o-a)*d,l<1&&requestAnimationFrame(i)};i()}function gi(e,t,n){let a=Date.now(),o=e.alpha,i=()=>{let s=Date.now()-a,r=Math.min(s/n,1),l=1-Math.pow(1-r,2);e.alpha=o+(1-o)*l,t.alpha=o+(1-o)*l,e.scale.set(1+.1*(1-l)),r<1&&requestAnimationFrame(i)};i()}function _i(e,t){var r;let n=Date.now(),a=e.alpha,o=e.position.y,i=o-((r=e.animationStartYOffset)!=null?r:5),s=()=>{let l=Date.now()-n,d=Math.min(l/t,1),p=1-Math.pow(1-d,2);e.alpha=a+(1-a)*p,e.position.y=o+(i-o)*p,d<1&&requestAnimationFrame(s)};s()}function ia(e,t,n,a){var p,f,u,c;let o=n.objects.get("ui_endgame_hand_1"),i=((p=o==null?void 0:o.gameplay)==null?void 0:p.tuning)||{},s=(f=i.click_animation_duration_ms)!=null?f:600,r=(u=i.click_delay_ms)!=null?u:2e3,l=(c=i.click_repeat_delay_ms)!=null?c:3e3,d=()=>{var k,S;e.visible=!0,e.alpha=1;let h=e.x,m=e.y,g=(k=i.hand_offset_x)!=null?k:50,x=(S=i.hand_offset_y)!=null?S:50,T=t.x+g,A=t.y+x,v=Date.now(),E=()=>{var Y,q,ae;let C=Date.now()-v,b=Math.min(C/s,1),D=b<.5?2*b*b:1-Math.pow(-2*b+2,2)/2;e.x=h+(T-h)*D,e.y=m+(A-m)*D;let L=(q=(Y=o==null?void 0:o.transform)==null?void 0:Y.scale)!=null?q:.1875,V=(ae=i.hand_scale_multiplier)!=null?ae:2.5,Z=L*V;e.scale.set(Z+Z*.2*Math.sin(b*Math.PI)),b<1?requestAnimationFrame(E):(t.scale.set(.95),setTimeout(()=>{t.scale.set(1),a()},100),setTimeout(()=>{e.alpha=0,setTimeout(()=>{e.x=h,e.y=m,d()},l)},500))};E()};setTimeout(d,r)}var je=require("pixi.js");function oa(e,t,n){var p,f,u,c;let a=t.objects.get("hand_tutorial_1"),o=((p=a==null?void 0:a.gameplay)==null?void 0:p.tuning)||{},i=(a==null?void 0:a.render)||{},s=(a==null?void 0:a.transform)||{},r=new je.Sprite(e);r.anchor.set(.5),r.alpha=0;let l=(f=o.hand_scale_multiplier)!=null?f:2.5,d=(u=s.scale)!=null?u:.1875;return r.scale.set(d*l),r.zIndex=(c=i.z_index)!=null?c:99999,r}function sa(e){var h,m,g,x,T,A,v,E,k,S;let t=e.objects.get("ui_tutorial_1"),n=t==null?void 0:t.ui,a=(t==null?void 0:t.render)||{},o=((h=e.engine.runtime)==null?void 0:h.ui_styles)||{},i=((m=e.engine.runtime)==null?void 0:m.ui)||{},s=(n==null?void 0:n.text)||i.tutorial_label||i.label_text||"CLEAN!",r=(n==null?void 0:n.font)||"brand.warning",l=(n==null?void 0:n.fontSize)||o.label_font_size||52,d=(x=n==null?void 0:n.letterSpacing)!=null?x:(g=o.label_letter_spacing)!=null?g:-1,p=(n==null?void 0:n.align)||"center",f=a.tint,u=typeof f=="string"?f:f?`#${f.toString(16).padStart(6,"0")}`:o.label_fill||"#FFF1C1",c=new je.Text({text:s,style:{fontFamily:G(r),fontSize:l,fontWeight:j(r),fill:u,align:p,letterSpacing:d,stroke:{color:o.label_stroke||"#8B0000",width:2},dropShadow:{color:o.label_shadow_color||"#000000",alpha:(T=o.label_shadow_alpha)!=null?T:.8,angle:(A=o.label_shadow_angle)!=null?A:0,blur:(v=o.label_shadow_blur)!=null?v:4,distance:(E=o.label_shadow_distance)!=null?E:2}}});return c.anchor.set(.5,0),c.alpha=(k=a.alpha)!=null?k:0,c.visible=(S=a.visible)!=null?S:!0,c}function ra(e,t,n,a,o){var l,d,p,f,u,c,h;let i=o.objects.get("hand_tutorial_1"),s=((l=i==null?void 0:i.gameplay)==null?void 0:l.tuning)||{},r=(i==null?void 0:i.transform)||{};if(!a){let m=(d=s.animation_duration)!=null?d:1.5,g=n%m/m,x=g<.5?2*g*g:-1+(4-2*g)*g,T=(p=s.x_offset)!=null?p:0,A=(f=s.y_offset)!=null?f:0,v=(u=s.animation_range)!=null?u:40,E=v*.7,k=v,S=t.x+T+E,C=t.y+A+k,b=S+E,D=C+k;e.x=S+(b-S)*x,e.y=C+(D-C)*x;let L=(c=r.scale)!=null?c:.1875,V=(h=s.hand_scale_multiplier)!=null?h:2.5;e.scale.set(L*V)}}var P=require("pixi.js");async function at(e,t,n=16777215,a=!1){try{let i=(globalThis.INLINE_ASSETS||{})[e];return await P.Assets.load(i||e)}catch{console.warn(`Asset missing: ${e}. Using placeholder.`);let i=new P.Graphics;return a?i.roundRect(0,0,60,120,10).fill({color:n}):(i.circle(30,30,30).fill({color:n}),i.rect(25,30,10,40).fill({color:n})),t.renderer.generateTexture(i)}}async function la(e,t,n){var m,g,x,T,A,v,E,k,S,C;let o=(globalThis.INLINE_ASSETS||{})[e]||e,i;try{i=await P.Assets.load(o),console.log("[character] loaded sheet",{path:e,sourceUsed:o})}catch(b){let L=(n.gameplay.character_sheet||{}).fallback_color||"#ff00ff";i=await at(e,t,new P.Color(L).toNumber(),!0),console.warn("[character] fallback loadAsset used",{path:e,err:b})}let s=i.source||i.baseTexture||i;s&&"valid"in s&&s.valid===!1&&typeof s.once=="function"&&await new Promise(b=>s.once("loaded",b));let r=n.gameplay.character_sheet||{},l=(m=r.cols)!=null?m:3,d=(g=r.rows)!=null?g:2,p=(v=(A=(x=s==null?void 0:s.width)!=null?x:s==null?void 0:s.pixelWidth)!=null?A:(T=s==null?void 0:s.baseTexture)==null?void 0:T.width)!=null?v:i.width,f=(C=(S=(E=s==null?void 0:s.height)!=null?E:s==null?void 0:s.pixelHeight)!=null?S:(k=s==null?void 0:s.baseTexture)==null?void 0:k.height)!=null?C:i.height,u=p/l,c=f/d;if(!Number.isFinite(u)||!Number.isFinite(c)||u<=0||c<=0||!s)return[i];let h=[];for(let b=0;b<d;b++)for(let D=0;D<l;D++)h.push(new P.Texture({source:s,frame:new P.Rectangle(D*u,b*c,u,c)}));return h.length>2&&h.push(...h.slice(1,-1).reverse()),console.log("[character] frames prepared",{count:h.length}),h}function ca(e,t){var p,f,u,c;let n=t.gameplay.brush||{},a=new P.Color(t.theme.brush_color||"#ffffff").toNumber(),o=(p=n.radius)!=null?p:20,i=(f=n.inner_radius)!=null?f:15,s=(u=n.alpha)!=null?u:.8,r=(c=n.inner_alpha)!=null?c:.3,l=n.inner_color||"#ffffff",d=new P.Graphics;return d.circle(0,0,o).fill({color:a,alpha:s}),d.circle(0,0,i).fill({color:new P.Color(l).toNumber(),alpha:r}),e.renderer.generateTexture(d)}0&&(module.exports={GameObject,GameObjectManager,Renderer,Transform,animateHandClick,animatePanelEntrance,createBrushTexture,createEndGamePanel,createHandTutorial,createPixiBase,createTutorialLabel,getRegisteredFontIds,loadAsset,loadCharacterFrames,registerFont,resolveFont,resolveFontWeight,updateHandAnimation});
@@ -0,0 +1 @@
1
+ import{b as lt}from"../chunk-CJIYY5BP.mjs";import{Application as Pn}from"pixi.js";async function Mn(e,t={}){let a=new Pn;await a.init({resizeTo:e,autoDensity:!0,resolution:window.devicePixelRatio||1,antialias:!0,backgroundAlpha:0});let o=n=>{var c,d,p,u,h,l;let i=(p=(d=(c=n==null?void 0:n.width)!=null?c:e.clientWidth)!=null?d:window.innerWidth)!=null?p:320,r=(l=(h=(u=n==null?void 0:n.height)!=null?u:e.clientHeight)!=null?h:window.innerHeight)!=null?l:480;a.renderer.resize(i,r)};e.innerHTML="",e.style.position="relative",e.style.display="flex",e.style.flexDirection="column",e.style.alignItems="stretch",e.style.width="100%",e.style.height="100dvh",e.style.minHeight="100vh",e.style.maxWidth="100%",e.style.maxHeight="100%",e.style.boxSizing="border-box",e.style.overflow="hidden",t.background&&(e.style.background=t.background),e.appendChild(a.canvas);let s=document.createElement("div");return s.setAttribute("style",["position:absolute","inset:0","display:flex","flex-direction:column","align-items:center","justify-content:flex-start","gap:12px","pointer-events:none","font-family:Arial,sans-serif","padding:20px 16px","padding-bottom:clamp(24px, 6vh, 96px)","box-sizing:border-box","z-index:2"].join(";")),e.appendChild(s),lt.on("pause",()=>{a.ticker.stop(),document.querySelectorAll("audio").forEach(i=>i.pause())}),lt.on("resume",()=>{a.ticker.start(),document.querySelectorAll("audio").forEach(i=>{i.currentTime>0&&!i.ended&&i.play().catch(()=>{})})}),lt.on("resize",({payload:n})=>{o({width:n==null?void 0:n.width,height:n==null?void 0:n.height})}),window.addEventListener("resize",()=>o()),requestAnimationFrame(()=>o()),{app:a,stage:a.stage,overlay:s,applySize:o}}var ct=class{constructor(t){var a,o;this.position=(t==null?void 0:t.position)||{x:0,y:0},this.scale=(a=t==null?void 0:t.scale)!=null?a:1,this.rotation=(o=t==null?void 0:t.rotation)!=null?o:0}update(t){t.position!==void 0&&(this.position={...t.position}),t.scale!==void 0&&(this.scale=t.scale),t.rotation!==void 0&&(this.rotation=t.rotation)}syncToPixi(t){t.position.set(this.position.x,this.position.y),t.scale.set(this.scale),t.rotation=this.rotation}},dt=class{constructor(t){var a,o,s,n;this.z_index=(a=t==null?void 0:t.z_index)!=null?a:0,this.alpha=(o=t==null?void 0:t.alpha)!=null?o:1,this.visible=(s=t==null?void 0:t.visible)!=null?s:!0,this.tint=(n=t==null?void 0:t.tint)!=null?n:null}update(t){t.z_index!==void 0&&(this.z_index=t.z_index),t.alpha!==void 0&&(this.alpha=t.alpha),t.visible!==void 0&&(this.visible=t.visible),t.tint!==void 0&&(this.tint=t.tint)}syncToPixi(t){t.zIndex=this.z_index,t.alpha=this.alpha,t.visible=this.visible,this.tint!==null&&"tint"in t&&(t.tint=this.tint)}},ht=class{constructor(t,a,o,s){this.instanceId=t,this.objectConfig=a,this.pixiObject=o,this._config=s,this.transform=new ct(s.transform),this.renderer=new dt(s.render),this.sync()}sync(){this.transform.syncToPixi(this.pixiObject),this.renderer.syncToPixi(this.pixiObject)}updateConfig(t){this._config=t,t.transform&&this.transform.update(t.transform),t.render&&this.renderer.update(t.render),this.sync()}getComponent(t){return this._config[t]}getPosition(){return{...this.transform.position}}setPosition(t,a){this.transform.position={x:t,y:a},this.transform.syncToPixi(this.pixiObject)}getAlpha(){return this.renderer.alpha}setAlpha(t){this.renderer.alpha=t,this.renderer.syncToPixi(this.pixiObject)}getVisible(){return this.renderer.visible}setVisible(t){this.renderer.visible=t,this.renderer.syncToPixi(this.pixiObject)}},gt=class{constructor(t){this.objects=new Map;this.config=t}create(t,a){let o=this.config.objects.get(t);if(!o)throw new Error(`Object config not found: ${t}`);let s=new ht(t,o.object_config||t,a,o);return this.objects.set(t,s),s}get(t){return this.objects.get(t)}updateConfig(t){this.config=t;for(let[a,o]of this.objects.entries()){let s=t.objects.get(a);s&&o.updateConfig(s)}}getAll(){return Array.from(this.objects.values())}remove(t){this.objects.delete(t)}clear(){this.objects.clear()}};var pt={"brand.primary":{family:"Arial, sans-serif",weight:"400"},"brand.warning":{family:"Arial Black, Arial, sans-serif",weight:"900"},"brand.heading":{family:"Arial Black, Arial, sans-serif",weight:"800"},"brand.body":{family:"Arial, sans-serif",weight:"400"}};function X(e){let t=pt[e];return t?t.family:(console.warn(`Font ID "${e}" not found in registry, using fallback`),"Arial, sans-serif")}function q(e){var a;let t=pt[e];return t&&(a=t.weight)!=null?a:"400"}function En(e,t){pt[e]=t}function Dn(){return Object.keys(pt)}import{Container as Yn,Graphics as ut,Text as tt,Sprite as Le,Texture as _t}from"pixi.js";function kn(e,t,a){var Dt,Yt,kt,zt,Ht,Wt,Bt,Rt,Lt,Gt,Nt,Xt,qt,$t,Vt,jt,Jt,Kt,Qt,Zt,Ot,It,Ut,te,ee,ne,ae,se,ie,oe,re,le,ce,de,he,pe,ue,me,fe,ye,ge,_e,be,xe,we,Te,Ce,Se,ve,Ae,Fe,Pe,Me,Ee,De,Ye,ke,ze,He,We;let o=e.objects.get("ui_endgame_1"),s=e.objects.get("ui_endgame_logo_1"),n=e.objects.get("ui_endgame_title_1"),i=e.objects.get("ui_endgame_subtitle_1"),r=e.objects.get("ui_endgame_footer_1"),c=e.objects.get("ui_endgame_cta_1"),d=e.objects.get("ui_endgame_cta_hint_1"),p=e.objects.get("ui_endgame_hand_1"),u=e.engine.runtime||{},h=new Yn;h.alpha=0,h.visible=!1;let l=((Dt=o==null?void 0:o.gameplay)==null?void 0:Dt.tuning)||{},m=(o==null?void 0:o.render)||{},f=(Yt=l.panel_width)!=null?Yt:320,y=(kt=l.panel_height)!=null?kt:400,_=(zt=l.panel_padding)!=null?zt:32,b=(Ht=l.panel_radius)!=null?Ht:24,C=m.background_color||l.panel_bg_color||"#1a0a0a",x=m.background_alpha!==void 0&&m.background_alpha!==null?m.background_alpha:(Wt=l.panel_bg_alpha)!=null?Wt:.98,S=m.border_color||l.panel_border_color||"#ffffff",v=(Bt=l.panel_border_width)!=null?Bt:2,w=(Rt=l.panel_border_alpha)!=null?Rt:.3,T=new ut;T.roundRect(-f/2,-y/2,f,y,b);let g=C.replace("#","");T.fill({color:parseInt(g,16),alpha:x});let A=S.replace("#","");T.stroke({color:parseInt(A,16),width:v,alpha:w});let M=new ut;M.roundRect(-f/2+2,-y/2+2,f-4,y-4,b-2),M.stroke({color:16777215,width:1,alpha:.1}),T.addChild(M),h.addChild(T);let B=((Lt=s==null?void 0:s.gameplay)==null?void 0:Lt.tuning)||{},$=(s==null?void 0:s.transform)||{},z=(Gt=B.logo_size)!=null?Gt:56,R=(Nt=$.scale)!=null?Nt:1,j=(qt=(Xt=$.offset)==null?void 0:Xt.y)!=null?qt:-130,qe=($t=B.logo_alpha)!=null?$t:1,F=new Le(a||_t.EMPTY);F.anchor.set(.5);let K=0,Q=0;if(a&&a!==_t.EMPTY){let P=a.frame,N=a.baseTexture;P&&P.width&&P.height?(K=P.width,Q=P.height):N&&N.width&&N.height?(K=N.width,Q=N.height):a.width&&a.height&&(K=a.width,Q=a.height)}if(K>0&&Q>0){let P=K/Q,N,yt;P>1?(N=z,yt=z/P):(yt=z,N=z*P);let Be=N/K,Re=yt/Q;Be*=R,Re*=R,F.scale.set(Be,Re)}else{let P=z/100*R;F.scale.set(P)}F.position.set(0,j),F.alpha=0,F.visible=!0,F.targetAlpha=qe,F.baseScaleX=F.scale.x,F.baseScaleY=F.scale.y,h.addChild(F);let E=n==null?void 0:n.ui,H=((Vt=n==null?void 0:n.gameplay)==null?void 0:Vt.tuning)||{},$e=(E==null?void 0:E.text)||"MISSION ACCOMPLISHED",xt=(E==null?void 0:E.font)||"brand.heading",Ve=(E==null?void 0:E.fontSize)||24,je=(jt=E==null?void 0:E.letterSpacing)!=null?jt:1,et=(Jt=n==null?void 0:n.render)==null?void 0:Jt.tint,Je=typeof et=="string"?et:et?`#${et.toString(16).padStart(6,"0")}`:"#00FF88",wt=f-_*2-30,Ke=H.font_weight_override?H.font_weight_override:q(xt),V=new tt({text:$e,style:{fontFamily:X(xt),fontSize:Ve,fontWeight:Ke,fill:Je,align:"center",letterSpacing:je,wordWrap:!1,stroke:{color:H.stroke_color||"#000000",width:(Kt=H.stroke_width)!=null?Kt:2},dropShadow:{color:H.shadow_color||"#000000",alpha:(Qt=H.shadow_alpha)!=null?Qt:.9,angle:(Zt=H.shadow_angle)!=null?Zt:0,blur:(Ot=H.shadow_blur)!=null?Ot:8,distance:(It=H.shadow_distance)!=null?It:4}}});if(V.width>wt){let P=wt/V.width;V.scale.set(P)}V.anchor.set(.5);let Tt=(Ut=l.title_offset_y)!=null?Ut:30,Qe=Tt+((te=H.animation_start_y_offset)!=null?te:-20);V.position.set(0,Qe),V.alpha=0,h.addChild(V);let D=i==null?void 0:i.ui,Ct=((ee=i==null?void 0:i.gameplay)==null?void 0:ee.tuning)||{},Ze=(D==null?void 0:D.text)||"RESCUE COMPLETE",St=(D==null?void 0:D.font)||"brand.body",Oe=(D==null?void 0:D.fontSize)||11,Ie=(ne=D==null?void 0:D.letterSpacing)!=null?ne:.5,nt=(ae=i==null?void 0:i.render)==null?void 0:ae.tint,Ue=typeof nt=="string"?nt:nt?`#${nt.toString(16).padStart(6,"0")}`:"#B0B0B0",Z=new tt({text:Ze,style:{fontFamily:X(St),fontSize:Oe,fontWeight:q(St),fill:Ue,align:"center",letterSpacing:Ie}});Z.anchor.set(.5);let tn=Tt+20+((se=Ct.animation_start_y_offset)!=null?se:-10);Z.position.set(0,tn),Z.alpha=0,Z.animationStartYOffset=(ie=Ct.animation_start_y_offset)!=null?ie:-10,h.addChild(Z);let L=r==null?void 0:r.ui,vt=((oe=r==null?void 0:r.gameplay)==null?void 0:oe.tuning)||{},en=(L==null?void 0:L.text)||((re=u.ui)==null?void 0:re.cta_hint)||"Continue the adventure!",At=(L==null?void 0:L.font)||"brand.body",nn=(L==null?void 0:L.fontSize)||16,at=(le=r==null?void 0:r.render)==null?void 0:le.tint,an=typeof at=="string"?at:at?`#${at.toString(16).padStart(6,"0")}`:"#FFFFFF",sn=(ce=vt.max_width)!=null?ce:f-_*2,on=(de=vt.line_height)!=null?de:1.4,O=new tt({text:en,style:{fontFamily:X(At),fontSize:nn,fontWeight:q(At),fill:an,align:"center",wordWrap:!0,wordWrapWidth:sn,lineHeight:on}});O.anchor.set(.5);let rn=(he=l.footer_offset_y)!=null?he:120;O.position.set(0,rn);let ln=((pe=r==null?void 0:r.render)==null?void 0:pe.alpha)!==void 0&&((ue=r==null?void 0:r.render)==null?void 0:ue.alpha)!==null?r.render.alpha:1;O.alpha=0,O.targetAlpha=ln,h.addChild(O);let G=((me=c==null?void 0:c.gameplay)==null?void 0:me.tuning)||{},Y=c==null?void 0:c.ui,st=(fe=G.button_width)!=null?fe:260,it=(ye=G.button_height)!=null?ye:56,Ft=(ge=G.button_radius)!=null?ge:16,cn=G.button_bg_color||((_e=u.theme)==null?void 0:_e.cta_background)||"#ffb43b",dn=(be=G.button_bg_alpha)!=null?be:1,hn=G.button_border_color||"#ffffff",pn=(xe=G.button_border_width)!=null?xe:2,un=(we=G.button_border_alpha)!=null?we:.2,ot=(Te=l.cta_offset_y)!=null?Te:160,W=new ut;W.roundRect(-st/2,-it/2,st,it,Ft);let mn=cn.replace("#","");W.fill({color:parseInt(mn,16),alpha:dn});let fn=hn.replace("#","");W.stroke({color:parseInt(fn,16),width:pn,alpha:un});let mt=new ut;mt.roundRect(-st/2+2,-it/2+2,st-4,it-4,Ft-2),mt.stroke({color:0,width:1,alpha:.15}),W.addChild(mt),W.position.set(0,ot),W.alpha=0,W.eventMode="static",W.cursor="pointer",h.addChild(W);let yn=(Y==null?void 0:Y.text)||((Ce=u.ui)==null?void 0:Ce.cta_label_end)||"DOWNLOAD",Pt=(Y==null?void 0:Y.font)||"brand.heading",gn=(Y==null?void 0:Y.fontSize)||20,_n=G.button_text_color||((Se=u.theme)==null?void 0:Se.cta_text)||"#1a0a0a",U=new tt({text:yn,style:{fontFamily:X(Pt),fontSize:gn,fontWeight:q(Pt),fill:_n,align:"center",letterSpacing:(ve=Y==null?void 0:Y.letterSpacing)!=null?ve:1}});U.anchor.set(.5),U.position.set(0,ot),U.alpha=0,h.addChild(U);let k=d==null?void 0:d.ui,Mt=((Ae=d==null?void 0:d.gameplay)==null?void 0:Ae.tuning)||{},bn=(k==null?void 0:k.text)||"Play the full game",Et=(k==null?void 0:k.font)||"brand.body",xn=(k==null?void 0:k.fontSize)||11,wn=(Fe=k==null?void 0:k.letterSpacing)!=null?Fe:0,rt=(Pe=d==null?void 0:d.render)==null?void 0:Pe.tint,Tn=typeof rt=="string"?rt:rt?`#${rt.toString(16).padStart(6,"0")}`:"#CCCCCC",I=new tt({text:bn,style:{fontFamily:X(Et),fontSize:xn,fontWeight:q(Et),fill:Tn,align:"center",letterSpacing:wn}});I.anchor.set(.5);let Cn=ot+35+((Me=Mt.animation_start_y_offset)!=null?Me:5);I.position.set(0,Cn),I.alpha=0,I.animationStartYOffset=(Ee=Mt.animation_start_y_offset)!=null?Ee:5,h.addChild(I);let ft=((De=p==null?void 0:p.gameplay)==null?void 0:De.tuning)||{},Sn=(Ye=ft.hand_offset_x)!=null?Ye:50,vn=(ke=ft.hand_offset_y)!=null?ke:50,An=(He=(ze=p==null?void 0:p.transform)==null?void 0:ze.scale)!=null?He:.1875,Fn=(We=ft.hand_scale_multiplier)!=null?We:2.5,J=new Le(t||_t.EMPTY);return J.anchor.set(.5),J.scale.set(An*Fn),J.position.set(Sn,ot+vn),J.alpha=0,J.visible=!1,h.addChild(J),{panel:h,logo:F,title:V,subtitle:Z,footer:O,ctaButton:W,ctaText:U,ctaHint:I,hand:J}}function zn(e,t,a){var v,w,T,g,A,M,B,$;let o=t.objects.get("ui_endgame_1"),s=((v=o==null?void 0:o.gameplay)==null?void 0:v.tuning)||{},n=(w=s.animation_duration_ms)!=null?w:800,i=(T=s.fade_in_duration_ms)!=null?T:600,r=(g=s.scale_animation_intensity)!=null?g:.1,c=(A=s.logo_animation_delay_ms)!=null?A:100,d=(M=s.title_animation_delay_ms)!=null?M:200,p=(B=s.footer_animation_delay_ms)!=null?B:300,u=($=s.cta_animation_delay_ms)!=null?$:400,{panel:h,logo:l,title:m,subtitle:f,footer:y,ctaButton:_,ctaText:b,ctaHint:C}=e;h.visible=!0;let x=Date.now(),S=()=>{let z=Date.now()-x,R=Math.min(z/i,1),j=1-Math.pow(1-R,3);h.alpha=j,h.scale.set(1+r*(1-j)),R<1?requestAnimationFrame(S):(setTimeout(()=>Hn(l,n,t),c),setTimeout(()=>{Wn(m,n),setTimeout(()=>Bn(f,n),50)},d),setTimeout(()=>Rn(y,n),p),setTimeout(()=>{Ln(_,b,n),setTimeout(()=>Gn(C,n),50),a&&a()},u))};S()}function Hn(e,t,a){var h,l,m,f,y;let o=a==null?void 0:a.objects.get("ui_endgame_logo_1"),n=(l=(((h=o==null?void 0:o.gameplay)==null?void 0:h.tuning)||{}).animation_start_scale)!=null?l:1.2,i=Date.now(),r=e.alpha,c=(m=e.targetAlpha)!=null?m:1,d=(f=e.baseScaleX)!=null?f:e.scale.x,p=(y=e.baseScaleY)!=null?y:e.scale.y,u=()=>{let _=Date.now()-i,b=Math.min(_/t,1),C=1-Math.pow(1-b,2);e.alpha=r+(c-r)*C;let x=n+(1-n)*C;e.scale.set(d*x,p*x),b<1&&requestAnimationFrame(u)};u()}function Wn(e,t){let a=Date.now(),o=e.alpha,s=e.y,n=()=>{let i=Date.now()-a,r=Math.min(i/t,1),c=1-Math.pow(1-r,2);e.alpha=o+(1-o)*c,e.y=s-20*(1-c),r<1&&requestAnimationFrame(n)};n()}function Bn(e,t){var r;let a=Date.now(),o=e.alpha,s=e.position.y,n=s-((r=e.animationStartYOffset)!=null?r:-10),i=()=>{let c=Date.now()-a,d=Math.min(c/t,1),p=1-Math.pow(1-d,2);e.alpha=o+(1-o)*p,e.position.y=s+(n-s)*p,d<1&&requestAnimationFrame(i)};i()}function Rn(e,t){var i;let a=Date.now(),o=e.alpha,s=(i=e.targetAlpha)!=null?i:1,n=()=>{let r=Date.now()-a,c=Math.min(r/t,1),d=1-Math.pow(1-c,2);e.alpha=o+(s-o)*d,c<1&&requestAnimationFrame(n)};n()}function Ln(e,t,a){let o=Date.now(),s=e.alpha,n=()=>{let i=Date.now()-o,r=Math.min(i/a,1),c=1-Math.pow(1-r,2);e.alpha=s+(1-s)*c,t.alpha=s+(1-s)*c,e.scale.set(1+.1*(1-c)),r<1&&requestAnimationFrame(n)};n()}function Gn(e,t){var r;let a=Date.now(),o=e.alpha,s=e.position.y,n=s-((r=e.animationStartYOffset)!=null?r:5),i=()=>{let c=Date.now()-a,d=Math.min(c/t,1),p=1-Math.pow(1-d,2);e.alpha=o+(1-o)*p,e.position.y=s+(n-s)*p,d<1&&requestAnimationFrame(i)};i()}function Nn(e,t,a,o){var p,u,h,l;let s=a.objects.get("ui_endgame_hand_1"),n=((p=s==null?void 0:s.gameplay)==null?void 0:p.tuning)||{},i=(u=n.click_animation_duration_ms)!=null?u:600,r=(h=n.click_delay_ms)!=null?h:2e3,c=(l=n.click_repeat_delay_ms)!=null?l:3e3,d=()=>{var v,w;e.visible=!0,e.alpha=1;let m=e.x,f=e.y,y=(v=n.hand_offset_x)!=null?v:50,_=(w=n.hand_offset_y)!=null?w:50,b=t.x+y,C=t.y+_,x=Date.now(),S=()=>{var z,R,j;let T=Date.now()-x,g=Math.min(T/i,1),A=g<.5?2*g*g:1-Math.pow(-2*g+2,2)/2;e.x=m+(b-m)*A,e.y=f+(C-f)*A;let M=(R=(z=s==null?void 0:s.transform)==null?void 0:z.scale)!=null?R:.1875,B=(j=n.hand_scale_multiplier)!=null?j:2.5,$=M*B;e.scale.set($+$*.2*Math.sin(g*Math.PI)),g<1?requestAnimationFrame(S):(t.scale.set(.95),setTimeout(()=>{t.scale.set(1),o()},100),setTimeout(()=>{e.alpha=0,setTimeout(()=>{e.x=m,e.y=f,d()},c)},500))};S()};setTimeout(d,r)}import{Sprite as Xn,Text as qn}from"pixi.js";function $n(e,t,a){var p,u,h,l;let o=t.objects.get("hand_tutorial_1"),s=((p=o==null?void 0:o.gameplay)==null?void 0:p.tuning)||{},n=(o==null?void 0:o.render)||{},i=(o==null?void 0:o.transform)||{},r=new Xn(e);r.anchor.set(.5),r.alpha=0;let c=(u=s.hand_scale_multiplier)!=null?u:2.5,d=(h=i.scale)!=null?h:.1875;return r.scale.set(d*c),r.zIndex=(l=n.z_index)!=null?l:99999,r}function Vn(e){var m,f,y,_,b,C,x,S,v,w;let t=e.objects.get("ui_tutorial_1"),a=t==null?void 0:t.ui,o=(t==null?void 0:t.render)||{},s=((m=e.engine.runtime)==null?void 0:m.ui_styles)||{},n=((f=e.engine.runtime)==null?void 0:f.ui)||{},i=(a==null?void 0:a.text)||n.tutorial_label||n.label_text||"CLEAN!",r=(a==null?void 0:a.font)||"brand.warning",c=(a==null?void 0:a.fontSize)||s.label_font_size||52,d=(_=a==null?void 0:a.letterSpacing)!=null?_:(y=s.label_letter_spacing)!=null?y:-1,p=(a==null?void 0:a.align)||"center",u=o.tint,h=typeof u=="string"?u:u?`#${u.toString(16).padStart(6,"0")}`:s.label_fill||"#FFF1C1",l=new qn({text:i,style:{fontFamily:X(r),fontSize:c,fontWeight:q(r),fill:h,align:p,letterSpacing:d,stroke:{color:s.label_stroke||"#8B0000",width:2},dropShadow:{color:s.label_shadow_color||"#000000",alpha:(b=s.label_shadow_alpha)!=null?b:.8,angle:(C=s.label_shadow_angle)!=null?C:0,blur:(x=s.label_shadow_blur)!=null?x:4,distance:(S=s.label_shadow_distance)!=null?S:2}}});return l.anchor.set(.5,0),l.alpha=(v=o.alpha)!=null?v:0,l.visible=(w=o.visible)!=null?w:!0,l}function jn(e,t,a,o,s){var c,d,p,u,h,l,m;let n=s.objects.get("hand_tutorial_1"),i=((c=n==null?void 0:n.gameplay)==null?void 0:c.tuning)||{},r=(n==null?void 0:n.transform)||{};if(!o){let f=(d=i.animation_duration)!=null?d:1.5,y=a%f/f,_=y<.5?2*y*y:-1+(4-2*y)*y,b=(p=i.x_offset)!=null?p:0,C=(u=i.y_offset)!=null?u:0,x=(h=i.animation_range)!=null?h:40,S=x*.7,v=x,w=t.x+b+S,T=t.y+C+v,g=w+S,A=T+v;e.x=w+(g-w)*_,e.y=T+(A-T)*_;let M=(l=r.scale)!=null?l:.1875,B=(m=i.hand_scale_multiplier)!=null?m:2.5;e.scale.set(M*B)}}import{Graphics as Ge,Assets as Ne,Texture as Jn,Rectangle as Kn,Color as bt}from"pixi.js";async function Xe(e,t,a=16777215,o=!1){try{let n=(globalThis.INLINE_ASSETS||{})[e];return await Ne.load(n||e)}catch{console.warn(`Asset missing: ${e}. Using placeholder.`);let n=new Ge;return o?n.roundRect(0,0,60,120,10).fill({color:a}):(n.circle(30,30,30).fill({color:a}),n.rect(25,30,10,40).fill({color:a})),t.renderer.generateTexture(n)}}async function Qn(e,t,a){var f,y,_,b,C,x,S,v,w,T;let s=(globalThis.INLINE_ASSETS||{})[e]||e,n;try{n=await Ne.load(s),console.log("[character] loaded sheet",{path:e,sourceUsed:s})}catch(g){let M=(a.gameplay.character_sheet||{}).fallback_color||"#ff00ff";n=await Xe(e,t,new bt(M).toNumber(),!0),console.warn("[character] fallback loadAsset used",{path:e,err:g})}let i=n.source||n.baseTexture||n;i&&"valid"in i&&i.valid===!1&&typeof i.once=="function"&&await new Promise(g=>i.once("loaded",g));let r=a.gameplay.character_sheet||{},c=(f=r.cols)!=null?f:3,d=(y=r.rows)!=null?y:2,p=(x=(C=(_=i==null?void 0:i.width)!=null?_:i==null?void 0:i.pixelWidth)!=null?C:(b=i==null?void 0:i.baseTexture)==null?void 0:b.width)!=null?x:n.width,u=(T=(w=(S=i==null?void 0:i.height)!=null?S:i==null?void 0:i.pixelHeight)!=null?w:(v=i==null?void 0:i.baseTexture)==null?void 0:v.height)!=null?T:n.height,h=p/c,l=u/d;if(!Number.isFinite(h)||!Number.isFinite(l)||h<=0||l<=0||!i)return[n];let m=[];for(let g=0;g<d;g++)for(let A=0;A<c;A++)m.push(new Jn({source:i,frame:new Kn(A*h,g*l,h,l)}));return m.length>2&&m.push(...m.slice(1,-1).reverse()),console.log("[character] frames prepared",{count:m.length}),m}function Zn(e,t){var p,u,h,l;let a=t.gameplay.brush||{},o=new bt(t.theme.brush_color||"#ffffff").toNumber(),s=(p=a.radius)!=null?p:20,n=(u=a.inner_radius)!=null?u:15,i=(h=a.alpha)!=null?h:.8,r=(l=a.inner_alpha)!=null?l:.3,c=a.inner_color||"#ffffff",d=new Ge;return d.circle(0,0,s).fill({color:o,alpha:i}),d.circle(0,0,n).fill({color:new bt(c).toNumber(),alpha:r}),e.renderer.generateTexture(d)}export{ht as GameObject,gt as GameObjectManager,dt as Renderer,ct as Transform,Nn as animateHandClick,zn as animatePanelEntrance,Zn as createBrushTexture,kn as createEndGamePanel,$n as createHandTutorial,Mn as createPixiBase,Vn as createTutorialLabel,Dn as getRegisteredFontIds,Xe as loadAsset,Qn as loadCharacterFrames,En as registerFont,X as resolveFont,q as resolveFontWeight,jn as updateHandAnimation};
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "handler-playable-sdk",
3
+ "version": "0.1.0",
4
+ "description": "Handler Playable SDK v0.1 with contract-aligned surface (root sandbox, canonical event envelope).",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.mjs",
12
+ "require": "./dist/index.js"
13
+ },
14
+ "./pixi": {
15
+ "types": "./dist/pixi/index.d.ts",
16
+ "import": "./dist/pixi/index.mjs",
17
+ "require": "./dist/pixi/index.js"
18
+ }
19
+ },
20
+ "scripts": {
21
+ "build": "tsup src/index.ts src/pixi/index.ts --format cjs,esm --dts --clean --minify",
22
+ "lint": "eslint 'src/**/*.{ts,tsx}'",
23
+ "typecheck": "tsc --noEmit",
24
+ "prepublishOnly": "npm run build"
25
+ },
26
+ "author": "Handler",
27
+ "license": "MIT",
28
+ "publishConfig": {
29
+ "access": "public"
30
+ },
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "https://github.com/HandlerAIGames/handler-playable-sdk.git"
34
+ },
35
+ "files": [
36
+ "dist",
37
+ "LICENSE",
38
+ "README.md"
39
+ ],
40
+ "peerDependencies": {
41
+ "pixi.js": "^8.0.0"
42
+ },
43
+ "peerDependenciesMeta": {
44
+ "pixi.js": {
45
+ "optional": true
46
+ }
47
+ },
48
+ "devDependencies": {
49
+ "eslint": "^8.57.1",
50
+ "pixi.js": "^8.8.1",
51
+ "ts-node": "^10.9.2",
52
+ "tsup": "^8.4.0",
53
+ "typescript": "^5.7.2"
54
+ }
55
+ }