"use strict"; function tarinaiRoundRectPath(ctx, x, y, w, h, r) { const rr = Math.max(0, Math.min(r || 0, Math.abs(w) / 2, Math.abs(h) / 2)); ctx.beginPath(); ctx.moveTo(x + rr, y); ctx.lineTo(x + w - rr, y); ctx.quadraticCurveTo(x + w, y, x + w, y + rr); ctx.lineTo(x + w, y + h - rr); ctx.quadraticCurveTo(x + w, y + h, x + w - rr, y + h); ctx.lineTo(x + rr, y + h); ctx.quadraticCurveTo(x, y + h, x, y + h - rr); ctx.lineTo(x, y + rr); ctx.quadraticCurveTo(x, y, x + rr, y); } function tarinaiChampionName(name = "") { const map = { "\u306F": "\u8987", "\u3046": "\u5B87", "\u3045": "\u30A5", "\u3063": "\uFF01" }; return String(name || "").replace(/[\u306F\u3046\u3045\u3063]/g, ch => map[ch] || ch); } if (typeof window !== "undefined") window.tarinaiChampionName = tarinaiChampionName; if (typeof globalThis !== "undefined") globalThis.tarinaiChampionName = tarinaiChampionName; class Tarinai { constructor(world, opts = {}) { if (world && !opts.birthSeed && !opts.familyKey && !opts.id && globalThis.TarinaiSeedFactory.prepareAddOptions) { opts = globalThis.TarinaiSeedFactory.prepareAddOptions(world, opts) || opts; } this.world = world; this.birthSeed = opts.birthSeed || opts.familyKey || opts.id || (globalThis.TarinaiSeedFactory.childSeed(world?.worldSeed || "", Number(world?.birthSerial || 0) + 1, []) || (crypto.randomUUID ? crypto.randomUUID() : `${Date.now()}-${Math.random()}`)); this.birthSerial = Number.isFinite(opts.birthSerial) ? Number(opts.birthSerial) : (globalThis.TarinaiSeedFactory.serialFromBirthSeed(this.birthSeed) || 0); const birthProfile = globalThis.TarinaiSeedFactory.birthProfile(this.birthSeed, opts) || {}; this.familyKey = opts.familyKey || this.birthSeed; this.id = opts.id || ""; this.liveToken = opts.liveToken || 0; this.name = opts.name || birthProfile.name || makeName(world); this.type = opts.type || birthProfile.type || baseSpriteId(); const typeMeta = SPRITES.find(s => s.id === this.type); if (typeMeta?.actionOnly || typeMeta?.displayOnly) this.type = birthProfile.type || baseSpriteId(); this.birthPersonality = normalizePersonality(opts.birthPersonality || birthProfile.birthPersonality); this.currentPersonality = normalizePersonality(opts.currentPersonality || this.birthPersonality); this.personalityDaily = opts.personalityDaily && typeof opts.personalityDaily === "object" ? JSON.parse(JSON.stringify(opts.personalityDaily)) : null; ensurePersonality(this); this.genetics = normalizeGenetics(opts.genetics || birthProfile.genetics, this.birthSeed); this.trait = makeTrait(this.type); this.trait.speed *= this.genetics.speedMul || 1; this.birthTime = opts.birthTime ?? this.world.time; const generationOpt = opts.generation ?? 1; const growthAtConstruct = generationOpt > 1 ? clamp(((this.world?.time || 0) - this.birthTime) / Math.max(1, CONFIG.childGrowTime || 120), 0, 1) : 1; this.x = opts.x ?? rand(100, world.w - 100); this.y = opts.y ?? rand(100, world.h - 100); this._lastValidX = Number.isFinite(this.x) ? this.x : null; this._lastValidY = Number.isFinite(this.y) ? this.y : null; this._lastSunbathDrawX = Number.isFinite(opts._lastSunbathDrawX) ? opts._lastSunbathDrawX : null; this._lastSunbathDrawY = Number.isFinite(opts._lastSunbathDrawY) ? opts._lastSunbathDrawY : null; this._activeSunbathSpriteId = typeof opts._activeSunbathSpriteId === "string" ? opts._activeSunbathSpriteId : ""; this.vx = opts.vx ?? rand(-12, 12); this.vy = opts.vy ?? rand(-12, 12); this.impulseVx = opts.impulseVx ?? 0; this.impulseVy = opts.impulseVy ?? 0; this.adultScale = Number.isFinite(opts.adultScale) ? opts.adultScale : (Number.isFinite(opts.baseAdultScale) ? opts.baseAdultScale : (Number.isFinite(opts.scale) && growthAtConstruct >= 1 ? opts.scale : (Number.isFinite(birthProfile.adultScale) ? birthProfile.adultScale : adultScaleFromGenetics(this.birthSeed, this.genetics)))); this.scale = Number.isFinite(opts.scale) ? opts.scale : (generationOpt > 1 ? scaleForGrowth(this.adultScale, growthAtConstruct) : this.adultScale); this.radius = 56 * this.scale; this.age = opts.age ?? rand(0, 20); this.lifeSpan = opts.lifeSpan ?? (Number.isFinite(birthProfile.lifeSpan) ? birthProfile.lifeSpan : rand(1400, 2200) * (this.genetics.lifeSpanMul || 1)); if (this.lifeSpan < 900) this.lifeSpan = Number.isFinite(birthProfile.lifeSpan) ? birthProfile.lifeSpan : rand(1200, 1800) * (this.genetics.lifeSpanMul || 1); this.generation = generationOpt; this.hasPaired = opts.hasPaired ?? false; this.hunger = opts.hunger ?? rand(16, 45); this.loneliness = opts.loneliness ?? rand(18, 62); const initialDayP = world?.dayProgress?.() || 0; const initialNight = initialDayP >= 0.66 || initialDayP < 0.18; const initialAfternoon = initialDayP >= 0.46 && initialDayP < 0.58; this.energy = opts.energy ?? (initialNight ? rand(34, 68) : (initialAfternoon ? rand(50, 82) : rand(66, 98))); this.circadianSleepPressure = Number.isFinite(opts.circadianSleepPressure) ? opts.circadianSleepPressure : (initialNight ? rand(58, 78) : (initialAfternoon ? rand(8, 18) : rand(0, 5))); const defaultNeeds = typeof createDefaultNeeds === "function" ? createDefaultNeeds() : { food: 0, sleep: 0, health: 0, safety: 0, social: 0, fulfill: 0 }; this.needs = opts.needs && typeof opts.needs === "object" ? { ...defaultNeeds, ...opts.needs } : { ...defaultNeeds }; this.needDisplay = opts.needDisplay && typeof opts.needDisplay === "object" ? { ...defaultNeeds, ...opts.needDisplay } : { ...this.needs }; this.needRaw = opts.needRaw && typeof opts.needRaw === "object" ? { ...defaultNeeds, ...opts.needRaw } : { ...this.needs }; this.needRawBase = opts.needRawBase && typeof opts.needRawBase === "object" ? { ...defaultNeeds, ...opts.needRawBase } : { ...this.needRaw }; this.needSatisfaction = opts.needSatisfaction && typeof opts.needSatisfaction === "object" ? { ...defaultNeeds, ...opts.needSatisfaction } : { ...defaultNeeds }; this.stress = typeof calculateStressFromNeeds === "function" ? calculateStressFromNeeds(this.needRaw || this.needs) : 0; this.previousNeeds = opts.previousNeeds && typeof opts.previousNeeds === "object" ? { ...defaultNeeds, ...opts.previousNeeds } : { ...this.needs }; this.need = ""; this.state = opts.state || "idle"; this.behaviorSerial = opts.behaviorSerial ?? 0; this.behavior = typeof globalThis.normalizeTarinaiBehaviorState === "function" ? globalThis.normalizeTarinaiBehaviorState(opts, this) : (opts.behavior && typeof opts.behavior === "object" ? { ...opts.behavior } : null); this.forcedBehaviorQueue = Array.isArray(opts.forcedBehaviorQueue) ? opts.forcedBehaviorQueue.map(v => ({ ...(v || {}) })).slice(-8) : []; this.actionCooldowns = opts.actionCooldowns && typeof opts.actionCooldowns === "object" ? { ...opts.actionCooldowns } : {}; this.needShock = opts.needShock && typeof opts.needShock === "object" ? { ...opts.needShock } : {}; this.buildPlan = opts.buildPlan && typeof opts.buildPlan === "object" ? { ...opts.buildPlan } : null; this.sleepSession = opts.sleepSession && typeof opts.sleepSession === "object" ? { ...opts.sleepSession } : null; this.hpBarTimer = opts.hpBarTimer ?? 0; this.stressBarTimer = opts.stressBarTimer ?? 0; this.affection = opts.affection ?? rand(0, 30); this.dead = !!opts.dead; this.deathReason = opts.deathReason || ""; this.lastDamageCause = opts.lastDamageCause || ""; this.lastDamageAmount = opts.lastDamageAmount ?? 0; this.lastDamageAt = opts.lastDamageAt ?? -999; this.lastMajorDamageCause = opts.lastMajorDamageCause || ""; this.lastMajorDamageAt = opts.lastMajorDamageAt ?? -999; this.thought = ""; this.target = null; this.targetKey = ""; this.targetSince = 0; this.targetBestDist = Infinity; this.targetGiveupKey = opts.targetGiveupKey || ""; this.targetGiveupUntil = opts.targetGiveupUntil ?? 0; this.panicTarget = opts.panicTarget || null; this.panicTargetUntil = opts.panicTargetUntil ?? 0; this.panicStuckTimer = opts.panicStuckTimer ?? 0; this.wanderAngle = rand(0, Math.PI * 2); this.wanderTarget = opts.wanderTarget && typeof opts.wanderTarget === "object" ? { ...opts.wanderTarget } : null; this.reproductionTimer = rand(4, CONFIG.reproductionCooldown); this.lastLog = 0; this.sleeping = false; this.awakeLockTimer = opts.awakeLockTimer ?? 0; this.goodMode = opts.goodMode ?? birthProfile.goodMode ?? stableChoice(this.birthSeed || this.familyKey, "good-mode", displayNormalSprites()) ?? "smile"; this.facing = opts.facing ?? (stableUnit(this.familyKey, "facing") < 0.5 ? -1 : 1); this.visualFacing = opts.visualFacing ?? this.facing; this.facingLockUntil = opts.facingLockUntil ?? 0; this.eatTimer = opts.eatTimer ?? 0; this.eatCooldown = opts.eatCooldown ?? 0; this.mealCooldownUntil = Number.isFinite(opts.mealCooldownUntil) ? opts.mealCooldownUntil : 0; this.foodReactTimer = opts.foodReactTimer ?? 0; this.surpriseTimer = opts.surpriseTimer ?? 0; this.fearTimer = opts.fearTimer ?? 0; this.sunbathTimer = opts.sunbathTimer ?? 0; this.sunbathCooldown = opts.sunbathCooldown ?? 0; this.sunbathFrameTimer = opts.sunbathFrameTimer ?? 0; this.sunbathSpriteVariant = opts.sunbathSpriteVariant ?? 1; this.sunbathAnchorX = Number.isFinite(opts.sunbathAnchorX) ? opts.sunbathAnchorX : null; this.sunbathAnchorY = Number.isFinite(opts.sunbathAnchorY) ? opts.sunbathAnchorY : null; this.intimidateTimer = opts.intimidateTimer ?? 0; this.intimidateTargetId = opts.intimidateTargetId || null; this.intimidatedTimer = opts.intimidatedTimer ?? 0; this.intimidationCooldown = opts.intimidationCooldown ?? 0; this.birthRitualTimer = opts.birthRitualTimer ?? 0; this.birthRitualMax = opts.birthRitualMax ?? 0; this.birthPartnerId = opts.birthPartnerId || null; this.birthRitualRole = opts.birthRitualRole || 0; this.birthRitualLeader = !!opts.birthRitualLeader; this.pokeFlashTimer = opts.pokeFlashTimer ?? 0; this.zunchiStain = opts.zunchiStain ?? 0; this.zunchiStainSeed = opts.zunchiStainSeed ?? stableUnit(this.familyKey, "zunchi-stain"); this.zunchiDisease = Boolean(opts.zunchiDisease); this.zunchiDiseaseSeverity = opts.zunchiDiseaseSeverity ?? (this.zunchiDisease ? 1 : 0); this.zunchiDiseaseCooldown = opts.zunchiDiseaseCooldown ?? 0; this.totalFightLosses = opts.totalFightLosses || 0; this.totalFightWins = opts.totalFightWins || 0; this.isTarinaiChampion = !!opts.isTarinaiChampion; this.tarinaiChampionSince = Number.isFinite(opts.tarinaiChampionSince) ? opts.tarinaiChampionSince : 0; this.isZunchiSlave = Boolean(opts.isZunchiSlave || opts.zunchiSlaveLocked); this.zunchiSlaveLocked = Boolean(opts.zunchiSlaveLocked || this.isZunchiSlave); this.formerName = opts.formerName || null; this.loveMochiTimer = opts.loveMochiTimer ?? 0; this.fightMochiTimer = opts.fightMochiTimer ?? 0; this.sleepDisease = Boolean(opts.sleepDisease); this.sleepDiseaseCooldown = opts.sleepDiseaseCooldown ?? 0; this.explosionDisease = Boolean(opts.explosionDisease); this.explosionDiseaseTimer = opts.explosionDiseaseTimer ?? (this.explosionDisease ? 60 : 0); this.fightDisease = Boolean(opts.fightDisease); this.fightDiseaseCooldown = opts.fightDiseaseCooldown ?? 0; this.lastBleedAt = opts.lastBleedAt ?? -999; this.favorite = Boolean(opts.favorite); this.insideNestBoxId = opts.insideNestBoxId || null; this.nestFade = opts.nestFade ?? (this.insideNestBoxId ? 1 : 0); this.nestBoxStayUntil = opts.nestBoxStayUntil ?? (this.insideNestBoxId ? ((world?.time || 0) + 10) : -Infinity); this.focusPulseTimer = opts.focusPulseTimer ?? 0; if (this.isZunchiSlave) { this.zunchiSlaveLocked = true; this.name = "\u305a\u3093\u3061\u3069\u308c\u3044"; applyZunchiSlavePersonality(this, { record: false, birth: false }); this.visibleSpriteId = "zunchi_slave"; } if (this.isTarinaiChampion) this.name = tarinaiChampionName(this.name); this.tempComfort = opts.tempComfort ?? (CONFIG.standardTemperature ?? 15); this.feltTemperature = opts.feltTemperature ?? this.tempComfort; this.temperatureStatus = null; this.tempTimer = opts.tempTimer ?? stableUnit(this.familyKey, "temp-start") * 0.55; this.parents = opts.parents || []; this.parentNames = opts.parentNames || []; this.children = opts.children || []; this.nextEatSound = opts.nextEatSound ?? 0; this.fightTimer = opts.fightTimer ?? 0; this.fightCooldown = opts.fightCooldown ?? deterministicRange(world, "initial-fight-cooldown", 0, CONFIG.fightCooldown, this.familyKey || this.id || opts.birthSeed); this.fightTargetId = opts.fightTargetId || null; this.fightTargetIds = Array.isArray(opts.fightTargetIds) ? [...opts.fightTargetIds] : (this.fightTargetId ? [this.fightTargetId] : []); this.nextHeadbutt = opts.nextHeadbutt ?? 0; const savedDigest = Number(opts.digest); this.digest = Number.isFinite(savedDigest) ? (savedDigest > 3 ? Math.min(2.99, savedDigest / 12.4 * 3) : Math.max(0, savedDigest)) : 0; this.poopCount = opts.poopCount ?? 0; this.oshiriByoZunchiStock = opts.oshiriByoZunchiStock ?? 0; this.lastMealColor = opts.lastMealColor || "#f7cf67"; this.grassEatTimer = opts.grassEatTimer ?? 0; this.hurtTimer = opts.hurtTimer ?? 0; this.stuckPushpinId = opts.stuckPushpinId || null; this.defeatedTimer = opts.defeatedTimer ?? 0; this.defeatedById = opts.defeatedById || null; this.fightWinnerId = opts.fightWinnerId || null; this.relationships = opts.relationships && typeof opts.relationships === "object" ? JSON.parse(JSON.stringify(opts.relationships)) : {}; this.fallTimer = opts.fallTimer ?? 0; this.fallMax = opts.fallMax ?? 1.15; this.fallDir = opts.fallDir ?? (stableUnit(this.familyKey, "fall-dir") < 0.5 ? -1 : 1); this.blastSpinTimer = opts.blastSpinTimer ?? 0; this.blastSpinMax = opts.blastSpinMax ?? 0; this.postBirthPeaceTimer = opts.postBirthPeaceTimer ?? 0; this.sleepFacing = stableUnit(this.familyKey, "sleep-facing") < 0.5 ? -1 : 1; this.nextBubbleAt = 0; this.nextSleepBubbleAt = 0; this.nextCursorHeartAt = opts.nextCursorHeartAt ?? 0; this.cursorPetting = opts.cursorPetting ?? 0; this.aiTimer = opts.aiTimer ?? stableUnit(this.familyKey, "ai-start") * 0.22; this.envTimer = opts.envTimer ?? stableUnit(this.familyKey, "env-start") * 0.55; this.visibleSpriteId = opts.visibleSpriteId || null; this.zunchiSlaveSpriteVariant = opts.zunchiSlaveSpriteVariant || (opts.isZunchiSlave ? (Math.random() < 0.5 ? "zunchi_slave" : "zunchi_slave_alt") : ""); this.spriteLockUntil = opts.spriteLockUntil ?? 0; this.entryTimer = opts.entryTimer ?? 0; const effectTimers = opts.itemEffectTimers && typeof opts.itemEffectTimers === "object" ? opts.itemEffectTimers : {}; this.itemEffectTimers = { laxative: Math.max(0, Number(effectTimers.laxative || opts.laxativeTimer || 0) || 0), ammo: Math.max(0, Number(effectTimers.ammo || opts.ammoTimer || 0) || 0), sedative: Math.max(0, Number(effectTimers.sedative || opts.sedativeTimer || 0) || 0), }; this.laxativePoopTimer = Number.isFinite(opts.laxativePoopTimer) ? opts.laxativePoopTimer : 3; this.ammoBumpCooldown = opts.ammoBumpCooldown || 0; this.powerItemMode = opts.powerItemMode === "protein" || opts.powerItemMode === "niteropu" ? opts.powerItemMode : ""; this.sizeItemMode = opts.sizeItemMode === "giant_drug" || opts.sizeItemMode === "dwarf_drug" ? opts.sizeItemMode : ""; this.lifeItemMode = opts.lifeItemMode === "mercury" ? opts.lifeItemMode : ""; this.mercuryLifeSpanBase = Number.isFinite(opts.mercuryLifeSpanBase) ? opts.mercuryLifeSpanBase : null; this.mercuryLifeMultiplier = Number.isFinite(opts.mercuryLifeMultiplier) ? Math.max(1, opts.mercuryLifeMultiplier) : (this.lifeItemMode === "mercury" ? 1.3 : 1); this.itemDamageTimer = Number.isFinite(opts.itemDamageTimer) ? opts.itemDamageTimer : 1; this.powerEffectPulse = opts.powerEffectPulse || 0; this.powerEffectKind = opts.powerEffectKind || this.powerItemMode || this.sizeItemMode || this.lifeItemMode || ""; this.mysteryDrugHistory = Array.isArray(opts.mysteryDrugHistory) ? opts.mysteryDrugHistory.slice(-8) : []; this.recentChangeCauses = Array.isArray(opts.recentChangeCauses) ? opts.recentChangeCauses.slice(-8) : []; if (this.refreshEffectiveSize) this.refreshEffectiveSize(); } } if (typeof window !== "undefined") window.Tarinai = Tarinai; if (typeof globalThis !== "undefined") globalThis.Tarinai = Tarinai;