Exact source used by this build. SHA-256: 373d3a13a0dfa78be5b6128f24483696d60a309e93cef0f4ea902f66e97195f5.

1 import type { Area, Player, SimRules, TilePosition, WorldState } from "../../types";
2 import type { Quality } from "../items";
3 import type { ShopDefinition } from "../shops";
4 
5 export interface BuildingDefinition {
6   id: string; name: string; description: string; manifestKey: string;
7   family: "coop" | "barn" | "utility"; kind?: string; tier: number; upgradeFrom: string | null;
8   cost: number; materials: { itemId: string; quantity: number }[]; buildDays: number; width: number; height: number; capacity: number;
9   interior: { width: number; height: number; troughs: number; hayHopper: boolean; heaterSlot: boolean; incubator: boolean; autoGrabberSlot: boolean } | null;
10   autoFeed: boolean; siloCapacity: number;
11 }
12 export interface AnimalSpecies {
13   id: string; name: string; description: string; manifestKey: string; role: string;
14   buildingFamily: "coop" | "barn"; minTier: number; purchasePrice: number; matureDays: number; productCycleDays: number;
15   productItemId: string; largeProductItemId: string | null; bonusProductItemId: string | null; toolItemId: string | null; outdoorOnly: boolean; grassTufts: number; petMoodGain: number; fedMoodGain: number; neglectedMoodPenalty: number; weatherMoodDelta: number; incubationMinutes: number | null; harvestXp: number;
16 }
17 export interface AnimalsContent {
18   farmAreaId: string; buildingPlacementPoiId: string; carpenterShopId: string; ranchShopId: string; hayItemId: string; grassStarterItemId: string; heaterItemId: string; autoGrabberItemId: string;
19   buildings: BuildingDefinition[]; species: AnimalSpecies[]; shops: ShopDefinition[];
20   care: {
21     friendshipMax: number; moodMax: number; initialFriendship: number; initialMood: number;
22     petFriendship: number; harvestFriendship: number; grassFriendship: number; unfedFriendshipPenalty: number; outsideFriendshipPenalty: number;
23     unpettedPenaltyBase: number; unpettedPenaltyFriendshipDivisor: number; unfedMoodPenalty: number; outsideMoodMultiplier: number;
24     returnMinute: number; sleepMinute: number; eveningMinute: number; productionMoodThreshold: number; qualityMoodThreshold: number;
25     qualityFriendshipDivisor: number; qualityMoodDivisor: number; qualityIridiumThreshold: number; qualityGoldDivisor: number;
26     largeMinFriendship: number; largeProductDivisor: number; bonusProductDivisor: number; moodHighThreshold: number; moodHighMultiplier: number; moodLowThreshold: number;
27     sellBaseMultiplier: number; woolFriendshipThreshold: number; woolFriendshipDaysReduction: number; truffleChancePerFrame: number;
28     truffleExtraFriendshipDivisor: number; simulationFramesPerSecond: number; dailyLuck: number; autoGrabberFarmingLevel: number;
29     closedDoorMoodGain: number; petXp: number; harvestToolEnergy: number;
30   };
31   pets: { choices: { id: "cat" | "dog"; manifestKey: string }[]; freeCount: number; friendshipMax: number; petFriendship: number; waterFriendship: number; missingBowlPenalty: number; bowl: TilePosition & { manifestKey: string }; start: TilePosition };
32   horse: { name: string; description: string; manifestKey: string; speedMultiplier: number; verticalClearance: number };
33   grass: { manifestKey: string; maxTufts: number; starterTufts: number; growChance: number; growMin: number; growMax: number; extraGrowMin: number; extraGrowMax: number; spreadMin: number; spreadMax: number; springGrowthPasses: number; spreadChance: number; hayChance: number; winterHayChance: number; growthSeasons: string[]; initialPatches: (TilePosition & { tufts: number })[] };
34   tools: { itemId: string; manifestKey: string; price: number; energyCost: number; farmingXp: number }[];
35   itemVisuals: Record<string, string>; visuals: Record<string, { color: number; icon: string }>;
36 }
37 export interface ProductValue { itemId: string; quality: Quality }
38 export interface AnimalProduct extends ProductValue { id: string; animalId: string; buildingId: string; areaId: string; x: number; y: number }
39 export interface FarmAnimal {
40   id: string; speciesId: string; name: string; buildingId: string; ownerId: string;
41   ageDays: number; friendship: number; mood: number; areaId: string; x: number; y: number;
42   pettedDay: string; fedDay: string; ateGrassDay: string; lastProductDay: number; readyProduct: ProductValue | null;
43   sleeping: boolean; outsideOvernight: boolean; truffleReady: boolean;
44 }
45 export interface Building {
46   id: string; definitionId: string; x: number; y: number; areaId: string; interiorAreaId: string;
47   construction: { definitionId: string; morningsRemaining: number } | null;
48   doorOpen: boolean; trough: number; heater: boolean; autoGrabber: boolean; storedProducts: AnimalProduct[];
49   incubation: { speciesId: string; morningsRemaining: number; readyAtMachineMinute: number; ownerId: string } | null;
50 }
51 export interface Grass { x: number; y: number; tufts: number; manifestKey: string }
52 export interface Pet { kind: "cat" | "dog"; name: string; friendship: number; pettedDay: string; bowlWateredDay: string; areaId: string; x: number; y: number; manifestKey: string }
53 export interface Horse { buildingId: string; name: string; areaId: string; x: number; y: number; riderId: string | null; manifestKey: string }
54 export interface AnimalsState {
55   buildings: Record<string, Building>; animals: Record<string, FarmAnimal>; products: Record<string, AnimalProduct>;
56   grass: Record<string, Grass>; hay: number; pet: Pet | null; horse: Horse | null; nextId: number;
57   initialized: boolean; lastOvernightDay: string; lastTickMinute: string;
58 }
59 export interface AnimalPlayerState { mounted: boolean }
60 export interface InteriorFeatures { buildingId: string; troughs: TilePosition[]; hayHopper: TilePosition | null; heaterSlot: TilePosition | null; incubator: TilePosition | null; autoGrabberSlot: TilePosition | null; exit: TilePosition; manifestKey: string }
61 export const animalDefaults = (): AnimalsState => ({ buildings: {}, animals: {}, products: {}, grass: {}, hay: 0, pet: null, horse: null, nextId: 1, initialized: false, lastOvernightDay: "", lastTickMinute: "" });
62 export function getAnimals(state: Pick<WorldState, "systems">): AnimalsState { return { ...animalDefaults(), ...(state.systems.animals as Partial<AnimalsState> | undefined) }; }
63 export function getAnimalsContent(rules: SimRules): AnimalsContent | undefined {
64   const data = rules.content?.animals;
65   return data && typeof data === "object" && "buildings" in data && "species" in data ? data as AnimalsContent : undefined;
66 }
67 export function getAnimalPlayer(player: Player): AnimalPlayerState { return { mounted: false, ...(player.systems.animals as Partial<AnimalPlayerState> | undefined) }; }
68 export function getInterior(area: Area): InteriorFeatures | undefined { return area.systems.animals as InteriorFeatures | undefined; }
69 export const grassKey = (tile: TilePosition): string => `${tile.x},${tile.y}`;
70 export function absoluteDay(state: Pick<WorldState, "clock">): number { return (state.clock.year - 1) * 112 + ["spring", "summer", "autumn", "winter"].indexOf(state.clock.season) * 28 + state.clock.day; }
71 export function buildingDoor(building: Building, definition: BuildingDefinition): TilePosition { return { x: building.x + Math.floor(definition.width / 2), y: building.y + definition.height }; }
72 export function animalSellPrice(species: Pick<AnimalSpecies, "purchasePrice">, friendship: number): number { return Math.floor(species.purchasePrice * (Math.max(0, Math.min(1000, friendship)) / 1000 + 0.3)); }
73 export function animalQualityScore(friendship: number, mood: number, professionBonus = 0): number { return friendship / 1000 - (1 - mood / 225) + professionBonus; }
74 /** Independent rolls in descending quality order. Low mood cannot produce quality goods. */
75 export function animalProductQuality(friendship: number, mood: number, rolls: readonly number[], professionBonus = 0): Quality {
76   if (mood < 150) return "regular";
77   const score = animalQualityScore(friendship, mood, professionBonus);
78   if (score > 0.95 && (rolls[0] ?? 1) < score / 2) return "iridium";
79   if ((rolls[1] ?? 1) < score / 2) return "gold";
80   return (rolls[2] ?? 1) < score ? "silver" : "regular";
81 }
82 export function animalLargeChance(friendship: number, mood: number): number {
83   if (friendship < 200 || mood < 150) return 0;
84   const moodModifier = mood > 200 ? mood * 1.5 : mood <= 100 ? mood - 100 : 0;
85   return Math.max(0, Math.min(1, (friendship + moodModifier) / 1200));
86 }
87 export function animalBonusChance(friendship: number, mood: number, dailyLuck = 0): number {
88   if (friendship < 200 || mood < 150) return 0;
89   const moodModifier = mood > 200 ? mood * 1.5 : mood <= 100 ? mood - 100 : 0;
90   return Math.max(0, Math.min(1, (friendship + moodModifier) / 4750 + dailyLuck));
91 }
92