//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);
            }
        }
Example #2
0
        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);
        }
Example #5
0
        /// <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);
        }
Example #6
0
 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));
 }
Example #7
0
        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());
        }