public Battlefield() { _instance = this; //InitBattleField(new MapObject(){ w = 3, h = 3 }); su = new Submarine(); sh = new Ship(); }
public Ship(int field_w, int field_h) { InitShip(field_w, field_h); bf = Battlefield.Instance; su = Submarine.Instance; _instance = this; }
public Ship() { //InitShip(_possiblePositions); bf = Battlefield.Instance; su = Submarine.Instance; history = new List<List<Point>> (); _instance = this; }
protected void Init() { bf = BattlefieldScript.Instance; //bfc = bf.bf; su = Submarine.Instance; sh = Ship.Instance; //la = LanguageScript.Instance; mo = move.Instance; la = LanguageScript.Instance; }
void Start() { _instance = this; bf = BattlefieldScript.Instance; //bfc = bf.bf; su = Submarine.Instance; sh = Ship.Instance; //la = LanguageScript.Instance; mo = move.Instance; reward = new Reward (false, 2); }
void Start() { Debug.Log ("!!! MULTIPLAYER INITIATED !!!"); _instance = this; bf = BattlefieldScript.Instance; bfc = bf.bf; su = Submarine.Instance; sh = Ship.Instance; la = LanguageScript.Instance; mo = move.Instance; reward = new Reward (false, 5); }
// Use this for initialization void Start () { _instance = this; bf = new Battlefield(); bf = Battlefield.Instance; su = Submarine.Instance; sh = Ship.Instance; InitField(); transform.FindChild("autoMove_btn").GetComponent<Button>().onClick.AddListener(DoAutoMove); transform.FindChild("shMove_btn").GetComponent<Button>().onClick.AddListener(DoShipMove); transform.FindChild("suMove_btn").GetComponent<Button>().onClick.AddListener(DoSubmarineMove); }
public void InitSubmarine() { List<Point> su_pp = new List<Point> (); foreach (Point p in MapObject.Su_possiblePositions) { su_pp.Add (p); //Debug.Log ("point addd"); } if (Submarine.Instance == null) { su = new Submarine(); su = Submarine.Instance; } else { su.InitSubmarine(su_pp); } }
public void RenderLightMap(GraphicsDevice graphics, SpriteBatch spriteBatch, Camera cam, RenderTarget2D backgroundObstructor = null) { if (!LightingEnabled) { return; } if (Math.Abs(currLightMapScale - GameMain.Config.LightMapScale) > 0.01f) { //lightmap scale has changed -> recreate render targets CreateRenderTargets(graphics); } Matrix spriteBatchTransform = cam.Transform * Matrix.CreateScale(new Vector3(GameMain.Config.LightMapScale, GameMain.Config.LightMapScale, 1.0f)); Matrix transform = cam.ShaderTransform * Matrix.CreateOrthographic(GameMain.GraphicsWidth, GameMain.GraphicsHeight, -1, 1) * 0.5f; bool highlightsVisible = UpdateHighlights(graphics, spriteBatch, spriteBatchTransform, cam); Rectangle viewRect = cam.WorldView; viewRect.Y -= cam.WorldView.Height; //check which lights need to be drawn activeLights.Clear(); foreach (LightSource light in lights) { if (!light.Enabled) { continue; } if ((light.Color.A < 1 || light.Range < 1.0f) && !light.LightSourceParams.OverrideLightSpriteAlpha.HasValue) { continue; } if (light.ParentBody != null) { light.Position = light.ParentBody.DrawPosition; if (light.ParentSub != null) { light.Position -= light.ParentSub.DrawPosition; } } float range = light.LightSourceParams.TextureRange; if (light.LightSprite != null) { float spriteRange = Math.Max( light.LightSprite.size.X * light.SpriteScale.X * (0.5f + Math.Abs(light.LightSprite.RelativeOrigin.X - 0.5f)), light.LightSprite.size.Y * light.SpriteScale.Y * (0.5f + Math.Abs(light.LightSprite.RelativeOrigin.Y - 0.5f))); range = Math.Max(spriteRange, range); } if (!MathUtils.CircleIntersectsRectangle(light.WorldPosition, range, viewRect)) { continue; } activeLights.Add(light); } //draw light sprites attached to characters //render into a separate rendertarget using alpha blending (instead of on top of everything else with alpha blending) //to prevent the lights from showing through other characters or other light sprites attached to the same character //--------------------------------------------------------------------------------------------------- graphics.SetRenderTarget(LimbLightMap); graphics.Clear(Color.Black); graphics.BlendState = BlendState.NonPremultiplied; spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied, transformMatrix: spriteBatchTransform); foreach (LightSource light in activeLights) { if (light.IsBackground || light.CurrentBrightness <= 0.0f) { continue; } //draw limb lights at this point, because they were skipped over previously to prevent them from being obstructed if (light.ParentBody?.UserData is Limb limb && !limb.Hide) { light.DrawSprite(spriteBatch, cam); } } spriteBatch.End(); //draw background lights //--------------------------------------------------------------------------------------------------- graphics.SetRenderTarget(LightMap); graphics.Clear(AmbientLight); graphics.BlendState = BlendState.Additive; spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, transformMatrix: spriteBatchTransform); Level.Loaded?.BackgroundCreatureManager?.DrawLights(spriteBatch, cam); foreach (LightSource light in activeLights) { if (!light.IsBackground || light.CurrentBrightness <= 0.0f) { continue; } light.DrawSprite(spriteBatch, cam); light.DrawLightVolume(spriteBatch, lightEffect, transform); } GameMain.ParticleManager.Draw(spriteBatch, true, null, Particles.ParticleBlendState.Additive); spriteBatch.End(); //draw a black rectangle on hulls to hide background lights behind subs //--------------------------------------------------------------------------------------------------- if (backgroundObstructor != null) { spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied); spriteBatch.Draw(backgroundObstructor, new Rectangle(0, 0, (int)(GameMain.GraphicsWidth * currLightMapScale), (int)(GameMain.GraphicsHeight * currLightMapScale)), Color.Black); spriteBatch.End(); } spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Opaque, transformMatrix: spriteBatchTransform); Dictionary <Hull, Rectangle> visibleHulls = GetVisibleHulls(cam); foreach (KeyValuePair <Hull, Rectangle> hull in visibleHulls) { GUI.DrawRectangle(spriteBatch, new Vector2(hull.Value.X, -hull.Value.Y), new Vector2(hull.Value.Width, hull.Value.Height), hull.Key.AmbientLight == Color.TransparentBlack ? Color.Black : hull.Key.AmbientLight.Multiply(hull.Key.AmbientLight.A / 255.0f), true); } spriteBatch.End(); SolidColorEffect.CurrentTechnique = SolidColorEffect.Techniques["SolidColor"]; SolidColorEffect.Parameters["color"].SetValue(AmbientLight.ToVector4()); spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, transformMatrix: spriteBatchTransform, effect: SolidColorEffect); Submarine.DrawDamageable(spriteBatch, null); spriteBatch.End(); graphics.BlendState = BlendState.Additive; //draw the focused item and character to highlight them, //and light sprites (done before drawing the actual light volumes so we can make characters obstruct the highlights and sprites) //--------------------------------------------------------------------------------------------------- spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, transformMatrix: spriteBatchTransform); foreach (LightSource light in activeLights) { //don't draw limb lights at this point, they need to be drawn after lights have been obstructed by characters if (light.IsBackground || light.ParentBody?.UserData is Limb || light.CurrentBrightness <= 0.0f) { continue; } light.DrawSprite(spriteBatch, cam); } spriteBatch.End(); if (highlightsVisible) { spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive); spriteBatch.Draw(HighlightMap, Vector2.Zero, Color.White); spriteBatch.End(); } //draw characters to obstruct the highlighted items/characters and light sprites //--------------------------------------------------------------------------------------------------- SolidColorEffect.CurrentTechnique = SolidColorEffect.Techniques["SolidVertexColor"]; spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, effect: SolidColorEffect, transformMatrix: spriteBatchTransform); foreach (Character character in Character.CharacterList) { if (character.CurrentHull == null || !character.Enabled || !character.IsVisible) { continue; } if (Character.Controlled?.FocusedCharacter == character) { continue; } Color lightColor = character.CurrentHull.AmbientLight == Color.TransparentBlack ? Color.Black : character.CurrentHull.AmbientLight.Multiply(character.CurrentHull.AmbientLight.A / 255.0f).Opaque(); foreach (Limb limb in character.AnimController.Limbs) { if (limb.DeformSprite != null) { continue; } limb.Draw(spriteBatch, cam, lightColor); } } spriteBatch.End(); DeformableSprite.Effect.CurrentTechnique = DeformableSprite.Effect.Techniques["DeformShaderSolidVertexColor"]; DeformableSprite.Effect.CurrentTechnique.Passes[0].Apply(); spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, transformMatrix: spriteBatchTransform); foreach (Character character in Character.CharacterList) { if (character.CurrentHull == null || !character.Enabled || !character.IsVisible) { continue; } if (Character.Controlled?.FocusedCharacter == character) { continue; } Color lightColor = character.CurrentHull.AmbientLight == Color.TransparentBlack ? Color.Black : character.CurrentHull.AmbientLight.Multiply(character.CurrentHull.AmbientLight.A / 255.0f).Opaque(); foreach (Limb limb in character.AnimController.Limbs) { if (limb.DeformSprite == null) { continue; } limb.Draw(spriteBatch, cam, lightColor); } } spriteBatch.End(); DeformableSprite.Effect.CurrentTechnique = DeformableSprite.Effect.Techniques["DeformShader"]; graphics.BlendState = BlendState.Additive; //draw the actual light volumes, additive particles, hull ambient lights and the halo around the player //--------------------------------------------------------------------------------------------------- spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, transformMatrix: spriteBatchTransform); spriteBatch.Draw(LimbLightMap, new Rectangle(cam.WorldView.X, -cam.WorldView.Y, cam.WorldView.Width, cam.WorldView.Height), Color.White); foreach (ElectricalDischarger discharger in ElectricalDischarger.List) { discharger.DrawElectricity(spriteBatch); } foreach (LightSource light in activeLights) { if (light.IsBackground || light.CurrentBrightness <= 0.0f) { continue; } light.DrawLightVolume(spriteBatch, lightEffect, transform); } lightEffect.World = transform; GameMain.ParticleManager.Draw(spriteBatch, false, null, Particles.ParticleBlendState.Additive); if (Character.Controlled != null) { DrawHalo(Character.Controlled); } else { foreach (Character character in Character.CharacterList) { if (character.Submarine == null || character.IsDead || !character.IsHuman) { continue; } DrawHalo(character); } } void DrawHalo(Character character) { if (character == null || character.Removed) { return; } Vector2 haloDrawPos = character.DrawPosition; haloDrawPos.Y = -haloDrawPos.Y; //ambient light decreases the brightness of the halo (no need for a bright halo if the ambient light is bright enough) float ambientBrightness = (AmbientLight.R + AmbientLight.B + AmbientLight.G) / 255.0f / 3.0f; Color haloColor = Color.White.Multiply(0.3f - ambientBrightness); if (haloColor.A > 0) { float scale = 512.0f / LightSource.LightTexture.Width; spriteBatch.Draw( LightSource.LightTexture, haloDrawPos, null, haloColor, 0.0f, new Vector2(LightSource.LightTexture.Width, LightSource.LightTexture.Height) / 2, scale, SpriteEffects.None, 0.0f); } } spriteBatch.End(); //draw the actual light volumes, additive particles, hull ambient lights and the halo around the player //--------------------------------------------------------------------------------------------------- graphics.SetRenderTarget(null); graphics.BlendState = BlendState.NonPremultiplied; }
public void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth = -1) { if (sections.Count == 0 && !IsActive || Hidden) { Drawable = false; return; } Vector2 drawOffset = Vector2.Zero; Submarine sub = item.Submarine; if (IsActive && sub == null) // currently being rewired, we need to get the sub from the connections in case the wire has been taken outside { if (connections[0] != null && connections[0].Item.Submarine != null) { sub = connections[0].Item.Submarine; } if (connections[1] != null && connections[1].Item.Submarine != null) { sub = connections[1].Item.Submarine; } } if (sub != null) { drawOffset = sub.DrawPosition + sub.HiddenSubPosition; } float depth = item.IsSelected ? 0.0f : wireSprite.Depth + ((item.ID % 100) * 0.00001f); if (item.IsHighlighted) { foreach (WireSection section in sections) { section.Draw(spriteBatch, this, Color.Gold, drawOffset, depth + 0.00001f, 0.7f); } } else if (item.IsSelected) { foreach (WireSection section in sections) { section.Draw(spriteBatch, this, Color.Red, drawOffset, depth + 0.00001f, 0.7f); } } foreach (WireSection section in sections) { section.Draw(spriteBatch, this, item.Color, drawOffset, depth, 0.3f); } if (nodes.Count > 0) { if (!IsActive) { if (connections[0] == null) { DrawHangingWire(spriteBatch, nodes[0] + drawOffset, depth); } if (connections[1] == null) { DrawHangingWire(spriteBatch, nodes.Last() + drawOffset, depth); } } if (IsActive && Vector2.Distance(newNodePos, nodes[nodes.Count - 1]) > nodeDistance) { WireSection.Draw( spriteBatch, this, new Vector2(nodes[nodes.Count - 1].X, nodes[nodes.Count - 1].Y) + drawOffset, new Vector2(newNodePos.X, newNodePos.Y) + drawOffset, item.Color * 0.5f, depth, 0.3f); } } if (!editing || !GameMain.SubEditorScreen.WiringMode) { return; } for (int i = 0; i < nodes.Count; i++) { Vector2 drawPos = nodes[i]; if (item.Submarine != null) { drawPos += item.Submarine.Position + item.Submarine.HiddenSubPosition; } drawPos.Y = -drawPos.Y; if ((highlightedNodeIndex == i && item.IsHighlighted) || (selectedNodeIndex == i && item.IsSelected)) { GUI.DrawRectangle(spriteBatch, drawPos + new Vector2(-10, -10), new Vector2(20, 20), Color.Red, false, 0.0f); } if (item.IsSelected) { GUI.DrawRectangle(spriteBatch, drawPos + new Vector2(-5, -5), new Vector2(10, 10), item.Color, true, 0.0f); } else { GUI.DrawRectangle(spriteBatch, drawPos + new Vector2(-3, -3), new Vector2(6, 6), item.Color, true, 0.0f); } } }
public Submarine(int field_w, int field_h) { x = field_w - 2; y = field_h - 2; _instance = this; }
private void Repair(Vector2 rayStart, Vector2 rayEnd, float deltaTime, Character user, float degreeOfSuccess, List <Body> ignoredBodies) { var collisionCategories = Physics.CollisionWall | Physics.CollisionCharacter | Physics.CollisionItem | Physics.CollisionLevel | Physics.CollisionRepair; //if the item can cut off limbs, activate nearby bodies to allow the raycast to hit them if (statusEffectLists != null && statusEffectLists.ContainsKey(ActionType.OnUse)) { if (statusEffectLists[ActionType.OnUse].Any(s => s.SeverLimbsProbability > 0.0f)) { float rangeSqr = ConvertUnits.ToSimUnits(Range); rangeSqr *= rangeSqr; foreach (Character c in Character.CharacterList) { if (!c.Enabled || !c.AnimController.BodyInRest) { continue; } //do a broad check first if (Math.Abs(c.WorldPosition.X - item.WorldPosition.X) > 1000.0f) { continue; } if (Math.Abs(c.WorldPosition.Y - item.WorldPosition.Y) > 1000.0f) { continue; } foreach (Limb limb in c.AnimController.Limbs) { if (Vector2.DistanceSquared(limb.SimPosition, item.SimPosition) < rangeSqr && Vector2.Dot(rayEnd - rayStart, limb.SimPosition - rayStart) > 0) { c.AnimController.BodyInRest = false; break; } } } } } float lastPickedFraction = 0.0f; if (RepairMultiple) { var bodies = Submarine.PickBodies(rayStart, rayEnd, ignoredBodies, collisionCategories, ignoreSensors: false, customPredicate: (Fixture f) => { if (RepairThroughHoles && f.IsSensor && f.Body?.UserData is Structure) { return(false); } if (f.Body?.UserData as string == "ruinroom") { return(false); } return(true); }, allowInsideFixture: true); lastPickedFraction = Submarine.LastPickedFraction; Type lastHitType = null; hitCharacters.Clear(); foreach (Body body in bodies) { Type bodyType = body.UserData?.GetType(); if (!RepairThroughWalls && bodyType != null && bodyType != lastHitType) { //stop the ray if it already hit a door/wall and is now about to hit some other type of entity if (lastHitType == typeof(Item) || lastHitType == typeof(Structure)) { break; } } Character hitCharacter = null; if (body.UserData is Limb limb) { hitCharacter = limb.character; } else if (body.UserData is Character character) { hitCharacter = character; } //only do damage once to each character even if they ray hit multiple limbs if (hitCharacter != null) { if (hitCharacters.Contains(hitCharacter)) { continue; } hitCharacters.Add(hitCharacter); } if (FixBody(user, deltaTime, degreeOfSuccess, body)) { lastPickedFraction = Submarine.LastPickedBodyDist(body); if (bodyType != null) { lastHitType = bodyType; } } } } else { FixBody(user, deltaTime, degreeOfSuccess, Submarine.PickBody(rayStart, rayEnd, ignoredBodies, collisionCategories, ignoreSensors: false, customPredicate: (Fixture f) => { if (RepairThroughHoles && f.IsSensor && f.Body?.UserData is Structure) { return(false); } if (f.Body?.UserData as string == "ruinroom") { return(false); } if (f.Body?.UserData is Item targetItem) { if (!HitItems) { return(false); } if (HitBrokenDoors) { if (targetItem.GetComponent <Door>() == null && targetItem.Condition <= 0) { return(false); } } else { if (targetItem.Condition <= 0) { return(false); } } } return(f.Body?.UserData != null); }, allowInsideFixture: true)); lastPickedFraction = Submarine.LastPickedFraction; } if (ExtinguishAmount > 0.0f && item.CurrentHull != null) { fireSourcesInRange.Clear(); //step along the ray in 10% intervals, collecting all fire sources in the range for (float x = 0.0f; x <= lastPickedFraction; x += 0.1f) { Vector2 displayPos = ConvertUnits.ToDisplayUnits(rayStart + (rayEnd - rayStart) * x); if (item.CurrentHull.Submarine != null) { displayPos += item.CurrentHull.Submarine.Position; } Hull hull = Hull.FindHull(displayPos, item.CurrentHull); if (hull == null) { continue; } foreach (FireSource fs in hull.FireSources) { if (fs.IsInDamageRange(displayPos, 100.0f) && !fireSourcesInRange.Contains(fs)) { fireSourcesInRange.Add(fs); } } } foreach (FireSource fs in fireSourcesInRange) { fs.Extinguish(deltaTime, ExtinguishAmount); #if SERVER GameMain.Server.KarmaManager.OnExtinguishingFire(user, deltaTime); #endif } } if (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer) { if (Rand.Range(0.0f, 1.0f) < FireProbability * deltaTime) { Vector2 displayPos = ConvertUnits.ToDisplayUnits(rayStart + (rayEnd - rayStart) * lastPickedFraction * 0.9f); if (item.CurrentHull.Submarine != null) { displayPos += item.CurrentHull.Submarine.Position; } new FireSource(displayPos); } } }
public void Dock(DockingPort target) { if (item.Submarine.DockedTo.Contains(target.item.Submarine)) { return; } if (dockingTarget != null) { Undock(); } if (target.item.Submarine == item.Submarine) { DebugConsole.ThrowError("Error - tried to dock a submarine to itself"); dockingTarget = null; return; } #if CLIENT PlaySound(ActionType.OnUse, item.WorldPosition); #endif if (!item.linkedTo.Contains(target.item)) { item.linkedTo.Add(target.item); } if (!target.item.linkedTo.Contains(item)) { target.item.linkedTo.Add(item); } if (!target.item.Submarine.DockedTo.Contains(item.Submarine)) { target.item.Submarine.DockedTo.Add(item.Submarine); } if (!item.Submarine.DockedTo.Contains(target.item.Submarine)) { item.Submarine.DockedTo.Add(target.item.Submarine); } dockingTarget = target; dockingTarget.dockingTarget = this; docked = true; dockingTarget.Docked = true; if (Character.Controlled != null && (Character.Controlled.Submarine == dockingTarget.item.Submarine || Character.Controlled.Submarine == item.Submarine)) { GameMain.GameScreen.Cam.Shake = Vector2.Distance(dockingTarget.item.Submarine.Velocity, item.Submarine.Velocity); } dockingDir = IsHorizontal ? Math.Sign(dockingTarget.item.WorldPosition.X - item.WorldPosition.X) : Math.Sign(dockingTarget.item.WorldPosition.Y - item.WorldPosition.Y); dockingTarget.dockingDir = -dockingDir; foreach (WayPoint wp in WayPoint.WayPointList) { if (wp.Submarine != item.Submarine || wp.SpawnType != SpawnType.Path) { continue; } if (!Submarine.RectContains(item.Rect, wp.Position)) { continue; } foreach (WayPoint wp2 in WayPoint.WayPointList) { if (wp2.Submarine != dockingTarget.item.Submarine || wp2.SpawnType != SpawnType.Path) { continue; } if (!Submarine.RectContains(dockingTarget.item.Rect, wp2.Position)) { continue; } wp.linkedTo.Add(wp2); wp2.linkedTo.Add(wp); } } CreateJoint(false); if (GameMain.Server != null) { item.CreateServerEvent(this); } }
/// <summary> /// Enables a workshop item by moving it to the game folder. /// </summary> public static bool EnableWorkShopItem(Workshop.Item item, bool allowFileOverwrite, out string errorMsg) { if (!item.Installed) { errorMsg = TextManager.Get("WorkshopErrorInstallRequiredToEnable").Replace("[itemname]", item.Title); DebugConsole.NewMessage(errorMsg, Microsoft.Xna.Framework.Color.Red); return(false); } string metaDataFilePath = Path.Combine(item.Directory.FullName, MetadataFileName); ContentPackage contentPackage = new ContentPackage(metaDataFilePath); string newContentPackagePath = GetWorkshopItemContentPackagePath(contentPackage); var allPackageFiles = Directory.GetFiles(item.Directory.FullName, "*", SearchOption.AllDirectories); List <string> nonContentFiles = new List <string>(); foreach (string file in allPackageFiles) { if (file == metaDataFilePath) { continue; } string relativePath = UpdaterUtil.GetRelativePath(file, item.Directory.FullName); string fullPath = Path.GetFullPath(relativePath); if (contentPackage.Files.Any(f => { string fp = Path.GetFullPath(f.Path); return(fp == fullPath); })) { continue; } if (ContentPackage.IsModFilePathAllowed(relativePath)) { nonContentFiles.Add(relativePath); } } if (!allowFileOverwrite) { // TODO: If you create a new mod via the workshop interface and enable it, it will show the error msg, but still allows you to enable the content. if (File.Exists(newContentPackagePath)) { errorMsg = TextManager.Get("WorkshopErrorOverwriteOnEnable") .Replace("[itemname]", item.Title) .Replace("[filename]", newContentPackagePath); DebugConsole.NewMessage(errorMsg, Microsoft.Xna.Framework.Color.Red); return(false); } foreach (ContentFile contentFile in contentPackage.Files) { string sourceFile = Path.Combine(item.Directory.FullName, contentFile.Path); if (File.Exists(sourceFile) && File.Exists(contentFile.Path)) { errorMsg = TextManager.Get("WorkshopErrorOverwriteOnEnable") .Replace("[itemname]", item.Title) .Replace("[filename]", contentFile.Path); DebugConsole.NewMessage(errorMsg, Microsoft.Xna.Framework.Color.Red); return(false); } } } try { foreach (ContentFile contentFile in contentPackage.Files) { string sourceFile = Path.Combine(item.Directory.FullName, contentFile.Path); if (!File.Exists(sourceFile)) { continue; } if (!ContentPackage.IsModFilePathAllowed(contentFile)) { DebugConsole.ThrowError(TextManager.Get("WorkshopErrorIllegalPathOnEnable").Replace("[filename]", contentFile.Path)); continue; } //make sure the destination directory exists Directory.CreateDirectory(Path.GetDirectoryName(contentFile.Path)); File.Copy(sourceFile, contentFile.Path, overwrite: true); } foreach (string nonContentFile in nonContentFiles) { string sourceFile = Path.Combine(item.Directory.FullName, nonContentFile); if (!File.Exists(sourceFile)) { continue; } if (!ContentPackage.IsModFilePathAllowed(nonContentFile)) { DebugConsole.ThrowError(TextManager.Get("WorkshopErrorIllegalPathOnEnable").Replace("[filename]", nonContentFile)); continue; } Directory.CreateDirectory(Path.GetDirectoryName(nonContentFile)); File.Copy(sourceFile, nonContentFile, overwrite: true); } } catch (Exception e) { errorMsg = TextManager.Get("WorkshopErrorEnableFailed").Replace("[itemname]", item.Title) + " " + e.Message; DebugConsole.NewMessage(errorMsg, Microsoft.Xna.Framework.Color.Red); return(false); } var newPackage = new ContentPackage(contentPackage.Path, newContentPackagePath) { SteamWorkshopUrl = item.Url, InstallTime = item.Modified > item.Created ? item.Modified : item.Created }; newPackage.Save(newContentPackagePath); ContentPackage.List.Add(newPackage); if (newPackage.CorePackage) { //if enabling a core package, disable all other core packages GameMain.Config.SelectedContentPackages.RemoveWhere(cp => cp.CorePackage); } GameMain.Config.SelectedContentPackages.Add(newPackage); GameMain.Config.SaveNewPlayerConfig(); if (newPackage.Files.Any(f => f.Type == ContentType.Submarine)) { Submarine.RefreshSavedSubs(); } errorMsg = ""; return(true); }
public override bool Use(float deltaTime, Character character = null) { if (character == null || character.Removed) { return(false); } if ((item.RequireAimToUse && !character.IsKeyDown(InputType.Aim)) || reloadTimer > 0.0f) { return(false); } IsActive = true; reloadTimer = reload; List <Body> limbBodies = new List <Body>(); foreach (Limb l in character.AnimController.Limbs) { limbBodies.Add(l.body.FarseerBody); } float degreeOfFailure = 1.0f - DegreeOfSuccess(character); degreeOfFailure *= degreeOfFailure; if (degreeOfFailure > Rand.Range(0.0f, 1.0f)) { ApplyStatusEffects(ActionType.OnFailure, 1.0f, character); } Projectile projectile = null; var containedItems = item.ContainedItems; if (containedItems == null) { return(true); } foreach (Item item in containedItems) { projectile = item.GetComponent <Projectile>(); if (projectile != null) { break; } } //projectile not found, see if one of the contained items contains projectiles if (projectile == null) { foreach (Item item in containedItems) { projectile = item.GetComponent <Projectile>(); if (projectile != null) { break; } } //projectile not found, see if one of the contained items contains projectiles if (projectile == null) { foreach (Item item in containedItems) { var containedSubItems = item.ContainedItems; if (containedSubItems == null) { continue; } foreach (Item subItem in containedSubItems) { projectile = subItem.GetComponent <Projectile>(); //apply OnUse statuseffects to the container in case it has to react to it somehow //(play a sound, spawn more projectiles, reduce condition...) if (subItem.Condition > 0.0f) { subItem.GetComponent <ItemContainer>()?.Item.ApplyStatusEffects(ActionType.OnUse, deltaTime); } if (projectile != null) { break; } } } } } if (projectile == null) { return(true); } float spread = MathHelper.ToRadians(MathHelper.Lerp(Spread, UnskilledSpread, degreeOfFailure)); float rotation = (item.body.Dir == 1.0f) ? item.body.Rotation : item.body.Rotation - MathHelper.Pi; rotation += spread * Rand.Range(-0.5f, 0.5f); projectile.User = character; //add the limbs of the shooter to the list of bodies to be ignored //so that the player can't shoot himself projectile.IgnoredBodies = new List <Body>(limbBodies); Vector2 projectilePos = item.SimPosition; Vector2 sourcePos = character?.AnimController == null ? item.SimPosition : character.AnimController.AimSourceSimPos; Vector2 barrelPos = TransformedBarrelPos; //make sure there's no obstacles between the base of the weapon (or the shoulder of the character) and the end of the barrel if (Submarine.PickBody(sourcePos, barrelPos, projectile.IgnoredBodies, Physics.CollisionWall | Physics.CollisionLevel | Physics.CollisionItemBlocking) == null) { //no obstacles -> we can spawn the projectile at the barrel projectilePos = barrelPos; } else if ((sourcePos - barrelPos).LengthSquared() > 0.0001f) { //spawn the projectile body.GetMaxExtent() away from the position where the raycast hit the obstacle projectilePos = sourcePos - Vector2.Normalize(barrelPos - projectilePos) * Math.Max(projectile.Item.body.GetMaxExtent(), 0.1f); } projectile.Item.body.ResetDynamics(); projectile.Item.SetTransform(projectilePos, rotation); projectile.Use(deltaTime); if (projectile.Item.Removed) { return(true); } projectile.User = character; projectile.Item.body.ApplyTorque(projectile.Item.body.Mass * degreeOfFailure * Rand.Range(-10.0f, 10.0f)); //set the rotation of the projectile again because dropping the projectile resets the rotation projectile.Item.SetTransform(projectilePos, rotation + (projectile.Item.body.Dir * projectile.LaunchRotationRadians)); //recoil item.body.ApplyLinearImpulse( new Vector2((float)Math.Cos(projectile.Item.body.Rotation), (float)Math.Sin(projectile.Item.body.Rotation)) * item.body.Mass * -50.0f, maxVelocity: NetConfig.MaxPhysicsBodyVelocity); item.RemoveContained(projectile.Item); Rope rope = item.GetComponent <Rope>(); if (rope != null) { rope.Attach(projectile.Item); } return(true); }
private void UpdateAutoPilot(float deltaTime) { if (controlledSub == null) { return; } if (posToMaintain != null) { Vector2 steeringVel = GetSteeringVelocity((Vector2)posToMaintain, 10.0f); TargetVelocity = Vector2.Lerp(TargetVelocity, steeringVel, AutoPilotSteeringLerp); showIceSpireWarning = false; return; } autopilotRayCastTimer -= deltaTime; autopilotRecalculatePathTimer -= deltaTime; if (autopilotRecalculatePathTimer <= 0.0f) { //periodically recalculate the path in case the sub ends up to a position //where it can't keep traversing the initially calculated path UpdatePath(); autopilotRecalculatePathTimer = RecalculatePathInterval; } if (steeringPath == null) { showIceSpireWarning = false; return; } steeringPath.CheckProgress(ConvertUnits.ToSimUnits(controlledSub.WorldPosition), 10.0f); connectedSubUpdateTimer -= deltaTime; if (connectedSubUpdateTimer <= 0.0f) { connectedSubs.Clear(); connectedSubs = controlledSub?.GetConnectedSubs(); connectedSubUpdateTimer = ConnectedSubUpdateInterval; } if (autopilotRayCastTimer <= 0.0f && steeringPath.NextNode != null) { Vector2 diff = ConvertUnits.ToSimUnits(steeringPath.NextNode.Position - controlledSub.WorldPosition); //if the node is close enough, check if it's visible float lengthSqr = diff.LengthSquared(); if (lengthSqr > 0.001f && lengthSqr < AutopilotMinDistToPathNode * AutopilotMinDistToPathNode) { diff = Vector2.Normalize(diff); //check if the next waypoint is visible from all corners of the sub //(i.e. if we can navigate directly towards it or if there's obstacles in the way) bool nextVisible = true; for (int x = -1; x < 2; x += 2) { for (int y = -1; y < 2; y += 2) { Vector2 cornerPos = new Vector2(controlledSub.Borders.Width * x, controlledSub.Borders.Height * y) / 2.0f; cornerPos = ConvertUnits.ToSimUnits(cornerPos * 1.1f + controlledSub.WorldPosition); float dist = Vector2.Distance(cornerPos, steeringPath.NextNode.SimPosition); if (Submarine.PickBody(cornerPos, cornerPos + diff * dist, null, Physics.CollisionLevel) == null) { continue; } nextVisible = false; x = 2; y = 2; } } if (nextVisible) { steeringPath.SkipToNextNode(); } } autopilotRayCastTimer = AutopilotRayCastInterval; } Vector2 newVelocity = Vector2.Zero; if (steeringPath.CurrentNode != null) { newVelocity = GetSteeringVelocity(steeringPath.CurrentNode.WorldPosition, 2.0f); } Vector2 avoidDist = new Vector2( Math.Max(1000.0f * Math.Abs(controlledSub.Velocity.X), controlledSub.Borders.Width * 0.75f), Math.Max(1000.0f * Math.Abs(controlledSub.Velocity.Y), controlledSub.Borders.Height * 0.75f)); float avoidRadius = avoidDist.Length(); float damagingWallAvoidRadius = MathHelper.Clamp(avoidRadius * 1.5f, 5000.0f, 10000.0f); Vector2 newAvoidStrength = Vector2.Zero; debugDrawObstacles.Clear(); //steer away from nearby walls showIceSpireWarning = false; var closeCells = Level.Loaded.GetCells(controlledSub.WorldPosition, 4); foreach (VoronoiCell cell in closeCells) { if (cell.DoesDamage) { foreach (GraphEdge edge in cell.Edges) { Vector2 closestPoint = MathUtils.GetClosestPointOnLineSegment(edge.Point1 + cell.Translation, edge.Point2 + cell.Translation, controlledSub.WorldPosition); Vector2 diff = closestPoint - controlledSub.WorldPosition; float dist = diff.Length() - Math.Max(controlledSub.Borders.Width, controlledSub.Borders.Height) / 2; if (dist > damagingWallAvoidRadius) { continue; } Vector2 normalizedDiff = Vector2.Normalize(diff); float dot = Vector2.Dot(normalizedDiff, controlledSub.Velocity); float avoidStrength = MathHelper.Clamp(MathHelper.Lerp(1.0f, 0.0f, dist / damagingWallAvoidRadius - dot), 0.0f, 1.0f); Vector2 avoid = -normalizedDiff * avoidStrength; newAvoidStrength += avoid; debugDrawObstacles.Add(new ObstacleDebugInfo(edge, edge.Center, 1.0f, avoid, cell.Translation)); if (dot > 0.0f) { showIceSpireWarning = true; } } continue; } foreach (GraphEdge edge in cell.Edges) { if (MathUtils.GetLineIntersection(edge.Point1 + cell.Translation, edge.Point2 + cell.Translation, controlledSub.WorldPosition, cell.Center, out Vector2 intersection)) { Vector2 diff = controlledSub.WorldPosition - intersection; //far enough -> ignore if (Math.Abs(diff.X) > avoidDist.X && Math.Abs(diff.Y) > avoidDist.Y) { debugDrawObstacles.Add(new ObstacleDebugInfo(edge, intersection, 0.0f, Vector2.Zero, Vector2.Zero)); continue; } if (diff.LengthSquared() < 1.0f) { diff = Vector2.UnitY; } Vector2 normalizedDiff = Vector2.Normalize(diff); float dot = controlledSub.Velocity == Vector2.Zero ? 0.0f : Vector2.Dot(controlledSub.Velocity, -normalizedDiff); //not heading towards the wall -> ignore if (dot < 1.0) { debugDrawObstacles.Add(new ObstacleDebugInfo(edge, intersection, dot, Vector2.Zero, cell.Translation)); continue; } Vector2 change = (normalizedDiff * Math.Max((avoidRadius - diff.Length()), 0.0f)) / avoidRadius; if (change.LengthSquared() < 0.001f) { continue; } newAvoidStrength += change * (dot - 1.0f); debugDrawObstacles.Add(new ObstacleDebugInfo(edge, intersection, dot - 1.0f, change * (dot - 1.0f), cell.Translation)); } } } avoidStrength = Vector2.Lerp(avoidStrength, newAvoidStrength, deltaTime * 10.0f); TargetVelocity = Vector2.Lerp(TargetVelocity, newVelocity + avoidStrength * 100.0f, AutoPilotSteeringLerp); //steer away from other subs foreach (Submarine sub in Submarine.Loaded) { if (sub == controlledSub || connectedSubs.Contains(sub)) { continue; } Point sizeSum = controlledSub.Borders.Size + sub.Borders.Size; Vector2 minDist = sizeSum.ToVector2() / 2; Vector2 diff = controlledSub.WorldPosition - sub.WorldPosition; float xDist = Math.Abs(diff.X); float yDist = Math.Abs(diff.Y); Vector2 maxAvoidDistance = minDist * 2; if (xDist > maxAvoidDistance.X || yDist > maxAvoidDistance.Y) { //far enough -> ignore continue; } float dot = controlledSub.Velocity == Vector2.Zero ? 0.0f : Vector2.Dot(Vector2.Normalize(controlledSub.Velocity), -diff); if (dot < 0.0f) { //heading away -> ignore continue; } float distanceFactor = MathHelper.Lerp(0, 1, MathUtils.InverseLerp(maxAvoidDistance.X + maxAvoidDistance.Y, minDist.X + minDist.Y, xDist + yDist)); float velocityFactor = MathHelper.Lerp(0, 1, MathUtils.InverseLerp(0, 3, controlledSub.Velocity.Length())); TargetVelocity += 100 * Vector2.Normalize(diff) * distanceFactor * velocityFactor; } //clamp velocity magnitude to 100.0f (Is this required? The X and Y components are clamped in the property setter) float velMagnitude = TargetVelocity.Length(); if (velMagnitude > 100.0f) { TargetVelocity *= 100.0f / velMagnitude; } #if CLIENT HintManager.OnAutoPilotPathUpdated(this); #endif }
public override bool Use(float deltaTime, Character character = null) { if (character == null || character.Removed) { return(false); } if ((item.RequireAimToUse && !character.IsKeyDown(InputType.Aim)) || reloadTimer > 0.0f) { return(false); } IsActive = true; reloadTimer = reload; if (item.AiTarget != null) { item.AiTarget.SoundRange = item.AiTarget.MaxSoundRange; item.AiTarget.SightRange = item.AiTarget.MaxSightRange; } limbBodies.Clear(); foreach (Limb l in character.AnimController.Limbs) { if (l.IsSevered) { continue; } limbBodies.Add(l.body.FarseerBody); } float degreeOfFailure = 1.0f - DegreeOfSuccess(character); degreeOfFailure *= degreeOfFailure; if (degreeOfFailure > Rand.Range(0.0f, 1.0f)) { ApplyStatusEffects(ActionType.OnFailure, 1.0f, character); } for (int i = 0; i < ProjectileCount; i++) { Projectile projectile = FindProjectile(triggerOnUseOnContainers: true); if (projectile == null) { return(true); } float spread = GetSpread(character); float rotation = (item.body.Dir == 1.0f) ? item.body.Rotation : item.body.Rotation - MathHelper.Pi; rotation += spread * Rand.Range(-0.5f, 0.5f); projectile.User = character; //add the limbs of the shooter to the list of bodies to be ignored //so that the player can't shoot himself projectile.IgnoredBodies = new List <Body>(limbBodies); Vector2 projectilePos = item.SimPosition; Vector2 sourcePos = character?.AnimController == null ? item.SimPosition : character.AnimController.AimSourceSimPos; Vector2 barrelPos = TransformedBarrelPos + item.body.SimPosition; //make sure there's no obstacles between the base of the weapon (or the shoulder of the character) and the end of the barrel if (Submarine.PickBody(sourcePos, barrelPos, projectile.IgnoredBodies, Physics.CollisionWall | Physics.CollisionLevel | Physics.CollisionItemBlocking) == null) { //no obstacles -> we can spawn the projectile at the barrel projectilePos = barrelPos; } else if ((sourcePos - barrelPos).LengthSquared() > 0.0001f) { //spawn the projectile body.GetMaxExtent() away from the position where the raycast hit the obstacle projectilePos = sourcePos - Vector2.Normalize(barrelPos - projectilePos) * Math.Max(projectile.Item.body.GetMaxExtent(), 0.1f); } projectile.Item.body.ResetDynamics(); projectile.Item.SetTransform(projectilePos, rotation); projectile.Use(deltaTime); projectile.Item.GetComponent <Rope>()?.Attach(item, projectile.Item); if (projectile.Item.Removed) { continue; } projectile.User = character; projectile.Item.body.ApplyTorque(projectile.Item.body.Mass * degreeOfFailure * Rand.Range(-10.0f, 10.0f)); //set the rotation of the projectile again because dropping the projectile resets the rotation projectile.Item.SetTransform(projectilePos, rotation + (projectile.Item.body.Dir * projectile.LaunchRotationRadians)); item.RemoveContained(projectile.Item); if (i == 0) { //recoil item.body.ApplyLinearImpulse( new Vector2((float)Math.Cos(projectile.Item.body.Rotation), (float)Math.Sin(projectile.Item.body.Rotation)) * item.body.Mass * -50.0f, maxVelocity: NetConfig.MaxPhysicsBodyVelocity); } } LaunchProjSpecific(); return(true); }
public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective) { var projectiles = GetLoadedProjectiles(); if (projectiles.Count == 0 || (projectiles.Count == 1 && objective.Option.ToLowerInvariant() != "fire at will")) { ItemContainer container = null; foreach (MapEntity e in item.linkedTo) { var containerItem = e as Item; if (containerItem == null) { continue; } container = containerItem.GetComponent <ItemContainer>(); if (container != null) { break; } } if (container == null || container.ContainableItems.Count == 0) { return(true); } var containShellObjective = new AIObjectiveContainItem(character, container.ContainableItems[0].Names[0], container); containShellObjective.IgnoreAlreadyContainedItems = true; objective.AddSubObjective(containShellObjective); return(false); } else if (GetAvailablePower() < powerConsumption) { var batteries = item.GetConnectedComponents <PowerContainer>(); float lowestCharge = 0.0f; PowerContainer batteryToLoad = null; foreach (PowerContainer battery in batteries) { if (batteryToLoad == null || battery.Charge < lowestCharge) { batteryToLoad = battery; lowestCharge = battery.Charge; } } if (batteryToLoad == null) { return(true); } if (batteryToLoad.RechargeSpeed < batteryToLoad.MaxRechargeSpeed * 0.4f) { objective.AddSubObjective(new AIObjectiveOperateItem(batteryToLoad, character, "", false)); return(false); } } //enough shells and power Character closestEnemy = null; float closestDist = 3000.0f; foreach (Character enemy in Character.CharacterList) { //ignore humans and characters that are inside the sub if (enemy.IsDead || enemy.SpeciesName == "human" || enemy.AnimController.CurrentHull != null) { continue; } float dist = Vector2.Distance(enemy.WorldPosition, item.WorldPosition); if (dist < closestDist) { closestEnemy = enemy; closestDist = dist; } } if (closestEnemy == null) { return(false); } character.CursorPosition = closestEnemy.WorldPosition; if (item.Submarine != null) { character.CursorPosition -= item.Submarine.Position; } character.SetInput(InputType.Aim, false, true); float enemyAngle = MathUtils.VectorToAngle(closestEnemy.WorldPosition - item.WorldPosition); float turretAngle = -rotation; if (Math.Abs(MathUtils.GetShortestAngle(enemyAngle, turretAngle)) > 0.01f) { return(false); } var pickedBody = Submarine.PickBody(ConvertUnits.ToSimUnits(item.WorldPosition), closestEnemy.SimPosition, null); if (pickedBody != null && !(pickedBody.UserData is Limb)) { return(false); } if (objective.Option.ToLowerInvariant() == "fire at will") { Use(deltaTime, character); } return(false); }
public override void Update(float deltaTime, Camera cam) { if (!searchedConnectedDockingPort) { FindConnectedDockingPort(); } networkUpdateTimer -= deltaTime; if (unsentChanges) { if (networkUpdateTimer <= 0.0f) { #if CLIENT if (GameMain.Client != null) { item.CreateClientEvent(this); correctionTimer = CorrectionDelay; } else #endif #if SERVER if (GameMain.Server != null) { item.CreateServerEvent(this); } #endif networkUpdateTimer = 0.1f; unsentChanges = false; } } controlledSub = item.Submarine; var sonar = item.GetComponent <Sonar>(); if (sonar != null && sonar.UseTransducers) { controlledSub = sonar.ConnectedTransducers.Any() ? sonar.ConnectedTransducers.First().Item.Submarine : null; } currPowerConsumption = powerConsumption; if (Voltage < MinVoltage) { return; } if (user != null && user.Removed) { user = null; } ApplyStatusEffects(ActionType.OnActive, deltaTime, null); float userSkill = 0.0f; if (user != null && controlledSub != null && (user.SelectedConstruction == item || item.linkedTo.Contains(user.SelectedConstruction))) { userSkill = user.GetSkillLevel("helm") / 100.0f; } // override autopilot pathing while the AI rams, and go full speed ahead if (AIRamTimer > 0f) { AIRamTimer -= deltaTime; TargetVelocity = GetSteeringVelocity(AITacticalTarget, 0f); } else if (AutoPilot) { UpdateAutoPilot(deltaTime); float throttle = 1.0f; if (controlledSub != null) { //if the sub is heading in the correct direction, throttle the speed according to the user's skill //if it's e.g. sinking due to extra water, don't throttle, but allow emptying up the ballast completely throttle = MathHelper.Clamp(Vector2.Dot(controlledSub.Velocity, TargetVelocity) / 100.0f, 0.0f, 1.0f); } float maxSpeed = MathHelper.Lerp(AutoPilotMaxSpeed, AIPilotMaxSpeed, userSkill) * 100.0f; TargetVelocity = TargetVelocity.ClampLength(MathHelper.Lerp(100.0f, maxSpeed, throttle)); } else { showIceSpireWarning = false; if (user != null && user.Info != null && user.SelectedConstruction == item && controlledSub != null && controlledSub.Velocity.LengthSquared() > 0.01f) { IncreaseSkillLevel(user, deltaTime); } Vector2 velocityDiff = steeringInput - targetVelocity; if (velocityDiff != Vector2.Zero) { if (steeringAdjustSpeed >= 0.99f) { TargetVelocity = steeringInput; } else { float steeringChange = 1.0f / (1.0f - steeringAdjustSpeed); steeringChange *= steeringChange * 10.0f; TargetVelocity += Vector2.Normalize(velocityDiff) * Math.Min(steeringChange * deltaTime, velocityDiff.Length()); } } } float velX = targetVelocity.X; if (controlledSub != null && controlledSub.FlippedX) { velX *= -1; } item.SendSignal(new Signal(velX.ToString(CultureInfo.InvariantCulture), sender: user), "velocity_x_out"); float velY = MathHelper.Lerp((neutralBallastLevel * 100 - 50) * 2, -100 * Math.Sign(targetVelocity.Y), Math.Abs(targetVelocity.Y) / 100.0f); item.SendSignal(new Signal(velY.ToString(CultureInfo.InvariantCulture), sender: user), "velocity_y_out"); // if our tactical AI pilot has left, revert back to maintaining position if (navigateTactically && (user == null || user.SelectedConstruction != item)) { navigateTactically = false; AIRamTimer = 0f; SetMaintainPosition(); } }
private void Awake() { _submarine = GetComponentInParent <Submarine>(); _particleSystem = GetComponent <ParticleSystem>(); }
public RespawnManager(NetworkMember networkMember, SubmarineInfo shuttleInfo) : base(null) { this.networkMember = networkMember; if (shuttleInfo != null) { RespawnShuttle = new Submarine(shuttleInfo, true); RespawnShuttle.PhysicsBody.FarseerBody.OnCollision += OnShuttleCollision; //prevent wifi components from communicating between the respawn shuttle and other subs List <WifiComponent> wifiComponents = new List <WifiComponent>(); foreach (Item item in Item.ItemList) { if (item.Submarine == RespawnShuttle) { wifiComponents.AddRange(item.GetComponents <WifiComponent>()); } } foreach (WifiComponent wifiComponent in wifiComponents) { wifiComponent.TeamID = Character.TeamType.FriendlyNPC; } ResetShuttle(); shuttleDoors = new List <Door>(); foreach (Item item in Item.ItemList) { if (item.Submarine != RespawnShuttle) { continue; } var steering = item.GetComponent <Steering>(); if (steering != null) { shuttleSteering = steering; } var door = item.GetComponent <Door>(); if (door != null) { shuttleDoors.Add(door); } //lock all wires to prevent the players from messing up the electronics var connectionPanel = item.GetComponent <ConnectionPanel>(); if (connectionPanel != null) { foreach (Connection connection in connectionPanel.Connections) { foreach (Wire wire in connection.Wires) { if (wire != null) { wire.Locked = true; } } } } } } else { RespawnShuttle = null; } #if SERVER if (networkMember is GameServer server) { maxTransportTime = server.ServerSettings.MaxTransportTime; } #endif }
private bool LoadElemProjSpecific(XElement subElement) { switch (subElement.Name.ToString().ToLowerInvariant()) { case "guiframe": if (subElement.Attribute("rect") != null) { DebugConsole.ThrowError("Error in item config \"" + item.ConfigFile + "\" - GUIFrame defined as rect, use RectTransform instead."); break; } GuiFrameSource = subElement; ReloadGuiFrame(); break; case "alternativelayout": AlternativeLayout = GUILayoutSettings.Load(subElement); break; case "itemsound": case "sound": string filePath = subElement.GetAttributeString("file", ""); if (filePath == "") { filePath = subElement.GetAttributeString("sound", ""); } if (filePath == "") { DebugConsole.ThrowError("Error when instantiating item \"" + item.Name + "\" - sound with no file path set"); break; } if (!filePath.Contains("/") && !filePath.Contains("\\") && !filePath.Contains(Path.DirectorySeparatorChar)) { filePath = Path.Combine(Path.GetDirectoryName(item.Prefab.FilePath), filePath); } ActionType type; try { type = (ActionType)Enum.Parse(typeof(ActionType), subElement.GetAttributeString("type", ""), true); } catch (Exception e) { DebugConsole.ThrowError("Invalid sound type in " + subElement + "!", e); break; } RoundSound sound = Submarine.LoadRoundSound(subElement); if (sound == null) { break; } ItemSound itemSound = new ItemSound(sound, type, subElement.GetAttributeBool("loop", false)) { VolumeProperty = subElement.GetAttributeString("volumeproperty", "").ToLowerInvariant() }; if (soundSelectionModes == null) { soundSelectionModes = new Dictionary <ActionType, SoundSelectionMode>(); } if (!soundSelectionModes.ContainsKey(type) || soundSelectionModes[type] == SoundSelectionMode.Random) { SoundSelectionMode selectionMode = SoundSelectionMode.Random; Enum.TryParse(subElement.GetAttributeString("selectionmode", "Random"), out selectionMode); soundSelectionModes[type] = selectionMode; } List <ItemSound> soundList = null; if (!sounds.TryGetValue(itemSound.Type, out soundList)) { soundList = new List <ItemSound>(); sounds.Add(itemSound.Type, soundList); hasSoundsOfType[(int)itemSound.Type] = true; } soundList.Add(itemSound); break; default: return(false); //unknown element } return(true); //element processed }
private Item?ScanForTargets(VineTile branch) { Hull parent = Behavior.Parent; Vector2 worldPos = Behavior.GetWorldPosition() + branch.Position; Vector2 pos = parent.Position + Behavior.Offset + branch.Position; Vector2 diameter = ConvertUnits.ToSimUnits(new Vector2(branch.Rect.Width / 2f, branch.Rect.Height / 2f)); Vector2 topLeft = ConvertUnits.ToSimUnits(pos) - diameter; Vector2 bottomRight = ConvertUnits.ToSimUnits(pos) + diameter; int highestPriority = 0; Item?currentItem = null; foreach (Item item in Item.ItemList.Where(it => !Behavior.ClaimedTargets.Contains(it))) { if (Behavior.IgnoredTargets.ContainsKey(item)) { continue; } int priority = 0; foreach (BallastFloraBehavior.AITarget target in Behavior.Targets) { if (!target.Matches(item) || target.Priority <= highestPriority) { continue; } priority = target.Priority; break; } if (priority == 0) { continue; } if (item.Submarine != parent.Submarine || Vector2.Distance(worldPos, item.WorldPosition) > Behavior.Sight) { continue; } Vector2 itemSimPos = ConvertUnits.ToSimUnits(item.Position); #if DEBUG Tuple <Vector2, Vector2> debugLine1 = Tuple.Create(parent.Position - ConvertUnits.ToDisplayUnits(topLeft), parent.Position - ConvertUnits.ToDisplayUnits(itemSimPos - diameter)); Tuple <Vector2, Vector2> debugLine2 = Tuple.Create(parent.Position - ConvertUnits.ToDisplayUnits(bottomRight), parent.Position - ConvertUnits.ToDisplayUnits(itemSimPos + diameter)); Behavior.debugSearchLines.Add(debugLine2); Behavior.debugSearchLines.Add(debugLine1); #endif Body?body1 = Submarine.CheckVisibility(itemSimPos - diameter, topLeft); if (Blocks(body1, item)) { continue; } Body?body2 = Submarine.CheckVisibility(itemSimPos + diameter, bottomRight); if (Blocks(body2, item)) { continue; } highestPriority = priority; currentItem = item; } if (currentItem != null) { foreach (BallastFloraBranch existingBranch in Behavior.Branches) { if (Behavior.BranchContainsTarget(existingBranch, currentItem)) { Behavior.ClaimTarget(currentItem, existingBranch); return(null); } } return(currentItem); } return(null);
private void Repair(Vector2 rayStart, Vector2 rayEnd, float deltaTime, Character user, float degreeOfSuccess, List <Body> ignoredBodies) { var collisionCategories = Physics.CollisionWall | Physics.CollisionCharacter | Physics.CollisionItem | Physics.CollisionLevel | Physics.CollisionRepair; float lastPickedFraction = 0.0f; if (RepairMultiple) { var bodies = Submarine.PickBodies(rayStart, rayEnd, ignoredBodies, collisionCategories, ignoreSensors: RepairThroughHoles, allowInsideFixture: true); lastPickedFraction = Submarine.LastPickedFraction; Type lastHitType = null; hitCharacters.Clear(); foreach (Body body in bodies) { Type bodyType = body.UserData?.GetType(); if (!RepairThroughWalls && bodyType != null && bodyType != lastHitType) { //stop the ray if it already hit a door/wall and is now about to hit some other type of entity if (lastHitType == typeof(Item) || lastHitType == typeof(Structure)) { break; } } Character hitCharacter = null; if (body.UserData is Limb limb) { hitCharacter = limb.character; } else if (body.UserData is Character character) { hitCharacter = character; } //only do damage once to each character even if they ray hit multiple limbs if (hitCharacter != null) { if (hitCharacters.Contains(hitCharacter)) { continue; } hitCharacters.Add(hitCharacter); } if (FixBody(user, deltaTime, degreeOfSuccess, body)) { lastPickedFraction = Submarine.LastPickedBodyDist(body); if (bodyType != null) { lastHitType = bodyType; } } } } else { FixBody(user, deltaTime, degreeOfSuccess, Submarine.PickBody(rayStart, rayEnd, ignoredBodies, collisionCategories, ignoreSensors: RepairThroughHoles, customPredicate: (Fixture f) => { return(f?.Body?.UserData != null); }, allowInsideFixture: true)); lastPickedFraction = Submarine.LastPickedFraction; } if (ExtinguishAmount > 0.0f && item.CurrentHull != null) { fireSourcesInRange.Clear(); //step along the ray in 10% intervals, collecting all fire sources in the range for (float x = 0.0f; x <= lastPickedFraction; x += 0.1f) { Vector2 displayPos = ConvertUnits.ToDisplayUnits(rayStart + (rayEnd - rayStart) * x); if (item.CurrentHull.Submarine != null) { displayPos += item.CurrentHull.Submarine.Position; } Hull hull = Hull.FindHull(displayPos, item.CurrentHull); if (hull == null) { continue; } foreach (FireSource fs in hull.FireSources) { if (fs.IsInDamageRange(displayPos, 100.0f) && !fireSourcesInRange.Contains(fs)) { fireSourcesInRange.Add(fs); } } } foreach (FireSource fs in fireSourcesInRange) { fs.Extinguish(deltaTime, ExtinguishAmount); #if SERVER GameMain.Server.KarmaManager.OnExtinguishingFire(user, deltaTime); #endif } } if (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer) { if (Rand.Range(0.0f, 1.0f) < FireProbability * deltaTime) { Vector2 displayPos = ConvertUnits.ToDisplayUnits(rayStart + (rayEnd - rayStart) * lastPickedFraction * 0.9f); if (item.CurrentHull.Submarine != null) { displayPos += item.CurrentHull.Submarine.Position; } new FireSource(displayPos); } } }
private void Repair(Vector2 rayStart, Vector2 rayEnd, float deltaTime, Character user, float degreeOfSuccess, List <Body> ignoredBodies) { if (ExtinquishAmount > 0.0f && item.CurrentHull != null) { Vector2 displayPos = ConvertUnits.ToDisplayUnits(rayStart + (rayEnd - rayStart) * Submarine.LastPickedFraction * 0.9f); displayPos += item.CurrentHull.Submarine.Position; Hull hull = Hull.FindHull(displayPos, item.CurrentHull); if (hull != null) { hull.Extinguish(deltaTime, ExtinquishAmount, displayPos); if (hull != item.CurrentHull) { item.CurrentHull.Extinguish(deltaTime, ExtinquishAmount, displayPos); } } } Body targetBody = Submarine.PickBody(rayStart, rayEnd, ignoredBodies); if (targetBody == null || targetBody.UserData == null) { return; } pickedPosition = Submarine.LastPickedPosition; Structure targetStructure; Limb targetLimb; Item targetItem; if ((targetStructure = (targetBody.UserData as Structure)) != null) { if (!fixableEntities.Contains("structure") && !fixableEntities.Contains(targetStructure.Name)) { return; } if (targetStructure.IsPlatform) { return; } int sectionIndex = targetStructure.FindSectionIndex(ConvertUnits.ToDisplayUnits(pickedPosition)); if (sectionIndex < 0) { return; } #if CLIENT Vector2 progressBarPos = targetStructure.SectionPosition(sectionIndex); if (targetStructure.Submarine != null) { progressBarPos += targetStructure.Submarine.DrawPosition; } var progressBar = user.UpdateHUDProgressBar( targetStructure, progressBarPos, 1.0f - targetStructure.SectionDamage(sectionIndex) / targetStructure.Health, Color.Red, Color.Green); if (progressBar != null) { progressBar.Size = new Vector2(60.0f, 20.0f); } #endif targetStructure.AddDamage(sectionIndex, -StructureFixAmount * degreeOfSuccess); //if the next section is small enough, apply the effect to it as well //(to make it easier to fix a small "left-over" section) for (int i = -1; i < 2; i += 2) { int nextSectionLength = targetStructure.SectionLength(sectionIndex + i); if ((sectionIndex == 1 && i == -1) || (sectionIndex == targetStructure.SectionCount - 2 && i == 1) || (nextSectionLength > 0 && nextSectionLength < Structure.wallSectionSize * 0.3f)) { //targetStructure.HighLightSection(sectionIndex + i); targetStructure.AddDamage(sectionIndex + i, -StructureFixAmount * degreeOfSuccess); } } } else if ((targetLimb = (targetBody.UserData as Limb)) != null) { targetLimb.character.AddDamage(CauseOfDeath.Damage, -LimbFixAmount * degreeOfSuccess, user); } else if ((targetItem = (targetBody.UserData as Item)) != null) { targetItem.IsHighlighted = true; ApplyStatusEffects(ActionType.OnUse, targetItem.AllPropertyObjects, deltaTime); } }
public void ThalamusOperate(WreckAI ai, float deltaTime, bool targetHumans, bool targetOtherCreatures, bool targetSubmarines, bool ignoreDelay) { if (ai == null) { return; } IsActive = true; if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; } if (updatePending) { if (updateTimer < 0.0f) { #if SERVER item.CreateServerEvent(this); #endif prevTargetRotation = targetRotation; updateTimer = 0.25f; } updateTimer -= deltaTime; } if (!ignoreDelay && waitTimer > 0) { waitTimer -= deltaTime; return; } Submarine closestSub = null; float maxDistance = 10000.0f; float shootDistance = AIRange; ISpatialEntity target = null; float closestDist = shootDistance * shootDistance; if (targetHumans || targetOtherCreatures) { foreach (var character in Character.CharacterList) { if (character == null || character.Removed || character.IsDead) { continue; } if (character.Params.Group.Equals(ai.Config.Entity, StringComparison.OrdinalIgnoreCase)) { continue; } bool isHuman = character.IsHuman || character.Params.Group.Equals(CharacterPrefab.HumanSpeciesName, StringComparison.OrdinalIgnoreCase); if (isHuman) { if (!targetHumans) { // Don't target humans if not defined to. continue; } } else if (!targetOtherCreatures) { // Don't target other creatures if not defined to. continue; } float dist = Vector2.DistanceSquared(character.WorldPosition, item.WorldPosition); if (dist > closestDist) { continue; } target = character; closestDist = dist; } } if (targetSubmarines) { if (target == null || target.Submarine != null) { closestDist = maxDistance * maxDistance; foreach (Submarine sub in Submarine.Loaded) { if (sub.Info.Type != SubmarineType.Player) { continue; } float dist = Vector2.DistanceSquared(sub.WorldPosition, item.WorldPosition); if (dist > closestDist) { continue; } closestSub = sub; closestDist = dist; } closestDist = shootDistance * shootDistance; if (closestSub != null) { foreach (var hull in Hull.hullList) { if (!closestSub.IsEntityFoundOnThisSub(hull, true)) { continue; } float dist = Vector2.DistanceSquared(hull.WorldPosition, item.WorldPosition); if (dist > closestDist) { continue; } target = hull; closestDist = dist; } } } } if (!ignoreDelay) { if (target == null) { // Random movement waitTimer = Rand.Value(Rand.RandSync.Unsynced) < 0.98f ? 0f : Rand.Range(5f, 20f); targetRotation = Rand.Range(minRotation, maxRotation); updatePending = true; return; } if (disorderTimer < 0) { // Random disorder disorderTimer = Rand.Range(0f, 3f); waitTimer = Rand.Range(0.25f, 1f); targetRotation = MathUtils.WrapAngleTwoPi(targetRotation += Rand.Range(-1f, 1f)); updatePending = true; return; } else { disorderTimer -= deltaTime; } } if (target == null) { return; } float angle = -MathUtils.VectorToAngle(target.WorldPosition - item.WorldPosition); targetRotation = MathUtils.WrapAngleTwoPi(angle); if (Math.Abs(targetRotation - prevTargetRotation) > 0.1f) { updatePending = true; } if (target is Hull targetHull) { Vector2 barrelDir = new Vector2((float)Math.Cos(rotation), -(float)Math.Sin(rotation)); if (!MathUtils.GetLineRectangleIntersection(item.WorldPosition, item.WorldPosition + barrelDir * AIRange, targetHull.WorldRect, out _)) { return; } } else { float midRotation = (minRotation + maxRotation) / 2.0f; while (midRotation - angle < -MathHelper.Pi) { angle -= MathHelper.TwoPi; } while (midRotation - angle > MathHelper.Pi) { angle += MathHelper.TwoPi; } if (angle < minRotation || angle > maxRotation) { return; } float enemyAngle = MathUtils.VectorToAngle(target.WorldPosition - item.WorldPosition); float turretAngle = -rotation; if (Math.Abs(MathUtils.GetShortestAngle(enemyAngle, turretAngle)) > 0.15f) { return; } } Vector2 start = ConvertUnits.ToSimUnits(item.WorldPosition); Vector2 end = ConvertUnits.ToSimUnits(target.WorldPosition); if (target.Submarine != null) { start -= target.Submarine.SimPosition; end -= target.Submarine.SimPosition; } var collisionCategories = Physics.CollisionWall | Physics.CollisionCharacter | Physics.CollisionItem | Physics.CollisionLevel; var pickedBody = Submarine.PickBody(start, end, null, collisionCategories, allowInsideFixture: true, customPredicate: (Fixture f) => { return(!item.StaticFixtures.Contains(f)); }); if (pickedBody == null) { return; } Character targetCharacter = null; if (pickedBody.UserData is Character c) { targetCharacter = c; } else if (pickedBody.UserData is Limb limb) { targetCharacter = limb.character; } if (targetCharacter != null) { if (targetCharacter.Params.Group.Equals(ai.Config.Entity, StringComparison.OrdinalIgnoreCase)) { // Don't shoot friendly characters return; } } else { if (pickedBody.UserData is ISpatialEntity e) { Submarine sub = e.Submarine; if (sub == null) { return; } if (!targetSubmarines) { return; } if (sub == Item.Submarine) { return; } // Don't shoot non-player submarines, i.e. wrecks or outposts. if (!sub.Info.IsPlayer) { return; } } else { // Hit something else, probably a level wall return; } } TryLaunch(deltaTime, ignorePower: true); }
/// <summary> /// Disables a workshop item by removing the files from the game folder. /// </summary> public static bool DisableWorkShopItem(Workshop.Item item, out string errorMsg) { if (!item.Installed) { errorMsg = "Cannot disable workshop item \"" + item.Title + "\" because it has not been installed."; DebugConsole.NewMessage(errorMsg, Microsoft.Xna.Framework.Color.Red); return(false); } ContentPackage contentPackage = new ContentPackage(Path.Combine(item.Directory.FullName, MetadataFileName)); string installedContentPackagePath = GetWorkshopItemContentPackagePath(contentPackage); var allPackageFiles = Directory.GetFiles(item.Directory.FullName, "*", SearchOption.AllDirectories); List <string> nonContentFiles = new List <string>(); foreach (string file in allPackageFiles) { if (file == MetadataFileName) { continue; } string relativePath = UpdaterUtil.GetRelativePath(file, item.Directory.FullName); string fullPath = Path.GetFullPath(relativePath); if (contentPackage.Files.Any(f => { string fp = Path.GetFullPath(f.Path); return(fp == fullPath); })) { continue; } if (ContentPackage.IsModFilePathAllowed(relativePath)) { nonContentFiles.Add(relativePath); } } if (File.Exists(installedContentPackagePath)) { File.Delete(installedContentPackagePath); } bool wasSub = contentPackage.Files.Any(f => f.Type == ContentType.Submarine); HashSet <string> directories = new HashSet <string>(); try { foreach (ContentFile contentFile in contentPackage.Files) { if (!ContentPackage.IsModFilePathAllowed(contentFile)) { //Workshop items are not allowed to add or modify files in the Content or Data folders; continue; } if (!File.Exists(contentFile.Path)) { continue; } File.Delete(contentFile.Path); directories.Add(Path.GetDirectoryName(contentFile.Path)); } foreach (string nonContentFile in nonContentFiles) { if (!ContentPackage.IsModFilePathAllowed(nonContentFile)) { //Workshop items are not allowed to add or modify files in the Content or Data folders; continue; } if (!File.Exists(nonContentFile)) { continue; } File.Delete(nonContentFile); directories.Add(Path.GetDirectoryName(nonContentFile)); } foreach (string directory in directories) { if (string.IsNullOrWhiteSpace(directory) || !Directory.Exists(directory)) { continue; } if (Directory.GetFiles(directory, "*", SearchOption.AllDirectories).Count() == 0) { Directory.Delete(directory, recursive: true); } } ContentPackage.List.RemoveAll(cp => System.IO.Path.GetFullPath(cp.Path) == System.IO.Path.GetFullPath(installedContentPackagePath)); GameMain.Config.SelectedContentPackages.RemoveWhere(cp => !ContentPackage.List.Contains(cp)); GameMain.Config.SaveNewPlayerConfig(); } catch (Exception e) { errorMsg = "Disabling the workshop item \"" + item.Title + "\" failed. " + e.Message; DebugConsole.NewMessage(errorMsg, Microsoft.Xna.Framework.Color.Red); return(false); } if (wasSub) { Submarine.RefreshSavedSubs(); } errorMsg = ""; return(true); }
public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective) { if (character.AIController.SelectedAiTarget?.Entity is Character previousTarget && previousTarget.IsDead) { character?.Speak(TextManager.Get("DialogTurretTargetDead"), null, 0.0f, "killedtarget" + previousTarget.ID, 30.0f); character.AIController.SelectTarget(null); } if (GetAvailableBatteryPower() < powerConsumption) { var batteries = item.GetConnectedComponents <PowerContainer>(); float lowestCharge = 0.0f; PowerContainer batteryToLoad = null; foreach (PowerContainer battery in batteries) { if (battery.Item.NonInteractable) { continue; } if (batteryToLoad == null || battery.Charge < lowestCharge) { batteryToLoad = battery; lowestCharge = battery.Charge; } } if (batteryToLoad == null) { return(true); } if (batteryToLoad.RechargeSpeed < batteryToLoad.MaxRechargeSpeed * 0.4f) { objective.AddSubObjective(new AIObjectiveOperateItem(batteryToLoad, character, objective.objectiveManager, option: "", requireEquip: false)); return(false); } } int usableProjectileCount = 0; int maxProjectileCount = 0; foreach (MapEntity e in item.linkedTo) { if (item.NonInteractable) { continue; } if (e is Item projectileContainer) { var containedItems = projectileContainer.ContainedItems; if (containedItems != null) { var container = projectileContainer.GetComponent <ItemContainer>(); maxProjectileCount += container.Capacity; int projectiles = containedItems.Count(it => it.Condition > 0.0f); usableProjectileCount += projectiles; } } } if (usableProjectileCount == 0) { ItemContainer container = null; Item containerItem = null; foreach (MapEntity e in item.linkedTo) { containerItem = e as Item; if (containerItem == null) { continue; } if (containerItem.NonInteractable) { continue; } if (character.AIController is HumanAIController aiController && aiController.IgnoredItems.Contains(containerItem)) { continue; } container = containerItem.GetComponent <ItemContainer>(); if (container != null) { break; } } if (container == null || container.ContainableItems.Count == 0) { character.Speak(TextManager.GetWithVariable("DialogCannotLoadTurret", "[itemname]", item.Name, true), null, 0.0f, "cannotloadturret", 30.0f); return(true); } if (objective.SubObjectives.None()) { if (!AIDecontainEmptyItems(character, objective, equip: true, sourceContainer: container)) { return(false); } } if (objective.SubObjectives.None()) { var loadItemsObjective = AIContainItems <Turret>(container, character, objective, usableProjectileCount + 1, equip: true, removeEmpty: true, dropItemOnDeselected: true); if (loadItemsObjective == null) { if (usableProjectileCount == 0) { character.Speak(TextManager.GetWithVariable("DialogCannotLoadTurret", "[itemname]", item.Name, true), null, 0.0f, "cannotloadturret", 30.0f); return(true); } } else { loadItemsObjective.ignoredContainerIdentifiers = new string[] { containerItem.prefab.Identifier }; character.Speak(TextManager.GetWithVariable("DialogLoadTurret", "[itemname]", item.Name, true), null, 0.0f, "loadturret", 30.0f); return(false); } } if (objective.SubObjectives.Any()) { return(false); } } //enough shells and power Character closestEnemy = null; float closestDist = AIRange * AIRange; foreach (Character enemy in Character.CharacterList) { // Ignore dead, friendly, and those that are inside the same sub if (enemy.IsDead || !enemy.Enabled || enemy.Submarine == character.Submarine) { continue; } if (HumanAIController.IsFriendly(character, enemy)) { continue; } float dist = Vector2.DistanceSquared(enemy.WorldPosition, item.WorldPosition); if (dist > closestDist) { continue; } float angle = -MathUtils.VectorToAngle(enemy.WorldPosition - item.WorldPosition); float midRotation = (minRotation + maxRotation) / 2.0f; while (midRotation - angle < -MathHelper.Pi) { angle -= MathHelper.TwoPi; } while (midRotation - angle > MathHelper.Pi) { angle += MathHelper.TwoPi; } if (angle < minRotation || angle > maxRotation) { continue; } closestEnemy = enemy; closestDist = dist; } if (closestEnemy == null) { return(false); } character.AIController.SelectTarget(closestEnemy.AiTarget); character.CursorPosition = closestEnemy.WorldPosition; if (character.Submarine != null) { character.CursorPosition -= character.Submarine.Position; } float enemyAngle = MathUtils.VectorToAngle(closestEnemy.WorldPosition - item.WorldPosition); float turretAngle = -rotation; if (Math.Abs(MathUtils.GetShortestAngle(enemyAngle, turretAngle)) > 0.15f) { return(false); } Vector2 start = ConvertUnits.ToSimUnits(item.WorldPosition); Vector2 end = ConvertUnits.ToSimUnits(closestEnemy.WorldPosition); if (closestEnemy.Submarine != null) { start -= closestEnemy.Submarine.SimPosition; end -= closestEnemy.Submarine.SimPosition; } var collisionCategories = Physics.CollisionWall | Physics.CollisionCharacter | Physics.CollisionItem | Physics.CollisionLevel; var pickedBody = Submarine.PickBody(start, end, null, collisionCategories, allowInsideFixture: true, customPredicate: (Fixture f) => { return(!item.StaticFixtures.Contains(f)); }); if (pickedBody == null) { return(false); } Character targetCharacter = null; if (pickedBody.UserData is Character c) { targetCharacter = c; } else if (pickedBody.UserData is Limb limb) { targetCharacter = limb.character; } if (targetCharacter != null) { if (HumanAIController.IsFriendly(character, targetCharacter)) { // Don't shoot friendly characters return(false); } } else { if (pickedBody.UserData is ISpatialEntity e) { Submarine sub = e.Submarine; if (sub == null) { return(false); } if (sub == Item.Submarine) { return(false); } // Don't shoot non-player submarines, i.e. wrecks or outposts. if (!sub.Info.IsPlayer) { return(false); } // Don't shoot friendly submarines. if (sub.TeamID == Item.Submarine.TeamID) { return(false); } } else { // Hit something else, probably a level wall return(false); } } character?.Speak(TextManager.GetWithVariable("DialogFireTurret", "[itemname]", item.Name, true), null, 0.0f, "fireturret", 5.0f); character.SetInput(InputType.Shoot, true, true); return(false); }
public override bool Use(float deltaTime, Character character = null) { if (character != null) { if (item.RequireAimToUse && !character.IsKeyDown(InputType.Aim)) { return(false); } } float degreeOfSuccess = character == null ? 0.5f : DegreeOfSuccess(character); if (Rand.Range(0.0f, 0.5f) > degreeOfSuccess) { ApplyStatusEffects(ActionType.OnFailure, deltaTime, character); return(false); } if (UsableIn == UseEnvironment.None) { ApplyStatusEffects(ActionType.OnFailure, deltaTime, character); return(false); } if (item.InWater) { if (UsableIn == UseEnvironment.Air) { ApplyStatusEffects(ActionType.OnFailure, deltaTime, character); return(false); } } else { if (UsableIn == UseEnvironment.Water) { ApplyStatusEffects(ActionType.OnFailure, deltaTime, character); return(false); } } Vector2 rayStart; Vector2 sourcePos = character?.AnimController == null ? item.SimPosition : character.AnimController.AimSourceSimPos; Vector2 barrelPos = item.SimPosition + ConvertUnits.ToSimUnits(TransformedBarrelPos); //make sure there's no obstacles between the base of the item (or the shoulder of the character) and the end of the barrel if (Submarine.PickBody(sourcePos, barrelPos, collisionCategory: Physics.CollisionWall | Physics.CollisionLevel | Physics.CollisionItemBlocking) == null) { //no obstacles -> we start the raycast at the end of the barrel rayStart = ConvertUnits.ToSimUnits(item.WorldPosition + TransformedBarrelPos); } else { rayStart = Submarine.LastPickedPosition + Submarine.LastPickedNormal * 0.1f; if (item.Submarine != null) { rayStart += item.Submarine.SimPosition; } } float spread = MathHelper.ToRadians(MathHelper.Lerp(UnskilledSpread, Spread, degreeOfSuccess)); float angle = item.body.Rotation + spread * Rand.Range(-0.5f, 0.5f); Vector2 rayEnd = rayStart + ConvertUnits.ToSimUnits(new Vector2( (float)Math.Cos(angle), (float)Math.Sin(angle)) * Range * item.body.Dir); ignoredBodies.Clear(); if (character != null) { foreach (Limb limb in character.AnimController.Limbs) { if (Rand.Range(0.0f, 0.5f) > degreeOfSuccess) { continue; } ignoredBodies.Add(limb.body.FarseerBody); } ignoredBodies.Add(character.AnimController.Collider.FarseerBody); } IsActive = true; activeTimer = 0.1f; debugRayStartPos = ConvertUnits.ToDisplayUnits(rayStart); debugRayEndPos = ConvertUnits.ToDisplayUnits(rayEnd); Submarine parentSub = character?.Submarine ?? item.Submarine; if (parentSub == null) { foreach (Submarine sub in Submarine.Loaded) { Rectangle subBorders = sub.Borders; subBorders.Location += new Point((int)sub.WorldPosition.X, (int)sub.WorldPosition.Y - sub.Borders.Height); if (!MathUtils.CircleIntersectsRectangle(item.WorldPosition, Range * 5.0f, subBorders)) { continue; } Repair(rayStart - sub.SimPosition, rayEnd - sub.SimPosition, deltaTime, character, degreeOfSuccess, ignoredBodies); } Repair(rayStart, rayEnd, deltaTime, character, degreeOfSuccess, ignoredBodies); } else { Repair(rayStart - parentSub.SimPosition, rayEnd - parentSub.SimPosition, deltaTime, character, degreeOfSuccess, ignoredBodies); } UseProjSpecific(deltaTime, rayStart); return(true); }
public override IEnumerator CounterMeasureEffect(Submarine submarine) { submarine.PickRandomInterrestPoint(); yield return(base.CounterMeasureEffect(submarine)); }
public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective) { if (!(objective.OperateTarget is Gap leak)) { return(true); } if (leak.Submarine == null) { return(true); } Vector2 fromCharacterToLeak = leak.WorldPosition - character.WorldPosition; float dist = fromCharacterToLeak.Length(); float reach = Range + ConvertUnits.ToDisplayUnits(((HumanoidAnimController)character.AnimController).ArmLength); //too far away -> consider this done and hope the AI is smart enough to move closer if (dist > reach * 2) { return(true); } character.AIController.SteeringManager.Reset(); //steer closer if almost in range if (dist > reach) { if (character.AnimController.InWater) { if (character.AIController.SteeringManager is IndoorsSteeringManager indoorSteering) { // Swimming inside the sub if (indoorSteering.CurrentPath != null && !indoorSteering.IsPathDirty && indoorSteering.CurrentPath.Unreachable) { Vector2 dir = Vector2.Normalize(fromCharacterToLeak); character.AIController.SteeringManager.SteeringManual(deltaTime, dir); } else { character.AIController.SteeringManager.SteeringSeek(character.GetRelativeSimPosition(leak)); } } else { // Swimming outside the sub character.AIController.SteeringManager.SteeringSeek(character.GetRelativeSimPosition(leak)); } } else { // TODO: use the collider size? if (!character.AnimController.InWater && character.AnimController is HumanoidAnimController && Math.Abs(fromCharacterToLeak.X) < 100.0f && fromCharacterToLeak.Y < 0.0f && fromCharacterToLeak.Y > -150.0f) { ((HumanoidAnimController)character.AnimController).Crouching = true; } Vector2 standPos = new Vector2(Math.Sign(-fromCharacterToLeak.X), Math.Sign(-fromCharacterToLeak.Y)) / 2; if (leak.IsHorizontal) { standPos.X *= 2; standPos.Y = 0; } else { standPos.X = 0; } character.AIController.SteeringManager.SteeringSeek(standPos); } } else { if (dist < reach / 2) { // Too close -> steer away character.AIController.SteeringManager.SteeringManual(deltaTime, Vector2.Normalize(character.SimPosition - leak.SimPosition)); } else if (dist <= reach) { // In range character.CursorPosition = leak.Position; character.CursorPosition += VectorExtensions.Forward(Item.body.TransformedRotation + (float)Math.Sin(sinTime) / 2, dist / 2); if (character.AnimController.InWater) { var torso = character.AnimController.GetLimb(LimbType.Torso); // Turn facing the target when not moving (handled in the animcontroller if not moving) Vector2 mousePos = ConvertUnits.ToSimUnits(character.CursorPosition); Vector2 diff = (mousePos - torso.SimPosition) * character.AnimController.Dir; float newRotation = MathUtils.VectorToAngle(diff); character.AnimController.Collider.SmoothRotate(newRotation, 5.0f); if (VectorExtensions.Angle(VectorExtensions.Forward(torso.body.TransformedRotation), fromCharacterToLeak) < MathHelper.PiOver4) { // Swim past Vector2 moveDir = leak.IsHorizontal ? Vector2.UnitY : Vector2.UnitX; moveDir *= character.AnimController.Dir; character.AIController.SteeringManager.SteeringManual(deltaTime, moveDir); } } } } if (item.RequireAimToUse) { bool isOperatingButtons = false; if (character.AIController.SteeringManager is IndoorsSteeringManager indoorSteering) { var door = indoorSteering.CurrentPath?.CurrentNode?.ConnectedDoor; if (door != null && !door.IsOpen) { isOperatingButtons = door.HasIntegratedButtons || door.Item.GetConnectedComponents <Controller>(true).Any(); } } if (!isOperatingButtons) { character.SetInput(InputType.Aim, false, true); } sinTime += deltaTime * 5; } // Press the trigger only when the tool is approximately facing the target. Vector2 fromItemToLeak = leak.WorldPosition - item.WorldPosition; var angle = VectorExtensions.Angle(VectorExtensions.Forward(item.body.TransformedRotation), fromItemToLeak); if (angle < MathHelper.PiOver4) { // Check that we don't hit any friendlies if (Submarine.PickBodies(item.SimPosition, leak.SimPosition, collisionCategory: Physics.CollisionCharacter).None(hit => { if (hit.UserData is Character c) { if (c == character) { return(false); } return(HumanAIController.IsFriendly(character, c)); } return(false); })) { character.SetInput(InputType.Shoot, false, true); Use(deltaTime, character); } } bool leakFixed = (leak.Open <= 0.0f || leak.Removed) && (leak.ConnectedWall == null || leak.ConnectedWall.Sections.Average(s => s.damage) < 1); if (leakFixed && leak.FlowTargetHull != null) { if (!leak.FlowTargetHull.ConnectedGaps.Any(g => !g.IsRoomToRoom && g.Open > 0.0f)) { character.Speak(TextManager.GetWithVariable("DialogLeaksFixed", "[roomname]", leak.FlowTargetHull.DisplayName, true), null, 0.0f, "leaksfixed", 10.0f); } else { character.Speak(TextManager.GetWithVariable("DialogLeakFixed", "[roomname]", leak.FlowTargetHull.DisplayName, true), null, 0.0f, "leakfixed", 10.0f); } } return(leakFixed); }
private void InitSubmarine() { su = new Submarine(w, h); su = Submarine.Instance; }
private void CreateHull() { var hullRects = new Rectangle[] { item.WorldRect, dockingTarget.item.WorldRect }; var subs = new Submarine[] { item.Submarine, dockingTarget.item.Submarine }; hulls = new Hull[2]; bodies = new Body[4]; if (dockingTarget.door != null) { CreateDoorBody(); } if (door != null) { dockingTarget.CreateDoorBody(); } if (IsHorizontal) { if (hullRects[0].Center.X > hullRects[1].Center.X) { hullRects = new Rectangle[] { dockingTarget.item.WorldRect, item.WorldRect }; subs = new Submarine[] { dockingTarget.item.Submarine, item.Submarine }; } hullRects[0] = new Rectangle(hullRects[0].Center.X, hullRects[0].Y, ((int)DockedDistance / 2), hullRects[0].Height); hullRects[1] = new Rectangle(hullRects[1].Center.X - ((int)DockedDistance / 2), hullRects[1].Y, ((int)DockedDistance / 2), hullRects[1].Height); for (int i = 0; i < 2; i++) { hullRects[i].Location -= MathUtils.ToPoint((subs[i].WorldPosition - subs[i].HiddenSubPosition)); hulls[i] = new Hull(MapEntityPrefab.list.Find(m => m.Name == "Hull"), hullRects[i], subs[i]); hulls[i].AddToGrid(subs[i]); for (int j = 0; j < 2; j++) { bodies[i + j * 2] = BodyFactory.CreateEdge(GameMain.World, ConvertUnits.ToSimUnits(new Vector2(hullRects[i].X, hullRects[i].Y - hullRects[i].Height * j)), ConvertUnits.ToSimUnits(new Vector2(hullRects[i].Right, hullRects[i].Y - hullRects[i].Height * j))); } } gap = new Gap(new Rectangle(hullRects[0].Right - 2, hullRects[0].Y, 4, hullRects[0].Height), true, subs[0]); if (gapId != null) { gap.ID = (ushort)gapId; } LinkHullsToGap(); } else { if (hullRects[0].Center.Y > hullRects[1].Center.Y) { hullRects = new Rectangle[] { dockingTarget.item.WorldRect, item.WorldRect }; subs = new Submarine[] { dockingTarget.item.Submarine, item.Submarine }; } hullRects[0] = new Rectangle(hullRects[0].X, hullRects[0].Y + (int)(-hullRects[0].Height + DockedDistance) / 2, hullRects[0].Width, ((int)DockedDistance / 2)); hullRects[1] = new Rectangle(hullRects[1].X, hullRects[1].Y - hullRects[1].Height / 2, hullRects[1].Width, ((int)DockedDistance / 2)); for (int i = 0; i < 2; i++) { hullRects[i].Location -= MathUtils.ToPoint((subs[i].WorldPosition - subs[i].HiddenSubPosition)); hulls[i] = new Hull(MapEntityPrefab.list.Find(m => m.Name == "Hull"), hullRects[i], subs[i]); hulls[i].AddToGrid(subs[i]); if (hullIds[i] != null) { hulls[i].ID = (ushort)hullIds[i]; } } gap = new Gap(new Rectangle(hullRects[0].X, hullRects[0].Y + 2, hullRects[0].Width, 4), false, subs[0]); if (gapId != null) { gap.ID = (ushort)gapId; } LinkHullsToGap(); } item.linkedTo.Add(hulls[0]); item.linkedTo.Add(hulls[1]); hullIds[0] = hulls[0].ID; hullIds[1] = hulls[1].ID; gap.DisableHullRechecks = true; gapId = gap.ID; item.linkedTo.Add(gap); foreach (Body body in bodies) { if (body == null) { continue; } body.BodyType = BodyType.Static; body.Friction = 0.5f; body.CollisionCategories = Physics.CollisionWall; } }
public static void UpdateEditing(List <Wire> wires) { //dragging a node of some wire if (draggingWire != null) { //cancel dragging if (!PlayerInput.LeftButtonHeld()) { draggingWire = null; selectedNodeIndex = null; } //update dragging else { MapEntity.DisableSelect = true; Submarine sub = null; if (draggingWire.connections[0] != null && draggingWire.connections[0].Item.Submarine != null) { sub = draggingWire.connections[0].Item.Submarine; } if (draggingWire.connections[1] != null && draggingWire.connections[1].Item.Submarine != null) { sub = draggingWire.connections[1].Item.Submarine; } Vector2 nodeWorldPos = GameMain.SubEditorScreen.Cam.ScreenToWorld(PlayerInput.MousePosition) - sub.HiddenSubPosition - sub.Position;// Nodes[(int)selectedNodeIndex]; nodeWorldPos.X = MathUtils.Round(nodeWorldPos.X, Submarine.GridSize.X / 2.0f); nodeWorldPos.Y = MathUtils.Round(nodeWorldPos.Y, Submarine.GridSize.Y / 2.0f); draggingWire.nodes[(int)selectedNodeIndex] = nodeWorldPos; draggingWire.UpdateSections(); MapEntity.SelectEntity(draggingWire.item); } return; } //a wire has been selected -> check if we should start dragging one of the nodes float nodeSelectDist = 10, sectionSelectDist = 5; highlightedNodeIndex = null; if (MapEntity.SelectedList.Count == 1 && MapEntity.SelectedList[0] is Item) { Wire selectedWire = ((Item)MapEntity.SelectedList[0]).GetComponent <Wire>(); if (selectedWire != null) { Vector2 mousePos = GameMain.SubEditorScreen.Cam.ScreenToWorld(PlayerInput.MousePosition); if (selectedWire.item.Submarine != null) { mousePos -= (selectedWire.item.Submarine.Position + selectedWire.item.Submarine.HiddenSubPosition); } //left click while holding ctrl -> check if the cursor is on a wire section, //and add a new node if it is if (PlayerInput.KeyDown(Keys.RightControl) || PlayerInput.KeyDown(Keys.LeftControl)) { if (PlayerInput.LeftButtonClicked()) { float temp = 0.0f; int closestSectionIndex = selectedWire.GetClosestSectionIndex(mousePos, sectionSelectDist, out temp); if (closestSectionIndex > -1) { selectedWire.nodes.Insert(closestSectionIndex + 1, mousePos); selectedWire.UpdateSections(); } } } else { //check if close enough to a node float temp = 0.0f; int closestIndex = selectedWire.GetClosestNodeIndex(mousePos, nodeSelectDist, out temp); if (closestIndex > -1) { highlightedNodeIndex = closestIndex; //start dragging the node if (PlayerInput.LeftButtonHeld()) { draggingWire = selectedWire; selectedNodeIndex = closestIndex; } //remove the node else if (PlayerInput.RightButtonClicked() && closestIndex > 0 && closestIndex < selectedWire.nodes.Count - 1) { selectedWire.nodes.RemoveAt(closestIndex); selectedWire.UpdateSections(); } } } } } //check which wire is highlighted with the cursor Wire highlighted = null; float closestDist = float.PositiveInfinity; foreach (Wire w in wires) { Vector2 mousePos = GameMain.SubEditorScreen.Cam.ScreenToWorld(PlayerInput.MousePosition); if (w.item.Submarine != null) { mousePos -= (w.item.Submarine.Position + w.item.Submarine.HiddenSubPosition); } float dist = 0.0f; int highlightedNode = w.GetClosestNodeIndex(mousePos, highlighted == null ? nodeSelectDist : closestDist, out dist); if (highlightedNode > -1) { if (dist < closestDist) { highlightedNodeIndex = highlightedNode; highlighted = w; closestDist = dist; } } if (w.GetClosestSectionIndex(mousePos, highlighted == null ? sectionSelectDist : closestDist, out dist) > -1) { //prefer nodes over sections if (dist + nodeSelectDist * 0.5f < closestDist) { highlightedNodeIndex = null; highlighted = w; closestDist = dist + nodeSelectDist * 0.5f; } } } if (highlighted != null) { highlighted.item.IsHighlighted = true; if (PlayerInput.LeftButtonClicked()) { MapEntity.DisableSelect = true; MapEntity.SelectEntity(highlighted.item); } } }
public MainGameScreen(Submarine owner) : base(owner) { }
/// <summary> /// Enables a workshop item by moving it to the game folder. /// </summary> public static bool EnableWorkShopItem(Workshop.Item item, bool allowFileOverwrite, out string errorMsg) { if (!item.Installed) { errorMsg = TextManager.GetWithVariable("WorkshopErrorInstallRequiredToEnable", "[itemname]", item.Title); DebugConsole.NewMessage(errorMsg, Microsoft.Xna.Framework.Color.Red); return(false); } string metaDataFilePath = Path.Combine(item.Directory.FullName, MetadataFileName); ContentPackage contentPackage = new ContentPackage(metaDataFilePath); string newContentPackagePath = GetWorkshopItemContentPackagePath(contentPackage); if (!contentPackage.IsCompatible()) { errorMsg = TextManager.GetWithVariables(contentPackage.GameVersion <= new Version(0, 0, 0, 0) ? "IncompatibleContentPackageUnknownVersion" : "IncompatibleContentPackage", new string[3] { "[packagename]", "[packageversion]", "[gameversion]" }, new string[3] { contentPackage.Name, contentPackage.GameVersion.ToString(), GameMain.Version.ToString() }); return(false); } if (contentPackage.CorePackage && !contentPackage.ContainsRequiredCorePackageFiles(out List <ContentType> missingContentTypes)) { errorMsg = TextManager.GetWithVariables("ContentPackageMissingCoreFiles", new string[2] { "[packagename]", "[missingfiletypes]" }, new string[2] { contentPackage.Name, string.Join(", ", missingContentTypes) }, new bool[2] { false, true }); return(false); } var allPackageFiles = Directory.GetFiles(item.Directory.FullName, "*", SearchOption.AllDirectories); List <string> nonContentFiles = new List <string>(); foreach (string file in allPackageFiles) { if (file == metaDataFilePath) { continue; } string relativePath = UpdaterUtil.GetRelativePath(file, item.Directory.FullName); string fullPath = Path.GetFullPath(relativePath); if (contentPackage.Files.Any(f => { string fp = Path.GetFullPath(f.Path); return(fp == fullPath); })) { continue; } if (ContentPackage.IsModFilePathAllowed(relativePath)) { nonContentFiles.Add(relativePath); } } if (!allowFileOverwrite) { if (File.Exists(newContentPackagePath) && !CheckFileEquality(newContentPackagePath, metaDataFilePath)) { errorMsg = TextManager.GetWithVariables("WorkshopErrorOverwriteOnEnable", new string[2] { "[itemname]", "[filename]" }, new string[2] { item.Title, newContentPackagePath }); DebugConsole.NewMessage(errorMsg, Microsoft.Xna.Framework.Color.Red); return(false); } foreach (ContentFile contentFile in contentPackage.Files) { string sourceFile = Path.Combine(item.Directory.FullName, contentFile.Path); if (File.Exists(sourceFile) && File.Exists(contentFile.Path) && !CheckFileEquality(sourceFile, contentFile.Path)) { errorMsg = TextManager.GetWithVariables("WorkshopErrorOverwriteOnEnable", new string[2] { "[itemname]", "[filename]" }, new string[2] { item.Title, contentFile.Path }); DebugConsole.NewMessage(errorMsg, Microsoft.Xna.Framework.Color.Red); return(false); } } } try { foreach (ContentFile contentFile in contentPackage.Files) { string sourceFile = Path.Combine(item.Directory.FullName, contentFile.Path); //path not allowed -> the content file must be a reference to an external file (such as some vanilla file outside the Mods folder) if (!ContentPackage.IsModFilePathAllowed(contentFile)) { //the content package is trying to copy a file to a prohibited path, which is not allowed if (File.Exists(sourceFile)) { errorMsg = TextManager.GetWithVariable("WorkshopErrorIllegalPathOnEnable", "[filename]", contentFile.Path); return(false); } //not trying to copy anything, so this is a reference to an external file //if the external file doesn't exist, we cannot enable the package else if (!File.Exists(contentFile.Path)) { errorMsg = TextManager.GetWithVariable("WorkshopErrorEnableFailed", "[itemname]", item.Title) + " " + TextManager.GetWithVariable("WorkshopFileNotFound", "[path]", "\"" + contentFile.Path + "\""); return(false); } continue; } else if (!File.Exists(sourceFile)) { if (File.Exists(contentFile.Path)) { //the file is already present in the game folder, all good continue; } else { //file not present in either the mod or the game folder -> cannot enable the package errorMsg = TextManager.GetWithVariable("WorkshopErrorEnableFailed", "[itemname]", item.Title) + " " + TextManager.GetWithVariable("WorkshopFileNotFound", "[path]", "\"" + contentFile.Path + "\""); return(false); } } //make sure the destination directory exists Directory.CreateDirectory(Path.GetDirectoryName(contentFile.Path)); File.Copy(sourceFile, contentFile.Path, overwrite: true); } foreach (string nonContentFile in nonContentFiles) { string sourceFile = Path.Combine(item.Directory.FullName, nonContentFile); if (!File.Exists(sourceFile)) { continue; } if (!ContentPackage.IsModFilePathAllowed(nonContentFile)) { DebugConsole.ThrowError(TextManager.GetWithVariable("WorkshopErrorIllegalPathOnEnable", "[filename]", nonContentFile)); continue; } Directory.CreateDirectory(Path.GetDirectoryName(nonContentFile)); File.Copy(sourceFile, nonContentFile, overwrite: true); } } catch (Exception e) { errorMsg = TextManager.GetWithVariable("WorkshopErrorEnableFailed", "[itemname]", item.Title) + " {" + e.Message + "}"; DebugConsole.NewMessage(errorMsg, Microsoft.Xna.Framework.Color.Red); return(false); } var newPackage = new ContentPackage(contentPackage.Path, newContentPackagePath) { SteamWorkshopUrl = item.Url, InstallTime = item.Modified > item.Created ? item.Modified : item.Created }; newPackage.Save(newContentPackagePath); ContentPackage.List.Add(newPackage); if (newPackage.CorePackage) { //if enabling a core package, disable all other core packages GameMain.Config.SelectedContentPackages.RemoveWhere(cp => cp.CorePackage); } GameMain.Config.SelectedContentPackages.Add(newPackage); GameMain.Config.SaveNewPlayerConfig(); if (newPackage.Files.Any(f => f.Type == ContentType.Submarine)) { Submarine.RefreshSavedSubs(); } errorMsg = ""; return(true); }
public virtual AIAction NextAction(Submarine sub, GameField field) { for (int i = 0; i < 5; i++) { switch (rnd.Next(5)) { case 0: addMarker(new Aim(field.Field[rnd.Next(field.Height), rnd.Next(field.Width)])); break; case 1: addMarker(new Circle(field.Field[rnd.Next(field.Height), rnd.Next(field.Width)])); break; case 2: addMarker(new Check(field.Field[rnd.Next(field.Height), rnd.Next(field.Width)])); break; case 3: addMarker(new Flag(field.Field[rnd.Next(field.Height), rnd.Next(field.Width)])); break; case 4: addMarker(new XMark(field.Field[rnd.Next(field.Height), rnd.Next(field.Width)])); break; } } List <Cell> path; switch (rnd.Next(3)) { case 0: int count = rnd.Next(3) + 1; int moveX = rnd.Next(Config.FIELD_HEIGHT); int moveY = rnd.Next(Config.FIELD_WIDTH); while (field.Field[moveX, moveY].Type == CellType.LAND) { moveX = rnd.Next(Config.FIELD_HEIGHT); moveY = rnd.Next(Config.FIELD_WIDTH); } path = field.getPath(sub.Cell, field.Field[moveX, moveY]); if (path == null) { path = new List <Cell>(); } return(new Move(path)); case 1: count = rnd.Next(3) + 1; moveX = rnd.Next(Config.FIELD_HEIGHT); moveY = rnd.Next(Config.FIELD_WIDTH); while (field.Field[moveX, moveY].Type == CellType.LAND) { moveX = rnd.Next(Config.FIELD_HEIGHT); moveY = rnd.Next(Config.FIELD_WIDTH); } path = field.getPath(sub.Cell, field.Field[moveX, moveY]); if (path == null) { path = new List <Cell>(); } return(new PlaceMine(path)); case 2: int x = rnd.Next(Config.FIELD_HEIGHT); int y = rnd.Next(Config.FIELD_WIDTH); while ((field.Field[x, y].Type == CellType.LAND) || ((x == sub.Cell.I) && (y == sub.Cell.J))) { x = rnd.Next(Config.FIELD_HEIGHT); y = rnd.Next(Config.FIELD_WIDTH); } path = field.getPath(sub.Cell, field.Field[x, y]); return(new LaunchTorpedo(path)); default: return(null); } }
public override IEnumerable <object> UpdateState() { //spawn some fish next to the player GameMain.GameScreen.BackgroundCreatureManager.SpawnSprites(2, Submarine.MainSub.Position + Character.Controlled.Position); foreach (Item item in Item.ItemList) { var wire = item.GetComponent <Wire>(); if (wire != null && wire.Connections.Any(c => c != null)) { wire.Locked = true; } } yield return(new WaitForSeconds(4.0f)); infoBox = CreateInfoFrame("Use WASD to move and the mouse to look around"); yield return(new WaitForSeconds(5.0f)); //----------------------------------- infoBox = CreateInfoFrame("Open the door at your right side by highlighting the button next to it with your cursor and pressing E"); Door tutorialDoor = Item.ItemList.Find(i => i.HasTag("tutorialdoor")).GetComponent <Door>(); while (!tutorialDoor.IsOpen && Character.Controlled.WorldPosition.X < tutorialDoor.Item.WorldPosition.X) { yield return(CoroutineStatus.Running); } yield return(new WaitForSeconds(2.0f)); //----------------------------------- infoBox = CreateInfoFrame("Hold W or S to walk up or down stairs. Use shift to run.", true); while (infoBox != null) { yield return(CoroutineStatus.Running); } //----------------------------------- infoBox = CreateInfoFrame("At the moment the submarine has no power, which means that crucial systems such as the oxygen generator or the engine aren't running. Let's fix this: go to the upper left corner of the submarine, where you'll find a nuclear reactor."); Reactor reactor = Item.ItemList.Find(i => i.HasTag("tutorialreactor")).GetComponent <Reactor>(); reactor.MeltDownTemp = 20000.0f; while (Vector2.Distance(Character.Controlled.Position, reactor.Item.Position) > 200.0f) { yield return(CoroutineStatus.Running); } infoBox = CreateInfoFrame("The reactor requires fuel rods to generate power. You can grab one from the steel cabinet by walking next to it and pressing E."); while (Character.Controlled.SelectedConstruction == null || Character.Controlled.SelectedConstruction.Name != "Steel Cabinet") { yield return(CoroutineStatus.Running); } infoBox = CreateInfoFrame("Pick up one of the fuel rods either by double-clicking or dragging and dropping it into your inventory."); while (!HasItem("Fuel Rod")) { yield return(CoroutineStatus.Running); } infoBox = CreateInfoFrame("Select the reactor by walking next to it and pressing E."); while (Character.Controlled.SelectedConstruction != reactor.Item) { yield return(CoroutineStatus.Running); } yield return(new WaitForSeconds(0.5f)); infoBox = CreateInfoFrame("Load the fuel rod into the reactor by dropping it into any of the 5 slots."); while (reactor.AvailableFuel <= 0.0f) { yield return(CoroutineStatus.Running); } infoBox = CreateInfoFrame("The reactor is now fueled up. Try turning it on by increasing the fission rate."); while (reactor.FissionRate <= 0.0f) { yield return(CoroutineStatus.Running); } yield return(new WaitForSeconds(0.5f)); infoBox = CreateInfoFrame("The reactor core has started generating heat, which in turn generates power for the submarine. The power generation is very low at the moment," + " because the reactor is set to shut itself down when the temperature rises above 500 degrees Celsius. You can adjust the temperature limit by changing the \"Shutdown Temperature\" in the control panel.", true); while (infoBox != null) { reactor.ShutDownTemp = Math.Min(reactor.ShutDownTemp, 5000.0f); yield return(CoroutineStatus.Running); } yield return(new WaitForSeconds(0.5f)); infoBox = CreateInfoFrame("The amount of power generated by the reactor should be kept close to the amount of power consumed by the devices in the submarine. " + "If there isn't enough power, devices won't function properly (or at all), and if there's too much power, some devices may be damaged." + " Try to raise the temperature of the reactor close to 3000 degrees by adjusting the fission and cooling rates.", true); while (Math.Abs(reactor.Temperature - 3000.0f) > 100.0f) { reactor.AutoTemp = false; reactor.ShutDownTemp = Math.Min(reactor.ShutDownTemp, 5000.0f); yield return(CoroutineStatus.Running); } yield return(new WaitForSeconds(0.5f)); infoBox = CreateInfoFrame("Looks like we're up and running! Now you should turn on the \"Automatic temperature control\", which will make the reactor " + "automatically adjust the temperature to a suitable level. Even though it's an easy way to keep the reactor up and running most of the time, " + "you should keep in mind that it changes the temperature very slowly and carefully, which may cause issues if there are sudden changes in grid load."); while (!reactor.AutoTemp) { yield return(CoroutineStatus.Running); } yield return(new WaitForSeconds(0.5f)); infoBox = CreateInfoFrame("That's the basics of operating the reactor! Now that there's power available for the engines, it's time to get the submarine moving. " + "Deselect the reactor by pressing E and head to the command room at the right edge of the vessel."); Steering steering = Item.ItemList.Find(i => i.HasTag("tutorialsteering")).GetComponent <Steering>(); Radar radar = steering.Item.GetComponent <Radar>(); while (Vector2.Distance(Character.Controlled.Position, steering.Item.Position) > 150.0f) { yield return(CoroutineStatus.Running); } CoroutineManager.StartCoroutine(KeepReactorRunning(reactor)); infoBox = CreateInfoFrame("Select the navigation terminal by walking next to it and pressing E."); while (Character.Controlled.SelectedConstruction != steering.Item) { yield return(CoroutineStatus.Running); } yield return(new WaitForSeconds(0.5f)); infoBox = CreateInfoFrame("There seems to be something wrong with the navigation terminal." + " There's nothing on the monitor, so it's probably out of power. The reactor must still be" + " running or the lights would've gone out, so it's most likely a problem with the wiring." + " Deselect the terminal by pressing E to start checking the wiring."); while (Character.Controlled.SelectedConstruction == steering.Item) { yield return(CoroutineStatus.Running); } yield return(new WaitForSeconds(1.0f)); infoBox = CreateInfoFrame("You need a screwdriver to check the wiring of the terminal." + " Equip a screwdriver by pulling it to either of the slots with a hand symbol, and then use it on the terminal by left clicking."); while (Character.Controlled.SelectedConstruction != steering.Item || Character.Controlled.SelectedItems.FirstOrDefault(i => i != null && i.Name == "Screwdriver") == null) { yield return(CoroutineStatus.Running); } infoBox = CreateInfoFrame("Here you can see all the wires connected to the terminal. Apparently there's no wire" + " going into the to the power connection - that's why the monitor isn't working." + " You should find a piece of wire to connect it. Try searching some of the cabinets scattered around the sub."); while (!HasItem("Wire")) { yield return(CoroutineStatus.Running); } infoBox = CreateInfoFrame("Head back to the navigation terminal to fix the wiring."); PowerTransfer junctionBox = Item.ItemList.Find(i => i != null && i.HasTag("tutorialjunctionbox")).GetComponent <PowerTransfer>(); while ((Character.Controlled.SelectedConstruction != junctionBox.Item && Character.Controlled.SelectedConstruction != steering.Item) || Character.Controlled.SelectedItems.FirstOrDefault(i => i != null && i.Name == "Screwdriver") == null) { yield return(CoroutineStatus.Running); } if (Character.Controlled.SelectedItems.FirstOrDefault(i => i != null && i.GetComponent <Wire>() != null) == null) { infoBox = CreateInfoFrame("Equip the wire by dragging it to one of the slots with a hand symbol."); while (Character.Controlled.SelectedItems.FirstOrDefault(i => i != null && i.GetComponent <Wire>() != null) == null) { yield return(CoroutineStatus.Running); } } infoBox = CreateInfoFrame("You can see the equipped wire at the middle of the connection panel. Drag it to the power connector."); var steeringConnection = steering.Item.Connections.Find(c => c.Name.Contains("power")); while (steeringConnection.Wires.FirstOrDefault(w => w != null) == null) { yield return(CoroutineStatus.Running); } infoBox = CreateInfoFrame("Now you have to connect the other end of the wire to a power source. " + "The junction box in the room just below the command room should do."); while (Character.Controlled.SelectedConstruction != null) { yield return(CoroutineStatus.Running); } yield return(new WaitForSeconds(2.0f)); infoBox = CreateInfoFrame("You can now move the other end of the wire around, and attach it on the wall by left clicking or " + "remove the previous attachment by right clicking. Or if you don't care for neatly laid out wiring, you can just " + "run it straight to the junction box."); while (Character.Controlled.SelectedConstruction == null || Character.Controlled.SelectedConstruction.GetComponent <PowerTransfer>() == null) { yield return(CoroutineStatus.Running); } infoBox = CreateInfoFrame("Connect the wire to the junction box by pulling it to the power connection, the same way you did with the navigation terminal."); while (radar.Voltage < 0.1f) { yield return(CoroutineStatus.Running); } infoBox = CreateInfoFrame("Great! Now we should be able to get moving."); while (Character.Controlled.SelectedConstruction != steering.Item) { yield return(CoroutineStatus.Running); } infoBox = CreateInfoFrame("You can take a look at the area around the sub by selecting the \"Sonar\" checkbox."); while (!radar.IsActive) { yield return(CoroutineStatus.Running); } yield return(new WaitForSeconds(0.5f)); infoBox = CreateInfoFrame("The green rectangle in the middle is the submarine, and the flickering shapes outside it are the walls of an underwater cavern. " + "Try moving the submarine by clicking somewhere on the monitor and dragging the pointer to the direction you want to go to."); while (steering.TargetVelocity == Vector2.Zero && steering.TargetVelocity.Length() < 50.0f) { yield return(CoroutineStatus.Running); } yield return(new WaitForSeconds(4.0f)); infoBox = CreateInfoFrame("The submarine moves up and down by pumping water in and out of the two ballast tanks at the bottom of the submarine. " + "The engine at the back of the sub moves it forwards and backwards.", true); while (infoBox != null) { yield return(CoroutineStatus.Running); } infoBox = CreateInfoFrame("Steer the submarine downwards, heading further into the cavern."); while (Submarine.MainSub.WorldPosition.Y > 40000.0f) { yield return(CoroutineStatus.Running); } yield return(new WaitForSeconds(1.0f)); var moloch = Character.Create( "Content/Characters/Moloch/moloch.xml", steering.Item.WorldPosition + new Vector2(3000.0f, -500.0f)); moloch.PlaySound(CharacterSound.SoundType.Attack); yield return(new WaitForSeconds(1.0f)); infoBox = CreateInfoFrame("Uh-oh... Something enormous just appeared on the radar."); List <Structure> windows = new List <Structure>(); foreach (Structure s in Structure.WallList) { if (s.CastShadow || !s.HasBody) { continue; } if (s.Rect.Right > steering.Item.CurrentHull.Rect.Right) { windows.Add(s); } } float slowdownTimer = 1.0f; bool broken = false; do { steering.TargetVelocity = Vector2.Zero; slowdownTimer = Math.Max(0.0f, slowdownTimer - CoroutineManager.DeltaTime * 0.3f); Submarine.MainSub.Velocity *= slowdownTimer; moloch.AIController.SelectTarget(steering.Item.CurrentHull.AiTarget); Vector2 steeringDir = windows[0].WorldPosition - moloch.WorldPosition; if (steeringDir != Vector2.Zero) { steeringDir = Vector2.Normalize(steeringDir); } moloch.AIController.SteeringManager.SteeringManual(CoroutineManager.DeltaTime, steeringDir * 100.0f); foreach (Structure window in windows) { for (int i = 0; i < window.SectionCount; i++) { if (!window.SectionIsLeaking(i)) { continue; } broken = true; break; } if (broken) { break; } } if (broken) { break; } yield return(CoroutineStatus.Running); } while (!broken); //fix everything except the command windows foreach (Structure w in Structure.WallList) { bool isWindow = windows.Contains(w); for (int i = 0; i < w.SectionCount; i++) { if (!w.SectionIsLeaking(i)) { continue; } if (isWindow) { //decrease window damage to slow down the leaking w.AddDamage(i, -w.SectionDamage(i) * 0.48f); } else { w.AddDamage(i, -100000.0f); } } } Submarine.MainSub.GodMode = true; var capacitor1 = Item.ItemList.Find(i => i.HasTag("capacitor1")).GetComponent <PowerContainer>(); var capacitor2 = Item.ItemList.Find(i => i.HasTag("capacitor1")).GetComponent <PowerContainer>(); CoroutineManager.StartCoroutine(KeepEnemyAway(moloch, new PowerContainer[] { capacitor1, capacitor2 })); infoBox = CreateInfoFrame("The hull has been breached! Close all the doors to the command room to stop the water from flooding the entire sub!"); Door commandDoor1 = Item.ItemList.Find(i => i.HasTag("commanddoor1")).GetComponent <Door>(); Door commandDoor2 = Item.ItemList.Find(i => i.HasTag("commanddoor2")).GetComponent <Door>(); Door commandDoor3 = Item.ItemList.Find(i => i.HasTag("commanddoor3")).GetComponent <Door>(); //wait until the player is out of the room and the doors are closed while (Character.Controlled.WorldPosition.X > commandDoor1.Item.WorldPosition.X || (commandDoor1.IsOpen || (commandDoor2.IsOpen || commandDoor3.IsOpen))) { //prevent the hull from filling up completely and crushing the player steering.Item.CurrentHull.Volume = Math.Min(steering.Item.CurrentHull.Volume, steering.Item.CurrentHull.FullVolume * 0.9f); yield return(CoroutineStatus.Running); } infoBox = CreateInfoFrame("You should quickly find yourself a diving mask or a diving suit. " + "There are some in the room next to the airlock."); bool divingMaskSelected = false; while (!HasItem("Diving Mask") && !HasItem("Diving Suit")) { if (!divingMaskSelected && Character.Controlled.FocusedItem != null && Character.Controlled.FocusedItem.Name == "Diving Suit") { infoBox = CreateInfoFrame("There can only be one item in each inventory slot, so you need to take off " + "the jumpsuit if you wish to wear a diving suit."); divingMaskSelected = true; } yield return(CoroutineStatus.Running); } if (HasItem("Diving Mask")) { infoBox = CreateInfoFrame("The diving mask will let you breathe underwater, but it won't protect from the water pressure outside the sub. " + "It should be fine for the situation at hand, but you still need to find an oxygen tank and drag it into the same slot as the mask." + "You should grab one or two from one of the cabinets."); } else if (HasItem("Diving Suit")) { infoBox = CreateInfoFrame("In addition to letting you breathe underwater, the suit will protect you from the water pressure outside the sub " + "(unlike the diving mask). However, you still need to drag an oxygen tank into the same slot as the suit to supply oxygen. " + "You should grab one or two from one of the cabinets."); } while (!HasItem("Oxygen Tank")) { yield return(CoroutineStatus.Running); } yield return(new WaitForSeconds(5.0f)); infoBox = CreateInfoFrame("Now you should stop the creature attacking the submarine before it does any more damage. Head to the railgun room at the upper right corner of the sub."); var railGun = Item.ItemList.Find(i => i.GetComponent <Turret>() != null); while (Vector2.Distance(Character.Controlled.Position, railGun.Position) > 500) { yield return(new WaitForSeconds(1.0f)); } infoBox = CreateInfoFrame("The railgun requires a large power surge to fire. The reactor can't provide a surge large enough, so we need to use the " + " supercapacitors in the railgun room. The capacitors need to be charged first; select them and crank up the recharge rate."); while (capacitor1.RechargeSpeed < 0.5f && capacitor2.RechargeSpeed < 0.5f) { yield return(new WaitForSeconds(1.0f)); } infoBox = CreateInfoFrame("The capacitors take some time to recharge, so now is a good " + "time to head to the room below and load some shells for the railgun."); var loader = Item.ItemList.Find(i => i.Name == "Railgun Loader").GetComponent <ItemContainer>(); while (Math.Abs(Character.Controlled.Position.Y - loader.Item.Position.Y) > 80) { yield return(CoroutineStatus.Running); } infoBox = CreateInfoFrame("Grab one of the shells. You can load it by selecting the railgun loader and dragging the shell to. " + "one of the free slots. You need two hands to carry a shell, so make sure you don't have anything else in either hand."); while (loader.Item.ContainedItems.FirstOrDefault(i => i != null && i.Name == "Railgun Shell") == null) { moloch.Health = 50.0f; capacitor1.Charge += 5.0f; capacitor2.Charge += 5.0f; yield return(CoroutineStatus.Running); } infoBox = CreateInfoFrame("Now we're ready to shoot! Select the railgun controller."); while (Character.Controlled.SelectedConstruction == null || Character.Controlled.SelectedConstruction.Name != "Railgun Controller") { yield return(CoroutineStatus.Running); } moloch.AnimController.SetPosition(ConvertUnits.ToSimUnits(Character.Controlled.WorldPosition + Vector2.UnitY * 600.0f)); infoBox = CreateInfoFrame("Use the right mouse button to aim and wait for the creature to come closer. When you're ready to shoot, " + "press the left mouse button."); while (!moloch.IsDead) { if (moloch.WorldPosition.Y > Character.Controlled.WorldPosition.Y + 600.0f) { moloch.AIController.SteeringManager.SteeringManual(CoroutineManager.DeltaTime, Character.Controlled.WorldPosition - moloch.WorldPosition); } moloch.AIController.SelectTarget(Character.Controlled.AiTarget); yield return(CoroutineStatus.Running); } Submarine.MainSub.GodMode = false; infoBox = CreateInfoFrame("The creature has died. Now you should fix the damages in the control room: " + "Grab a welding tool from the closet in the railgun room."); while (!HasItem("Welding Tool")) { yield return(CoroutineStatus.Running); } infoBox = CreateInfoFrame("The welding tool requires fuel to work. Grab a welding fuel tank and attach it to the tool " + "by dragging it into the same slot."); do { var weldingTool = Character.Controlled.Inventory.Items.FirstOrDefault(i => i != null && i.Name == "Welding Tool"); if (weldingTool != null && weldingTool.ContainedItems.FirstOrDefault(contained => contained != null && contained.Name == "Welding Fuel Tank") != null) { break; } yield return(CoroutineStatus.Running); } while (true); infoBox = CreateInfoFrame("You can aim with the tool using the right mouse button and weld using the left button. " + "Head to the command room to fix the leaks there."); do { broken = false; foreach (Structure window in windows) { for (int i = 0; i < window.SectionCount; i++) { if (!window.SectionIsLeaking(i)) { continue; } broken = true; break; } if (broken) { break; } } yield return(new WaitForSeconds(1.0f)); } while (broken); infoBox = CreateInfoFrame("The hull is fixed now, but there's still quite a bit of water inside the sub. It should be pumped out " + "using the bilge pump in the room at the bottom of the submarine."); Pump pump = Item.ItemList.Find(i => i.HasTag("tutorialpump")).GetComponent <Pump>(); while (Vector2.Distance(Character.Controlled.Position, pump.Item.Position) > 100.0f) { yield return(CoroutineStatus.Running); } infoBox = CreateInfoFrame("The two pumps inside the ballast tanks " + "are connected straight to the navigation terminal and can't be manually controlled unless you mess with their wiring, " + "so you should only use the pump in the middle room to pump out the water. Select it, turn it on and adjust the pumping speed " + "to start pumping water out.", true); while (infoBox != null) { yield return(CoroutineStatus.Running); } bool brokenMsgShown = false; Item brokenBox = null; while (pump.FlowPercentage > 0.0f || pump.CurrFlow <= 0.0f || !pump.IsActive) { if (!brokenMsgShown && pump.Voltage < pump.MinVoltage && Character.Controlled.SelectedConstruction == pump.Item) { brokenMsgShown = true; infoBox = CreateInfoFrame("Looks like the pump isn't getting any power. The water must have short-circuited some of the junction " + "boxes. You can check which boxes are broken by selecting them."); while (true) { if (Character.Controlled.SelectedConstruction != null && Character.Controlled.SelectedConstruction.GetComponent <PowerTransfer>() != null && Character.Controlled.SelectedConstruction.Condition == 0.0f) { brokenBox = Character.Controlled.SelectedConstruction; infoBox = CreateInfoFrame("Here's our problem: this junction box is broken. Luckily engineers are adept at fixing electrical devices - " + "you just need to find a spare wire and click the \"Fix\"-button to repair the box."); break; } if (pump.Voltage > pump.MinVoltage) { break; } yield return(CoroutineStatus.Running); } } if (brokenBox != null && brokenBox.Condition > 50.0f && pump.Voltage < pump.MinVoltage) { yield return(new WaitForSeconds(1.0f)); if (pump.Voltage < pump.MinVoltage) { infoBox = CreateInfoFrame("The pump is still not running. Check if there are more broken junction boxes between the pump and the reactor."); } brokenBox = null; } yield return(CoroutineStatus.Running); } infoBox = CreateInfoFrame("The pump is up and running. Wait for the water to be drained out."); while (pump.Item.CurrentHull.Volume > 1000.0f) { yield return(CoroutineStatus.Running); } infoBox = CreateInfoFrame("That was all there is to this tutorial! Now you should be able to handle " + "most of the basic tasks on board the submarine."); yield return(new WaitForSeconds(4.0f)); Character.Controlled = null; GameMain.GameScreen.Cam.TargetPos = Vector2.Zero; GameMain.LightManager.LosEnabled = false; var cinematic = new TransitionCinematic(Submarine.MainSub, GameMain.GameScreen.Cam, 5.0f); while (cinematic.Running) { yield return(CoroutineStatus.Running); } Submarine.Unload(); GameMain.MainMenuScreen.Select(); yield return(CoroutineStatus.Success); }
public Submarine() { _instance = this; //InitSubmarine(_possiblePositions); }
private static IUnit CreateUnit(UnitType type, int x, int y) { IUnit unit; switch (type) { case UnitType.Settlers: unit = new Settlers(); break; case UnitType.Militia: unit = new Militia(); break; case UnitType.Phalanx: unit = new Phalanx(); break; case UnitType.Legion: unit = new Legion(); break; case UnitType.Musketeers: unit = new Musketeers(); break; case UnitType.Riflemen: unit = new Riflemen(); break; case UnitType.Cavalry: unit = new Cavalry(); break; case UnitType.Knights: unit = new Knights(); break; case UnitType.Catapult: unit = new Catapult(); break; case UnitType.Cannon: unit = new Cannon(); break; case UnitType.Chariot: unit = new Chariot(); break; case UnitType.Armor: unit = new Armor(); break; case UnitType.MechInf: unit = new MechInf(); break; case UnitType.Artillery: unit = new Artillery(); break; case UnitType.Fighter: unit = new Fighter(); break; case UnitType.Bomber: unit = new Bomber(); break; case UnitType.Trireme: unit = new Trireme(); break; case UnitType.Sail: unit = new Sail(); break; case UnitType.Frigate: unit = new Frigate(); break; case UnitType.Ironclad: unit = new Ironclad(); break; case UnitType.Cruiser: unit = new Cruiser(); break; case UnitType.Battleship: unit = new Battleship(); break; case UnitType.Submarine: unit = new Submarine(); break; case UnitType.Carrier: unit = new Carrier(); break; case UnitType.Transport: unit = new Transport(); break; case UnitType.Nuclear: unit = new Nuclear(); break; case UnitType.Diplomat: unit = new Diplomat(); break; case UnitType.Caravan: unit = new Caravan(); break; default: return(null); } unit.X = x; unit.Y = y; return(unit); }
private void CreateHulls() { var hullRects = new Rectangle[] { item.WorldRect, DockingTarget.item.WorldRect }; var subs = new Submarine[] { item.Submarine, DockingTarget.item.Submarine }; bodies = new Body[4]; if (DockingTarget.door != null) { CreateDoorBody(); } if (door != null) { DockingTarget.CreateDoorBody(); } if (IsHorizontal) { if (hullRects[0].Center.X > hullRects[1].Center.X) { hullRects = new Rectangle[] { DockingTarget.item.WorldRect, item.WorldRect }; subs = new Submarine[] { DockingTarget.item.Submarine, item.Submarine }; } hullRects[0] = new Rectangle(hullRects[0].Center.X, hullRects[0].Y, ((int)DockedDistance / 2), hullRects[0].Height); hullRects[1] = new Rectangle(hullRects[1].Center.X - ((int)DockedDistance / 2), hullRects[1].Y, ((int)DockedDistance / 2), hullRects[1].Height); //expand hulls if needed, so there's no empty space between the sub's hulls and docking port hulls int leftSubRightSide = int.MinValue, rightSubLeftSide = int.MaxValue; foreach (Hull hull in Hull.hullList) { for (int i = 0; i < 2; i++) { if (hull.Submarine != subs[i]) { continue; } if (hull.WorldRect.Y < hullRects[i].Y - hullRects[i].Height) { continue; } if (hull.WorldRect.Y - hull.WorldRect.Height > hullRects[i].Y) { continue; } if (i == 0) //left hull { leftSubRightSide = Math.Max(hull.WorldRect.Right, leftSubRightSide); } else //upper hull { rightSubLeftSide = Math.Min(hull.WorldRect.X, rightSubLeftSide); } } } //expand left hull to the rightmost hull of the sub at the left side //(unless the difference is more than 100 units - if the distance is very large //there's something wrong with the positioning of the docking ports or submarine hulls) int leftHullDiff = (hullRects[0].X - leftSubRightSide) + 5; if (leftHullDiff > 0) { if (leftHullDiff > 100) { DebugConsole.ThrowError("Creating hulls between docking ports failed. The leftmost docking port seems to be very far from any hulls in the left-side submarine."); } else { hullRects[0].X -= leftHullDiff; hullRects[0].Width += leftHullDiff; } } int rightHullDiff = (rightSubLeftSide - hullRects[1].Right) + 5; if (rightHullDiff > 0) { if (rightHullDiff > 100) { DebugConsole.ThrowError("Creating hulls between docking ports failed. The rightmost docking port seems to be very far from any hulls in the right-side submarine."); } else { hullRects[1].Width += rightHullDiff; } } for (int i = 0; i < 2; i++) { hullRects[i].Location -= MathUtils.ToPoint((subs[i].WorldPosition - subs[i].HiddenSubPosition)); hulls[i] = new Hull(MapEntityPrefab.Find(null, "hull"), hullRects[i], subs[i]); hulls[i].AddToGrid(subs[i]); hulls[i].FreeID(); for (int j = 0; j < 2; j++) { bodies[i + j * 2] = BodyFactory.CreateEdge(GameMain.World, ConvertUnits.ToSimUnits(new Vector2(hullRects[i].X, hullRects[i].Y - hullRects[i].Height * j)), ConvertUnits.ToSimUnits(new Vector2(hullRects[i].Right, hullRects[i].Y - hullRects[i].Height * j))); } } gap = new Gap(new Rectangle(hullRects[0].Right - 2, hullRects[0].Y, 4, hullRects[0].Height), true, subs[0]); } else { if (hullRects[0].Center.Y > hullRects[1].Center.Y) { hullRects = new Rectangle[] { DockingTarget.item.WorldRect, item.WorldRect }; subs = new Submarine[] { DockingTarget.item.Submarine, item.Submarine }; } hullRects[0] = new Rectangle(hullRects[0].X, hullRects[0].Y + (int)(-hullRects[0].Height + DockedDistance) / 2, hullRects[0].Width, ((int)DockedDistance / 2)); hullRects[1] = new Rectangle(hullRects[1].X, hullRects[1].Y - hullRects[1].Height / 2, hullRects[1].Width, ((int)DockedDistance / 2)); //expand hulls if needed, so there's no empty space between the sub's hulls and docking port hulls int upperSubBottom = int.MaxValue, lowerSubTop = int.MinValue; foreach (Hull hull in Hull.hullList) { for (int i = 0; i < 2; i++) { if (hull.Submarine != subs[i]) { continue; } if (hull.WorldRect.Right < hullRects[i].X) { continue; } if (hull.WorldRect.X > hullRects[i].Right) { continue; } if (i == 0) //lower hull { lowerSubTop = Math.Max(hull.WorldRect.Y, lowerSubTop); } else //upper hull { upperSubBottom = Math.Min(hull.WorldRect.Y - hull.WorldRect.Height, upperSubBottom); } } } //expand lower hull to the topmost hull of the lower sub //(unless the difference is more than 100 units - if the distance is very large //there's something wrong with the positioning of the docking ports or submarine hulls) int lowerHullDiff = ((hullRects[0].Y - hullRects[0].Height) - lowerSubTop) + 5; if (lowerHullDiff > 0) { if (lowerHullDiff > 100) { DebugConsole.ThrowError("Creating hulls between docking ports failed. The lower docking port seems to be very far from any hulls in the lower submarine."); } else { hullRects[0].Height += lowerHullDiff; } } int upperHullDiff = (upperSubBottom - hullRects[1].Y) + 5; if (upperHullDiff > 0) { if (upperHullDiff > 100) { DebugConsole.ThrowError("Creating hulls between docking ports failed. The upper docking port seems to be very far from any hulls in the upper submarine."); } else { hullRects[1].Y += upperHullDiff; hullRects[1].Height += upperHullDiff; } } //difference between the edges of the hulls (to avoid a gap between the hulls) //0 is lower int midHullDiff = ((hullRects[1].Y - hullRects[1].Height) - hullRects[0].Y) + 2; if (midHullDiff > 100) { DebugConsole.ThrowError("Creating hulls between docking ports failed. The upper hull seems to be very far from the lower hull."); } else if (midHullDiff > 0) { hullRects[0].Height += midHullDiff / 2 + 1; hullRects[1].Y -= midHullDiff / 2 + 1; hullRects[1].Height += midHullDiff / 2 + 1; } for (int i = 0; i < 2; i++) { hullRects[i].Location -= MathUtils.ToPoint((subs[i].WorldPosition - subs[i].HiddenSubPosition)); hulls[i] = new Hull(MapEntityPrefab.Find(null, "hull"), hullRects[i], subs[i]); hulls[i].AddToGrid(subs[i]); hulls[i].FreeID(); } gap = new Gap(new Rectangle(hullRects[0].X, hullRects[0].Y + 2, hullRects[0].Width, 4), false, subs[0]); } LinkHullsToGaps(); hulls[0].ShouldBeSaved = false; hulls[1].ShouldBeSaved = false; item.linkedTo.Add(hulls[0]); item.linkedTo.Add(hulls[1]); gap.FreeID(); gap.DisableHullRechecks = true; gap.ShouldBeSaved = false; item.linkedTo.Add(gap); foreach (Body body in bodies) { if (body == null) { continue; } body.BodyType = BodyType.Static; body.Friction = 0.5f; body.CollisionCategories = Physics.CollisionWall; } }
void DealDamage(Submarine submarine) { submarine.TakeDamage(Side, Damage); }