387 lines
18 KiB
JavaScript
387 lines
18 KiB
JavaScript
|
|
"use strict";
|
||
|
|
|
||
|
|
(function (global) {
|
||
|
|
const Tarinai = global.Tarinai;
|
||
|
|
if (!Tarinai) throw new Error("Tarinai is not available for mixin: tarinai_item_effects.js");
|
||
|
|
|
||
|
|
const EFFECT_REGISTRY = global.TarinaiEffectRegistry || null;
|
||
|
|
const ITEM_EFFECT_LABELS = Object.freeze(EFFECT_REGISTRY?.labels?.() || {});
|
||
|
|
const ITEM_EFFECT_COLORS = Object.freeze(EFFECT_REGISTRY?.colors?.() || {});
|
||
|
|
const RANDOM_EFFECT_POOL = Object.freeze(EFFECT_REGISTRY?.randomPool?.() || [
|
||
|
|
"love_mochi", "fight_mochi", "sleep_drug", "laxative", "ammo", "protein", "niteropu", "mercury", "giant_drug", "dwarf_drug",
|
||
|
|
"disease_zunchi", "disease_sleep", "disease_explosion", "disease_fight",
|
||
|
|
]);
|
||
|
|
|
||
|
|
function effectLabel(id = "") {
|
||
|
|
return EFFECT_REGISTRY?.label?.(id) || ITEM_EFFECT_LABELS[id] || id || "不明";
|
||
|
|
}
|
||
|
|
|
||
|
|
function sizeDurationScale(item = null) {
|
||
|
|
const size = item?.toolSize || "medium";
|
||
|
|
if (size === "small") return 0.74;
|
||
|
|
if (size === "large") return 1.45;
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
function pickRandomEffects(count = 2) {
|
||
|
|
const pool = [...RANDOM_EFFECT_POOL];
|
||
|
|
const out = [];
|
||
|
|
while (pool.length && out.length < count) {
|
||
|
|
const idx = Math.floor(Math.random() * pool.length);
|
||
|
|
out.push(pool.splice(idx, 1)[0]);
|
||
|
|
}
|
||
|
|
return out;
|
||
|
|
}
|
||
|
|
|
||
|
|
Object.defineProperties(Tarinai.prototype, Object.getOwnPropertyDescriptors({
|
||
|
|
ensureItemEffectState() {
|
||
|
|
if (!this.itemEffectTimers || typeof this.itemEffectTimers !== "object") this.itemEffectTimers = { laxative: 0, ammo: 0 };
|
||
|
|
if (!Number.isFinite(this.itemEffectTimers.laxative)) this.itemEffectTimers.laxative = 0;
|
||
|
|
if (!Number.isFinite(this.itemEffectTimers.ammo)) this.itemEffectTimers.ammo = 0;
|
||
|
|
if (!Number.isFinite(this.laxativePoopTimer)) this.laxativePoopTimer = 3;
|
||
|
|
if (!Number.isFinite(this.ammoBumpCooldown)) this.ammoBumpCooldown = 0;
|
||
|
|
if (this.powerItemMode !== "protein" && this.powerItemMode !== "niteropu") this.powerItemMode = "";
|
||
|
|
if (this.sizeItemMode !== "giant_drug" && this.sizeItemMode !== "dwarf_drug") this.sizeItemMode = "";
|
||
|
|
if (this.lifeItemMode !== "mercury") this.lifeItemMode = "";
|
||
|
|
if (!Number.isFinite(this.mercuryLifeSpanBase)) this.mercuryLifeSpanBase = null;
|
||
|
|
if (!Number.isFinite(this.mercuryLifeMultiplier)) this.mercuryLifeMultiplier = this.lifeItemMode === "mercury" ? 1.3 : 1;
|
||
|
|
this.mercuryLifeMultiplier = Math.max(1, this.mercuryLifeMultiplier || 1);
|
||
|
|
if (!Number.isFinite(this.itemDamageTimer)) this.itemDamageTimer = 1;
|
||
|
|
if (!Number.isFinite(this.powerEffectPulse)) this.powerEffectPulse = 0;
|
||
|
|
if (!Array.isArray(this.mysteryDrugHistory)) this.mysteryDrugHistory = [];
|
||
|
|
},
|
||
|
|
|
||
|
|
hasItemEffect(id = "") {
|
||
|
|
this.ensureItemEffectState();
|
||
|
|
if (id === "protein" || id === "niteropu") return this.powerItemMode === id;
|
||
|
|
if (id === "giant_drug" || id === "dwarf_drug") return this.sizeItemMode === id;
|
||
|
|
if (id === "mercury") return this.lifeItemMode === "mercury";
|
||
|
|
return (this.itemEffectTimers?.[id] || 0) > 0.04;
|
||
|
|
},
|
||
|
|
|
||
|
|
setTimedItemEffect(id = "", seconds = 0) {
|
||
|
|
this.ensureItemEffectState();
|
||
|
|
if (!(id in this.itemEffectTimers)) this.itemEffectTimers[id] = 0;
|
||
|
|
this.itemEffectTimers[id] = Math.max(this.itemEffectTimers[id] || 0, Number(seconds) || 0);
|
||
|
|
if (id === "laxative") this.laxativePoopTimer = Math.min(Math.max(this.laxativePoopTimer || 3, 0.2), 3);
|
||
|
|
},
|
||
|
|
|
||
|
|
itemSizeMultiplier() {
|
||
|
|
this.ensureItemEffectState();
|
||
|
|
if (this.sizeItemMode === "giant_drug") return 3;
|
||
|
|
if (this.sizeItemMode === "dwarf_drug") return 0.25;
|
||
|
|
return 1;
|
||
|
|
},
|
||
|
|
|
||
|
|
effectiveScale() {
|
||
|
|
return (this.scale || 0.28) * (this.itemSizeMultiplier ? this.itemSizeMultiplier() : 1);
|
||
|
|
},
|
||
|
|
|
||
|
|
refreshEffectiveSize() {
|
||
|
|
const mult = this.itemSizeMultiplier ? this.itemSizeMultiplier() : 1;
|
||
|
|
this.radius = 56 * (this.scale || 0.28) * mult;
|
||
|
|
return this.radius;
|
||
|
|
},
|
||
|
|
|
||
|
|
setPowerItemMode(mode = "") {
|
||
|
|
this.ensureItemEffectState();
|
||
|
|
if (mode !== "protein" && mode !== "niteropu") return false;
|
||
|
|
this.powerItemMode = mode;
|
||
|
|
this.powerEffectKind = mode;
|
||
|
|
this.powerEffectPulse = Math.max(this.powerEffectPulse || 0, 3.2);
|
||
|
|
this.spawnPowerItemEffect(mode, 1.5);
|
||
|
|
return true;
|
||
|
|
},
|
||
|
|
|
||
|
|
setSizeItemMode(mode = "") {
|
||
|
|
this.ensureItemEffectState();
|
||
|
|
if (mode !== "giant_drug" && mode !== "dwarf_drug") return false;
|
||
|
|
this.sizeItemMode = mode;
|
||
|
|
this.itemDamageTimer = Math.min(Math.max(this.itemDamageTimer || 1, 0.08), 1);
|
||
|
|
this.refreshEffectiveSize();
|
||
|
|
this.powerEffectKind = mode;
|
||
|
|
this.powerEffectPulse = Math.max(this.powerEffectPulse || 0, 4.0);
|
||
|
|
this.spawnPowerItemEffect(mode, mode === "giant_drug" ? 1.9 : 1.2);
|
||
|
|
return true;
|
||
|
|
},
|
||
|
|
|
||
|
|
setMercuryEffect() {
|
||
|
|
this.ensureItemEffectState();
|
||
|
|
if (this.lifeItemMode !== "mercury" || !Number.isFinite(this.mercuryLifeSpanBase)) {
|
||
|
|
this.mercuryLifeSpanBase = Number.isFinite(this.lifeSpan) ? this.lifeSpan : null;
|
||
|
|
this.mercuryLifeMultiplier = 1;
|
||
|
|
}
|
||
|
|
this.lifeItemMode = "mercury";
|
||
|
|
this.mercuryLifeMultiplier = Math.max(1, (this.mercuryLifeMultiplier || 1) * 1.3);
|
||
|
|
if (Number.isFinite(this.mercuryLifeSpanBase)) this.lifeSpan = Math.max(this.age + 5, this.mercuryLifeSpanBase * this.mercuryLifeMultiplier);
|
||
|
|
this.powerEffectKind = "mercury";
|
||
|
|
this.powerEffectPulse = Math.max(this.powerEffectPulse || 0, 2.7);
|
||
|
|
this.spawnPowerItemEffect("mercury", 0.95);
|
||
|
|
return true;
|
||
|
|
},
|
||
|
|
|
||
|
|
clearAllItemEffects() {
|
||
|
|
this.ensureItemEffectState();
|
||
|
|
for (const key of Object.keys(this.itemEffectTimers || {})) this.itemEffectTimers[key] = 0;
|
||
|
|
this.loveMochiTimer = 0;
|
||
|
|
this.fightMochiTimer = 0;
|
||
|
|
this.powerItemMode = "";
|
||
|
|
this.sizeItemMode = "";
|
||
|
|
if (this.lifeItemMode === "mercury" && Number.isFinite(this.mercuryLifeSpanBase)) {
|
||
|
|
this.lifeSpan = Math.max(this.age + 5, this.mercuryLifeSpanBase);
|
||
|
|
}
|
||
|
|
this.lifeItemMode = "";
|
||
|
|
this.mercuryLifeSpanBase = null;
|
||
|
|
this.mercuryLifeMultiplier = 1;
|
||
|
|
this.laxativePoopTimer = 3;
|
||
|
|
this.ammoBumpCooldown = 0;
|
||
|
|
this.itemDamageTimer = 1;
|
||
|
|
this.powerEffectKind = "zunda_juice";
|
||
|
|
this.powerEffectPulse = Math.max(this.powerEffectPulse || 0, 2.2);
|
||
|
|
this.refreshEffectiveSize();
|
||
|
|
this.spawnPowerItemEffect("zunda_juice", 1.4);
|
||
|
|
return true;
|
||
|
|
},
|
||
|
|
|
||
|
|
applyParamItemEffect(type = "", item = null, options = {}) {
|
||
|
|
this.ensureItemEffectState();
|
||
|
|
const scale = sizeDurationScale(item);
|
||
|
|
let applied = [];
|
||
|
|
if (type === "laxative") {
|
||
|
|
this.setTimedItemEffect("laxative", Math.round(60 * scale));
|
||
|
|
this.spawnPowerItemEffect("laxative", 0.75);
|
||
|
|
applied.push("laxative");
|
||
|
|
} else if (type === "protein") {
|
||
|
|
this.setPowerItemMode("protein");
|
||
|
|
applied.push("protein");
|
||
|
|
} else if (type === "niteropu") {
|
||
|
|
this.setPowerItemMode("niteropu");
|
||
|
|
applied.push("niteropu");
|
||
|
|
} else if (type === "ammo") {
|
||
|
|
this.setTimedItemEffect("ammo", Math.round(18 * scale));
|
||
|
|
this.spawnPowerItemEffect("ammo", 1.1);
|
||
|
|
applied.push("ammo");
|
||
|
|
} else if (type === "mercury") {
|
||
|
|
this.setMercuryEffect();
|
||
|
|
applied.push("mercury");
|
||
|
|
} else if (type === "giant_drug") {
|
||
|
|
this.setSizeItemMode("giant_drug");
|
||
|
|
applied.push("giant_drug");
|
||
|
|
} else if (type === "dwarf_drug") {
|
||
|
|
this.setSizeItemMode("dwarf_drug");
|
||
|
|
applied.push("dwarf_drug");
|
||
|
|
} else if (type === "zunda_juice") {
|
||
|
|
this.clearAllItemEffects();
|
||
|
|
applied.push("zunda_juice");
|
||
|
|
} else if (type === "mystery_drug") {
|
||
|
|
const picks = pickRandomEffects(2);
|
||
|
|
for (const effect of picks) this.applyRandomizedBehaviorEffect(effect, item);
|
||
|
|
this.mysteryDrugHistory = [...(this.mysteryDrugHistory || []), `${picks.map(effectLabel).join(" + ")}`].slice(-8);
|
||
|
|
this.bubble("???", 1.2, ITEM_EFFECT_COLORS.mystery_drug);
|
||
|
|
this.spawnPowerItemEffect("mystery_drug", 1.0);
|
||
|
|
applied = picks;
|
||
|
|
} else {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
if (!options.silentLog && this.world?.log) {
|
||
|
|
const label = type === "mystery_drug" ? `怪しい薬(${applied.map(effectLabel).join(" + ")})` : effectLabel(type);
|
||
|
|
this.world.log(`${this.name}に${label}の効果が出た。`, "food", { participants: [this] });
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
},
|
||
|
|
|
||
|
|
applyRandomizedBehaviorEffect(effect = "", item = null) {
|
||
|
|
const scale = sizeDurationScale(item);
|
||
|
|
if (effect === "love_mochi") {
|
||
|
|
this.loveMochiTimer = Math.max(this.loveMochiTimer || 0, 42 * scale + rand(4, 12));
|
||
|
|
this.stress = clamp(this.stress - 8, 0, 130);
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
if (effect === "fight_mochi") {
|
||
|
|
this.fightMochiTimer = Math.max(this.fightMochiTimer || 0, 52 * scale + rand(10, 18));
|
||
|
|
this.stress = clamp(this.stress + 10, 0, 130);
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
if (effect === "sleep_drug") return this.infectSleepDisease?.(item) || true;
|
||
|
|
if (effect === "laxative") { this.setTimedItemEffect("laxative", Math.round(60 * scale)); return true; }
|
||
|
|
if (effect === "ammo") { this.setTimedItemEffect("ammo", Math.round(18 * scale)); return true; }
|
||
|
|
if (effect === "protein") return this.setPowerItemMode("protein");
|
||
|
|
if (effect === "niteropu") return this.setPowerItemMode("niteropu");
|
||
|
|
if (effect === "mercury") return this.setMercuryEffect();
|
||
|
|
if (effect === "giant_drug") return this.setSizeItemMode("giant_drug");
|
||
|
|
if (effect === "dwarf_drug") return this.setSizeItemMode("dwarf_drug");
|
||
|
|
if (effect === "disease_zunchi") return this.infectZunchiDisease?.(item, true) || true;
|
||
|
|
if (effect === "disease_sleep") return this.infectSleepDisease?.(item) || true;
|
||
|
|
if (effect === "disease_explosion") return this.infectExplosionDisease?.(item) || true;
|
||
|
|
if (effect === "disease_fight") return this.infectFightDisease?.(item) || true;
|
||
|
|
return false;
|
||
|
|
},
|
||
|
|
|
||
|
|
diseaseChance(base = 0) {
|
||
|
|
const b = Math.max(0, Number(base) || 0);
|
||
|
|
if (this.powerItemMode === "protein") return b * 0.5;
|
||
|
|
if (this.powerItemMode === "niteropu") return b * 2.0;
|
||
|
|
return b;
|
||
|
|
},
|
||
|
|
|
||
|
|
outgoingDamage(amount = 0) {
|
||
|
|
let value = Number(amount) || 0;
|
||
|
|
if (this.powerItemMode === "protein") value *= 1.75;
|
||
|
|
if (this.powerItemMode === "niteropu") value *= 0.5;
|
||
|
|
if (this.sizeItemMode === "giant_drug") value *= 2.0;
|
||
|
|
if (this.sizeItemMode === "dwarf_drug") value *= 0.25;
|
||
|
|
return value;
|
||
|
|
},
|
||
|
|
|
||
|
|
itemSpeedMultiplier() {
|
||
|
|
let value = this.hasItemEffect("ammo") ? 10 : 1;
|
||
|
|
if (this.sizeItemMode === "giant_drug") value *= 1.5;
|
||
|
|
if (this.sizeItemMode === "dwarf_drug") value *= 0.5;
|
||
|
|
return value;
|
||
|
|
},
|
||
|
|
|
||
|
|
applyFoodHungerRelief(amount = 0) {
|
||
|
|
if (this.hasItemEffect("laxative")) return 0;
|
||
|
|
this.hunger -= Number(amount) || 0;
|
||
|
|
return amount;
|
||
|
|
},
|
||
|
|
|
||
|
|
updateItemEffects(dt = 0) {
|
||
|
|
this.ensureItemEffectState();
|
||
|
|
const step = Math.max(0, Number(dt) || 0);
|
||
|
|
this.refreshEffectiveSize();
|
||
|
|
for (const key of Object.keys(this.itemEffectTimers)) this.itemEffectTimers[key] = Math.max(0, (this.itemEffectTimers[key] || 0) - step);
|
||
|
|
this.ammoBumpCooldown = Math.max(0, (this.ammoBumpCooldown || 0) - step);
|
||
|
|
this.powerEffectPulse = Math.max(0, (this.powerEffectPulse || 0) - step);
|
||
|
|
if (this.hasItemEffect("laxative")) {
|
||
|
|
this.laxativePoopTimer = Math.max(0, (this.laxativePoopTimer || 3) - step);
|
||
|
|
while (this.laxativePoopTimer <= 0 && this.hasItemEffect("laxative")) {
|
||
|
|
this.laxativePoopTimer += 3;
|
||
|
|
this.forceLaxativePoop();
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
this.laxativePoopTimer = Math.min(Math.max(this.laxativePoopTimer || 3, 0.2), 3);
|
||
|
|
}
|
||
|
|
if (this.sizeItemMode === "giant_drug" || this.sizeItemMode === "dwarf_drug") {
|
||
|
|
this.itemDamageTimer = Math.max(0, (this.itemDamageTimer || 1) - step);
|
||
|
|
while (this.itemDamageTimer <= 0 && !this.dead && (this.sizeItemMode === "giant_drug" || this.sizeItemMode === "dwarf_drug")) {
|
||
|
|
this.itemDamageTimer += 1;
|
||
|
|
this.damage?.(1, this.sizeItemMode === "giant_drug" ? "巨大薬" : "矮小薬");
|
||
|
|
if (this.world?.effects) this.spawnPowerItemEffect(this.sizeItemMode, 0.25);
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
this.itemDamageTimer = Math.min(Math.max(this.itemDamageTimer || 1, 0.08), 1);
|
||
|
|
}
|
||
|
|
if (this.powerEffectPulse > 0.04 && Math.random() < step * 2.4) this.spawnPowerItemEffect(this.powerEffectKind || this.powerItemMode || this.sizeItemMode || this.lifeItemMode, 0.38);
|
||
|
|
if (this.powerItemMode === "protein" || this.powerItemMode === "niteropu") {
|
||
|
|
this.powerEffectKind = this.powerItemMode;
|
||
|
|
if (Math.random() < step * 0.72) this.spawnPowerItemEffect(this.powerItemMode, this.powerItemMode === "protein" ? 0.32 : 0.28);
|
||
|
|
}
|
||
|
|
if (this.hasItemEffect("ammo") && Math.random() < step * 3.0) this.spawnPowerItemEffect("ammo", 0.32);
|
||
|
|
if ((this.sizeItemMode === "giant_drug" || this.sizeItemMode === "dwarf_drug") && Math.random() < step * 1.4) this.spawnPowerItemEffect(this.sizeItemMode, 0.24);
|
||
|
|
},
|
||
|
|
|
||
|
|
forceLaxativePoop() {
|
||
|
|
if (!this.world || this.dead) return;
|
||
|
|
const side = this.facingDir ? this.facingDir() : (this.facing || 1);
|
||
|
|
const x = this.x - side * this.radius * rand(0.72, 0.96);
|
||
|
|
const y = this.y + this.radius * rand(0.25, 0.48);
|
||
|
|
this.world.spawnZunchi?.(x, y);
|
||
|
|
this.poopCount = (this.poopCount || 0) + 1;
|
||
|
|
this.digest = Math.max(0, (this.digest || 0) - 3.0);
|
||
|
|
this.bubble("ぶりゅっ", 1.2, ITEM_EFFECT_COLORS.laxative);
|
||
|
|
if ((this.world.time || 0) > (this.nextLaxativeLogAt || -999)) {
|
||
|
|
this.nextLaxativeLogAt = (this.world.time || 0) + 9;
|
||
|
|
this.world.log?.(`${this.name}は下剤の効果でずんちを放出した。`, "accident", { participants: [this] });
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
spawnPowerItemEffect(kind = "", intensity = 1) {
|
||
|
|
if (!this.world?.effects) return;
|
||
|
|
const color = kind === "protein"
|
||
|
|
? "rgba(224,54,40,0.88)"
|
||
|
|
: (kind === "niteropu" ? "rgba(116,80,190,0.84)" : (ITEM_EFFECT_COLORS[kind] || "rgba(255,242,160,0.85)"));
|
||
|
|
const n = Math.max(1, Math.round((kind === "protein" || kind === "niteropu" ? 3 : 2) * intensity));
|
||
|
|
for (let i = 0; i < n; i++) {
|
||
|
|
const angle = rand(0, Math.PI * 2);
|
||
|
|
const r = this.radius * rand(0.18, 0.82);
|
||
|
|
const type = kind === "niteropu" ? "zunchi_miasma" : (kind === "ammo" ? "fall" : (kind === "protein" ? "fall" : "ring"));
|
||
|
|
const startY = kind === "protein" ? this.y + this.radius * rand(0.1, 0.55) : (kind === "niteropu" ? this.y - this.radius * rand(0.1, 0.70) : this.y + Math.sin(angle) * r * 0.55);
|
||
|
|
const vy = kind === "protein" ? -rand(38, 82) * intensity : (kind === "niteropu" ? rand(26, 68) * intensity : Math.sin(angle) * rand(8, 26) * intensity - rand(10, 28) * (kind === "giant_drug" || kind === "mercury" ? 1 : 0.35));
|
||
|
|
this.world.effects.push(new Effect(type, this.x + Math.cos(angle) * r, startY, {
|
||
|
|
vx: (kind === "protein" || kind === "niteropu") ? rand(-18, 18) * intensity : Math.cos(angle) * rand(8, 34) * intensity,
|
||
|
|
vy,
|
||
|
|
size: rand(5, 11) * (0.75 + intensity * 0.30),
|
||
|
|
life: rand(0.42, 0.90),
|
||
|
|
color,
|
||
|
|
}));
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
ammoCollisionKnockback(dt = 0) {
|
||
|
|
if (!this.hasItemEffect("ammo") || !this.world || this.dead || (this.ammoBumpCooldown || 0) > 0) return;
|
||
|
|
const speed = Math.hypot(this.vx || 0, this.vy || 0);
|
||
|
|
if (speed < 120) return;
|
||
|
|
const nx = (this.vx || 1) / Math.max(speed, 1);
|
||
|
|
const ny = (this.vy || 0) / Math.max(speed, 1);
|
||
|
|
const hitRadius = this.radius + 18;
|
||
|
|
for (const o of this.world.nearbyTarinai?.(this.x, this.y, hitRadius + 28, true) || []) {
|
||
|
|
if (!o || o === this || o.dead) continue;
|
||
|
|
const d = distXY(this.x, this.y, o.x, o.y);
|
||
|
|
if (d > this.radius + o.radius + 12) continue;
|
||
|
|
const px = d > 0.01 ? (o.x - this.x) / d : nx;
|
||
|
|
const py = d > 0.01 ? (o.y - this.y) / d : ny;
|
||
|
|
const push = clamp(speed * 2.2, 260, 820);
|
||
|
|
o.impulseVx = (o.impulseVx || 0) + px * push;
|
||
|
|
o.impulseVy = (o.impulseVy || 0) + py * push;
|
||
|
|
o.vx += px * 160;
|
||
|
|
o.vy += py * 160;
|
||
|
|
o.hurtTimer = Math.max(o.hurtTimer || 0, 1.1);
|
||
|
|
o.fallTimer = Math.max(o.fallTimer || 0, 0.72);
|
||
|
|
this.world.spawnFallEffect?.(o.x, o.y + o.radius * 0.45, 1.05);
|
||
|
|
audio.damage?.(12);
|
||
|
|
this.ammoBumpCooldown = 0.12;
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
for (const ant of this.world.nearbyAnts?.(this.x, this.y, hitRadius + 22, true) || []) {
|
||
|
|
if (!ant || ant.dead) continue;
|
||
|
|
const d = distXY(this.x, this.y, ant.x, ant.y);
|
||
|
|
if (d > this.radius + (ant.r || 4) + 12) continue;
|
||
|
|
const px = d > 0.01 ? (ant.x - this.x) / d : nx;
|
||
|
|
const py = d > 0.01 ? (ant.y - this.y) / d : ny;
|
||
|
|
const push = clamp(speed * 1.7, 180, 620);
|
||
|
|
ant.vx = (ant.vx || 0) + px * push;
|
||
|
|
ant.vy = (ant.vy || 0) + py * push;
|
||
|
|
ant.hp = Math.max(0, (ant.hp ?? ant.maxHp ?? 32) - 4);
|
||
|
|
ant.hpBarTimer = Math.max(ant.hpBarTimer || 0, 1.2);
|
||
|
|
this.world.spawnFallEffect?.(ant.x, ant.y, 0.65);
|
||
|
|
audio.antDie?.();
|
||
|
|
this.ammoBumpCooldown = 0.10;
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
activeItemEffectSummary() {
|
||
|
|
this.ensureItemEffectState();
|
||
|
|
const rows = [];
|
||
|
|
const add = (id, timer = 0) => {
|
||
|
|
const label = effectLabel(id);
|
||
|
|
const detail = EFFECT_REGISTRY?.display?.(id, timer, this) || "";
|
||
|
|
rows.push({ id, label, detail });
|
||
|
|
};
|
||
|
|
if (this.powerItemMode === "protein") add("protein");
|
||
|
|
if (this.powerItemMode === "niteropu") add("niteropu");
|
||
|
|
if (this.lifeItemMode === "mercury") add("mercury");
|
||
|
|
if (this.sizeItemMode === "giant_drug") add("giant_drug");
|
||
|
|
if (this.sizeItemMode === "dwarf_drug") add("dwarf_drug");
|
||
|
|
if ((this.itemEffectTimers.laxative || 0) > 0.04) add("laxative", this.itemEffectTimers.laxative);
|
||
|
|
if ((this.itemEffectTimers.ammo || 0) > 0.04) add("ammo", this.itemEffectTimers.ammo);
|
||
|
|
if ((this.loveMochiTimer || 0) > 0.1) add("love_mochi", this.loveMochiTimer);
|
||
|
|
if ((this.fightMochiTimer || 0) > 0.1) add("fight_mochi", this.fightMochiTimer);
|
||
|
|
return rows;
|
||
|
|
},
|
||
|
|
}));
|
||
|
|
|
||
|
|
global.TARINAI_ITEM_EFFECT_LABELS = ITEM_EFFECT_LABELS;
|
||
|
|
global.TARINAI_ITEM_EFFECT_COLORS = ITEM_EFFECT_COLORS;
|
||
|
|
})(typeof window !== "undefined" ? window : globalThis);
|