food_chain/worker.js
2026-09-29 23:45:12 +09:00

22 lines
4 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import {World,DT} from './engine.js?v=45';
import {roleCode} from './ecology/feeding.js?v=45';
import {NORMAL_PLAYBACK_DAYS_PER_SECOND} from './time.js?v=45';
const seedBuffer=new Uint32Array(1);crypto.getRandomValues(seedBuffer);const world=new World(seedBuffer[0]);let speed=1,statMode=false,last=performance.now(),debt=0,lastSend=0,lastMeasure=last,steps=0,actual=0,selected=null,limited=false,lastSummary=0,forceSnapshot=true,lastHistoryTime=-1,lastEvent=null,awaitingAck=false,waterDirty=true,rocksDirty=true,moistureDirty=true,lastFieldSend=0;
onmessage=({data:d})=>{
if(d.type==='ack'){awaitingAck=false;return}
if(d.type==='speed'){speed=d.speed;statMode=d.stat;debt=0;last=performance.now();forceSnapshot=true}
if(d.type==='water'){const ok=world.setWater(d.water);if(ok){world.log('水域を編集');waterDirty=true;moistureDirty=true}postMessage({type:'notice',text:ok?'水域を更新しました':'生物の退避先がないため、水域の変更を取り消しました'});forceSnapshot=true;send()}
if(d.type==='rocks'){const ok=world.setRocks(d.rocks);if(ok){world.log('岩を編集');rocksDirty=true}postMessage({type:'notice',text:ok?'岩を更新しました':'安全な退避先がないため、岩の変更を取り消しました'});forceSnapshot=true;send()}
if(d.type==='load-profile'){try{world.setAquaticProfile(d.profile);moistureDirty=true;forceSnapshot=true;send()}catch(error){postMessage({type:'notice',text:error.message})}}
if(d.type==='select'){selected=d.id;forceSnapshot=true;send()}
};
function send(){
if(awaitingAck)return;const now=performance.now(),summary=forceSnapshot||now-lastSummary>650,field=!statMode&&(forceSnapshot||waterDirty||rocksDirty||moistureDirty||now-lastFieldSend>750),a=world.animals.find(a=>a.id===selected&&!a.dead);const packet={type:'state',actual,limited,statMode};const alive=statMode?[]:world.animals.filter(a=>!a.dead),data=new Float32Array(alive.length*7),ids=new Uint32Array(alive.length),sids=new Uint32Array(alive.length);
for(let i=0;i<alive.length;i++){const a=alive[i],j=i*7;ids[i]=a.id;sids[i]=a.sid;data[j]=a.x;data[j+1]=a.y;data[j+2]=world.radius(a);data[j+3]=a.vx;data[j+4]=a.vy;data[j+5]=roleCode(world.species[a.sid]?.trophicRole);data[j+6]=a.g.waterAffinity}packet.animalData=data;packet.animalIds=ids;packet.animalSids=sids;
if(summary){packet.stats=world.stats();const newest=world.history.at(-1)?.time;if(forceSnapshot||newest!==lastHistoryTime){packet.history=world.history;lastHistoryTime=newest}if(forceSnapshot||world.events[0]!==lastEvent){packet.events=world.events;lastEvent=world.events[0]}packet.selected=a?{id:a.id,sid:a.sid,g:a.g,age:a.age,energy:a.energy,stamina:a.stamina,maxStamina:world.staminaCapacity(a),concealment:world.concealmentAt(a.x,a.y),action:a.action,parents:a.parents,generation:a.generation,mass:world.biomass(a),maxEnergy:world.maxEnergy(a)}:null;lastSummary=now}
if(field){packet.carcasses=world.carcasses;if(waterDirty){packet.water=world.water;waterDirty=false}if(rocksDirty){packet.rocks=world.rocks;rocksDirty=false}packet.trees=world.trees.map(({x,y,r,trunk,leaves,maxLeaves,variant})=>({x,y,r,trunk,leaves,maxLeaves,variant}));packet.fallen=world.fallen;packet.plants=world.plants;if(moistureDirty){packet.moisture=world.moisture;moistureDirty=false}lastFieldSend=now}if(statMode)packet.carcasses=[];awaitingAck=true;postMessage(packet,[data.buffer,ids.buffer,sids.buffer]);forceSnapshot=false;lastSend=now;
}
function loop(){const now=performance.now(),elapsed=Math.min(.2,(now-last)/1000);last=now;if(speed){debt=Math.min(debt+elapsed*speed*NORMAL_PLAYBACK_DAYS_PER_SECOND,DT*300);const start=performance.now();while(debt>=DT&&performance.now()-start<22){world.step();debt-=DT;steps++;if(world.animals.length>18000){speed=0;limited=true;world.log('計算保護:18,000個体を超えたため一時停止');break}}}if(now-lastMeasure>1000){actual=steps*DT/((now-lastMeasure)/1000);steps=0;lastMeasure=now}const sendInterval=statMode?900:speed>=100?250:speed>=20?160:100;if(now-lastSend>sendInterval)send();setTimeout(loop,4)}send();loop();