4 using Microsoft.Xna.Framework;
5 using System.Collections.Generic;
6 using System.Globalization;
23 private float hunger = 50.0f;
26 get {
return hunger; }
27 set { hunger = MathHelper.Clamp(value, 0.0f,
MaxHunger); }
30 private float happiness = 50.0f;
33 get {
return happiness; }
34 set { happiness = MathHelper.Clamp(value, 0.0f,
MaxHappiness); }
62 private float? UnstunY {
get;
set; }
68 private class ItemProduction
75 public List<Item> Items;
76 public Vector2 HungerRange;
77 public Vector2 HappinessRange;
79 public float HungerRate;
80 public float InvHungerRate;
81 public float HappinessRate;
82 public float InvHappinessRate;
84 private readonly
float totalCommonness;
87 public ItemProduction(XElement element)
89 Items =
new List<Item>();
91 HungerRate = element.GetAttributeFloat(
"hungerrate", 0.0f);
92 InvHungerRate = element.GetAttributeFloat(
"invhungerrate", 0.0f);
93 HappinessRate = element.GetAttributeFloat(
"happinessrate", 0.0f);
94 InvHappinessRate = element.GetAttributeFloat(
"invhappinessrate", 0.0f);
96 string[] requiredHappinessStr = element.GetAttributeString(
"requiredhappiness",
"0-100").Split(
'-');
97 string[] requiredHungerStr = element.GetAttributeString(
"requiredhunger",
"0-100").Split(
'-');
98 HappinessRange =
new Vector2(0, 100);
99 HungerRange =
new Vector2(0, 100);
101 if (requiredHappinessStr.Length >= 2)
103 if (
float.TryParse(requiredHappinessStr[0], NumberStyles.Any, CultureInfo.InvariantCulture, out tempF)) { HappinessRange.X = tempF; }
104 if (
float.TryParse(requiredHappinessStr[1], NumberStyles.Any, CultureInfo.InvariantCulture, out tempF)) { HappinessRange.Y = tempF; }
106 if (requiredHungerStr.Length >= 2)
108 if (
float.TryParse(requiredHungerStr[0], NumberStyles.Any, CultureInfo.InvariantCulture, out tempF)) { HungerRange.X = tempF; }
109 if (
float.TryParse(requiredHungerStr[1], NumberStyles.Any, CultureInfo.InvariantCulture, out tempF)) { HungerRange.Y = tempF; }
111 Rate = element.GetAttributeFloat(
"rate", 0.016f);
112 totalCommonness = 0.0f;
113 foreach (var subElement
in element.Elements())
115 switch (subElement.Name.LocalName.ToLowerInvariant())
118 Identifier identifier = subElement.GetAttributeIdentifier(
"identifier", Identifier.Empty);
121 Prefab = identifier.IsEmpty ? null : ItemPrefab.Find(
"", subElement.GetAttributeIdentifier(
"identifier", Identifier.Empty)),
122 Commonness = subElement.GetAttributeFloat(
"commonness", 0.0f)
124 totalCommonness += newItemToProduce.Commonness;
125 Items.Add(newItemToProduce);
135 if (pet.Happiness < HappinessRange.X || pet.Happiness > HappinessRange.Y) {
return; }
136 if (pet.Hunger < HungerRange.X || pet.Hunger > HungerRange.Y) {
return; }
138 float currentRate = Rate;
139 currentRate += HappinessRate * (pet.Happiness - HappinessRange.X) / (HappinessRange.Y - HappinessRange.X);
140 currentRate += InvHappinessRate * (1.0f - ((pet.Happiness - HappinessRange.X) / (HappinessRange.Y - HappinessRange.X)));
141 currentRate += HungerRate * (pet.Hunger - HungerRange.X) / (HungerRange.Y - HungerRange.X);
142 currentRate += InvHungerRate * (1.0f - ((pet.Hunger - HungerRange.X) / (HungerRange.Y - HungerRange.X)));
143 timer -= currentRate * deltaTime;
147 float r = Rand.Range(0.0f, totalCommonness);
148 float aggregate = 0.0f;
149 for (
int i = 0; i < Items.Count; i++)
151 aggregate += Items[i].Commonness;
152 if (aggregate >= r && Items[i].Prefab !=
null)
154 GameAnalyticsManager.AddDesignEvent(
"MicroInteraction:" + (GameMain.GameSession?.GameMode?.Preset.Identifier.Value ??
"null") +
":PetProducedItem:" + pet.AIController.Character.SpeciesName +
":" + Items[i].Prefab.Identifier);
155 Entity.Spawner?.AddItemToSpawnQueue(Items[i].Prefab, pet.AIController.Character.WorldPosition);
166 public Vector2 HungerRange;
169 public float Priority;
170 public bool IgnoreContained;
175 private readonly List<ItemProduction> itemsToProduce =
new List<ItemProduction>();
176 private readonly List<Food> foods =
new List<Food>();
195 PlayForce = element.GetAttributeFloat(
"playforce", 15.0f);
197 foreach (var subElement
in element.Elements())
199 switch (subElement.Name.LocalName.ToLowerInvariant())
201 case "itemproduction":
202 itemsToProduce.Add(
new ItemProduction(subElement));
207 Tag = subElement.GetAttributeString(
"tag",
""),
208 Hunger = subElement.GetAttributeFloat(
"hunger", -1),
209 Happiness = subElement.GetAttributeFloat(
"happiness", 1),
210 Priority = subElement.GetAttributeFloat(
"priority", 100),
211 IgnoreContained = subElement.GetAttributeBool(
"ignorecontained",
true)
213 string[] requiredHungerStr = subElement.GetAttributeString(
"requiredhunger",
"0-100").Split(
'-');
214 food.HungerRange =
new Vector2(0, 100);
215 if (requiredHungerStr.Length >= 2)
217 if (
float.TryParse(requiredHungerStr[0], NumberStyles.Any, CultureInfo.InvariantCulture, out
float tempF)) { food.HungerRange.X = tempF; }
218 if (
float.TryParse(requiredHungerStr[1], NumberStyles.Any, CultureInfo.InvariantCulture, out tempF)) { food.HungerRange.Y = tempF; }
248 if (character ==
null || !character.
IsDead) {
return false; }
249 bool success =
OnEat(
"dead".ToIdentifier());
257 private bool OnEat(IEnumerable<Identifier> tags)
259 foreach (Identifier tag
in tags)
261 if (
OnEat(tag)) {
return true; }
268 for (
int i = 0; i < foods.Count; i++)
270 if (tag == foods[i].Tag)
272 Hunger += foods[i].Hunger;
303 var tag = item.GetComponent<
NameTag>();
304 if (tag !=
null && !
string.IsNullOrWhiteSpace(tag.WrittenName))
317 if (character?.Removed ??
true || character.IsDead) {
return; }
319 if (UnstunY.HasValue)
324 if (character.SimPosition.Y < (UnstunY.Value + extent * 3.0f) &&
325 character.AnimController.MainLimb.body.LinearVelocity.Y < 0.0f)
327 character.IsRagdolled =
false;
332 character.IsRagdolled =
true;
337 character.IsRagdolled =
false;
350 for (
int i = 0; i < foods.Count; i++)
352 Food food = foods[i];
353 if (
Hunger >= food.HungerRange.X &&
Hunger <= food.HungerRange.Y)
355 if (food.TargetParams ==
null)
357 if (
AIController.AIParams.TryGetTarget(food.Tag, out TargetParams target))
359 food.TargetParams = target;
361 else if (
AIController.AIParams.TryAddNewTarget(food.Tag,
AIState.Eat, food.Priority, out TargetParams targetParams))
363 food.TargetParams = targetParams;
365 if (food.TargetParams !=
null)
367 food.TargetParams.State =
AIState.Eat;
368 food.TargetParams.Priority = food.Priority;
369 food.TargetParams.IgnoreContained = food.IgnoreContained;
373 else if (food.TargetParams !=
null)
376 food.TargetParams =
null;
385 if (character.SelectedBy !=
null)
387 character.IsRagdolled =
true;
388 UnstunY = character.SimPosition.Y;
391 for (
int i = 0; i < itemsToProduce.Count; i++)
393 itemsToProduce[i].Update(
this, deltaTime);
405 if (petBehavior ==
null) {
continue; }
407 XElement petElement =
new XElement(
"pet",
409 new XAttribute(
"ownerhash", petBehavior.Owner?.Info?.GetIdentifier() ?? 0),
410 new XAttribute(
"seed", c.
Seed));
412 var petBehaviorElement =
new XElement(
"petbehavior",
413 new XAttribute(
"hunger", petBehavior.Hunger.ToString(
"G", CultureInfo.InvariantCulture)),
414 new XAttribute(
"happiness", petBehavior.Happiness.ToString(
"G", CultureInfo.InvariantCulture)));
415 petElement.Add(petBehaviorElement);
417 var healthElement =
new XElement(
"health");
419 petElement.Add(healthElement);
423 var inventoryElement =
new XElement(
"inventory");
425 petElement.Add(inventoryElement);
428 petsElement.Add(petElement);
434 foreach (var subElement
in petsElement.Elements())
436 string speciesName = subElement.GetAttributeString(
"speciesname",
"");
437 string seed = subElement.GetAttributeString(
"seed",
"123");
438 int ownerHash = subElement.GetAttributeInt(
"ownerhash", 0);
439 Vector2 spawnPos = Vector2.Zero;
455 if (characterPrefab ==
null)
457 DebugConsole.ThrowError($
"Failed to load the pet \"{speciesName}\". Character prefab not found.");
460 var pet =
Character.
Create(characterPrefab, spawnPos, seed, spawnInitialItems:
false);
464 if (petBehavior !=
null)
466 petBehavior.
Owner = owner;
467 var petBehaviorElement = subElement.Element(
"petbehavior");
468 if (petBehaviorElement !=
null)
470 petBehavior.Hunger = petBehaviorElement.GetAttributeFloat(
"hunger", 50.0f);
471 petBehavior.Happiness = petBehaviorElement.GetAttributeFloat(
"happiness", 50.0f);
475 var inventoryElement = subElement.Element(
"inventory");
476 if (inventoryElement !=
null)
readonly Character Character
AfflictionPrefab is a prefab that defines a type of affliction that can be applied to a character....
static AfflictionPrefab InternalDamage
void Save(XElement healthElement)
CharacterHealth CharacterHealth
virtual AIController AIController
static Character Create(CharacterInfo characterInfo, Vector2 position, string seed, ushort id=Entity.NullEntityID, bool isRemotePlayer=false, bool hasAi=true, RagdollParams ragdoll=null, bool spawnInitialItems=true)
Create a new character
override Vector2? SimPosition
static void SaveInventory(Inventory inventory, XElement parentElement)
static readonly List< Character > CharacterList
CharacterInventory Inventory
void PlaySound(CharacterSound.SoundType soundType, float soundIntervalFactor=1.0f, float maxInterval=0)
readonly AnimController AnimController
void SpawnInventoryItems(Inventory inventory, ContentXElement itemData)
int GetIdentifier()
Returns a presumably (not guaranteed) unique and persistent hash using the (current) Name,...
Contains character data that should be editable in the character editor.
static CharacterPrefab FindBySpeciesName(Identifier speciesName)
virtual Vector2 WorldPosition
static GameSession?? GameSession
static NetworkMember NetworkMember
readonly Identifier Identifier
virtual IEnumerable< Item > AllItems
All items contained in the inventory. Stacked items are returned as individual instances....
IReadOnlyCollection< Identifier > GetTags()
float HappyThreshold
At which point is the pet considered "happy" (playing happy sounds and showing the icon)
float UnhappyThreshold
At which point is the pet considered "unhappy" (playing unhappy sounds and showing the icon)
float HappinessDecreaseRate
static void LoadPets(XElement petsElement)
bool OnEat(Identifier tag)
void Play(Character player)
void Update(float deltaTime)
static void SavePets(XElement petsElement)
float HungryThreshold
At which point is the pet considered "hungry" (playing unhappy sounds and showing the icon)
bool OnEat(Character character)
StatusIndicatorType GetCurrentStatusIndicatorType()
PetBehavior(XElement element, EnemyAIController aiController)
readonly Identifier Identifier
override Vector2? WorldPosition
static List< WayPoint > WayPointList