public void Update(float deltaTime) { if (ParentTrigger != null && !ParentTrigger.IsTriggered) { return; } triggerers.RemoveWhere(t => t.Removed); bool isNotClient = true; #if CLIENT isNotClient = GameMain.Client == null; #endif if (!UseNetworkSyncing || isNotClient) { if (ForceFluctuationStrength > 0.0f) { //no need for force fluctuation (or network updates) if the trigger limits velocity and there are no triggerers if (forceMode != TriggerForceMode.LimitVelocity || triggerers.Any()) { forceFluctuationTimer += deltaTime; if (forceFluctuationTimer > ForceFluctuationInterval) { NeedsNetworkSyncing = true; currentForceFluctuation = Rand.Range(1.0f - ForceFluctuationStrength, 1.0f); forceFluctuationTimer = 0.0f; } } } if (randomTriggerProbability > 0.0f) { randomTriggerTimer += deltaTime; if (randomTriggerTimer > randomTriggerInterval) { if (Rand.Range(0.0f, 1.0f) < randomTriggerProbability) { NeedsNetworkSyncing = true; triggeredTimer = stayTriggeredDelay; } randomTriggerTimer = 0.0f; } } } if (stayTriggeredDelay > 0.0f) { if (triggerers.Count == 0) { triggeredTimer -= deltaTime; } else { triggeredTimer = stayTriggeredDelay; } } foreach (Entity triggerer in triggerers) { foreach (StatusEffect effect in statusEffects) { if (triggerer is Character) { effect.Apply(effect.type, deltaTime, triggerer, (Character)triggerer); } else if (triggerer is Item) { effect.Apply(effect.type, deltaTime, triggerer, ((Item)triggerer).AllPropertyObjects); } } if (triggerer is IDamageable damageable) { foreach (Attack attack in attacks) { attack.DoDamage(null, damageable, WorldPosition, deltaTime, false); } } else if (triggerer is Submarine submarine) { foreach (Attack attack in attacks) { float structureDamage = attack.GetStructureDamage(deltaTime); if (structureDamage > 0.0f) { Explosion.RangedStructureDamage(worldPosition, attack.DamageRange, structureDamage); } } } if (Force.LengthSquared() > 0.01f) { if (triggerer is Character character) { ApplyForce(character.AnimController.Collider, deltaTime); foreach (Limb limb in character.AnimController.Limbs) { ApplyForce(limb.body, deltaTime); } } else if (triggerer is Submarine submarine) { ApplyForce(submarine.SubBody.Body, deltaTime); } } if (triggerer == Character.Controlled || triggerer == Character.Controlled?.Submarine) { GameMain.GameScreen.Cam.Shake = Math.Max(GameMain.GameScreen.Cam.Shake, cameraShake); } } }
protected StatusEffect(XElement element) { requiredItems = new List <RelatedItem>(); particleEmitters = new List <ParticleEmitterPrefab>(); IEnumerable <XAttribute> attributes = element.Attributes(); List <XAttribute> propertyAttributes = new List <XAttribute>(); foreach (XAttribute attribute in attributes) { switch (attribute.Name.ToString()) { case "type": try { type = (ActionType)Enum.Parse(typeof(ActionType), attribute.Value, true); } catch { string[] split = attribute.Value.Split('='); type = (ActionType)Enum.Parse(typeof(ActionType), split[0], true); string[] containingNames = split[1].Split(','); onContainingNames = new HashSet <string>(); for (int i = 0; i < containingNames.Length; i++) { onContainingNames.Add(containingNames[i].Trim()); } } break; case "target": string[] Flags = attribute.Value.Split(','); foreach (string s in Flags) { targetTypes |= (TargetType)Enum.Parse(typeof(TargetType), s, true); } break; case "disabledeltatime": disableDeltaTime = ToolBox.GetAttributeBool(attribute, false); break; case "setvalue": setValue = ToolBox.GetAttributeBool(attribute, false); break; case "targetnames": string[] names = attribute.Value.Split(','); targetNames = new HashSet <string>(); for (int i = 0; i < names.Length; i++) { targetNames.Add(names[i].Trim()); } break; case "sound": sound = Sound.Load(attribute.Value.ToString()); break; case "duration": duration = ToolBox.GetAttributeFloat(attribute, 0.0f); break; default: propertyAttributes.Add(attribute); break; } } int count = propertyAttributes.Count; propertyNames = new string[count]; propertyEffects = new object[count]; int n = 0; foreach (XAttribute attribute in propertyAttributes) { propertyNames[n] = attribute.Name.ToString().ToLowerInvariant(); propertyEffects[n] = ToolBox.GetAttributeObject(attribute); n++; } foreach (XElement subElement in element.Elements()) { switch (subElement.Name.ToString().ToLowerInvariant()) { case "explosion": explosion = new Explosion(subElement); break; case "fire": FireSize = ToolBox.GetAttributeFloat(subElement, "size", 10.0f); break; case "use": case "useitem": useItem = true; break; case "particleemitter": particleEmitters.Add(new ParticleEmitterPrefab(subElement)); break; case "requireditem": case "requireditems": RelatedItem newRequiredItem = RelatedItem.Load(subElement); if (newRequiredItem == null) { continue; } requiredItems.Add(newRequiredItem); break; } } }
private void ApplyImpact(float impact, Vector2 direction, Vector2 impactPos, bool applyDamage = true) { if (impact < MinCollisionImpact) { return; } Vector2 impulse = direction * impact * 0.5f; impulse = impulse.ClampLength(MaxCollisionImpact); if (!MathUtils.IsValid(impulse)) { string errorMsg = "Invalid impulse in SubmarineBody.ApplyImpact: " + impulse + ". Direction: " + direction + ", body position: " + Body.SimPosition + ", impact: " + impact + "."; if (GameMain.NetworkMember != null) { errorMsg += GameMain.NetworkMember.IsClient ? " Playing as a client." : " Hosting a server."; } if (GameSettings.VerboseLogging) { DebugConsole.ThrowError(errorMsg); } GameAnalyticsManager.AddErrorEventOnce( "SubmarineBody.ApplyImpact:InvalidImpulse", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); return; } #if CLIENT if (Character.Controlled != null && Character.Controlled.Submarine == submarine) { GameMain.GameScreen.Cam.Shake = impact * 2.0f; if (submarine.Info.Type == SubmarineType.Player && !submarine.DockedTo.Any(s => s.Info.Type != SubmarineType.Player)) { float angularVelocity = (impactPos.X - Body.SimPosition.X) / ConvertUnits.ToSimUnits(submarine.Borders.Width / 2) * impulse.Y - (impactPos.Y - Body.SimPosition.Y) / ConvertUnits.ToSimUnits(submarine.Borders.Height / 2) * impulse.X; GameMain.GameScreen.Cam.AngularVelocity = MathHelper.Clamp(angularVelocity * 0.1f, -1.0f, 1.0f); } } #endif foreach (Character c in Character.CharacterList) { if (c.Submarine != submarine) { continue; } foreach (Limb limb in c.AnimController.Limbs) { if (limb.IsSevered) { continue; } limb.body.ApplyLinearImpulse(limb.Mass * impulse, 10.0f); } c.AnimController.Collider.ApplyLinearImpulse(c.AnimController.Collider.Mass * impulse, 10.0f); bool holdingOntoSomething = false; if (c.SelectedConstruction != null) { var controller = c.SelectedConstruction.GetComponent <Items.Components.Controller>(); holdingOntoSomething = controller != null && controller.LimbPositions.Any(); } //stun for up to 1 second if the impact equal or higher to the maximum impact if (impact >= MaxCollisionImpact && !holdingOntoSomething) { c.SetStun(Math.Min(impulse.Length() * 0.2f, 1.0f)); } } foreach (Item item in Item.ItemList) { if (item.Submarine != submarine || item.CurrentHull == null || item.body == null || !item.body.Enabled) { continue; } item.body.ApplyLinearImpulse(item.body.Mass * impulse, 10.0f); } float dmg = applyDamage ? impact * ImpactDamageMultiplier : 0.0f; var damagedStructures = Explosion.RangedStructureDamage( ConvertUnits.ToDisplayUnits(impactPos), impact * 50.0f, dmg, dmg); #if CLIENT PlayDamageSounds(damagedStructures, impactPos, impact, "StructureBlunt"); #endif }
private void ApplyImpact(float impact, Vector2 direction, Contact contact) { if (impact < 3.0f) { return; } Vector2 tempNormal; FixedArray2 <Vector2> worldPoints; contact.GetWorldManifold(out tempNormal, out worldPoints); Vector2 lastContactPoint = worldPoints[0]; if (Character.Controlled != null && Character.Controlled.Submarine == submarine) { GameMain.GameScreen.Cam.Shake = impact * 2.0f; } Vector2 impulse = direction * impact * 0.5f; impulse = impulse.ClampLength(5.0f); foreach (Character c in Character.CharacterList) { if (c.Submarine != submarine) { continue; } if (impact > 2.0f) { c.SetStun((impact - 2.0f) * 0.1f); } foreach (Limb limb in c.AnimController.Limbs) { limb.body.ApplyLinearImpulse(limb.Mass * impulse, 20.0f); } c.AnimController.Collider.ApplyLinearImpulse(c.AnimController.Collider.Mass * impulse, 20.0f); } foreach (Item item in Item.ItemList) { if (item.Submarine != submarine || item.CurrentHull == null || item.body == null || !item.body.Enabled) { continue; } item.body.ApplyLinearImpulse(item.body.Mass * impulse, 20.0f); } var damagedStructures = Explosion.RangedStructureDamage(ConvertUnits.ToDisplayUnits(lastContactPoint), impact * 50.0f, impact * ImpactDamageMultiplier); #if CLIENT //play a damage sound for the structure that took the most damage float maxDamage = 0.0f; Structure maxDamageStructure = null; foreach (KeyValuePair <Structure, float> structureDamage in damagedStructures) { if (maxDamageStructure == null || structureDamage.Value > maxDamage) { maxDamage = structureDamage.Value; maxDamageStructure = structureDamage.Key; } } if (maxDamageStructure != null) { SoundPlayer.PlayDamageSound( "StructureBlunt", impact * 10.0f, ConvertUnits.ToDisplayUnits(lastContactPoint), MathHelper.Clamp(maxDamage * 4.0f, 1000.0f, 4000.0f), maxDamageStructure.Tags); } #endif }
protected StatusEffect(XElement element, string parentDebugName) { requiredItems = new List <RelatedItem>(); spawnItems = new List <ItemSpawnInfo>(); Afflictions = new List <Affliction>(); ReduceAffliction = new List <Pair <string, float> >(); tags = new HashSet <string>(element.GetAttributeString("tags", "").Split(',')); Range = element.GetAttributeFloat("range", 0.0f); IEnumerable <XAttribute> attributes = element.Attributes(); List <XAttribute> propertyAttributes = new List <XAttribute>(); propertyConditionals = new List <PropertyConditional>(); foreach (XAttribute attribute in attributes) { switch (attribute.Name.ToString()) { case "type": if (!Enum.TryParse(attribute.Value, true, out type)) { DebugConsole.ThrowError("Invalid action type \"" + attribute.Value + "\" in StatusEffect (" + parentDebugName + ")"); } break; case "target": string[] Flags = attribute.Value.Split(','); foreach (string s in Flags) { if (!Enum.TryParse(s, true, out TargetType targetType)) { DebugConsole.ThrowError("Invalid target type \"" + s + "\" in StatusEffect (" + parentDebugName + ")"); } else { targetTypes |= targetType; } } break; case "disabledeltatime": disableDeltaTime = attribute.GetAttributeBool(false); break; case "setvalue": setValue = attribute.GetAttributeBool(false); break; case "targetnames": DebugConsole.ThrowError("Error in StatusEffect config (" + parentDebugName + ") - use identifiers or tags to define the targets instead of names."); break; case "targetidentifiers": string[] identifiers = attribute.Value.Split(','); targetIdentifiers = new HashSet <string>(); for (int i = 0; i < identifiers.Length; i++) { targetIdentifiers.Add(identifiers[i].Trim().ToLowerInvariant()); } break; case "duration": duration = attribute.GetAttributeFloat(0.0f); break; case "stackable": Stackable = attribute.GetAttributeBool(true); break; case "checkconditionalalways": CheckConditionalAlways = attribute.GetAttributeBool(false); break; case "conditionalcomparison": case "comparison": if (!Enum.TryParse(attribute.Value, out conditionalComparison)) { DebugConsole.ThrowError("Invalid conditional comparison type \"" + attribute.Value + "\" in StatusEffect (" + parentDebugName + ")"); } break; case "sound": DebugConsole.ThrowError("Error in StatusEffect " + element.Parent.Name.ToString() + " - sounds should be defined as child elements of the StatusEffect, not as attributes."); break; default: propertyAttributes.Add(attribute); break; } } int count = propertyAttributes.Count; propertyNames = new string[count]; propertyEffects = new object[count]; int n = 0; foreach (XAttribute attribute in propertyAttributes) { propertyNames[n] = attribute.Name.ToString().ToLowerInvariant(); propertyEffects[n] = XMLExtensions.GetAttributeObject(attribute); n++; } foreach (XElement subElement in element.Elements()) { switch (subElement.Name.ToString().ToLowerInvariant()) { case "explosion": explosion = new Explosion(subElement, parentDebugName); break; case "fire": FireSize = subElement.GetAttributeFloat("size", 10.0f); break; case "use": case "useitem": useItemCount++; break; case "remove": case "removeitem": removeItem = true; break; case "requireditem": case "requireditems": RelatedItem newRequiredItem = RelatedItem.Load(subElement, parentDebugName); if (newRequiredItem == null) { DebugConsole.ThrowError("Error in StatusEffect config - requires an item with no identifiers."); continue; } requiredItems.Add(newRequiredItem); break; case "conditional": IEnumerable <XAttribute> conditionalAttributes = subElement.Attributes(); foreach (XAttribute attribute in conditionalAttributes) { if (attribute.Name.ToString().ToLowerInvariant() == "targetitemcomponent") { continue; } propertyConditionals.Add(new PropertyConditional(attribute)); } break; case "affliction": AfflictionPrefab afflictionPrefab; if (subElement.Attribute("name") != null) { DebugConsole.ThrowError("Error in StatusEffect (" + parentDebugName + ") - define afflictions using identifiers instead of names."); string afflictionName = subElement.GetAttributeString("name", "").ToLowerInvariant(); afflictionPrefab = AfflictionPrefab.List.Find(ap => ap.Name.ToLowerInvariant() == afflictionName); if (afflictionPrefab == null) { DebugConsole.ThrowError("Error in StatusEffect (" + parentDebugName + ") - Affliction prefab \"" + afflictionName + "\" not found."); continue; } } else { string afflictionIdentifier = subElement.GetAttributeString("identifier", "").ToLowerInvariant(); afflictionPrefab = AfflictionPrefab.List.Find(ap => ap.Identifier.ToLowerInvariant() == afflictionIdentifier); if (afflictionPrefab == null) { DebugConsole.ThrowError("Error in StatusEffect (" + parentDebugName + ") - Affliction prefab with the identifier \"" + afflictionIdentifier + "\" not found."); continue; } } float afflictionStrength = subElement.GetAttributeFloat(1.0f, "amount", "strength"); Afflictions.Add(afflictionPrefab.Instantiate(afflictionStrength)); break; case "reduceaffliction": if (subElement.Attribute("name") != null) { DebugConsole.ThrowError("Error in StatusEffect (" + parentDebugName + ") - define afflictions using identifiers or types instead of names."); ReduceAffliction.Add(new Pair <string, float>( subElement.GetAttributeString("name", "").ToLowerInvariant(), subElement.GetAttributeFloat(1.0f, "amount", "strength", "reduceamount"))); } else { string name = subElement.GetAttributeString("identifier", null) ?? subElement.GetAttributeString("type", null); name = name.ToLowerInvariant(); if (AfflictionPrefab.List.Any(ap => ap.Identifier == name || ap.AfflictionType == name)) { ReduceAffliction.Add(new Pair <string, float>( name, subElement.GetAttributeFloat(1.0f, "amount", "strength", "reduceamount"))); } else { DebugConsole.ThrowError("Error in StatusEffect (" + parentDebugName + ") - Affliction prefab with the identifier or type \"" + name + "\" not found."); } } break; case "spawnitem": var newSpawnItem = new ItemSpawnInfo(subElement, parentDebugName); if (newSpawnItem.ItemPrefab != null) { spawnItems.Add(newSpawnItem); } break; } } InitProjSpecific(element, parentDebugName); }
protected StatusEffect(XElement element) { requiredItems = new List <RelatedItem>(); tags = new HashSet <string>(element.GetAttributeString("tags", "").Split(',')); #if CLIENT particleEmitters = new List <ParticleEmitter>(); #endif IEnumerable <XAttribute> attributes = element.Attributes(); List <XAttribute> propertyAttributes = new List <XAttribute>(); propertyConditionals = new List <PropertyConditional>(); foreach (XAttribute attribute in attributes) { switch (attribute.Name.ToString()) { case "type": try { type = (ActionType)Enum.Parse(typeof(ActionType), attribute.Value, true); } catch { string[] split = attribute.Value.Split('='); type = (ActionType)Enum.Parse(typeof(ActionType), split[0], true); string[] containingNames = split[1].Split(','); onContainingNames = new HashSet <string>(); for (int i = 0; i < containingNames.Length; i++) { onContainingNames.Add(containingNames[i].Trim()); } } break; case "target": string[] Flags = attribute.Value.Split(','); foreach (string s in Flags) { targetTypes |= (TargetType)Enum.Parse(typeof(TargetType), s, true); } break; case "disabledeltatime": disableDeltaTime = attribute.GetAttributeBool(false); break; case "setvalue": setValue = attribute.GetAttributeBool(false); break; case "targetnames": string[] names = attribute.Value.Split(','); targetNames = new HashSet <string>(); for (int i = 0; i < names.Length; i++) { targetNames.Add(names[i].Trim()); } break; case "duration": duration = attribute.GetAttributeFloat(0.0f); break; case "stackable": Stackable = attribute.GetAttributeBool(true); break; case "checkconditionalalways": CheckConditionalAlways = attribute.GetAttributeBool(false); break; case "sound": DebugConsole.ThrowError("Error in StatusEffect " + element.Parent.Name.ToString() + " - sounds should be defined as child elements of the StatusEffect, not as attributes."); break; default: propertyAttributes.Add(attribute); break; } } int count = propertyAttributes.Count; propertyNames = new string[count]; propertyEffects = new object[count]; int n = 0; foreach (XAttribute attribute in propertyAttributes) { propertyNames[n] = attribute.Name.ToString().ToLowerInvariant(); propertyEffects[n] = XMLExtensions.GetAttributeObject(attribute); n++; } foreach (XElement subElement in element.Elements()) { switch (subElement.Name.ToString().ToLowerInvariant()) { case "explosion": explosion = new Explosion(subElement); break; case "fire": FireSize = subElement.GetAttributeFloat("size", 10.0f); break; case "use": case "useitem": useItemCount++; break; case "remove": case "removeitem": removeItem = true; break; case "requireditem": case "requireditems": RelatedItem newRequiredItem = RelatedItem.Load(subElement); if (newRequiredItem == null) { continue; } requiredItems.Add(newRequiredItem); break; case "conditional": IEnumerable <XAttribute> conditionalAttributes = subElement.Attributes(); foreach (XAttribute attribute in conditionalAttributes) { propertyConditionals.Add(new PropertyConditional(attribute)); } break; #if CLIENT case "particleemitter": particleEmitters.Add(new ParticleEmitter(subElement)); break; case "sound": sound = Sound.Load(subElement); loopSound = subElement.GetAttributeBool("loop", false); break; #endif } } }
public void Update(float deltaTime) { if (ParentTrigger != null && !ParentTrigger.IsTriggered) { return; } triggerers.RemoveWhere(t => t.Removed); if (PhysicsBody != null) { //failsafe to ensure triggerers get removed when they're far from the trigger float maxExtent = Math.Max(ConvertUnits.ToDisplayUnits(PhysicsBody.GetMaxExtent() * 5), 5000.0f); triggerers.RemoveWhere(t => { return(Vector2.Distance(t.WorldPosition, WorldPosition) > maxExtent); }); } bool isNotClient = true; #if CLIENT isNotClient = GameMain.Client == null; #endif if (!UseNetworkSyncing || isNotClient) { if (ForceFluctuationStrength > 0.0f) { //no need for force fluctuation (or network updates) if the trigger limits velocity and there are no triggerers if (forceMode != TriggerForceMode.LimitVelocity || triggerers.Any()) { forceFluctuationTimer += deltaTime; if (forceFluctuationTimer > ForceFluctuationInterval) { NeedsNetworkSyncing = true; currentForceFluctuation = Rand.Range(1.0f - ForceFluctuationStrength, 1.0f); forceFluctuationTimer = 0.0f; } } } if (randomTriggerProbability > 0.0f) { randomTriggerTimer += deltaTime; if (randomTriggerTimer > randomTriggerInterval) { if (Rand.Range(0.0f, 1.0f) < randomTriggerProbability) { NeedsNetworkSyncing = true; triggeredTimer = stayTriggeredDelay; } randomTriggerTimer = 0.0f; } } } if (stayTriggeredDelay > 0.0f) { if (triggerers.Count == 0) { triggeredTimer -= deltaTime; } else { triggeredTimer = stayTriggeredDelay; } } if (triggerOnce) { if (triggeredOnce) { return; } if (triggerers.Count > 0) { triggeredOnce = true; } } foreach (Entity triggerer in triggerers) { foreach (StatusEffect effect in statusEffects) { Vector2?position = null; if (effect.HasTargetType(StatusEffect.TargetType.This)) { position = WorldPosition; } if (triggerer is Character character) { effect.Apply(effect.type, deltaTime, triggerer, character, position); if (effect.HasTargetType(StatusEffect.TargetType.Contained) && character.Inventory != null) { foreach (Item item in character.Inventory.AllItemsMod) { if (item.ContainedItems == null) { continue; } foreach (Item containedItem in item.ContainedItems) { effect.Apply(effect.type, deltaTime, triggerer, containedItem.AllPropertyObjects, position); } } } } else if (triggerer is Item item) { effect.Apply(effect.type, deltaTime, triggerer, item.AllPropertyObjects, position); } if (effect.HasTargetType(StatusEffect.TargetType.NearbyItems) || effect.HasTargetType(StatusEffect.TargetType.NearbyCharacters)) { var targets = new List <ISerializableEntity>(); effect.GetNearbyTargets(worldPosition, targets); effect.Apply(effect.type, deltaTime, triggerer, targets); } } if (triggerer is IDamageable damageable) { foreach (Attack attack in attacks) { attack.DoDamage(null, damageable, WorldPosition, deltaTime, false); } } else if (triggerer is Submarine submarine) { foreach (Attack attack in attacks) { float structureDamage = attack.GetStructureDamage(deltaTime); if (structureDamage > 0.0f) { Explosion.RangedStructureDamage(worldPosition, attack.DamageRange, structureDamage, levelWallDamage: 0.0f); } } if (!string.IsNullOrWhiteSpace(InfectIdentifier)) { submarine.AttemptBallastFloraInfection(InfectIdentifier, deltaTime, InfectionChance); } } if (Force.LengthSquared() > 0.01f) { if (triggerer is Character character) { ApplyForce(character.AnimController.Collider, deltaTime); foreach (Limb limb in character.AnimController.Limbs) { if (limb.IsSevered) { continue; } ApplyForce(limb.body, deltaTime); } } else if (triggerer is Submarine submarine) { ApplyForce(submarine.SubBody.Body, deltaTime); } } if (triggerer == Character.Controlled || triggerer == Character.Controlled?.Submarine) { GameMain.GameScreen.Cam.Shake = Math.Max(GameMain.GameScreen.Cam.Shake, cameraShake); } } }
private void ApplyImpact(float impact, Vector2 direction, Contact contact, bool applyDamage = true) { float minImpact = 3.0f; if (impact < minImpact) { return; } contact.GetWorldManifold(out Vector2 tempNormal, out FixedArray2 <Vector2> worldPoints); Vector2 lastContactPoint = worldPoints[0]; Vector2 impulse = direction * impact * 0.5f; impulse = impulse.ClampLength(5.0f); if (!MathUtils.IsValid(impulse)) { string errorMsg = "Invalid impulse in SubmarineBody.ApplyImpact: " + impulse + ". Direction: " + direction + ", body position: " + Body.SimPosition + ", impact: " + impact + "."; if (GameMain.NetworkMember != null) { errorMsg += GameMain.NetworkMember.IsClient ? " Playing as a client." : " Hosting a server."; } if (GameSettings.VerboseLogging) { DebugConsole.ThrowError(errorMsg); } GameAnalyticsManager.AddErrorEventOnce( "SubmarineBody.ApplyImpact:InvalidImpulse", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); return; } #if CLIENT if (Character.Controlled != null && Character.Controlled.Submarine == submarine) { GameMain.GameScreen.Cam.Shake = impact * 2.0f; float angularVelocity = (lastContactPoint.X - Body.SimPosition.X) / ConvertUnits.ToSimUnits(submarine.Borders.Width / 2) * impulse.Y - (lastContactPoint.Y - Body.SimPosition.Y) / ConvertUnits.ToSimUnits(submarine.Borders.Height / 2) * impulse.X; GameMain.GameScreen.Cam.AngularVelocity = MathHelper.Clamp(angularVelocity * 0.1f, -1.0f, 1.0f); } #endif foreach (Character c in Character.CharacterList) { if (c.Submarine != submarine) { continue; } if (impact > 2.0f) { c.SetStun((impact - 2.0f) * 0.1f); } foreach (Limb limb in c.AnimController.Limbs) { limb.body.ApplyLinearImpulse(limb.Mass * impulse, 20.0f); } c.AnimController.Collider.ApplyLinearImpulse(c.AnimController.Collider.Mass * impulse, 20.0f); } foreach (Item item in Item.ItemList) { if (item.Submarine != submarine || item.CurrentHull == null || item.body == null || !item.body.Enabled) { continue; } item.body.ApplyLinearImpulse(item.body.Mass * impulse, 20.0f); } var damagedStructures = Explosion.RangedStructureDamage( ConvertUnits.ToDisplayUnits(lastContactPoint), impact * 50.0f, applyDamage ? impact * ImpactDamageMultiplier : 0.0f); #if CLIENT //play a damage sound for the structure that took the most damage float maxDamage = 0.0f; Structure maxDamageStructure = null; foreach (KeyValuePair <Structure, float> structureDamage in damagedStructures) { if (maxDamageStructure == null || structureDamage.Value > maxDamage) { maxDamage = structureDamage.Value; maxDamageStructure = structureDamage.Key; } } if (maxDamageStructure != null) { SoundPlayer.PlayDamageSound( "StructureBlunt", impact * 10.0f, ConvertUnits.ToDisplayUnits(lastContactPoint), MathHelper.Lerp(2000.0f, 10000.0f, (impact - minImpact) / 2.0f), maxDamageStructure.Tags); } #endif }