I'm really jazzed about my progress so far. My mod adds a wardrobe to the game, which is essentially just a re-purposed equipment rack, but only for apparel.
I've implemented a state-machine with the wardrobe so that during warm seasons (Spring, Summer) cold-weather clothing will be stored in the wardrobe. During cold seasons (Fall, Winter) warm-weather clothing will be stored. When the seasons change, the pawn that owns the wardrobe will change clothing as appropriate. Further, the state-machine allows only one head item and one torso item at a time. Cold weather clothing is anything that has a negative value for ComfyTemperatureMax, which is currently just tuques and parkas for vanilla apparel.
I owe a large amount of thanks to Haplo and Rikiki and some others whose code I've been studying. Thank you for sharing!
Some things left to do:
- Figure out how to present dialogs. I want to re-use the AssignOwner dialog for beds to allow players to manually re-assign wardrobes.
- Implement ExposeData() for game saving
- Figure out the magic sauce behind JobDrivers. My is working perfectly, but I don't understand why. I call it with an instance of my wardrobe Building, but the constructor takes a Pawn. How does that even work?
- Probably other little tweaks
It's been a really fun project to work on.
Did something similar in clutter mod maybe it will help
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using Verse;
using Verse.AI;
using Verse.Noise;
using Verse.Sound;
using VerseBase;
using RimWorld;
using System;
namespace ClutterModule
{
class ClothLocker : Building
{
private int counter = 0;
private Pawn OwnerPawn;
private int TxtCounter = 0;
private int txtpos = 0;
private bool Stored = false;
private bool PawnArrival = false;
private bool ChangeClothArrival = false;
private int WornCount = 0;
private List<Apparel> NewClothSetList = new List<Apparel>();
private List<Apparel> StoredClothSetList = new List<Apparel>();
private List<string> EmptyTxt = new List<string>
{
" Empty",
" Only darkness",
" Concentrated dust",
" Reverse engineered sock"
};
public override void SpawnSetup()
{
base.SpawnSetup();
txtpos = UnityEngine.Random.Range(1, EmptyTxt.Count);
}
public override void ExposeData()
{
base.ExposeData();
Scribe_Values.LookValue<int>(ref counter, "counter");
Scribe_Values.LookValue<int>(ref txtpos, "txtpos");
Scribe_Values.LookValue<int>(ref TxtCounter, "TxtCounter");
Scribe_Values.LookValue<bool>(ref PawnArrival, "PawnArrival");
Scribe_Values.LookValue<bool>(ref ChangeClothArrival, "ChangeClothArrival");
Scribe_Collections.LookList<Apparel>(ref this.NewClothSetList, "NewClothSetList", LookMode.Deep, null);
Scribe_Collections.LookList<Apparel>(ref this.StoredClothSetList, "StoredClothSetList", LookMode.Deep, null);
Scribe_References.LookReference<Pawn>(ref OwnerPawn, "OwnerPawn");
}
private void ClothChange()
{
if(!Stored)
{
if (NewClothSetList.Count == WornCount || WornCount == 0)
{
Stored = true;
OwnerPawn.drawer.rotator.FaceSquare(this.Position);
}
if (OwnerPawn.apparel.WornApparelListForReading.Count > 0)
{
Apparel s = OwnerPawn.apparel.WornApparelListForReading.First();
NewClothSetList.Add(s);
OwnerPawn.apparel.TryDrop(s, out s, base.Position, true);
s.DeSpawn();
}
}
else if (Stored)
{
if(StoredClothSetList.Count>0)
{
foreach(Apparel thing in StoredClothSetList)
{
OwnerPawn.apparel.Wear(thing, false);
}
StoredClothSetList.Clear();
}
else if (StoredClothSetList.Count <=0)
{
StoredClothSetList=NewClothSetList;
NewClothSetList = new List<Apparel>();
ChangeClothArrival=false;
Stored = false;
WornCount = 0;
txtpos = UnityEngine.Random.Range(1, EmptyTxt.Count);
counter = 0;
}
}
this.OwnerPawn.drawer.renderer.graphics.ResolveApparelGraphics();
}
//Puff stuff
public void PuffThing()
{
for (int i = 0; i < 30; i++)
{
MoteMaker.ThrowAirPuffUp(this.DrawPos, AltitudeLayer.PawnState);
}
}
public override void Tick()
{
base.Tick();
if (OwnerPawn != null)
{
if (OwnerPawn.Position== this.InteractionCell && PawnArrival)
{
if (WornCount == 0 || WornCount < OwnerPawn.apparel.WornApparelCount)
{
WornCount = OwnerPawn.apparel.WornApparelCount;
}
Job job = new Job(JobDefOf.Wait, 50);
if(OwnerPawn.CurJob != job)
{
OwnerPawn.jobs.StopAll();
OwnerPawn.jobs.StartJob(job);
PawnArrival = false;
}
}
else if (OwnerPawn.Position == this.InteractionCell && ChangeClothArrival)
{
ClothChange();
if (Math.IEEERemainder((double)counter, (double)10.0f) == 1)
{
this.PuffThing();
}
}
if(PawnArrival)
{
counter += 1;
if (counter > 5000)
{
PawnArrival = false;
ChangeClothArrival = false;
Stored = false;
WornCount = 0;
counter = 0;
}
}
}
if(TxtCounter>20000)
{
txtpos = UnityEngine.Random.Range(1, EmptyTxt.Count);
TxtCounter = 0;
}
TxtCounter += 1;
}
public override IEnumerable<FloatMenuOption> GetFloatMenuOptionsFor(Pawn myPawn)
{
List<FloatMenuOption> list = new List<FloatMenuOption>();
{
if (!myPawn.CanReserve(this, ReservationType.Use))
{
FloatMenuOption item = new FloatMenuOption("CannotUseReserved", null);
return new List<FloatMenuOption>
{
item
};
}
if (!myPawn.CanReach(this, PathMode.Touch, Danger.Some))
{
FloatMenuOption item2 = new FloatMenuOption("CannotUseNoPath", null);
return new List<FloatMenuOption>
{
item2
};
}
if (myPawn != OwnerPawn)
{
Action action3 = delegate
{
OwnerPawn = myPawn;
};
list.Add(new FloatMenuOption("Make "+myPawn.Name.nick+" owner", action3));
}
if (StoredClothSetList.Count <= 0 && myPawn == OwnerPawn)
{
Action action1 = delegate
{
Job job1 = new Job(JobDefOf.Goto, this.InteractionCell);
myPawn.playerController.TakeOrderedJob(job1);
OwnerPawn = myPawn;
myPawn.Reserve(this, ReservationType.Use);
PawnArrival = true;
ChangeClothArrival = true;
};
list.Add(new FloatMenuOption("Store Cloth Set", action1));
}
if (StoredClothSetList.Count > 0 && myPawn == OwnerPawn)
{
Action action2 = delegate
{
Job job1 = new Job(JobDefOf.Goto, this.InteractionCell);
myPawn.playerController.TakeOrderedJob(job1);
myPawn.Reserve(this, ReservationType.Use);
ChangeClothArrival = true;
PawnArrival = true;
};
list.Add(new FloatMenuOption("Switch Cloth Sets", action2));
}
}
return list;
}
public override string GetInspectString()
{
StringBuilder stringBuilder = new StringBuilder();
if (OwnerPawn != null)
{
stringBuilder.Append(this.def.label + " owner is " + OwnerPawn.Name.nick);
}
if (OwnerPawn == null)
{
stringBuilder.Append(this.def.label + " has no owner.");
}
stringBuilder.AppendLine();
stringBuilder.Append("Stored stuff:");
stringBuilder.AppendLine();
if (StoredClothSetList.Count > 0 && StoredClothSetList != null)
{
for (int i = 0; i < StoredClothSetList.Count; i++)
{
stringBuilder.Append(StoredClothSetList.ElementAt(i).def.label + ", ");
}
}
if (StoredClothSetList.Count <= 0 && StoredClothSetList != null)
{
stringBuilder.Append(EmptyTxt.ElementAt(txtpos));
}
return stringBuilder.ToString();
}
public override void Destroy(DestroyMode mode = DestroyMode.Vanish)
{
if (StoredClothSetList.Count > 0 && (mode == DestroyMode.Deconstruct || mode == DestroyMode.Kill))
{
foreach(Apparel thing in StoredClothSetList)
{
thing.Destroy();
}
}
base.Destroy(mode);
}
}
}
This sounds nothing short of awesome! Good luck with it and i hope to see it in the released section soon. :)
I just started using your mod the other day and it was SWEET! But I do have an issue that I'm hoping you can straighten me out on...
I noticed that the PriorityHaul command was missing sometime after I loaded your mod. This was added via a mod called Priorityhaul by Gaesatae. Thinking that there was a conflict I tried moving PriorityHaul to the bottom in my ordered list of mods to add. That restored my ability to designate items for priority hauling but the three leftmost buttons that I see when I click on one of your smart wardrobes show red X icons though the labels beneath them; Assign Owner, Assign Owner of Room, and Reset Wardrobe do still appear.
I did try moving your mod below PriorityHaul in the mod load order but the red x issue remained. I'm NOT sure whether the three buttons still function as they used to. I'll advise more after a few experiments.