385 lines
20 KiB
JavaScript
385 lines
20 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.TarinaiItemRegistry.effect || null;
|
|
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) || id || "\u4e0d\u660e";
|
|
}
|
|
|
|
function effectColor(id = "", fallback = "rgba(255,242,160,0.85)") {
|
|
return EFFECT_REGISTRY?.color?.(id) || fallback;
|
|
}
|
|
|
|
function sizeDurationScale(item = null) {
|
|
const size = item?.toolSize || "medium";
|
|
if (size === "small") return 0.74;
|
|
if (size === "large") return 1.45;
|
|
return 1;
|
|
}
|
|
|
|
function recordItemChange(tarinai, id = "", reason = "\u30a2\u30a4\u30c6\u30e0\u52b9\u679c") {
|
|
tarinai?.recordChangeCause?.(reason, effectLabel(id));
|
|
}
|
|
|
|
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);
|
|
this.world?.emit?.("effect:applied", { tarinai: this, effectId: id, duration: this.itemEffectTimers[id] || 0 });
|
|
recordItemChange(this, id, "\u6642\u9593\u52b9\u679c");
|
|
return true;
|
|
},
|
|
|
|
itemSizeMultiplier() {
|
|
this.ensureItemEffectState();
|
|
if (this.sizeItemMode === "giant_drug") return EFFECT_REGISTRY?.modifier?.("giant_drug", "sizeMultiplier", 3) ?? 3;
|
|
if (this.sizeItemMode === "dwarf_drug") return EFFECT_REGISTRY?.modifier?.("dwarf_drug", "sizeMultiplier", 0.25) ?? 0.25;
|
|
return 1;
|
|
},
|
|
|
|
effectiveScale() {
|
|
return (this.scale || 0.28) * (this.itemSizeMultiplier ? this.itemSizeMultiplier() : 1);
|
|
},
|
|
|
|
refreshEffectiveSize() {
|
|
const oldMax = Number.isFinite(this.maxEnergy) ? Math.max(1, this.maxEnergy) : 100;
|
|
const oldEnergy = Number.isFinite(this.energy) ? this.energy : oldMax;
|
|
const mult = this.itemSizeMultiplier ? this.itemSizeMultiplier() : 1;
|
|
this.radius = 56 * (this.scale || 0.28) * mult;
|
|
const nextMax = typeof tarinaiMaxEnergy === "function" ? tarinaiMaxEnergy(this) : 100;
|
|
this.maxEnergy = nextMax;
|
|
const ratio = clamp(oldEnergy / oldMax, 0, 1.35);
|
|
this.energy = clamp(ratio * nextMax, 0, nextMax);
|
|
return this.radius;
|
|
},
|
|
|
|
energyRatio() {
|
|
return typeof tarinaiEnergyRatio === "function" ? tarinaiEnergyRatio(this) : clamp((this.energy || 0) / Math.max(1, this.maxEnergy || 100), 0, 1);
|
|
},
|
|
|
|
metabolicHungerMultiplier() {
|
|
return typeof tarinaiMetabolicHungerScale === "function" ? tarinaiMetabolicHungerScale(this) : 1;
|
|
},
|
|
|
|
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);
|
|
this.world?.emit?.("effect:applied", { tarinai: this, effectId: mode, permanent: true });
|
|
recordItemChange(this, mode, "\u80fd\u529b\u5909\u5316");
|
|
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);
|
|
this.world?.emit?.("effect:applied", { tarinai: this, effectId: mode, permanent: true });
|
|
recordItemChange(this, mode, "\u4f53\u683c\u5909\u5316");
|
|
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);
|
|
this.world?.emit?.("effect:applied", { tarinai: this, effectId: "mercury", permanent: true, multiplier: this.mercuryLifeMultiplier });
|
|
recordItemChange(this, "mercury", "\u5bff\u547d\u5909\u5316");
|
|
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);
|
|
this.world?.emit?.("effect:removed", { tarinai: this, effectId: "all_removable", source: "zunda_juice" });
|
|
recordItemChange(this, "zunda_juice", "\u52b9\u679c\u89e3\u9664");
|
|
return true;
|
|
},
|
|
|
|
applyParamItemEffect(type = "", item = null, options = {}) {
|
|
this.ensureItemEffectState();
|
|
const scale = sizeDurationScale(item);
|
|
let applied = [];
|
|
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, effectColor("mystery_drug"));
|
|
this.spawnPowerItemEffect("mystery_drug", 1.0);
|
|
applied = picks;
|
|
} else if (EFFECT_REGISTRY?.apply?.(type, this, { item, scale, source: options.source || "item", nutrition: options.nutrition })) {
|
|
applied.push(type);
|
|
if (type === "laxative") {
|
|
this.spawnPowerItemEffect("laxative", 0.75);
|
|
} else if (type === "ammo") {
|
|
this.spawnPowerItemEffect("ammo", 1.1);
|
|
}
|
|
} else {
|
|
return false;
|
|
}
|
|
if (!options.silentLog && this.world?.log) {
|
|
const label = type === "mystery_drug" ? `\u602a\u3057\u3044\u85ac(${applied.map(effectLabel).join(" + ")})` : effectLabel(type);
|
|
this.world.log(`${this.name}\u306b${label}\u306e\u52b9\u679c\u304c\u51fa\u305f\u3002`, "food", { participants: [this] });
|
|
}
|
|
this.recordChangeCause?.("\u30a2\u30a4\u30c6\u30e0\u6442\u53d6", applied.map(effectLabel).join(" + ") || effectLabel(type));
|
|
return true;
|
|
},
|
|
|
|
applyRandomizedBehaviorEffect(effect = "", item = null) {
|
|
const scale = sizeDurationScale(item);
|
|
return Boolean(EFFECT_REGISTRY?.apply?.(effect, this, { item, scale, source: "mystery_drug" }));
|
|
},
|
|
|
|
diseaseChance(base = 0) {
|
|
const b = Math.max(0, Number(base) || 0);
|
|
if (this.powerItemMode === "protein") return b * (EFFECT_REGISTRY?.modifier?.("protein", "diseaseChanceMultiplier", 0.5) ?? 0.5);
|
|
if (this.powerItemMode === "niteropu") return b * (EFFECT_REGISTRY?.modifier?.("niteropu", "diseaseChanceMultiplier", 2.0) ?? 2.0);
|
|
return b;
|
|
},
|
|
|
|
outgoingDamage(amount = 0) {
|
|
let value = (Number(amount) || 0) * (this.geneticStatRatio ? this.geneticStatRatio("attack") : 1);
|
|
if (this.powerItemMode === "protein") value *= EFFECT_REGISTRY?.modifier?.("protein", "damageMultiplier", 1.75) ?? 1.75;
|
|
if (this.powerItemMode === "niteropu") value *= EFFECT_REGISTRY?.modifier?.("niteropu", "damageMultiplier", 0.5) ?? 0.5;
|
|
if (this.sizeItemMode === "giant_drug") value *= EFFECT_REGISTRY?.modifier?.("giant_drug", "damageMultiplier", 2.0) ?? 2.0;
|
|
if (this.sizeItemMode === "dwarf_drug") value *= EFFECT_REGISTRY?.modifier?.("dwarf_drug", "damageMultiplier", 0.25) ?? 0.25;
|
|
return value;
|
|
},
|
|
|
|
itemSpeedMultiplier() {
|
|
let value = this.hasItemEffect("ammo") ? (EFFECT_REGISTRY?.modifier?.("ammo", "speedMultiplier", 10) ?? 10) : 1;
|
|
if (this.sizeItemMode === "giant_drug") value *= EFFECT_REGISTRY?.modifier?.("giant_drug", "speedMultiplier", 1.5) ?? 1.5;
|
|
if (this.sizeItemMode === "dwarf_drug") value *= EFFECT_REGISTRY?.modifier?.("dwarf_drug", "speedMultiplier", 0.5) ?? 0.5;
|
|
return value;
|
|
},
|
|
|
|
applyFoodHungerRelief(amount = 0) {
|
|
const raw = Math.max(0, Number(amount) || 0);
|
|
const actual = this.hasItemEffect("laxative") ? raw * (EFFECT_REGISTRY?.modifier?.("laxative", "hungerReliefMultiplier", 0) ?? 0) : raw;
|
|
const now = this.world?.time || 0;
|
|
if (actual >= 3.5) {
|
|
const day = Number(this.world?.config?.dayLength || CONFIG?.dayLength || 120) || 120;
|
|
const base = day / 3.65;
|
|
const jitter = 0.82 + stableUnit(this.familyKey || this.id, "meal-cooldown") * 0.36;
|
|
this.mealCooldownUntil = Math.max(this.mealCooldownUntil || 0, now + base * jitter);
|
|
this.needSatisfaction = this.needSatisfaction || (typeof createDefaultNeeds === "function" ? createDefaultNeeds() : {});
|
|
this.needSatisfaction.food = Math.max(this.needSatisfaction.food || 0, Math.min(88, actual * 1.85));
|
|
this.actionCooldowns = this.actionCooldowns || {};
|
|
this.actionCooldowns.food = Math.max(this.actionCooldowns.food || 0, 5.2);
|
|
}
|
|
this.hunger -= actual;
|
|
if (actual >= 3.5 && this.hunger < 68 && (getTarinaiBehaviorNeed(this)) === "food" && !(typeof isTarinaiBehaviorForced === "function" ? isTarinaiBehaviorForced(this) : Boolean(this.behavior?.source === "forced" || this.behavior?.forcedId || this.behavior?.forcedRequest))) {
|
|
this.behaviorLockTimer = Math.min(this.behaviorLockTimer || 0, 0.25);
|
|
}
|
|
return actual;
|
|
},
|
|
|
|
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 += EFFECT_REGISTRY?.get?.("laxative")?.tickInterval || 3;
|
|
EFFECT_REGISTRY?.tick?.("laxative", this, step);
|
|
}
|
|
} 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;
|
|
EFFECT_REGISTRY?.tick?.(this.sizeItemMode, this, step);
|
|
}
|
|
} 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;
|
|
if (this.hasLodgedPinEffect?.("blocksZunchi")) {
|
|
this.oshiriByoZunchiStock = Math.min(24, Math.max(0, this.oshiriByoZunchiStock || 0) + 1);
|
|
this.thought = (this.oshiriByoZunchiStock || 0) >= 6 ? "\u304a\u3057\u308a\u92f2\u3067\u305a\u3093\u3061\u304c\u6e9c\u307e\u3063\u3066\u3064\u3089\u3044" : "\u304a\u3057\u308a\u92f2\u3067\u305a\u3093\u3061\u3092\u6211\u6162\u3057\u3066\u3044\u308b";
|
|
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);
|
|
const now = this.world?.time || 0;
|
|
this.bubble("\u3076\u308a\u3085\u3063", 1.2, effectColor("laxative"), { force: true });
|
|
if ((this.world.time || 0) > (this.nextLaxativeLogAt || -999)) {
|
|
this.nextLaxativeLogAt = (this.world.time || 0) + 9;
|
|
this.world.log?.(`${this.name}\u306f\u4e0b\u5264\u306e\u52b9\u679c\u3067\u305a\u3093\u3061\u3092\u653e\u51fa\u3057\u305f\u3002`, "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)" : effectColor(kind));
|
|
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);
|
|
const bumpDamage = Math.max(0.6, Math.min(3.0, speed / 260));
|
|
o.damage?.(bumpDamage, "\u5f3e\u85ac\u885d\u7a81");
|
|
this.adjustPersonality?.("aggression", 0.012, "after ammo knockback");
|
|
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();
|
|
if (EFFECT_REGISTRY.activeSummary) return EFFECT_REGISTRY.activeSummary(this);
|
|
return [];
|
|
},
|
|
}));
|
|
|
|
})(typeof window !== "undefined" ? window : globalThis);
|