ecspresso
Version:
A minimal Entity-Component-System library for typescript and javascript.
5 lines (3 loc) • 10.5 kB
JavaScript
var n=((Q)=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(Q,{get:(V,J)=>(typeof require<"u"?require:V)[J]}):Q)(function(Q){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+Q+'" is not supported')});import{definePlugin as f}from"ecspresso";var T={neighbors(Q,V,J){let Z=V%Q.width,Y=(V-Z)/Q.width,X=0;if(Z>0)J[X++]=V-1;if(Z<Q.width-1)J[X++]=V+1;if(Y>0)J[X++]=V-Q.width;if(Y<Q.height-1)J[X++]=V+Q.width;return X},stepCost(Q,V,J){return Q.cells[J]??0},heuristic(Q,V,J){let Z=V%Q.width,Y=(V-Z)/Q.width,X=J%Q.width,q=(J-X)/Q.width;return Math.abs(Z-X)+Math.abs(Y-q)}},M=(Q)=>{let V=()=>{throw Error(`pathfinding: topology '${Q}' is not implemented in v1`)};return{neighbors:V,stepCost:V,heuristic:V}},I=Object.freeze({square4:T,square8:M("square8"),"hex-pointy":M("hex-pointy"),"hex-flat":M("hex-flat")});function C(Q){let V=Q.topology??"square4",J=Q.cellSize??32,Z=Q.originX??0,Y=Q.originY??0,{width:X,height:q}=Q,B=Q.defaultCost??1;if(!Number.isInteger(X)||X<=0)throw Error(`pathfinding: width must be a positive integer, got ${X}`);if(!Number.isInteger(q)||q<=0)throw Error(`pathfinding: height must be a positive integer, got ${q}`);if(J<=0)throw Error(`pathfinding: cellSize must be > 0, got ${J}`);if(B<0||B>255)throw Error(`pathfinding: defaultCost must be in 0–255, got ${B}`);let W=X*q,D=Q.cells??new Uint8Array(W).fill(B);if(D.length!==W)throw Error(`pathfinding: cells length ${D.length} does not match width*height ${W}`);let H=1/J;return{topology:V,width:X,height:q,cellSize:J,originX:Z,originY:Y,cells:D,worldToCell:(j,k)=>{let z=Math.floor((j-Z)*H),R=Math.floor((k-Y)*H),O=z<0?0:z>=X?X-1:z;return(R<0?0:R>=q?q-1:R)*X+O},cellToWorld:(j)=>{let k=j%X,z=(j-k)/X;return{x:Z+(k+0.5)*J,y:Y+(z+0.5)*J}},cellFromXY:(j,k)=>k*X+j,cellToXY:(j)=>{let k=j%X;return{x:k,y:(j-k)/X}}}}function s(Q){return{pathRequest:{target:{x:Q.x,y:Q.y}}}}function v(Q,V,J){let Z=Q.size;Q.size=Z+1;while(Z>0){let Y=Z-1>>1;if((Q.priorities[Y]??0)<=J)break;Q.ids[Z]=Q.ids[Y]??0,Q.priorities[Z]=Q.priorities[Y]??0,Z=Y}Q.ids[Z]=V,Q.priorities[Z]=J}function S(Q){let V=Q.ids[0]??-1,J=Q.size-1;if(Q.size=J,J<=0)return V;let Z=Q.ids[J]??0,Y=Q.priorities[J]??0,X=0,q=J>>1;while(X<q){let B=(X<<1)+1,W=B+1;if(W<J&&(Q.priorities[W]??0)<(Q.priorities[B]??0))B=W;if((Q.priorities[B]??0)>=Y)break;Q.ids[X]=Q.ids[B]??0,Q.priorities[X]=Q.priorities[B]??0,X=B}return Q.ids[X]=Z,Q.priorities[X]=Y,V}function K(Q,V){let J=1,Z=V;while((Q[Z]??-1)!==-1)J++,Z=Q[Z]??-1;let Y=Array(J);Z=V;for(let X=J-1;X>=0;X--)if(Y[X]=Z,X>0)Z=Q[Z]??-1;return Y}function L(Q,V,J,Z){let Y=Q.cells.length;if(V<0||V>=Y)return null;if(J<0||J>=Y)return null;let X=Z?.maxNodesExpanded??1e4,q=Z?.blockedCells,B=Z?.goalTolerance??0,W=I[Q.topology],D=new Float32Array(Y);D.fill(Number.POSITIVE_INFINITY);let H=new Int32Array(Y);H.fill(-1);let U=new Uint8Array(Y),$={ids:new Int32Array(Y),priorities:new Float32Array(Y),size:0},N=[];D[V]=0,v($,V,W.heuristic(Q,V,J));let G=0;while($.size>0){if(G>=X)return null;let j=S($);if(U[j])continue;if(U[j]=1,G++,W.heuristic(Q,j,J)<=B)return K(H,j);N.length=0;let k=W.neighbors(Q,j,N);for(let z=0;z<k;z++){let R=N[z]??-1;if(R<0||U[R])continue;if((Q.cells[R]??0)===0)continue;if(q&&q.has(R))continue;let E=(D[j]??Number.POSITIVE_INFINITY)+W.stepCost(Q,j,R);if(E<(D[R]??Number.POSITIVE_INFINITY))D[R]=E,H[R]=j,v($,R,E+W.heuristic(Q,R,J))}}return null}function r(Q){let{grid:V,systemGroup:J="ai",priority:Z=150,phase:Y="update",maxRequestsPerFrame:X=4,maxNodesExpanded:q=1e4}=Q;return f("pathfinding").withComponentTypes().withEventTypes().withResourceTypes().withLabels().withGroups().requires().install((B)=>{B.addResource("navGrid",V),B.addSystem("pathfinding-request").setPriority(Z).inPhase(Y).inGroup(J).addQuery("requests",{with:["pathRequest","worldTransform"]}).setProcess(({queries:W,ecs:D})=>{let H=D.getResource("navGrid"),U=0;for(let $ of W.requests){if(U>=X)break;U++;let{pathRequest:N,worldTransform:G}=$.components,j=H.worldToCell(G.x,G.y),k=H.worldToCell(N.target.x,N.target.y),z=L(H,j,k,{maxNodesExpanded:q});if(D.commands.removeComponent($.id,"pathRequest"),z===null){D.eventBus.publish("pathBlocked",{entityId:$.id});continue}let R=z.slice(1).map((b)=>H.cellToWorld(b));if(D.eventBus.publish("pathFound",{entityId:$.id,path:R}),R.length===0)continue;let O=D.getComponent($.id,"path");if(O)O.waypoints=R,O.currentIndex=0,D.markChanged($.id,"path");else D.addComponent($.id,"path",{waypoints:R,currentIndex:0});let E=R[0];if(!E)continue;let A=D.getComponent($.id,"moveTarget");if(A)A.x=E.x,A.y=E.y,D.markChanged($.id,"moveTarget");else D.addComponent($.id,"moveTarget",{x:E.x,y:E.y})}}),B.addSystem("pathfinding-waypoint-advance").inGroup(J).setEventHandlers({arriveAtTarget({data:W,ecs:D}){let H=D.getComponent(W.entityId,"path");if(!H)return;let U=H.currentIndex+1;if(U>=H.waypoints.length){D.commands.removeComponent(W.entityId,"path");return}H.currentIndex=U,D.markChanged(W.entityId,"path");let $=H.waypoints[U];if(!$)return;D.commands.addComponent(W.entityId,"moveTarget",{x:$.x,y:$.y})}})})}import{definePlugin as w}from"ecspresso";var m=2147483648,u=1073741824,x=536870912,_=536870911;function i(Q){return{id:(Q&_)>>>0,flipH:(Q&m)!==0,flipV:(Q&u)!==0,flipD:(Q&x)!==0}}function g(Q){return Uint32Array.from(Q)}function F(Q){if(!Q)return{};let V={};for(let J of Q)V[J.name]=J.value;return V}function y(Q){let{width:V,height:J,tileSize:Z,layers:Y,tilesets:X}=Q;if(!Number.isFinite(V)||V<=0)throw Error(`tilemap: width must be > 0, got ${V}`);if(!Number.isFinite(J)||J<=0)throw Error(`tilemap: height must be > 0, got ${J}`);if(!Number.isFinite(Z)||Z<=0)throw Error(`tilemap: tileSize must be > 0, got ${Z}`);if(X.length===0)throw Error("tilemap: at least one tileset is required");let q=V*J,B=Y.map((U)=>{let $=g(U.tiles);if($.length!==q)throw Error(`tilemap: layer "${U.name}" tile count ${$.length} does not match width*height ${q}`);return{name:U.name,tiles:$,parallax:U.parallax?{x:U.parallax.x,y:U.parallax.y}:{x:1,y:1},opacity:U.opacity??1,visible:U.visible??!0}}),W=X.map((U,$)=>{if(U.firstgid===void 0&&$>0)throw Error(`tilemap: runtime tileset at index ${$} ("${U.textureKey}") must specify an explicit firstgid`);return{textureKey:U.textureKey,columns:U.columns,tileWidth:U.tileWidth,tileHeight:U.tileHeight,firstgid:U.firstgid??1}}),D=new Map;if(Q.tileMetadata)for(let[U,$]of Object.entries(Q.tileMetadata))D.set(Number(U),{...$});let H=(Q.objectLayers??[]).map((U)=>({name:U.name,objects:U.objects.map(($)=>({...$,properties:{...$.properties}}))}));return P({width:V,height:J,tileWidth:Z,tileHeight:Z,layers:B,tilesets:W,tileMetadata:D,objectLayers:H})}function c(Q,V){let{width:J,height:Z,tilewidth:Y,tileheight:X}=Q,q=J*Z,B=Q.tilesets.map((U)=>{let $=V.tilesetTextures[U.image];if(!$)throw Error(`tilemap: no texture key registered for tileset image "${U.image}"`);return{textureKey:$,columns:U.columns,tileWidth:U.tilewidth,tileHeight:U.tileheight,firstgid:U.firstgid}}),W=new Map;for(let U of Q.tilesets){if(!U.tiles)continue;for(let $ of U.tiles)W.set(U.firstgid+$.id,F($.properties))}let D=[],H=[];for(let U of Q.layers)if(U.type==="tilelayer"){if(U.data.length!==q)throw Error(`tilemap: layer "${U.name}" data length ${U.data.length} does not match map ${q}`);D.push({name:U.name,tiles:Uint32Array.from(U.data),parallax:{x:U.parallaxx??1,y:U.parallaxy??1},opacity:U.opacity,visible:U.visible})}else H.push({name:U.name,objects:U.objects.map(($)=>({name:$.name??"",type:$.type??"",x:$.x,y:$.y,width:$.width??0,height:$.height??0,rotation:$.rotation??0,properties:F($.properties)}))});return P({width:J,height:Z,tileWidth:Y,tileHeight:X,layers:D,tilesets:B,tileMetadata:W,objectLayers:H})}function P(Q){let{width:V,height:J,tileWidth:Z,tileHeight:Y,layers:X,tilesets:q,tileMetadata:B,objectLayers:W}=Q,D=(U,$,N)=>{if(U<0||$<0||U>=V||$>=J)return!1;let G=$*V+U;for(let j=0;j<X.length;j++){let k=(X[j].tiles[G]??0)&_;if(k===0)continue;let z=B.get(k);if(z&&z[N]===!0)return!0}return!1},H=(U)=>{return B.get(U)?.walkable===!0?1:0};return{width:V,height:J,tileWidth:Z,tileHeight:Y,layers:X,tilesets:q,tileMetadata:B,objectLayers:W,tileToWorld(U,$){return{x:(U+0.5)*Z,y:($+0.5)*Y}},worldToTile(U,$){return{tx:Math.floor(U/Z),ty:Math.floor($/Y)}},getTile(U,$,N){let G=X[U];if(!G)return 0;if($<0||N<0||$>=V||N>=J)return 0;return(G.tiles[N*V+$]??0)&_},isSolid:(U,$)=>D(U,$,"solid"),isOpaque:(U,$)=>D(U,$,"blocksSight"),isWalkable:(U,$)=>D(U,$,"walkable"),buildNavGrid(U,$){let N=X[U];if(!N)throw Error(`tilemap: buildNavGrid — no layer at index ${U}`);let G=new Uint8Array(V*J),j=$??H;for(let k=0;k<G.length;k++){let z=(N.tiles[k]??0)&_,R=j(z)|0;G[k]=R<0?0:R>255?255:R}return C({width:V,height:J,cellSize:Z,cells:G})},getObjectLayer(U){return W.find(($)=>$.name===U)?.objects??[]},getObjects(U){let $=[];for(let N of W)for(let G of N.objects)if(G.type===U)$.push(G);return $}}}function h(Q){let V=[];for(let J=0;J<Q.height;J++){let Z=-1;for(let Y=0;Y<=Q.width;Y++){let X=Y<Q.width&&Q.isSolid(Y,J);if(X&&Z===-1)Z=Y;else if(!X&&Z!==-1)V.push({tx:Z,ty:J,tw:Y-Z,th:1}),Z=-1}}return V}function e(Q={}){let{collisionLayer:V,collidesWith:J}=Q;return w("tilemap").withComponentTypes().withResourceTypes().withLabels().withGroups().install((Z)=>{let Y=new Map,X=new Map,q=(H)=>{let U=X.get(H);if(!U)return;for(let $ of U)Z.removeEntity($);X.delete(H)},B=(H,U)=>{if(!V)return;let $=[];for(let N of h(U)){let G=(N.tx+N.tw/2)*U.tileWidth,j=(N.ty+N.th/2)*U.tileHeight,k={tilemapCollider:{dataKey:H},aabbCollider:{width:N.tw*U.tileWidth,height:N.th*U.tileHeight},collisionLayer:{layer:V,collidesWith:J??[]},localTransform:{x:G,y:j,rotation:0,scaleX:1,scaleY:1},worldTransform:{x:G,y:j,rotation:0,scaleX:1,scaleY:1}},z=Z.spawn(k);$.push(z.id)}if($.length>0)X.set(H,$)},W=(H,U)=>{return q(H),Y.set(H,U),B(H,U),U},D={entries:Y,registerRuntime(H,U){return W(H,y(U))},async registerAsset(H,U,$){let N=await Z.loadAsset(U);return W(H,c(N,$??{tilesetTextures:{}}))},get:(H)=>Y.get(H),has:(H)=>Y.has(H)};Z.addResource("tilemaps",D)})}function t(Q,V,J){return{tilemap:{dataKey:Q,layerIndex:V,tilesetKey:J?.tilesetKey,opacity:J?.opacity??1,parallax:J?.parallax??{x:1,y:1},cullingMode:J?.cullingMode??"viewport",cameraRef:J?.cameraRef,tintFn:J?.tintFn}}}export{c as parseTiledJSON,i as decodeGid,e as createTilemapPlugin,t as createTilemapLayer,y as createLoadedTilemap,_ as TILE_GID_MASK,u as TILE_FLIP_VERTICAL,m as TILE_FLIP_HORIZONTAL,x as TILE_FLIP_DIAGONAL};
//# debugId=2E25150012F8083364756E2164756E21
//# sourceMappingURL=tilemap.js.map