//public ThingDef def { get { return _def; } set { _def = value; } } #endregion Properties #region Methods /// <summary> /// Used during Rimworld Save/Load. /// </summary> /// <remarks>passed by ref since during load the contents of the variable is restored from the save.</remarks> public void ExposeData() { Scribe_Values.Look(ref _count, "count", _defaultCount); Scribe_Values.Look(ref _type, "DefType"); ThingDef td = thingDef; LoadoutGenericDef gd = genericDef; if (_type == typeof(ThingDef)) { Scribe_Defs.Look(ref td, "def"); } if (_type == typeof(LoadoutGenericDef)) { Scribe_Defs.Look(ref gd, "def"); } if (Scribe.mode == LoadSaveMode.LoadingVars) { _def = (_type == typeof(ThingDef) ? td as Def : gd as Def); } //Scribe_Defs.LookDef( ref _def, "def" ); // when saving _def is defined. When loading _def should have gotten it's contents by now. if (genericDef != null) { Scribe_Values.Look(ref _countType, "countType", LoadoutCountType.pickupDrop); } }
const int advanceTicks = 1; // GenTicks.TicksPerRealSecond / 4; /// <summary> /// Purpose is to handle deciding if a generic's state (something on the map or not) should be checked or not based on current frame. /// </summary> /// <param name="def"></param> /// <returns></returns> private bool GetVisibleGeneric(LoadoutGenericDef def) { if (GenTicks.TicksAbs >= genericVisibility[def].ticksToRecheck) { genericVisibility[def].ticksToRecheck = GenTicks.TicksAbs + (advanceTicks * genericVisibility[def].position); genericVisibility[def].check = Find.CurrentMap.listerThings.AllThings.Find(x => def.lambda(x.GetInnerIfMinified().def) && !x.def.Minifiable) == null; } return(genericVisibility[def].check); }
/// <summary> /// Constructor for Generic slots which use a lambda to determine what is picked up and stored. /// </summary> /// <param name="def">LoadoutGenericDef to use for picking up items.</param> /// <param name="count">Optional int how many should be picked up. If left blank then the default value from the LoadoutGenericDef is used.</param> public LoadoutSlot(LoadoutGenericDef def, int count = 0) { _type = typeof(LoadoutGenericDef); if (count < 1) { count = def.defaultCount; } _count = count < 1 ? _count = 1 : _count = count; _countType = def.defaultCountType; _def = def; }
//UNDONE This doesn't define weapons as yet and the code might not handle that well. Want to get various things stable first RE inventory. // But we can define generics for short range, assault, pistol, melee. /* (ProfoundDarkness) Some issues with weapons is that they have durability, quality, and often made of stuffs. * Also could use a super-generic which fetches x clips for each weapon on the pawn (working on that for something else). * I'm thinking we could add another button (more clutter) to each loadout slot which is only displayed if the item * has key properties. Clicking that button would show a new window which lets the user configure parameters like * a range slider for durability, range slider for quality, and a checklist for stuffs (assuming is made of stuffs). */ /// <summary> /// This constructor gets run on startup of RimWorld and generates the various LoadoutGenericDef instance objects akin to having been loaded from xml. /// </summary> static LoadoutGenericDef() { // Used in a handful of places where all loaded ThingDefs are useful. IEnumerable <ThingDef> everything = DefDatabase <ThingDef> .AllDefs; // need to generate a list as that's how new defs are taken by DefDatabase. List <LoadoutGenericDef> defs = new List <LoadoutGenericDef>(); LoadoutGenericDef generic = new LoadoutGenericDef(); generic.defName = "GenericMeal"; generic.description = "Generic Loadout for Meals. Intended for compatibility with pawns automatically picking up a meal for themself."; generic.label = "CE_Generic_Meal".Translate(); generic.defaultCountType = LoadoutCountType.pickupDrop; // Fits with disabling of RimWorld Pawn behavior of fetching meals themselves. generic._lambda = td => td.IsNutritionGivingIngestible && td.ingestible.preferability >= FoodPreferability.MealAwful && !td.IsDrug; generic.isBasic = true; defs.Add(generic); //Log.Message(string.Concat("CombatExtended :: LoadoutGenericDef :: ", generic.LabelCap, " list: ", string.Join(", ", DefDatabase<ThingDef>.AllDefs.Where(t => generic.lambda(t)).Select(t => t.label).ToArray()))); float targetNutrition = 0.85f; generic = new LoadoutGenericDef(); generic.defName = "GenericRawFood"; generic.description = "Generic Loadout for Raw Food. Intended for compatibility with pawns automatically picking up raw food to train animals."; generic.label = "CE_Generic_RawFood".Translate(); // Exclude drugs and corpses. Also exclude any food worse than RawBad as in testing the pawns would not even pick it up for training. generic._lambda = td => td.IsNutritionGivingIngestible && td.ingestible.preferability <= FoodPreferability.RawTasty && td.ingestible.HumanEdible && td.plant == null && !td.IsDrug && !td.IsCorpse; generic.defaultCount = Convert.ToInt32(Math.Floor(targetNutrition / everything.Where(td => generic.lambda(td)).Average(td => td.ingestible.nutrition))); //generic.defaultCount = 1; generic.isBasic = false; // doesn't need to be in loadouts by default as animal interaction talks to HoldTracker now. //TODO: Test pawns fetching raw food if no meal is available, if so then add a patch to have that talk to HoldTracker too. defs.Add(generic); //Log.Message(string.Concat("CombatExtended :: LoadoutGenericDef :: ", generic.LabelCap, " list: ", string.Join(", ", DefDatabase<ThingDef>.AllDefs.Where(t => generic.lambda(t)).Select(t => t.label + " B(" + t.GetStatValueAbstract(CE_StatDefOf.Bulk) + ") M(" + t.GetStatValueAbstract(StatDefOf.Mass) + ")").ToArray()))); generic = new LoadoutGenericDef(); generic.defName = "GenericDrugs"; generic.defaultCount = 3; generic.description = "Generic Loadout for Drugs. Intended for compatibility with pawns automatically picking up drugs in compliance with drug policies."; generic.label = "CE_Generic_Drugs".Translate(); generic.thingRequestGroup = ThingRequestGroup.Drug; generic.isBasic = true; defs.Add(generic); //Log.Message(string.Concat("CombatExtended :: LoadoutGenericDef :: ", generic.LabelCap, " list: ", string.Join(", ", DefDatabase<ThingDef>.AllDefs.Where(t => generic.lambda(t)).Select(t => t.label).ToArray()))); generic = new LoadoutGenericDef(); generic.defName = "GenericMedicine"; generic.defaultCount = 5; generic.defaultCountType = LoadoutCountType.pickupDrop; generic.description = "Generic Loadout for Medicine. Intended for pawns which will handle triage activities."; generic.label = "CE_Generic_Medicine".Translate(); generic.thingRequestGroup = ThingRequestGroup.Medicine; // now for the guns and ammo... // Get a list of guns that are player acquireable (not menuHidden but could also go with not dropOnDeath) which have expected comps/compProperties/verbs. List <ThingDef> guns = everything.Where(td => !td.menuHidden && td.HasComp(typeof(CompAmmoUser)) && td.GetCompProperties <CompProperties_AmmoUser>() != null && td.Verbs.FirstOrDefault(v => v is VerbPropertiesCE) != null).ToList(); string ammoLabel = "CE_Generic_Ammo".Translate(); const string ammoDescription = "Generic Loadout ammo for {0}. Intended for generic collection of ammo for given gun."; foreach (ThingDef gun in guns) { // make sure the gun has ammo defined... if (gun.GetCompProperties <CompProperties_AmmoUser>().ammoSet.ammoTypes.Count <= 0) { continue; } generic = new LoadoutGenericDef(); generic.defName = "GenericAmmo-" + gun.defName; generic.description = string.Format(ammoDescription, gun.LabelCap); generic.label = string.Format(ammoLabel, gun.LabelCap); generic.defaultCount = gun.GetCompProperties <CompProperties_AmmoUser>().magazineSize; generic.defaultCountType = LoadoutCountType.pickupDrop; // we want ammo to get picked up. //generic._lambda = td => td is AmmoDef && gun.GetCompProperties<CompProperties_AmmoUser>().ammoSet.ammoTypes.Contains(td); generic.thingRequestGroup = ThingRequestGroup.HaulableEver; generic._lambda = td => td is AmmoDef && gun.GetCompProperties <CompProperties_AmmoUser>().ammoSet.ammoTypes.Any(al => al.ammo == td); defs.Add(generic); //Log.Message(string.Concat("CombatExtended :: LoadoutGenericDef :: ", generic.LabelCap, " list: ", string.Join(", ", DefDatabase<ThingDef>.AllDefs.Where(t => generic.lambda(t)).Select(t => t.label).ToArray()))); } // finally we add all the defs generated to the DefDatabase. DefDatabase <LoadoutGenericDef> .Add(defs); }
/// <summary> /// Generates a loadout from a pawn's current equipment and inventory. Attempts to put items which fit in Generics that are default/DropExcess into said Generic. /// </summary> /// <param name="pawn">Pawn to check equipment/inventory on and generate a Loadout from.</param> /// <returns>Loadout which was generated based on Pawn's inventory.</returns> public static Loadout GenerateLoadoutFromPawn(this Pawn pawn) { // generate the name for this new pawn based loadout. string newName = string.Concat(pawn.Name.ToStringShort, " ", "CE_DefaultLoadoutName".Translate()); Regex reNum = new Regex(@"^(.*?)\d+$"); if (reNum.IsMatch(newName)) { newName = reNum.Replace(newName, @"$1"); } newName = LoadoutManager.GetUniqueLabel(newName); // set basic loadout properties. Loadout loadout = new Loadout(newName); loadout.defaultLoadout = false; loadout.canBeDeleted = true; LoadoutSlot slot = null; // grab the pawn's current equipment as a loadoutslot. if (pawn.equipment?.Primary != null) { slot = new LoadoutSlot(pawn.equipment.Primary.def); loadout.AddSlot(slot); } // get a list of generics which are drop only. Assumes that anything that doesn't fit here is a Specific slot later. IEnumerable <LoadoutGenericDef> generics = DefDatabase <LoadoutGenericDef> .AllDefs.Where(gd => gd.defaultCountType == LoadoutCountType.dropExcess); // enumerate each item in the pawn's inventory and add appropriate slots. foreach (Thing thing in pawn.inventory.innerContainer) { LoadoutGenericDef foundGeneric = null; // first check if it's a generic-able item... foreach (LoadoutGenericDef generic in generics) { if (generic.lambda(thing.def)) { foundGeneric = generic; break; } } // assign a loadout slot that fits the thing. if (foundGeneric != null) { slot = new LoadoutSlot(foundGeneric, thing.stackCount); } else { slot = new LoadoutSlot(thing.def, thing.stackCount); } // add the slot (this also takes care of adding to existing slots) loadout.AddSlot(slot); } // finally check the loadout and make sure that it has sufficient generics like what happens with a new loadout in the management UI. foreach (LoadoutGenericDef generic in generics.Where(gd => gd.isBasic)) { slot = loadout.Slots.FirstOrDefault(s => s.genericDef == generic); if (slot != null) { if (slot.count < slot.genericDef.defaultCount) { slot.count = slot.genericDef.defaultCount; } } else { slot = new LoadoutSlot(generic); loadout.AddSlot(slot); } } return(loadout); }
public static string GetWeightAndBulkTip(this LoadoutGenericDef def, int count = 1) { return("CE_Weight".Translate() + ": " + StatDefOf.Mass.ValueToString(def.mass * count, StatDefOf.Mass.toStringNumberSense) + "\n" + "CE_Bulk".Translate() + ": " + CE_StatDefOf.Bulk.ValueToString(def.bulk * count, CE_StatDefOf.Bulk.toStringNumberSense)); }
private void DrawSlot(Rect row, LoadoutSlot slot, bool slotDraggable = true) { // set up rects // dragging handle (square) | label (fill) | count (50px) | delete (iconSize) Rect draggingHandle = new Rect(row); draggingHandle.width = row.height; Rect labelRect = new Rect(row); if (slotDraggable) { labelRect.xMin = draggingHandle.xMax; } labelRect.xMax = row.xMax - _countFieldSize.x - _iconSize - 2 * _margin; Rect countRect = new Rect( row.xMax - _countFieldSize.x - _iconSize - 2 * _margin, row.yMin + (row.height - _countFieldSize.y) / 2f, _countFieldSize.x, _countFieldSize.y); Rect ammoRect = new Rect( countRect.xMin - _iconSize - _margin, row.yMin + (row.height - _iconSize) / 2f, _iconSize, _iconSize); Rect countModeRect = new Rect( ammoRect.xMin - _iconSize - _margin, row.yMin + (row.height - _iconSize) / 2f, _iconSize, _iconSize); Rect deleteRect = new Rect(countRect.xMax + _margin, row.yMin + (row.height - _iconSize) / 2f, _iconSize, _iconSize); // dragging on dragHandle if (slotDraggable) { TooltipHandler.TipRegion(draggingHandle, "CE_DragToReorder".Translate()); GUI.DrawTexture(draggingHandle, _iconMove); if (Mouse.IsOver(draggingHandle) && Input.GetMouseButtonDown(0)) { Dragging = slot; } } // interactions (main row rect) if (!Mouse.IsOver(deleteRect)) { Widgets.DrawHighlightIfMouseover(row); TooltipHandler.TipRegion(row, slot.genericDef != null ? slot.genericDef.GetWeightAndBulkTip(slot.count) : slot.thingDef.GetWeightAndBulkTip(slot.count)); } // label Text.Anchor = TextAnchor.MiddleLeft; Widgets.Label(labelRect, slot.LabelCap); Text.Anchor = TextAnchor.UpperLeft; // easy ammo adder, ranged weapons only if (slot.thingDef != null && slot.thingDef.IsRangedWeapon) { // make sure there's an ammoset defined AmmoSetDef ammoSet = ((slot.thingDef.GetCompProperties <CompProperties_AmmoUser>() == null) ? null : slot.thingDef.GetCompProperties <CompProperties_AmmoUser>().ammoSet); bool?temp = !((((ammoSet == null) ? null : ammoSet.ammoTypes)).NullOrEmpty()); if (temp ?? false) { if (Widgets.ButtonImage(ammoRect, _iconAmmoAdd)) { List <FloatMenuOption> options = new List <FloatMenuOption>(); int magazineSize = (slot.thingDef.GetCompProperties <CompProperties_AmmoUser>() == null) ? 0 : slot.thingDef.GetCompProperties <CompProperties_AmmoUser>().magazineSize; foreach (AmmoLink link in ((ammoSet == null) ? null : ammoSet.ammoTypes)) { options.Add(new FloatMenuOption(link.ammo.LabelCap, delegate { CurrentLoadout.AddSlot(new LoadoutSlot(link.ammo, (magazineSize <= 1 ? link.ammo.defaultAmmoCount : magazineSize))); })); } // Add in the generic for this gun. LoadoutGenericDef generic = DefDatabase <LoadoutGenericDef> .GetNamed("GenericAmmo-" + slot.thingDef.defName); if (generic != null) { options.Add(new FloatMenuOption(generic.LabelCap, delegate { CurrentLoadout.AddSlot(new LoadoutSlot(generic)); })); } Find.WindowStack.Add(new FloatMenu(options, "CE_AddAmmoFor".Translate(slot.thingDef.LabelCap))); } } } // count DrawCountField(countRect, slot); // toggle count mode if (slot.genericDef != null) { Texture2D curModeIcon = slot.countType == LoadoutCountType.dropExcess ? _iconDropExcess : _iconPickupDrop; string tipString = slot.countType == LoadoutCountType.dropExcess ? "Drop Excess" : "Pickup Missing and Drop Excess"; if (Widgets.ButtonImage(countModeRect, curModeIcon)) { slot.countType = slot.countType == LoadoutCountType.dropExcess ? LoadoutCountType.pickupDrop : LoadoutCountType.dropExcess; } TooltipHandler.TipRegion(countModeRect, tipString); } // delete if (Mouse.IsOver(deleteRect)) { GUI.DrawTexture(row, TexUI.HighlightTex); } if (Widgets.ButtonImage(deleteRect, _iconClear)) { CurrentLoadout.RemoveSlot(slot); } TooltipHandler.TipRegion(deleteRect, "CE_DeleteFilter".Translate()); }