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); }
71 private float? UnstunY {
get;
set; }
77 private class ItemProduction
84 public List<Item> Items;
85 public Vector2 HungerRange;
86 public Vector2 HappinessRange;
88 public float HungerRate;
89 public float InvHungerRate;
90 public float HappinessRate;
91 public float InvHappinessRate;
93 private readonly
float totalCommonness;
96 public ItemProduction(XElement element)
98 Items =
new List<Item>();
100 HungerRate = element.GetAttributeFloat(
"hungerrate", 0.0f);
101 InvHungerRate = element.GetAttributeFloat(
"invhungerrate", 0.0f);
102 HappinessRate = element.GetAttributeFloat(
"happinessrate", 0.0f);
103 InvHappinessRate = element.GetAttributeFloat(
"invhappinessrate", 0.0f);
105 string[] requiredHappinessStr = element.GetAttributeString(
"requiredhappiness",
"0-100").Split(
'-');
106 string[] requiredHungerStr = element.GetAttributeString(
"requiredhunger",
"0-100").Split(
'-');
107 HappinessRange =
new Vector2(0, 100);
108 HungerRange =
new Vector2(0, 100);
110 if (requiredHappinessStr.Length >= 2)
112 if (
float.TryParse(requiredHappinessStr[0], NumberStyles.Any, CultureInfo.InvariantCulture, out tempF)) { HappinessRange.X = tempF; }
113 if (
float.TryParse(requiredHappinessStr[1], NumberStyles.Any, CultureInfo.InvariantCulture, out tempF)) { HappinessRange.Y = tempF; }
115 if (requiredHungerStr.Length >= 2)
117 if (
float.TryParse(requiredHungerStr[0], NumberStyles.Any, CultureInfo.InvariantCulture, out tempF)) { HungerRange.X = tempF; }
118 if (
float.TryParse(requiredHungerStr[1], NumberStyles.Any, CultureInfo.InvariantCulture, out tempF)) { HungerRange.Y = tempF; }
120 Rate = element.GetAttributeFloat(
"rate", 0.016f);
121 totalCommonness = 0.0f;
122 foreach (var subElement
in element.Elements())
124 switch (subElement.Name.LocalName.ToLowerInvariant())
127 Identifier identifier = subElement.GetAttributeIdentifier(
"identifier", Identifier.Empty);
130 Prefab = identifier.IsEmpty ? null : ItemPrefab.Find(
"", subElement.GetAttributeIdentifier(
"identifier", Identifier.Empty)),
131 Commonness = subElement.GetAttributeFloat(
"commonness", 0.0f)
133 totalCommonness += newItemToProduce.Commonness;
134 Items.Add(newItemToProduce);
144 if (pet.Happiness < HappinessRange.X || pet.Happiness > HappinessRange.Y) {
return; }
145 if (pet.Hunger < HungerRange.X || pet.Hunger > HungerRange.Y) {
return; }
147 float currentRate = Rate;
148 currentRate += HappinessRate * (pet.Happiness - HappinessRange.X) / (HappinessRange.Y - HappinessRange.X);
149 currentRate += InvHappinessRate * (1.0f - ((pet.Happiness - HappinessRange.X) / (HappinessRange.Y - HappinessRange.X)));
150 currentRate += HungerRate * (pet.Hunger - HungerRange.X) / (HungerRange.Y - HungerRange.X);
151 currentRate += InvHungerRate * (1.0f - ((pet.Hunger - HungerRange.X) / (HungerRange.Y - HungerRange.X)));
152 timer -= currentRate * deltaTime;
156 float r = Rand.Range(0.0f, totalCommonness);
157 float aggregate = 0.0f;
158 for (
int i = 0; i < Items.Count; i++)
160 aggregate += Items[i].Commonness;
161 if (aggregate >= r && Items[i].Prefab !=
null)
163 GameAnalyticsManager.AddDesignEvent(
"MicroInteraction:" + (GameMain.GameSession?.GameMode?.Preset.Identifier.Value ??
"null") +
":PetProducedItem:" + pet.AIController.Character.SpeciesName +
":" + Items[i].Prefab.Identifier);
164 Entity.Spawner?.AddItemToSpawnQueue(Items[i].Prefab, pet.AIController.Character.WorldPosition);
174 public Identifier Tag;
175 public Vector2 HungerRange;
178 public float Priority;
179 public bool IgnoreContained;
184 private readonly List<ItemProduction> itemsToProduce =
new List<ItemProduction>();
185 private readonly List<Food> foods =
new List<Food>();
209 foreach (var subElement
in element.Elements())
211 switch (subElement.Name.LocalName.ToLowerInvariant())
213 case "itemproduction":
214 itemsToProduce.Add(
new ItemProduction(subElement));
219 Tag = subElement.GetAttributeIdentifier(
"tag", Identifier.Empty),
220 Hunger = subElement.GetAttributeFloat(
"hunger", -1),
221 Happiness = subElement.GetAttributeFloat(
"happiness", 1),
222 Priority = subElement.GetAttributeFloat(
"priority", 100),
223 IgnoreContained = subElement.GetAttributeBool(
"ignorecontained",
true)
225 string[] requiredHungerStr = subElement.GetAttributeString(
"requiredhunger",
"0-100").Split(
'-');
226 food.HungerRange =
new Vector2(0, 100);
227 if (requiredHungerStr.Length >= 2)
229 if (
float.TryParse(requiredHungerStr[0], NumberStyles.Any, CultureInfo.InvariantCulture, out
float tempF)) { food.HungerRange.X = tempF; }
230 if (
float.TryParse(requiredHungerStr[1], NumberStyles.Any, CultureInfo.InvariantCulture, out tempF)) { food.HungerRange.Y = tempF; }
261 if (character ==
null || !character.
IsDead) {
return false; }
262 bool success =
OnEat(
"dead".ToIdentifier());
270 private bool OnEat(IEnumerable<Identifier> tags)
272 foreach (Identifier tag
in tags)
274 if (
OnEat(tag)) {
return true; }
281 for (
int i = 0; i < foods.Count; i++)
283 if (tag == foods[i].Tag)
285 Hunger += foods[i].Hunger;
324 var tag = item.GetComponent<
NameTag>();
325 if (tag !=
null && !
string.IsNullOrWhiteSpace(tag.WrittenName))
338 if (character?.Removed ??
true || character.IsDead) {
return; }
340 if (UnstunY.HasValue)
345 if (character.SimPosition.Y < (UnstunY.Value + extent * 3.0f) &&
346 character.AnimController.MainLimb.body.LinearVelocity.Y < 0.0f)
348 character.IsRagdolled =
false;
353 character.IsRagdolled =
true;
358 character.IsRagdolled =
false;
371 for (
int i = 0; i < foods.Count; i++)
373 Food food = foods[i];
374 if (
Hunger >= food.HungerRange.X &&
Hunger <= food.HungerRange.Y)
376 if (food.TargetParams ==
null)
378 if (
AIController.AIParams.TryGetTargets(food.Tag, out IEnumerable<TargetParams> existingTargetParams))
380 foreach (var targetParams
in existingTargetParams)
382 food.TargetParams = targetParams;
385 else if (
AIController.AIParams.TryAddNewTarget(food.Tag,
AIState.Eat, food.Priority, out TargetParams targetParams))
387 food.TargetParams = targetParams;
389 if (food.TargetParams !=
null)
391 food.TargetParams.State =
AIState.Eat;
392 food.TargetParams.Priority = food.Priority;
393 food.TargetParams.IgnoreContained = food.IgnoreContained;
397 else if (food.TargetParams !=
null)
400 food.TargetParams =
null;
409 if (character.SelectedBy !=
null)
411 character.IsRagdolled =
true;
412 UnstunY = character.SimPosition.Y;
415 for (
int i = 0; i < itemsToProduce.Count; i++)
417 itemsToProduce[i].Update(
this, deltaTime);
429 if (petBehavior ==
null) {
continue; }
431 XElement petElement =
new XElement(
"pet",
433 new XAttribute(
"ownerhash", petBehavior.Owner?.Info?.GetIdentifier() ?? 0),
434 new XAttribute(
"seed", c.
Seed));
436 var petBehaviorElement =
new XElement(
"petbehavior",
437 new XAttribute(
"hunger", petBehavior.Hunger.ToString(
"G", CultureInfo.InvariantCulture)),
438 new XAttribute(
"happiness", petBehavior.Happiness.ToString(
"G", CultureInfo.InvariantCulture)));
439 petElement.Add(petBehaviorElement);
441 var healthElement =
new XElement(
"health");
443 petElement.Add(healthElement);
447 var inventoryElement =
new XElement(
"inventory");
449 petElement.Add(inventoryElement);
452 petsElement.Add(petElement);
458 foreach (var subElement
in petsElement.Elements())
460 string speciesName = subElement.GetAttributeString(
"speciesname",
"");
461 string seed = subElement.GetAttributeString(
"seed",
"123");
462 int ownerHash = subElement.GetAttributeInt(
"ownerhash", 0);
463 Vector2 spawnPos = Vector2.Zero;
483 if (characterPrefab ==
null)
485 DebugConsole.ThrowError($
"Failed to load the pet \"{speciesName}\". Character prefab not found.");
488 var pet =
Character.
Create(characterPrefab, spawnPos, seed, spawnInitialItems:
false);
492 if (petBehavior !=
null)
494 petBehavior.
Owner = owner;
495 var petBehaviorElement = subElement.Element(
"petbehavior");
496 if (petBehaviorElement !=
null)
498 petBehavior.Hunger = petBehaviorElement.GetAttributeFloat(
"hunger", 50.0f);
499 petBehavior.Happiness = petBehaviorElement.GetAttributeFloat(
"happiness", 50.0f);
503 var inventoryElement = subElement.Element(
"inventory");
504 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
bool IsFriendly(Character other)
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)
bool HideStatusIndicators
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)
bool ToggleOwner
Should the pet lose ownership (and stop following) when the same character interacts with it twice?...
readonly Identifier Identifier
override Vector2? WorldPosition
static Submarine MainSub
Note that this can be null in some situations, e.g. editors and missions that don't load a submarine.
static List< WayPoint > WayPointList