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; }
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)); }
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"); }