@doodle-engine/core 0.0.18 ā 0.0.19
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/CHANGELOG.md +6 -0
- package/dist/assets/loader.d.ts +34 -0
- package/dist/assets/loader.d.ts.map +1 -0
- package/dist/assets/manifest.d.ts +20 -0
- package/dist/assets/manifest.d.ts.map +1 -0
- package/dist/core.cjs +2 -2
- package/dist/core.js +594 -492
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/assets.d.ts +45 -0
- package/dist/types/assets.d.ts.map +1 -0
- package/dist/types/entities.d.ts +38 -0
- package/dist/types/entities.d.ts.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Framework-agnostic asset loading primitives.
|
|
3
|
+
*
|
|
4
|
+
* The default implementation uses fetch + Cache API for browsers.
|
|
5
|
+
* Custom implementations can be provided for other environments
|
|
6
|
+
* (e.g., desktop wrappers where assets are already local).
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Asset loader interface.
|
|
10
|
+
*/
|
|
11
|
+
export interface AssetLoader {
|
|
12
|
+
/** Check if an asset is already cached/available */
|
|
13
|
+
isAvailable(path: string): Promise<boolean>;
|
|
14
|
+
/** Load a single asset, returns when ready */
|
|
15
|
+
load(path: string): Promise<void>;
|
|
16
|
+
/** Load multiple assets with progress callback */
|
|
17
|
+
loadMany(paths: string[], onProgress?: (loaded: number, total: number, current: string) => void): Promise<void>;
|
|
18
|
+
/** Get a URL that can be used in src attributes (may be blob URL or original) */
|
|
19
|
+
getUrl(path: string): string;
|
|
20
|
+
/** Preload assets that might be needed soon (non-blocking) */
|
|
21
|
+
prefetch(paths: string[]): void;
|
|
22
|
+
/** Clear all cached assets */
|
|
23
|
+
clear(): Promise<void>;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Create the default browser-based asset loader.
|
|
27
|
+
*
|
|
28
|
+
* Uses the Cache API when available (browser production).
|
|
29
|
+
* Falls back to plain fetch (warms browser cache) when Cache API is unavailable.
|
|
30
|
+
*
|
|
31
|
+
* @param version - Cache version string; changing this busts the cache
|
|
32
|
+
*/
|
|
33
|
+
export declare function createAssetLoader(version?: string): AssetLoader;
|
|
34
|
+
//# sourceMappingURL=loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/assets/loader.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,oDAAoD;IACpD,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAE3C,8CAA8C;IAC9C,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAEjC,kDAAkD;IAClD,QAAQ,CACN,KAAK,EAAE,MAAM,EAAE,EACf,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,GACpE,OAAO,CAAC,IAAI,CAAC,CAAA;IAEhB,iFAAiF;IACjF,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAA;IAE5B,8DAA8D;IAC9D,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAAA;IAE/B,8BAA8B;IAC9B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACvB;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,MAAY,GAAG,WAAW,CAiGpE"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Asset path extraction for manifest generation.
|
|
3
|
+
* Used by CLI at build time.
|
|
4
|
+
*/
|
|
5
|
+
import type { ContentRegistry } from '../types/registry';
|
|
6
|
+
import type { GameConfig } from '../types/entities';
|
|
7
|
+
/**
|
|
8
|
+
* Determine asset type from file extension.
|
|
9
|
+
*/
|
|
10
|
+
export declare function getAssetType(path: string): 'image' | 'audio' | 'video';
|
|
11
|
+
/**
|
|
12
|
+
* Extract all asset paths from a content registry and game config.
|
|
13
|
+
* Shell assets come first (tier 1); game assets are tier 2.
|
|
14
|
+
* Paths are deduplicated ā shell assets are never duplicated in game.
|
|
15
|
+
*/
|
|
16
|
+
export declare function extractAssetPaths(registry: ContentRegistry, config: GameConfig): {
|
|
17
|
+
shell: string[];
|
|
18
|
+
game: string[];
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=manifest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../src/assets/manifest.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AACxD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAEnD;;GAEG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAUtE;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,eAAe,EACzB,MAAM,EAAE,UAAU,GACjB;IAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,CAsFrC"}
|
package/dist/core.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var D=Object.defineProperty;var R=(t,e,n)=>e in t?D(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n;var E=(t,e,n)=>R(t,typeof e!="symbol"?e+"":e,n);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function L(t,e){switch(t.type){case"hasFlag":return V(t.flag,e);case"notFlag":return P(t.flag,e);case"hasItem":return $(t.itemId,e);case"variableEquals":return q(t.variable,t.value,e);case"variableGreaterThan":return F(t.variable,t.value,e);case"variableLessThan":return M(t.variable,t.value,e);case"atLocation":return W(t.locationId,e);case"questAtStage":return G(t.questId,t.stageId,e);case"characterAt":return j(t.characterId,t.locationId,e);case"characterInParty":return H(t.characterId,e);case"relationshipAbove":return Q(t.characterId,t.value,e);case"relationshipBelow":return J(t.characterId,t.value,e);case"timeIs":return U(t.startHour,t.endHour,e);case"itemAt":return B(t.itemId,t.locationId,e);case"roll":return _(t.min,t.max,t.threshold);default:return!1}}function v(t,e){return t.every(n=>L(n,e))}function V(t,e){return e.flags[t]===!0}function P(t,e){return e.flags[t]!==!0}function $(t,e){return e.inventory.includes(t)}function q(t,e,n){return n.variables[t]===e}function F(t,e,n){const r=n.variables[t];return typeof r=="number"&&r>e}function M(t,e,n){const r=n.variables[t];return typeof r=="number"&&r<e}function W(t,e){return e.currentLocation===t}function G(t,e,n){return n.questProgress[t]===e}function j(t,e,n){const r=n.characterState[t];return(r==null?void 0:r.location)===e}function H(t,e){const n=e.characterState[t];return(n==null?void 0:n.inParty)===!0}function Q(t,e,n){const r=n.characterState[t];return r!==void 0&&r.relationship>e}function J(t,e,n){const r=n.characterState[t];return r!==void 0&&r.relationship<e}function U(t,e,n){const r=n.currentTime.hour;return t<e?r>=t&&r<e:r>=t||r<e}function B(t,e,n){return n.itemLocations[t]===e}function _(t,e,n){return Math.floor(Math.random()*(e-t+1))+t>=n}function x(t,e){switch(t.type){case"setFlag":return z(t.flag,e);case"clearFlag":return Y(t.flag,e);case"setVariable":return K(t.variable,t.value,e);case"addVariable":return X(t.variable,t.value,e);case"addItem":return Z(t.itemId,e);case"removeItem":return ee(t.itemId,e);case"moveItem":return te(t.itemId,t.locationId,e);case"goToLocation":return ne(t.locationId,e);case"advanceTime":return re(t.hours,e);case"setQuestStage":return ae(t.questId,t.stageId,e);case"addJournalEntry":return ie(t.entryId,e);case"startDialogue":return se(t.dialogueId,e);case"endDialogue":return oe(e);case"setCharacterLocation":return le(t.characterId,t.locationId,e);case"addToParty":return ce(t.characterId,e);case"removeFromParty":return ue(t.characterId,e);case"setRelationship":return de(t.characterId,t.value,e);case"addRelationship":return he(t.characterId,t.value,e);case"setCharacterStat":return pe(t.characterId,t.stat,t.value,e);case"addCharacterStat":return ge(t.characterId,t.stat,t.value,e);case"setMapEnabled":return fe(t.enabled,e);case"playMusic":return e;case"playSound":return be(t.sound,e);case"notify":return me(t.message,e);case"playVideo":return ye(t.file,e);case"showInterlude":return Se(t.interludeId,e);case"roll":return ve(t.variable,t.min,t.max,e);default:return e}}function S(t,e){return t.reduce((n,r)=>x(r,n),e)}function z(t,e){return{...e,flags:{...e.flags,[t]:!0}}}function Y(t,e){return{...e,flags:{...e.flags,[t]:!1}}}function K(t,e,n){return{...n,variables:{...n.variables,[t]:e}}}function X(t,e,n){const r=n.variables[t],a=typeof r=="number"?r+e:e;return{...n,variables:{...n.variables,[t]:a}}}function Z(t,e){return e.inventory.includes(t)?e:{...e,inventory:[...e.inventory,t],itemLocations:{...e.itemLocations,[t]:"inventory"}}}function ee(t,e){return{...e,inventory:e.inventory.filter(n=>n!==t)}}function te(t,e,n){return{...n,inventory:n.inventory.filter(r=>r!==t),itemLocations:{...n.itemLocations,[t]:e}}}function ne(t,e){return{...e,currentLocation:t}}function re(t,e){const n=e.currentTime.hour+t,r=Math.floor(n/24),a=n%24;return{...e,currentTime:{day:e.currentTime.day+r,hour:a}}}function ae(t,e,n){return{...n,questProgress:{...n.questProgress,[t]:e}}}function ie(t,e){return e.unlockedJournalEntries.includes(t)?e:{...e,unlockedJournalEntries:[...e.unlockedJournalEntries,t]}}function se(t,e){return{...e,dialogueState:{dialogueId:t,nodeId:""}}}function oe(t){return{...t,dialogueState:null}}function le(t,e,n){const r=n.characterState[t];return r?{...n,characterState:{...n.characterState,[t]:{...r,location:e}}}:n}function ce(t,e){const n=e.characterState[t];return n?{...e,characterState:{...e.characterState,[t]:{...n,inParty:!0}}}:e}function ue(t,e){const n=e.characterState[t];return n?{...e,characterState:{...e.characterState,[t]:{...n,inParty:!1}}}:e}function de(t,e,n){const r=n.characterState[t];return r?{...n,characterState:{...n.characterState,[t]:{...r,relationship:e}}}:n}function he(t,e,n){const r=n.characterState[t];return r?{...n,characterState:{...n.characterState,[t]:{...r,relationship:r.relationship+e}}}:n}function pe(t,e,n,r){const a=r.characterState[t];return a?{...r,characterState:{...r.characterState,[t]:{...a,stats:{...a.stats,[e]:n}}}}:r}function ge(t,e,n,r){const a=r.characterState[t];if(!a)return r;const s=a.stats[e],i=typeof s=="number"?s+n:n;return{...r,characterState:{...r.characterState,[t]:{...a,stats:{...a.stats,[e]:i}}}}}function fe(t,e){return{...e,mapEnabled:t}}function me(t,e){return{...e,notifications:[...e.notifications,t]}}function be(t,e){return{...e,pendingSounds:[...e.pendingSounds,t]}}function ye(t,e){return{...e,pendingVideo:t}}function Se(t,e){return{...e,pendingInterlude:t}}function ve(t,e,n,r){const a=Math.floor(Math.random()*(n-e+1))+e;return{...r,variables:{...r.variables,[t]:a}}}function w(t,e,n){let r;if(t.startsWith("@")){const a=t.slice(1);r=e[a]??t}else r=t;return n&&r.includes("{")&&(r=r.replace(/\{(\w+)\}/g,(a,s)=>{const i=n[s];return i!==void 0?String(i):`{${s}}`})),r}function Ie(t,e){return n=>w(n,t,e)}function k(t,e){const n=e.locales[t.currentLocale]??{},r=m=>w(m,n,t.variables),a=Te(t.currentLocation,e,r),s=Ne(t,e,r),i=Ee(t,e,r),{dialogue:o,choices:l}=we(t,e,r),c=Ae(t,e,r),d=Ce(t,e,r),f=Le(t,e,r),b=xe(t,e,r),u=t.mapEnabled?ke(t,e,r):null,g=e.locations[t.currentLocation],h=(g==null?void 0:g.music)??"",p=(g==null?void 0:g.ambient)??"",y=t.notifications.map(r),N=[...t.pendingSounds],O=t.pendingVideo;let C=null;if(t.pendingInterlude){const m=e.interludes[t.pendingInterlude];m&&(C={id:m.id,background:m.background,banner:m.banner,music:m.music,voice:m.voice,sounds:m.sounds,scroll:m.scroll??!0,scrollSpeed:m.scrollSpeed??30,text:r(m.text)})}return{location:a,charactersHere:s,itemsHere:i,choices:l,dialogue:o,party:c,inventory:d,quests:f,journal:b,variables:{...t.variables},time:t.currentTime,map:u,music:h,ambient:p,notifications:y,pendingSounds:N,pendingVideo:O,pendingInterlude:C}}function Te(t,e,n){const r=e.locations[t];return r?{id:r.id,name:n(r.name),description:n(r.description),banner:r.banner}:{id:t,name:t,description:`Location not found: ${t}`,banner:""}}function Ne(t,e,n){const r=[];for(const[a,s]of Object.entries(t.characterState))if(s.location===t.currentLocation){const i=e.characters[a];i&&r.push({id:i.id,name:n(i.name),biography:n(i.biography),portrait:i.portrait,location:s.location,inParty:s.inParty,relationship:s.relationship,stats:s.stats})}return r}function Ee(t,e,n){const r=[];for(const[a,s]of Object.entries(t.itemLocations))if(s===t.currentLocation){const i=e.items[a];i&&r.push({id:i.id,name:n(i.name),description:n(i.description),icon:i.icon,image:i.image,stats:i.stats})}return r}function we(t,e,n){var l,c;if(!t.dialogueState)return{dialogue:null,choices:[]};const r=e.dialogues[t.dialogueState.dialogueId];if(!r)return{dialogue:null,choices:[]};const a=r.nodes.find(d=>{var f;return d.id===((f=t.dialogueState)==null?void 0:f.nodeId)});if(!a)return{dialogue:null,choices:[]};const s=a.speaker?n(((l=e.characters[a.speaker])==null?void 0:l.name)??a.speaker):"Narrator",i={speaker:a.speaker,speakerName:s,text:n(a.text),portrait:a.portrait??((c=e.characters[a.speaker??""])==null?void 0:c.portrait),voice:a.voice},o=a.choices.filter(d=>!d.conditions||d.conditions.length===0?!0:v(d.conditions,t)).map(d=>({id:d.id,text:n(d.text)}));return{dialogue:i,choices:o}}function Ae(t,e,n){const r=[];for(const[a,s]of Object.entries(t.characterState))if(s.inParty){const i=e.characters[a];i&&r.push({id:i.id,name:n(i.name),biography:n(i.biography),portrait:i.portrait,location:s.location,inParty:s.inParty,relationship:s.relationship,stats:s.stats})}return r}function Ce(t,e,n){return t.inventory.map(r=>{const a=e.items[r];return a?{id:a.id,name:n(a.name),description:n(a.description),icon:a.icon,image:a.image,stats:a.stats}:null}).filter(r=>r!==null)}function Le(t,e,n){const r=[];for(const[a,s]of Object.entries(t.questProgress)){const i=e.quests[a];if(!i)continue;const o=i.stages.find(l=>l.id===s);o&&r.push({id:i.id,name:n(i.name),description:n(i.description),currentStage:o.id,currentStageDescription:n(o.description)})}return r}function xe(t,e,n){return t.unlockedJournalEntries.map(r=>{const a=e.journalEntries[r];return a?{id:a.id,title:n(a.title),text:n(a.text),category:a.category}:null}).filter(r=>r!==null)}function ke(t,e,n){const r=Object.keys(e.maps);if(r.length===0)return null;const a=e.maps[r[0]];if(!a)return null;const s=a.locations.map(i=>{const o=e.locations[i.id];return{id:i.id,name:o?n(o.name):i.id,x:i.x,y:i.y,isCurrent:i.id===t.currentLocation}});return{id:a.id,name:n(a.name),image:a.image,scale:a.scale,locations:s}}class Oe{constructor(e,n){E(this,"registry");E(this,"state");this.registry=e,this.state=n}newGame(e){const n={};for(const[a,s]of Object.entries(this.registry.characters))n[a]={location:s.location,inParty:!1,relationship:0,stats:{...s.stats}};const r={};for(const[a,s]of Object.entries(this.registry.items))r[a]=s.location;return this.state={currentLocation:e.startLocation,currentTime:{...e.startTime},flags:{...e.startFlags},variables:{...e.startVariables},inventory:[...e.startInventory],questProgress:{},unlockedJournalEntries:[],playerNotes:[],dialogueState:null,characterState:n,itemLocations:r,mapEnabled:!0,notifications:[],pendingSounds:[],pendingVideo:null,pendingInterlude:null,currentLocale:"en"},this.checkTriggeredDialogues(),this.checkTriggeredInterludes(),this.buildSnapshotAndClearTransients()}loadGame(e){return this.state={...e.state},this.buildSnapshotAndClearTransients()}saveGame(){return{version:"1.0",timestamp:new Date().toISOString(),state:{...this.state}}}selectChoice(e){var i;if(!this.state.dialogueState)return this.buildSnapshotAndClearTransients();const n=this.registry.dialogues[this.state.dialogueState.dialogueId];if(!n)return this.buildSnapshotAndClearTransients();const r=n.nodes.find(o=>{var l;return o.id===((l=this.state.dialogueState)==null?void 0:l.nodeId)});if(!r)return this.buildSnapshotAndClearTransients();const a=r.choices.find(o=>o.id===e);if(!a)return this.buildSnapshotAndClearTransients();if(a.effects&&(this.state=S(a.effects,this.state)),((i=this.state.dialogueState)==null?void 0:i.nodeId)==="")return this.initDialogue(this.state.dialogueState.dialogueId);const s=n.nodes.find(o=>o.id===a.next);if(!s)return this.state={...this.state,dialogueState:null},this.buildSnapshotAndClearTransients();if(this.state={...this.state,dialogueState:{dialogueId:n.id,nodeId:s.id}},s.effects&&(this.state=S(s.effects,this.state)),s.choices.length===0){const o=this.resolveNextNode(s);o?this.state={...this.state,dialogueState:{dialogueId:n.id,nodeId:o}}:this.state={...this.state,dialogueState:null}}return this.buildSnapshotAndClearTransients()}talkTo(e){const n=this.registry.characters[e];return!n||!n.dialogue?this.buildSnapshotAndClearTransients():this.initDialogue(n.dialogue)}initDialogue(e){const n=this.registry.dialogues[e];if(!n)return this.buildSnapshotAndClearTransients();const r=n.nodes.find(a=>a.id===n.startNode);if(!r)return this.buildSnapshotAndClearTransients();if(this.state={...this.state,dialogueState:{dialogueId:n.id,nodeId:r.id}},r.effects&&(this.state=S(r.effects,this.state)),r.choices.length===0){const a=this.resolveNextNode(r);a?this.state={...this.state,dialogueState:{dialogueId:n.id,nodeId:a}}:this.state={...this.state,dialogueState:null}}return this.buildSnapshotAndClearTransients()}takeItem(e){return this.state.itemLocations[e]!==this.state.currentLocation?this.buildSnapshotAndClearTransients():(this.state={...this.state,inventory:[...this.state.inventory,e],itemLocations:{...this.state.itemLocations,[e]:"inventory"}},this.buildSnapshotAndClearTransients())}travelTo(e){if(!this.state.mapEnabled)return this.buildSnapshotAndClearTransients();const n=Object.keys(this.registry.maps);if(n.length===0)return this.buildSnapshotAndClearTransients();const r=this.registry.maps[n[0]];if(!r)return this.buildSnapshotAndClearTransients();const a=r.locations.find(f=>f.id===this.state.currentLocation),s=r.locations.find(f=>f.id===e);if(!a||!s)return this.buildSnapshotAndClearTransients();const i=Math.sqrt(Math.pow(s.x-a.x,2)+Math.pow(s.y-a.y,2)),o=Math.round(i*r.scale),l=this.state.currentTime.hour+o,c=Math.floor(l/24),d=l%24;return this.state={...this.state,currentLocation:e,dialogueState:null,currentTime:{day:this.state.currentTime.day+c,hour:d}},this.checkTriggeredDialogues(),this.checkTriggeredInterludes(),this.buildSnapshotAndClearTransients()}writeNote(e,n){const r={id:`note_${Date.now()}`,title:e,text:n};return this.state={...this.state,playerNotes:[...this.state.playerNotes,r]},this.buildSnapshotAndClearTransients()}deleteNote(e){return this.state={...this.state,playerNotes:this.state.playerNotes.filter(n=>n.id!==e)},this.buildSnapshotAndClearTransients()}setLocale(e){return this.state={...this.state,currentLocale:e},this.buildSnapshotAndClearTransients()}getSnapshot(){return this.buildSnapshotAndClearTransients()}buildSnapshotAndClearTransients(){const e=k(this.state,this.registry);return this.state={...this.state,notifications:[],pendingSounds:[],pendingVideo:null,pendingInterlude:null},e}resolveNextNode(e){if(e.conditionalNext&&e.conditionalNext.length>0){for(const n of e.conditionalNext)if(v([n.condition],this.state))return n.next}return e.next??null}checkTriggeredDialogues(){for(const e of Object.values(this.registry.dialogues)){if(e.triggerLocation!==this.state.currentLocation||e.conditions&&!v(e.conditions,this.state))continue;const n=e.nodes.find(r=>r.id===e.startNode);if(n){if(this.state={...this.state,dialogueState:{dialogueId:e.id,nodeId:n.id}},n.effects&&(this.state=S(n.effects,this.state)),n.choices.length===0){const r=this.resolveNextNode(n);r?this.state={...this.state,dialogueState:{dialogueId:e.id,nodeId:r}}:this.state={...this.state,dialogueState:null}}break}}}checkTriggeredInterludes(){for(const e of Object.values(this.registry.interludes))if(e.triggerLocation===this.state.currentLocation&&!(e.triggerConditions&&!v(e.triggerConditions,this.state))){this.state={...this.state,pendingInterlude:e.id},e.effects&&(this.state=S(e.effects,this.state));break}}}function De(t){return t.split(`
|
|
2
|
-
`).map((e,n)=>({original:e,lineNumber:n+1})).map(({original:e,lineNumber:n})=>{let r=e;const a=e.indexOf("#");if(a===-1)r=e;else{const o=e.match(/"[^"]*"/);if(o){const l=e.indexOf(o[0]),c=l+o[0].length;a<l?r=e.substring(0,a):a>=l&&a<c?r=e.substring(0,c)+e.substring(c).split("#")[0]:r=e.substring(0,a)}else r=e.substring(0,a)}const s=r.length-r.trimStart().length;return{line:r.trim(),lineNumber:n,indent:s}}).filter(e=>e.line.length>0)}function A(t){const e=t.trim();return e.startsWith("@")?e:e.startsWith('"')&&e.endsWith('"')?e.substring(1,e.length-1):e}function I(t){const e=t.trim().split(/\s+/),n=e[0];switch(n){case"hasFlag":return{type:"hasFlag",flag:e[1]};case"notFlag":return{type:"notFlag",flag:e[1]};case"hasItem":return{type:"hasItem",itemId:e[1]};case"variableEquals":return{type:"variableEquals",variable:e[1],value:isNaN(Number(e[2]))?e[2]:Number(e[2])};case"variableGreaterThan":return{type:"variableGreaterThan",variable:e[1],value:Number(e[2])};case"variableLessThan":return{type:"variableLessThan",variable:e[1],value:Number(e[2])};case"atLocation":return{type:"atLocation",locationId:e[1]};case"questAtStage":return{type:"questAtStage",questId:e[1],stageId:e[2]};case"characterAt":return{type:"characterAt",characterId:e[1],locationId:e[2]};case"characterInParty":return{type:"characterInParty",characterId:e[1]};case"relationshipAbove":return{type:"relationshipAbove",characterId:e[1],value:Number(e[2])};case"relationshipBelow":return{type:"relationshipBelow",characterId:e[1],value:Number(e[2])};case"timeIs":return{type:"timeIs",startHour:Number(e[1]),endHour:Number(e[2])};case"itemAt":return{type:"itemAt",itemId:e[1],locationId:e[2]};case"roll":return{type:"roll",min:Number(e[1]),max:Number(e[2]),threshold:Number(e[3])};default:throw new Error(`Unknown condition type: ${n}`)}}function T(t){const e=t.trim();if(e.startsWith("NOTIFY "))return{type:"notify",message:A(e.substring(7))};if(e.startsWith("MUSIC "))return{type:"playMusic",track:e.substring(6).trim()};if(e.startsWith("SOUND "))return{type:"playSound",sound:e.substring(6).trim()};if(e.startsWith("VIDEO "))return{type:"playVideo",file:e.substring(6).trim()};if(e.startsWith("INTERLUDE "))return{type:"showInterlude",interludeId:e.substring(10).trim()};if(e.startsWith("ROLL ")){const a=e.split(/\s+/);return{type:"roll",variable:a[1],min:Number(a[2]),max:Number(a[3])}}const n=e.split(/\s+/),r=n[0];switch(r){case"SET":if(n[1]==="flag")return{type:"setFlag",flag:n[2]};if(n[1]==="variable")return{type:"setVariable",variable:n[2],value:isNaN(Number(n[3]))?n[3]:Number(n[3])};if(n[1]==="questStage")return{type:"setQuestStage",questId:n[2],stageId:n[3]};if(n[1]==="characterLocation")return{type:"setCharacterLocation",characterId:n[2],locationId:n[3]};if(n[1]==="relationship")return{type:"setRelationship",characterId:n[2],value:Number(n[3])};if(n[1]==="characterStat")return{type:"setCharacterStat",characterId:n[2],stat:n[3],value:isNaN(Number(n[4]))?n[4]:Number(n[4])};if(n[1]==="mapEnabled")return{type:"setMapEnabled",enabled:n[2]==="true"};throw new Error(`Unknown SET effect: ${n[1]}`);case"CLEAR":if(n[1]==="flag")return{type:"clearFlag",flag:n[2]};throw new Error(`Unknown CLEAR effect: ${n[1]}`);case"ADD":if(n[1]==="variable")return{type:"addVariable",variable:n[2],value:Number(n[3])};if(n[1]==="item")return{type:"addItem",itemId:n[2]};if(n[1]==="journalEntry")return{type:"addJournalEntry",entryId:n[2]};if(n[1]==="toParty")return{type:"addToParty",characterId:n[2]};if(n[1]==="relationship")return{type:"addRelationship",characterId:n[2],value:Number(n[3])};if(n[1]==="characterStat")return{type:"addCharacterStat",characterId:n[2],stat:n[3],value:Number(n[4])};throw new Error(`Unknown ADD effect: ${n[1]}`);case"REMOVE":if(n[1]==="item")return{type:"removeItem",itemId:n[2]};if(n[1]==="fromParty")return{type:"removeFromParty",characterId:n[2]};throw new Error(`Unknown REMOVE effect: ${n[1]}`);case"MOVE":if(n[1]==="item")return{type:"moveItem",itemId:n[2],locationId:n[3]};throw new Error(`Unknown MOVE effect: ${n[1]}`);case"GOTO":if(n[1]==="location")return{type:"goToLocation",locationId:n[2]};throw new Error("GOTO should not be parsed as an effect");case"ADVANCE":if(n[1]==="time")return{type:"advanceTime",hours:Number(n[2])};throw new Error(`Unknown ADVANCE effect: ${n[1]}`);case"START":if(n[1]==="dialogue")return{type:"startDialogue",dialogueId:n[2]};throw new Error(`Unknown START effect: ${n[1]}`);case"END":if(n[1]==="dialogue")return{type:"endDialogue"};throw new Error("END should not be parsed as an effect");default:throw new Error(`Unknown effect keyword: ${r}`)}}function Re(t,e,n){const r=t[e],a=A(r.line.substring(7)),s=[],i=[];let o="",l=e+1;const c=r.indent;for(;l<t.length;){const u=t[l];if(u.line==="END"&&u.indent===c){l++;break}if(u.line.startsWith("REQUIRE ")){const g=u.line.substring(8).trim();s.push(I(g)),l++}else if(u.line.startsWith("GOTO ")){const g=u.line.substring(5).trim();if(g.startsWith("location ")){const h=g.substring(9).trim();i.push({type:"goToLocation",locationId:h}),i.push({type:"endDialogue"}),o=""}else o=g;l++}else u.line.includes(":")||i.push(T(u.line)),l++}const d=a.replace(/[@"]/g,"").replace(/[^a-z0-9]/gi,"_");return{choice:{id:`${n}_choice_${d.toLowerCase().substring(0,30)}`,text:a,conditions:s.length>0?s:void 0,effects:i.length>0?i:void 0,next:o||""},nextIndex:l}}function Ve(t,e){const n=t[e],r=n.line.substring(3).trim(),a=I(r);let s;const i=[];let o=e+1;const l=n.indent;for(;o<t.length;){const c=t[o];if(c.line==="END"&&c.indent===l){o++;break}c.line.startsWith("GOTO ")?(s=c.line.substring(5).trim(),o++):(i.push(T(c.line)),o++)}return{condition:a,next:s,effects:i,nextIndex:o}}function Pe(t,e){const r=t[e].line.substring(5).trim();let a=null,s="",i,o;const l=[],c=[],d=[];let f;const b=[];let u=e+1;for(;u<t.length;){const h=t[u];if(h.line.startsWith("NODE "))break;if(h.line.includes(":")&&!h.line.startsWith("VOICE")){const p=h.line.indexOf(":"),y=h.line.substring(0,p).trim(),N=h.line.substring(p+1).trim();y==="NARRATOR"?a=null:a=y.toLowerCase(),s=A(N),u++}else if(h.line.startsWith("VOICE "))i=h.line.substring(6).trim(),u++;else if(h.line.startsWith("PORTRAIT "))o=h.line.substring(9).trim(),u++;else if(h.line.startsWith("CHOICE ")){const p=Re(t,u,r);c.push(p.choice),u=p.nextIndex}else if(h.line.startsWith("IF ")){const p=Ve(t,u);p.next&&b.push({condition:p.condition,next:p.next}),d.push(...p.effects),u=p.nextIndex}else if(h.line.startsWith("GOTO ")){const p=h.line.substring(5).trim();if(p.startsWith("location ")){const y=p.substring(9).trim();d.push({type:"goToLocation",locationId:y}),d.push({type:"endDialogue"})}else f=p;u++}else d.push(T(h.line)),u++}const g={id:r,speaker:a,text:s,voice:i,portrait:o,conditions:l.length>0?l:void 0,choices:c,effects:d.length>0?d:void 0,next:f};return b.length>0&&(g.conditionalNext=b),{node:g,nextIndex:u}}function $e(t,e){const n=De(t);let r;const a=[],s=[];let i="",o=0;for(;o<n.length;){const l=n[o];if(l.line.startsWith("TRIGGER "))r=l.line.substring(8).trim(),o++;else if(l.line.startsWith("REQUIRE ")){const c=l.line.substring(8).trim();a.push(I(c)),o++}else if(l.line.startsWith("NODE ")){const c=Pe(n,o);s.push(c.node),i||(i=c.node.id),o=c.nextIndex}else throw new Error(`Unexpected token at line ${l.lineNumber}: ${l.line}`)}return{id:e,triggerLocation:r,conditions:a.length>0?a:void 0,startNode:i,nodes:s}}function qe(t,e){const n=t;window.doodle={setFlag(r){n.state.flags[r]=!0,e(),console.log(`š¾ Flag set: ${r}`)},clearFlag(r){delete n.state.flags[r],e(),console.log(`š¾ Flag cleared: ${r}`)},setVariable(r,a){n.state.variables[r]=a,e(),console.log(`š¾ Variable set: ${r} = ${a}`)},getVariable(r){const a=n.state.variables[r];return console.log(`š¾ Variable: ${r} = ${a}`),a},teleport(r){t.travelTo(r),e(),console.log(`š¾ Teleported to: ${r}`)},triggerDialogue(r){const a=n.registry.dialogues[r];if(!a){console.error(`š¾ Dialogue not found: ${r}`);return}const s=a.nodes.find(i=>i.id===a.startNode);if(!s){console.error(`š¾ Start node not found for dialogue: ${r}`);return}n.state.dialogueState={dialogueId:a.id,nodeId:s.id},e(),console.log(`š¾ Triggered dialogue: ${r}`)},setQuestStage(r,a){n.state.questProgress[r]=a,e(),console.log(`š¾ Quest stage set: ${r} -> ${a}`)},addItem(r){n.state.inventory.includes(r)?console.log(`š¾ Item already in inventory: ${r}`):(n.state.inventory.push(r),n.state.itemLocations[r]="inventory",e(),console.log(`š¾ Item added: ${r}`))},removeItem(r){const a=n.state.inventory.indexOf(r);a!==-1?(n.state.inventory.splice(a,1),delete n.state.itemLocations[r],e(),console.log(`š¾ Item removed: ${r}`)):console.log(`š¾ Item not in inventory: ${r}`)},inspect(){const r=t.getSnapshot();console.log("š¾ DOODLE ENGINE INSPECTOR š¾"),console.log(""),console.log("Current Location:",r.location.name),console.log("Current Time:",`Day ${r.time.day}, Hour ${r.time.hour}`),console.log("Flags:",Object.keys(n.state.flags)),console.log("Variables:",n.state.variables),console.log("Inventory:",r.inventory.map(a=>a.name)),console.log("Quest Progress:",n.state.questProgress),console.log(""),console.log("Available commands:"),console.log(" doodle.setFlag(flag)"),console.log(" doodle.clearFlag(flag)"),console.log(" doodle.setVariable(variable, value)"),console.log(" doodle.getVariable(variable)"),console.log(" doodle.teleport(locationId)"),console.log(" doodle.triggerDialogue(dialogueId)"),console.log(" doodle.setQuestStage(questId, stageId)"),console.log(" doodle.addItem(itemId)"),console.log(" doodle.removeItem(itemId)"),console.log(" doodle.inspectState()"),console.log(" doodle.inspectRegistry()"),console.log(""),console.log("š Docs: https://katbella.com/doodle-engine/")},inspectState(){return console.log("š¾ GAME STATE:",n.state),n.state},inspectRegistry(){return console.log("š¾ CONTENT REGISTRY:",n.registry),n.registry}},console.log("š¾ Doodle Engine dev tools enabled! Type `doodle.inspect()` to see available commands.\nš You can also check out the docs: https://katbella.com/doodle-engine/")}const Fe="0.0.1";exports.Engine=Oe;exports.VERSION=Fe;exports.applyEffect=x;exports.applyEffects=S;exports.buildSnapshot=k;exports.createResolver=Ie;exports.enableDevTools=qe;exports.evaluateCondition=L;exports.evaluateConditions=v;exports.parseCondition=I;exports.parseDialogue=$e;exports.parseEffect=T;exports.resolveText=w;
|
|
1
|
+
"use strict";var D=Object.defineProperty;var V=(n,e,t)=>e in n?D(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t;var N=(n,e,t)=>V(n,typeof e!="symbol"?e+"":e,t);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function $(n){var t;const e=((t=n.split(".").pop())==null?void 0:t.toLowerCase())??"";return["jpg","jpeg","png","gif","webp","svg","avif"].includes(e)?"image":["mp4","webm","ogv","mov"].includes(e)?"video":"audio"}function F(n,e){const t=new Set,r=new Set;if(e.shell){const{splash:o,loading:i,title:s,uiSounds:l}=e.shell;o&&(o.logo&&t.add(o.logo),o.background&&t.add(o.background),o.sound&&t.add(o.sound)),i&&(i.background&&t.add(i.background),i.music&&t.add(i.music)),s&&(s.logo&&t.add(s.logo),s.background&&t.add(s.background),s.music&&t.add(s.music)),l&&(l.click&&t.add(l.click),l.hover&&t.add(l.hover),l.menuOpen&&t.add(l.menuOpen),l.menuClose&&t.add(l.menuClose))}const a=o=>{o&&!t.has(o)&&r.add(o)};for(const o of Object.values(n.locations))a(o.banner),a(o.music),a(o.ambient);for(const o of Object.values(n.characters))a(o.portrait);for(const o of Object.values(n.items))a(o.icon),a(o.image);for(const o of Object.values(n.maps))a(o.image);for(const o of Object.values(n.interludes))if(a(o.background),a(o.banner),a(o.music),a(o.voice),o.sounds)for(const i of o.sounds)a(i);for(const o of Object.values(n.dialogues))for(const i of o.nodes)a(i.voice),a(i.portrait);return{shell:Array.from(t).filter(Boolean),game:Array.from(r).filter(Boolean)}}const L="doodle-engine-assets-";function P(n="1"){const e=`${L}${n}`,t=new Set,r=typeof caches<"u";async function a(){if(!r)return null;try{return await caches.open(e)}catch{return null}}async function o(i){if(t.has(i))return;const s=await a();if(s){if(await s.match(i)){t.add(i);return}const c=await fetch(i);if(!c.ok)throw new Error(`Failed to load asset: ${i} (${c.status})`);await s.put(i,c)}else{const l=await fetch(i);if(!l.ok)throw new Error(`Failed to load asset: ${i} (${l.status})`)}t.add(i)}return{async isAvailable(i){if(t.has(i))return!0;const s=await a();return s?await s.match(i)!==void 0:!1},async load(i){await o(i)},async loadMany(i,s){let l=0;const c=i.length;for(const u of i)s==null||s(l,c,u),await o(u),l++,s==null||s(l,c,u)},getUrl(i){return i},prefetch(i){for(const s of i)o(s).catch(()=>{})},async clear(){if(t.clear(),!!r)try{const i=await caches.keys();await Promise.all(i.filter(s=>s.startsWith(L)).map(s=>caches.delete(s)))}catch{}}}}function x(n,e){switch(n.type){case"hasFlag":return q(n.flag,e);case"notFlag":return M(n.flag,e);case"hasItem":return W(n.itemId,e);case"variableEquals":return j(n.variable,n.value,e);case"variableGreaterThan":return G(n.variable,n.value,e);case"variableLessThan":return H(n.variable,n.value,e);case"atLocation":return Q(n.locationId,e);case"questAtStage":return J(n.questId,n.stageId,e);case"characterAt":return U(n.characterId,n.locationId,e);case"characterInParty":return B(n.characterId,e);case"relationshipAbove":return _(n.characterId,n.value,e);case"relationshipBelow":return z(n.characterId,n.value,e);case"timeIs":return Y(n.startHour,n.endHour,e);case"itemAt":return X(n.itemId,n.locationId,e);case"roll":return K(n.min,n.max,n.threshold);default:return!1}}function S(n,e){return n.every(t=>x(t,e))}function q(n,e){return e.flags[n]===!0}function M(n,e){return e.flags[n]!==!0}function W(n,e){return e.inventory.includes(n)}function j(n,e,t){return t.variables[n]===e}function G(n,e,t){const r=t.variables[n];return typeof r=="number"&&r>e}function H(n,e,t){const r=t.variables[n];return typeof r=="number"&&r<e}function Q(n,e){return e.currentLocation===n}function J(n,e,t){return t.questProgress[n]===e}function U(n,e,t){const r=t.characterState[n];return(r==null?void 0:r.location)===e}function B(n,e){const t=e.characterState[n];return(t==null?void 0:t.inParty)===!0}function _(n,e,t){const r=t.characterState[n];return r!==void 0&&r.relationship>e}function z(n,e,t){const r=t.characterState[n];return r!==void 0&&r.relationship<e}function Y(n,e,t){const r=t.currentTime.hour;return n<e?r>=n&&r<e:r>=n||r<e}function X(n,e,t){return t.itemLocations[n]===e}function K(n,e,t){return Math.floor(Math.random()*(e-n+1))+n>=t}function k(n,e){switch(n.type){case"setFlag":return Z(n.flag,e);case"clearFlag":return ee(n.flag,e);case"setVariable":return te(n.variable,n.value,e);case"addVariable":return ne(n.variable,n.value,e);case"addItem":return re(n.itemId,e);case"removeItem":return ae(n.itemId,e);case"moveItem":return ie(n.itemId,n.locationId,e);case"goToLocation":return oe(n.locationId,e);case"advanceTime":return se(n.hours,e);case"setQuestStage":return le(n.questId,n.stageId,e);case"addJournalEntry":return ce(n.entryId,e);case"startDialogue":return ue(n.dialogueId,e);case"endDialogue":return de(e);case"setCharacterLocation":return he(n.characterId,n.locationId,e);case"addToParty":return pe(n.characterId,e);case"removeFromParty":return fe(n.characterId,e);case"setRelationship":return ge(n.characterId,n.value,e);case"addRelationship":return me(n.characterId,n.value,e);case"setCharacterStat":return be(n.characterId,n.stat,n.value,e);case"addCharacterStat":return ye(n.characterId,n.stat,n.value,e);case"setMapEnabled":return ve(n.enabled,e);case"playMusic":return e;case"playSound":return Ie(n.sound,e);case"notify":return Se(n.message,e);case"playVideo":return Te(n.file,e);case"showInterlude":return Ee(n.interludeId,e);case"roll":return Ne(n.variable,n.min,n.max,e);default:return e}}function v(n,e){return n.reduce((t,r)=>k(r,t),e)}function Z(n,e){return{...e,flags:{...e.flags,[n]:!0}}}function ee(n,e){return{...e,flags:{...e.flags,[n]:!1}}}function te(n,e,t){return{...t,variables:{...t.variables,[n]:e}}}function ne(n,e,t){const r=t.variables[n],a=typeof r=="number"?r+e:e;return{...t,variables:{...t.variables,[n]:a}}}function re(n,e){return e.inventory.includes(n)?e:{...e,inventory:[...e.inventory,n],itemLocations:{...e.itemLocations,[n]:"inventory"}}}function ae(n,e){return{...e,inventory:e.inventory.filter(t=>t!==n)}}function ie(n,e,t){return{...t,inventory:t.inventory.filter(r=>r!==n),itemLocations:{...t.itemLocations,[n]:e}}}function oe(n,e){return{...e,currentLocation:n}}function se(n,e){const t=e.currentTime.hour+n,r=Math.floor(t/24),a=t%24;return{...e,currentTime:{day:e.currentTime.day+r,hour:a}}}function le(n,e,t){return{...t,questProgress:{...t.questProgress,[n]:e}}}function ce(n,e){return e.unlockedJournalEntries.includes(n)?e:{...e,unlockedJournalEntries:[...e.unlockedJournalEntries,n]}}function ue(n,e){return{...e,dialogueState:{dialogueId:n,nodeId:""}}}function de(n){return{...n,dialogueState:null}}function he(n,e,t){const r=t.characterState[n];return r?{...t,characterState:{...t.characterState,[n]:{...r,location:e}}}:t}function pe(n,e){const t=e.characterState[n];return t?{...e,characterState:{...e.characterState,[n]:{...t,inParty:!0}}}:e}function fe(n,e){const t=e.characterState[n];return t?{...e,characterState:{...e.characterState,[n]:{...t,inParty:!1}}}:e}function ge(n,e,t){const r=t.characterState[n];return r?{...t,characterState:{...t.characterState,[n]:{...r,relationship:e}}}:t}function me(n,e,t){const r=t.characterState[n];return r?{...t,characterState:{...t.characterState,[n]:{...r,relationship:r.relationship+e}}}:t}function be(n,e,t,r){const a=r.characterState[n];return a?{...r,characterState:{...r.characterState,[n]:{...a,stats:{...a.stats,[e]:t}}}}:r}function ye(n,e,t,r){const a=r.characterState[n];if(!a)return r;const o=a.stats[e],i=typeof o=="number"?o+t:t;return{...r,characterState:{...r.characterState,[n]:{...a,stats:{...a.stats,[e]:i}}}}}function ve(n,e){return{...e,mapEnabled:n}}function Se(n,e){return{...e,notifications:[...e.notifications,n]}}function Ie(n,e){return{...e,pendingSounds:[...e.pendingSounds,n]}}function Te(n,e){return{...e,pendingVideo:n}}function Ee(n,e){return{...e,pendingInterlude:n}}function Ne(n,e,t,r){const a=Math.floor(Math.random()*(t-e+1))+e;return{...r,variables:{...r.variables,[n]:a}}}function w(n,e,t){let r;if(n.startsWith("@")){const a=n.slice(1);r=e[a]??n}else r=n;return t&&r.includes("{")&&(r=r.replace(/\{(\w+)\}/g,(a,o)=>{const i=t[o];return i!==void 0?String(i):`{${o}}`})),r}function we(n,e){return t=>w(t,n,e)}function O(n,e){const t=e.locales[n.currentLocale]??{},r=m=>w(m,t,n.variables),a=Ae(n.currentLocation,e,r),o=Ce(n,e,r),i=Le(n,e,r),{dialogue:s,choices:l}=xe(n,e,r),c=ke(n,e,r),u=Oe(n,e,r),g=Re(n,e,r),b=De(n,e,r),d=n.mapEnabled?Ve(n,e,r):null,f=e.locations[n.currentLocation],h=(f==null?void 0:f.music)??"",p=(f==null?void 0:f.ambient)??"",y=n.notifications.map(r),E=[...n.pendingSounds],R=n.pendingVideo;let C=null;if(n.pendingInterlude){const m=e.interludes[n.pendingInterlude];m&&(C={id:m.id,background:m.background,banner:m.banner,music:m.music,voice:m.voice,sounds:m.sounds,scroll:m.scroll??!0,scrollSpeed:m.scrollSpeed??30,text:r(m.text)})}return{location:a,charactersHere:o,itemsHere:i,choices:l,dialogue:s,party:c,inventory:u,quests:g,journal:b,variables:{...n.variables},time:n.currentTime,map:d,music:h,ambient:p,notifications:y,pendingSounds:E,pendingVideo:R,pendingInterlude:C}}function Ae(n,e,t){const r=e.locations[n];return r?{id:r.id,name:t(r.name),description:t(r.description),banner:r.banner}:{id:n,name:n,description:`Location not found: ${n}`,banner:""}}function Ce(n,e,t){const r=[];for(const[a,o]of Object.entries(n.characterState))if(o.location===n.currentLocation){const i=e.characters[a];i&&r.push({id:i.id,name:t(i.name),biography:t(i.biography),portrait:i.portrait,location:o.location,inParty:o.inParty,relationship:o.relationship,stats:o.stats})}return r}function Le(n,e,t){const r=[];for(const[a,o]of Object.entries(n.itemLocations))if(o===n.currentLocation){const i=e.items[a];i&&r.push({id:i.id,name:t(i.name),description:t(i.description),icon:i.icon,image:i.image,stats:i.stats})}return r}function xe(n,e,t){var l,c;if(!n.dialogueState)return{dialogue:null,choices:[]};const r=e.dialogues[n.dialogueState.dialogueId];if(!r)return{dialogue:null,choices:[]};const a=r.nodes.find(u=>{var g;return u.id===((g=n.dialogueState)==null?void 0:g.nodeId)});if(!a)return{dialogue:null,choices:[]};const o=a.speaker?t(((l=e.characters[a.speaker])==null?void 0:l.name)??a.speaker):"Narrator",i={speaker:a.speaker,speakerName:o,text:t(a.text),portrait:a.portrait??((c=e.characters[a.speaker??""])==null?void 0:c.portrait),voice:a.voice},s=a.choices.filter(u=>!u.conditions||u.conditions.length===0?!0:S(u.conditions,n)).map(u=>({id:u.id,text:t(u.text)}));return{dialogue:i,choices:s}}function ke(n,e,t){const r=[];for(const[a,o]of Object.entries(n.characterState))if(o.inParty){const i=e.characters[a];i&&r.push({id:i.id,name:t(i.name),biography:t(i.biography),portrait:i.portrait,location:o.location,inParty:o.inParty,relationship:o.relationship,stats:o.stats})}return r}function Oe(n,e,t){return n.inventory.map(r=>{const a=e.items[r];return a?{id:a.id,name:t(a.name),description:t(a.description),icon:a.icon,image:a.image,stats:a.stats}:null}).filter(r=>r!==null)}function Re(n,e,t){const r=[];for(const[a,o]of Object.entries(n.questProgress)){const i=e.quests[a];if(!i)continue;const s=i.stages.find(l=>l.id===o);s&&r.push({id:i.id,name:t(i.name),description:t(i.description),currentStage:s.id,currentStageDescription:t(s.description)})}return r}function De(n,e,t){return n.unlockedJournalEntries.map(r=>{const a=e.journalEntries[r];return a?{id:a.id,title:t(a.title),text:t(a.text),category:a.category}:null}).filter(r=>r!==null)}function Ve(n,e,t){const r=Object.keys(e.maps);if(r.length===0)return null;const a=e.maps[r[0]];if(!a)return null;const o=a.locations.map(i=>{const s=e.locations[i.id];return{id:i.id,name:s?t(s.name):i.id,x:i.x,y:i.y,isCurrent:i.id===n.currentLocation}});return{id:a.id,name:t(a.name),image:a.image,scale:a.scale,locations:o}}class $e{constructor(e,t){N(this,"registry");N(this,"state");this.registry=e,this.state=t}newGame(e){const t={};for(const[a,o]of Object.entries(this.registry.characters))t[a]={location:o.location,inParty:!1,relationship:0,stats:{...o.stats}};const r={};for(const[a,o]of Object.entries(this.registry.items))r[a]=o.location;return this.state={currentLocation:e.startLocation,currentTime:{...e.startTime},flags:{...e.startFlags},variables:{...e.startVariables},inventory:[...e.startInventory],questProgress:{},unlockedJournalEntries:[],playerNotes:[],dialogueState:null,characterState:t,itemLocations:r,mapEnabled:!0,notifications:[],pendingSounds:[],pendingVideo:null,pendingInterlude:null,currentLocale:"en"},this.checkTriggeredDialogues(),this.checkTriggeredInterludes(),this.buildSnapshotAndClearTransients()}loadGame(e){return this.state={...e.state},this.buildSnapshotAndClearTransients()}saveGame(){return{version:"1.0",timestamp:new Date().toISOString(),state:{...this.state}}}selectChoice(e){var i;if(!this.state.dialogueState)return this.buildSnapshotAndClearTransients();const t=this.registry.dialogues[this.state.dialogueState.dialogueId];if(!t)return this.buildSnapshotAndClearTransients();const r=t.nodes.find(s=>{var l;return s.id===((l=this.state.dialogueState)==null?void 0:l.nodeId)});if(!r)return this.buildSnapshotAndClearTransients();const a=r.choices.find(s=>s.id===e);if(!a)return this.buildSnapshotAndClearTransients();if(a.effects&&(this.state=v(a.effects,this.state)),((i=this.state.dialogueState)==null?void 0:i.nodeId)==="")return this.initDialogue(this.state.dialogueState.dialogueId);const o=t.nodes.find(s=>s.id===a.next);if(!o)return this.state={...this.state,dialogueState:null},this.buildSnapshotAndClearTransients();if(this.state={...this.state,dialogueState:{dialogueId:t.id,nodeId:o.id}},o.effects&&(this.state=v(o.effects,this.state)),o.choices.length===0){const s=this.resolveNextNode(o);s?this.state={...this.state,dialogueState:{dialogueId:t.id,nodeId:s}}:this.state={...this.state,dialogueState:null}}return this.buildSnapshotAndClearTransients()}talkTo(e){const t=this.registry.characters[e];return!t||!t.dialogue?this.buildSnapshotAndClearTransients():this.initDialogue(t.dialogue)}initDialogue(e){const t=this.registry.dialogues[e];if(!t)return this.buildSnapshotAndClearTransients();const r=t.nodes.find(a=>a.id===t.startNode);if(!r)return this.buildSnapshotAndClearTransients();if(this.state={...this.state,dialogueState:{dialogueId:t.id,nodeId:r.id}},r.effects&&(this.state=v(r.effects,this.state)),r.choices.length===0){const a=this.resolveNextNode(r);a?this.state={...this.state,dialogueState:{dialogueId:t.id,nodeId:a}}:this.state={...this.state,dialogueState:null}}return this.buildSnapshotAndClearTransients()}takeItem(e){return this.state.itemLocations[e]!==this.state.currentLocation?this.buildSnapshotAndClearTransients():(this.state={...this.state,inventory:[...this.state.inventory,e],itemLocations:{...this.state.itemLocations,[e]:"inventory"}},this.buildSnapshotAndClearTransients())}travelTo(e){if(!this.state.mapEnabled)return this.buildSnapshotAndClearTransients();const t=Object.keys(this.registry.maps);if(t.length===0)return this.buildSnapshotAndClearTransients();const r=this.registry.maps[t[0]];if(!r)return this.buildSnapshotAndClearTransients();const a=r.locations.find(g=>g.id===this.state.currentLocation),o=r.locations.find(g=>g.id===e);if(!a||!o)return this.buildSnapshotAndClearTransients();const i=Math.sqrt(Math.pow(o.x-a.x,2)+Math.pow(o.y-a.y,2)),s=Math.round(i*r.scale),l=this.state.currentTime.hour+s,c=Math.floor(l/24),u=l%24;return this.state={...this.state,currentLocation:e,dialogueState:null,currentTime:{day:this.state.currentTime.day+c,hour:u}},this.checkTriggeredDialogues(),this.checkTriggeredInterludes(),this.buildSnapshotAndClearTransients()}writeNote(e,t){const r={id:`note_${Date.now()}`,title:e,text:t};return this.state={...this.state,playerNotes:[...this.state.playerNotes,r]},this.buildSnapshotAndClearTransients()}deleteNote(e){return this.state={...this.state,playerNotes:this.state.playerNotes.filter(t=>t.id!==e)},this.buildSnapshotAndClearTransients()}setLocale(e){return this.state={...this.state,currentLocale:e},this.buildSnapshotAndClearTransients()}getSnapshot(){return this.buildSnapshotAndClearTransients()}buildSnapshotAndClearTransients(){const e=O(this.state,this.registry);return this.state={...this.state,notifications:[],pendingSounds:[],pendingVideo:null,pendingInterlude:null},e}resolveNextNode(e){if(e.conditionalNext&&e.conditionalNext.length>0){for(const t of e.conditionalNext)if(S([t.condition],this.state))return t.next}return e.next??null}checkTriggeredDialogues(){for(const e of Object.values(this.registry.dialogues)){if(e.triggerLocation!==this.state.currentLocation||e.conditions&&!S(e.conditions,this.state))continue;const t=e.nodes.find(r=>r.id===e.startNode);if(t){if(this.state={...this.state,dialogueState:{dialogueId:e.id,nodeId:t.id}},t.effects&&(this.state=v(t.effects,this.state)),t.choices.length===0){const r=this.resolveNextNode(t);r?this.state={...this.state,dialogueState:{dialogueId:e.id,nodeId:r}}:this.state={...this.state,dialogueState:null}}break}}}checkTriggeredInterludes(){for(const e of Object.values(this.registry.interludes))if(e.triggerLocation===this.state.currentLocation&&!(e.triggerConditions&&!S(e.triggerConditions,this.state))){this.state={...this.state,pendingInterlude:e.id},e.effects&&(this.state=v(e.effects,this.state));break}}}function Fe(n){return n.split(`
|
|
2
|
+
`).map((e,t)=>({original:e,lineNumber:t+1})).map(({original:e,lineNumber:t})=>{let r=e;const a=e.indexOf("#");if(a===-1)r=e;else{const s=e.match(/"[^"]*"/);if(s){const l=e.indexOf(s[0]),c=l+s[0].length;a<l?r=e.substring(0,a):a>=l&&a<c?r=e.substring(0,c)+e.substring(c).split("#")[0]:r=e.substring(0,a)}else r=e.substring(0,a)}const o=r.length-r.trimStart().length;return{line:r.trim(),lineNumber:t,indent:o}}).filter(e=>e.line.length>0)}function A(n){const e=n.trim();return e.startsWith("@")?e:e.startsWith('"')&&e.endsWith('"')?e.substring(1,e.length-1):e}function I(n){const e=n.trim().split(/\s+/),t=e[0];switch(t){case"hasFlag":return{type:"hasFlag",flag:e[1]};case"notFlag":return{type:"notFlag",flag:e[1]};case"hasItem":return{type:"hasItem",itemId:e[1]};case"variableEquals":return{type:"variableEquals",variable:e[1],value:isNaN(Number(e[2]))?e[2]:Number(e[2])};case"variableGreaterThan":return{type:"variableGreaterThan",variable:e[1],value:Number(e[2])};case"variableLessThan":return{type:"variableLessThan",variable:e[1],value:Number(e[2])};case"atLocation":return{type:"atLocation",locationId:e[1]};case"questAtStage":return{type:"questAtStage",questId:e[1],stageId:e[2]};case"characterAt":return{type:"characterAt",characterId:e[1],locationId:e[2]};case"characterInParty":return{type:"characterInParty",characterId:e[1]};case"relationshipAbove":return{type:"relationshipAbove",characterId:e[1],value:Number(e[2])};case"relationshipBelow":return{type:"relationshipBelow",characterId:e[1],value:Number(e[2])};case"timeIs":return{type:"timeIs",startHour:Number(e[1]),endHour:Number(e[2])};case"itemAt":return{type:"itemAt",itemId:e[1],locationId:e[2]};case"roll":return{type:"roll",min:Number(e[1]),max:Number(e[2]),threshold:Number(e[3])};default:throw new Error(`Unknown condition type: ${t}`)}}function T(n){const e=n.trim();if(e.startsWith("NOTIFY "))return{type:"notify",message:A(e.substring(7))};if(e.startsWith("MUSIC "))return{type:"playMusic",track:e.substring(6).trim()};if(e.startsWith("SOUND "))return{type:"playSound",sound:e.substring(6).trim()};if(e.startsWith("VIDEO "))return{type:"playVideo",file:e.substring(6).trim()};if(e.startsWith("INTERLUDE "))return{type:"showInterlude",interludeId:e.substring(10).trim()};if(e.startsWith("ROLL ")){const a=e.split(/\s+/);return{type:"roll",variable:a[1],min:Number(a[2]),max:Number(a[3])}}const t=e.split(/\s+/),r=t[0];switch(r){case"SET":if(t[1]==="flag")return{type:"setFlag",flag:t[2]};if(t[1]==="variable")return{type:"setVariable",variable:t[2],value:isNaN(Number(t[3]))?t[3]:Number(t[3])};if(t[1]==="questStage")return{type:"setQuestStage",questId:t[2],stageId:t[3]};if(t[1]==="characterLocation")return{type:"setCharacterLocation",characterId:t[2],locationId:t[3]};if(t[1]==="relationship")return{type:"setRelationship",characterId:t[2],value:Number(t[3])};if(t[1]==="characterStat")return{type:"setCharacterStat",characterId:t[2],stat:t[3],value:isNaN(Number(t[4]))?t[4]:Number(t[4])};if(t[1]==="mapEnabled")return{type:"setMapEnabled",enabled:t[2]==="true"};throw new Error(`Unknown SET effect: ${t[1]}`);case"CLEAR":if(t[1]==="flag")return{type:"clearFlag",flag:t[2]};throw new Error(`Unknown CLEAR effect: ${t[1]}`);case"ADD":if(t[1]==="variable")return{type:"addVariable",variable:t[2],value:Number(t[3])};if(t[1]==="item")return{type:"addItem",itemId:t[2]};if(t[1]==="journalEntry")return{type:"addJournalEntry",entryId:t[2]};if(t[1]==="toParty")return{type:"addToParty",characterId:t[2]};if(t[1]==="relationship")return{type:"addRelationship",characterId:t[2],value:Number(t[3])};if(t[1]==="characterStat")return{type:"addCharacterStat",characterId:t[2],stat:t[3],value:Number(t[4])};throw new Error(`Unknown ADD effect: ${t[1]}`);case"REMOVE":if(t[1]==="item")return{type:"removeItem",itemId:t[2]};if(t[1]==="fromParty")return{type:"removeFromParty",characterId:t[2]};throw new Error(`Unknown REMOVE effect: ${t[1]}`);case"MOVE":if(t[1]==="item")return{type:"moveItem",itemId:t[2],locationId:t[3]};throw new Error(`Unknown MOVE effect: ${t[1]}`);case"GOTO":if(t[1]==="location")return{type:"goToLocation",locationId:t[2]};throw new Error("GOTO should not be parsed as an effect");case"ADVANCE":if(t[1]==="time")return{type:"advanceTime",hours:Number(t[2])};throw new Error(`Unknown ADVANCE effect: ${t[1]}`);case"START":if(t[1]==="dialogue")return{type:"startDialogue",dialogueId:t[2]};throw new Error(`Unknown START effect: ${t[1]}`);case"END":if(t[1]==="dialogue")return{type:"endDialogue"};throw new Error("END should not be parsed as an effect");default:throw new Error(`Unknown effect keyword: ${r}`)}}function Pe(n,e,t){const r=n[e],a=A(r.line.substring(7)),o=[],i=[];let s="",l=e+1;const c=r.indent;for(;l<n.length;){const d=n[l];if(d.line==="END"&&d.indent===c){l++;break}if(d.line.startsWith("REQUIRE ")){const f=d.line.substring(8).trim();o.push(I(f)),l++}else if(d.line.startsWith("GOTO ")){const f=d.line.substring(5).trim();if(f.startsWith("location ")){const h=f.substring(9).trim();i.push({type:"goToLocation",locationId:h}),i.push({type:"endDialogue"}),s=""}else s=f;l++}else d.line.includes(":")||i.push(T(d.line)),l++}const u=a.replace(/[@"]/g,"").replace(/[^a-z0-9]/gi,"_");return{choice:{id:`${t}_choice_${u.toLowerCase().substring(0,30)}`,text:a,conditions:o.length>0?o:void 0,effects:i.length>0?i:void 0,next:s||""},nextIndex:l}}function qe(n,e){const t=n[e],r=t.line.substring(3).trim(),a=I(r);let o;const i=[];let s=e+1;const l=t.indent;for(;s<n.length;){const c=n[s];if(c.line==="END"&&c.indent===l){s++;break}c.line.startsWith("GOTO ")?(o=c.line.substring(5).trim(),s++):(i.push(T(c.line)),s++)}return{condition:a,next:o,effects:i,nextIndex:s}}function Me(n,e){const r=n[e].line.substring(5).trim();let a=null,o="",i,s;const l=[],c=[],u=[];let g;const b=[];let d=e+1;for(;d<n.length;){const h=n[d];if(h.line.startsWith("NODE "))break;if(h.line.includes(":")&&!h.line.startsWith("VOICE")){const p=h.line.indexOf(":"),y=h.line.substring(0,p).trim(),E=h.line.substring(p+1).trim();y==="NARRATOR"?a=null:a=y.toLowerCase(),o=A(E),d++}else if(h.line.startsWith("VOICE "))i=h.line.substring(6).trim(),d++;else if(h.line.startsWith("PORTRAIT "))s=h.line.substring(9).trim(),d++;else if(h.line.startsWith("CHOICE ")){const p=Pe(n,d,r);c.push(p.choice),d=p.nextIndex}else if(h.line.startsWith("IF ")){const p=qe(n,d);p.next&&b.push({condition:p.condition,next:p.next}),u.push(...p.effects),d=p.nextIndex}else if(h.line.startsWith("GOTO ")){const p=h.line.substring(5).trim();if(p.startsWith("location ")){const y=p.substring(9).trim();u.push({type:"goToLocation",locationId:y}),u.push({type:"endDialogue"})}else g=p;d++}else u.push(T(h.line)),d++}const f={id:r,speaker:a,text:o,voice:i,portrait:s,conditions:l.length>0?l:void 0,choices:c,effects:u.length>0?u:void 0,next:g};return b.length>0&&(f.conditionalNext=b),{node:f,nextIndex:d}}function We(n,e){const t=Fe(n);let r;const a=[],o=[];let i="",s=0;for(;s<t.length;){const l=t[s];if(l.line.startsWith("TRIGGER "))r=l.line.substring(8).trim(),s++;else if(l.line.startsWith("REQUIRE ")){const c=l.line.substring(8).trim();a.push(I(c)),s++}else if(l.line.startsWith("NODE ")){const c=Me(t,s);o.push(c.node),i||(i=c.node.id),s=c.nextIndex}else throw new Error(`Unexpected token at line ${l.lineNumber}: ${l.line}`)}return{id:e,triggerLocation:r,conditions:a.length>0?a:void 0,startNode:i,nodes:o}}function je(n,e){const t=n;window.doodle={setFlag(r){t.state.flags[r]=!0,e(),console.log(`š¾ Flag set: ${r}`)},clearFlag(r){delete t.state.flags[r],e(),console.log(`š¾ Flag cleared: ${r}`)},setVariable(r,a){t.state.variables[r]=a,e(),console.log(`š¾ Variable set: ${r} = ${a}`)},getVariable(r){const a=t.state.variables[r];return console.log(`š¾ Variable: ${r} = ${a}`),a},teleport(r){n.travelTo(r),e(),console.log(`š¾ Teleported to: ${r}`)},triggerDialogue(r){const a=t.registry.dialogues[r];if(!a){console.error(`š¾ Dialogue not found: ${r}`);return}const o=a.nodes.find(i=>i.id===a.startNode);if(!o){console.error(`š¾ Start node not found for dialogue: ${r}`);return}t.state.dialogueState={dialogueId:a.id,nodeId:o.id},e(),console.log(`š¾ Triggered dialogue: ${r}`)},setQuestStage(r,a){t.state.questProgress[r]=a,e(),console.log(`š¾ Quest stage set: ${r} -> ${a}`)},addItem(r){t.state.inventory.includes(r)?console.log(`š¾ Item already in inventory: ${r}`):(t.state.inventory.push(r),t.state.itemLocations[r]="inventory",e(),console.log(`š¾ Item added: ${r}`))},removeItem(r){const a=t.state.inventory.indexOf(r);a!==-1?(t.state.inventory.splice(a,1),delete t.state.itemLocations[r],e(),console.log(`š¾ Item removed: ${r}`)):console.log(`š¾ Item not in inventory: ${r}`)},inspect(){const r=n.getSnapshot();console.log("š¾ DOODLE ENGINE INSPECTOR š¾"),console.log(""),console.log("Current Location:",r.location.name),console.log("Current Time:",`Day ${r.time.day}, Hour ${r.time.hour}`),console.log("Flags:",Object.keys(t.state.flags)),console.log("Variables:",t.state.variables),console.log("Inventory:",r.inventory.map(a=>a.name)),console.log("Quest Progress:",t.state.questProgress),console.log(""),console.log("Available commands:"),console.log(" doodle.setFlag(flag)"),console.log(" doodle.clearFlag(flag)"),console.log(" doodle.setVariable(variable, value)"),console.log(" doodle.getVariable(variable)"),console.log(" doodle.teleport(locationId)"),console.log(" doodle.triggerDialogue(dialogueId)"),console.log(" doodle.setQuestStage(questId, stageId)"),console.log(" doodle.addItem(itemId)"),console.log(" doodle.removeItem(itemId)"),console.log(" doodle.inspectState()"),console.log(" doodle.inspectRegistry()"),console.log(""),console.log("š Docs: https://katbella.com/doodle-engine/")},inspectState(){return console.log("š¾ GAME STATE:",t.state),t.state},inspectRegistry(){return console.log("š¾ CONTENT REGISTRY:",t.registry),t.registry}},console.log("š¾ Doodle Engine dev tools enabled! Type `doodle.inspect()` to see available commands.\nš You can also check out the docs: https://katbella.com/doodle-engine/")}const Ge="0.0.1";exports.Engine=$e;exports.VERSION=Ge;exports.applyEffect=k;exports.applyEffects=v;exports.buildSnapshot=O;exports.createAssetLoader=P;exports.createResolver=we;exports.enableDevTools=je;exports.evaluateCondition=x;exports.evaluateConditions=S;exports.extractAssetPaths=F;exports.getAssetType=$;exports.parseCondition=I;exports.parseDialogue=We;exports.parseEffect=T;exports.resolveText=w;
|