From 379f6aefced2d4aec75f5cf26c576f60063de11d Mon Sep 17 00:00:00 2001 From: ForsakenShell Date: Wed, 20 Apr 2016 11:12:48 -0600 Subject: [PATCH] Alpha 13 Update --- About/About.xml | 6 +- Assemblies/A2B.dll | Bin 67072 -> 73216 bytes Defs/A2BDataDefs/A2BData.xml | 98 +-- Defs/DesignationDefs/A2B_Designations.xml | 2 +- Defs/DesignatorDefs/A2B_Designators.xml | 10 - Defs/ThingDefs/Building_A2B.xml | 80 ++- Defs/ThingDefs/Building_A2B2.xml | 70 +-- Languages/English/Keyed/A2B.xml | 3 + Source/A2B/A2B.csproj | 2 +- Source/A2B/A2B.sln | 2 +- Source/A2B/A2B.userprefs | 25 +- Source/A2B/Buildings/Building_ConveyorBelt.cs | 4 +- Source/A2B/Components/BeltComponent.cs | 91 ++- Source/A2B/Components/BeltItemContainer.cs | 560 +++++++++++++++--- Source/A2B/Components/BeltLiftComponent.cs | 37 +- Source/A2B/Components/BeltLoaderComponent.cs | 37 +- .../A2B/Components/BeltSelectorComponent.cs | 275 +++++++-- Source/A2B/Components/BeltSlideComponent.cs | 23 +- .../A2B/Components/BeltSplitterComponent.cs | 2 +- .../A2B/Components/BeltUndercoverComponent.cs | 32 +- Source/A2B/Components/BeltUndercoverCover.cs | 4 +- .../Components/BeltUndergroundComponent.cs | 95 ++- .../A2B/Components/BeltUndertakerComponent.cs | 218 ++++--- .../A2B/Components/BeltUnloaderComponent.cs | 2 +- .../Components/Extensions/BeltUtilities.cs | 49 +- Source/A2B/Components/UndertakerMode.cs | 3 +- .../Designator_ToggleUndercoverCover.cs | 10 +- .../JobDriver_UndercoverCoverToggle.cs | 2 +- Source/A2B/Properties/AssemblyInfo.cs | 6 +- Source/A2B/Utilities/A2BDataDef.cs | 5 + Source/A2B/Utilities/A2BMonitor.cs | 64 +- Source/A2B/Utilities/A2BResearch.cs | 28 +- Source/A2B/Utilities/Constants.cs | 55 +- 33 files changed, 1392 insertions(+), 508 deletions(-) delete mode 100644 Defs/DesignatorDefs/A2B_Designators.xml 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 964d76a7432166d7ca87d963ca3ca0edf03c4947..723efdae87eb298bd736bf93906ce0d78f46397d 100755 GIT binary patch literal 73216 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 ZKlvvop?!4nGkgBY_w@gF@c#h?{x7{xtRVmZ literal 67072 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 s-2yAC8+r{^Md#{8_-v4} - - - - - 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 ); + } } }