diff --git a/1.6/1.6/Assemblies/WulaFallenEmpire.dll b/1.6/1.6/Assemblies/WulaFallenEmpire.dll
index 77389a20..b9011b20 100644
Binary files a/1.6/1.6/Assemblies/WulaFallenEmpire.dll and b/1.6/1.6/Assemblies/WulaFallenEmpire.dll differ
diff --git a/1.6/1.6/Defs/ThingDefs_Buildings/Building_WULA_Shuttle.xml b/1.6/1.6/Defs/ThingDefs_Buildings/Building_WULA_Shuttle.xml
new file mode 100644
index 00000000..a5359922
--- /dev/null
+++ b/1.6/1.6/Defs/ThingDefs_Buildings/Building_WULA_Shuttle.xml
@@ -0,0 +1,269 @@
+
+
+
+
+ WULA_ArmedShuttle
+
+ A chemfuel-powered shuttle designed for long-distance travel, equipped with a turret for defense. It is capable of reaching orbital locations.
+ WulaFallenEmpire.Building_ArmedShuttle
+ true
+ Building
+ 50
+ true
+ PassThroughOnly
+ 0.5
+ (3,5)
+ true
+ (0.56, 0.62, 0.9)
+ 1
+
+ Graphic_Multi
+ Things/Building/PassengerShuttle/PassengerShuttle
+ CutoutComplex
+ (3,5)
+
+ (1.8, 1.0, 4.1)
+ (-0.1, 0, 0)
+
+
+
+ 600
+ 0.5
+ 40000
+ 150
+ 0.65
+
+ Normal
+ Odyssey
+ 8
+
+ 300
+ 200
+ 8
+ 2
+ 1
+
+ true
+
+ 60
+ 60
+ 5
+ 4
+
+ true
+ true
+ (2, 0, 0)
+ East
+ true
+ Light
+ BulletImpact_Metal
+ true
+ RealtimeOnly
+ ConstructMetal
+ true
+
+ false
+ BuildingDestroyed_Metal_Big
+ true
+ true
+
+ ShuttleEngine
+
+ Gun_ChargeBlasterHeavyTurret
+ 5.5
+ 1.75
+ (0, 0.05)
+
+
+ ITab_ContentsTransporter
+ ITab_Shells
+
+
+ Shuttles
+
+
+
+ Ship_ArmedShuttle
+
+
+ 3
+ 50
+ ArmedShuttleLeaving_WULA
+ PassengerShuttle
+ 3750
+ 62
+ {0} is ready to launch again.
+
+
+ 500
+ true
+ true
+ Shuttle_PawnLoaded
+ Shuttle_PawnExit
+ true
+
+
+ 400
+ true
+ 400
+
+
+ Chemfuel
+
+
+ Chemfuel
+ Chemfuel
+ true
+ 1
+ true
+ false
+ true
+ true
+
+
+ ShuttleIdle_Ambience
+
+
+
+ PlaceWorker_NotUnderRoof
+ PlaceWorker_TurretTop
+
+ 2601
+
+
+
+ WULA_Bullet_ArmedShuttle
+
+
+ Things/Projectile/Bullet_Big
+ Graphic_Single
+
+
+ Bullet
+ 25
+ 70
+
+
+
+
+ Gun_ChargeBlasterHeavyTurret
+
+ A pulse-charged rapid-fire blaster for area fire.
+
+ Things/Item/Equipment/WeaponRanged/ChargeBlasterLight
+ Graphic_Single
+
+
+ 0.08
+ 5.5
+
+
+
+ Verb_Shoot
+ true
+ WULA_Bullet_ArmedShuttle
+ 1.25
+ 3.9
+ 45.9
+ 7
+ 9
+ Shot_ChargeBlaster
+ GunTail_Heavy
+ 9
+
+
+
+
+
+ ArmedShuttleIncoming_WULA
+
+ WulaFallenEmpire.ArmedShuttleIncoming
+
+ Graphic_Multi
+ Things/Building/PassengerShuttle/PassengerShuttle
+ CutoutComplex
+ (3,5)
+
+ (3,5)
+
+ Shuttle_Landing
+ 250
+ 200~250
+ (3.5,5.5)
+
+
+ (0,30)
+ (0.5,5)
+ (0.9,-5)
+ (0.95,0)
+
+
+
+
+ (0.95,2.5)
+ (1,0)
+
+
+
+
+ (0.6,0.6)
+ (0.95,0.1)
+
+
+
+
+
+
+ ArmedShuttleLeaving_WULA
+
+ PassengerShuttleLeaving
+ true
+
+ Graphic_Multi
+ Things/Building/PassengerShuttle/PassengerShuttle
+ CutoutComplex
+ (3,5)
+
+ (3,5)
+
+ true
+ Shuttle_Leaving
+ -10
+ -40~-15
+ 0.05
+ Things/Skyfaller/SkyfallerShadowRectangle
+ (3.5,5.5)
+ 1
+
+
+ (0,0)
+ (0.15,10)
+ (0.5,-5)
+
+
+
+
+ (0,0)
+ (0.08,2)
+
+
+
+
+ (0,0.2)
+ (0.4,0.7)
+
+
+
+
+
+
+ Ship_ArmedShuttle
+
+ WULA_ArmedShuttle
+ ArmedShuttleIncoming_WULA
+ ArmedShuttleLeaving_WULA
+ PassengerShuttle
+ true
+
+
+
\ No newline at end of file
diff --git a/1.6/Odyssey/Defs/ThingDefs_Buildings/Buildings_WULA_Odyssey.xml b/1.6/Odyssey/Defs/ThingDefs_Buildings/Buildings_WULA_Odyssey.xml
index 6d339395..4d4c4c2f 100644
--- a/1.6/Odyssey/Defs/ThingDefs_Buildings/Buildings_WULA_Odyssey.xml
+++ b/1.6/Odyssey/Defs/ThingDefs_Buildings/Buildings_WULA_Odyssey.xml
@@ -30,11 +30,6 @@
1
WULA_Buildings
-
- 100
- 2
- 1
-
12
diff --git a/Source/WulaFallenEmpire/ArmedShuttleIncoming.cs b/Source/WulaFallenEmpire/ArmedShuttleIncoming.cs
new file mode 100644
index 00000000..7ed990e0
--- /dev/null
+++ b/Source/WulaFallenEmpire/ArmedShuttleIncoming.cs
@@ -0,0 +1,21 @@
+using RimWorld;
+using Verse;
+using System.Linq;
+using UnityEngine;
+
+namespace WulaFallenEmpire
+{
+ public class ArmedShuttleIncoming : PassengerShuttleIncoming
+ {
+ public new Building_ArmedShuttle Shuttle => (Building_ArmedShuttle)base.innerContainer.FirstOrDefault();
+
+ public override Color DrawColor => Shuttle.DrawColor;
+
+ protected override void Impact()
+ {
+ Shuttle.TryGetComp()?.Notify_Arrived();
+ // Do not call base.Impact(), as it leads to the InvalidCastException in the parent class.
+ // The base Skyfaller.Tick() will handle the rest of the impact logic after this method returns.
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/WulaFallenEmpire/Building_ArmedShuttle.cs b/Source/WulaFallenEmpire/Building_ArmedShuttle.cs
new file mode 100644
index 00000000..373566d6
--- /dev/null
+++ b/Source/WulaFallenEmpire/Building_ArmedShuttle.cs
@@ -0,0 +1,661 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using RimWorld;
+using RimWorld.Planet;
+using UnityEngine;
+using Verse;
+using Verse.AI;
+using Verse.Sound;
+
+namespace WulaFallenEmpire
+{
+ [StaticConstructorOnStartup]
+ public class Building_ArmedShuttle : Building, IAttackTargetSearcher, IRenameable
+ {
+ // --- TurretTop nested class ---
+ public class TurretTop
+ {
+ private Building_ArmedShuttle parentTurret;
+ private float curRotationInt;
+ private int ticksUntilIdleTurn;
+ private int idleTurnTicksLeft;
+ private bool idleTurnClockwise;
+
+ private const float IdleTurnDegreesPerTick = 0.26f;
+ private const int IdleTurnDuration = 140;
+ private const int IdleTurnIntervalMin = 150;
+ private const int IdleTurnIntervalMax = 350;
+ public static readonly int ArtworkRotation = -90;
+
+ public float CurRotation
+ {
+ get => curRotationInt;
+ set
+ {
+ curRotationInt = value % 360f;
+ if (curRotationInt < 0f) curRotationInt += 360f;
+ }
+ }
+
+ public TurretTop(Building_ArmedShuttle ParentTurret)
+ {
+ this.parentTurret = ParentTurret;
+ }
+
+ public void SetRotationFromOrientation() => CurRotation = parentTurret.Rotation.AsAngle;
+
+ public void ForceFaceTarget(LocalTargetInfo targ)
+ {
+ if (targ.IsValid)
+ {
+ CurRotation = (targ.Cell.ToVector3Shifted() - parentTurret.DrawPos).AngleFlat();
+ }
+ }
+
+ public void TurretTopTick()
+ {
+ LocalTargetInfo currentTarget = parentTurret.CurrentTarget;
+ if (currentTarget.IsValid)
+ {
+ CurRotation = (currentTarget.Cell.ToVector3Shifted() - parentTurret.DrawPos).AngleFlat();
+ ticksUntilIdleTurn = Rand.RangeInclusive(150, 350);
+ }
+ else if (ticksUntilIdleTurn > 0)
+ {
+ ticksUntilIdleTurn--;
+ if (ticksUntilIdleTurn == 0)
+ {
+ idleTurnClockwise = Rand.Value < 0.5f;
+ idleTurnTicksLeft = 140;
+ }
+ }
+ else
+ {
+ CurRotation += idleTurnClockwise ? 0.26f : -0.26f;
+ idleTurnTicksLeft--;
+ if (idleTurnTicksLeft <= 0)
+ {
+ ticksUntilIdleTurn = Rand.RangeInclusive(150, 350);
+ }
+ }
+ }
+
+ public void DrawTurret()
+ {
+ Vector3 v = new Vector3(parentTurret.def.building.turretTopOffset.x, 0f, parentTurret.def.building.turretTopOffset.y).RotatedBy(CurRotation);
+ float turretTopDrawSize = parentTurret.def.building.turretTopDrawSize;
+ float num = parentTurret.AttackVerb?.AimAngleOverride ?? CurRotation;
+ Vector3 pos = parentTurret.DrawPos + Altitudes.AltIncVect + v;
+ Quaternion q = ((float)ArtworkRotation + num).ToQuat();
+ Graphics.DrawMesh(matrix: Matrix4x4.TRS(pos, q, new Vector3(turretTopDrawSize, 1f, turretTopDrawSize)), mesh: MeshPool.plane10, material: parentTurret.TurretTopMaterial, layer: 0);
+ }
+ }
+
+ // --- Fields ---
+ protected LocalTargetInfo forcedTarget = LocalTargetInfo.Invalid;
+ private LocalTargetInfo lastAttackedTarget;
+ private int lastAttackTargetTick;
+ private StunHandler stunner;
+ private bool triedGettingStunner;
+ protected int burstCooldownTicksLeft;
+ protected int burstWarmupTicksLeft;
+ protected LocalTargetInfo currentTargetInt = LocalTargetInfo.Invalid;
+ private bool holdFire;
+ private bool burstActivated;
+ public Thing gun;
+ protected TurretTop top;
+ protected CompPowerTrader powerComp;
+ protected CompCanBeDormant dormantComp;
+ protected CompInitiatable initiatableComp;
+ protected CompMannable mannableComp;
+ protected CompInteractable interactableComp;
+ public CompRefuelable refuelableComp;
+ protected Effecter progressBarEffecter;
+ protected CompMechPowerCell powerCellComp;
+ protected CompHackable hackableComp;
+ private string shuttleName;
+ private CompLaunchable cachedLaunchableComp;
+ private CompTransporter cachedTransporterComp;
+ private CompShuttle cachedShuttleComp;
+ public static readonly CachedTexture RefuelFromCargoIcon = new CachedTexture("UI/Commands/RefuelPassengerShuttle");
+ private static List tmpContainedThings = new List();
+
+ // --- PROPERTIES ---
+ public virtual Material TurretTopMaterial => def.building.turretTopMat;
+ protected bool IsStunned
+ {
+ get
+ {
+ if (!triedGettingStunner)
+ {
+ stunner = GetComp()?.StunHandler;
+ triedGettingStunner = true;
+ }
+ return stunner != null && stunner.Stunned;
+ }
+ }
+ public LocalTargetInfo TargetCurrentlyAimingAt => CurrentTarget;
+ public Verb CurrentEffectiveVerb => AttackVerb;
+ public LocalTargetInfo LastAttackedTarget => lastAttackedTarget;
+ public int LastAttackTargetTick => lastAttackTargetTick;
+ public LocalTargetInfo ForcedTarget => forcedTarget;
+ public virtual bool IsEverThreat => true;
+ public bool Active => (powerComp == null || powerComp.PowerOn) && (dormantComp == null || dormantComp.Awake) && (initiatableComp == null || initiatableComp.Initiated) && (interactableComp == null || burstActivated) && (powerCellComp == null || !powerCellComp.depleted) && (hackableComp == null || !hackableComp.IsHacked);
+ public CompEquippable GunCompEq => gun.TryGetComp();
+ public virtual LocalTargetInfo CurrentTarget => currentTargetInt;
+ private bool WarmingUp => burstWarmupTicksLeft > 0;
+ public virtual Verb AttackVerb => GunCompEq.PrimaryVerb;
+ public bool IsMannable => mannableComp != null;
+ private bool PlayerControlled => (base.Faction == Faction.OfPlayer || MannedByColonist) && !MannedByNonColonist && !IsActivable;
+ protected virtual bool CanSetForcedTarget => mannableComp != null && PlayerControlled;
+ private bool CanToggleHoldFire => PlayerControlled;
+ private bool IsMortar => def.building.IsMortar;
+ private bool IsMortarOrProjectileFliesOverhead => AttackVerb.ProjectileFliesOverhead() || IsMortar;
+ private bool IsActivable => interactableComp != null;
+ protected virtual bool HideForceTargetGizmo => false;
+ public TurretTop Top => top;
+ private bool CanExtractShell => PlayerControlled && (gun.TryGetComp()?.Loaded ?? false);
+ private bool MannedByColonist => mannableComp != null && mannableComp.ManningPawn != null && mannableComp.ManningPawn.Faction == Faction.OfPlayer;
+ private bool MannedByNonColonist => mannableComp != null && mannableComp.ManningPawn != null && mannableComp.ManningPawn.Faction != Faction.OfPlayer;
+ public CompLaunchable LaunchableComp => cachedLaunchableComp ?? (cachedLaunchableComp = GetComp());
+ public CompTransporter TransporterComp => cachedTransporterComp ?? (cachedTransporterComp = GetComp());
+ public CompShuttle ShuttleComp => cachedShuttleComp ?? (cachedShuttleComp = GetComp());
+ public string RenamableLabel { get => shuttleName ?? BaseLabel; set => shuttleName = value; }
+ public string BaseLabel => def.LabelCap;
+ public string InspectLabel => RenamableLabel;
+ public override string Label => RenamableLabel;
+ public float FuelLevel => refuelableComp.Fuel;
+ public float MaxFuelLevel => refuelableComp.Props.fuelCapacity;
+ Thing IAttackTargetSearcher.Thing => this;
+
+ // --- CONSTRUCTOR ---
+ public Building_ArmedShuttle()
+ {
+ top = new TurretTop(this);
+ }
+
+ // --- METHODS ---
+ public override void SpawnSetup(Map map, bool respawningAfterLoad)
+ {
+ base.SpawnSetup(map, respawningAfterLoad);
+ dormantComp = GetComp();
+ initiatableComp = GetComp();
+ powerComp = GetComp();
+ mannableComp = GetComp();
+ interactableComp = GetComp();
+ refuelableComp = GetComp();
+ powerCellComp = GetComp();
+ hackableComp = GetComp();
+ if (!respawningAfterLoad)
+ {
+ top.SetRotationFromOrientation();
+ ShuttleComp.shipParent.Start();
+ }
+ }
+
+ public override void PostMake()
+ {
+ base.PostMake();
+ burstCooldownTicksLeft = def.building.turretInitialCooldownTime.SecondsToTicks();
+ MakeGun();
+ }
+
+ public override void DeSpawn(DestroyMode mode = DestroyMode.Vanish)
+ {
+ base.DeSpawn(mode);
+ ResetCurrentTarget();
+ progressBarEffecter?.Cleanup();
+ }
+
+ public override void ExposeData()
+ {
+ base.ExposeData();
+ Scribe_TargetInfo.Look(ref forcedTarget, "forcedTarget");
+ Scribe_TargetInfo.Look(ref lastAttackedTarget, "lastAttackedTarget");
+ Scribe_Values.Look(ref lastAttackTargetTick, "lastAttackTargetTick", 0);
+ Scribe_Values.Look(ref burstCooldownTicksLeft, "burstCooldownTicksLeft", 0);
+ Scribe_Values.Look(ref burstWarmupTicksLeft, "burstWarmupTicksLeft", 0);
+ Scribe_TargetInfo.Look(ref currentTargetInt, "currentTarget");
+ Scribe_Values.Look(ref holdFire, "holdFire", defaultValue: false);
+ Scribe_Values.Look(ref burstActivated, "burstActivated", defaultValue: false);
+ Scribe_Deep.Look(ref gun, "gun");
+ Scribe_Values.Look(ref shuttleName, "shuttleName");
+ if (Scribe.mode == LoadSaveMode.PostLoadInit)
+ {
+ if (gun == null)
+ {
+ Log.Error("Turret had null gun after loading. Recreating.");
+ MakeGun();
+ }
+ else
+ {
+ UpdateGunVerbs();
+ }
+ }
+ }
+
+ protected override void Tick()
+ {
+ base.Tick();
+ if (forcedTarget.HasThing && (!forcedTarget.Thing.Spawned || !base.Spawned || forcedTarget.Thing.Map != base.Map))
+ {
+ forcedTarget = LocalTargetInfo.Invalid;
+ }
+ if (CanExtractShell && MannedByColonist)
+ {
+ CompChangeableProjectile compChangeableProjectile = gun.TryGetComp();
+ if (!compChangeableProjectile.allowedShellsSettings.AllowedToAccept(compChangeableProjectile.LoadedShell))
+ {
+ ExtractShell();
+ }
+ }
+ if (forcedTarget.IsValid && !CanSetForcedTarget) ResetForcedTarget();
+ if (!CanToggleHoldFire) holdFire = false;
+ if (forcedTarget.ThingDestroyed) ResetForcedTarget();
+
+ if (Active && (mannableComp == null || mannableComp.MannedNow) && !IsStunned && base.Spawned)
+ {
+ GunCompEq.verbTracker.VerbsTick();
+ if (AttackVerb.state != VerbState.Bursting)
+ {
+ burstActivated = false;
+ if (WarmingUp)
+ {
+ burstWarmupTicksLeft--;
+ if (burstWarmupTicksLeft <= 0) BeginBurst();
+ }
+ else
+ {
+ if (burstCooldownTicksLeft > 0)
+ {
+ burstCooldownTicksLeft--;
+ if (IsMortar)
+ {
+ if (progressBarEffecter == null) progressBarEffecter = EffecterDefOf.ProgressBar.Spawn();
+ progressBarEffecter.EffectTick(this, TargetInfo.Invalid);
+ MoteProgressBar mote = ((SubEffecter_ProgressBar)progressBarEffecter.children[0]).mote;
+ mote.progress = 1f - (float)Mathf.Max(burstCooldownTicksLeft, 0) / (float)BurstCooldownTime().SecondsToTicks();
+ mote.offsetZ = -0.8f;
+ }
+ }
+ if (burstCooldownTicksLeft <= 0 && this.IsHashIntervalTick(15))
+ {
+ TryStartShootSomething(canBeginBurstImmediately: true);
+ }
+ }
+ }
+ top.TurretTopTick();
+ }
+ else
+ {
+ ResetCurrentTarget();
+ }
+ }
+
+ public override IEnumerable GetGizmos()
+ {
+ foreach (Gizmo gizmo in base.GetGizmos()) yield return gizmo;
+ if (CanExtractShell)
+ {
+ CompChangeableProjectile compChangeableProjectile = gun.TryGetComp();
+ Command_Action command_Action = new Command_Action();
+ command_Action.defaultLabel = "CommandExtractShell".Translate();
+ command_Action.defaultDesc = "CommandExtractShellDesc".Translate();
+ command_Action.icon = compChangeableProjectile.LoadedShell.uiIcon;
+ command_Action.iconAngle = compChangeableProjectile.LoadedShell.uiIconAngle;
+ command_Action.iconOffset = compChangeableProjectile.LoadedShell.uiIconOffset;
+ command_Action.iconDrawScale = GenUI.IconDrawScale(compChangeableProjectile.LoadedShell);
+ command_Action.action = delegate { ExtractShell(); };
+ yield return command_Action;
+ }
+ CompChangeableProjectile compChangeableProjectile2 = gun.TryGetComp();
+ if (compChangeableProjectile2 != null)
+ {
+ foreach (Gizmo item in StorageSettingsClipboard.CopyPasteGizmosFor(compChangeableProjectile2.GetStoreSettings()))
+ {
+ yield return item;
+ }
+ }
+ if (!HideForceTargetGizmo)
+ {
+ if (CanSetForcedTarget)
+ {
+ Command_VerbTarget command_VerbTarget = new Command_VerbTarget();
+ command_VerbTarget.defaultLabel = "CommandSetForceAttackTarget".Translate();
+ command_VerbTarget.defaultDesc = "CommandSetForceAttackTargetDesc".Translate();
+ command_VerbTarget.icon = ContentFinder.Get("UI/Commands/Attack");
+ command_VerbTarget.verb = AttackVerb;
+ command_VerbTarget.hotKey = KeyBindingDefOf.Misc4;
+ command_VerbTarget.drawRadius = false;
+ command_VerbTarget.requiresAvailableVerb = false;
+ if (base.Spawned && IsMortarOrProjectileFliesOverhead && base.Position.Roofed(base.Map))
+ {
+ command_VerbTarget.Disable("CannotFire".Translate() + ": " + "Roofed".Translate().CapitalizeFirst());
+ }
+ yield return command_VerbTarget;
+ }
+ if (forcedTarget.IsValid)
+ {
+ Command_Action command_Action2 = new Command_Action();
+ command_Action2.defaultLabel = "CommandStopForceAttack".Translate();
+ command_Action2.defaultDesc = "CommandStopForceAttackDesc".Translate();
+ command_Action2.icon = ContentFinder.Get("UI/Commands/Halt");
+ command_Action2.action = delegate
+ {
+ ResetForcedTarget();
+ SoundDefOf.Tick_Low.PlayOneShotOnCamera();
+ };
+ if (!forcedTarget.IsValid)
+ {
+ command_Action2.Disable("CommandStopAttackFailNotForceAttacking".Translate());
+ }
+ command_Action2.hotKey = KeyBindingDefOf.Misc5;
+ yield return command_Action2;
+ }
+ }
+ if (CanToggleHoldFire)
+ {
+ Command_Toggle command_Toggle = new Command_Toggle();
+ command_Toggle.defaultLabel = "CommandHoldFire".Translate();
+ command_Toggle.defaultDesc = "CommandHoldFireDesc".Translate();
+ command_Toggle.icon = ContentFinder.Get("UI/Commands/HoldFire");
+ command_Toggle.hotKey = KeyBindingDefOf.Misc6;
+ command_Toggle.toggleAction = delegate
+ {
+ holdFire = !holdFire;
+ if (holdFire) ResetForcedTarget();
+ };
+ command_Toggle.isActive = () => holdFire;
+ yield return command_Toggle;
+ }
+ foreach (Gizmo gizmo in ShuttleComp.CompGetGizmosExtra()) yield return gizmo;
+ foreach (Gizmo gizmo in LaunchableComp.CompGetGizmosExtra()) yield return gizmo;
+ foreach (Gizmo gizmo in TransporterComp.CompGetGizmosExtra()) yield return gizmo;
+ float fuelInShuttle = FuelInShuttle();
+ string text = null;
+ if (fuelInShuttle <= 0f) text = "NoFuelInShuttle".Translate();
+ if (Mathf.Approximately(FuelLevel, MaxFuelLevel)) text = "ShuttleFullyFueled".Translate();
+ Command_Action refuelAction = new Command_Action();
+ refuelAction.defaultLabel = "CommandRefuelShuttleFromCargo".Translate();
+ refuelAction.defaultDesc = "CommandRefuelShuttleFromCargoDesc".Translate();
+ refuelAction.icon = RefuelFromCargoIcon.Texture;
+ refuelAction.action = delegate
+ {
+ int to = Mathf.FloorToInt(Mathf.Min(fuelInShuttle, MaxFuelLevel - FuelLevel));
+ Dialog_Slider window = new Dialog_Slider((int val) => "RefuelShuttleCount".Translate(val), 1, to, delegate(int count)
+ {
+ ConsumeFuelFromInventory(count);
+ refuelableComp.Refuel(count);
+ });
+ Find.WindowStack.Add(window);
+ };
+ refuelAction.Disabled = !text.NullOrEmpty();
+ refuelAction.disabledReason = text;
+ yield return refuelAction;
+ }
+
+ public void OrderAttack(LocalTargetInfo targ)
+ {
+ if (!targ.IsValid)
+ {
+ if (forcedTarget.IsValid) ResetForcedTarget();
+ return;
+ }
+ if ((targ.Cell - base.Position).LengthHorizontal < AttackVerb.verbProps.EffectiveMinRange(targ, this))
+ {
+ Messages.Message("MessageTargetBelowMinimumRange".Translate(), this, MessageTypeDefOf.RejectInput, historical: false);
+ return;
+ }
+ if ((targ.Cell - base.Position).LengthHorizontal > AttackVerb.EffectiveRange)
+ {
+ Messages.Message("MessageTargetBeyondMaximumRange".Translate(), this, MessageTypeDefOf.RejectInput, historical: false);
+ return;
+ }
+ if (forcedTarget != targ)
+ {
+ forcedTarget = targ;
+ if (burstCooldownTicksLeft <= 0) TryStartShootSomething(canBeginBurstImmediately: false);
+ }
+ if (holdFire)
+ {
+ Messages.Message("MessageTurretWontFireBecauseHoldFire".Translate(def.label), this, MessageTypeDefOf.RejectInput, historical: false);
+ }
+ }
+
+ public bool ThreatDisabled(IAttackTargetSearcher disabledFor)
+ {
+ if (!IsEverThreat) return true;
+ if (powerComp != null && !powerComp.PowerOn) return true;
+ if (mannableComp != null && !mannableComp.MannedNow) return true;
+ if (dormantComp != null && !dormantComp.Awake) return true;
+ if (initiatableComp != null && !initiatableComp.Initiated) return true;
+ if (powerCellComp != null && powerCellComp.depleted) return true;
+ if (hackableComp != null && hackableComp.IsHacked) return true;
+ return false;
+ }
+
+ protected void OnAttackedTarget(LocalTargetInfo target)
+ {
+ lastAttackTargetTick = Find.TickManager.TicksGame;
+ lastAttackedTarget = target;
+ }
+
+ public void TryStartShootSomething(bool canBeginBurstImmediately)
+ {
+ if (progressBarEffecter != null)
+ {
+ progressBarEffecter.Cleanup();
+ progressBarEffecter = null;
+ }
+ if (!base.Spawned || (holdFire && CanToggleHoldFire) || (AttackVerb.ProjectileFliesOverhead() && base.Map.roofGrid.Roofed(base.Position)) || !AttackVerb.Available())
+ {
+ ResetCurrentTarget();
+ return;
+ }
+ bool wasValid = currentTargetInt.IsValid;
+ currentTargetInt = forcedTarget.IsValid ? forcedTarget : TryFindNewTarget();
+ if (!wasValid && currentTargetInt.IsValid && def.building.playTargetAcquiredSound)
+ {
+ SoundDefOf.TurretAcquireTarget.PlayOneShot(new TargetInfo(base.Position, base.Map));
+ }
+ if (currentTargetInt.IsValid)
+ {
+ float warmupTime = def.building.turretBurstWarmupTime.RandomInRange;
+ if (warmupTime > 0f)
+ {
+ burstWarmupTicksLeft = warmupTime.SecondsToTicks();
+ }
+ else if (canBeginBurstImmediately)
+ {
+ BeginBurst();
+ }
+ else
+ {
+ burstWarmupTicksLeft = 1;
+ }
+ }
+ else
+ {
+ ResetCurrentTarget();
+ }
+ }
+
+ public virtual LocalTargetInfo TryFindNewTarget()
+ {
+ IAttackTargetSearcher searcher = this;
+ Faction faction = searcher.Thing.Faction;
+ float range = AttackVerb.EffectiveRange;
+ if (Rand.Value < 0.5f && AttackVerb.ProjectileFliesOverhead() && faction.HostileTo(Faction.OfPlayer))
+ {
+ if (base.Map.listerBuildings.allBuildingsColonist.Where(delegate(Building x)
+ {
+ float minRange = AttackVerb.verbProps.EffectiveMinRange(x, this);
+ float distSq = x.Position.DistanceToSquared(base.Position);
+ return distSq > minRange * minRange && distSq < range * range;
+ }).TryRandomElement(out Building result))
+ {
+ return result;
+ }
+ }
+ TargetScanFlags flags = TargetScanFlags.NeedThreat | TargetScanFlags.NeedAutoTargetable;
+ if (!AttackVerb.ProjectileFliesOverhead())
+ {
+ flags |= TargetScanFlags.NeedLOSToAll | TargetScanFlags.LOSBlockableByGas;
+ }
+ if (AttackVerb.IsIncendiary_Ranged())
+ {
+ flags |= TargetScanFlags.NeedNonBurning;
+ }
+ if (IsMortar)
+ {
+ flags |= TargetScanFlags.NeedNotUnderThickRoof;
+ }
+ return (Thing)AttackTargetFinder.BestShootTargetFromCurrentPosition(searcher, flags, IsValidTarget);
+ }
+
+ private IAttackTargetSearcher TargSearcher() => (mannableComp != null && mannableComp.MannedNow) ? (IAttackTargetSearcher)mannableComp.ManningPawn : this;
+
+ private bool IsValidTarget(Thing t)
+ {
+ if (t is Pawn pawn)
+ {
+ if (base.Faction == Faction.OfPlayer && pawn.IsPrisoner) return false;
+ if (AttackVerb.ProjectileFliesOverhead())
+ {
+ RoofDef roof = base.Map.roofGrid.RoofAt(t.Position);
+ if (roof != null && roof.isThickRoof) return false;
+ }
+ if (mannableComp == null) return !GenAI.MachinesLike(base.Faction, pawn);
+ if (pawn.RaceProps.Animal && pawn.Faction == Faction.OfPlayer) return false;
+ }
+ return true;
+ }
+
+ protected virtual void BeginBurst()
+ {
+ AttackVerb.TryStartCastOn(CurrentTarget);
+ OnAttackedTarget(CurrentTarget);
+ }
+
+ protected void BurstComplete()
+ {
+ burstCooldownTicksLeft = BurstCooldownTime().SecondsToTicks();
+ }
+
+ protected float BurstCooldownTime() => (def.building.turretBurstCooldownTime >= 0f) ? def.building.turretBurstCooldownTime : AttackVerb.verbProps.defaultCooldownTime;
+
+ public override string GetInspectString()
+ {
+ StringBuilder sb = new StringBuilder(base.GetInspectString());
+ if (AttackVerb.verbProps.minRange > 0f)
+ {
+ sb.AppendLine("MinimumRange".Translate() + ": " + AttackVerb.verbProps.minRange.ToString("F0"));
+ }
+ if (base.Spawned && IsMortarOrProjectileFliesOverhead && base.Position.Roofed(base.Map))
+ {
+ sb.AppendLine("CannotFire".Translate() + ": " + "Roofed".Translate().CapitalizeFirst());
+ }
+ else if (base.Spawned && burstCooldownTicksLeft > 0 && BurstCooldownTime() > 5f)
+ {
+ sb.AppendLine("CanFireIn".Translate() + ": " + burstCooldownTicksLeft.ToStringSecondsFromTicks());
+ }
+ CompChangeableProjectile changeable = gun.TryGetComp();
+ if (changeable != null)
+ {
+ sb.AppendLine(changeable.Loaded ? "ShellLoaded".Translate(changeable.LoadedShell.LabelCap, changeable.LoadedShell) : "ShellNotLoaded".Translate());
+ }
+ return sb.ToString().TrimEndNewlines();
+ }
+
+ protected override void DrawAt(Vector3 drawLoc, bool flip = false)
+ {
+ top.DrawTurret();
+ base.DrawAt(drawLoc, flip);
+ }
+
+ public override void DrawExtraSelectionOverlays()
+ {
+ base.DrawExtraSelectionOverlays();
+ float range = AttackVerb.EffectiveRange;
+ if (range < 90f) GenDraw.DrawRadiusRing(base.Position, range);
+ float minRange = AttackVerb.verbProps.EffectiveMinRange(allowAdjacentShot: true);
+ if (minRange < 90f && minRange > 0.1f) GenDraw.DrawRadiusRing(base.Position, minRange);
+ if (WarmingUp)
+ {
+ int degrees = (int)(burstWarmupTicksLeft * 0.5f);
+ GenDraw.DrawAimPie(this, CurrentTarget, degrees, (float)def.size.x * 0.5f);
+ }
+ if (forcedTarget.IsValid && (!forcedTarget.HasThing || forcedTarget.Thing.Spawned))
+ {
+ Vector3 b = forcedTarget.HasThing ? forcedTarget.Thing.TrueCenter() : forcedTarget.Cell.ToVector3Shifted();
+ Vector3 a = this.TrueCenter();
+ b.y = a.y = AltitudeLayer.MetaOverlays.AltitudeFor();
+ GenDraw.DrawLineBetween(a, b, MaterialPool.MatFrom(GenDraw.LineTexPath, ShaderDatabase.Transparent, new Color(1f, 0.5f, 0.5f)));
+ }
+ }
+
+ private void ExtractShell() => GenPlace.TryPlaceThing(gun.TryGetComp().RemoveShell(), base.Position, base.Map, ThingPlaceMode.Near);
+
+ private void ResetForcedTarget()
+ {
+ forcedTarget = LocalTargetInfo.Invalid;
+ burstWarmupTicksLeft = 0;
+ if (burstCooldownTicksLeft <= 0) TryStartShootSomething(canBeginBurstImmediately: false);
+ }
+
+ private void ResetCurrentTarget()
+ {
+ currentTargetInt = LocalTargetInfo.Invalid;
+ burstWarmupTicksLeft = 0;
+ }
+
+ public void MakeGun()
+ {
+ gun = ThingMaker.MakeThing(def.building.turretGunDef);
+ UpdateGunVerbs();
+ }
+
+ private void UpdateGunVerbs()
+ {
+ List allVerbs = gun.TryGetComp().AllVerbs;
+ for (int i = 0; i < allVerbs.Count; i++)
+ {
+ allVerbs[i].caster = this;
+ allVerbs[i].castCompleteCallback = BurstComplete;
+ }
+ }
+
+ private float FuelInShuttle()
+ {
+ float num = 0f;
+ foreach (Thing item in (IEnumerable)TransporterComp.innerContainer)
+ {
+ if (refuelableComp.Props.fuelFilter.Allows(item))
+ {
+ num += (float)item.stackCount;
+ }
+ }
+ return num;
+ }
+
+ private void ConsumeFuelFromInventory(int fuelAmount)
+ {
+ tmpContainedThings.Clear();
+ tmpContainedThings.AddRange(TransporterComp.innerContainer);
+ int num = fuelAmount;
+ int num2 = tmpContainedThings.Count - 1;
+ while (num2 >= 0)
+ {
+ Thing thing = tmpContainedThings[num2];
+ if (refuelableComp.Props.fuelFilter.Allows(thing))
+ {
+ Thing thing2 = thing.SplitOff(Mathf.Min(num, thing.stackCount));
+ num -= thing2.stackCount;
+ }
+ if (num > 0) num2--;
+ else break;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/WulaFallenEmpire/WulaFallenEmpire.csproj b/Source/WulaFallenEmpire/WulaFallenEmpire.csproj
index e75c9522..c19c1cff 100644
--- a/Source/WulaFallenEmpire/WulaFallenEmpire.csproj
+++ b/Source/WulaFallenEmpire/WulaFallenEmpire.csproj
@@ -1,5 +1,6 @@
-
+
Debug
@@ -166,13 +167,16 @@
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file