Files
LuaCsForBarotraumaEP/Subsurface/Source/Characters/Character.cs
Regalis 1a3c18c727 EntitySpawner sends spawn/removal messages to clients using EntityEvents.
EntityEvents and EntitySpawner used to work independently of each other, with separate IDs, and there was no guarantee that spawning and events would happen in the correct order. For example, a client could fail to read events during midround syncing because the entity has been removed, or read an event for an incorrect entity because the entity has been removed and the ID taken by some other entity.
2017-03-27 21:05:32 +03:00

1938 lines
66 KiB
C#

using FarseerPhysics;
using FarseerPhysics.Dynamics;
using FarseerPhysics.Dynamics.Joints;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Barotrauma.Networking;
using Barotrauma.Particles;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace Barotrauma
{
partial class Character : Entity, IDamageable, IPropertyObject, IClientSerializable, IServerSerializable
{
public static List<Character> CharacterList = new List<Character>();
public static bool DisableControls;
//the Character that the player is currently controlling
private static Character controlled;
public static Character Controlled
{
get { return controlled; }
set
{
if (controlled == value) return;
controlled = value;
CharacterHUD.Reset();
if (controlled != null)
{
controlled.Enabled = true;
}
}
}
public List<Item> SpawnItems = new List<Item>();
private bool enabled;
public bool Enabled
{
get
{
return enabled;
}
set
{
if (value == enabled) return;
enabled = value;
foreach (Limb limb in AnimController.Limbs)
{
limb.body.Enabled = enabled;
}
AnimController.Collider.Enabled = value;
}
}
public Hull PreviousHull = null;
public Hull CurrentHull = null;
public readonly bool IsRemotePlayer;
private CharacterInventory inventory;
protected float lastRecvPositionUpdateTime;
public readonly Dictionary<string, ObjectProperty> Properties;
public Dictionary<string, ObjectProperty> ObjectProperties
{
get { return Properties; }
}
protected Key[] keys;
private Item selectedConstruction;
private Item[] selectedItems;
public byte TeamID = 0;
public AnimController AnimController;
private Vector2 cursorPosition;
protected bool needsAir;
protected float oxygen, oxygenAvailable;
protected float drowningTime;
private float health, lastSentHealth;
protected float minHealth, maxHealth;
protected Item closestItem;
private Character closestCharacter, selectedCharacter;
private Dictionary<object, HUDProgressBar> hudProgressBars;
private bool isDead;
private CauseOfDeath lastAttackCauseOfDeath;
private CauseOfDeath causeOfDeath;
public readonly bool IsHumanoid;
//the name of the species (e.q. human)
public readonly string SpeciesName;
protected float soundTimer;
protected float soundInterval;
private float bleeding;
private List<CharacterSound> sounds;
private float attackCoolDown;
public Entity ViewTarget
{
get;
set;
}
private CharacterInfo info;
public CharacterInfo Info
{
get
{
return info;
}
set
{
if (info != null && info != value) info.Remove();
info = value;
if (info != null) info.Character = this;
}
}
public string Name
{
get
{
return info != null && !string.IsNullOrWhiteSpace(info.Name) ? info.Name : SpeciesName;
}
}
public string ConfigPath
{
get;
private set;
}
public float Mass
{
get { return AnimController.Mass; }
}
public CharacterInventory Inventory
{
get { return inventory; }
}
private Color speechBubbleColor;
private float speechBubbleTimer;
private float lockHandsTimer;
public bool LockHands
{
get
{
return lockHandsTimer > 0.0f;
}
set
{
lockHandsTimer = MathHelper.Clamp(lockHandsTimer + (value ? 1.0f : -0.5f), 0.0f, 10.0f);
}
}
public bool AllowInput
{
get { return !IsUnconscious && Stun <= 0.0f && !isDead; }
}
public Vector2 CursorPosition
{
get { return cursorPosition; }
set
{
if (!MathUtils.IsValid(value)) return;
cursorPosition = value;
}
}
public Vector2 CursorWorldPosition
{
get { return Submarine == null ? cursorPosition : cursorPosition + Submarine.Position; }
}
public Character ClosestCharacter
{
get { return closestCharacter; }
}
public Character SelectedCharacter
{
get { return selectedCharacter; }
}
private float lowPassMultiplier;
public float LowPassMultiplier
{
get { return lowPassMultiplier; }
set { lowPassMultiplier = MathHelper.Clamp(value, 0.0f, 1.0f); }
}
private float obstructVisionAmount;
public bool ObstructVision
{
get
{
return obstructVisionAmount > 0.5f;
}
set
{
obstructVisionAmount = 1.0f;
}
}
public float SoundRange
{
get { return aiTarget.SoundRange; }
}
public float SightRange
{
get { return aiTarget.SightRange; }
}
private float pressureProtection;
public float PressureProtection
{
get { return pressureProtection; }
set
{
pressureProtection = MathHelper.Clamp(value, 0.0f, 100.0f);
}
}
public bool IsUnconscious
{
get { return (needsAir && oxygen <= 0.0f) || health <= 0.0f; }
}
public bool NeedsAir
{
get { return needsAir; }
set { needsAir = value; }
}
public float Oxygen
{
get { return oxygen; }
set
{
if (!MathUtils.IsValid(value)) return;
oxygen = MathHelper.Clamp(value, -100.0f, 100.0f);
if (oxygen == -100.0f) Kill(AnimController.InWater ? CauseOfDeath.Drowning : CauseOfDeath.Suffocation);
}
}
public float OxygenAvailable
{
get { return oxygenAvailable; }
set { oxygenAvailable = MathHelper.Clamp(value, 0.0f, 100.0f); }
}
public float Stun
{
get { return AnimController.StunTimer; }
set
{
if (GameMain.Client != null) return;
StartStun(value);
}
}
public float Health
{
get { return health; }
set
{
if (!MathUtils.IsValid(value)) return;
if (GameMain.Client != null) return;
float newHealth = MathHelper.Clamp(value, minHealth, maxHealth);
if (newHealth == health) return;
health = newHealth;
if (GameMain.Server != null)
{
if (Math.Abs(health - lastSentHealth) > (maxHealth - minHealth) / 255.0f || Math.Sign(health) != Math.Sign(lastSentHealth))
{
GameMain.Server.CreateEntityEvent(this, new object[] { NetEntityEvent.Type.Status });
lastSentHealth = health;
}
}
}
}
public float MaxHealth
{
get { return maxHealth; }
}
public float Bleeding
{
get { return bleeding; }
set
{
if (!MathUtils.IsValid(value)) return;
if (GameMain.Client != null) return;
float newBleeding = MathHelper.Clamp(value, 0.0f, 5.0f);
if (newBleeding == bleeding) return;
bleeding = newBleeding;
if (GameMain.Server != null)
GameMain.Server.CreateEntityEvent(this, new object[] { NetEntityEvent.Type.Status });
}
}
public Dictionary<object, HUDProgressBar> HUDProgressBars
{
get { return hudProgressBars; }
}
public HuskInfection huskInfection;
public float HuskInfectionState
{
get
{
return huskInfection == null ? 0.0f : huskInfection.IncubationTimer;
}
set
{
if (ConfigPath != humanConfigFile) return;
if (value <= 0.0f)
{
if (huskInfection != null)
{
//already active, can't cure anymore
if (huskInfection.State == HuskInfection.InfectionState.Active) return;
huskInfection.Remove(this);
huskInfection = null;
}
}
else
{
if (huskInfection == null) huskInfection = new HuskInfection(this);
huskInfection.IncubationTimer = MathHelper.Clamp(value, 0.0f, 1.0f);
}
}
}
public bool CanSpeak
{
get
{
return !IsUnconscious && Stun <= 0.0f && (huskInfection == null || huskInfection.CanSpeak);
}
}
public bool DoesBleed
{
get;
private set;
}
public float BleedingDecreaseSpeed
{
get;
private set;
}
public float PressureTimer
{
get;
private set;
}
public float DisableImpactDamageTimer
{
get;
set;
}
public float SpeedMultiplier
{
get;
set;
}
public Item[] SelectedItems
{
get { return selectedItems; }
}
public Item SelectedConstruction
{
get { return selectedConstruction; }
set { selectedConstruction = value; }
}
public Item ClosestItem
{
get { return closestItem; }
}
public virtual AIController AIController
{
get { return null; }
}
public bool IsDead
{
get { return isDead; }
}
public CauseOfDeath CauseOfDeath
{
get { return causeOfDeath; }
}
public bool CanBeSelected
{
get
{
return isDead || Stun > 0.0f || LockHands || IsUnconscious;
}
}
public override Vector2 SimPosition
{
get { return AnimController.Collider.SimPosition; }
}
public override Vector2 Position
{
get { return ConvertUnits.ToDisplayUnits(SimPosition); }
}
public override Vector2 DrawPosition
{
get { return AnimController.MainLimb.body.DrawPosition; }
}
public delegate void OnDeathHandler(Character character, CauseOfDeath causeOfDeath);
public OnDeathHandler OnDeath;
public static Character Create(CharacterInfo characterInfo, Vector2 position, bool isRemotePlayer = false, bool hasAi=true)
{
return Create(characterInfo.File, position, characterInfo, isRemotePlayer, hasAi);
}
public static Character Create(string file, Vector2 position, CharacterInfo characterInfo = null, bool isRemotePlayer = false, bool hasAi=true)
{
#if LINUX
if (!System.IO.File.Exists(file))
{
//if the file was not found, attempt to convert the name of the folder to upper case
var splitPath = file.Split('/');
if (splitPath.Length > 2)
{
splitPath[splitPath.Length-2] =
splitPath[splitPath.Length-2].First().ToString().ToUpper() + splitPath[splitPath.Length-2].Substring(1);
file = string.Join("/", splitPath);
}
if (!System.IO.File.Exists(file))
{
DebugConsole.ThrowError("Spawning a character failed - file \""+file+"\" not found!");
return null;
}
}
#else
if (!System.IO.File.Exists(file))
{
DebugConsole.ThrowError("Spawning a character failed - file \""+file+"\" not found!");
return null;
}
#endif
Character newCharacter = null;
if (file != humanConfigFile)
{
var aiCharacter = new AICharacter(file, position, characterInfo, isRemotePlayer);
var ai = new EnemyAIController(aiCharacter, file);
aiCharacter.SetAI(ai);
aiCharacter.minHealth = 0.0f;
newCharacter = aiCharacter;
}
else if (hasAi)
{
var aiCharacter = new AICharacter(file, position, characterInfo, isRemotePlayer);
var ai = new HumanAIController(aiCharacter);
aiCharacter.SetAI(ai);
aiCharacter.minHealth = -100.0f;
newCharacter = aiCharacter;
}
else
{
newCharacter = new Character(file, position, characterInfo, isRemotePlayer);
newCharacter.minHealth = -100.0f;
}
if (GameMain.Server != null && Entity.Spawner != null)
Entity.Spawner.CreateNetworkEvent(newCharacter, false);
return newCharacter;
}
protected Character(string file, Vector2 position, CharacterInfo characterInfo = null, bool isRemotePlayer = false)
: base(null)
{
keys = new Key[Enum.GetNames(typeof(InputType)).Length];
for (int i = 0; i < Enum.GetNames(typeof(InputType)).Length; i++)
{
keys[i] = new Key(GameMain.Config.KeyBind((InputType)i));
}
ConfigPath = file;
selectedItems = new Item[2];
hudProgressBars = new Dictionary<object, HUDProgressBar>();
IsRemotePlayer = isRemotePlayer;
oxygen = 100.0f;
oxygenAvailable = 100.0f;
aiTarget = new AITarget(this);
lowPassMultiplier = 1.0f;
Properties = ObjectProperty.GetProperties(this);
Info = characterInfo;
if (file == humanConfigFile && characterInfo == null)
{
Info = new CharacterInfo(file);
}
XDocument doc = ToolBox.TryLoadXml(file);
if (doc == null || doc.Root == null) return;
SpeciesName = ToolBox.GetAttributeString(doc.Root, "name", "Unknown");
IsHumanoid = ToolBox.GetAttributeBool(doc.Root, "humanoid", false);
if (IsHumanoid)
{
AnimController = new HumanoidAnimController(this, doc.Root.Element("ragdoll"));
AnimController.TargetDir = Direction.Right;
inventory = new CharacterInventory(16, this);
}
else
{
AnimController = new FishAnimController(this, doc.Root.Element("ragdoll"));
PressureProtection = 100.0f;
}
AnimController.SetPosition(ConvertUnits.ToSimUnits(position));
maxHealth = ToolBox.GetAttributeFloat(doc.Root, "health", 100.0f);
health = maxHealth;
DoesBleed = ToolBox.GetAttributeBool(doc.Root, "doesbleed", true);
BleedingDecreaseSpeed = ToolBox.GetAttributeFloat(doc.Root, "bleedingdecreasespeed", 0.05f);
needsAir = ToolBox.GetAttributeBool(doc.Root, "needsair", false);
drowningTime = ToolBox.GetAttributeFloat(doc.Root, "drowningtime", 10.0f);
soundInterval = ToolBox.GetAttributeFloat(doc.Root, "soundinterval", 10.0f);
var soundElements = doc.Root.Elements("sound").ToList();
sounds = new List<CharacterSound>();
foreach (XElement soundElement in soundElements)
{
sounds.Add(new CharacterSound(soundElement));
}
if (file == humanConfigFile)
{
if (Info.PickedItemIDs.Any())
{
for (ushort i = 0; i < Info.PickedItemIDs.Count; i++ )
{
if (Info.PickedItemIDs[i] == 0) continue;
Item item = FindEntityByID(Info.PickedItemIDs[i]) as Item;
System.Diagnostics.Debug.Assert(item != null);
if (item == null) continue;
item.Pick(this, true, true, true);
inventory.TryPutItem(item, i, false, false);
}
}
}
AnimController.FindHull(null);
if (AnimController.CurrentHull != null) Submarine = AnimController.CurrentHull.Submarine;
CharacterList.Add(this);
//characters start disabled in the multiplayer mode, and are enabled if/when
// - controlled by the player
// - client receives a position update from the server
// - server receives an input message from the client controlling the character
// - if an AICharacter, the server enables it when close enough to any of the players
Enabled = GameMain.NetworkMember == null;
}
private static string humanConfigFile;
public static string HumanConfigFile
{
get
{
if (string.IsNullOrEmpty(humanConfigFile))
{
var characterFiles = GameMain.SelectedPackage.GetFilesOfType(ContentType.Character);
humanConfigFile = characterFiles.Find(c => c.EndsWith("human.xml"));
if (humanConfigFile == null)
{
DebugConsole.ThrowError("Couldn't find a config file for humans from the selected content package!");
DebugConsole.ThrowError("(The config file must end with \"human.xml\")");
return "";
}
}
return humanConfigFile;
}
}
public bool IsKeyHit(InputType inputType)
{
if (GameMain.Server != null && Character.Controlled != this)
{
switch (inputType)
{
case InputType.Left:
return !(dequeuedInput.HasFlag(InputNetFlags.Left)) && (prevDequeuedInput.HasFlag(InputNetFlags.Left));
case InputType.Right:
return !(dequeuedInput.HasFlag(InputNetFlags.Right)) && (prevDequeuedInput.HasFlag(InputNetFlags.Right));
case InputType.Up:
return !(dequeuedInput.HasFlag(InputNetFlags.Up)) && (prevDequeuedInput.HasFlag(InputNetFlags.Up));
case InputType.Down:
return !(dequeuedInput.HasFlag(InputNetFlags.Down)) && (prevDequeuedInput.HasFlag(InputNetFlags.Down));
case InputType.Run:
return !(dequeuedInput.HasFlag(InputNetFlags.Run)) && (prevDequeuedInput.HasFlag(InputNetFlags.Run));
case InputType.Crouch:
return !(dequeuedInput.HasFlag(InputNetFlags.Crouch)) && (prevDequeuedInput.HasFlag(InputNetFlags.Crouch));
case InputType.Select:
return dequeuedInput.HasFlag(InputNetFlags.Select); //TODO: clean up the way this input is registered
case InputType.Use:
return !(dequeuedInput.HasFlag(InputNetFlags.Use)) && (prevDequeuedInput.HasFlag(InputNetFlags.Use));
default:
return false;
}
}
return keys[(int)inputType].Hit;
}
public bool IsKeyDown(InputType inputType)
{
if (GameMain.Server != null && Character.Controlled != this)
{
switch (inputType)
{
case InputType.Left:
return dequeuedInput.HasFlag(InputNetFlags.Left);
case InputType.Right:
return dequeuedInput.HasFlag(InputNetFlags.Right);
case InputType.Up:
return dequeuedInput.HasFlag(InputNetFlags.Up);
case InputType.Down:
return dequeuedInput.HasFlag(InputNetFlags.Down);
case InputType.Run:
return dequeuedInput.HasFlag(InputNetFlags.Run);
case InputType.Crouch:
return dequeuedInput.HasFlag(InputNetFlags.Crouch);
case InputType.Select:
return false; //TODO: clean up the way this input is registered
case InputType.Aim:
return dequeuedInput.HasFlag(InputNetFlags.Aim);
case InputType.Use:
return dequeuedInput.HasFlag(InputNetFlags.Use);
case InputType.Attack:
return dequeuedInput.HasFlag(InputNetFlags.Attack);
}
return false;
}
return keys[(int)inputType].Held;
}
public void SetInput(InputType inputType, bool hit, bool held)
{
keys[(int)inputType].Hit = hit;
keys[(int)inputType].Held = held;
}
public void ClearInput(InputType inputType)
{
keys[(int)inputType].Hit = false;
keys[(int)inputType].Held = false;
}
public void ClearInputs()
{
foreach (Key key in keys)
{
key.Hit = false;
key.Held = false;
}
}
public override string ToString()
{
return (info != null && !string.IsNullOrWhiteSpace(info.Name)) ? info.Name : SpeciesName;
}
public void GiveJobItems(WayPoint spawnPoint)
{
if (info == null || info.Job == null) return;
info.Job.GiveJobItems(this, spawnPoint);
}
public int GetSkillLevel(string skillName)
{
return (Info==null || Info.Job==null) ? 0 : Info.Job.GetSkillLevel(skillName);
}
float findClosestTimer;
public Vector2 GetTargetMovement()
{
Vector2 targetMovement = Vector2.Zero;
if (IsKeyDown(InputType.Left)) targetMovement.X -= 1.0f;
if (IsKeyDown(InputType.Right)) targetMovement.X += 1.0f;
if (IsKeyDown(InputType.Up)) targetMovement.Y += 1.0f;
if (IsKeyDown(InputType.Down)) targetMovement.Y -= 1.0f;
//the vertical component is only used for falling through platforms and climbing ladders when not in water,
//so the movement can't be normalized or the Character would walk slower when pressing down/up
if (AnimController.InWater)
{
float length = targetMovement.Length();
if (length > 0.0f) targetMovement = targetMovement / length;
}
if (IsKeyDown(InputType.Run))
{
//can't run if
// - not a humanoid
// - dragging someone
// - crouching
// - moving backwards
if (AnimController is HumanoidAnimController &&
selectedCharacter == null &&
!((HumanoidAnimController)AnimController).Crouching &&
Math.Sign(targetMovement.X) != -Math.Sign(AnimController.Dir))
{
targetMovement *= 3.0f;
}
}
targetMovement *= SpeedMultiplier;
SpeedMultiplier = 1.0f;
return targetMovement;
}
public void Control(float deltaTime, Camera cam)
{
ViewTarget = null;
if (!AllowInput) return;
if (!(this is AICharacter) || controlled == this)
{
Vector2 targetMovement = GetTargetMovement();
AnimController.TargetMovement = targetMovement;
AnimController.IgnorePlatforms = AnimController.TargetMovement.Y < 0.0f;
}
if (AnimController is HumanoidAnimController)
{
((HumanoidAnimController) AnimController).Crouching = IsKeyDown(InputType.Crouch);
}
if (AnimController.onGround &&
!AnimController.InWater &&
AnimController.Anim != AnimController.Animation.UsingConstruction &&
AnimController.Anim != AnimController.Animation.CPR)
{
//Limb head = AnimController.GetLimb(LimbType.Head);
if (cursorPosition.X < AnimController.Collider.Position.X - 10.0f)
{
AnimController.TargetDir = Direction.Left;
}
else if (cursorPosition.X > AnimController.Collider.Position.X + 10.0f)
{
AnimController.TargetDir = Direction.Right;
}
}
if (GameMain.Server != null && Character.Controlled != this)
{
if (dequeuedInput.HasFlag(InputNetFlags.FacingLeft))
{
AnimController.TargetDir = Direction.Left;
}
else
{
AnimController.TargetDir = Direction.Right;
}
}
else if (GameMain.Client != null && Character.controlled != this)
{
if (memState.Count > 0)
{
AnimController.TargetDir = memState[0].Direction;
}
}
if (attackCoolDown >0.0f)
{
attackCoolDown -= deltaTime;
}
else if (IsKeyDown(InputType.Attack))
{
var attackLimb = AnimController.Limbs.FirstOrDefault(l => l.attack != null);
if (attackLimb != null)
{
Vector2 attackPos =
attackLimb.SimPosition + Vector2.Normalize(cursorPosition - attackLimb.Position) * ConvertUnits.ToSimUnits(attackLimb.attack.Range);
var body = Submarine.PickBody(
attackLimb.SimPosition,
attackPos,
AnimController.Limbs.Select(l => l.body.FarseerBody).ToList(),
Physics.CollisionCharacter | Physics.CollisionWall);
IDamageable attackTarget = null;
if (body != null)
{
attackPos = Submarine.LastPickedPosition;
if (body.UserData is Submarine)
{
var sub = ((Submarine)body.UserData);
body = Submarine.PickBody(
attackLimb.SimPosition - ((Submarine)body.UserData).SimPosition,
attackPos - ((Submarine)body.UserData).SimPosition,
AnimController.Limbs.Select(l => l.body.FarseerBody).ToList(),
Physics.CollisionWall);
if (body != null)
{
attackPos = Submarine.LastPickedPosition + sub.SimPosition;
attackTarget = body.UserData as IDamageable;
}
}
else
{
if (body.UserData is IDamageable)
{
attackTarget = (IDamageable)body.UserData;
}
else if (body.UserData is Limb)
{
attackTarget = ((Limb)body.UserData).character;
}
}
}
attackLimb.UpdateAttack(deltaTime, attackPos, attackTarget);
if (attackLimb.AttackTimer > attackLimb.attack.Duration)
{
attackLimb.AttackTimer = 0.0f;
attackCoolDown = 1.0f;
}
}
}
for (int i = 0; i < selectedItems.Length; i++ )
{
if (selectedItems[i] == null) continue;
if (i == 1 && selectedItems[0] == selectedItems[1]) continue;
if (IsKeyDown(InputType.Use)) selectedItems[i].Use(deltaTime, this);
if (IsKeyDown(InputType.Aim) && selectedItems[i] != null) selectedItems[i].SecondaryUse(deltaTime, this);
}
if (selectedConstruction != null)
{
if (IsKeyDown(InputType.Use)) selectedConstruction.Use(deltaTime, this);
if (selectedConstruction != null && IsKeyDown(InputType.Aim)) selectedConstruction.SecondaryUse(deltaTime, this);
}
if (selectedCharacter!=null)
{
if (Vector2.DistanceSquared(selectedCharacter.WorldPosition, WorldPosition) > 90000.0f || !selectedCharacter.CanBeSelected)
{
DeselectCharacter();
}
}
if (IsRemotePlayer)
{
foreach (Key key in keys)
{
key.ResetHit();
}
}
}
public bool HasEquippedItem(Item item)
{
return !inventory.IsInLimbSlot(item, InvSlotType.Any);
}
public bool HasSelectedItem(Item item)
{
return selectedItems.Contains(item);
}
public bool TrySelectItem(Item item)
{
bool rightHand = inventory.IsInLimbSlot(item, InvSlotType.RightHand);
bool leftHand = inventory.IsInLimbSlot(item, InvSlotType.LeftHand);
bool selected = false;
if (rightHand && SelectedItems[0] == null)
{
selectedItems[0] = item;
selected = true;
}
if (leftHand && SelectedItems[1] == null)
{
selectedItems[1] = item;
selected = true;
}
return selected;
}
public bool TrySelectItem(Item item, int index)
{
if (selectedItems[index] != null) return false;
selectedItems[index] = item;
return true;
}
public void DeselectItem(Item item)
{
for (int i = 0; i < selectedItems.Length; i++)
{
if (selectedItems[i] == item) selectedItems[i] = null;
}
}
public bool CanAccessInventory(Inventory inventory)
{
if (!AllowInput || LockHands) return false;
if (inventory.Owner is Character && inventory.Owner != this)
{
var owner = (Character)inventory.Owner;
return owner.isDead || owner.IsUnconscious || owner.Stun > 0.0f || owner.LockHands;
}
if (inventory.Owner is Item)
{
var owner = (Item)inventory.Owner;
if (!CanAccessItem(owner))
{
return false;
}
}
return true;
}
public bool CanAccessItem(Item item)
{
if (!AllowInput || LockHands) return false;
if (item.ParentInventory != null)
{
return CanAccessInventory(item.ParentInventory);
}
float maxDist = item.PickDistance * 1.2f;
if (maxDist <= 0.01f)
{
maxDist = 150.0f;
}
if (Vector2.DistanceSquared(WorldPosition, item.WorldPosition) < maxDist*maxDist ||
item.IsInsideTrigger(WorldPosition))
{
return true;
}
return item.GetComponent<Items.Components.Ladder>() != null;
}
private Item FindClosestItem(Vector2 mouseSimPos, out float distance)
{
distance = 0.0f;
Limb torso = AnimController.GetLimb(LimbType.Torso);
if (torso == null) return null;
Vector2 pos = (torso.body.TargetPosition != null) ? (Vector2)torso.body.TargetPosition : torso.SimPosition;
Vector2 pickPos = mouseSimPos;
if (Submarine != null)
{
pos += Submarine.SimPosition;
pickPos += Submarine.SimPosition;
}
if (selectedConstruction != null) pickPos = ConvertUnits.ToSimUnits(selectedConstruction.WorldPosition);
return Item.FindPickable(pos, pickPos, AnimController.CurrentHull, selectedItems, out distance);
}
private Character FindClosestCharacter(Vector2 mouseSimPos, float maxDist = 150.0f)
{
Character closestCharacter = null;
float closestDist = 0.0f;
maxDist = ConvertUnits.ToSimUnits(maxDist);
foreach (Character c in CharacterList)
{
if (c == this || !c.enabled) continue;
if (Vector2.DistanceSquared(SimPosition, c.SimPosition) > maxDist*maxDist) continue;
float dist = Vector2.DistanceSquared(mouseSimPos, c.SimPosition);
if (dist < maxDist*maxDist && (closestCharacter==null || dist<closestDist))
{
closestCharacter = c;
closestDist = dist;
continue;
}
}
return closestCharacter;
}
private void TransformCursorPos()
{
if (Submarine == null)
{
//character is outside but cursor position inside
if (cursorPosition.Y > Level.Loaded.Size.Y)
{
var sub = Submarine.FindContaining(cursorPosition);
if (sub != null) cursorPosition += sub.Position;
}
}
else
{
//character is inside but cursor position is outside
if (cursorPosition.Y < Level.Loaded.Size.Y)
{
cursorPosition -= Submarine.Position;
}
}
}
public void SelectCharacter(Character character)
{
if (character == null) return;
selectedCharacter = character;
}
public void DeselectCharacter()
{
if (selectedCharacter == null) return;
foreach (Limb limb in selectedCharacter.AnimController.Limbs)
{
if (limb.pullJoint != null) limb.pullJoint.Enabled = false;
}
selectedCharacter = null;
}
/// <summary>
/// Control the Character according to player input
/// </summary>
public void ControlLocalPlayer(float deltaTime, Camera cam, bool moveCam = true)
{
if (!DisableControls)
{
for (int i = 0; i < keys.Length; i++ )
{
keys[i].SetState();
}
}
else
{
foreach (Key key in keys)
{
if (key == null) continue;
key.Reset();
}
}
if (moveCam && needsAir)
{
if (pressureProtection < 80.0f &&
(AnimController.CurrentHull == null || AnimController.CurrentHull.LethalPressure > 50.0f))
{
float pressure = AnimController.CurrentHull == null ? 100.0f : AnimController.CurrentHull.LethalPressure;
cam.Zoom = MathHelper.Lerp(cam.Zoom,
(pressure / 50.0f) * Rand.Range(1.0f, 1.05f),
(pressure - 50.0f) / 50.0f);
}
cam.OffsetAmount = MathHelper.Lerp(cam.OffsetAmount, 250.0f, 0.05f);
}
cursorPosition = cam.ScreenToWorld(PlayerInput.MousePosition);
if (AnimController.CurrentHull != null && AnimController.CurrentHull.Submarine != null)
{
cursorPosition -= AnimController.CurrentHull.Submarine.Position;
}
Vector2 mouseSimPos = ConvertUnits.ToSimUnits(cursorPosition);
if (Lights.LightManager.ViewTarget == this && Vector2.DistanceSquared(AnimController.Limbs[0].SimPosition, mouseSimPos) > 1.0f)
{
Body body = Submarine.PickBody(AnimController.Limbs[0].SimPosition, mouseSimPos);
Structure structure = null;
if (body != null) structure = body.UserData as Structure;
if (structure != null)
{
if (!structure.CastShadow && moveCam)
{
cam.OffsetAmount = MathHelper.Lerp(cam.OffsetAmount, 500.0f, 0.05f);
}
}
}
if (!LockHands)
{
//find the closest item if selectkey has been hit, or if the Character is being
//controlled by the player (in order to highlight it)
if (findClosestTimer <= 0.0f || Screen.Selected == GameMain.EditMapScreen)
{
closestCharacter = FindClosestCharacter(mouseSimPos);
if (closestCharacter != null && closestCharacter.info==null)
{
closestCharacter = null;
}
float closestItemDist = 0.0f;
closestItem = FindClosestItem(mouseSimPos, out closestItemDist);
if (closestCharacter != null && closestItem != null)
{
if (Vector2.DistanceSquared(closestCharacter.SimPosition, mouseSimPos) < ConvertUnits.ToSimUnits(closestItemDist)*ConvertUnits.ToSimUnits(closestItemDist))
{
if (selectedConstruction != closestItem) closestItem = null;
}
else
{
closestCharacter = null;
}
}
findClosestTimer = 0.1f;
}
else
{
findClosestTimer -= deltaTime;
}
if (selectedCharacter == null && closestItem != null)
{
closestItem.IsHighlighted = true;
if (!LockHands && closestItem.Pick(this))
{
}
}
if (IsKeyHit(InputType.Select))
{
if (selectedCharacter != null)
{
DeselectCharacter();
}
else if (closestCharacter != null && closestCharacter.IsHumanoid && closestCharacter.CanBeSelected)
{
SelectCharacter(closestCharacter);
}
}
}
else
{
if (selectedCharacter != null) DeselectCharacter();
selectedConstruction = null;
closestItem = null;
closestCharacter = null;
}
DisableControls = false;
}
public static void UpdateAnimAll(float deltaTime)
{
foreach (Character c in CharacterList)
{
if (!c.Enabled || c.AnimController.Frozen) continue;
c.AnimController.UpdateAnim(deltaTime);
}
}
public static void AddAllToGUIUpdateList()
{
for (int i = 0; i < CharacterList.Count; i++)
{
CharacterList[i].AddToGUIUpdateList();
}
}
public static void UpdateAll(Camera cam, float deltaTime)
{
if (GameMain.Client == null)
{
foreach (Character c in CharacterList)
{
if (!(c is AICharacter)) continue;
if (GameMain.Server != null)
{
//disable AI characters that are far away from all clients and the host's character
c.Enabled =
CharacterList.Any(c2 =>
(c2.IsRemotePlayer || c2 == GameMain.Server.Character) &&
Vector2.Distance(c2.WorldPosition, c.WorldPosition) < NetConfig.CharacterIgnoreDistance);
}
else
{
//disable AI characters that are far away from the sub and the controlled character
c.Enabled = Vector2.Distance(Submarine.MainSub.WorldPosition, c.WorldPosition) < NetConfig.CharacterIgnoreDistance ||
(controlled != null && Vector2.Distance(controlled.WorldPosition, c.WorldPosition) < NetConfig.CharacterIgnoreDistance);
}
}
}
for (int i = 0; i < CharacterList.Count; i++)
{
CharacterList[i].Update(cam, deltaTime);
}
}
public virtual void AddToGUIUpdateList()
{
if (controlled == this)
{
CharacterHUD.AddToGUIUpdateList(this);
}
}
public virtual void Update(Camera cam, float deltaTime)
{
if (GameMain.Client != null && this == Controlled && !isSynced) return;
if (!Enabled) return;
PreviousHull = CurrentHull;
CurrentHull = Hull.FindHull(WorldPosition, CurrentHull, true);
//if (PreviousHull != CurrentHull && Character.Controlled == this) Hull.DetectItemVisibility(this); //WIP item culling
speechBubbleTimer = Math.Max(0.0f, speechBubbleTimer - deltaTime);
obstructVisionAmount = Math.Max(obstructVisionAmount - deltaTime, 0.0f);
if (inventory!=null)
{
foreach (Item item in inventory.Items)
{
if (item == null || item.body == null || item.body.Enabled) continue;
item.SetTransform(SimPosition, 0.0f);
item.Submarine = Submarine;
}
}
if (isDead) return;
if (huskInfection != null) huskInfection.Update(deltaTime, this);
if (GameMain.NetworkMember != null)
{
UpdateNetInput();
}
else
{
AnimController.Frozen = false;
}
DisableImpactDamageTimer -= deltaTime;
if (needsAir)
{
bool protectedFromPressure = PressureProtection > 0.0f;
protectedFromPressure = protectedFromPressure && WorldPosition.Y > SubmarineBody.DamageDepth;
if (!protectedFromPressure &&
(AnimController.CurrentHull == null || AnimController.CurrentHull.LethalPressure >= 80.0f))
{
PressureTimer += ((AnimController.CurrentHull == null) ?
100.0f : AnimController.CurrentHull.LethalPressure) * deltaTime;
if (PressureTimer >= 100.0f)
{
if (controlled == this) cam.Zoom = 5.0f;
Implode();
return;
}
}
else
{
PressureTimer = 0.0f;
}
}
if (controlled == this)
{
Lights.LightManager.ViewTarget = this;
CharacterHUD.Update(deltaTime, this);
foreach (HUDProgressBar progressBar in hudProgressBars.Values)
{
progressBar.Update(deltaTime);
}
foreach (var pb in hudProgressBars.Where(pb => pb.Value.FadeTimer<=0.0f).ToList())
{
hudProgressBars.Remove(pb.Key);
}
}
if (IsUnconscious)
{
UpdateUnconscious(deltaTime);
return;
}
if (controlled == this)
{
ControlLocalPlayer(deltaTime, cam);
}
Control(deltaTime, cam);
if (selectedConstruction != null && !selectedConstruction.IsInPickRange(WorldPosition))
{
selectedConstruction = null;
}
if (controlled != this && !(this is AICharacter))
{
if (!LockHands)
{
Vector2 mouseSimPos = ConvertUnits.ToSimUnits(cursorPosition);
if (IsKeyHit(InputType.Select) && GameMain.Server==null)
{
closestCharacter = FindClosestCharacter(mouseSimPos);
if (closestCharacter != null && closestCharacter.info == null)
{
closestCharacter = null;
}
float closestItemDist = 0.0f;
closestItem = FindClosestItem(mouseSimPos, out closestItemDist);
if (closestCharacter != null && closestItem != null)
{
if (closestItem != null) closestItemDist = (closestItem.Position - AnimController.Collider.Position).Length();
if (Vector2.Distance(closestCharacter.SimPosition, mouseSimPos) < ConvertUnits.ToSimUnits(closestItemDist))
{
if (selectedConstruction != closestItem) closestItem = null;
}
else
{
closestCharacter = null;
}
}
}
if (selectedCharacter == null && closestItem != null)
{
//DebugConsole.NewMessage(closestItem.ToString(), Color.Yellow);
//closestItem.IsHighlighted = true;
if (!LockHands && closestItem.Pick(this))
{
if (AnimController.Anim == AnimController.Animation.Climbing)
{
//DebugConsole.NewMessage("ladder woo",Color.Lime);
}
}
}
if (IsKeyHit(InputType.Select))
{
if (selectedCharacter != null)
{
DeselectCharacter();
}
else if (closestCharacter != null && closestCharacter.IsHumanoid && closestCharacter.CanBeSelected)
{
SelectCharacter(closestCharacter);
}
}
}
else
{
if (selectedCharacter != null) DeselectCharacter();
selectedConstruction = null;
closestItem = null;
closestCharacter = null;
}
}
if (selectedCharacter != null && AnimController.Anim == AnimController.Animation.CPR)
{
if (GameMain.Client == null) selectedCharacter.Oxygen += (GetSkillLevel("Medical") / 10.0f) * deltaTime;
}
UpdateSightRange();
if (aiTarget != null) aiTarget.SoundRange = 0.0f;
lowPassMultiplier = MathHelper.Lerp(lowPassMultiplier, 1.0f, 0.1f);
if (needsAir) UpdateOxygen(deltaTime);
Health -= bleeding * deltaTime;
Bleeding -= BleedingDecreaseSpeed * deltaTime;
if (health <= minHealth) Kill(CauseOfDeath.Bloodloss);
if (!IsDead) LockHands = false;
}
private void UpdateOxygen(float deltaTime)
{
float prevOxygen = oxygen;
Oxygen += deltaTime * (oxygenAvailable < 30.0f ? -5.0f : 10.0f);
if (prevOxygen > 0.0f && Oxygen <= 0.0f && controlled == this)
{
SoundPlayer.PlaySound("drown");
}
PressureProtection -= deltaTime * 100.0f;
float hullAvailableOxygen = 0.0f;
if (!AnimController.HeadInWater && AnimController.CurrentHull != null)
{
hullAvailableOxygen = AnimController.CurrentHull.OxygenPercentage;
AnimController.CurrentHull.Oxygen -= Hull.OxygenConsumptionSpeed * deltaTime;
}
OxygenAvailable += Math.Sign(hullAvailableOxygen - oxygenAvailable) * deltaTime * 50.0f;
}
private void UpdateUnconscious(float deltaTime)
{
Stun = Math.Max(5.0f, Stun);
AnimController.ResetPullJoints();
selectedConstruction = null;
if (oxygen <= 0.0f) Oxygen -= deltaTime * 0.5f;
if (health <= 0.0f)
{
AddDamage(bleeding > 0.5f ? CauseOfDeath.Bloodloss : CauseOfDeath.Damage, Math.Max(bleeding, 1.0f) * deltaTime, null);
}
}
private void UpdateSightRange()
{
if (aiTarget == null) return;
aiTarget.SightRange = Mass*100.0f + AnimController.Collider.LinearVelocity.Length()*500.0f;
}
public void ShowSpeechBubble(float duration, Color color)
{
speechBubbleTimer = Math.Max(speechBubbleTimer, duration);
speechBubbleColor = color;
}
public void Draw(SpriteBatch spriteBatch)
{
if (!Enabled) return;
AnimController.Draw(spriteBatch);
}
public void DrawHUD(SpriteBatch spriteBatch, Camera cam)
{
CharacterHUD.Draw(spriteBatch, this, cam);
}
public virtual void DrawFront(SpriteBatch spriteBatch, Camera cam)
{
if (!Enabled) return;
if (GameMain.DebugDraw)
{
AnimController.DebugDraw(spriteBatch);
if (aiTarget != null) aiTarget.Draw(spriteBatch);
}
/*if (memPos != null && memPos.Count > 0 && controlled == this)
{
PosInfo serverPos = memPos.Last();
Vector2 remoteVec = ConvertUnits.ToDisplayUnits(serverPos.Position);
if (Submarine != null)
{
remoteVec += Submarine.DrawPosition;
}
remoteVec.Y = -remoteVec.Y;
PosInfo localPos = memLocalPos.Find(m => m.ID == serverPos.ID);
int mpind = memLocalPos.FindIndex(lp => lp.ID == localPos.ID);
PosInfo localPos1 = mpind > 0 ? memLocalPos[mpind - 1] : null;
PosInfo localPos2 = mpind < memLocalPos.Count-1 ? memLocalPos[mpind + 1] : null;
Vector2 localVec = ConvertUnits.ToDisplayUnits(localPos.Position);
Vector2 localVec1 = localPos1 != null ? ConvertUnits.ToDisplayUnits(((PosInfo)localPos1).Position) : Vector2.Zero;
Vector2 localVec2 = localPos2 != null ? ConvertUnits.ToDisplayUnits(((PosInfo)localPos2).Position) : Vector2.Zero;
if (Submarine != null)
{
localVec += Submarine.DrawPosition;
localVec1 += Submarine.DrawPosition;
localVec2 += Submarine.DrawPosition;
}
localVec.Y = -localVec.Y;
localVec1.Y = -localVec1.Y;
localVec2.Y = -localVec2.Y;
//GUI.DrawLine(spriteBatch, remoteVec, localVec, Color.Yellow, 0, 10);
if (localPos1 != null) GUI.DrawLine(spriteBatch, remoteVec, localVec1, Color.Lime, 0, 2);
if (localPos2 != null) GUI.DrawLine(spriteBatch, remoteVec + Vector2.One, localVec2 + Vector2.One, Color.Red, 0, 2);
}*/
Vector2 mouseDrawPos = CursorWorldPosition;
mouseDrawPos.Y = -mouseDrawPos.Y;
GUI.DrawLine(spriteBatch, mouseDrawPos - new Vector2(0, 5), mouseDrawPos + new Vector2(0, 5), Color.Red, 0, 10);
Vector2 closestItemPos = closestItem != null ? closestItem.DrawPosition : Vector2.Zero;
closestItemPos.Y = -closestItemPos.Y;
GUI.DrawLine(spriteBatch, closestItemPos - new Vector2(0, 5), closestItemPos + new Vector2(0, 5), Color.Lime, 0, 10);
if (this == controlled || GUI.DisableHUD) return;
Vector2 pos = DrawPosition;
pos.Y = -pos.Y;
if (speechBubbleTimer > 0.0f)
{
GUI.SpeechBubbleIcon.Draw(spriteBatch, pos - Vector2.UnitY * 100.0f,
speechBubbleColor * Math.Min(speechBubbleTimer, 1.0f), 0.0f,
Math.Min((float)speechBubbleTimer, 1.0f));
}
if (this == controlled) return;
if (info != null)
{
Vector2 namePos = new Vector2(pos.X, pos.Y - 110.0f - (5.0f / cam.Zoom)) - GUI.Font.MeasureString(Info.Name) * 0.5f / cam.Zoom;
Color nameColor = Color.White;
if (Character.Controlled != null && TeamID != Character.Controlled.TeamID)
{
nameColor = Color.Red;
}
GUI.Font.DrawString(spriteBatch, Info.Name, namePos + new Vector2(1.0f / cam.Zoom, 1.0f / cam.Zoom), Color.Black, 0.0f, Vector2.Zero, 1.0f / cam.Zoom, SpriteEffects.None, 0.001f);
GUI.Font.DrawString(spriteBatch, Info.Name, namePos, nameColor, 0.0f, Vector2.Zero, 1.0f / cam.Zoom, SpriteEffects.None, 0.0f);
if (GameMain.DebugDraw)
{
GUI.Font.DrawString(spriteBatch, ID.ToString(), namePos - new Vector2(0.0f, 20.0f), Color.White);
}
}
if (isDead) return;
if (health < maxHealth * 0.98f)
{
Vector2 healthBarPos = new Vector2(pos.X - 50, DrawPosition.Y + 100.0f);
GUI.DrawProgressBar(spriteBatch, healthBarPos, new Vector2(100.0f, 15.0f), health / maxHealth, Color.Lerp(Color.Red, Color.Green, health / maxHealth) * 0.8f);
}
}
/// <summary>
/// Creates a progress bar that's "linked" to the specified object (or updates an existing one if there's one already linked to the object)
/// The progress bar will automatically fade out after 1 sec if the method hasn't been called during that time
/// </summary>
public HUDProgressBar UpdateHUDProgressBar(object linkedObject, Vector2 worldPosition, float progress, Color emptyColor, Color fullColor)
{
if (controlled != this) return null;
HUDProgressBar progressBar = null;
if (!hudProgressBars.TryGetValue(linkedObject, out progressBar))
{
progressBar = new HUDProgressBar(worldPosition, Submarine, emptyColor, fullColor);
hudProgressBars.Add(linkedObject, progressBar);
}
progressBar.WorldPosition = worldPosition;
progressBar.FadeTimer = Math.Max(progressBar.FadeTimer, 1.0f);
progressBar.Progress = progress;
return progressBar;
}
public void PlaySound(CharacterSound.SoundType soundType)
{
if (sounds == null || sounds.Count == 0) return;
var matchingSounds = sounds.FindAll(s => s.Type == soundType);
if (matchingSounds.Count == 0) return;
var selectedSound = matchingSounds[Rand.Int(matchingSounds.Count)];
selectedSound.Sound.Play(1.0f, selectedSound.Range, AnimController.WorldPosition);
}
public virtual void AddDamage(CauseOfDeath causeOfDeath, float amount, IDamageable attacker)
{
Health = health-amount;
if (amount > 0.0f)
{
lastAttackCauseOfDeath = causeOfDeath;
if (controlled == this) CharacterHUD.TakeDamage(amount);
}
if (health <= minHealth) Kill(causeOfDeath);
}
public virtual AttackResult AddDamage(IDamageable attacker, Vector2 worldPosition, Attack attack, float deltaTime, bool playSound = false)
{
var attackResult = AddDamage(worldPosition, attack.DamageType, attack.GetDamage(deltaTime), attack.GetBleedingDamage(deltaTime), attack.Stun, playSound, attack.TargetForce);
var attackingCharacter = attacker as Character;
if (attackingCharacter != null && attackingCharacter.AIController == null)
{
GameServer.Log(Name + " attacked by " + attackingCharacter.Name+". Damage: "+attackResult.Damage+" Bleeding damage: "+attackResult.Bleeding, Color.Orange);
}
return attackResult;
}
public AttackResult AddDamage(Vector2 worldPosition, DamageType damageType, float amount, float bleedingAmount, float stun, bool playSound, float attackForce = 0.0f)
{
StartStun(stun);
Limb closestLimb = null;
float closestDistance = 0.0f;
foreach (Limb limb in AnimController.Limbs)
{
float distance = Vector2.Distance(worldPosition, limb.WorldPosition);
if (closestLimb == null || distance < closestDistance)
{
closestLimb = limb;
closestDistance = distance;
}
}
if (Math.Abs(attackForce) > 0.0f)
{
closestLimb.body.ApplyForce((closestLimb.WorldPosition - worldPosition) * attackForce);
}
AttackResult attackResult = closestLimb.AddDamage(worldPosition, damageType, amount, bleedingAmount, playSound);
AddDamage(damageType == DamageType.Burn ? CauseOfDeath.Burn : causeOfDeath, attackResult.Damage, null);
//health -= attackResult.Damage;
//if (health <= 0.0f && damageType == DamageType.Burn) Kill(CauseOfDeath.Burn);
if (DoesBleed)
{
Bleeding += attackResult.Bleeding;
}
return attackResult;
}
public void StartStun(float stunTimer, bool allowStunDecrease = false, bool isNetworkMessage = false)
{
if (GameMain.Client != null && !isNetworkMessage) return;
if ((stunTimer <= AnimController.StunTimer && !allowStunDecrease) || !MathUtils.IsValid(stunTimer)) return;
if (GameMain.Server != null &&
(Math.Sign(stunTimer) != Math.Sign(AnimController.StunTimer) || Math.Abs(stunTimer - AnimController.StunTimer) > 0.1f))
{
GameMain.Server.CreateEntityEvent(this, new object[] { NetEntityEvent.Type.Status });
}
if (Math.Sign(stunTimer) != Math.Sign(AnimController.StunTimer)) AnimController.ResetPullJoints();
AnimController.StunTimer = Math.Max(AnimController.StunTimer, stunTimer);
selectedConstruction = null;
}
private void Implode(bool isNetworkMessage = false)
{
if (!isNetworkMessage)
{
if (GameMain.Client != null) return;
}
Health = minHealth;
BreakJoints();
Kill(CauseOfDeath.Pressure, isNetworkMessage);
}
public void BreakJoints()
{
Vector2 centerOfMass = AnimController.GetCenterOfMass();
foreach (Limb limb in AnimController.Limbs)
{
limb.AddDamage(limb.SimPosition, DamageType.Blunt, 500.0f, 0.0f, false);
Vector2 diff = centerOfMass - limb.SimPosition;
if (diff == Vector2.Zero) continue;
limb.body.ApplyLinearImpulse(diff * 10.0f);
// limb.Damage = 100.0f;
}
SoundPlayer.PlaySound("implode", 1.0f, 150.0f, WorldPosition);
for (int i = 0; i < 10; i++)
{
Particle p = GameMain.ParticleManager.CreateParticle("waterblood",
ConvertUnits.ToDisplayUnits(centerOfMass) + Rand.Vector(5.0f),
Rand.Vector(10.0f));
if (p != null) p.Size *= 2.0f;
GameMain.ParticleManager.CreateParticle("bubbles",
ConvertUnits.ToDisplayUnits(centerOfMass) + Rand.Vector(5.0f),
new Vector2(Rand.Range(-50f, 50f), Rand.Range(-100f, 50f)));
}
foreach (var joint in AnimController.limbJoints)
{
joint.LimitEnabled = false;
}
}
public void Kill(CauseOfDeath causeOfDeath, bool isNetworkMessage = false)
{
if (isDead) return;
//clients aren't allowed to kill characters unless they receive a network message
if (!isNetworkMessage && GameMain.Client != null)
{
return;
}
if (GameMain.NetworkMember != null)
{
if (Character.controlled == this)
{
string chatMessage = InfoTextManager.GetInfoText("Self_CauseOfDeath." + causeOfDeath.ToString());
if (GameMain.Client!=null) chatMessage += " Your chat messages will only be visible to other dead players.";
GameMain.NetworkMember.AddChatMessage(chatMessage, ChatMessageType.Dead);
GameMain.LightManager.LosEnabled = false;
controlled = null;
}
if (GameMain.Server != null)
GameMain.Server.CreateEntityEvent(this, new object[] { NetEntityEvent.Type.Status });
}
AnimController.Frozen = false;
GameServer.Log(Name+" has died (Cause of death: "+causeOfDeath+")", Color.Red);
if (OnDeath != null) OnDeath(this, causeOfDeath);
PlaySound(CharacterSound.SoundType.Die);
isDead = true;
this.causeOfDeath = causeOfDeath;
AnimController.movement = Vector2.Zero;
AnimController.TargetMovement = Vector2.Zero;
for (int i = 0; i < selectedItems.Length; i++ )
{
if (selectedItems[i] != null) selectedItems[i].Drop(this);
}
if (aiTarget != null)
{
aiTarget.Remove();
aiTarget = null;
}
foreach (Limb limb in AnimController.Limbs)
{
if (limb.pullJoint == null) continue;
limb.pullJoint.Enabled = false;
}
foreach (RevoluteJoint joint in AnimController.limbJoints)
{
joint.MotorEnabled = false;
}
if (GameMain.GameSession != null)
{
GameMain.GameSession.KillCharacter(this);
}
}
public void Revive(bool isNetworkMessage)
{
isDead = false;
aiTarget = new AITarget(this);
Health = Math.Max(maxHealth * 0.1f, health);
foreach (RevoluteJoint joint in AnimController.limbJoints)
{
joint.MotorEnabled = true;
}
if (GameMain.GameSession != null)
{
GameMain.GameSession.ReviveCharacter(this);
}
}
public override void Remove()
{
base.Remove();
if (info != null) info.Remove();
CharacterList.Remove(this);
if (controlled == this) controlled = null;
if (GameMain.Client != null && GameMain.Client.Character == this) GameMain.Client.Character = null;
if (aiTarget != null) aiTarget.Remove();
if (AnimController != null) AnimController.Remove();
if (Lights.LightManager.ViewTarget == this) Lights.LightManager.ViewTarget = null;
if (selectedItems[0] != null) selectedItems[0].Drop(this);
if (selectedItems[1] != null) selectedItems[1].Drop(this);
if (GameMain.GameSession?.CrewManager != null &&
GameMain.GameSession.CrewManager.characters.Contains(this))
{
GameMain.GameSession.CrewManager.characters.Remove(this);
}
foreach (Character c in CharacterList)
{
if (c.closestCharacter == this) c.closestCharacter = null;
if (c.selectedCharacter == this) c.selectedCharacter = null;
}
}
}
}