280 lines
15 KiB
JavaScript
280 lines
15 KiB
JavaScript
"use strict";
|
|
|
|
(function (global) {
|
|
const rawDefinitions = {
|
|
laxative: {
|
|
label: "\u4e0b\u5264", color: "rgba(119, 136, 58, 0.78)", kind: "timed", defaultDuration: 60,
|
|
tags: ["item", "behavior", "food"], display: timer => `${Math.ceil(timer || 0)}\u79d2 / 3\u79d2\u3054\u3068\u306b\u305a\u3093\u3061 / \u98df\u4e8b\u306e\u7a7a\u8179\u56de\u5fa9\u306a\u3057`,
|
|
},
|
|
ammo: {
|
|
label: "\u5f3e\u85ac", color: "rgba(216, 142, 38, 0.82)", kind: "timed", defaultDuration: 18,
|
|
tags: ["item", "behavior", "movement"], display: timer => `${Math.ceil(timer || 0)}\u79d2 / \u6b69\u884c\u901f\u5ea610\u500d / \u885d\u7a81\u3067\u5f3e\u304d\u98db\u3070\u3059`,
|
|
},
|
|
protein: {
|
|
label: "\u30d7\u30ed\u30c6\u30a4\u30f3", color: "rgba(239, 122, 51, 0.86)", kind: "permanent", exclusiveGroup: "power",
|
|
tags: ["item", "behavior", "power"], display: () => "\u6c38\u7d9a / \u4e0e\u30c0\u30e1\u30fc\u30b81.75\u500d / \u75c5\u6c17\u78ba\u73870.5\u500d",
|
|
},
|
|
niteropu: {
|
|
label: "\u30f3\u30a4\u30c6\u30ed\u30d7", color: "rgba(88, 76, 166, 0.78)", kind: "permanent", exclusiveGroup: "power",
|
|
tags: ["item", "behavior", "power"], display: () => "\u6c38\u7d9a / \u4e0e\u30c0\u30e1\u30fc\u30b80.5\u500d / \u75c5\u6c17\u78ba\u73872\u500d",
|
|
},
|
|
mercury: {
|
|
label: "\u6c34\u9280", color: "rgba(96, 146, 166, 0.82)", kind: "permanent", stack: "multiply",
|
|
tags: ["item", "life"], display: (_timer, t) => `\u6c38\u7d9a / \u5bff\u547dx${((t && t.mercuryLifeMultiplier) || 1.3).toFixed(2)}\uff08\u98df\u3079\u308b\u305f\u3073\u4e57\u7b97\uff09`,
|
|
},
|
|
giant_drug: {
|
|
label: "\u5de8\u5927\u85ac", color: "rgba(228, 123, 58, 0.86)", kind: "permanent", exclusiveGroup: "body_size",
|
|
tags: ["item", "body", "behavior"], display: () => "\u6c38\u7d9a / \u30b5\u30a4\u30ba3\u500d / \u653b\u64832\u500d / \u901f\u5ea61.5\u500d / \u6bce\u79d21\u30c0\u30e1\u30fc\u30b8",
|
|
},
|
|
dwarf_drug: {
|
|
label: "\u77ee\u5c0f\u85ac", color: "rgba(109, 107, 190, 0.82)", kind: "permanent", exclusiveGroup: "body_size",
|
|
tags: ["item", "body", "behavior"], display: () => "\u6c38\u7d9a / \u30b5\u30a4\u30ba0.25\u500d / \u653b\u64830.25\u500d / \u901f\u5ea60.5\u500d / \u6bce\u79d21\u30c0\u30e1\u30fc\u30b8",
|
|
},
|
|
love_mochi: {
|
|
label: "\u3078\u3053\u9905", color: "rgba(190,82,132,0.78)", kind: "timer", timerProp: "loveMochiTimer",
|
|
tags: ["item", "mochi", "behavior"], display: timer => `${Math.ceil(timer || 0)}\u79d2 / \u7e41\u6b96\u884c\u52d5\u304c\u8d77\u304d\u3084\u3059\u3044`,
|
|
},
|
|
fight_mochi: {
|
|
label: "\u3051\u3093\u304b\u9905", color: "rgba(180,86,52,0.80)", kind: "timer", timerProp: "fightMochiTimer",
|
|
tags: ["item", "mochi", "behavior"], display: timer => `${Math.ceil(timer || 0)}\u79d2 / \u55a7\u5629\u304c\u8d77\u304d\u3084\u3059\u3044`,
|
|
},
|
|
sleep_drug: {
|
|
label: "\u306d\u3080\u308a\u85ac", color: "rgba(104,84,168,0.78)", kind: "disease", tags: ["item", "disease", "behavior"],
|
|
},
|
|
mystery_drug: {
|
|
label: "\u602a\u3057\u3044\u85ac", color: "rgba(60, 154, 87, 0.80)", kind: "instant", tags: ["item", "behavior"],
|
|
},
|
|
zunda_juice: {
|
|
label: "\u305a\u3093\u3060\u6c41", color: "rgba(87, 180, 79, 0.86)", kind: "cleanse", tags: ["item", "cleanse"],
|
|
},
|
|
disease_zunchi: { label: "\u305a\u3093\u3061\u75c5", color: "rgba(113, 93, 59, 0.74)", kind: "disease", tags: ["disease", "behavior"] },
|
|
disease_sleep: { label: "\u306d\u3080\u308a\u75c5", color: "rgba(104,84,168,0.78)", kind: "disease", tags: ["disease", "behavior"] },
|
|
disease_explosion: { label: "\u7206\u767a\u75c5", color: "rgba(210, 90, 44, 0.78)", kind: "disease", tags: ["disease", "behavior"] },
|
|
disease_fight: { label: "\u304d\u305a\u3064\u304d\u75c5", color: "rgba(180,86,52,0.80)", kind: "disease", tags: ["disease", "behavior"] },
|
|
};
|
|
|
|
Object.assign(rawDefinitions.laxative, {
|
|
modifiers: { hungerReliefMultiplier: 0 },
|
|
tickInterval: 3,
|
|
bubblePolicy: "none",
|
|
mysteryEligible: true,
|
|
zundaJuiceRemovable: true,
|
|
});
|
|
Object.assign(rawDefinitions.ammo, {
|
|
modifiers: { speedMultiplier: 10 },
|
|
visualEffectId: "fall",
|
|
bubblePolicy: "none",
|
|
mysteryEligible: true,
|
|
zundaJuiceRemovable: true,
|
|
});
|
|
Object.assign(rawDefinitions.protein, {
|
|
modifiers: { damageMultiplier: 1.75, diseaseChanceMultiplier: 0.5 },
|
|
visualEffectId: "fall_up_red",
|
|
bubblePolicy: "none",
|
|
mysteryEligible: true,
|
|
zundaJuiceRemovable: true,
|
|
});
|
|
Object.assign(rawDefinitions.niteropu, {
|
|
modifiers: { damageMultiplier: 0.5, diseaseChanceMultiplier: 2 },
|
|
visualEffectId: "fall_down_purple",
|
|
bubblePolicy: "none",
|
|
mysteryEligible: true,
|
|
zundaJuiceRemovable: true,
|
|
});
|
|
Object.assign(rawDefinitions.mercury, {
|
|
modifiers: { lifeMultiplierPerUse: 1.3 },
|
|
bubblePolicy: "none",
|
|
mysteryEligible: true,
|
|
zundaJuiceRemovable: true,
|
|
});
|
|
Object.assign(rawDefinitions.giant_drug, {
|
|
modifiers: { sizeMultiplier: 3, damageMultiplier: 2, speedMultiplier: 1.5, damagePerSecond: 1 },
|
|
bubblePolicy: "none",
|
|
mysteryEligible: true,
|
|
zundaJuiceRemovable: true,
|
|
});
|
|
Object.assign(rawDefinitions.dwarf_drug, {
|
|
modifiers: { sizeMultiplier: 0.25, damageMultiplier: 0.25, speedMultiplier: 0.5, damagePerSecond: 1 },
|
|
bubblePolicy: "none",
|
|
mysteryEligible: true,
|
|
zundaJuiceRemovable: true,
|
|
});
|
|
for (const id of ["love_mochi", "fight_mochi", "sleep_drug"]) {
|
|
Object.assign(rawDefinitions[id], { mysteryEligible: true, zundaJuiceRemovable: id !== "sleep_drug" });
|
|
}
|
|
Object.assign(rawDefinitions.zunda_juice, { removesItemEffects: true, mysteryEligible: false });
|
|
|
|
Object.assign(rawDefinitions.love_mochi, {
|
|
onApply(tarinai, context = {}) {
|
|
const scale = context.scale ?? 1;
|
|
tarinai.loveMochiTimer = Math.max(tarinai.loveMochiTimer || 0, 42 * scale + rand(4, 12));
|
|
const nutrition = Number(context.nutrition) || 0;
|
|
if (typeof applyNeedRelief === "function") applyNeedRelief(tarinai, { fulfill: -(context.source === "eating" ? nutrition * 0.5 : 12), social: -4 });
|
|
if (context.source === "eating") {
|
|
if (tarinai.forceBehavior) tarinai.forceBehavior("approach_mate", { source: "love_mochi", priority: 150, ttl: 24, searchRange: 820, reasonText: "へこ餅で繁殖できる相手を探している" });
|
|
else if (typeof queueForcedTarinaiBehavior === "function") queueForcedTarinaiBehavior(tarinai, "approach_mate", { source: "love_mochi", priority: 150, ttl: 24, searchRange: 820, reasonText: "へこ餅で繁殖できる相手を探している" });
|
|
}
|
|
return true;
|
|
},
|
|
});
|
|
Object.assign(rawDefinitions.fight_mochi, {
|
|
onApply(tarinai, context = {}) {
|
|
const scale = context.scale ?? 1;
|
|
tarinai.fightMochiTimer = Math.max(tarinai.fightMochiTimer || 0, 52 * scale + rand(10, 18));
|
|
const nutrition = Number(context.nutrition) || 0;
|
|
if (typeof applyNeedShock === "function") applyNeedShock(tarinai, { safety: context.source === "eating" ? nutrition * 0.5 : 16 });
|
|
if (context.source === "eating") {
|
|
tarinai.fearTimer = Math.max(tarinai.fearTimer || 0, 0.32);
|
|
if (tarinai.forceBehavior) tarinai.forceBehavior("fight_rival", { source: "fight_mochi", priority: 180, ttl: 24, searchRange: 820, reasonText: "けんか餅で気に入らない相手を探している" });
|
|
else if (typeof queueForcedTarinaiBehavior === "function") queueForcedTarinaiBehavior(tarinai, "fight_rival", { source: "fight_mochi", priority: 180, ttl: 24, searchRange: 820, reasonText: "けんか餅で気に入らない相手を探している" });
|
|
}
|
|
return true;
|
|
},
|
|
});
|
|
Object.assign(rawDefinitions.sleep_drug, {
|
|
onApply(tarinai, context = {}) {
|
|
if (context.source === "eating") {
|
|
const nutrition = Number(context.nutrition) || 0;
|
|
if (typeof applyNeedRelief === "function") applyNeedRelief(tarinai, { safety: -nutrition * 0.25, sleep: -nutrition * 0.15 });
|
|
tarinai.energy = clamp(tarinai.energy - nutrition * 0.20, 0, 100);
|
|
tarinai.fearTimer = Math.max(0, (tarinai.fearTimer || 0) - 0.18);
|
|
tarinai.fightMochiTimer = Math.max(0, (tarinai.fightMochiTimer || 0) - 2.5);
|
|
const baseChance = Math.min(0.98, 0.35 + nutrition * 0.075);
|
|
const sleepChance = tarinai.diseaseChance ? tarinai.diseaseChance(baseChance) : baseChance;
|
|
if (tarinai.forceBehavior) tarinai.forceBehavior("sleep_anywhere", { source: "sleep_drug", priority: 145, ttl: 14, duration: 8, minDuration: 8, reasonText: "ねむり薬で眠ろうとしている" });
|
|
else if (typeof queueForcedTarinaiBehavior === "function") queueForcedTarinaiBehavior(tarinai, "sleep_anywhere", { source: "sleep_drug", priority: 145, ttl: 14, duration: 8, minDuration: 8, reasonText: "ねむり薬で眠ろうとしている" });
|
|
if (!tarinai.sleepDisease && Math.random() < Math.min(0.98, sleepChance)) return tarinai.infectSleepDisease?.(context.item || null) || true;
|
|
return true;
|
|
}
|
|
return tarinai.infectSleepDisease?.(context.item || null) || true;
|
|
},
|
|
});
|
|
Object.assign(rawDefinitions.laxative, {
|
|
onApply(tarinai, context = {}) {
|
|
return tarinai.setTimedItemEffect?.("laxative", Math.round((this.defaultDuration || 60) * (context.scale ?? 1)));
|
|
},
|
|
onTick(tarinai) {
|
|
tarinai.forceLaxativePoop?.();
|
|
return true;
|
|
},
|
|
});
|
|
Object.assign(rawDefinitions.ammo, {
|
|
onApply(tarinai, context = {}) {
|
|
return tarinai.setTimedItemEffect?.("ammo", Math.round((this.defaultDuration || 18) * (context.scale ?? 1)));
|
|
},
|
|
});
|
|
Object.assign(rawDefinitions.protein, {
|
|
onApply(tarinai) { return tarinai.setPowerItemMode?.("protein"); },
|
|
});
|
|
Object.assign(rawDefinitions.niteropu, {
|
|
onApply(tarinai) { return tarinai.setPowerItemMode?.("niteropu"); },
|
|
});
|
|
Object.assign(rawDefinitions.mercury, {
|
|
onApply(tarinai) { return tarinai.setMercuryEffect?.(); },
|
|
});
|
|
Object.assign(rawDefinitions.giant_drug, {
|
|
onApply(tarinai) { return tarinai.setSizeItemMode?.("giant_drug"); },
|
|
onTick(tarinai) {
|
|
tarinai.damage?.(this.modifiers?.damagePerSecond || 1, this.label);
|
|
if (tarinai.world?.effects) tarinai.spawnPowerItemEffect?.("giant_drug", 0.25);
|
|
return true;
|
|
},
|
|
});
|
|
Object.assign(rawDefinitions.dwarf_drug, {
|
|
onApply(tarinai) { return tarinai.setSizeItemMode?.("dwarf_drug"); },
|
|
onTick(tarinai) {
|
|
tarinai.damage?.(this.modifiers?.damagePerSecond || 1, this.label);
|
|
if (tarinai.world?.effects) tarinai.spawnPowerItemEffect?.("dwarf_drug", 0.25);
|
|
return true;
|
|
},
|
|
});
|
|
Object.assign(rawDefinitions.zunda_juice, {
|
|
onApply(tarinai) { return tarinai.clearAllItemEffects?.(); },
|
|
});
|
|
Object.assign(rawDefinitions.disease_zunchi, {
|
|
onApply(tarinai, context = {}) { return tarinai.infectZunchiDisease?.(context.item || null, true) || true; },
|
|
});
|
|
Object.assign(rawDefinitions.disease_sleep, {
|
|
onApply(tarinai, context = {}) { return tarinai.infectSleepDisease?.(context.item || null) || true; },
|
|
});
|
|
Object.assign(rawDefinitions.disease_explosion, {
|
|
onApply(tarinai, context = {}) { return tarinai.infectExplosionDisease?.(context.item || null) || true; },
|
|
});
|
|
Object.assign(rawDefinitions.disease_fight, {
|
|
onApply(tarinai, context = {}) { return tarinai.infectFightDisease?.(context.item || null) || true; },
|
|
});
|
|
|
|
class EffectRegistry extends global.TarinaiRegistryBase {
|
|
label(id = "") { return super.label(id, id || "\u4e0d\u660e"); }
|
|
color(id = "") { return super.color(id, "rgba(255,242,160,0.85)"); }
|
|
display(id = "", timer = 0, tarinai = null) {
|
|
const def = this.get(id);
|
|
if (!def) return "";
|
|
return typeof def.display === "function" ? def.display(timer, tarinai, def) : (def.display || "");
|
|
}
|
|
apply(id = "", tarinai = null, context = {}) {
|
|
const def = this.get(id);
|
|
if (!def || !tarinai || typeof def.onApply !== "function") return false;
|
|
return Boolean(def.onApply.call(def, tarinai, { ...context, effectId: id, registry: this }));
|
|
}
|
|
tick(id = "", tarinai = null, dt = 0, context = {}) {
|
|
const def = this.get(id);
|
|
if (!def || !tarinai || typeof def.onTick !== "function") return false;
|
|
return Boolean(def.onTick.call(def, tarinai, Math.max(0, Number(dt) || 0), { ...context, effectId: id, registry: this }));
|
|
}
|
|
remove(id = "", tarinai = null, context = {}) {
|
|
const def = this.get(id);
|
|
if (!def || !tarinai || typeof def.onRemove !== "function") return false;
|
|
return Boolean(def.onRemove.call(def, tarinai, { ...context, effectId: id, registry: this }));
|
|
}
|
|
modifier(id = "", key = "", fallback = 1) {
|
|
const value = this.get(id)?.modifiers?.[key];
|
|
return Number.isFinite(value) ? value : fallback;
|
|
}
|
|
timerValue(tarinai = null, id = "") {
|
|
const def = this.get(id);
|
|
if (!def || !tarinai) return 0;
|
|
if (def.timerProp) return Number(tarinai[def.timerProp] || 0);
|
|
if (def.kind === "timed") return Number(tarinai.itemEffectTimers?.[id] || 0);
|
|
return 0;
|
|
}
|
|
isActive(tarinai = null, id = "") {
|
|
if (!tarinai) return false;
|
|
const def = this.get(id);
|
|
if (!def) return false;
|
|
if (id === "protein" || id === "niteropu") return tarinai.powerItemMode === id;
|
|
if (id === "giant_drug" || id === "dwarf_drug") return tarinai.sizeItemMode === id;
|
|
if (id === "mercury") return tarinai.lifeItemMode === "mercury";
|
|
return this.timerValue(tarinai, id) > 0.04;
|
|
}
|
|
activeSummary(tarinai = null) {
|
|
const order = ["protein", "niteropu", "mercury", "giant_drug", "dwarf_drug", "laxative", "ammo", "love_mochi", "fight_mochi"];
|
|
return order
|
|
.filter(id => this.isActive(tarinai, id))
|
|
.map(id => ({
|
|
id,
|
|
label: this.label(id),
|
|
detail: this.display(id, this.timerValue(tarinai, id), tarinai),
|
|
}));
|
|
}
|
|
labels() { return super.labels(); }
|
|
colors() { return super.colors(); }
|
|
randomPool() {
|
|
const itemEffects = [...this.defs.entries()]
|
|
.filter(([id, def]) => Boolean(def.mysteryEligible && id !== "mystery_drug" && id !== "zunda_juice" && !(def.tags || []).includes("disease")))
|
|
.map(([id]) => id)
|
|
.filter(id => id !== "mystery_drug" && id !== "zunda_juice");
|
|
const diseases = global.TarinaiDiseaseRegistry?.mysteryPool?.() || [];
|
|
return [...new Set([...itemEffects, ...diseases])];
|
|
}
|
|
permanentIds() { return [...this.defs.entries()].filter(([, def]) => def.kind === "permanent").map(([id]) => id); }
|
|
timedIds() { return [...this.defs.entries()].filter(([, def]) => def.kind === "timed" || def.kind === "timer").map(([id]) => id); }
|
|
debugSnapshot() {
|
|
return [...this.defs.entries()].map(([id, def]) => ({ id, label: def.label, kind: def.kind, group: def.exclusiveGroup || "" }));
|
|
}
|
|
}
|
|
|
|
const registry = new EffectRegistry(rawDefinitions);
|
|
global.TarinaiEffectRegistry = registry;
|
|
global.TARINAI_EFFECT_DEFINITIONS = Object.freeze(rawDefinitions);
|
|
})(typeof window !== "undefined" ? window : globalThis);
|