This commit is contained in:
33333-33333 2026-06-21 14:09:35 +09:00
commit 37087d08a7
189 changed files with 12864 additions and 9589 deletions

View file

@ -3,15 +3,15 @@
const ANT_NEST_START_COUNT = 6;
const ANT_NEST_MAX_COUNT = 10;
const ANT_NEST_MAX_OUTSIDE = 3;
const ANT_WORKER_HP = 32;
const ANT_QUEEN_HP = 180;
const ANT_WORKER_HP = 48;
const ANT_QUEEN_HP = 270;
const ANT_WORKER_RADIUS = 4.2;
const ANT_QUEEN_RADIUS = 9.5;
const ANT_WORKER_SEARCH_SPEED = 8.625;
const ANT_WORKER_RETURN_SPEED = 12.0;
const ANT_WORKER_RETURN_WEAK_SPEED = 6.0;
const ANT_WORKER_DRAG_SPEED = 4.125;
const ANT_QUEEN_SPEED = 9.0;
const ANT_WORKER_SEARCH_SPEED = 12.9375;
const ANT_WORKER_RETURN_SPEED = 18.0;
const ANT_WORKER_RETURN_WEAK_SPEED = 9.0;
const ANT_WORKER_DRAG_SPEED = 6.1875;
const ANT_QUEEN_SPEED = 13.5;
function clampAntHp(v) {
return clamp(Number(v) || 0, 0, ANT_WORKER_HP);
@ -43,6 +43,11 @@ function syncAntNestCount(worldRef, nest) {
ensureAntNestWorkerPool(nest);
const outside = (worldRef?.ants || []).filter(a => a && !a.dead && a.kind === "worker" && a.homeId === nest.id).length;
nest.antCount = clamp((nest.antWorkers?.length || 0) + outside, 0, ANT_NEST_MAX_COUNT);
if (nest.antCount <= 0) {
nest.antWorkers = [];
nest.amount = 0;
if (worldRef) worldRef.drawListDirty = true;
}
return nest.antCount;
}
@ -81,6 +86,50 @@ class AntActor {
return (this.world?.items || []).find(it => it && !it.dead && it.id === this.homeId && it.type === "ant_nest") || null;
}
adoptableNest() {
let best = null;
let bestScore = Infinity;
for (const nest of this.world?.items || []) {
if (!nest || nest.dead || nest.type !== "ant_nest") continue;
if (typeof ensureAntNestWorkerPool === "function") ensureAntNestWorkerPool(nest);
const outside = (this.world?.ants || []).filter(a => a && !a.dead && a.kind === "worker" && a.homeId === nest.id).length;
const inside = Array.isArray(nest.antWorkers) ? nest.antWorkers.length : Math.max(0, (nest.antCount || 0) - outside);
const count = inside + outside;
if (count >= (ANT_NEST_MAX_COUNT || 10)) continue;
const d = distXY(this.x, this.y, nest.x, nest.y);
const score = d + count * 26;
if (score < bestScore) { best = nest; bestScore = score; }
}
return best;
}
updateWanderNoNest(dt) {
const adopt = this.adoptableNest();
if (adopt) {
this.homeId = adopt.id;
this.state = "return";
this.targetId = "";
this.targetToken = 0;
this.targetRef = null;
this.world.drawListDirty = true;
return;
}
this.targetId = "";
this.targetToken = 0;
this.targetRef = null;
this.state = "wander";
this.nextTurn -= dt;
if (this.nextTurn <= 0) {
this.wanderAngle += rand(-1.35, 1.35);
this.nextTurn = rand(0.55, 1.45);
}
const speed = (ANT_WORKER_SEARCH_SPEED || 10) * clamp((this.hp || this.maxHp || ANT_WORKER_HP) / Math.max(1, this.maxHp || ANT_WORKER_HP), 0.45, 1.0) * 0.86;
this.vx = Math.cos(this.wanderAngle) * speed;
this.vy = Math.sin(this.wanderAngle) * speed * 0.78;
this.x = clamp(this.x + this.vx * dt, CONFIG.worldPadding, this.world.w - CONFIG.worldPadding);
this.y = clamp(this.y + this.vy * dt, CONFIG.worldPadding, this.world.h - CONFIG.worldPadding);
}
targetTarinai() {
if (this.targetRef && !this.targetRef.dead && this.targetRef.id === this.targetId && this.targetRef.liveToken === this.targetToken) return this.targetRef;
const live = this.world?.liveTarinaiById?.(this.targetId, this.targetToken) || null;
@ -134,7 +183,7 @@ class AntActor {
updateWorker(dt) {
const home = this.homeNest();
if (!home) {
this.dead = true;
this.updateWanderNoNest(dt);
return;
}
if (this.hp <= 0) {
@ -158,6 +207,7 @@ class AntActor {
target.antDraggedBy = this.id;
target.fearTimer = Math.max(target.fearTimer || 0, 0.55);
target.thought = "\u30a2\u30ea\u306b\u904b\u3070\u308c\u3066\u3044\u308b";
audio.antDrag?.();
this.world.drawListDirty = true;
return;
}
@ -180,7 +230,9 @@ class AntActor {
returnToNest(home) {
if (!home || home.dead || home.type !== "ant_nest") {
this.dead = true;
const adopt = this.adoptableNest();
if (adopt) { this.homeId = adopt.id; this.state = "return"; return; }
this.updateWanderNoNest(0.016);
return;
}
ensureAntNestWorkerPool(home).push(clampAntHp(this.hp));
@ -262,6 +314,7 @@ class AntActor {
dieAsCorpse(home = null) {
if (this.dead) return;
this.dead = true;
audio.antDie?.();
const corpse = new Item("ant_corpse", this.x, this.y);
corpse.amount = 24;
corpse.workerSprite = this.workerSpriteId();
@ -288,9 +341,12 @@ class AntActor {
base.antCount = ANT_NEST_START_COUNT;
base.antWorkers = buildAntWorkerPool(ANT_NEST_START_COUNT);
if (!force && this.world.placementBlocked?.(base)) return null;
audio.antNest?.();
this.world.items.push(base);
this.world.itemCounts[base.type] = (this.world.itemCounts[base.type] || 0) + 1;
this.world.rebuildSpatial?.();
this.world.markTerrainDirty?.("ant-nest-built");
this.world.emit?.("ant:nest-built", { nest: base, queen: this });
this.world.rebuildSpatial?.(true);
this.world.drawListDirty = true;
this.world.log("\u5973\u738b\u30a2\u30ea\u304c\u65b0\u3057\u3044\u30a2\u30ea\u306e\u5de3\u3092\u4f5c\u3063\u305f\u3002", "event");
return base;
@ -347,7 +403,7 @@ class AntActor {
const img = typeof getRenderableImage === "function" ? getRenderableImage(id, id) : images.get(id);
const lightState = lighting || getLightingState(this.world);
ctx.save();
const shadow = projectedShadowParams(lightState, this.kind === "queen" ? 0.58 : 0.26);
const shadow = projectedShadowParams(lightState, this.kind === "queen" ? 0.78 : 0.26);
drawProjectedShadow(ctx, this.x, this.y + this.r * 0.62, this.r * 1.5, this.r * 0.30, { ...shadow, alpha: shadow.alpha * 0.72 });
ctx.translate(this.x, this.y);
const angle = this.kind === "queen"
@ -355,7 +411,7 @@ class AntActor {
: Math.atan2(this.vy || 0, this.vx || 1);
ctx.rotate(angle);
if (img) {
const w = this.kind === "queen" ? this.r * 4.1 : this.r * 3.8;
const w = this.kind === "queen" ? this.r * 5.25 : this.r * 3.8;
const metrics = getImageMetrics(id);
const h = w * (metrics?.ratio || 0.52);
ctx.globalAlpha = 0.96;