diff --git a/About/About.xml b/About/About.xml index 9a4e1d1..27ce32f 100755 --- a/About/About.xml +++ b/About/About.xml @@ -1,8 +1,8 @@ - A2B_Core v0.12.1 + A2B_Core v0.13.0 noone, asarium, FredrikLH, TehJoE, bigmap001, 1000101 - Alpha 12d + Alpha 13 http://bit.ly/1EdE0Xc - This mod adds conveyor belts to Rimworld. Now you can efficiently transport goods to the most remote locations of your encampment. + This mod adds conveyor belts to Rimworld. Now you can efficiently transport goods to the most remote locations of your encampment. \ No newline at end of file diff --git a/Assemblies/A2B.dll b/Assemblies/A2B.dll index 964d76a..723efda 100755 Binary files a/Assemblies/A2B.dll and b/Assemblies/A2B.dll differ diff --git a/Defs/A2BDataDefs/A2BData.xml b/Defs/A2BDataDefs/A2BData.xml index d7e79ff..a35b8f0 100644 --- a/Defs/A2BDataDefs/A2BData.xml +++ b/Defs/A2BDataDefs/A2BData.xml @@ -1,6 +1,6 @@ - - - - - A2BCore - - - 0.12.1 - - - 100 - - - 180 - -90 - - -
  • A2B_BeltSpeed
  • -
    - - - -10.0 - -20.0 - - -
  • A2B_Climatization
  • -
    - - - 0.05 - -0.025 - - -
  • A2B_Durability
  • -
    - - - 5.0 - 5.0 - 0.20 - 0.20 - - -
  • A2B_Reliability
  • -
    - -
    + + + + A2BCore + + + 0.13.0 + + + 100 + + + 0.1 + 10 + + + 180 + -90 + + +
  • A2B_BeltSpeed
  • +
    + + + -10.0 + -20.0 + + +
  • A2B_Climatization
  • +
    + + + 0.03 + -0.015 + + +
  • A2B_Durability
  • +
    + + + 5.0 + 5.0 + 0.20 + 0.20 + + +
  • A2B_Reliability
  • +
    + +
    \ No newline at end of file diff --git a/Defs/DesignationDefs/A2B_Designations.xml b/Defs/DesignationDefs/A2B_Designations.xml index 41b2f0d..ee1e900 100644 --- a/Defs/DesignationDefs/A2B_Designations.xml +++ b/Defs/DesignationDefs/A2B_Designations.xml @@ -5,7 +5,7 @@ A2BUndercoverCoverToggleDesignation Designations/Flick Thing - true + true \ No newline at end of file diff --git a/Defs/DesignatorDefs/A2B_Designators.xml b/Defs/DesignatorDefs/A2B_Designators.xml deleted file mode 100644 index fe033d0..0000000 --- a/Defs/DesignatorDefs/A2B_Designators.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - A2BUndercoverCoverToggleDesignator - A2B.Designator_ToggleUndercoverCover - ConveyorBelts - - - \ No newline at end of file diff --git a/Defs/ThingDefs/Building_A2B.xml b/Defs/ThingDefs/Building_A2B.xml index cb20a72..4f10936 100755 --- a/Defs/ThingDefs/Building_A2B.xml +++ b/Defs/ThingDefs/Building_A2B.xml @@ -26,13 +26,27 @@ 1 0.34 + +
  • +
  • + + Normal ConveyorBelts - A2B - + + +
  • A2B
  • + + + + + MinifiedFurniture + + + A2BBelt A2B.Building_ConveyorBelt @@ -48,7 +62,7 @@ The basic unit of a conveyor belt. -
  • +
  • CompPowerTrader 10 PowerOnSmall @@ -56,7 +70,7 @@ true true
  • -
  • +
  • CompGlower 1 (255,200,0,0) @@ -67,13 +81,14 @@ (1,1) - 15 + 10 + 1 B - 1 + 1 - + A2BCurve Building @@ -89,7 +104,7 @@ The basic unit of a conveyor belt, only curved. -
  • +
  • CompPowerTrader 10 PowerOnSmall @@ -97,7 +112,7 @@ true true
  • -
  • +
  • CompGlower 1 (255,200,0,0) @@ -108,12 +123,13 @@ (1,1) - 15 + 10 + 1 - V + V - + A2BLoader Building_Storage @@ -150,7 +166,7 @@ Loads any item you want onto a belt. -
  • +
  • CompPowerTrader 10 PowerOnSmall @@ -158,7 +174,7 @@ true true
  • -
  • +
  • CompGlower 1 (255,200,0,0) @@ -168,14 +184,15 @@
  • - 15 + 10 + 1 true L - Item + Item
    - + A2BUnloader Building @@ -191,7 +208,7 @@ Unload the content of a conveyor belt into a pile or onto a hopper. -
  • +
  • CompPowerTrader 10 PowerOnSmall @@ -199,7 +216,7 @@ true true
  • -
  • +
  • CompGlower 1 (255,200,0,0) @@ -210,12 +227,13 @@ (1,1) - 15 + 10 + 1 - U + U - + A2BSplitter Building @@ -231,7 +249,7 @@ Sends items left, forward, and right in sequence. -
  • +
  • CompPowerTrader 15 PowerOnSmall @@ -239,7 +257,7 @@ true true
  • -
  • +
  • CompGlower 1 (255,200,0,0) @@ -250,12 +268,13 @@ (1,1) - 20 + 10 + 2 - P + P - + A2BMerger Building @@ -271,7 +290,7 @@ Merges up to three belts into one. -
  • +
  • CompPowerTrader 15 PowerOnSmall @@ -279,7 +298,7 @@ true true
  • -
  • +
  • CompGlower 1 (255,200,0,0) @@ -290,7 +309,8 @@ (1,1) - 20 + 10 + 1 M diff --git a/Defs/ThingDefs/Building_A2B2.xml b/Defs/ThingDefs/Building_A2B2.xml index afa95cf..876c72c 100644 --- a/Defs/ThingDefs/Building_A2B2.xml +++ b/Defs/ThingDefs/Building_A2B2.xml @@ -1,38 +1,17 @@ - - Building - - BulletImpactSteel - - true - true - - Light - ConstructMetal - Repair - false - false - true - BuildingRubble - - Waist - PassThroughOnly - 1000 - - MapMeshAndRealTime - true - 1 - 0.34 - - Normal + + +
  • A2B2
  • + +
    - ConveyorBelts - A2B2 + + MinifiedFurniture - + A2BSelector Building_Storage @@ -51,7 +30,7 @@ Sort and redirect items. -
  • +
  • CompPowerTrader 25 PowerOnSmall @@ -59,7 +38,7 @@ true true
  • -
  • +
  • CompGlower 1 (255,200,0,0) @@ -70,12 +49,13 @@ (1,1) - 25 + 10 + 2 H - + A2BUndercover Building @@ -114,13 +94,14 @@ (1,1) - 25 + 20 + 1 1 G - + A2BUndercoverCover Building @@ -161,7 +142,7 @@ None - + A2BUndertaker Building @@ -185,12 +166,13 @@ (1,1) - 50 + 20 + 1 R - + A2BLift Building @@ -208,7 +190,7 @@ Connects surface and underground belts. -
  • +
  • CompPowerTrader 50 PowerOnSmall @@ -216,7 +198,7 @@ true true
  • -
  • +
  • CompGlower 3 (255,58,0,0) @@ -227,12 +209,13 @@ (1,1) - 50 + 20 + 1 None - + A2BSlide Building @@ -256,7 +239,8 @@ (1,1) - 50 + 20 + 1 None diff --git a/Languages/English/Keyed/A2B.xml b/Languages/English/Keyed/A2B.xml index 9840faa..8216909 100755 --- a/Languages/English/Keyed/A2B.xml +++ b/Languages/English/Keyed/A2B.xml @@ -34,4 +34,7 @@ South West + Output {0}\nto ground + Allow selector output\n{0} to ground + \ No newline at end of file diff --git a/Source/A2B/A2B.csproj b/Source/A2B/A2B.csproj index 43251b9..6179bb4 100755 --- a/Source/A2B/A2B.csproj +++ b/Source/A2B/A2B.csproj @@ -12,7 +12,7 @@ v3.5 512 - 0.12.1.0 + 0.12.2.0 none diff --git a/Source/A2B/A2B.sln b/Source/A2B/A2B.sln index 6160723..30a46b3 100755 --- a/Source/A2B/A2B.sln +++ b/Source/A2B/A2B.sln @@ -23,7 +23,7 @@ Global $1.inheritsSet = VisualStudio $1.inheritsScope = text/plain $1.scope = text/plain - version = 0.12.1.0 + version = 0.12.2.0 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Source/A2B/A2B.userprefs b/Source/A2B/A2B.userprefs index 63a3c0b..45c1a17 100644 --- a/Source/A2B/A2B.userprefs +++ b/Source/A2B/A2B.userprefs @@ -1,15 +1,22 @@  - - - - - - - - + - + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Source/A2B/Buildings/Building_ConveyorBelt.cs b/Source/A2B/Buildings/Building_ConveyorBelt.cs index 546b749..76534a2 100644 --- a/Source/A2B/Buildings/Building_ConveyorBelt.cs +++ b/Source/A2B/Buildings/Building_ConveyorBelt.cs @@ -40,8 +40,8 @@ public override void Tick() { base.Tick(); - CompPowerTrader power = GetComp(); - BeltComponent belt = GetComp(); + //CompPowerTrader power = GetComp(); + //BeltComponent belt = GetComp(); if (Graphic.GetType() == typeof(AnimatedGraphic)) { diff --git a/Source/A2B/Components/BeltComponent.cs b/Source/A2B/Components/BeltComponent.cs index 626eee8..974ef8d 100644 --- a/Source/A2B/Components/BeltComponent.cs +++ b/Source/A2B/Components/BeltComponent.cs @@ -121,8 +121,12 @@ public virtual IntVec3 GetDestinationForThing([NotNull] Thing thing) public virtual bool CanAcceptFrom( BeltComponent belt, bool onlyCheckConnection = false ) { // If I can't accept from anyone, I certainly can't accept from you. - if( !onlyCheckConnection && !CanAcceptSomething() ) - return false; + /* + if( !onlyCheckConnection ) + foreach( var thing in belt.ItemContainer.ThingsToMove ) + if( !CanAcceptThing( thing ) ) + return false; + */ // This belt isn't on the other belts output level if( belt.OutputLevel != this.InputLevel ) @@ -152,16 +156,39 @@ public virtual bool CanAcceptFrom(Rot4 direction) * Returns whether the component can accept items at all. Useful for locking/disabling components without * messing with directional routing code. **/ - public virtual bool CanAcceptSomething() + public virtual bool CanAcceptThing( Thing thing ) { - return (Empty && BeltPhase == Phase.Active); + if( BeltPhase != Phase.Active ) + return false; + if( Empty ) + return true; + foreach( var item in ItemContainer.ThingStatus ) + { + if( + ( item.Status == MovementStatus.WaitClear )&& + ( item.Thing.def == thing.def )&& + ( item.Thing.stackCount < item.Thing.def.stackLimit ) + ) + { + item.Status = MovementStatus.WaitMerge; + return true; + } + if( + ( item.Status == MovementStatus.WaitMerge )&& + ( item.Merge == thing ) + ) + { + return true; + } + } + return false; } /** * Returns whether the component is allowed to output to anything other than a belt. Most can not - the unloader * is an example of one that can, however. **/ - public virtual bool CanOutputToNonBelt() + public virtual bool CanOutputToNonBelt( IntVec3 beltDest, Thing thing ) { return false; } @@ -171,17 +198,17 @@ protected virtual void MoveThingTo([NotNull] Thing thing, IntVec3 beltDest) { OnBeginMove(thing, beltDest); - if( CanOutputToNonBelt() && beltDest.NoStorageBlockersIn( thing ) ) + if( CanOutputToNonBelt( beltDest, thing ) && beltDest.NoStorageBlockersIn( thing ) ) { ItemContainer.DropItem(thing, beltDest); } else if ( OutputLevel == Level.Surface ) { // Find a belt component at our output level - var belt = beltDest.GetBeltComponent(); + var belt = beltDest.GetBeltSurfaceComponent(); - // Check if there is a belt, if it is empty, and also check if it is active ! - if (belt == null || !belt.ItemContainer.Empty || belt.BeltPhase != Phase.Active) + // Check if there is a belt, if it can accept this thing + if( belt == null || !belt.CanAcceptThing( thing ) ) { return; } @@ -197,6 +224,7 @@ protected virtual void MoveThingTo([NotNull] Thing thing, IntVec3 beltDest) #region Drawing Stuff + /* protected static void DrawGUIOverlay([NotNull] ThingStatus status, Vector3 drawPos ) { if( Find.CameraMap.CurrentZoom != CameraZoomRange.Closest ) @@ -227,7 +255,7 @@ protected static void DrawGUIOverlay([NotNull] ThingStatus status, Vector3 drawP labelText, Color.white ); - } + }*/ protected virtual Vector3 GetOffset([NotNull] ThingStatus status) { @@ -279,11 +307,11 @@ protected virtual Vector3 GetOffset([NotNull] ThingStatus status) #region Callbacks (Core) - public override void PostDestroy(DestroyMode mode = DestroyMode.Vanish) + public override void PostDeSpawn () { ItemContainer.Destroy(); - base.PostDestroy(mode); + base.PostDeSpawn(); } public override void PostSpawnSetup() @@ -321,7 +349,7 @@ public override void PostDraw() status.Thing.DrawAt(drawPos); - DrawGUIOverlay(status, drawPos); + //DrawGUIOverlay(status, drawPos); } } @@ -329,10 +357,10 @@ public override void CompTick() { base.CompTick(); - if ((Find.TickManager.TicksGame + GetHashCode()) % 250 == 0) + if( parent.IsHashIntervalTick( 250 ) ) ItemContainer.TickRare(); - if ((Find.TickManager.TicksGame + GetHashCode()) % A2BData.OccasionalTicks == 0) + if( parent.IsHashIntervalTick( A2BData.OccasionalTicks ) ) OnOccasionalTick(); if (BeltPhase == Phase.Frozen && Rand.Range(0.0f, 1.0f) < 0.05) @@ -417,7 +445,7 @@ private void DoBeltTick() // Turn on, incl. 'system online' glow _beltPhase = Phase.Active; if( GlowerComponent != null ) - GlowerComponent.Lit = true; + GlowerComponent.UpdateLit(); // If it's an underground belt, don't auto-pickup // as it has lost it's targeting vector @@ -448,9 +476,9 @@ private void DoBeltTick() // Active 'yellow' color if( GlowerComponent != null ) - GlowerComponent.Lit = true; // in principle not required (should be already ON ...) + GlowerComponent.UpdateLit(); // in principle not required (should be already ON ...) - ItemContainer.Tick(); + ItemContainer.MoveTick(); PostItemContainerTick(); @@ -480,7 +508,7 @@ private void DoBeltTick() // Turn glower off if( GlowerComponent != null ) - GlowerComponent.Lit = false; + GlowerComponent.UpdateLit(); // Phase, inactive _beltPhase = Phase.Offline; @@ -496,6 +524,29 @@ private void DoBeltTick() } } + public virtual bool MovingThings() + { + return ItemContainer.MovingThings(); + } + + #region Power Stuff + + public virtual bool AllowLowPowerMode() + { + return true; + } + + public virtual float GetBasePowerConsumption() + { + if( PowerComponent == null ) + { + return 0f; + } + return PowerComponent.Props.basePowerConsumption; + } + + #endregion + #region Reliability Stuff public virtual void DoJamCheck() @@ -557,7 +608,7 @@ public override string CompInspectStringExtra() return statusText + "\n" + Constants.TxtContents.Translate() - + " " + ((IThingContainerOwner) ItemContainer).GetContainer().ContentsString; + + " " + ItemContainer.ContentsString; } } } diff --git a/Source/A2B/Components/BeltItemContainer.cs b/Source/A2B/Components/BeltItemContainer.cs index 265303a..1dad3c8 100644 --- a/Source/A2B/Components/BeltItemContainer.cs +++ b/Source/A2B/Components/BeltItemContainer.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text; using A2B.Annotations; using RimWorld; using Verse; @@ -12,25 +13,81 @@ namespace A2B { - public class ThingStatus + public enum MovementStatus { - public ThingStatus([NotNull] Thing thing, int counter) + Undefined, + Moving, + WaitClear, + WaitMerge, + MergingIn, + MergingOut + } + + public class ThingStatus : IExposable + { + public ThingStatus([NotNull] Thing thing, int counter, MovementStatus status) { Thing = thing; Counter = counter; + Status = status; + Merge = null; + } + + public ThingStatus() + { + Thing = null; + Counter = 0; + Status = MovementStatus.Undefined; + Merge = null; + } + + public void ExposeData() + { + var t = Thing; + var c = Counter; + var s = Status; + var m = Merge; + Scribe_Values.LookValue( ref c, "counter" ); + Scribe_Values.LookValue( ref s, "status" ); + Scribe_References.LookReference( ref m, "merge" ); + Scribe_Deep.LookDeep( ref t, "thing" ); + Thing = t; + Counter = c; + Status = s; + Merge = m; + } + + public bool IsWaiting + { + get + { + if( + ( Status == MovementStatus.WaitClear )|| + ( Status == MovementStatus.WaitMerge ) + ) + return true; + return false; + } } [NotNull] public Thing Thing { get; private set; } - public int Counter { get; private set; } + public int Counter { get; set; } + + public MovementStatus Status { get; set; } + + public Thing Merge { get; set; } + } public class BeltItemContainer : IExposable, IThingContainerOwner { private readonly BeltComponent _parentComponent; - private readonly Dictionary _thingCounter; + private List _thingStatus; + + //private readonly Dictionary _thingCount; private ThingContainer _container; @@ -40,7 +97,7 @@ static BeltItemContainer() } private static int cycle = 0; - public static bool DoAtmosphereEffects() + public static bool DoAtmosphereEffects( object target ) { int cells = (int) (Find.Map.Area * 0.0006f); @@ -49,7 +106,7 @@ public static bool DoAtmosphereEffects() cycle = 0; var cell = MapCellsInRandomOrder.Get(cycle++); - var belt = cell.GetBeltComponent(); + var belt = cell.GetBeltSurfaceComponent(); if (belt != null) { belt.ItemContainer.AtmosphereEffectsTick(); @@ -63,30 +120,37 @@ public BeltItemContainer([NotNull] BeltComponent component) { _parentComponent = component; + _thingStatus = new List(); + _container = new ThingContainer(this); - _thingCounter = new Dictionary(); + + //_thingCount = new Dictionary(); } [NotNull] public IEnumerable Contents { - get { return _container; } + //get { return _container; } + get { return _thingStatus.Select( s => s.Thing ).ToList(); } } [NotNull] public IEnumerable ThingsToMove { - get { return _thingCounter.Where(pair => pair.Value >= TicksToMove).Select(pair => pair.Key).ToList(); } + //get { return _thingCount.Where(pair => pair.Value >= TicksToMove).Select(pair => pair.Key).ToList(); } + get { return _thingStatus.Where( s => ( !s.IsWaiting )&&( s.Counter >= TicksToMove ) ).Select( s => s.Thing ).ToList(); } } public bool WorkToDo { - get { return _thingCounter.Any(pair => pair.Value >= TicksToMove); } + //get { return _thingCount.Any(pair => pair.Value >= TicksToMove); } + get { return _thingStatus.Any( s => !s.IsWaiting ); } } public bool Empty { - get { return _container.Count < 1 ; } + //get { return _container.Count < 1 ; } + get { return _thingStatus.Count < 1; } } public int TicksToMove { @@ -96,13 +160,25 @@ public bool Empty [NotNull] public IEnumerable ThingStatus { - get { return _container.Select(thing => new ThingStatus(thing, _thingCounter[thing])); } + //get { return _container.Select(thing => new ThingStatus(thing, _thingCount[thing])); } + get { return _thingStatus; } } #region Saveable Members public void ExposeData() { + Scribe_Collections.LookList( ref _thingStatus, "contents", LookMode.Deep ); + + if( + ( Scribe.mode == LoadSaveMode.LoadingVars )&& + ( _thingStatus == null ) + ) + { + _thingStatus = new List(); + } + + /* Scribe_Deep.LookDeep(ref _container, "container"); if (Scribe.mode == LoadSaveMode.LoadingVars) @@ -110,7 +186,7 @@ public void ExposeData() Dictionary counterDictionary = null; Scribe_Fixed.LookDictionary(ref counterDictionary, "thingCounter", LookMode.Value); - _thingCounter.Clear(); + _thingCount.Clear(); if (counterDictionary != null) { foreach (var pair in counterDictionary) @@ -119,23 +195,45 @@ public void ExposeData() if (thing != null) { - _thingCounter.Add(thing, pair.Value); + _thingCount.Add(thing, pair.Value); } } } } else if (Scribe.mode == LoadSaveMode.Saving) { - var counterDictionary = _thingCounter.ToDictionary(pair => pair.Key.ThingID, pair => pair.Value); + var counterDictionary = _thingCount.ToDictionary(pair => pair.Key.ThingID, pair => pair.Value); Scribe_Fixed.LookDictionary(ref counterDictionary, "thingCounter", LookMode.Value); } + */ } #endregion #region ThingContainerOwner Members + public string ContentsString + { + get + { + if( _thingStatus.Count == 0 ) + return "NothingLower".Translate(); + StringBuilder stringBuilder = new StringBuilder(); + for( int index = 0; index < _thingStatus.Count; ++index ) + { + if( index != 0 ) + stringBuilder.Append( ", " ); + stringBuilder.Append( _thingStatus[ index ].Thing.Label ); + #if DEBUG + stringBuilder.Append( " " + _thingStatus[ index ].Status ); + stringBuilder.Append( " " + _thingStatus[ index ].Counter ); + #endif + } + return stringBuilder.ToString(); + } + } + [NotNull] ThingContainer IThingContainerOwner.GetContainer() { @@ -147,15 +245,183 @@ IntVec3 IThingContainerOwner.GetPosition() { return _parentComponent.parent.PositionHeld; } + + [NotNull] + bool IThingContainerOwner.Spawned + { + get + { + return this._parentComponent.parent.Spawned; + } + } + #endregion - public void Tick() + public bool MovingThings() + { + return _thingStatus.Count( s => !s.IsWaiting ) > 0 ; + } + + public void MoveTick() { - _container.ThingContainerTick(); + if( _parentComponent.AllowLowPowerMode() ) + { + if( _parentComponent.MovingThings() ) + { + _parentComponent.PowerComponent.PowerOutput = -_parentComponent.GetBasePowerConsumption(); + } + else + { + _parentComponent.PowerComponent.PowerOutput = -_parentComponent.GetBasePowerConsumption() * A2BData.LowPowerFactor; + } + } + + foreach( var thingStatus in _thingStatus.Where(ShouldIncreaseCounter)) + { + thingStatus.Counter++; + } + + if( !_parentComponent.parent.IsHashIntervalTick( 30 ) ) + { + // Only do a status update twice per second + return; + } - foreach (var thing in Contents.Where(ShouldIncreaseCounter)) + for( int index = _thingStatus.Count - 1; index >= 0; --index ) { - _thingCounter[thing]++; + var thingStatus = _thingStatus[ index ]; + + if( thingStatus.Status == MovementStatus.Moving ) + { + // Item it moving + if( thingStatus.Counter >= TicksToMove / 2 ) + { + // It's at least half way, get destination for item + var destination = _parentComponent.GetDestinationForThing( thingStatus.Thing ); + var belt = destination.GetBeltComponent( _parentComponent ); + if( + ( + ( belt != null )&& + ( !belt.Empty ) + )|| + ( + ( belt == null )&& + ( + ( !_parentComponent.CanOutputToNonBelt( destination, thingStatus.Thing ) )|| + ( !destination.NoStorageBlockersIn( thingStatus.Thing ) ) + ) + ) + ) + { + // Belt isn't empty or no belt and can't output to non-belt or non-belt is blocked + thingStatus.Status = MovementStatus.WaitClear; + } + } + } + else if( thingStatus.Status == MovementStatus.MergingIn ) + { + var mergeStatus = GetWaitMerge(); + if( + ( mergeStatus != null )&& + ( mergeStatus.Merge == thingStatus.Thing )&& + ( thingStatus.Merge == mergeStatus.Thing ) + ) + { + // Merging item into existing stack when it reaches it + if( thingStatus.Counter >= mergeStatus.Counter ) + { + // Try absorb incoming stack + if( mergeStatus.Thing.TryAbsorbStack( thingStatus.Thing, true ) ) + { + // Merged stacks, change status of waiting stack + mergeStatus.Merge = null; + mergeStatus.Status = MovementStatus.WaitClear; + _thingStatus.Remove( thingStatus ); + break; + } + // Make the left-overs(!?) wait + thingStatus.Merge = null; + thingStatus.Status = MovementStatus.WaitClear; + } + } + else + { + // Could find merge wait? + Log.Error( string.Format( "Merge source {0} ({1}) does not match merge target {2} ({3})!", thingStatus.Thing.ThingID, thingStatus.Merge?.ThingID, mergeStatus.Thing.ThingID, mergeStatus.Merge?.ThingID ) ); + thingStatus.Status = MovementStatus.WaitClear; + } + } + else if( thingStatus.Status == MovementStatus.WaitClear ) + { + if( !MovingThings() ) + { + // Nothing moving on this belt + + // Get destination for thing + var destination = _parentComponent.GetDestinationForThing( thingStatus.Thing ); + var belt = destination.GetBeltComponent( _parentComponent ); + if( + ( belt != null )&& + ( belt.CanAcceptThing( thingStatus.Thing ) ) + ) + { + if( belt.Empty ) + { + // Next belt is ready + thingStatus.Status = MovementStatus.Moving; + } + else + { + var mergeStatus = belt.ItemContainer.GetWaitMerge(); + if( + ( mergeStatus.Merge == null )&& + ( belt.ItemContainer.GetWantedDef() == thingStatus.Thing.def ) + ) + { + // Merge this with the next belts stack + + var count = belt.ItemContainer.GetWantedCount(); + if( thingStatus.Thing.stackCount > count ) + { + // Split the off the amount needed + var newStack = thingStatus.Thing.SplitOff( count ); + if( newStack != null ) + { + AddItem( newStack, thingStatus.Counter, MovementStatus.MergingOut, mergeStatus.Thing ); + mergeStatus.Merge = newStack; + } + } + else + { + // Merge whole stack + mergeStatus.Merge = thingStatus.Thing; + thingStatus.Merge = mergeStatus.Thing; + thingStatus.Status = MovementStatus.MergingOut; + } + break; + } + } + } + else if( + ( belt == null )&& + ( _parentComponent.CanOutputToNonBelt( destination, thingStatus.Thing ) )&& + ( destination.NoStorageBlockersIn( thingStatus.Thing ) ) + ) + { + // Dumping on floor which is now clear + thingStatus.Status = MovementStatus.Moving; + } + } + } + else if( thingStatus.Status == MovementStatus.WaitMerge ) + { + // Waiting for something to come in + if( thingStatus.Merge == null ) + { + // No source for merge??? + thingStatus.Status = MovementStatus.WaitClear; + } + } } } @@ -167,80 +433,153 @@ public void AtmosphereEffectsTick() } } + #region Tickers for items on the belt + + public void Tick() + { + for( int index = _thingStatus.Count - 1; index >= 0; --index ) + { + if( _thingStatus[ index ].Thing.def.tickerType == TickerType.Normal ) + _thingStatus[ index ].Thing.Tick(); + } + } + // We still want items to rot while sitting on the belts, but ThingContainer doesn't call // TickRare on its contents, which is where the rotting mechanic takes place. public void TickRare() { - foreach (var thing in Contents.ToList()) { - if (thing.def.tickerType == TickerType.Rare) - thing.TickRare(); + for( int index = _thingStatus.Count - 1; index >= 0; --index ) + { + if( _thingStatus[ index ].Thing.def.tickerType == TickerType.Rare ) + _thingStatus[ index ].Thing.TickRare(); } } - private bool ShouldIncreaseCounter([NotNull] Thing thing) + #endregion + + public ThingStatus GetStatusForThing( [NotNull] Thing thing ) { - var currentCounter = _thingCounter[thing]; - if (currentCounter < TicksToMove / 2 )//&& !_parentComponent.IsReceiver()) + foreach( var thingStatus in _thingStatus ) { - // Always increase the counter until half the belt speed is reached - return true; + if( thingStatus.Thing == thing ) + return thingStatus; } + return null; + } - // Never go above 100% - if (currentCounter >= TicksToMove) + private ThingStatus GetWaitMerge() + { + foreach( var thingStatus in _thingStatus ) { - return false; + if( thingStatus.Status == MovementStatus.WaitMerge ) + { + return thingStatus; + } } + return null; + } - var destination = _parentComponent.GetDestinationForThing(thing); - BeltComponent belt = null; + private int GetWantedCount() + { + if( _thingStatus.Count < 1 ) + { + return 0; + } + var thingStatus = GetWaitMerge(); + if( thingStatus != null ) + { + return thingStatus.Thing.def.stackLimit - thingStatus.Thing.stackCount; + } + return 0; + } - if( _parentComponent.OutputLevel == Level.Surface ) + private ThingDef GetWantedDef() + { + if( _thingStatus.Count < 1 ) + { + return null; + } + var thingStatus = GetWaitMerge(); + if( thingStatus != null ) { - // Surface belts get the fast treatment + return thingStatus.Thing.def; + } + return null; + } - belt = destination.GetBeltComponent(); + private bool ShouldIncreaseCounter([NotNull] ThingStatus thingStatus) + { + if( thingStatus.IsWaiting ) + { + return false; + } - } else { - // Underground belts need more robust checking to handle strange configurations - var belts = destination.GetBeltUndergroundComponents(); + if( thingStatus.Counter < TicksToMove / 2 )//&& !_parentComponent.IsReceiver()) + { + // Always increase the counter until half the belt speed is reached + return true; + } - // Scan for prefered belt (lift) otherwise continue underground - if( ( belts != null )&& - ( belts.Count > 0 ) ) - { + // Never go above 100% + if( thingStatus.Counter >= TicksToMove ) + { + return false; + } - var p = _parentComponent as BeltUndergroundComponent; - var lift = belts.Find( b => b.IsLift() && b.CanAcceptFrom( _parentComponent ) ); - var under = belts.Find( b => b.IsUndercover() ); - if( ( lift != null )&& - ( ( lift.BeltPhase == Phase.Active )|| - ( under == null ) ) ) - belt = lift; - else - belt = under; - - } + if( thingStatus.Status == MovementStatus.MergingIn ) + { + // Keep moving item until it merges with it's target + return true; } + // Get destination for thing + var destination = _parentComponent.GetDestinationForThing( thingStatus.Thing ); + var belt = destination.GetBeltComponent( _parentComponent ); + // If no belt items, then move things only if this can output to non-belts if (belt == null) { - return (_parentComponent.CanOutputToNonBelt() && destination.NoStorageBlockersIn(thing)); + return (_parentComponent.CanOutputToNonBelt( destination, thingStatus.Thing ) && destination.NoStorageBlockersIn( thingStatus.Thing )); } // There is a belt, only move things if it can accept them from us - return belt.CanAcceptFrom(_parentComponent); + if( !belt.CanAcceptFrom( _parentComponent, true ) ) + return false; + + // An empty belt accepts all + if( belt.Empty ) + return true; + + // Only move the stack if the next belt wants it + ThingDef wantedDef = belt.ItemContainer.GetWantedDef(); + return wantedDef == thingStatus.Thing.def; } - public bool AddItem([NotNull] Thing t, int initialCounter = 0) + public bool AddItem([NotNull] Thing t, int initialCounter = 0, MovementStatus initialStatus = MovementStatus.Moving, Thing mergeTarget = null ) { + var thingStatus = new ThingStatus( t, initialCounter, initialStatus ); + if( thingStatus == null ) + return false; + + thingStatus.Merge = mergeTarget; + _thingStatus.Add( thingStatus ); + + SlotGroupUtility.Notify_TakingThing( t ); + if( t.Spawned ) + t.DeSpawn(); + if( t.HasAttachment(ThingDefOf.Fire ) ) + t.GetAttachment(ThingDefOf.Fire ).Destroy( DestroyMode.Vanish ); + + t.holder = _container; + + /* if (!_container.TryAdd(t)) { return false; } - _thingCounter[t] = initialCounter; + _thingCount[t] = initialCounter; + */ return true; } @@ -249,14 +588,54 @@ public void TransferItem([NotNull] Thing item, [NotNull] BeltItemContainer other if (!_parentComponent.PreItemTransfer(item, other._parentComponent)) return; - _container.Remove(item); - _thingCounter.Remove(item); - other.AddItem(item); + var thingStatus = GetStatusForThing( item ); + _thingStatus.Remove( thingStatus ); + + if( thingStatus.Status == MovementStatus.MergingOut ) + { + thingStatus.Status = MovementStatus.MergingIn; + } + other.AddItem( item, 0, thingStatus.Status, thingStatus.Merge ); _parentComponent.OnItemTransfer(item, other._parentComponent); other._parentComponent.OnItemReceived(item, _parentComponent); } + private bool TryDrop( ThingStatus thingStatus, IntVec3 dropLoc, ThingPlaceMode mode, out Thing lastResultingThing ) + { + lastResultingThing = null; + if( + ( thingStatus == null )|| + ( thingStatus.Thing == null ) + ) + { + return false; + } + if( thingStatus.Merge != null ) + { + // Make sure any items which are merging release their lock on their target + var belt = thingStatus.Merge.PositionHeld.GetBeltComponent( this._parentComponent ); + if( belt == null ) + { + Log.Error( string.Format( "Can not find belt for merge target {0}!", thingStatus.Merge ) ); + } + else + { + var mergeStatus = belt.ItemContainer.GetStatusForThing( thingStatus.Merge ); + if( mergeStatus == null ) + { + Log.Error( string.Format( "Can not get ThingStatus for merge target {0} from belt {1}!", thingStatus.Merge, belt.parent ) ); + } + else + { + mergeStatus.Merge = null; + mergeStatus.Status = MovementStatus.WaitClear; + } + } + } + return GenDrop.TryDropSpawn( thingStatus.Thing, dropLoc, mode, out lastResultingThing ); + } + public void DropItem([NotNull] Thing item, IntVec3 position, bool forced = false) { var backupSound = item.def.soundDrop; @@ -265,40 +644,54 @@ public void DropItem([NotNull] Thing item, IntVec3 position, bool forced = false { item.def.soundDrop = null; - if (!_container.Contains(item)) + var thingStatus = GetStatusForThing( item ); + if( thingStatus == null ) + { + Log.Message( string.Format( "Unable to find ThingStatus for {0} in {1}!", item, _parentComponent.parent ) ); return; + } Thing droppedItem; - if (!_container.TryDrop(item, position, ThingPlaceMode.Direct, out droppedItem) && !forced) - { - return; + if( !forced ) + { + if( !TryDrop( thingStatus, position, ThingPlaceMode.Direct, out droppedItem ) ) + { + Log.Message( string.Format( "Unable to drop {0} at {1}!", thingStatus.Thing, position ) ); + } } // Might prevent those "has null owner" errors... - else if (forced && _container.Contains(item) && !_container.TryDrop(item, position, ThingPlaceMode.Near, out droppedItem)) + else if( !TryDrop( thingStatus, position, ThingPlaceMode.Near, out droppedItem ) ) { - _container.Remove(item); - item.holder = null; - return; + Log.Message( string.Format( "Unable to drop {0} near {1}!", thingStatus.Thing, position ) ); } + _thingStatus.Remove( thingStatus ); - // Play the sound as that isn't handled by the ThingContainer anymore... - if (backupSound != null) + if( droppedItem == null ) { - backupSound.PlayOneShot(position); + return; } - _thingCounter.Remove(item); - item.holder = null; + droppedItem.holder = null; - if (droppedItem is ThingWithComps) + // Play the drop sound + if( backupSound != null ) { - droppedItem.SetForbidden(false); + backupSound.PlayOneShot( position ); + droppedItem.def.soundDrop = backupSound; } - if (droppedItem.def.defName.Contains("Chunk") && Find.DesignationManager.DesignationOn(droppedItem, DesignationDefOf.Haul) == null) + if( droppedItem is ThingWithComps ) + { + droppedItem.SetForbidden( false ); + } + + if( + ( droppedItem.def.defName.Contains( "Chunk" ) )&& + ( Find.DesignationManager.DesignationOn( droppedItem, DesignationDefOf.Haul ) == null ) + ) { // If this is a chunk AND not already haulable ("designated twice" warning) make it haulable - Find.DesignationManager.AddDesignation(new Designation(droppedItem, DesignationDefOf.Haul)); + Find.DesignationManager.AddDesignation( new Designation( droppedItem, DesignationDefOf.Haul ) ); } } finally @@ -311,18 +704,27 @@ public void DropItem([NotNull] Thing item, IntVec3 position, bool forced = false public void DropAll(IntVec3 position, bool forced = false) { // Check if there is anything on the belt: yes? -> make it accessible to colonists + for( int index = _thingStatus.Count - 1; index >= 0; index-- ) + { + var thingStatus = _thingStatus[ index ]; + DropItem( thingStatus.Thing, position, forced ); + } + if( _thingStatus.Count > 0 ) + Log.Error( "A2B: Tried DropAll but items remain!" ); + /* foreach (var thing in _container.ToList()) { DropItem(thing, position, forced); } - _thingCounter.Clear(); + _thingCount.Clear(); + */ } public void Destroy() { DropAll(_parentComponent.parent.Position, true); - _container.ClearAndDestroyContents(); + //_container.ClearAndDestroyContents(); } } } diff --git a/Source/A2B/Components/BeltLiftComponent.cs b/Source/A2B/Components/BeltLiftComponent.cs index bf9f2cf..4f88911 100644 --- a/Source/A2B/Components/BeltLiftComponent.cs +++ b/Source/A2B/Components/BeltLiftComponent.cs @@ -42,6 +42,37 @@ public override void PostExposeData() } } + public override float GetBasePowerConsumption() + { + if( PowerComponent == null ) + { + return 0f; + } + // Powered lifts use additional power based + // on how many components it's driving + return PowerComponent.Props.basePowerConsumption + poweredCount * A2BData.PowerPerUndercover; + } + + public override bool MovingThings() + { + // Is the lift itself moving an item? + if( ItemContainer.MovingThings() ) + { + return true; + } + if( poweredBelts.NullOrEmpty() ) + return false; + + // Check all the undercovers the lift is pulling, don't include slides in the scan + foreach( var undercover in poweredBelts.FindAll( b => b is BeltUndercoverComponent ) ) + { + if( undercover.MovingThings() ) + { + return true; + } + } + return false; + } public override void OnItemTransfer(Thing item, BeltComponent other) { @@ -65,10 +96,10 @@ protected override void MoveThingTo([NotNull] Thing thing, IntVec3 beltDest) OnBeginMove(thing, beltDest); // Lifts only output to surface - var belt = beltDest.GetBeltComponent(); + var belt = beltDest.GetBeltSurfaceComponent(); - // Check if there is a belt, if it is empty, and also check if it is active ! - if (belt == null || !belt.ItemContainer.Empty || belt.BeltPhase != Phase.Active) + // Check if there is a belt, if it can accept this thing + if( belt == null || !belt.CanAcceptThing( thing ) ) { return; } diff --git a/Source/A2B/Components/BeltLoaderComponent.cs b/Source/A2B/Components/BeltLoaderComponent.cs index b384436..f1aad1d 100644 --- a/Source/A2B/Components/BeltLoaderComponent.cs +++ b/Source/A2B/Components/BeltLoaderComponent.cs @@ -55,7 +55,7 @@ public override void Jam() /** * Belt loaders never accept items from other belt components. **/ - public override bool CanAcceptSomething() + public override bool CanAcceptThing( Thing thing ) { return false; } @@ -65,25 +65,46 @@ protected override void PostItemContainerTick() // Check the things that are on the ground at our position // If the thing can be moved and the destination is empty it can be added to our container // This should fix the "pawn carry items to the loader all the time"-bug - foreach (var thing in Find.ThingGrid.ThingsAt(parent.Position)) + var thingsAt = Find.ThingGrid.ThingsAt( parent.Position ).ToList(); + for( var index = thingsAt.Count - 1; index >= 0; index-- ) { - if ((thing.def.category == ThingCategory.Item) && (thing != parent)) + var thing = thingsAt[ index ]; + if( + ( thing.def.category == ThingCategory.Item )&& + ( thing != parent ) + ) { - var destination = GetDestinationForThing(thing); - var destBelt = destination.GetBeltComponent(); + var destination = GetDestinationForThing( thing ); + var destBelt = destination.GetBeltSurfaceComponent(); - if (destBelt == null) + if( destBelt == null ) { continue; } // Do not load items unless the next element can accept it - if (!destBelt.CanAcceptFrom(this)) + if( !destBelt.CanAcceptFrom( this ) ) { continue; } - ItemContainer.AddItem(thing, BeltSpeed / 2); + // Try to merge with existing stacks + bool fullyMerged = false; + foreach( var thingStatus in ItemContainer.ThingStatus.Where( status => status.IsWaiting ) ) + { + if( thingStatus.Thing.def == thing.def ) + { + if( thingStatus.Thing.TryAbsorbStack( thing, true ) ) + { + fullyMerged = true; + break; + } + } + } + + // If not fully merged, add the remaining stack + if( !fullyMerged ) + ItemContainer.AddItem( thing, BeltSpeed / 2 ); } } } diff --git a/Source/A2B/Components/BeltSelectorComponent.cs b/Source/A2B/Components/BeltSelectorComponent.cs index 6bbe020..8070a7d 100644 --- a/Source/A2B/Components/BeltSelectorComponent.cs +++ b/Source/A2B/Components/BeltSelectorComponent.cs @@ -1,98 +1,285 @@ using System; +using System.Collections.Generic; + using RimWorld; using Verse; +using UnityEngine; namespace A2B { public class BeltSelectorComponent : BeltComponent { - protected Rot4 nextDest = Rot4.West; + protected ISlotGroupParent slotParent; + protected bool hasStorageSettings; protected string _mythingID; - protected IntVec3 _splitterDest; + + protected Rot4[] inputVectors; + protected Rot4[] outputOneVectors; + protected Rot4[] outputTwoVectors; + + protected bool allowOutputOneToGround = false; + protected bool allowOutputTwoToGround = false; + + protected IntVec3[] inputPos; + protected IntVec3[] outputOnePos; + protected IntVec3[] outputTwoPos; + + protected IntVec3 _lastOnePosition; + protected IntVec3 _lastTwoPosition; public override void PostExposeData() { base.PostExposeData(); Scribe_Values.LookValue(ref hasStorageSettings, "hasStorageSettings"); + Scribe_Values.LookValue(ref allowOutputOneToGround, "allowOutputOneToGround"); + Scribe_Values.LookValue(ref allowOutputTwoToGround, "allowOutputTwoToGround"); } public override void PostSpawnSetup() { base.PostSpawnSetup(); - ISlotGroupParent slotParent = parent as ISlotGroupParent; - if (slotParent == null) + GetIOVectors(); + + inputPos = RelativeRotationToPosition( inputVectors ); + outputOnePos = RelativeRotationToPosition( outputOneVectors ); + outputTwoPos = RelativeRotationToPosition( outputTwoVectors ); + + _lastOnePosition = outputOnePos[ 0 ]; + _lastTwoPosition = outputTwoPos[ 0 ]; + + slotParent = parent as ISlotGroupParent; + if( slotParent == null ) { throw new InvalidOperationException("parent is not a SlotGroupParent!"); } // we kinda want to not overwrite custom storage settings every save/load... - if (!hasStorageSettings) - slotParent.GetStoreSettings().filter.SetDisallowAll(); + if( !hasStorageSettings ) + { + // First disallow all + slotParent.GetStoreSettings().filter.SetDisallowAll(); + foreach( var outputOne in outputOnePos ) + { // Copy from output one vectors if available + var slotGroup = outputOne.GetSlotGroup(); + if( slotGroup != null ) + { + slotParent.GetStoreSettings().CopyFrom( slotGroup.Settings ); + allowOutputOneToGround = true; + break; + } + } + } hasStorageSettings = true; } - public override IntVec3 GetDestinationForThing(Thing thing) + private IntVec3[] RelativeRotationToPosition( Rot4[] vectors ) { - // Test the 'selection' idea ... - ISlotGroupParent slotParent = parent as ISlotGroupParent; - if (slotParent == null) + int count = vectors.GetLength( 0 ); + var dests = new IntVec3[ count ]; + for( int index = 0; index < count; ++index ) { - throw new InvalidOperationException("parent is not a SlotGroupParent!"); + dests[ index ] = this.GetPositionFromRelativeRotation( vectors[ index ] ); } - - var selectionSettings = slotParent.GetStoreSettings(); - if (selectionSettings.AllowedToAccept(thing)) - return this.GetPositionFromRelativeRotation(Rot4.North); + return dests; + } + + public virtual void GetIOVectors() + { + inputVectors = new Rot4[]{ Rot4.South }; + outputOneVectors = new Rot4[]{ Rot4.North }; + outputTwoVectors = new Rot4[]{ Rot4.West, Rot4.East }; + } - // A list of destinations - indexing modulo 2 lets us cycle them and avoid - // long chains of if-statements. - IntVec3[] dests = { - this.GetPositionFromRelativeRotation(Rot4.West), - this.GetPositionFromRelativeRotation(Rot4.East) - }; + private bool CanOutputToSlotGroup( IntVec3 beltDest, Thing thing ) + { + var slotGroup = beltDest.GetSlotGroup(); + if( slotGroup == null ) + return true; + return slotGroup.Settings.AllowedToAccept( thing ); + } + + public override bool CanOutputToNonBelt( IntVec3 beltDest, Thing thing ) + { + if( allowOutputOneToGround ) + for( int index = 0; index < outputOnePos.GetLength( 0 ); ++index ) + if( beltDest == outputOnePos[ index ] ) + return CanOutputToSlotGroup( beltDest, thing ); + if( allowOutputTwoToGround ) + for( int index = 0; index < outputTwoPos.GetLength( 0 ); ++index ) + if( beltDest == outputTwoPos[ index ] ) + return CanOutputToSlotGroup( beltDest, thing ); + return false; + } - // Determine where we are going in the destination list (and default to left) - int index = Math.Max(0, Array.FindIndex(dests, dir => (dir == _splitterDest))); + protected virtual IntVec3 GetGroundVector( Thing thing, IntVec3[] vectors, IntVec3 lastVector ) + { + int limit = vectors.GetLength( 0 ); - // Do we have a new item ? - if (_mythingID == thing.ThingID && IsFreeBelt(_splitterDest)) + // Same item? + if( + ( _mythingID == thing.ThingID )&& + ( lastVector.NoStorageBlockersIn( thing ) ) + ) { - return _splitterDest; + return lastVector; } - else - { - _mythingID = thing.ThingID; - // Try the next destination - index = (index + 1) % 2; - if (IsFreeBelt(dests[index])) + // New item + _mythingID = thing.ThingID; + + // Vector index + int index = Math.Max( 0, Array.FindIndex( vectors, dest => dest == lastVector ) ); + int lastIndex = index; + + // Try vectors until all have been checked + do + { + // Try next vector + index = ( index + 1 ) % limit; + if( vectors[ index ].NoStorageBlockersIn( thing ) ) { - _splitterDest = dests[index]; - return _splitterDest; + return vectors[ index ]; } + }while( index != lastIndex ); + + // Can't find a free one + return IntVec3.Invalid; + } + + protected virtual IntVec3 GetOutputVector( Thing thing, IntVec3[] vectors, IntVec3 lastVector, bool allowGround ) + { + int limit = vectors.GetLength( 0 ); + + // Same item? + if( + ( _mythingID == thing.ThingID )&& + ( IsFreeBelt( lastVector ) ) + ) + { + return lastVector; + } + + // New item + _mythingID = thing.ThingID; - // Try the one after that - index = (index + 1) % 2; - if (IsFreeBelt(dests[index])) + // Vector index + int index = Math.Max( 0, Array.FindIndex( vectors, dest => dest == lastVector ) ); + int lastIndex = index; + + // Try vectors until all have been checked + do + { + // Try next vector + index = ( index + 1 ) % limit; + if( IsFreeBelt( vectors[ index ] ) ) { - _splitterDest = dests[index]; - return _splitterDest; + return vectors[ index ]; } + }while( index != lastIndex ); + + // Can't find a free one + if( !allowGround ) + return IntVec3.Invalid; + + // Check the ground + return GetGroundVector( thing, vectors, lastVector ); + } - // Give up and use our current destination - return _splitterDest; + public override bool CanAcceptFrom( Rot4 direction ) + { + return Array.Exists( inputVectors, v => v == direction ); + } + + public override IntVec3 GetDestinationForThing(Thing thing) + { + // Test the 'selection' idea ... + if( slotParent == null ) + { + throw new InvalidOperationException("parent is not a SlotGroupParent!"); } + + IntVec3 destination; + + // Matches filter? + var selectionSettings = slotParent.GetStoreSettings(); + if( selectionSettings.AllowedToAccept( thing ) ) + { + // Send it to the next "1" output + destination = GetOutputVector( thing, outputOnePos, _lastOnePosition, allowOutputOneToGround ); + if( destination != IntVec3.Invalid ) + _lastOnePosition = destination; + return _lastOnePosition; + } + + // Doesn't match, send it to the next "2" output + destination = GetOutputVector( thing, outputTwoPos, _lastTwoPosition, allowOutputTwoToGround ); + if( destination != IntVec3.Invalid ) + _lastTwoPosition = destination; + return _lastTwoPosition; } - protected bool IsFreeBelt(IntVec3 position) + protected bool IsFreeBelt( IntVec3 position ) { - BeltComponent destBelt = position.GetBeltComponent(); - return (destBelt != null && destBelt.CanAcceptFrom(this)); + BeltComponent destBelt = position.GetBeltSurfaceComponent(); + return ( + ( destBelt != null )&& + ( destBelt.CanAcceptFrom( this ) )&& + ( destBelt.Empty ) + ); } + public override IEnumerable CompGetGizmosExtra() + { + // Show gizmo buttons to allow the player to allow outputing directly to the ground + Command_Action actionToggleOneToGround = new Command_Action(); + if( actionToggleOneToGround != null ) + { + if( allowOutputOneToGround ) + { + actionToggleOneToGround.icon = Constants.IconSelectorToGroundTrue; + } + else + { + actionToggleOneToGround.icon = Constants.IconSelectorToGroundFalse; + } + actionToggleOneToGround.defaultDesc = Constants.TxtSelectorToGroundToggleDescription.Translate( "1" ); + actionToggleOneToGround.defaultLabel = Constants.TxtSelectorToGroundToggle.Translate( "1" ); + actionToggleOneToGround.activateSound = Constants.ButtonClick; + actionToggleOneToGround.action = new Action( delegate() + { + allowOutputOneToGround = !allowOutputOneToGround; + } ); + } + yield return actionToggleOneToGround; + + Command_Action actionToggleTwoToGround = new Command_Action(); + if( actionToggleTwoToGround != null ) + { + if( allowOutputTwoToGround ) + { + actionToggleTwoToGround.icon = Constants.IconSelectorToGroundTrue; + } + else + { + actionToggleTwoToGround.icon = Constants.IconSelectorToGroundFalse; + } + actionToggleTwoToGround.defaultDesc = Constants.TxtSelectorToGroundToggleDescription.Translate( "2" ); + actionToggleTwoToGround.defaultLabel = Constants.TxtSelectorToGroundToggle.Translate( "2" ); + actionToggleTwoToGround.activateSound = Constants.ButtonClick; + actionToggleTwoToGround.action = new Action( delegate() + { + allowOutputTwoToGround = !allowOutputTwoToGround; + } ); + } + yield return actionToggleTwoToGround; + + // No more gizmos + yield break; + } + } } \ No newline at end of file diff --git a/Source/A2B/Components/BeltSlideComponent.cs b/Source/A2B/Components/BeltSlideComponent.cs index 67e75e5..459b239 100644 --- a/Source/A2B/Components/BeltSlideComponent.cs +++ b/Source/A2B/Components/BeltSlideComponent.cs @@ -28,6 +28,12 @@ public override void PostSpawnSetup() _outputLevel = Level.Underground; } + public override bool AllowLowPowerMode() + { + // Powered lifts will handle this for us + return false; + } + public override IntVec3 GetDestinationForThing( Thing thing ) { return this.GetPositionFromRelativeRotation( Rot4.North ); @@ -47,16 +53,21 @@ protected override void MoveThingTo([NotNull] Thing thing, IntVec3 beltDest) var belts = beltDest.GetBeltUndergroundComponents(); var lift = belts.Find( b => b.IsLift() && b.outputDirection == this.outputDirection ); var under = belts.Find( tuc => tuc.IsUndercover() ); - if( ( lift != null )&& - ( ( lift.BeltPhase == Phase.Active )|| - ( under == null ) ) ) - // Use the lift unless it's unpowered and there is an undercover + if( + ( lift != null )&& + ( + ( lift.BeltPhase == Phase.Active )|| + ( under == null ) + ) + ) + { // Use the lift unless it's unpowered and there is an undercover belt = lift; + } else belt = under; - // Check if there is a belt, if it is empty, and also check if it is active ! - if (belt == null || !belt.ItemContainer.Empty || belt.BeltPhase != Phase.Active) + // Check if there is a belt, if it can accept this thing + if( belt == null || !belt.CanAcceptThing( thing ) ) { return; } diff --git a/Source/A2B/Components/BeltSplitterComponent.cs b/Source/A2B/Components/BeltSplitterComponent.cs index f0a1ea6..b74b808 100644 --- a/Source/A2B/Components/BeltSplitterComponent.cs +++ b/Source/A2B/Components/BeltSplitterComponent.cs @@ -80,7 +80,7 @@ public override IntVec3 GetDestinationForThing(Thing thing) private bool IsFreeBelt(IntVec3 position, bool onlyCheckConnection = false) { - BeltComponent destBelt = position.GetBeltComponent(); + BeltComponent destBelt = position.GetBeltSurfaceComponent(); return (destBelt != null && destBelt.CanAcceptFrom(this, onlyCheckConnection)); } diff --git a/Source/A2B/Components/BeltUndercoverComponent.cs b/Source/A2B/Components/BeltUndercoverComponent.cs index 571bb6b..3bcb99e 100644 --- a/Source/A2B/Components/BeltUndercoverComponent.cs +++ b/Source/A2B/Components/BeltUndercoverComponent.cs @@ -51,6 +51,12 @@ public BeltUndercoverComponent () get { return ( Cover != null ); } } + public override bool AllowLowPowerMode() + { + // Powered lifts will handle this for us + return false; + } + public override IntVec3 GetDestinationForThing( Thing thing ) { // Prefer straight @@ -60,8 +66,12 @@ public override IntVec3 GetDestinationForThing( Thing thing ) public override bool CanAcceptFrom( BeltComponent belt, bool onlyCheckConnection = false ) { // If I can't accept from anyone, I certainly can't accept from you. - if( !onlyCheckConnection && !CanAcceptSomething() ) - return false; + /* + if( !onlyCheckConnection ) + foreach( var thing in belt.ItemContainer.ThingsToMove ) + if( !CanAcceptThing( thing ) ) + return false; + */ // This belt isn't on the other belts output level if( belt.OutputLevel != this.InputLevel ) @@ -90,15 +100,21 @@ protected override void MoveThingTo([NotNull] Thing thing, IntVec3 beltDest) var belts = beltDest.GetBeltUndergroundComponents(); var lift = belts.Find( b => b.IsLift() && b.outputDirection == this.outputDirection ); var under = belts.Find( u => u.IsUndercover() ); - if( ( lift != null )&& - ( ( lift.BeltPhase == Phase.Active )|| - ( under == null ) ) ) + if( + ( lift != null )&& + ( + ( lift.BeltPhase == Phase.Active )|| + ( under == null ) + ) + ) + { belt = lift; + } else belt = under; - // Check if there is a belt, if it is empty, and also check if it is active ! - if (belt == null || !belt.ItemContainer.Empty || belt.BeltPhase != Phase.Active) + // Check if there is a belt, if it can accept this thing + if( belt == null || !belt.CanAcceptThing( thing ) ) { return; } @@ -160,7 +176,7 @@ public override void PostDraw() Graphics.DrawMesh( MeshPool.plane10, thingMatrix, thingMat, 0 ); - DrawGUIOverlay( status, overlayPos ); + //DrawGUIOverlay( status, overlayPos ); } // Draw frame diff --git a/Source/A2B/Components/BeltUndercoverCover.cs b/Source/A2B/Components/BeltUndercoverCover.cs index 1ceee16..6f4cb29 100644 --- a/Source/A2B/Components/BeltUndercoverCover.cs +++ b/Source/A2B/Components/BeltUndercoverCover.cs @@ -42,14 +42,14 @@ public override void PostSpawnSetup() ParentBelt.CoverWasDestroyed = false; } - public override void PostDestroy( DestroyMode mode = DestroyMode.Vanish) + public override void PostDestroy( DestroyMode mode, bool wasSpawned ) { // If we were destroyed, tell our parent undercover belt if( ( mode == DestroyMode.Kill )&& ( ParentBelt != null ) ){ ParentBelt.CoverWasDestroyed = true; } - base.PostDestroy( mode ); + base.PostDestroy( mode, wasSpawned ); } } diff --git a/Source/A2B/Components/BeltUndergroundComponent.cs b/Source/A2B/Components/BeltUndergroundComponent.cs index 0c3118e..a66c6ab 100644 --- a/Source/A2B/Components/BeltUndergroundComponent.cs +++ b/Source/A2B/Components/BeltUndergroundComponent.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Linq; using A2B.Annotations; using RimWorld; using UnityEngine; @@ -71,13 +72,18 @@ public virtual bool CoverIsOn get { return true; } } + private bool gameJustLoaded = false; + // Belt component of actual power source + private string _headParentString = string.Empty; private Thing _headParent = null; private BeltUndergroundComponent _powerHead = null; public BeltUndergroundComponent PowerHead { get { return _powerHead; } set { // Only allow set if this belt doesn't have CompPowerTrader if( value == null ){ + _headParentString = string.Empty; + _beltPhase = Phase.Offline; _headParent = null; _powerHead = null; PowerComponent = null; @@ -87,6 +93,7 @@ public virtual bool CoverIsOn _powerHead = value; _headParent = _powerHead.parent; PowerComponent = _headParent.TryGetComp(); + _beltPhase = PowerComponent.PowerOn ? Phase.Active : Phase.Offline; } } } @@ -98,8 +105,8 @@ public virtual bool CoverIsOn protected List< BeltUndergroundComponent > poweredBelts = null; protected int poweredCount { // Make sure we only return undercover count, slides require a powered lift but do not draw power - get { return poweredBelts == null ? 0 : - poweredBelts.FindAll( b => ( ( b as BeltUndercoverComponent ) != null ) ).Count; } + get { return poweredBelts.NullOrEmpty() ? 0 : + poweredBelts.FindAll( b => b is BeltUndercoverComponent ).Count; } } @@ -110,16 +117,7 @@ public BeltUndergroundComponent() _outputLevel = Level.Underground; } - #region Infered Power Stuff - - public void RecomputerPower() - { - if( this.IsLift() ){ - // Powered lifts use additional power based - // on how many components it's driving - PowerComponent.PowerOutput = -( PowerComponent.props.basePowerConsumption + poweredCount * A2BData.powerPerUndercover ); - } - } + #region Power Stuff public bool RegisterInferedPowerComponent( BeltUndergroundComponent belt, Rot4 dir ) { @@ -137,15 +135,17 @@ public bool RegisterInferedPowerComponent( BeltUndergroundCompon if( belt.MultiVector == true ) belt.outputDirection = dir; - RecomputerPower(); return true; } public bool UnregisterInferedPowerComponent( BeltUndergroundComponent belt ) { - if( ( poweredBelts == null )|| - ( !poweredBelts.Contains( belt ) ) ){ + if( + ( poweredBelts.NullOrEmpty() )|| + ( !poweredBelts.Contains( belt ) ) + ) + { return false; } @@ -157,7 +157,6 @@ public bool UnregisterInferedPowerComponent( BeltUndergroundComp belt.outputDirection = Rot4.Invalid; // Unregistered - RecomputerPower(); return true; } @@ -290,6 +289,42 @@ public override void OnOccasionalTick() } } + public override void CompTick () + { + if( gameJustLoaded ) + { + if( _headParentString != string.Empty ) + { + if( PowerDirection == Rot4.Invalid ) + { + Log.Error( parent.ThingID + " :: PowerDirection is invalid!" ); + } + else + { + var headThing = Find.ListerThings.AllThings.FirstOrDefault( thing => thing.ThingID == _headParentString ); + if( headThing == null ) + { + Log.Error( parent.ThingID + " :: Unable to get Thing from ThingID " + _headParentString ); + } + else + { + var parentBelt = headThing.TryGetComp(); + if( parentBelt == null ) + { + Log.Error( parent.ThingID + " :: " + _headParentString + " is not a valid underground belt component!" ); + } + else + { + parentBelt.RegisterInferedPowerComponent( this, PowerDirection ); + } + } + } + } + gameJustLoaded = false; + } + base.CompTick(); + } + public override void OnItemTransfer(Thing item, BeltComponent other) { // Tell the undercover which direction the item came from @@ -322,37 +357,33 @@ public override void PostExposeData() if( this.IsLift() == false ){ Scribe_Values.LookValue( ref PowerDirection, "powerDirection", Rot4.Invalid, true ); - Scribe_References.LookReference( ref _headParent, "powerHead" ); + if( Scribe.mode == LoadSaveMode.Saving ) + _headParentString = _headParent.ThingID; - if( Scribe.mode == LoadSaveMode.PostLoadInit ){ - if( ( _headParent != null )&& - ( PowerDirection != Rot4.Invalid ) ){ - var p = _headParent.TryGetComp(); - if( p != null ){ - p.RegisterInferedPowerComponent( this, PowerDirection ); - } - } else { - PowerHead = null; - } + Scribe_Values.LookValue( ref _headParentString, "powerHead", string.Empty, true ); + + if( Scribe.mode == LoadSaveMode.ResolvingCrossRefs ) + { + gameJustLoaded = true; } } } - public override void PostDestroy(DestroyMode mode = DestroyMode.Vanish) + public override void PostDeSpawn() { ItemContainer.Destroy(); // Disconnect all infered power users - if( poweredBelts != null ) - foreach( var b in poweredBelts ) - UnregisterInferedPowerComponent( b ); + if( !poweredBelts.NullOrEmpty() ) + for( int index = poweredBelts.Count - 1; index >= 0; index-- ) + UnregisterInferedPowerComponent( poweredBelts[ index ] ); // Disconnect from power head if( ( PowerHead != null )&& ( PowerHead != this ) ) PowerHead.UnregisterInferedPowerComponent( this ); - base.PostDestroy(mode); + base.PostDeSpawn(); } public override string CompInspectStringExtra() diff --git a/Source/A2B/Components/BeltUndertakerComponent.cs b/Source/A2B/Components/BeltUndertakerComponent.cs index 8e8f1c5..39ed908 100644 --- a/Source/A2B/Components/BeltUndertakerComponent.cs +++ b/Source/A2B/Components/BeltUndertakerComponent.cs @@ -12,6 +12,18 @@ namespace A2B { + public struct UndertakerToggleData + { + public BeltUndertakerComponent target; + public bool forced; + + public UndertakerToggleData( BeltUndertakerComponent target, bool forced ) + { + this.target = target; + this.forced = forced; + } + } + [UsedImplicitly] public class BeltUndertakerComponent : BeltUndergroundComponent { @@ -33,7 +45,7 @@ public override void PostSpawnSetup() public override void PostExposeData() { base.PostExposeData(); - Scribe_Values.LookValue( ref forcedMode, "forcedMode" ); + Scribe_Values.LookValue( ref forcedMode, "forcedMode" ); } private bool modeReset() @@ -43,41 +55,16 @@ private bool modeReset() if( forcedMode ) return false; - // Get the top belt connection - BeltComponent topBelt = this.GetPositionFromRelativeRotation( Rot4.South ).GetBeltComponent(); + // Needs mode toggle, register for update + if( + ( !this.IsSlide() )&& + ( !this.IsLift() ) + ) + { + A2BMonitor.RegisterTickAction( this.parent.ThingID, UndertakerToggleMode, new UndertakerToggleData( this, false ) ); + return true; + } - if( topBelt != null ) - { - // We have a belt connected to the top - if( topBelt.CanAcceptFrom( this, true ) ) - { - // The top belt can accept from this belt, - // therefore this is a powered lift - if( !this.IsLift() ) - { - // Make sure it's a powered version - ChangeOperationalMode( UndertakerMode.PoweredLift ); - - // Force exit now to allow change - return true; - } - } - else - { - // This undertaker can't feed the top belt, assume the top - // belt feeds the undertaker and operate as a slide - if( !this.IsSlide() ) - { - // Slides don't use power and we don't want - // them to transmit to allow segmenting the - // power network used by the conveyors - ChangeOperationalMode( UndertakerMode.UnpoweredSlide ); - - // Force exit now to allow change - return true; - } - } - } return false; } @@ -95,11 +82,6 @@ public override void OnOccasionalTick() if( modeReset() ) return; - // Abort if still in config mode - if( ( !this.IsSlide() )&& - ( !this.IsLift() ) ) - return; - // Configured, now process base.OnOccasionalTick(); @@ -117,45 +99,6 @@ public override void OnOccasionalTick() } - private void ChangeOperationalMode( UndertakerMode newMode, bool forced = false ) - { - // Get the def we need - string beltDefName; - switch ( newMode ) - { - case UndertakerMode.PoweredLift: - beltDefName = "A2BLift"; - break; - case UndertakerMode.UnpoweredSlide: - beltDefName = "A2BSlide"; - break; - default: - return; // Invalid mode change - } - - // Get the thing def for the belt - ThingDef beltDef = DefDatabase.GetNamed( beltDefName ); - - // Get our current position and rotation - IntVec3 beltPos = parent.Position; - Rot4 beltRot = parent.Rotation; - - // Make the new belt - Thing beltThing = ThingMaker.MakeThing( beltDef ); - beltThing.SetFactionDirect( Faction.OfColony ); - beltThing.HitPoints = parent.HitPoints; - - // Set the new belt mode - BeltUndertakerComponent beltComp = beltThing.TryGetComp(); - beltComp.forcedMode = forced; - - // Remove this belt - parent.Destroy( DestroyMode.Vanish ); - - // Spawn new belt - GenSpawn.Spawn( beltThing, beltPos, beltRot ); - } - public override IntVec3 GetDestinationForThing( Thing thing) { return IntVec3.Invalid; @@ -174,7 +117,7 @@ public override void PostDraw() status.Thing.DrawAt(drawPos); - DrawGUIOverlay(status, drawPos); + //DrawGUIOverlay(status, drawPos); } if( ( ( !this.IsSlide() )&&( !this.IsLift() ) )|| ( ( this.IsSlide() )&&( BeltPhase == Phase.Offline ) ) ) @@ -245,25 +188,21 @@ public override IEnumerable CompGetGizmosExtra() Command_Action actionToggleMode = new Command_Action(); if( actionToggleMode != null ) { - actionToggleMode.icon = ContentFinder.Get( "UI/Icons/Commands/UndertakerMode", true);; + actionToggleMode.icon = Constants.IconUndertakerToggle; actionToggleMode.defaultLabel = Constants.TxtUndertakerModeToggle.Translate(); - actionToggleMode.activateSound = SoundDef.Named( "Click" ); + actionToggleMode.activateSound = Constants.ButtonClick; if( this.IsLift() ) { actionToggleMode.defaultDesc = Constants.TxtUndertakerModeSlide.Translate(); - actionToggleMode.action = new Action( delegate() - { - ChangeOperationalMode( UndertakerMode.UnpoweredSlide, true ); - } ); } else { actionToggleMode.defaultDesc = Constants.TxtUndertakerModeLift.Translate(); - actionToggleMode.action = new Action( delegate() - { - ChangeOperationalMode( UndertakerMode.PoweredLift, true ); - } ); } + actionToggleMode.action = new Action( delegate() + { + A2BMonitor.RegisterTickAction( this.parent.ThingID, UndertakerToggleMode, new UndertakerToggleData( this, true ) ); + } ); if( actionToggleMode.action != null ) { yield return actionToggleMode; @@ -272,5 +211,104 @@ public override IEnumerable CompGetGizmosExtra() // No more gizmos yield break; } + + #region Static Callbacks + + public static bool UndertakerToggleMode( object target ) + { + var toggleData = (UndertakerToggleData) target; + UndertakerMode toggleMode = UndertakerMode.Undefined; + if( toggleData.target.IsLift() ) + { + toggleMode = UndertakerMode.UnpoweredSlide; + } + else if( toggleData.target.IsSlide() ) + { + toggleMode = UndertakerMode.PoweredLift; + } + else + { + toggleMode = UndertakerMode.AutoDetect; + } + return ChangeOperationalMode( toggleData.target, toggleMode, toggleData.forced ); + } + + private static bool ChangeOperationalMode( BeltUndertakerComponent undertaker, UndertakerMode newMode, bool forced ) + { + // Get the new def + ThingDef beltDef; + switch ( newMode ) + { + case UndertakerMode.PoweredLift: + beltDef = Constants.DefBeltLift; + break; + case UndertakerMode.UnpoweredSlide: + beltDef = Constants.DefBeltSlide; + break; + case UndertakerMode.AutoDetect: + BeltComponent topBelt = undertaker.GetPositionFromRelativeRotation( Rot4.South ).GetBeltSurfaceComponent(); + + if( topBelt != null ) + { + // We have a belt connected to the top + if( topBelt.CanAcceptFrom( undertaker, true ) ) + { + // The top belt can accept from this belt, + // therefore this is a powered lift + if( undertaker.IsLift() ) + { + // Already a lift + return true; + } + + // Switch this to a lift + beltDef = Constants.DefBeltLift; + break; + } + else + { + // This undertaker can't feed the top belt, assume the top + // belt feeds the undertaker and operate as a slide + if( undertaker.IsSlide() ) + { + // Already a slide + return true; + } + + // Switch this to a slide + beltDef = Constants.DefBeltSlide; + break; + } + } + // No top belt, don't do anything yet + return false; + default: + // Invalid mode change, deregister to allow for a proper mode change + return true; + } + + // Get target position and rotation + IntVec3 beltPos = undertaker.parent.Position; + Rot4 beltRot = undertaker.parent.Rotation; + + // Make the new belt + Thing beltThing = ThingMaker.MakeThing( beltDef ); + beltThing.SetFactionDirect( Faction.OfColony ); + beltThing.HitPoints = undertaker.parent.HitPoints; + + // Set the new belt mode + BeltUndertakerComponent beltComp = beltThing.TryGetComp(); + beltComp.forcedMode = forced; + + // Remove the existing belt + undertaker.parent.Destroy(); + + // Spawn new belt + GenSpawn.Spawn( beltThing, beltPos, beltRot ); + return true; + } + + #endregion + } } diff --git a/Source/A2B/Components/BeltUnloaderComponent.cs b/Source/A2B/Components/BeltUnloaderComponent.cs index b049719..0b7c1cd 100644 --- a/Source/A2B/Components/BeltUnloaderComponent.cs +++ b/Source/A2B/Components/BeltUnloaderComponent.cs @@ -9,7 +9,7 @@ namespace A2B { public class BeltUnloaderComponent : BeltComponent { - public override bool CanOutputToNonBelt() + public override bool CanOutputToNonBelt( IntVec3 beltDest, Thing thing ) { return true; } diff --git a/Source/A2B/Components/Extensions/BeltUtilities.cs b/Source/A2B/Components/Extensions/BeltUtilities.cs index 6e8a1d8..5c64aa6 100644 --- a/Source/A2B/Components/Extensions/BeltUtilities.cs +++ b/Source/A2B/Components/Extensions/BeltUtilities.cs @@ -59,21 +59,58 @@ public static void DrawUndercoverFrame( this BeltUndercoverComponent belt ) } [CanBeNull] - public static BeltComponent GetBeltComponent(this IntVec3 position ) + public static BeltComponent GetBeltComponent( this IntVec3 position, BeltComponent source ) + { + switch( source.OutputLevel ) + { + case Level.Surface: + return position.GetBeltSurfaceComponent(); + + case Level.Underground: + var belts = position.GetBeltUndergroundComponents(); + + // Scan for prefered belt (lift) otherwise continue underground + if( ( belts != null )&& + ( belts.Count > 0 ) ) + { + + //var p = _parentComponent as BeltUndergroundComponent; + var lift = belts.Find( b => b.IsLift() && b.CanAcceptFrom( source ) ); + var under = belts.Find( b => b.IsUndercover() ); + if( + ( lift != null )&& + ( + ( lift.BeltPhase == Phase.Active )|| + ( under == null ) + ) + ) + return lift; + else + return under; + } + return null; + + default: + return null; + } + } + + [CanBeNull] + public static BeltComponent GetBeltSurfaceComponent(this IntVec3 position ) { // BUGFIX: Previously, this function would grab the first building it saw at a given position. This is a problem // if a power conduit was on the same tile, as it was possible to miss the BeltComponent entirely. This is a more // robust method of identifying BeltComponents at a given location because it first finds ALL buildings on a tile. - - // CHANGE: Belts now have a level (underground and surface), this function returns a surface component + + // CHANGE: Belts now have a level (underground and surface), this function returns a surface component // Since this query is lazily evaluated, it is much faster than using ThingsListAt. try { return Find.ThingGrid.ThingsAt(position) // All things at a given position - .OfType() // Only ones that can be converted to ThingWithComps - .Select(tc => tc.TryGetComp()) // Grab the BeltComponent from each one - .First(b => (b != null)&&(b.InputLevel & Level.Surface)!= 0);// Get the first non-null entry on the surface + .OfType() // Only ones that can be converted to ThingWithComps + .Select(tc => tc.TryGetComp()) // Grab the BeltComponent from each one + .First(b => (b != null)&&(b.InputLevel & Level.Surface)!= 0);// Get the first non-null entry on the surface } catch (InvalidOperationException) { return null; // Didn't find one at all } diff --git a/Source/A2B/Components/UndertakerMode.cs b/Source/A2B/Components/UndertakerMode.cs index 9b8ebab..214c3e5 100644 --- a/Source/A2B/Components/UndertakerMode.cs +++ b/Source/A2B/Components/UndertakerMode.cs @@ -4,6 +4,7 @@ public enum UndertakerMode { Undefined = 0, PoweredLift, - UnpoweredSlide + UnpoweredSlide, + AutoDetect } } \ No newline at end of file diff --git a/Source/A2B/Designators/Designator_ToggleUndercoverCover.cs b/Source/A2B/Designators/Designator_ToggleUndercoverCover.cs index ec56ea8..9bdbec7 100644 --- a/Source/A2B/Designators/Designator_ToggleUndercoverCover.cs +++ b/Source/A2B/Designators/Designator_ToggleUndercoverCover.cs @@ -13,15 +13,21 @@ namespace A2B public class Designator_ToggleUndercoverCover : Designator { - public Designator_ToggleUndercoverCover() + + public void LoadDesignatorIcon() { this.icon = Constants.IconUndercoverCoverToggle; + } + + public Designator_ToggleUndercoverCover() + { + LongEventHandler.ExecuteWhenFinished( LoadDesignatorIcon ); this.defaultLabel = Constants.TxtUndercoverCoverToggle.Translate(); this.defaultDesc = Constants.TxtUnderUndercoverCoverToggleDesc.Translate(); this.useMouseIcon = true; this.soundDragSustain = SoundDefOf.DesignateDragStandard; this.soundDragChanged = SoundDefOf.DesignateDragStandardChanged; - this.soundSucceeded = SoundDef.Named( "Click" ); + this.soundSucceeded = Constants.ButtonClick; this.hotKey = Constants.KeyUndercoverCoverToggle; } diff --git a/Source/A2B/JobDrivers/JobDriver_UndercoverCoverToggle.cs b/Source/A2B/JobDrivers/JobDriver_UndercoverCoverToggle.cs index c2bb1cb..a6972da 100644 --- a/Source/A2B/JobDrivers/JobDriver_UndercoverCoverToggle.cs +++ b/Source/A2B/JobDrivers/JobDriver_UndercoverCoverToggle.cs @@ -19,7 +19,7 @@ protected override IEnumerable MakeNewToils() // Destroyed, // On fire // Designator removed - this.FailOnDestroyed( TargetIndex.A ); + this.FailOnDespawnedNullOrForbidden( TargetIndex.A ); this.FailOnBurningImmobile( TargetIndex.A ); this.FailOnThingMissingDesignation( TargetIndex.A, Constants.DesignationUndercoverCoverToggle ); // Reserve the target diff --git a/Source/A2B/Properties/AssemblyInfo.cs b/Source/A2B/Properties/AssemblyInfo.cs index d1dcae0..83d563a 100755 --- a/Source/A2B/Properties/AssemblyInfo.cs +++ b/Source/A2B/Properties/AssemblyInfo.cs @@ -10,7 +10,7 @@ [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("A2B")] -[assembly: AssemblyCopyright("Copyright © 2014, 2015")] +[assembly: AssemblyCopyright("Copyright © 2014-2016")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -35,5 +35,5 @@ // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("0.12.1.0")] -[assembly: AssemblyFileVersion("0.12.1.0")] +[assembly: AssemblyVersion("0.13.0.0")] +[assembly: AssemblyFileVersion("0.13.0.0")] diff --git a/Source/A2B/Utilities/A2BDataDef.cs b/Source/A2B/Utilities/A2BDataDef.cs index f125aef..0ffbe47 100644 --- a/Source/A2B/Utilities/A2BDataDef.cs +++ b/Source/A2B/Utilities/A2BDataDef.cs @@ -11,6 +11,7 @@ namespace A2B { + public class A2BDataDef : Def { // Core mod versioning @@ -41,6 +42,10 @@ public class A2BDataDef : Def public float ReliabilityStartThresholdOffset; public List< string > ReliabilityResearch; + // Power data + public float LowPowerFactor = 0.1f; + public float PowerPerUndercover = 10f; + } } diff --git a/Source/A2B/Utilities/A2BMonitor.cs b/Source/A2B/Utilities/A2BMonitor.cs index f4a6ee5..a5b9377 100644 --- a/Source/A2B/Utilities/A2BMonitor.cs +++ b/Source/A2B/Utilities/A2BMonitor.cs @@ -7,29 +7,29 @@ namespace A2B // Return true to automatically deregister // DO NOT DEREGISTER FROM WITHIN A CALLBACK! // Invalidated lists are bad, mmm'kay? - public delegate bool MonitorAction(); + public delegate bool MonitorAction( object target ); public class A2BMonitor : MapComponent { - private static Dictionary tickActions = new Dictionary(); - private static Dictionary occasionalActions = new Dictionary(); - private static Dictionary updateActions = new Dictionary(); - private static Dictionary guiActions = new Dictionary(); - private static Dictionary exposeActions = new Dictionary(); + private static Dictionary> tickActions = new Dictionary>(); + private static Dictionary> occasionalActions = new Dictionary>(); + private static Dictionary> updateActions = new Dictionary>(); + private static Dictionary> guiActions = new Dictionary>(); + private static Dictionary> exposeActions = new Dictionary>(); private static List< string > removeKeys = new List< string >(); private bool firstTick = true; - private void ProcessCallbacks( Dictionary d ) + private void ProcessCallbacks( Dictionary> d ) { removeKeys.Clear(); - foreach (var p in d) - if( p.Value() == true ) + foreach( var p in d ) + if( p.Value.First( p.Value.Second ) == true ) removeKeys.Add( p.Key ); - foreach (var k in removeKeys) + foreach( var k in removeKeys ) d.Remove( k ); } @@ -46,7 +46,7 @@ public override void MapComponentTick() ProcessCallbacks( tickActions ); - // Occasional only get fewer ticks so they don't bomb the system + // Occasional get fewer ticks so they don't bomb the system if( ( firstTick == true )|| ( ( Find.TickManager.TicksGame + GetHashCode() ) % A2BData.OccasionalTicks == 0 ) ) ProcessCallbacks( occasionalActions ); @@ -70,62 +70,62 @@ public override void ExposeData() #region Register/Deregister Actions - public static void RegisterUpdateAction(string name, MonitorAction action) + public static void RegisterUpdateAction( string name, MonitorAction action, object target = null ) { if( !updateActions.ContainsKey( name ) ) - updateActions.Add(name, action); + updateActions.Add( name, new Pair( action, target ) ); } - public static void DeregisterUpdateAction(string name) + public static void DeregisterUpdateAction( string name ) { - updateActions.Remove(name); + updateActions.Remove( name ); } - public static void RegisterTickAction(string name, MonitorAction action) + public static void RegisterTickAction( string name, MonitorAction action, object target = null ) { if( !tickActions.ContainsKey( name ) ) - tickActions.Add(name, action); + tickActions.Add( name, new Pair( action, target ) ); } - public static void DeregisterTickAction(string name) + public static void DeregisterTickAction( string name ) { - tickActions.Remove(name); + tickActions.Remove( name ); } - public static void RegisterOccasionalAction(string name, MonitorAction action) + public static void RegisterOccasionalAction( string name, MonitorAction action, object target = null ) { if( !occasionalActions.ContainsKey( name ) ){ // Invoke accasional actions immediately - if( action.Invoke() == false ) - occasionalActions.Add(name, action); + if( action.Invoke( target ) == false ) + occasionalActions.Add( name, new Pair( action, target ) ); } } - public static void DeregisterOccasionalAction(string name) + public static void DeregisterOccasionalAction( string name ) { - occasionalActions.Remove(name); + occasionalActions.Remove( name ); } - public static void RegisterGUIAction(string name, MonitorAction action) + public static void RegisterGUIAction( string name, MonitorAction action, object target = null ) { if( !guiActions.ContainsKey( name ) ) - guiActions.Add(name, action); + guiActions.Add( name, new Pair( action, target ) ); } - public static void DeregisterGUIAction(string name) + public static void DeregisterGUIAction( string name ) { - guiActions.Remove(name); + guiActions.Remove( name ); } - public static void RegisterExposeDataAction(string name, MonitorAction action) + public static void RegisterExposeDataAction( string name, MonitorAction action, object target = null ) { if( !exposeActions.ContainsKey( name ) ) - exposeActions.Add(name, action); + exposeActions.Add( name, new Pair( action, target ) ); } - public static void DeregisterExposeDataAction(string name) + public static void DeregisterExposeDataAction( string name ) { - exposeActions.Remove(name); + exposeActions.Remove( name ); } #endregion diff --git a/Source/A2B/Utilities/A2BResearch.cs b/Source/A2B/Utilities/A2BResearch.cs index bd71cee..491fe0b 100644 --- a/Source/A2B/Utilities/A2BResearch.cs +++ b/Source/A2B/Utilities/A2BResearch.cs @@ -47,7 +47,8 @@ public static class A2BData public static A2B_Reliability Reliability; // Power for underground powered belts - public static float powerPerUndercover = 1000f; + public static float LowPowerFactor; + public static float PowerPerUndercover; public static A2BDataDef def { @@ -82,7 +83,9 @@ static A2BData() Reliability.StartThreshold = def.ReliabilityStartThresholdBase; Reliability.FlatRateThreshold = def.ReliabilityFlatRateThresholdBase; - A2BMonitor.RegisterTickAction( "A2BResearch.UndercoverPowerInit", A2BResearch.UndercoverPowerInit ); + LowPowerFactor = def.LowPowerFactor; + PowerPerUndercover = def.PowerPerUndercover; + A2BMonitor.RegisterTickAction( "A2BResearch.BeltSpeedInit", A2BResearch.BeltSpeedInit ); A2BMonitor.RegisterOccasionalAction( "A2BResearch.BeltSpeed", A2BResearch.BeltSpeed ); @@ -119,25 +122,14 @@ public static void CheckVersion(Version version) public static class A2BResearch { - public static bool UndercoverPowerInit() - { - var baseBelt = DefDatabase.GetNamed( "A2BBelt" ); - if( baseBelt != null ){ - var beltComps = baseBelt.CompDefFor(); - if( beltComps != null ) - A2BData.powerPerUndercover = beltComps.basePowerConsumption; - } - return true; - } - - public static bool BeltSpeedInit() + public static bool BeltSpeedInit( object target ) { A2BData.BeltSpeed.TicksToMove = A2BData.def.BeltSpeedBase; AnimatedGraphic.animationRate = ( (float)A2BData.BeltSpeed.TicksToMove / 90.0f); return true; } - public static bool BeltSpeed() + public static bool BeltSpeed( object target ) { if (A2BResearch.ResearchGroupComplete(A2BData.def.BeltSpeedResearch)) { A2BData.BeltSpeed.TicksToMove += A2BData.def.BeltSpeedOffset; @@ -148,7 +140,7 @@ public static bool BeltSpeed() return false; } - public static bool Climatization() + public static bool Climatization( object target ) { if (A2BResearch.ResearchGroupComplete(A2BData.def.ClimatizationResearch)) { A2BData.Climatization.FreezeTemperature += A2BData.def.ClimatizationMinTemperatureOffset; @@ -158,7 +150,7 @@ public static bool Climatization() return false; } - public static bool Durability() + public static bool Durability( object target ) { if (A2BResearch.ResearchGroupComplete(A2BData.def.DurabilityResearch)) { A2BData.Durability.DeteriorateChance += A2BData.def.DurabilityOffset; @@ -168,7 +160,7 @@ public static bool Durability() return false; } - public static bool Reliability() + public static bool Reliability( object target ) { if (A2BResearch.ResearchGroupComplete(A2BData.def.ReliabilityResearch)) { A2BData.Reliability.FlatRateThreshold += A2BData.def.ReliabilityFlatRateThresholdOffset; diff --git a/Source/A2B/Utilities/Constants.cs b/Source/A2B/Utilities/Constants.cs index 2663887..7856c37 100755 --- a/Source/A2B/Utilities/Constants.cs +++ b/Source/A2B/Utilities/Constants.cs @@ -3,9 +3,14 @@ namespace A2B { + + [StaticConstructorOnStartup] public static class Constants { - public const string msgPowerDisconnect = "PowerDisconnect"; + + #region Text Keys + + public const string msgPowerDisconnect = "PowerDisconnect"; public const string msgPowerConnect = "PowerConnect"; public const string TxtActive = "A2B_Active"; @@ -23,6 +28,9 @@ public static class Constants public const string TxtLiftDrivingComponents = "A2B_LiftDrivingComponents"; + public const string TxtSelectorToGroundToggleDescription = "A2B_Selector_To_Ground_Toggle_Desc"; + public const string TxtSelectorToGroundToggle = "A2B_Selector_To_Ground_Toggle"; + public const string TxtUndertakerMode = "A2B_Undertaker_Mode"; public const string TxtUndertakerModeToggle = "A2B_Undertaker_Mode_Toggle"; public const string TxtUndertakerModeUndefined = "A2B_Undertaker_Mode_Undefined"; @@ -41,11 +49,13 @@ public static class Constants public const string TxtDirectionSouth = "A2B_Direction_South"; public const string TxtDirectionWest = "A2B_Direction_West"; - public static ThingDef DefBeltUndercover = DefDatabase.GetNamed( "A2BUndercover", true ); + #endregion - public static Texture2D IconUndercoverCoverToggle = ContentFinder.Get( "UI/Icons/Commands/UndercoverCoverToggle", true); + #region Key Defs - //public static Material UndercoverFrame = MaterialPool.MatFrom( "Things/Building/UndergroundFrame" ); + public static ThingDef DefBeltUndercover = DefDatabase.GetNamed( "A2BUndercover", true ); + public static ThingDef DefBeltLift = DefDatabase.GetNamed( "A2BLift", true ); + public static ThingDef DefBeltSlide = DefDatabase.GetNamed( "A2BSlide", true ); public static DesignationDef DesignationUndercoverCoverToggle = DefDatabase.GetNamed( "A2BUndercoverCoverToggleDesignation", true ); @@ -53,5 +63,42 @@ public static class Constants public static KeyBindingDef KeyUndercoverCoverToggle = DefDatabase.GetNamed( "A2BUndercoverCoverToggleKeyBinding", true ); + public static SoundDef ButtonClick = DefDatabase.GetNamed( "Click", true ); + + #endregion + + #region Icon Paths + + public static string PathIconSelectorToGroundTrue = "UI/Icons/Commands/UndertakerMode"; + public static string PathIconSelectorToGroundFalse = "UI/Icons/Commands/UndercoverCoverToggle"; + + public static string PathIconUndertakerToggle = "UI/Icons/Commands/UndertakerMode"; + public static string PathIconUndercoverCoverToggle = "UI/Icons/Commands/UndercoverCoverToggle"; + + #endregion + + #region Icon Textures + + public static Texture2D IconSelectorToGroundTrue; + public static Texture2D IconSelectorToGroundFalse; + public static Texture2D IconUndertakerToggle; + public static Texture2D IconUndercoverCoverToggle; + + #endregion + + //public static Material UndercoverFrame = MaterialPool.MatFrom( "Things/Building/UndergroundFrame" ); + + static Constants() + { + LongEventHandler.ExecuteWhenFinished( LoadIcons ); + } + + static void LoadIcons() + { + IconSelectorToGroundTrue = ContentFinder.Get( PathIconSelectorToGroundTrue, true ); + IconSelectorToGroundFalse = ContentFinder.Get( PathIconSelectorToGroundFalse, true ); + IconSelectorToGroundFalse = ContentFinder.Get( PathIconSelectorToGroundFalse, true ); + IconUndercoverCoverToggle = ContentFinder.Get( PathIconUndercoverCoverToggle, true ); + } } }