SC CODE: // Copyright 2024. Civilware. All rights reserved.
// TELA Decentralized Web Document (TELA-DOC-1)
Function InitializePrivate() Uint64
10 IF init() == 0 THEN GOTO 30
20 RETURN 1
30 STORE("nameHdr", "data3.js")
31 STORE("descrHdr", "Data file 3")
32 STORE("iconURLHdr", "")
33 STORE("dURL", "data3.js")
34 STORE("docType", "TELA-JS-1")
35 STORE("subDir", "")
36 STORE("fileCheckC", "227054ffc819a797eb2f5f4e9a5a117c3c949b590809597eaa77825e778d0339")
37 STORE("fileCheckS", "1518b525a907bb3f93d53ba3a8a955ecf206fd1174903bd2278e34a9c28c323a")
100 RETURN 0
End Function
Function init() Uint64
10 IF EXISTS("owner") == 0 THEN GOTO 30
20 RETURN 1
30 STORE("owner", address())
50 STORE("docVersion", "1.0.0")
60 STORE("hash", HEX(TXID()))
70 STORE("likes", 0)
80 STORE("dislikes", 0)
100 RETURN 0
End Function
Function address() String
10 DIM s as String
20 LET s = SIGNER()
30 IF IS_ADDRESS_VALID(s) THEN GOTO 50
40 RETURN "anon"
50 RETURN ADDRESS_STRING(s)
End Function
Function Rate(r Uint64) Uint64
10 DIM addr as String
15 LET addr = address()
16 IF r < 100 && EXISTS(addr) == 0 && addr != "anon" THEN GOTO 30
20 RETURN 1
30 STORE(addr, ""+r+"_"+BLOCK_HEIGHT())
40 IF r < 50 THEN GOTO 70
50 STORE("likes", LOAD("likes")+1)
60 RETURN 0
70 STORE("dislikes", LOAD("dislikes")+1)
100 RETURN 0
End Function
/*class Camera{constructor(){this.x=0,this.y=0,this.z=5,this.pitch=0,this.yaw=0,this.zoom=1,this.fov=Math.PI/4,this.near=1,this.far=1e3,this.mode="lookAt",this.viewMatrix=new Float32Array([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]),this.lastHitX=0,this.lastHitY=0,this.lastHitz=0}getViewMatrix(){if("lookAt"===this.mode)return this.viewMatrix;if("topdown"===this.mode)return this.viewMatrix;if("barrel"===this.mode)return this.viewMatrix;const t=modelMatrix(-this.x,-this.y,-this.z),a=rotationX(-this.pitch),r=rotationY(-this.yaw);return multiply(a,multiply(r,t))}getProjectionMatrix(t){return perspectiveMatrix(this.fov*this.zoom,t,this.near,this.far)}}function perspectiveMatrix(t,a,r,e){const i=1/Math.tan(t/2),c=1/(r-e);return new Float32Array([i/a,0,0,0,0,i,0,0,0,0,(e+r)*c,-1,0,0,2*e*r*c,0])}function lookAt(t,a,r){const e=t[0],i=t[1],c=t[2];let o=a[0]-e,n=a[1]-i,s=a[2]-c;const m=Math.hypot(o,n,s);o/=m,n/=m,s/=m;let l=n*r[2]-s*r[1],x=s*r[0]-o*r[2],y=o*r[1]-n*r[0];const z=Math.hypot(l,x,y);l/=z,x/=z,y/=z;let h=x*s-y*n,p=y*o-l*s,u=l*n-x*o;return new Float32Array([l,h,-o,0,x,p,-n,0,y,u,-s,0,-(l*e+x*i+y*c),-(h*e+p*i+u*c),o*e+n*i+s*c,1])}function lerp(t,a,r){return t+(a-t)*r}function updateCamera(){const t=game.player;if("barrel"===camera.mode?input.zoomIn?camera.zoom=lerp(camera.zoom,.4,.1):input.zoomOut&&(camera.zoom=lerp(camera.zoom,1,.1)):camera.zoom=lerp(camera.zoom,1,.1),camera.zoom!==camera.lastZoom&&(camera.projectionMatrix=camera.getProjectionMatrix(aspect),camera.lastZoom=camera.zoom),"lookAt"===camera.mode){t.steerAngle||t.turnSpeed,t.speed;const a=15,r=a+.1*t.speed,e=t.forward[0],i=t.forward[2],c=hmap.sample(t.x+2*e,t.z+2*i),o=2*(hmap.sample(t.x,t.z)-c);t.pitchSmooth=lerp(t.pitchSmooth||0,o,.1);const n=[t.x,t.groundY+1.5+2*t.pitchSmooth,t.z],s=[n[0]-e*r,n[1]+5+.05*t.speed+3*t.pitchSmooth,n[2]-i*r],m={x:t.x,y:t.groundY+1.5,z:t.z},l={x:s[0]-m.x,y:s[1]-m.y,z:s[2]-m.z},x=Math.sqrt(l.x*l.x+l.y*l.y+l.z*l.z),y=raycastObjects(m,{x:l.x/x,y:l.y/x,z:l.z/x},2,x,!1);let z=0;if(y&&(z=x-y.dist),z>0){const t=Math.min(.2*z,1),r=s[0]+e*(t*a),c=s[2]+i*(t*a);camera.x=lerp(camera.x,r,.2),camera.z=lerp(camera.z,c,.2)}else camera.x=lerp(camera.x,s[0],.12),camera.y=lerp(camera.y,s[1],.12),camera.z=lerp(camera.z,s[2],.12);const h={x:camera.x,y:camera.y,z:camera.z};camera.viewMatrix=lookAt([camera.x,camera.y,camera.z],n,[0,1,0]);const p=t.barrel.transform.getMatrix();if(!p)return;const u=aimer(p),f=u.clip,d=u.hit;if(rayIntersectsTank(h,{x:d.x-camera.x,y:d.y-camera.y,z:d.z-camera.z},t))return void ctx.clearRect(0,0,uicanvas.width,uicanvas.height);const M=f[0]/f[3],w=f[1]/f[3],g=(.5*M+.5)*uicanvas.width,v=(1-(.5*w+.5))*uicanvas.height;if(f[3]<=0||M<-1||M>1||w<-1||w>1)return;return ctx.fillStyle="rgba(255,0,0,0.6)",ctx.beginPath(),ctx.arc(g,v,4,0,2*Math.PI),void ctx.fill()}if("topdown"===camera.mode){const a=[t.x,t.y+100,t.z],r=[t.x,t.y,t.z];camera.viewMatrix=lookAt(a,r,[0,0,-1]);const e=t.barrel.transform.getMatrix();if(!e)return;const i=aimer(e).clip,c=i[0]/i[3],o=i[1]/i[3],n=(.5*c+.5)*uicanvas.width,s=(1-(.5*o+.5))*uicanvas.height;if(i[3]<=0||c<-1||c>1||o<-1||o>1)return;return ctx.fillStyle="rgba(255,0,0,0.6)",ctx.beginPath(),ctx.arc(n,s,4,0,2*Math.PI),void ctx.fill()}if("barrel"===camera.mode){const t=game.player.barrel.transform.getMatrix();if(!t)return;const a=game.player.turret.transform.getMatrix(),r={x:t[8],y:t[9],z:t[10]},e={x:a[4],y:a[5],z:a[6]},i={x:a[12]+r.x,y:a[13]+r.y,z:a[14]+r.z},c=0,o=.6,n={x:i.x-r.x*c,y:i.y+o,z:i.z-r.z*c},s={x:i.x+50*r.x,y:i.y+50*r.y,z:i.z+50*r.z};camera.viewMatrix=lookAt([n.x,n.y,n.z],[s.x,s.y,s.z],[e.x,e.y,e.z]);const m=aimer(t).clip,l=m[0]/m[3],x=m[1]/m[3],y=(.5*l+.5)*uicanvas.width,z=(1-(.5*x+.5))*uicanvas.height;return ctx.fillStyle="rgba(255,0,0,0.6)",ctx.beginPath(),ctx.arc(y,z,4,0,2*Math.PI),void ctx.fill()}}function aimer(t){const a={x:t[8],y:t[9],z:t[10]},r={x:t[12]+a.x,y:t[13]+a.y,z:t[14]+a.z},e=raycastTerrain(r,a),i=raycastObjects(r,a,2,1e3,!0);let c=e&&i?e.dist<i.dist?e:i:e||i;c||(c={x:r.x+2e3*a.x,y:r.y+2e3*a.y,z:r.z+2e3*a.z}),isFinite(camera.lastHitX)||(camera.lastHitX=c.x),isFinite(camera.lastHitY)||(camera.lastHitY=c.y),isFinite(camera.lastHitZ)||(camera.lastHitZ=c.z),camera.lastHitX=lerp(camera.lastHitX,c.x,.2),camera.lastHitY=lerp(camera.lastHitY,c.y,.2),camera.lastHitZ=lerp(camera.lastHitZ,c.z,.2);const o=[camera.lastHitX,camera.lastHitY,camera.lastHitZ];return{clip:multiplyMat4Vec4(camera.projectionMatrix,multiplyMat4Vec4(camera.viewMatrix,[...o,1])),hit:c}}function resetCameraForLookAt(t){const a=15+.1*t.speed,r=[t.x,t.groundY+1.5,t.z],e=t.forward[0],i=t.forward[2],c=[r[0]-e*a,r[1]+5+.05*t.speed,r[2]-i*a];camera.x=c[0],camera.y=c[1],camera.z=c[2],camera.viewMatrix=lookAt(c,r,[0,1,0])}function raycastTerrain(t,a,r=2e3){const e=Math.abs(t.y-hmap.sample(t.x,t.z));let i=2;e<20&&(i=1),e<10&&(i=.5),e<5&&(i=.25);for(let e=0;e<r;e+=i){const r=t.x+a.x*e,i=t.z+a.z*e,c=hmap.sample(r,i);if(c>t.y+a.y*e)return{x:r,y:c,z:i,dist:e}}return null}function raycastObjects(t,a,r=2,e=2e3,i=!1,c=null){for(let o=0;o<e;o+=r){const r=t.x+a.x*o,e=t.y+a.y*o,n=t.z+a.z*o;if(i)for(const t of game.enemies)if(c!==t&&!t.dead&&t.obb&&pointInTankOBB(r,e,n,t))return{x:r,y:e,z:n,dist:o,t:t};for(const t of worldObjects)if("obb"===t.colliderType){if(pointInOBB(r,e,n,t))return{x:r,y:e,z:n,dist:o,obj:t}}else if("circle"===t.colliderType&&pointInCylinder(r,e,n,t))return{x:r,y:e,z:n,dist:o,obj:t}}return null}function rayIntersectsTank(t,a,r){const e=Math.sqrt(a.x*a.x+a.y*a.y+a.z*a.z);a.x/=e,a.y/=e,a.z/=e;const i=.25,c=e/i;let o=t.x,n=t.y,s=t.z;for(let t=0;t<c;t++)if(o+=a.x*i,n+=a.y*i,s+=a.z*i,pointInTankOBB(o,n,s,r))return!0;return!1}function updateBoundingBoxes(){for(const e of worldObjects)"circle"!==e.colliderType&&"obb"!==e.colliderType||updateObjectOBB(e);bbxsUpdated=!0}function updateTankOBB(e){const o=Math.sin(e.yaw),s=Math.cos(e.yaw);e.obb.cx=e.x,e.obb.cy=e.groundY+e.halfHeight,e.obb.cz=e.z,e.obb.hw=e.collider.halfWidth,e.obb.hd=e.collider.halfLength,e.obb.hh=e.collider.halfHeight,e.obb.w=e.width,e.obb.d=e.length,e.obb.h=e.height,e.obb.rightX=s,e.obb.rightZ=o,e.obb.forwardX=-o,e.obb.forwardZ=s}function updateObjectOBB(e){e.obb.cx=e.transform.x,e.obb.cy=e.transform.y,e.obb.cz=e.transform.z,e.obb.hw=e.halfWidth,e.obb.hd=e.halfDepth,e.obb.hh=e.halfHeight,e.obb.w=e.transform.sx,e.obb.d=e.transform.sz,e.obb.h=e.transform.sy;const o=e.transform.ry;e.obb.rightX=Math.cos(o),e.obb.rightZ=Math.sin(o),e.obb.forwardX=-Math.sin(o),e.obb.forwardZ=Math.cos(o),e.radius=Math.sqrt(e.obb.w*e.obb.w+e.obb.d*e.obb.d)}function updateCollisions(){const e=game.player;worldObjects[0].obb||updateBoundingBoxes(),updateTankOBB(e),worldCollisions(e);for(let o=0;o<game.enemies.length;o++){const s=game.enemies[o];updateTankOBB(s),worldCollisions(s),resolveTankVsTank(e,s);for(let e=o+1;e<game.enemies.length;e++)resolveTankVsTank(s,game.enemies[e])}}function worldCollisions(e){for(const o of worldObjects){const s=o.transform.x-e.x,r=o.transform.z-e.z;if(!(Math.abs(s)>o.radius+e.radius)&&!(Math.abs(r)>o.radius+e.radius))if("circle"===o.colliderType){if(intersectsCircleVsOBB(e.obb,o,s,r)){if("mine"===o.type)return destroyMine(o,e);resolveCircleVsOBB(e,o)}}else if("obb"===o.colliderType){const t=satOBBvsOBB_MTV(e.obb,o.obb,s,r);t&&resolveOBB_TankVsObject(e,o,t)}}}function satOBBvsOBB_MTV(e,o,s,r){const t=[{x:e.rightX,z:e.rightZ},{x:e.forwardX,z:e.forwardZ},{x:o.rightX,z:o.rightZ},{x:o.forwardX,z:o.forwardZ}];let n=1/0,a=null;for(const i of t){const t=s*i.x+r*i.z,b=Math.abs(e.hw*(e.rightX*i.x+e.rightZ*i.z))+Math.abs(e.hd*(e.forwardX*i.x+e.forwardZ*i.z))+(Math.abs(o.w*(o.rightX*i.x+o.rightZ*i.z))+Math.abs(o.d*(o.forwardX*i.x+o.forwardZ*i.z)))-Math.abs(t)-.5;if(b<=0)return null;if(b<n){n=b;const e=t<0?-1:1;a={x:i.x*e,z:i.z*e}}}return a?{x:a.x*n,z:a.z*n}:null}function resolveOBB_TankVsObject(e,o,s){if(!isFinite(s.x)||!isFinite(s.z))return;if(0===s.x&&0===s.z)return;e.x-=s.x,e.z-=s.z;let r=Math.hypot(s.x,s.z);r<1e-5&&(r=1e-5);const t=s.x/r,n=s.z/r,a=e.forward[0],i=e.forward[2],b=a*t+i*n;if(b>0){e.forward[0]=a-b*t,e.forward[2]=i-b*n;const o=Math.hypot(e.forward[0],e.forward[2]);o>1e-5&&(e.forward[0]/=o,e.forward[2]/=o)}e.speed*=b>.7?.2:b>.3?.5:.9}function intersectsCircleVsOBB(e,o,s,r){o.transform.x,o.transform.z;const t=o.r,n=o.transform.ry,a=Math.cos(-n),i=Math.sin(-n),b=s*a-r*i,d=s*i+r*a,c=b-Math.max(-e.hw,Math.min(b,e.hw)),f=d-Math.max(-e.hd,Math.min(d,e.hd));return c*c+f*f<=t*t}function resolveCircleVsOBB(e,o){const s=o.transform.x,r=o.transform.z,t=o.r,n=s-e.obb.cx,a=r-e.obb.cz,i=o.transform.ry,b=Math.cos(-i),d=Math.sin(-i),c=n*b-a*d,f=n*d+a*b,l=c-Math.max(-e.obb.hw,Math.min(c,e.obb.hw)),h=f-Math.max(-e.obb.hd,Math.min(f,e.obb.hd)),m=t-Math.sqrt(l*l+h*h);if(m>0){const o=e.obb.cx-s,t=e.obb.cz-r,n=Math.sqrt(o*o+t*t),a=0===n?1:o/n,i=0===n?0:t/n,b=e.forward[0]*a+e.forward[2]*i,d=1.1*m;e.x+=a*d,e.z+=i*d,b>.1&&(e.speed=0)}}function pointInOBB(e,o,s,r){const t=e-r.obb.cx,n=s-r.obb.cz,a=t*r.obb.rightX+n*r.obb.rightZ,i=t*r.obb.forwardX+n*r.obb.forwardZ,b=o-r.obb.cy;return Math.abs(a)<=r.obb.w&&Math.abs(i)<=r.obb.d&&Math.abs(b)<=r.obb.h}function pointInCylinder(e,o,s,r){const t=e-r.transform.x,n=s-r.transform.z,a=t*t+n*n,i=r.transform.sx,b=o-r.transform.y,d=r.transform.sy;return a<=i*i&&Math.abs(b)<=d}function pointInCylinderTilted(e,o,s,r){const t=e-r.transform.x,n=o-r.transform.y,a=s-r.transform.z,i=r.transform.getRotationMatrix(),b=[i[0],i[4],i[8],i[1],i[5],i[9],i[2],i[6],i[10]],d=[b[0],b[3],b[6],b[1],b[4],b[7],b[2],b[5],b[8]],c=d[0]*t+d[1]*n+d[2]*a,f=d[3]*t+d[4]*n+d[5]*a,l=d[6]*t+d[7]*n+d[8]*a,h=r.transform.sx;if(c*c+l*l>h*h)return!1;const m=r.transform.sy;return!(Math.abs(f)>m)}function pointInTankOBB(e,o,s,r){const t=r.transform.getMatrix(),n=e-t[12],a=o-t[13],i=s-t[14],b=n*t[0]+a*t[1]+i*t[2],d=n*t[4]+a*t[5]+i*t[6],c=n*t[8]+a*t[9]+i*t[10];return Math.abs(b)<=r.obb.hw&&Math.abs(d)<=r.obb.hh&&Math.abs(c)<=r.obb.hd}function handleHit(e,o){if(o.baseId&&"player"===e.owner){if(game.bases[o.baseId].hp-=e.damage,game.bases[o.baseId].hp>50)for(const e of game.enemies)e.activated||distance(e,game.bases[o.baseId])<75&&(e.activated=!0);game.bases[o.baseId].hp<=0&&destroyBase(game.bases[o.baseId])}o.destructible&&"barrel"===o.type&&(o.hp-=e.damage,o.hp<=0&&!o.destroyed&&destroyBarrel(o)),o.destructible&&"mine"===o.type&&(o.hp-=e.damage,o.hp<=0&&!o.destroyed&&destroyMine(o)),"mg"===e.type?spawnTriangles(e,5,.05):spawnTriangles(e,10,.12),explode(e)}function checkProjectileCollision(e){if(e.y<=hmap.sample(e.x,e.z))explode(e);else{for(const o of worldObjects)if("obb"!==o.colliderType){if("circle"===o.colliderType){if(0===o.transform.rx&&0===o.transform.rz?pointInCylinder(e.x,e.y,e.z,o):pointInCylinderTilted(e.x,e.y,e.z,o))return void handleHit(e,o)}}else if(pointInOBB(e.x,e.y,e.z,o))return void handleHit(e,o);if("enemy"===e.owner&&pointInTankOBB(e.x,e.y,e.z,game.player))handleVehicleHit(e,game.player);else for(const o of game.enemies)if("enemy"!==e.owner&&pointInTankOBB(e.x,e.y,e.z,o))return void handleVehicleHit(e,o)}}function destroyBase(e){for(const o of e.boxes){const e=worldObjects.indexOf(o);-1!==e&&worldObjects.splice(e,1);const s=scene.indexOf(o);-1!==s&&scene.splice(s,1)}delete game.bases[e.id],spawnTriangles(e,60,2),spawnTriangles(e,100,.5),sfxExplosion(e)}function destroyBarrel(e){const o=worldObjects.indexOf(e);-1!==o&&worldObjects.splice(o,1);const s=scene.indexOf(e);-1!==s&&scene.splice(s,1),spawnTriangles({x:e.transform.x,y:e.transform.y,z:e.transform.z},100,2),spawnExplosion(e,40,150),sfxExplosion({x:e.transform.x,y:e.transform.y,z:e.transform.z})}function destroyMine(e,o=null){if(t-e.deployed<1||o===game.player&&Math.abs(game.player.speed)<.1)return;const s=worldObjects.indexOf(e);-1!==s&&worldObjects.splice(s,1);const r=scene.indexOf(e);-1!==r&&scene.splice(r,1),spawnTriangles({x:e.transform.x,y:e.transform.y,z:e.transform.z},100,2),spawnExplosion(e,30,12),sfxExplosion({x:e.transform.x,y:e.transform.y,z:e.transform.z})}function handleVehicleHit(e,o){sfxHit(o),o.health-=e.damage,o.health<=0&&killTank(o),e.alive=!1,spawnTriangles(e)}function killTank(e){spawnTriangles(e,40,.2),e.dead=!0;const o=scene.indexOf(e),s=scene.indexOf(e.turret),r=scene.indexOf(e.barrel);-1!==r&&scene.splice(r,1),-1!==s&&scene.splice(s,1),-1!==o&&scene.splice(o,1),game.enemies=game.enemies.filter(e=>!e.dead),sfxExplosion(e)}function resolveTankVsTank(e,o){const s=o.obb.cx-e.obb.cx,r=o.obb.cz-e.obb.cz;if(Math.abs(s)>e.radius+o.radius)return;if(Math.abs(r)>e.radius+o.radius)return;const t=satOBBvsOBB_MTV(e.obb,o.obb,s,r);if(!t||!isFinite(t.x)||!isFinite(t.z))return;const n=e.mass+o.mass,a=o.mass/n,i=e.mass/n;e.x-=t.x*a,e.z-=t.z*a,o.x+=t.x*i,o.z+=t.z*i;const b=Math.sin(e.yaw)*e.speed,d=Math.cos(e.yaw)*e.speed,c=b-Math.sin(o.yaw)*o.speed,f=d-Math.cos(o.yaw)*o.speed,l=Math.hypot(t.x,t.z),h=.5*-(c*(t.x/l)+f*(t.z/l));e.speed+=h*(o.mass/n),o.speed-=h*(e.mass/n)}function updateEnemy(t,e){if(!t.activated){const a=t.x-game.player.x,r=t.z-game.player.z;if(!(a*a+r*r<22500||t.health!==t.maxHealth))return;t.activationTimer+=e,t.activationTimer>.5&&(t.activated=!0);for(const e of game.enemies)e.activated||distance(t,e)<75&&(e.activated=!0)}let a,r,n=0;if(t.forceReverse){a=1*normalizeAngle(t.forceTurn-t.yaw),r=0,n=1}else[a,dangerAhead]=computeEnemyTurn(t,e),r=computeEnemyThrottle(t,dangerAhead,e);const o=t.x-t.lastX,i=t.z-t.lastZ;if(o*-Math.sin(t.yaw)+i*Math.cos(t.yaw)<.1?t.stuckTimer+=e:t.stuckTimer=0,t.lastX=t.x,t.lastZ=t.z,t.forceReverse=!1,(t.stuckTimer>1&&t.stuckTimer<2||t.forceReverse&&t.stuckTimer<2)&&(t.forceReverse=!0,t.forceTurn=t.yaw+.6*Math.PI),t.stuckTimer>=2){const e=getAvoidance(t);t.tempGoal={x:t.x+Math.cos(e.angle),z:t.z+Math.sin(e.angle)},t.stuckTimer>5&&(t.stuckTimer=0)}switch(t.profile.steeringModel){case"tank":updateTankSteering(t,a,e);break;case"car":updateCarSteering(t,a,e);break;case"drift":updateDriftSteering(t,a,e)}if(updateMovement(t,r,n,e),!t.barrel.transform.getMatrix())return;const s=t.barrel.transform.getMatrix(),c=s[8],l=s[9],h=s[10],u=s[12]+.4*c,f=s[13]+.4*l,m=s[14]+.4*h,y=game.player.transform.getMatrix(),g=y[12]-u,p=y[13]-f,M=y[14]-m,d=Math.atan2(-g,M),x=Math.atan2(p,Math.sqrt(g*g+M*M)),z=normalizeAngle(d-(t.yaw+t.turret.yaw)),w=normalizeAngle(x-Math.atan2(l,Math.sqrt(c*c+h*h)));t.turret.yaw+=2*z*e*2,t.turret.pitch+=2*w*e*1.5,t.turret.pitch=Math.max(-.3,Math.min(t.turret.pitch,.5)),t.primaryFireCooldown<=0&&(tryTurretShot(t,z,w),t.primaryFireCooldown=t.primaryFireRate),t.secondaryFireCooldown<=0&&(tryMachineGunShot(t,z),t.secondaryFireCooldown=t.primaryFireRate),t.primaryFireCooldown-=e,t.secondaryFireCooldown-=e}function computeEnemyTurn(t,e){const a=game.player.x-t.x,r=game.player.z-t.z,n=Math.hypot(a,r),o=a/n,i=r/n,s=raycastObjects({x:t.x,y:t.groundY+t.halfHeight,z:t.z},{x:o,y:0,z:i},1,n),c=getAvoidance(t);let l;if(null==s)l=Math.atan2(-a,r),t.tempGoal=null;else{t.tempGoal||(t.tempGoal=pickRadiusWaypoint(t,c));const e=t.tempGoal.x-t.x,a=t.tempGoal.z-t.z;l=Math.atan2(-e,a),Math.hypot(e,a)<10&&(t.tempGoal=pickRadiusWaypoint(t,c))}let h=0,u=0;t.speed>1&&([h,u]=getAvoidanceAngleAdjustment(t,l)),l+=h;let f=normalizeAngle(l-t.yaw);const m=Math.abs(f);let y;return y="car"===t.profile.steeringModel&&m<.5?m<.1?0:.15*f:1*f,[y,u]}function getAvoidance(t){const e=t.yaw,a=t.x+-Math.sin(e),r=t.z+Math.cos(e),n=[],o=[];for(let i=-1;i<=1;i+=.15){const s=e+i,c={x:-Math.sin(s),y:0,z:Math.cos(s)},l=raycastObjects({x:a,y:t.groundY+2,z:r},c,1,120),h=l?l.dist:120;if(l&&l.dist<8)continue;const u=a+c.x*h,f=r+c.z*h,m=game.player.x-u,y=game.player.z-f,g=h/120,p=1/(Math.hypot(m,y)+1);l||n.push({angle:s,score:.4*g+.6*p}),o.push({angle:s,score:g})}return n.length>0?(n.sort((t,e)=>e.score-t.score),{angle:n[0].angle}):o.length>0?(o.sort((t,e)=>e.score-t.score),{angle:o[0].angle}):{angle:e}}function pickRadiusWaypoint(t,e){const a=Math.hypot(game.player.x-t.x,game.player.z-t.z),r=Math.min(80,Math.max(30,.4*a)),n=e.angle;return{x:t.x+-Math.sin(n)*r,z:t.z+Math.cos(n)*r}}function getAvoidanceAngleAdjustment(t,e){const a=t.x,r=t.z,n=[{off:0,weight:1},{off:.25,weight:.7},{off:-.25,weight:.7},{off:.55,weight:.5},{off:-.55,weight:.5}];let o=0,i=0;for(const e of n){rayHitsObject(a,r,t.yaw+e.off+Math.PI/2,10)&&(e.off>0?o+=e.weight:i+=e.weight)}const s=o+i;if(0===s)return[0,0];let c=0;return c=o>i?-.5:.5,[c,s]}function computeEnemyThrottle(t,e=0,a){if(e=Math.abs(e),t.speed>4){if(e>1)return.5;if(e>=2)return-.5;if(e>=2.5&&t.speed>7)return-1}return 1}function tryMachineGunShot(t,e){if(!(Math.abs(e)<.2))return;rayHitsObject(t.x,t.z,t.yaw,300);shootMachineGun(t)}function tryTurretShot(t,e,a){const r=Math.abs(e)<.1,n=Math.abs(a)<.1;if(!r||!n)return;const o=t.barrel.transform.getMatrix(),i=t.turret.transform.getMatrix(),s=getWorldPos(i),c=getForward(o),l=raycastObjects(s,c,2,1e3,!0,t),h=rayHitsTerrain3D(t,1e3);r&&n&&!1!==l&&!h&&shootTurret(t)}function rayHitsObject(t,e,a,r=2e3){const n=Math.cos(a),o=Math.sin(a);for(let a=0;a<r;a+=2){const r=t+n*a,i=e+o*a;for(const t of worldObjects)if("obb"===t.colliderType){if(pointInOBB(r,0,i,t))return a}else if("circle"===t.colliderType&&pointInCylinder(r,0,i,t))return a}return!1}function rayHitsTerrain3D(t,e=2e3){const a=t.yaw+t.turret.yaw,r=t.x,n=t.turret.y,o=t.z,i=game.player.x-r,s=game.player.z-o;e=Math.min(Math.sqrt(i*i+s*s),e);for(let i=2;i<e;i+=5){const e=r+Math.cos(a)*i,s=o+Math.sin(a)*i;if(hmap.sample(e,s)>n+Math.sin(t.turret.pitch)*i)return!0}return!1}function normalizeAngle(t){return(t%=2*Math.PI)<-Math.PI&&(t+=2*Math.PI),t>Math.PI&&(t-=2*Math.PI),t}*/ |
| SC Arguments: [Name:SC_ACTION Type:uint64 Value:'1' Name:SC_CODE Type:string Value:'// Copyright 2024. Civilware. All rights reserved.
// TELA Decentralized Web Document (TELA-DOC-1)
Function InitializePrivate() Uint64
10 IF init() == 0 THEN GOTO 30
20 RETURN 1
30 STORE("nameHdr", "data3.js")
31 STORE("descrHdr", "Data file 3")
32 STORE("iconURLHdr", "")
33 STORE("dURL", "data3.js")
34 STORE("docType", "TELA-JS-1")
35 STORE("subDir", "")
36 STORE("fileCheckC", "227054ffc819a797eb2f5f4e9a5a117c3c949b590809597eaa77825e778d0339")
37 STORE("fileCheckS", "1518b525a907bb3f93d53ba3a8a955ecf206fd1174903bd2278e34a9c28c323a")
100 RETURN 0
End Function
Function init() Uint64
10 IF EXISTS("owner") == 0 THEN GOTO 30
20 RETURN 1
30 STORE("owner", address())
50 STORE("docVersion", "1.0.0")
60 STORE("hash", HEX(TXID()))
70 STORE("likes", 0)
80 STORE("dislikes", 0)
100 RETURN 0
End Function
Function address() String
10 DIM s as String
20 LET s = SIGNER()
30 IF IS_ADDRESS_VALID(s) THEN GOTO 50
40 RETURN "anon"
50 RETURN ADDRESS_STRING(s)
End Function
Function Rate(r Uint64) Uint64
10 DIM addr as String
15 LET addr = address()
16 IF r < 100 && EXISTS(addr) == 0 && addr != "anon" THEN GOTO 30
20 RETURN 1
30 STORE(addr, ""+r+"_"+BLOCK_HEIGHT())
40 IF r < 50 THEN GOTO 70
50 STORE("likes", LOAD("likes")+1)
60 RETURN 0
70 STORE("dislikes", LOAD("dislikes")+1)
100 RETURN 0
End Function
/*class Camera{constructor(){this.x=0,this.y=0,this.z=5,this.pitch=0,this.yaw=0,this.zoom=1,this.fov=Math.PI/4,this.near=1,this.far=1e3,this.mode="lookAt",this.viewMatrix=new Float32Array([1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1]),this.lastHitX=0,this.lastHitY=0,this.lastHitz=0}getViewMatrix(){if("lookAt"===this.mode)return this.viewMatrix;if("topdown"===this.mode)return this.viewMatrix;if("barrel"===this.mode)return this.viewMatrix;const t=modelMatrix(-this.x,-this.y,-this.z),a=rotationX(-this.pitch),r=rotationY(-this.yaw);return multiply(a,multiply(r,t))}getProjectionMatrix(t){return perspectiveMatrix(this.fov*this.zoom,t,this.near,this.far)}}function perspectiveMatrix(t,a,r,e){const i=1/Math.tan(t/2),c=1/(r-e);return new Float32Array([i/a,0,0,0,0,i,0,0,0,0,(e+r)*c,-1,0,0,2*e*r*c,0])}function lookAt(t,a,r){const e=t[0],i=t[1],c=t[2];let o=a[0]-e,n=a[1]-i,s=a[2]-c;const m=Math.hypot(o,n,s);o/=m,n/=m,s/=m;let l=n*r[2]-s*r[1],x=s*r[0]-o*r[2],y=o*r[1]-n*r[0];const z=Math.hypot(l,x,y);l/=z,x/=z,y/=z;let h=x*s-y*n,p=y*o-l*s,u=l*n-x*o;return new Float32Array([l,h,-o,0,x,p,-n,0,y,u,-s,0,-(l*e+x*i+y*c),-(h*e+p*i+u*c),o*e+n*i+s*c,1])}function lerp(t,a,r){return t+(a-t)*r}function updateCamera(){const t=game.player;if("barrel"===camera.mode?input.zoomIn?camera.zoom=lerp(camera.zoom,.4,.1):input.zoomOut&&(camera.zoom=lerp(camera.zoom,1,.1)):camera.zoom=lerp(camera.zoom,1,.1),camera.zoom!==camera.lastZoom&&(camera.projectionMatrix=camera.getProjectionMatrix(aspect),camera.lastZoom=camera.zoom),"lookAt"===camera.mode){t.steerAngle||t.turnSpeed,t.speed;const a=15,r=a+.1*t.speed,e=t.forward[0],i=t.forward[2],c=hmap.sample(t.x+2*e,t.z+2*i),o=2*(hmap.sample(t.x,t.z)-c);t.pitchSmooth=lerp(t.pitchSmooth||0,o,.1);const n=[t.x,t.groundY+1.5+2*t.pitchSmooth,t.z],s=[n[0]-e*r,n[1]+5+.05*t.speed+3*t.pitchSmooth,n[2]-i*r],m={x:t.x,y:t.groundY+1.5,z:t.z},l={x:s[0]-m.x,y:s[1]-m.y,z:s[2]-m.z},x=Math.sqrt(l.x*l.x+l.y*l.y+l.z*l.z),y=raycastObjects(m,{x:l.x/x,y:l.y/x,z:l.z/x},2,x,!1);let z=0;if(y&&(z=x-y.dist),z>0){const t=Math.min(.2*z,1),r=s[0]+e*(t*a),c=s[2]+i*(t*a);camera.x=lerp(camera.x,r,.2),camera.z=lerp(camera.z,c,.2)}else camera.x=lerp(camera.x,s[0],.12),camera.y=lerp(camera.y,s[1],.12),camera.z=lerp(camera.z,s[2],.12);const h={x:camera.x,y:camera.y,z:camera.z};camera.viewMatrix=lookAt([camera.x,camera.y,camera.z],n,[0,1,0]);const p=t.barrel.transform.getMatrix();if(!p)return;const u=aimer(p),f=u.clip,d=u.hit;if(rayIntersectsTank(h,{x:d.x-camera.x,y:d.y-camera.y,z:d.z-camera.z},t))return void ctx.clearRect(0,0,uicanvas.width,uicanvas.height);const M=f[0]/f[3],w=f[1]/f[3],g=(.5*M+.5)*uicanvas.width,v=(1-(.5*w+.5))*uicanvas.height;if(f[3]<=0||M<-1||M>1||w<-1||w>1)return;return ctx.fillStyle="rgba(255,0,0,0.6)",ctx.beginPath(),ctx.arc(g,v,4,0,2*Math.PI),void ctx.fill()}if("topdown"===camera.mode){const a=[t.x,t.y+100,t.z],r=[t.x,t.y,t.z];camera.viewMatrix=lookAt(a,r,[0,0,-1]);const e=t.barrel.transform.getMatrix();if(!e)return;const i=aimer(e).clip,c=i[0]/i[3],o=i[1]/i[3],n=(.5*c+.5)*uicanvas.width,s=(1-(.5*o+.5))*uicanvas.height;if(i[3]<=0||c<-1||c>1||o<-1||o>1)return;return ctx.fillStyle="rgba(255,0,0,0.6)",ctx.beginPath(),ctx.arc(n,s,4,0,2*Math.PI),void ctx.fill()}if("barrel"===camera.mode){const t=game.player.barrel.transform.getMatrix();if(!t)return;const a=game.player.turret.transform.getMatrix(),r={x:t[8],y:t[9],z:t[10]},e={x:a[4],y:a[5],z:a[6]},i={x:a[12]+r.x,y:a[13]+r.y,z:a[14]+r.z},c=0,o=.6,n={x:i.x-r.x*c,y:i.y+o,z:i.z-r.z*c},s={x:i.x+50*r.x,y:i.y+50*r.y,z:i.z+50*r.z};camera.viewMatrix=lookAt([n.x,n.y,n.z],[s.x,s.y,s.z],[e.x,e.y,e.z]);const m=aimer(t).clip,l=m[0]/m[3],x=m[1]/m[3],y=(.5*l+.5)*uicanvas.width,z=(1-(.5*x+.5))*uicanvas.height;return ctx.fillStyle="rgba(255,0,0,0.6)",ctx.beginPath(),ctx.arc(y,z,4,0,2*Math.PI),void ctx.fill()}}function aimer(t){const a={x:t[8],y:t[9],z:t[10]},r={x:t[12]+a.x,y:t[13]+a.y,z:t[14]+a.z},e=raycastTerrain(r,a),i=raycastObjects(r,a,2,1e3,!0);let c=e&&i?e.dist<i.dist?e:i:e||i;c||(c={x:r.x+2e3*a.x,y:r.y+2e3*a.y,z:r.z+2e3*a.z}),isFinite(camera.lastHitX)||(camera.lastHitX=c.x),isFinite(camera.lastHitY)||(camera.lastHitY=c.y),isFinite(camera.lastHitZ)||(camera.lastHitZ=c.z),camera.lastHitX=lerp(camera.lastHitX,c.x,.2),camera.lastHitY=lerp(camera.lastHitY,c.y,.2),camera.lastHitZ=lerp(camera.lastHitZ,c.z,.2);const o=[camera.lastHitX,camera.lastHitY,camera.lastHitZ];return{clip:multiplyMat4Vec4(camera.projectionMatrix,multiplyMat4Vec4(camera.viewMatrix,[...o,1])),hit:c}}function resetCameraForLookAt(t){const a=15+.1*t.speed,r=[t.x,t.groundY+1.5,t.z],e=t.forward[0],i=t.forward[2],c=[r[0]-e*a,r[1]+5+.05*t.speed,r[2]-i*a];camera.x=c[0],camera.y=c[1],camera.z=c[2],camera.viewMatrix=lookAt(c,r,[0,1,0])}function raycastTerrain(t,a,r=2e3){const e=Math.abs(t.y-hmap.sample(t.x,t.z));let i=2;e<20&&(i=1),e<10&&(i=.5),e<5&&(i=.25);for(let e=0;e<r;e+=i){const r=t.x+a.x*e,i=t.z+a.z*e,c=hmap.sample(r,i);if(c>t.y+a.y*e)return{x:r,y:c,z:i,dist:e}}return null}function raycastObjects(t,a,r=2,e=2e3,i=!1,c=null){for(let o=0;o<e;o+=r){const r=t.x+a.x*o,e=t.y+a.y*o,n=t.z+a.z*o;if(i)for(const t of game.enemies)if(c!==t&&!t.dead&&t.obb&&pointInTankOBB(r,e,n,t))return{x:r,y:e,z:n,dist:o,t:t};for(const t of worldObjects)if("obb"===t.colliderType){if(pointInOBB(r,e,n,t))return{x:r,y:e,z:n,dist:o,obj:t}}else if("circle"===t.colliderType&&pointInCylinder(r,e,n,t))return{x:r,y:e,z:n,dist:o,obj:t}}return null}function rayIntersectsTank(t,a,r){const e=Math.sqrt(a.x*a.x+a.y*a.y+a.z*a.z);a.x/=e,a.y/=e,a.z/=e;const i=.25,c=e/i;let o=t.x,n=t.y,s=t.z;for(let t=0;t<c;t++)if(o+=a.x*i,n+=a.y*i,s+=a.z*i,pointInTankOBB(o,n,s,r))return!0;return!1}function updateBoundingBoxes(){for(const e of worldObjects)"circle"!==e.colliderType&&"obb"!==e.colliderType||updateObjectOBB(e);bbxsUpdated=!0}function updateTankOBB(e){const o=Math.sin(e.yaw),s=Math.cos(e.yaw);e.obb.cx=e.x,e.obb.cy=e.groundY+e.halfHeight,e.obb.cz=e.z,e.obb.hw=e.collider.halfWidth,e.obb.hd=e.collider.halfLength,e.obb.hh=e.collider.halfHeight,e.obb.w=e.width,e.obb.d=e.length,e.obb.h=e.height,e.obb.rightX=s,e.obb.rightZ=o,e.obb.forwardX=-o,e.obb.forwardZ=s}function updateObjectOBB(e){e.obb.cx=e.transform.x,e.obb.cy=e.transform.y,e.obb.cz=e.transform.z,e.obb.hw=e.halfWidth,e.obb.hd=e.halfDepth,e.obb.hh=e.halfHeight,e.obb.w=e.transform.sx,e.obb.d=e.transform.sz,e.obb.h=e.transform.sy;const o=e.transform.ry;e.obb.rightX=Math.cos(o),e.obb.rightZ=Math.sin(o),e.obb.forwardX=-Math.sin(o),e.obb.forwardZ=Math.cos(o),e.radius=Math.sqrt(e.obb.w*e.obb.w+e.obb.d*e.obb.d)}function updateCollisions(){const e=game.player;worldObjects[0].obb||updateBoundingBoxes(),updateTankOBB(e),worldCollisions(e);for(let o=0;o<game.enemies.length;o++){const s=game.enemies[o];updateTankOBB(s),worldCollisions(s),resolveTankVsTank(e,s);for(let e=o+1;e<game.enemies.length;e++)resolveTankVsTank(s,game.enemies[e])}}function worldCollisions(e){for(const o of worldObjects){const s=o.transform.x-e.x,r=o.transform.z-e.z;if(!(Math.abs(s)>o.radius+e.radius)&&!(Math.abs(r)>o.radius+e.radius))if("circle"===o.colliderType){if(intersectsCircleVsOBB(e.obb,o,s,r)){if("mine"===o.type)return destroyMine(o,e);resolveCircleVsOBB(e,o)}}else if("obb"===o.colliderType){const t=satOBBvsOBB_MTV(e.obb,o.obb,s,r);t&&resolveOBB_TankVsObject(e,o,t)}}}function satOBBvsOBB_MTV(e,o,s,r){const t=[{x:e.rightX,z:e.rightZ},{x:e.forwardX,z:e.forwardZ},{x:o.rightX,z:o.rightZ},{x:o.forwardX,z:o.forwardZ}];let n=1/0,a=null;for(const i of t){const t=s*i.x+r*i.z,b=Math.abs(e.hw*(e.rightX*i.x+e.rightZ*i.z))+Math.abs(e.hd*(e.forwardX*i.x+e.forwardZ*i.z))+(Math.abs(o.w*(o.rightX*i.x+o.rightZ*i.z))+Math.abs(o.d*(o.forwardX*i.x+o.forwardZ*i.z)))-Math.abs(t)-.5;if(b<=0)return null;if(b<n){n=b;const e=t<0?-1:1;a={x:i.x*e,z:i.z*e}}}return a?{x:a.x*n,z:a.z*n}:null}function resolveOBB_TankVsObject(e,o,s){if(!isFinite(s.x)||!isFinite(s.z))return;if(0===s.x&&0===s.z)return;e.x-=s.x,e.z-=s.z;let r=Math.hypot(s.x,s.z);r<1e-5&&(r=1e-5);const t=s.x/r,n=s.z/r,a=e.forward[0],i=e.forward[2],b=a*t+i*n;if(b>0){e.forward[0]=a-b*t,e.forward[2]=i-b*n;const o=Math.hypot(e.forward[0],e.forward[2]);o>1e-5&&(e.forward[0]/=o,e.forward[2]/=o)}e.speed*=b>.7?.2:b>.3?.5:.9}function intersectsCircleVsOBB(e,o,s,r){o.transform.x,o.transform.z;const t=o.r,n=o.transform.ry,a=Math.cos(-n),i=Math.sin(-n),b=s*a-r*i,d=s*i+r*a,c=b-Math.max(-e.hw,Math.min(b,e.hw)),f=d-Math.max(-e.hd,Math.min(d,e.hd));return c*c+f*f<=t*t}function resolveCircleVsOBB(e,o){const s=o.transform.x,r=o.transform.z,t=o.r,n=s-e.obb.cx,a=r-e.obb.cz,i=o.transform.ry,b=Math.cos(-i),d=Math.sin(-i),c=n*b-a*d,f=n*d+a*b,l=c-Math.max(-e.obb.hw,Math.min(c,e.obb.hw)),h=f-Math.max(-e.obb.hd,Math.min(f,e.obb.hd)),m=t-Math.sqrt(l*l+h*h);if(m>0){const o=e.obb.cx-s,t=e.obb.cz-r,n=Math.sqrt(o*o+t*t),a=0===n?1:o/n,i=0===n?0:t/n,b=e.forward[0]*a+e.forward[2]*i,d=1.1*m;e.x+=a*d,e.z+=i*d,b>.1&&(e.speed=0)}}function pointInOBB(e,o,s,r){const t=e-r.obb.cx,n=s-r.obb.cz,a=t*r.obb.rightX+n*r.obb.rightZ,i=t*r.obb.forwardX+n*r.obb.forwardZ,b=o-r.obb.cy;return Math.abs(a)<=r.obb.w&&Math.abs(i)<=r.obb.d&&Math.abs(b)<=r.obb.h}function pointInCylinder(e,o,s,r){const t=e-r.transform.x,n=s-r.transform.z,a=t*t+n*n,i=r.transform.sx,b=o-r.transform.y,d=r.transform.sy;return a<=i*i&&Math.abs(b)<=d}function pointInCylinderTilted(e,o,s,r){const t=e-r.transform.x,n=o-r.transform.y,a=s-r.transform.z,i=r.transform.getRotationMatrix(),b=[i[0],i[4],i[8],i[1],i[5],i[9],i[2],i[6],i[10]],d=[b[0],b[3],b[6],b[1],b[4],b[7],b[2],b[5],b[8]],c=d[0]*t+d[1]*n+d[2]*a,f=d[3]*t+d[4]*n+d[5]*a,l=d[6]*t+d[7]*n+d[8]*a,h=r.transform.sx;if(c*c+l*l>h*h)return!1;const m=r.transform.sy;return!(Math.abs(f)>m)}function pointInTankOBB(e,o,s,r){const t=r.transform.getMatrix(),n=e-t[12],a=o-t[13],i=s-t[14],b=n*t[0]+a*t[1]+i*t[2],d=n*t[4]+a*t[5]+i*t[6],c=n*t[8]+a*t[9]+i*t[10];return Math.abs(b)<=r.obb.hw&&Math.abs(d)<=r.obb.hh&&Math.abs(c)<=r.obb.hd}function handleHit(e,o){if(o.baseId&&"player"===e.owner){if(game.bases[o.baseId].hp-=e.damage,game.bases[o.baseId].hp>50)for(const e of game.enemies)e.activated||distance(e,game.bases[o.baseId])<75&&(e.activated=!0);game.bases[o.baseId].hp<=0&&destroyBase(game.bases[o.baseId])}o.destructible&&"barrel"===o.type&&(o.hp-=e.damage,o.hp<=0&&!o.destroyed&&destroyBarrel(o)),o.destructible&&"mine"===o.type&&(o.hp-=e.damage,o.hp<=0&&!o.destroyed&&destroyMine(o)),"mg"===e.type?spawnTriangles(e,5,.05):spawnTriangles(e,10,.12),explode(e)}function checkProjectileCollision(e){if(e.y<=hmap.sample(e.x,e.z))explode(e);else{for(const o of worldObjects)if("obb"!==o.colliderType){if("circle"===o.colliderType){if(0===o.transform.rx&&0===o.transform.rz?pointInCylinder(e.x,e.y,e.z,o):pointInCylinderTilted(e.x,e.y,e.z,o))return void handleHit(e,o)}}else if(pointInOBB(e.x,e.y,e.z,o))return void handleHit(e,o);if("enemy"===e.owner&&pointInTankOBB(e.x,e.y,e.z,game.player))handleVehicleHit(e,game.player);else for(const o of game.enemies)if("enemy"!==e.owner&&pointInTankOBB(e.x,e.y,e.z,o))return void handleVehicleHit(e,o)}}function destroyBase(e){for(const o of e.boxes){const e=worldObjects.indexOf(o);-1!==e&&worldObjects.splice(e,1);const s=scene.indexOf(o);-1!==s&&scene.splice(s,1)}delete game.bases[e.id],spawnTriangles(e,60,2),spawnTriangles(e,100,.5),sfxExplosion(e)}function destroyBarrel(e){const o=worldObjects.indexOf(e);-1!==o&&worldObjects.splice(o,1);const s=scene.indexOf(e);-1!==s&&scene.splice(s,1),spawnTriangles({x:e.transform.x,y:e.transform.y,z:e.transform.z},100,2),spawnExplosion(e,40,150),sfxExplosion({x:e.transform.x,y:e.transform.y,z:e.transform.z})}function destroyMine(e,o=null){if(t-e.deployed<1||o===game.player&&Math.abs(game.player.speed)<.1)return;const s=worldObjects.indexOf(e);-1!==s&&worldObjects.splice(s,1);const r=scene.indexOf(e);-1!==r&&scene.splice(r,1),spawnTriangles({x:e.transform.x,y:e.transform.y,z:e.transform.z},100,2),spawnExplosion(e,30,12),sfxExplosion({x:e.transform.x,y:e.transform.y,z:e.transform.z})}function handleVehicleHit(e,o){sfxHit(o),o.health-=e.damage,o.health<=0&&killTank(o),e.alive=!1,spawnTriangles(e)}function killTank(e){spawnTriangles(e,40,.2),e.dead=!0;const o=scene.indexOf(e),s=scene.indexOf(e.turret),r=scene.indexOf(e.barrel);-1!==r&&scene.splice(r,1),-1!==s&&scene.splice(s,1),-1!==o&&scene.splice(o,1),game.enemies=game.enemies.filter(e=>!e.dead),sfxExplosion(e)}function resolveTankVsTank(e,o){const s=o.obb.cx-e.obb.cx,r=o.obb.cz-e.obb.cz;if(Math.abs(s)>e.radius+o.radius)return;if(Math.abs(r)>e.radius+o.radius)return;const t=satOBBvsOBB_MTV(e.obb,o.obb,s,r);if(!t||!isFinite(t.x)||!isFinite(t.z))return;const n=e.mass+o.mass,a=o.mass/n,i=e.mass/n;e.x-=t.x*a,e.z-=t.z*a,o.x+=t.x*i,o.z+=t.z*i;const b=Math.sin(e.yaw)*e.speed,d=Math.cos(e.yaw)*e.speed,c=b-Math.sin(o.yaw)*o.speed,f=d-Math.cos(o.yaw)*o.speed,l=Math.hypot(t.x,t.z),h=.5*-(c*(t.x/l)+f*(t.z/l));e.speed+=h*(o.mass/n),o.speed-=h*(e.mass/n)}function updateEnemy(t,e){if(!t.activated){const a=t.x-game.player.x,r=t.z-game.player.z;if(!(a*a+r*r<22500||t.health!==t.maxHealth))return;t.activationTimer+=e,t.activationTimer>.5&&(t.activated=!0);for(const e of game.enemies)e.activated||distance(t,e)<75&&(e.activated=!0)}let a,r,n=0;if(t.forceReverse){a=1*normalizeAngle(t.forceTurn-t.yaw),r=0,n=1}else[a,dangerAhead]=computeEnemyTurn(t,e),r=computeEnemyThrottle(t,dangerAhead,e);const o=t.x-t.lastX,i=t.z-t.lastZ;if(o*-Math.sin(t.yaw)+i*Math.cos(t.yaw)<.1?t.stuckTimer+=e:t.stuckTimer=0,t.lastX=t.x,t.lastZ=t.z,t.forceReverse=!1,(t.stuckTimer>1&&t.stuckTimer<2||t.forceReverse&&t.stuckTimer<2)&&(t.forceReverse=!0,t.forceTurn=t.yaw+.6*Math.PI),t.stuckTimer>=2){const e=getAvoidance(t);t.tempGoal={x:t.x+Math.cos(e.angle),z:t.z+Math.sin(e.angle)},t.stuckTimer>5&&(t.stuckTimer=0)}switch(t.profile.steeringModel){case"tank":updateTankSteering(t,a,e);break;case"car":updateCarSteering(t,a,e);break;case"drift":updateDriftSteering(t,a,e)}if(updateMovement(t,r,n,e),!t.barrel.transform.getMatrix())return;const s=t.barrel.transform.getMatrix(),c=s[8],l=s[9],h=s[10],u=s[12]+.4*c,f=s[13]+.4*l,m=s[14]+.4*h,y=game.player.transform.getMatrix(),g=y[12]-u,p=y[13]-f,M=y[14]-m,d=Math.atan2(-g,M),x=Math.atan2(p,Math.sqrt(g*g+M*M)),z=normalizeAngle(d-(t.yaw+t.turret.yaw)),w=normalizeAngle(x-Math.atan2(l,Math.sqrt(c*c+h*h)));t.turret.yaw+=2*z*e*2,t.turret.pitch+=2*w*e*1.5,t.turret.pitch=Math.max(-.3,Math.min(t.turret.pitch,.5)),t.primaryFireCooldown<=0&&(tryTurretShot(t,z,w),t.primaryFireCooldown=t.primaryFireRate),t.secondaryFireCooldown<=0&&(tryMachineGunShot(t,z),t.secondaryFireCooldown=t.primaryFireRate),t.primaryFireCooldown-=e,t.secondaryFireCooldown-=e}function computeEnemyTurn(t,e){const a=game.player.x-t.x,r=game.player.z-t.z,n=Math.hypot(a,r),o=a/n,i=r/n,s=raycastObjects({x:t.x,y:t.groundY+t.halfHeight,z:t.z},{x:o,y:0,z:i},1,n),c=getAvoidance(t);let l;if(null==s)l=Math.atan2(-a,r),t.tempGoal=null;else{t.tempGoal||(t.tempGoal=pickRadiusWaypoint(t,c));const e=t.tempGoal.x-t.x,a=t.tempGoal.z-t.z;l=Math.atan2(-e,a),Math.hypot(e,a)<10&&(t.tempGoal=pickRadiusWaypoint(t,c))}let h=0,u=0;t.speed>1&&([h,u]=getAvoidanceAngleAdjustment(t,l)),l+=h;let f=normalizeAngle(l-t.yaw);const m=Math.abs(f);let y;return y="car"===t.profile.steeringModel&&m<.5?m<.1?0:.15*f:1*f,[y,u]}function getAvoidance(t){const e=t.yaw,a=t.x+-Math.sin(e),r=t.z+Math.cos(e),n=[],o=[];for(let i=-1;i<=1;i+=.15){const s=e+i,c={x:-Math.sin(s),y:0,z:Math.cos(s)},l=raycastObjects({x:a,y:t.groundY+2,z:r},c,1,120),h=l?l.dist:120;if(l&&l.dist<8)continue;const u=a+c.x*h,f=r+c.z*h,m=game.player.x-u,y=game.player.z-f,g=h/120,p=1/(Math.hypot(m,y)+1);l||n.push({angle:s,score:.4*g+.6*p}),o.push({angle:s,score:g})}return n.length>0?(n.sort((t,e)=>e.score-t.score),{angle:n[0].angle}):o.length>0?(o.sort((t,e)=>e.score-t.score),{angle:o[0].angle}):{angle:e}}function pickRadiusWaypoint(t,e){const a=Math.hypot(game.player.x-t.x,game.player.z-t.z),r=Math.min(80,Math.max(30,.4*a)),n=e.angle;return{x:t.x+-Math.sin(n)*r,z:t.z+Math.cos(n)*r}}function getAvoidanceAngleAdjustment(t,e){const a=t.x,r=t.z,n=[{off:0,weight:1},{off:.25,weight:.7},{off:-.25,weight:.7},{off:.55,weight:.5},{off:-.55,weight:.5}];let o=0,i=0;for(const e of n){rayHitsObject(a,r,t.yaw+e.off+Math.PI/2,10)&&(e.off>0?o+=e.weight:i+=e.weight)}const s=o+i;if(0===s)return[0,0];let c=0;return c=o>i?-.5:.5,[c,s]}function computeEnemyThrottle(t,e=0,a){if(e=Math.abs(e),t.speed>4){if(e>1)return.5;if(e>=2)return-.5;if(e>=2.5&&t.speed>7)return-1}return 1}function tryMachineGunShot(t,e){if(!(Math.abs(e)<.2))return;rayHitsObject(t.x,t.z,t.yaw,300);shootMachineGun(t)}function tryTurretShot(t,e,a){const r=Math.abs(e)<.1,n=Math.abs(a)<.1;if(!r||!n)return;const o=t.barrel.transform.getMatrix(),i=t.turret.transform.getMatrix(),s=getWorldPos(i),c=getForward(o),l=raycastObjects(s,c,2,1e3,!0,t),h=rayHitsTerrain3D(t,1e3);r&&n&&!1!==l&&!h&&shootTurret(t)}function rayHitsObject(t,e,a,r=2e3){const n=Math.cos(a),o=Math.sin(a);for(let a=0;a<r;a+=2){const r=t+n*a,i=e+o*a;for(const t of worldObjects)if("obb"===t.colliderType){if(pointInOBB(r,0,i,t))return a}else if("circle"===t.colliderType&&pointInCylinder(r,0,i,t))return a}return!1}function rayHitsTerrain3D(t,e=2e3){const a=t.yaw+t.turret.yaw,r=t.x,n=t.turret.y,o=t.z,i=game.player.x-r,s=game.player.z-o;e=Math.min(Math.sqrt(i*i+s*s),e);for(let i=2;i<e;i+=5){const e=r+Math.cos(a)*i,s=o+Math.sin(a)*i;if(hmap.sample(e,s)>n+Math.sin(t.turret.pitch)*i)return!0}return!1}function normalizeAngle(t){return(t%=2*Math.PI)<-Math.PI&&(t+=2*Math.PI),t>Math.PI&&(t-=2*Math.PI),t}*/'] |