UNPKG

ecspresso

Version:

A minimal Entity-Component-System library for typescript and javascript.

5 lines (3 loc) 5.21 kB
var P=((j)=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(j,{get:(J,A)=>(typeof require<"u"?require:J)[A]}):j)(function(j){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+j+'" is not supported')});import{definePlugin as q}from"ecspresso";var T={neighbors(j,J,A){let H=J%j.width,K=(J-H)/j.width,D=0;if(H>0)A[D++]=J-1;if(H<j.width-1)A[D++]=J+1;if(K>0)A[D++]=J-j.width;if(K<j.height-1)A[D++]=J+j.width;return D},stepCost(j,J,A){return j.cells[A]??0},heuristic(j,J,A){let H=J%j.width,K=(J-H)/j.width,D=A%j.width,E=(A-D)/j.width;return Math.abs(H-D)+Math.abs(K-E)}},k=(j)=>{let J=()=>{throw Error(`pathfinding: topology '${j}' is not implemented in v1`)};return{neighbors:J,stepCost:J,heuristic:J}},z=Object.freeze({square4:T,square8:k("square8"),"hex-pointy":k("hex-pointy"),"hex-flat":k("hex-flat")});function f(j){let J=j.topology??"square4",A=j.cellSize??32,H=j.originX??0,K=j.originY??0,{width:D,height:E}=j,_=j.defaultCost??1;if(!Number.isInteger(D)||D<=0)throw Error(`pathfinding: width must be a positive integer, got ${D}`);if(!Number.isInteger(E)||E<=0)throw Error(`pathfinding: height must be a positive integer, got ${E}`);if(A<=0)throw Error(`pathfinding: cellSize must be > 0, got ${A}`);if(_<0||_>255)throw Error(`pathfinding: defaultCost must be in 0–255, got ${_}`);let U=D*E,Q=j.cells??new Uint8Array(U).fill(_);if(Q.length!==U)throw Error(`pathfinding: cells length ${Q.length} does not match width*height ${U}`);let $=1/A;return{topology:J,width:D,height:E,cellSize:A,originX:H,originY:K,cells:Q,worldToCell:(W,N)=>{let B=Math.floor((W-H)*$),Z=Math.floor((N-K)*$),X=B<0?0:B>=D?D-1:B;return(Z<0?0:Z>=E?E-1:Z)*D+X},cellToWorld:(W)=>{let N=W%D,B=(W-N)/D;return{x:H+(N+0.5)*A,y:K+(B+0.5)*A}},cellFromXY:(W,N)=>N*D+W,cellToXY:(W)=>{let N=W%D;return{x:N,y:(W-N)/D}}}}function y(j){return{pathRequest:{target:{x:j.x,y:j.y}}}}function O(j,J,A){let H=j.size;j.size=H+1;while(H>0){let K=H-1>>1;if((j.priorities[K]??0)<=A)break;j.ids[H]=j.ids[K]??0,j.priorities[H]=j.priorities[K]??0,H=K}j.ids[H]=J,j.priorities[H]=A}function C(j){let J=j.ids[0]??-1,A=j.size-1;if(j.size=A,A<=0)return J;let H=j.ids[A]??0,K=j.priorities[A]??0,D=0,E=A>>1;while(D<E){let _=(D<<1)+1,U=_+1;if(U<A&&(j.priorities[U]??0)<(j.priorities[_]??0))_=U;if((j.priorities[_]??0)>=K)break;j.ids[D]=j.ids[_]??0,j.priorities[D]=j.priorities[_]??0,D=_}return j.ids[D]=H,j.priorities[D]=K,J}function F(j,J){let A=1,H=J;while((j[H]??-1)!==-1)A++,H=j[H]??-1;let K=Array(A);H=J;for(let D=A-1;D>=0;D--)if(K[D]=H,D>0)H=j[H]??-1;return K}function b(j,J,A,H){let K=j.cells.length;if(J<0||J>=K)return null;if(A<0||A>=K)return null;let D=H?.maxNodesExpanded??1e4,E=H?.blockedCells,_=H?.goalTolerance??0,U=z[j.topology],Q=new Float32Array(K);Q.fill(Number.POSITIVE_INFINITY);let $=new Int32Array(K);$.fill(-1);let I=new Uint8Array(K),V={ids:new Int32Array(K),priorities:new Float32Array(K),size:0},M=[];Q[J]=0,O(V,J,U.heuristic(j,J,A));let R=0;while(V.size>0){if(R>=D)return null;let W=C(V);if(I[W])continue;if(I[W]=1,R++,U.heuristic(j,W,A)<=_)return F($,W);M.length=0;let N=U.neighbors(j,W,M);for(let B=0;B<N;B++){let Z=M[B]??-1;if(Z<0||I[Z])continue;if((j.cells[Z]??0)===0)continue;if(E&&E.has(Z))continue;let L=(Q[W]??Number.POSITIVE_INFINITY)+U.stepCost(j,W,Z);if(L<(Q[Z]??Number.POSITIVE_INFINITY))Q[Z]=L,$[Z]=W,O(V,Z,L+U.heuristic(j,Z,A))}}return null}function m(j){let{grid:J,systemGroup:A="ai",priority:H=150,phase:K="update",maxRequestsPerFrame:D=4,maxNodesExpanded:E=1e4}=j;return q("pathfinding").withComponentTypes().withEventTypes().withResourceTypes().withLabels().withGroups().requires().install((_)=>{_.addResource("navGrid",J),_.addSystem("pathfinding-request").setPriority(H).inPhase(K).inGroup(A).addQuery("requests",{with:["pathRequest","worldTransform"]}).setProcess(({queries:U,ecs:Q})=>{let $=Q.getResource("navGrid"),I=0;for(let V of U.requests){if(I>=D)break;I++;let{pathRequest:M,worldTransform:R}=V.components,W=$.worldToCell(R.x,R.y),N=$.worldToCell(M.target.x,M.target.y),B=b($,W,N,{maxNodesExpanded:E});if(Q.commands.removeComponent(V.id,"pathRequest"),B===null){Q.eventBus.publish("pathBlocked",{entityId:V.id});continue}let Z=B.slice(1).map((G)=>$.cellToWorld(G));if(Q.eventBus.publish("pathFound",{entityId:V.id,path:Z}),Z.length===0)continue;let X=Q.getComponent(V.id,"path");if(X)X.waypoints=Z,X.currentIndex=0,Q.markChanged(V.id,"path");else Q.addComponent(V.id,"path",{waypoints:Z,currentIndex:0});let L=Z[0];if(!L)continue;let Y=Q.getComponent(V.id,"moveTarget");if(Y)Y.x=L.x,Y.y=L.y,Q.markChanged(V.id,"moveTarget");else Q.addComponent(V.id,"moveTarget",{x:L.x,y:L.y})}}),_.addSystem("pathfinding-waypoint-advance").inGroup(A).setEventHandlers({arriveAtTarget({data:U,ecs:Q}){let $=Q.getComponent(U.entityId,"path");if(!$)return;let I=$.currentIndex+1;if(I>=$.waypoints.length){Q.commands.removeComponent(U.entityId,"path");return}$.currentIndex=I,Q.markChanged(U.entityId,"path");let V=$.waypoints[I];if(!V)return;Q.commands.addComponent(U.entityId,"moveTarget",{x:V.x,y:V.y})}})})}export{b as findPath,m as createPathfindingPlugin,y as createPathRequest,f as createNavGrid}; //# debugId=72F70AA348AB2CF664756E2164756E21 //# sourceMappingURL=pathfinding.js.map