private void BuyThing(Toil toil)
        {
            Job curJob = toil.actor.jobs.curJob;

            //Toils_Haul.ErrorCheckForCarry(toil.actor, Item);
            if (curJob.count == 0)
            {
                throw new Exception(string.Concat("BuyItem job had count = ", curJob.count, ". Job: ", curJob));
            }

            if (Item.MarketValue <= 0)
            {
                return;
            }
            int maxSpace  = toil.actor.GetInventorySpaceFor(Item);
            var inventory = toil.actor.inventory.innerContainer;

            Thing silver = inventory.FirstOrDefault(i => i.def == ThingDefOf.Silver);

            if (silver == null)
            {
                return;
            }

            var itemCost      = Item.MarketValue * PriceFactor;
            var maxAffordable = Mathf.FloorToInt(silver.stackCount / itemCost);

            if (maxAffordable < 1)
            {
                return;
            }

            // Changed formula a bit, so guests are less likely to leave small stacks if they can afford it
            var maxWanted = Rand.RangeInclusive(1, maxAffordable);
            int count     = Mathf.Min(Item.stackCount, maxSpace, maxWanted);

            var price = Mathf.FloorToInt(count * itemCost);

            if (silver.stackCount < price)
            {
                return;
            }

            var map = toil.actor.MapHeld;
            var inventoryItemsBefore = inventory.ToArray();
            var thing = Item.SplitOff(count);

            // Notification
            if (Settings.enableBuyNotification)
            {
                Messages.Message("GuestBoughtItem".Translate(new NamedArgument(toil.actor.Faction, "FACTION"), price, new NamedArgument(toil.actor, "PAWN"), new NamedArgument(thing, "ITEM")), toil.actor, MessageTypeDefOf.SilentInput);
            }

            int tookItems;

            if (thing.def.IsApparel && thing is Apparel apparel && !ApparelUtility.HasPartsToWear(pawn, apparel.def) && ItemUtility.AlienFrameworkAllowsIt(toil.actor.def, apparel.def, "CanWear"))
            {
                toil.actor.apparel.Wear(apparel);
                tookItems = apparel.stackCount;
            }
Esempio n. 2
0
        public override Job TryGiveJob(Pawn pawn)
        {
            var map          = pawn.MapHeld;
            var shoppingArea = pawn.GetShoppingArea();
            var things       = shoppingArea.ActiveCells.SelectMany(cell => map.thingGrid.ThingsListAtFast(cell)).Where(t => t.def.EverHaulable && t.def.tradeability != Tradeability.None &&
                                                                                                                       ItemUtility.IsBuyableAtAll(pawn, t) && Qualifies(t)).ToList();
            var storage = shoppingArea.ActiveCells.Select(cell => map.edificeGrid[cell]).OfType <Building_Storage>();

            things.AddRange(storage.SelectMany(s => s.slotGroup.HeldThings.Where(t => ItemUtility.IsBuyableAtAll(pawn, t) && Qualifies(t))));
            if (things.Count == 0)
            {
                return(null);
            }

            // Try some things
            var   selection = things.TakeRandom(5).Where(t => pawn.CanReach(t.Position, PathEndMode.Touch, Danger.None, false, TraverseMode.PassDoors)).ToArray();
            Thing thing     = null;

            if (selection.Length > 1)
            {
                thing = selection.MaxBy(t => Likey(pawn, t));
            }
            else if (selection.Length == 1)
            {
                thing = selection[0];
            }

            if (thing == null)
            {
                return(null);
            }

            if (Likey(pawn, thing) <= 0.5f)
            {
                //Log.Message(thing.Label + ": not interesting for " + pawn.NameStringShort);
                int duration = Rand.Range(JobDriver_BuyItem.MinShoppingDuration, JobDriver_BuyItem.MaxShoppingDuration);

                var canBrowse = CellFinder.TryRandomClosewalkCellNear(thing.Position, map, 2, out var standTarget) && ItemUtility.IsBuyableNow(pawn, thing);
                if (canBrowse)
                {
                    return(new Job(jobDefBrowse, standTarget, thing)
                    {
                        expiryInterval = duration * 2
                    });
                }

                return(null);
            }

            //Log.Message($"{pawn.NameShortColored} is going to buy {thing.LabelShort} at {thing.Position}.");
            return(new Job(jobDefBuy, thing));
        }
Esempio n. 3
0
        private static float Likey(Pawn pawn, Thing thing)
        {
            if (thing == null)
            {
                return(0);
            }

            // Health of object
            var hpFactor = thing.def.useHitPoints?(float)thing.HitPoints / thing.MaxHitPoints:1;

            // Apparel
            var appFactor = thing is Apparel apparel ? 1 + ApparelScoreGain(pawn, apparel) : 0.8f; // Not apparel, less likey
            //Log.Message(thing.Label + " - apparel score: " + appFactor);

            var hungerFactor = GetHungerFactor(pawn);

            // Food
            if (ItemUtility.IsFood(thing) && pawn.RaceProps.CanEverEat(thing))
            {
                appFactor = FoodUtility.FoodOptimality(pawn, thing, FoodUtility.GetFinalIngestibleDef(thing), 0, true) / 300f; // 300 = optimality max
                //Log.Message($"{pawn.LabelShort} looked at {thing.LabelShort} at {thing.Position} and scored it {appFactor}.");
                appFactor += hungerFactor;
                //Log.Message($"{pawn.LabelShort} added {hungerFactor} to the score for his hunger.");
                if (thing.def.IsWithinCategory(ThingCategoryDefOf.PlantFoodRaw))
                {
                    appFactor -= 0.25f;
                }
                if (thing.def.IsWithinCategory(ThingCategoryDefOf.MeatRaw))
                {
                    appFactor -= 0.5f;
                }
            }
            // Other consumables
            else if (ItemUtility.IsIngestible(thing) && thing.def.ingestible.joy > 0)
            {
                appFactor = 1 + thing.def.ingestible.joy * 0.5f;

                // Hungry? Care less about other stuff
                if (hungerFactor > 0)
                {
                    appFactor -= hungerFactor;
                }
            }
            else
            {
                // Hungry? Care less about other stuff
                if (hungerFactor > 0)
                {
                    appFactor -= hungerFactor;
                }
            }

            if (EquipmentUtility.IsBiocoded(thing) && !EquipmentUtility.IsBiocodedFor(thing, pawn))
            {
                return(0);
            }

            // Weapon
            if (thing.def.IsRangedWeapon)
            {
                if (pawn.story.traits.HasTrait(TraitDefOf.Brawler))
                {
                    return(0);
                }
                if (pawn.apparel.WornApparel.OfType <ShieldBelt>().Any())
                {
                    return(0);
                }
            }
            if (thing.def.IsWeapon)
            {
                // Weapon is also good!
                appFactor = 1;
                if (pawn.RaceProps.Humanlike && pawn.WorkTagIsDisabled(WorkTags.Violent))
                {
                    return(0);
                }
                if (!pawn.health.capacities.CapableOf(PawnCapacityDefOf.Manipulation))
                {
                    return(0);
                }
                if (!ItemUtility.AlienFrameworkAllowsIt(pawn.def, thing.def, "CanEquip"))
                {
                    return(0);
                }
            }
            // Shield belt
            if (thing is ShieldBelt)
            {
                if (pawn.equipment.Primary?.def.IsRangedWeapon == true)
                {
                    return(0);
                }
                if (!ItemUtility.AlienFrameworkAllowsIt(pawn.def, thing.def, "CanEquip"))
                {
                    return(0);
                }
            }

            // Quality of object
            var qFactor = 0.7f;

            if (thing.TryGetQuality(out var cat))
            {
                qFactor  = (float)cat;
                qFactor -= (float)QualityCategory.Normal;
                qFactor /= (float)QualityCategory.Masterwork - (float)QualityCategory.Normal;
                qFactor += 1;
                //Log.Message(thing.Label+" - quality: "+cat+" = "+ qFactor);
            }
            // Tech level of object
            var tFactor = 0.8f;

            if (thing.def.techLevel != TechLevel.Undefined)
            {
                tFactor  = (float)thing.def.techLevel;
                tFactor -= (float)pawn.Faction.def.techLevel;
                tFactor /= (float)TechLevel.Spacer;
                tFactor += 1;
                //Log.Message(thing.Label + " - techlevel: " + thing.def.techLevel + " = " + tFactor);
            }
            var rFactor = Rand.RangeSeeded(0.5f, 1.7f, pawn.thingIDNumber * 60509 + thing.thingIDNumber * 33151);

            //if(hpFactor*hpFactor*qFactor*qFactor*tFactor*appFactor > 0.5)
            //    Log.Message($"{thing.LabelShort.Colorize(Color.yellow)} - score: {hpFactor * hpFactor * qFactor * qFactor * tFactor * appFactor}, random: {rFactor}");
            return(Mathf.Max(0, hpFactor * hpFactor * qFactor * qFactor * tFactor * appFactor * rFactor)); // <= 0.5 = don't buy
        }
 private static bool CanEat(Thing thing, Pawn pawn)
 {
     return thing.def.IsNutritionGivingIngestible && thing.def.IsWithinCategory(ThingCategoryDefOf.Foods) && ItemUtility.AlienFrameworkAllowsIt(pawn.def, thing.def, "CanEat");
 }