"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);