"use strict"; (function(global){ const LOOSE_DYNAMIC_TYPES = new Set([ "ball","balloon","zunchi","pushpin","oshibyo","genkotsu", "firecracker","flame_firecracker","sticky_bomb","water" ]); function nearby(world,x,y,r){ return world?.nearbyItems?.(x,y,r,true) || world?.items || []; } function oilAt(world,x,y){ for(const o of (world?.itemsOfType?.("oil")||[])) { if(o&&!o.dead&&distXY(x,y,o.x,o.y)<=Math.max(12,o.r||58)) return o; } return null; } function groundFrictionMultiplier(world,x,y){ return oilAt(world,x,y)?0:1; } function spawnOilFire(item,world,initial=false){ if(!item||!world)return null; const r=Math.max(24,item.r||58); const angle=initial ? global.deterministicAngle?.(world,"oil-ignite-fire",item,item.oilBurnTimer||0) ?? 0 : Math.random()*Math.PI*2; const distance=initial ? 0 : Math.random()*r*.58; return global.TarinaiFireRuntimeSystem?.spawnFireAt?.( world, item.x+Math.cos(angle)*distance, item.y+Math.sin(angle)*distance*.58, {radius:initial?20:12+Math.random()*5,amount:initial?72:30+Math.random()*24,life:initial?4.4:2.8+Math.random()*2.0,source:item,mergeRadius:initial?48:42} ) || null; } function igniteOil(item,world){ if(!item||item.dead||item.type!=="oil"||item.oilBurning)return false; item.oilBurning=true; item.oilBurnTimer=0; item.nextOilFireAt=Number(world?.time||0); spawnOilFire(item,world,true); if(world){ world.drawListDirty=true; world.markSpatialDirty?.("oil-ignited"); } return true; } function igniteOilNear(world,x,y,r=120){ let n=0; for(const o of (world?.itemsOfType?.("oil")||[])){ if(o&&!o.dead&&distXY(x,y,o.x,o.y)<=r+Math.max(12,o.r||58)) n+=igniteOil(o,world)?1:0; } return n; } function igniteFromBurningOilContact(item,world){ if(!item||!world||!item.oilBurning)return 0; const radius=Math.max(24,Number(item.r||58)); let ignited=0; const tarinai=world.nearbyTarinai?.(item.x,item.y,radius+48,true)||world.tarinai||[]; for(const t of tarinai){ if(!t||t.dead||t.insideNestBoxId||world.isTarinaiHiddenInNestBox?.(t))continue; if(distXY(item.x,item.y,t.x,t.y)>radius+Math.max(12,Number(t.radius||18))*.72)continue; const wasBurning=Boolean(t.burning||(t.burnTimer||0)>.02); if(typeof t.igniteFire==="function")t.igniteFire(item,false); if(!wasBurning&&Boolean(t.burning||(t.burnTimer||0)>.02))ignited++; } const ants=world.nearbyAnts?.(item.x,item.y,radius+24,true)||world.ants||[]; for(const ant of ants){ if(!ant||ant.dead||distXY(item.x,item.y,ant.x,ant.y)>radius+Math.max(4,Number(ant.r||5)))continue; const wasBurning=Boolean(ant.burning||(ant.burnTimer||0)>.02); global.TarinaiFireRuntimeSystem?.igniteAntByFire?.(ant,item,world); if(!wasBurning&&Boolean(ant.burning||(ant.burnTimer||0)>.02))ignited++; } return ignited; } function updateOil(item,dt,world){ if(!item.oilBurning){ for(const f of (world?.itemsOfType?.("fire")||[])){ if(f&&!f.dead&&distXY(item.x,item.y,f.x,f.y)<=Math.max(item.r||58,24)+Math.max(f.r||14,10)){ igniteOil(item,world); break; } } return true; } const now=Number(world?.time||0); igniteFromBurningOilContact(item,world); item.oilBurnTimer=(item.oilBurnTimer||0)+dt; item.amount=Math.max(0,(item.amount||100)-dt*5.2); if(now>=Number(item.nextOilFireAt||0)){ item.nextOilFireAt=now+0.36+Math.random()*0.34; spawnOilFire(item,world,false); } if(item.amount<=0){ item.amount=0; world?.markItemBucketsDirty?.("oil-burnout"); world?.markSpatialDirty?.("oil-burnout"); world && (world.drawListDirty=true); } return true; } function isTrappableDynamic(it,world){ if(!it||it.dead||it.type==="bear_trap"||it.type==="poison_gas"||it.type==="fire") return false; if(it.isStructure) return false; if(["rope","rod","spring","wire","insulated_wire","rotator","reciprocator","poison_block"].includes(it.type)) return false; if(LOOSE_DYNAMIC_TYPES.has(it.type)) return true; if((it.dropTimer||0)>0 || Math.hypot(it.vx||0,it.vy||0)>0.2) return true; return Boolean(world?.spatial?.isDynamicItem?.(it) && !it.isStructure); } function trapTarget(item,world){ const ref=item?.trapTargetRef; if(ref&&!ref.dead&&(!item.trapTargetId||ref.id===item.trapTargetId)) return ref; if(!item?.trapTargetId) return null; const found=world?.itemById?.(item.trapTargetId) || (world?.items||[]).find(it=>it&&!it.dead&&it.id===item.trapTargetId) || (world?.tarinai||[]).find(t=>t&&!t.dead&&t.id===item.trapTargetId) || null; item.trapTargetRef=found; return found; } function hasTrapDamageStat(target){ if(!target||target.dead)return false; if(Number.isFinite(Number(target.hp))||Number.isFinite(Number(target.health)))return true; if(Number.isFinite(Number(target.maxHp))||Number.isFinite(Number(target.maxHealth)))return true; if(typeof target.damage==="function"&&Number.isFinite(Number(target.energy)))return true; return false; } function damageHeld(target,world,trap){ if(!target||target.dead||!hasTrapDamageStat(target))return false; if(typeof target.damage==="function"){ try { target.damage(2,"\u7f60\u306b\u6355\u307e\u3063\u305f",{source:trap}); } catch (_) { target.damage(2,"\u7f60\u306b\u6355\u307e\u3063\u305f",{source:trap}); } if (target.world && Number.isFinite(Number(target.radius))) target.hurtTimer=Math.max(Number(target.hurtTimer||0),.62); return true; } if(Number.isFinite(Number(target.hp))){ target.hp=Math.max(0,Number(target.hp)-2); target.hpBarTimer=Math.max(Number(target.hpBarTimer||0),.8); if(target.hp<=0) global.TarinaiStructureLifecycle?.deleteItem?.(world,target,{reason:"bear-trap",breaker:trap}); return true; } if(Number.isFinite(Number(target.health))){ target.health=Math.max(0,Number(target.health)-2); return true; } return false; } function holdTrapTarget(trap,target,world){ if(!trap||!target||target.dead)return false; const now=Number(world?.time||0); target._bearTrapHoldUntil=Math.max(Number(target._bearTrapHoldUntil||0),Number(trap.trapUntil||now)); target._bearTrapOwnerId=trap.id; target._bearTrapHoldX=trap.x; target._bearTrapHoldY=trap.y; target.x=trap.x; target.y=trap.y; target.prevX=trap.x; target.prevY=trap.y; target.vx=0; target.vy=0; target.impulseVx=0; target.impulseVy=0; target.spinVelocity=0; const body=global.TarinaiPhysicsBodySystem?.isBodyType?.(target) ? global.TarinaiPhysicsBodySystem.ensureBody?.(target,world,{syncFromLegacy:true}) : null; if(body?.pose){ body.pose.x=trap.x; body.pose.y=trap.y; } if(body?.velocity){ body.velocity.x=0; body.velocity.y=0; body.velocity.linear=0; body.velocity.angular=0; } target._physicsExternalPoseDirty=true; return true; } function triggerBearTrap(item,target,world){ if(!item||!target||target.dead)return false; const now=Number(world?.time||0); item.trapTargetId=target.id||""; item.trapTargetRef=target; item.trapTriggeredAt=now; item.trapDamageableTarget=hasTrapDamageStat(target); item.trapHoldDuration=item.trapDamageableTarget?10:5; item.trapUntil=now+item.trapHoldDuration; item.nextTrapDamageAt=now+.5; item.trapClosed=true; holdTrapTarget(item,target,world); world?.queueEffect?.("ring",item.x,item.y,{size:Math.max(34,(item.r||34)*1.2),life:.22,color:"rgba(190,204,214,0.58)"}); if(world){ world.drawListDirty=true; world.markSpatialDirty?.("bear-trap-trigger"); } return true; } function updateBearTrap(item,dt,world){ const now=Number(world?.time||0); if(item.trapClosed || item.trapUntil>0){ const target=trapTarget(item,world); if(now>=Number(item.trapUntil||0)){ if(target){ target._bearTrapHoldUntil=0; target._bearTrapOwnerId=""; target._bearTrapHoldX=undefined; target._bearTrapHoldY=undefined; } item.trapTargetRef=null; item.trapTargetId=""; const removed=global.TarinaiStructureLifecycle?.deleteItem?.(world,item,{reason:"bear-trap-expired"}); if(!removed)item.amount=0; world?.markItemBucketsDirty?.("bear-trap-expired"); world?.markSpatialDirty?.("bear-trap-expired"); return true; } if(target){ holdTrapTarget(item,target,world); if(item.trapDamageableTarget&&now>=Number(item.nextTrapDamageAt||0)){ item.nextTrapDamageAt=now+.5; damageHeld(target,world,item); } } return true; } const captureRadius=Math.max(46,(item.r||34)*1.45); let best=null,bestD=Infinity; for(const it of nearby(world,item.x,item.y,captureRadius)){ if(it===item||!isTrappableDynamic(it,world))continue; if(Number(it._bearTrapHoldUntil||0)>now)continue; const d=distXY(item.x,item.y,it.x,it.y); const rr=Math.max(12,(item.r||34)*.72)+Math.max(7,(it.r||12)*.62); if(d<=rr&&dnow)continue; const d=distXY(item.x,item.y,t.x,t.y); const rr=Math.max(12,(item.r||34)*.72)+Math.max(10,(t.radius||18)*.62); if(d<=rr&&d=Number(trap.trapUntil||0))continue; const target=trapTarget(trap,world); if(target&&holdTrapTarget(trap,target,world)) held++; } if(held) world?.markSpatialDirty?.("bear-trap-hold"); return held; } function leakageRadius(item){ return Math.max(260,(Number(item?.r)||34)*8.2); } function pulseLeakage(item,world){ if(!item||item.dead||!world)return 0; const powered=Boolean(item._signalDriven&&item._signalOn); if(!powered)return 0; const now=Number(world.time||0); if(nowradius+(t.radius||18))continue; const q=Math.max(.2,1-d/Math.max(1,radius)); t.damage?.(1.5+.9*q,"\u6f0f\u96fb",{source:item}); t.hurtTimer=Math.max(t.hurtTimer||0,.22); t.electricShockUntil=Math.max(Number(t.electricShockUntil||0),now+.48); t.setActionState?.("panic",{target:null,reason:"\u611f\u96fb\u3057\u3066\u3044\u308b\u3002",wake:true,sleeping:false}); if(t.isAcquiredTarinai&&now>=Number(t.ochibiElectricVoiceAt||0)){t.ochibiElectricVoiceAt=now+1.8;if(t.bubble?.("びりびりちゃ!",1.4,"rgba(70,104,168,0.82)",{force:true})){const a=globalThis.TarinaiAudio;if(a?.playSample)a.playSample("voice_ochibi_electric_006",.9,"voice")||a.play?.("voice_ochibi_electric_006",{category:"voice",minGap:.9});}} world.queueEffect?.("electric_shock",t.x,t.y-Math.max(2,(t.radius||18)*.15),{size:Math.max(18,(t.radius||18)*1.35),life:.32,color:"rgba(122,224,255,0.98)",importance:3,forceVisual:true}); hit++; } for(const ant of world.ants||[]){ if(!ant||ant.dead)continue; const d=distXY(item.x,item.y,ant.x,ant.y); if(d>radius+(ant.r||6))continue; ant.hp=Math.max(0,Number(ant.hp??ant.maxHp??32)-1.4); ant.hpBarTimer=Math.max(Number(ant.hpBarTimer||0),.7); hit++; } world.queueEffect?.("ring",item.x,item.y,{size:radius*.62,life:.22,color:"rgba(112,206,255,0.38)"}); world.drawListDirty=true; return hit; } function updateLeakage(item,dt,world){ // Electrical damage is applied after the signal graph has been resolved for // this simulation step. Keep the per-item update as a lightweight no-op. return true; } function updatePoweredHazards(world,dt){ let hit=0; for(const item of (world?.itemsOfType?.("leakage_device")||[])) hit+=pulseLeakage(item,world); return hit; } function spawnPoisonGas(world,can){ if(!world||!can||can._gasReleased)return 0; can._gasReleased=true; let n=0; const sizeScale=Math.max(.55,Math.min(1.8,Number(can.r||28)/28)); const count=Math.max(7,Math.round(10*sizeScale)); for(let i=0;i