public static void PrePostIngested(Pawn ingester, Thing t, int num) { Need_Water need_water = ingester.needs.water(); if (need_water == null) { return; } var comp = t.TryGetComp <CompWaterSource>(); if (comp == null) { return; } // 食事のついでの水分摂取の場合、帰ってくる水分量は常に1個分 float gotWaterAmount = MizuUtility.GetWater(ingester, t, need_water.WaterWanted, true); // 後で個数を掛け算する gotWaterAmount *= num; if (!ingester.Dead) { need_water.CurLevel += gotWaterAmount; } ingester.records.AddTo(MizuDef.Record_WaterDrank, gotWaterAmount); }
public static List <ThoughtDef> ThoughtsFromGettingWater(Pawn getter, Thing t) { // 空のリスト thoughtList.Clear(); // 心情ステータスの無いポーンには空のリストを返す if (getter.needs == null || getter.needs.mood == null) { return(thoughtList); } var comp = t.TryGetComp <CompWaterSource>(); if (comp == null) { return(thoughtList); } if (!comp.IsWaterSource) { return(thoughtList); } var waterType = MizuUtility.GetWaterType(t); var waterTypeDef = MizuDef.Dic_WaterTypeDef[waterType]; bool isDirect = comp.SourceType == CompProperties_WaterSource.SourceType.Building; ThoughtsFromWaterTypeDef(getter, waterTypeDef, isDirect, thoughtList); return(thoughtList); }
public static bool TryFindHiddenWaterSpot(Pawn pawn, out IntVec3 result) { var hiddenWaterSpot = pawn.Map.GetComponent <MapComponent_HiddenWaterSpot>(); if (hiddenWaterSpot == null) { Log.Error("hiddenWaterSpot is null"); result = IntVec3.Invalid; return(false); } bool isFound = false; float maxScore = float.MinValue; result = IntVec3.Invalid; foreach (var c in hiddenWaterSpot.SpotCells) { float curDist = (pawn.Position - c).LengthManhattan; if (pawn.CanReach(c, PathEndMode.ClosestTouch, Danger.Deadly)) { float curScore = MizuUtility.GetWaterTerrainScore(pawn, c, curDist, false); if (maxScore < curScore) { isFound = true; maxScore = curScore; result = c; } } } return(isFound); }
protected override Thing FinishAction() { // 地下水脈の水の種類から水アイテムの種類を決定 var waterThingDef = MizuUtility.GetWaterThingDefFromWaterType(Pool.WaterType); // 水アイテムの水源情報を得る var compprop = waterThingDef?.GetCompProperties <CompProperties_WaterSource>(); if (compprop == null) { return(null); } // 地下水脈から水を減らす Pool.CurrentWaterVolume = Mathf.Max(0, Pool.CurrentWaterVolume - (compprop.waterVolume * Ext.getItemCount)); // 水を生成 var createThing = ThingMaker.MakeThing(waterThingDef); if (createThing == null) { return(null); } // 個数設定 createThing.stackCount = Ext.getItemCount; return(createThing); }
protected override Thing FinishAction() { var targetWaterType = WaterType.NoWater; if (this.Ext.canDrawFromFaucet) { // 蛇口の場合 targetWaterType = this.WaterNet.StoredWaterTypeForFaucet; } else { // 自分自身の場合 targetWaterType = this.WorkTable.TankComp.StoredWaterType; } // 水道網の水の種類から水アイテムの種類を決定 var waterThingDef = MizuUtility.GetWaterThingDefFromWaterType(targetWaterType); if (waterThingDef == null) { return(null); } // 水アイテムの水源情報を得る var compprop = waterThingDef.GetCompProperties <CompProperties_WaterSource>(); if (compprop == null) { return(null); } // 水道網から水を減らす if (this.Ext.canDrawFromFaucet) { // 蛇口の場合 this.WaterNet.DrawWaterVolumeForFaucet(compprop.waterVolume * this.Ext.getItemCount); } else { // 自分自身の場合 this.WorkTable.TankComp.DrawWaterVolume(compprop.waterVolume * this.Ext.getItemCount); } // 水を生成 var createThing = ThingMaker.MakeThing(waterThingDef); if (createThing == null) { return(null); } // 個数設定 createThing.stackCount = this.Ext.getItemCount; return(createThing); }
protected override Job TryGiveJob(Pawn pawn) { Need_Water need_water = pawn.needs.water(); if (need_water == null) { return(null); } if (need_water.lastSearchWaterTick + SearchWaterIntervalTick > Find.TickManager.TicksGame) { return(null); } // Only trink if we're really thirsty. if (need_water.CurLevelPercentage > need_water.PercentageThreshThirsty) { return(null); } need_water.lastSearchWaterTick = Find.TickManager.TicksGame; Thing thing = MizuUtility.TryFindBestWaterSourceFor(pawn, pawn, false, true); if (thing != null) { if (thing.CanDrinkWater()) { return(new Job(MizuDef.Job_DrinkWater, thing) { count = MizuUtility.WillGetStackCountOf(pawn, thing) }); } else if (thing is IBuilding_DrinkWater) { return(new Job(MizuDef.Job_DrinkWaterFromBuilding, thing)); } } // 何も見つからなかった場合は隠し水飲み場を探す // 人間、家畜、野生の動物全て IntVec3 hiddenWaterSpot; if (MizuUtility.TryFindHiddenWaterSpot(pawn, out hiddenWaterSpot)) { return(new Job(MizuDef.Job_DrinkWater, hiddenWaterSpot) { count = 1 }); } // 水を発見できず return(null); }
private static Thing SpawnedWaterSearchInnerScan(Pawn eater, IntVec3 root, List <Thing> searchSet, PathEndMode peMode, TraverseParms traverseParams, bool priorQuality, float maxDistance = 9999f, Predicate <Thing> validator = null) { // 探索対象リストなし if (searchSet == null) { return(null); } // 対象のポーンを決める(取得者優先、次点で摂取者) Pawn pawn = traverseParams.pawn ?? eater; Thing result = null; float maxScore = float.MinValue; foreach (var thing in searchSet) { // アイテムとの距離が限界以上離れていたらダメ float lengthManhattan = (float)(root - thing.Position).LengthManhattan; if (lengthManhattan > maxDistance) { continue; } // 現時点での候補アイテムのスコア(摂取者にとって)を超えていないならダメ float thingScore = MizuUtility.GetWaterItemScore(eater, thing, lengthManhattan, priorQuality); if (thingScore < maxScore) { continue; } // ポーンがそこまでたどり着けなければだめ if (!pawn.Map.reachability.CanReach(root, thing, peMode, traverseParams)) { continue; } // まだ出現していない場合はダメ if (!thing.Spawned) { continue; } // アイテムが指定の条件を満たしていないならダメ if (validator != null && !validator(thing)) { continue; } // すべての条件を満足 result = thing; maxScore = thingScore; } return(result); }
// public override void PostIngested(Pawn ingester) // { // base.PostIngested(ingester); // Need_Water need_water = ingester.needs.water(); // if (need_water == null) return; // float gotWaterAmount = MizuUtility.GetWater(ingester, this.parent, need_water.WaterWanted, true); // if (!ingester.Dead) // { // need_water.CurLevel += gotWaterAmount; // } // ingester.records.AddTo(MizuDef.Record_WaterDrank, gotWaterAmount); // } public override IEnumerable <FloatMenuOption> CompFloatMenuOptions(Pawn selPawn) { foreach (var floatMenuOption in base.CompFloatMenuOptions(selPawn)) { yield return(floatMenuOption); } if (SourceType != CompProperties_WaterSource.SourceType.Item || parent.def.IsIngestible) { yield break; } // 水アイテムで、食べることが出来ないものは飲める if (!selPawn.IsColonistPlayerControlled) { yield break; } var stringBuilder = new StringBuilder(); stringBuilder.Append( string.Format(MizuStrings.FloatMenuGetWater.Translate(), parent.LabelNoCount).CapitalizeFirst()); if (!parent.IsSociallyProper(selPawn)) { // 囚人部屋のものは表示を追加 stringBuilder.Append(string.Concat(" (", "ReservedForPrisoners".Translate(), ")")); } foreach (var p in parent.Map.mapPawns.AllPawns) { if (!parent.Map.reservationManager.ReservedBy(parent, p)) { continue; } // 予約されている物は表示を追加 stringBuilder.AppendLine(); stringBuilder.Append(string.Format(string.Concat(" (", "ReservedBy".Translate(p.LabelShort, p), ")"))); break; } yield return(new FloatMenuOption( stringBuilder.ToString(), () => { var job = new Job(MizuDef.Job_DrinkWater, parent) { count = MizuUtility.WillGetStackCountOf(selPawn, parent) }; selPawn.jobs.TryTakeOrderedJob(job, JobTag.SatisfyingNeeds); })); }
protected override Job TryGiveJob(Pawn pawn) { Need_Water need_water = pawn.needs.water(); if (need_water == null) { return(null); } // 最後に水を探してから少し経つまで次の探索はしない if (need_water.lastSearchWaterTick + SearchWaterIntervalTick > Find.TickManager.TicksGame) { return(null); } need_water.lastSearchWaterTick = Find.TickManager.TicksGame; // 水の供給源を探す Thing thing = MizuUtility.TryFindBestWaterSourceFor(pawn, pawn, false, true); if (thing != null) { if (thing.CanDrinkWater()) { // 水アイテムが見つかった return(new Job(MizuDef.Job_DrinkWater, thing) { count = MizuUtility.WillGetStackCountOf(pawn, thing) }); } else if (thing is IBuilding_DrinkWater) { // 水を汲める設備が見つかった return(new Job(MizuDef.Job_DrinkWaterFromBuilding, thing)); } } // 何も見つからなかった場合は隠し水飲み場を探す // 人間、家畜、野生の動物全て IntVec3 hiddenWaterSpot; if (MizuUtility.TryFindHiddenWaterSpot(pawn, out hiddenWaterSpot)) { return(new Job(MizuDef.Job_DrinkWater, hiddenWaterSpot) { count = 1 }); } // 水を発見できず return(null); }
public override bool HasJobOnThing(Pawn pawn, Thing t, bool forced = false) { Pawn taker = pawn; Pawn giver = t as Pawn; // 与える相手が人でない、自分自身に与える→× if (giver == null || giver == taker) { return(false); } // 人間のような食事の与え方をする仕事だが、与える相手が人間ではない→× if (this.def.feedHumanlikesOnly && !giver.RaceProps.Humanlike) { return(false); } // 動物のような食事の与え方をする仕事だが、与える相手が動物ではない→× if (this.def.feedAnimalsOnly && !giver.RaceProps.Animal) { return(false); } // 与える相手が水分要求を持っているが、喉が渇いていると感じていない→× if (giver.needs.water() == null || giver.needs.water().CurLevelPercentage > giver.needs.water().PercentageThreshSlightlyThirsty + 0.02f) { return(false); } // 与える相手の状態が、誰かに食事を与えてもらうべき状態ではない→× if (!FeedPatientUtility.ShouldBeFed(giver)) { return(false); } // 給仕者が与える相手を「予約可能&到達可能」ではない→× if (!taker.CanReserveAndReach(t, PathEndMode.ClosestTouch, Danger.Deadly, 1, -1, null, forced)) { return(false); } if (MizuUtility.TryFindBestWaterSourceFor(taker, giver, true, false) == null) { // 与えられる水があるか探したが見つからなかった JobFailReason.Is(MizuStrings.JobFailReasonNoWater.Translate()); return(false); } return(true); }
public static Toil FinishDrinkTerrain(TargetIndex terrainVecIndex) { Toil toil = new Toil(); toil.initAction = delegate { Pawn actor = toil.actor; Need_Water need_water = actor.needs.water(); float numWater = need_water.MaxLevel - need_water.CurLevel; TerrainDef terrain = actor.Map.terrainGrid.TerrainAt(actor.CurJob.GetTarget(terrainVecIndex).Cell); WaterTerrainType drankTerrainType = terrain.GetWaterTerrainType(); if (actor.needs.mood != null) { // 直接飲んだ if (actor.CanManipulate()) { actor.needs.mood.thoughts.memories.TryGainMemory(MizuDef.Thought_DrankScoopedWater); } else { actor.needs.mood.thoughts.memories.TryGainMemory(MizuDef.Thought_SippedWaterLikeBeast); } ThoughtDef thoughtDef = MizuUtility.GetThoughtDefFromTerrainType(drankTerrainType); if (thoughtDef != null) { // 水の種類による心情 actor.needs.mood.thoughts.memories.TryGainMemory(thoughtDef); } } if (drankTerrainType == WaterTerrainType.SeaWater) { // 海水の場合の健康状態悪化 actor.health.AddHediff(HediffMaker.MakeHediff(MizuDef.Hediff_DrankSeaWater, actor)); } if (!actor.Dead) { actor.needs.water().CurLevel += numWater; } actor.records.AddTo(MizuDef.Record_WaterDrank, numWater); }; toil.defaultCompleteMode = ToilCompleteMode.Instant; return(toil); }
public override void Generate(Map map, GenStepParams parms) { var waterGrid = map.GetComponent <MapComponent_DeepWaterGrid>(); MizuUtility.GenerateUndergroundWaterGrid( map, waterGrid, basePoolNum, minWaterPoolNum, baseRainFall, basePlantDensity, literPerCell, poolCellRange, baseRegenRateRange, rainRegenRatePerCell); }
public override int CountProducts(Bill_Production bill) { var ext = bill.recipe.GetModExtension <DefExtension_WaterRecipe>(); var building = bill.billStack.billGiver as IBuilding_DrinkWater; var waterDef = MizuUtility.GetWaterThingDefFromWaterType(building.WaterType); if (waterDef == null) { return(0); } int numOfWater = bill.Map.resourceCounter.GetCount(waterDef); return(numOfWater); }
public override Job JobOnThing(Pawn getter, Thing target, bool forced = false) { Pawn patient = target as Pawn; // 水を探す Thing waterThing = MizuUtility.TryFindBestWaterSourceFor(getter, patient, true, false); if (waterThing == null) { return(null); } // 水を与えるジョブを発行 return(new Job(MizuDef.Job_FeedWaterPatient) { targetA = waterThing, targetB = patient, count = MizuUtility.WillGetStackCountOf(patient, waterThing) }); }
public override void MapComponentUpdate() { base.MapComponentUpdate(); drawer.CellBoolDrawerUpdate(); var anyPools = false; foreach (var pool in pools) { if (pool == null) { continue; } pool.Update(); anyPools = true; } if (anyPools) { return; } if (gridUpdates > 20) { if (gridUpdates >= 50) { return; } Log.Message("No water no life: Water grid not found, and failed to regenerate after 20 tries"); gridUpdates = 55; return; } gridUpdates++; poolIDGrid = new ushort[map.cellIndices.NumGridCells]; drawer = new CellBoolDrawer(this, map.Size.x, map.Size.z, 1f); pools = new List <UndergroundWaterPool>(); MizuUtility.GenerateUndergroundWaterGrid(map, this); }
private static Toil FinishDrinkSomeone(TargetIndex thingIndex, Func <Toil, Pawn> pawnGetter) { Toil toil = new Toil(); toil.initAction = () => { Thing thing = toil.actor.jobs.curJob.GetTarget(thingIndex).Thing; Pawn getter = pawnGetter(toil); if (getter == null) { return; } float wantedWaterAmount = getter.needs.water().WaterWanted; float gotWaterAmount = MizuUtility.GetWater(getter, thing, wantedWaterAmount, false); if (!getter.Dead) { getter.needs.water().CurLevel += gotWaterAmount; } getter.records.AddTo(MizuDef.Record_WaterDrank, gotWaterAmount); }; toil.defaultCompleteMode = ToilCompleteMode.Instant; return(toil); }
protected override Thing FinishAction() { // 現在の地形から水の種類を決定 var terrainDef = Map.terrainGrid.TerrainAt(job.GetTarget(BillGiverInd).Thing.Position); var waterTerrainType = terrainDef.GetWaterTerrainType(); var waterThingDef = MizuUtility.GetWaterThingDefFromTerrainType(waterTerrainType); if (waterThingDef == null) { return(null); } // 水を生成 var createThing = ThingMaker.MakeThing(waterThingDef); if (createThing == null) { return(null); } // 個数設定 createThing.stackCount = Ext.getItemCount; return(createThing); }
protected bool IsFoundWater(IBillGiver giver, DefExtension_WaterRecipe ext, List <ThingAmount> chosen) { if (ext == null) { return(true); } if (giver == null) { return(false); } var thing = giver as Thing; if (thing == null) { return(false); } switch (ext.recipeType) { case DefExtension_WaterRecipe.RecipeType.DrawFromTerrain: { // 水質チェック return(ext.needWaterTerrainTypes != null && ext.needWaterTerrainTypes.Contains(thing.Map.terrainGrid.TerrainAt(thing.Position).GetWaterTerrainType())); } case DefExtension_WaterRecipe.RecipeType.DrawFromWaterPool: { var waterGrid = thing.Map.GetComponent <MapComponent_ShallowWaterGrid>(); var pool = waterGrid.GetPool(thing.Map.cellIndices.CellToIndex(thing.Position)); // 水質条件チェック if (!ext.needWaterTypes.Contains(pool.WaterType)) { return(false); } // 入力水道網の水の種類から水アイテムの種類を決定 var waterThingDef = MizuUtility.GetWaterThingDefFromWaterType(pool.WaterType); if (waterThingDef == null) { return(false); } // 水アイテムの水源情報を得る var compprop = waterThingDef.GetCompProperties <CompProperties_WaterSource>(); if (compprop == null) { return(false); } // 水量チェック if (pool.CurrentWaterVolume < compprop.waterVolume * ext.getItemCount) { return(false); } return(true); } case DefExtension_WaterRecipe.RecipeType.DrawFromWaterNet: { var workTable = giver as Building_WaterNetWorkTable; if (workTable == null || workTable.InputWaterNet == null) { return(false); } var targetWaterType = WaterType.NoWater; var targetWaterVolume = 0.0f; if (ext.canDrawFromFaucet) { // 蛇口から汲むレシピ targetWaterType = workTable.InputWaterNet.StoredWaterTypeForFaucet; targetWaterVolume = workTable.InputWaterNet.StoredWaterVolumeForFaucet; } else { // 自身から汲むレシピ(水箱など) targetWaterType = workTable.TankComp.StoredWaterType; targetWaterVolume = workTable.TankComp.StoredWaterVolume; } // 水質チェック if (!ext.needWaterTypes.Contains(targetWaterType)) { return(false); } // 入力水道網の水の種類から水アイテムの種類を決定 var waterThingDef = MizuUtility.GetWaterThingDefFromWaterType(targetWaterType); if (waterThingDef == null) { return(false); } // 水アイテムの水源情報を得る var compprop = waterThingDef.GetCompProperties <CompProperties_WaterSource>(); if (compprop == null) { return(false); } // 水量チェック if (targetWaterVolume < compprop.waterVolume * ext.getItemCount) { return(false); } return(true); } case DefExtension_WaterRecipe.RecipeType.PourWater: return(true); default: Log.Error("recipeType is Undefined"); return(false); } }
static void Postfix(Caravan ___caravan) { // キャラバンのポーンの水分要求の処理 foreach (Pawn pawn in ___caravan.pawns) { if (pawn.needs == null) { continue; } Need_Water need_water = pawn.needs.water(); if (need_water == null) { continue; } // 喉が渇いてない場合は飲まない if (need_water.CurCategory <= ThirstCategory.Healthy) { continue; } // タイルが0以上(?)、死んでない、ローカルではなく惑星マップ上にいる(キャラバンしてる)、そのポーンが地形から水を飲める(心情がある/ない、脱水症状まで進んでいる/いない、など) if (pawn.Tile >= 0 && !pawn.Dead && pawn.IsWorldPawn() && pawn.CanDrinkFromTerrain()) { WaterTerrainType drankTerrainType = ___caravan.GetWaterTerrainType(); // 水を飲めない場所 if (drankTerrainType == WaterTerrainType.NoWater) { continue; } // 地形から水を飲む need_water.CurLevel = 1.0f; if (drankTerrainType == WaterTerrainType.SeaWater) { // 海水の場合の健康状態悪化 pawn.health.AddHediff(HediffMaker.MakeHediff(MizuDef.Hediff_DrankSeaWater, pawn)); } // 心情要求がなければここで終了 if (pawn.needs.mood == null) { continue; } // 直接水を飲んだ心情付加 if (pawn.CanManipulate()) { pawn.needs.mood.thoughts.memories.TryGainMemory(MizuDef.Thought_DrankScoopedWater); } else { pawn.needs.mood.thoughts.memories.TryGainMemory(MizuDef.Thought_SippedWaterLikeBeast); } // キャラバンのいる地形に応じた心情を付加 ThoughtDef thoughtDef = MizuUtility.GetThoughtDefFromTerrainType(drankTerrainType); if (thoughtDef != null) { // 水の種類による心情 pawn.needs.mood.thoughts.memories.TryGainMemory(thoughtDef); } continue; } // 水アイテムを探す Thing waterThing; Pawn inventoryPawn; // アイテムが見つからない if (!MizuCaravanUtility.TryGetBestWater(___caravan, pawn, out waterThing, out inventoryPawn)) { continue; } // アイテムに応じた水分を摂取&心情変化&健康変化 float numWater = MizuUtility.GetWater(pawn, waterThing, need_water.WaterWanted, false); need_water.CurLevel += numWater; pawn.records.AddTo(MizuDef.Record_WaterDrank, numWater); // 水アイテムが消滅していない場合(スタックの一部だけ消費した場合等)はここで終了 if (!waterThing.Destroyed) { continue; } if (inventoryPawn != null) { // 誰かの所持品にあった水スタックを飲みきったのであれば、所持品欄から除去 inventoryPawn.inventory.innerContainer.Remove(waterThing); // 移動不可状態を一旦リセット(して再計算させる?) ___caravan.RecacheImmobilizedNow(); // 水の残量再計算フラグON MizuCaravanUtility.daysWorthOfWaterDirty = true; } if (!MizuCaravanUtility.TryGetBestWater(___caravan, pawn, out waterThing, out inventoryPawn)) { // 飲んだことにより水がなくなったら警告を出す Messages.Message(string.Format(MizuStrings.MessageCaravanRunOutOfWater.Translate(), ___caravan.LabelCap, pawn.Label), ___caravan, MessageTypeDefOf.ThreatBig); } } }
static void Postfix(ref float y, float width, Thing thing, bool inventory = false) { // 所持品に含まれる水アイテムに、所持品を直接摂取するボタンを増やす // yは次の行のtop上端座標、widthは右端座標 // db=DrinkButton const float dbWidth = 24f; const float dbHeight = 24f; // 1アイコンにつき幅24f // 情報アイコン、ドロップアイコン、食べるアイコンを考慮すれば3個 // 食べると飲むを分離できたはずなので2で良い float dbRight = width - 24f * 2; float dbTop = y - 28f; Pawn selPawn = Find.Selector.SingleSelectedThing as Pawn; Corpse selCorpse = Find.Selector.SingleSelectedThing as Corpse; if (selPawn == null && selCorpse != null) { selPawn = selCorpse.InnerPawn; } // ポーンデータがないなら終了 if (selPawn == null) { return; } // プレイヤーが操作するポーンではない、またはそのポーンは倒れている→終了 if (!selPawn.IsColonistPlayerControlled || selPawn.Downed) { return; } // 水として飲めないアイテムなら終了 if (!thing.CanGetWater() || !thing.CanDrinkWaterNow()) { return; } // 水アイテムでなかったり、食べられるものは能動的に飲むことはできない var comp = thing.TryGetComp <CompWaterSource>(); if (comp == null || comp.SourceType != CompProperties_WaterSource.SourceType.Item || thing.IsIngestibleFor(selPawn)) { return; } // ツールチップとボタンを追加 Rect dbRect = new Rect(dbRight - dbWidth, dbTop, dbWidth, dbHeight); TooltipHandler.TipRegion(dbRect, string.Format(MizuStrings.FloatMenuGetWater.Translate(), thing.LabelNoCount)); if (Widgets.ButtonImage(dbRect, MizuGraphics.Texture_ButtonIngest)) { SoundDefOf.Tick_High.PlayOneShotOnCamera(null); Job job = new Job(MizuDef.Job_DrinkWater, thing) { count = MizuUtility.WillGetStackCountOf(selPawn, thing) }; selPawn.jobs.TryTakeOrderedJob(job, JobTag.SatisfyingNeeds); } }
public override Job JobOnThing(Pawn pawn, Thing t, bool forced = false) { Pawn warden = pawn; Pawn prisoner = t as Pawn; // 世話が必要でない if (!base.ShouldTakeCareOfPrisoner(warden, prisoner)) { return(null); } // 囚人が食事を持って来てもらえる扱いではない if (!prisoner.guest.CanBeBroughtFood) { return(null); } // 囚人は牢屋にいない if (!prisoner.Position.IsInPrisonCell(prisoner.Map)) { return(null); } Need_Water need_water = prisoner.needs.water(); // 水分要求がない if (need_water == null) { return(null); } // 喉が渇いていない if (need_water.CurLevelPercentage >= need_water.PercentageThreshThirsty + 0.02f) { return(null); } // (囚人が病人だから)食事を与えられるべき状態である(部屋に運ばれたものを自分で食べることができない) if (WardenFeedUtility.ShouldBeFed(prisoner)) { return(null); } // 水が見つからない Thing thing = MizuUtility.TryFindBestWaterSourceFor(warden, prisoner, false, false); if (thing == null) { return(null); } // 見つかった水アイテムは既に囚人がいる部屋の中にある if (thing.GetRoom(RegionType.Set_Passable) == prisoner.GetRoom(RegionType.Set_Passable)) { return(null); } // 部屋の中に十分な量の水がある if (WorkGiver_Warden_DeliverWater.WaterAvailableInRoomTo(prisoner)) { return(null); } // 水を運んでくるジョブを発行 return(new Job(MizuDef.Job_DeliverWater, thing, prisoner) { count = MizuUtility.WillGetStackCountOf(prisoner, thing), targetC = RCellFinder.SpotToChewStandingNear(prisoner, thing) }); }
public static Thing TryFindBestWaterSourceFor(Pawn getter, Pawn eater, bool priorQuality, bool allowBuilding, bool canUseInventory = true, bool allowForbidden = false, bool allowSociallyImproper = false) { // ドラッグ嫌いではない // →ドラッグを許可 bool allowDrug = !eater.IsTeetotaler(); Thing inventoryThing = null; if (canUseInventory && getter.CanManipulate()) { // 所持品から探すフラグON、取得者は操作が可能 // →所持品からベストな飲み物を探す inventoryThing = MizuUtility.BestWaterInInventory(getter, WaterPreferability.SeaWater, WaterPreferability.ClearWater, 0f, allowDrug); } if (inventoryThing != null) { // 所持品から見つかり、取得者はプレイヤーではない // →そのまま飲む if (getter.Faction != Faction.OfPlayer) { return(inventoryThing); } // プレイヤーだった場合 // →腐りかけならそのまま飲む if (inventoryThing.IsRotSoonForWater()) { return(inventoryThing); } } // プレイヤー&所持品の水は新鮮 // →マップからも探す Thing mapThing = MizuUtility.BestWaterSourceOnMap(getter, eater, priorQuality, allowBuilding, WaterPreferability.ClearWater, allowDrug, allowForbidden, allowSociallyImproper); if (eater.RaceProps.Animal && eater.Faction != Faction.OfPlayer) { // 野生の動物の場合、探したものが一定の距離以上であれば選択肢から除外 // 1個しかない水飲み場に全動物が集まるのを防ぐ if (mapThing != null && (eater.Position - mapThing.Position).LengthManhattan >= MizuUtility.SearchWaterRadiusForWildAnimal) { mapThing = null; } } if (inventoryThing == null && mapThing == null) { // 所持品にまともな水なし、マップからいかなる水も見つけられない // →ランクを落として所持品から探しなおす if (canUseInventory && getter.CanManipulate()) { // 見つかっても見つからなくてもその結果を返す return(MizuUtility.BestWaterInInventory(getter, WaterPreferability.SeaWater, WaterPreferability.ClearWater, 0f, allowDrug)); } // 所持品から探せる状態ではない return(null); } // 所持品にまともな水なし、マップから水が見つかった // →マップの水を取得 if (inventoryThing == null && mapThing != null) { return(mapThing); } // 所持品からまともな水が見つかった、マップからはいかなる水も見つけられない // →所持品の水を取得 if (inventoryThing != null && mapThing == null) { return(inventoryThing); } // 所持品からまともな水が、マップからは何らかの水が見つかった // →どちらが良いか評価(スコアが高い方が良い) float scoreMapThing = MizuUtility.GetWaterItemScore(eater, mapThing, (float)(getter.Position - mapThing.Position).LengthManhattan, priorQuality); float scoreInventoryThing = MizuUtility.GetWaterItemScore(eater, inventoryThing, 0f, priorQuality); // 所持品のほうを優先しやすくする scoreInventoryThing += 30f; // マップの水のほうが高スコア if (scoreMapThing > scoreInventoryThing) { return(mapThing); } // 所持品の水のほうが高スコア return(inventoryThing); }
public static float GetWaterItemScore(Pawn eater, Thing t, float dist, bool priorQuality) { var comp = t.TryGetComp <CompWaterSource>(); // 水源ではない or 水源として使えない if (comp == null || !comp.IsWaterSource) { return(float.MinValue); } // 食べられるものは飲み物としては選ばない方針 if (t.def.IsIngestible) { return(float.MinValue); } // 水アイテムなのに水分量が少ない(食事におまけで付いてる水分など) // 1個あたりが少なくても、一度に摂取できる量が多い場合は水分摂取アイテムとして有効 if (comp.SourceType == CompProperties_WaterSource.SourceType.Item && comp.WaterAmount * comp.MaxNumToGetAtOnce < Need_Water.MinWaterAmountPerOneDrink) { return(float.MinValue); } var waterTypeDef = MizuDef.Dic_WaterTypeDef[comp.WaterType]; // 基本点計算 // 距離 float distScore = -dist; // 心情変化量(水質) // メモ // きれい= +10 // 普通 = 0 // 生水 = 0 // 泥水 = -6 // 海水 = -6 float thoughtScore = 0f; // 禁欲の影響も含まれている foreach (var thought in MizuUtility.ThoughtsFromGettingWater(eater, t)) { thoughtScore += thought.stages[0].baseMoodEffect; } // 食中毒 // メモ // きれい= 0 => 0 // 普通 = 0 => 0 // 生水 = 0.01 => -10 // 泥水 = 0.03 => -30 // 海水 = 0.03 => -30 float foodPoisoningScore = -(waterTypeDef.foodPoisonChance * 1000f); // 健康悪化 float hediffScore = 0f; if (waterTypeDef.hediffs != null) { foreach (var hediff in waterTypeDef.hediffs) { hediffScore -= 100f; } } // 腐敗進行度 float rotScore = 0f; if (t.IsRotSoonForWater()) { rotScore += 10f; } // 飲むのにかかる時間 float drinkTickScore = 0f; if (comp.SourceType == CompProperties_WaterSource.SourceType.Item) { drinkTickScore = -comp.BaseDrinkTicks / 100f / comp.WaterAmount; } else if (comp.SourceType == CompProperties_WaterSource.SourceType.Building) { drinkTickScore = -comp.BaseDrinkTicks / 100f / 0.35f; } // 基本点合計メモ // 心情,食中毒,健康,合計(禁欲) // きれい= +10, 0, 0, +10( 0) // 普通 = 0, 0, 0, 0( 0) // 生水 = 0, -10, 0, -10( -10) // 泥水 = -6, -30, 0, -36( -30) // 海水 = -6, -30,-100,-136(-130) // 各種状態によるスコアの変化 // 水質優先モードか否か if (priorQuality) { distScore /= 10f; } return(distScore + thoughtScore + foodPoisoningScore + rotScore + drinkTickScore); }
public static float GetWaterTerrainScore(Pawn eater, IntVec3 c, float dist, bool priorQuality) { TerrainDef terrain = c.GetTerrain(eater.Map); // 水源ではない or 水源として使えない if (!terrain.IsWater()) { return(float.MinValue); } var waterTypeDef = MizuDef.Dic_WaterTypeDef[terrain.ToWaterType()]; // 基本点計算 // 距離 float distScore = -dist; // 心情変化量(水質) // メモ // きれい= +10 // 普通 = 0 // 生水 = 0 // 泥水 = -6 // 海水 = -6 float thoughtScore = 0f; // 禁欲の影響も含まれている List <ThoughtDef> thoughtList = new List <ThoughtDef>(); MizuUtility.ThoughtsFromWaterTypeDef(eater, waterTypeDef, true, thoughtList); foreach (var thought in thoughtList) { thoughtScore += thought.stages[0].baseMoodEffect; } // 食中毒 // メモ // きれい= 0 => 0 // 普通 = 0 => 0 // 生水 = 0.01 => -10 // 泥水 = 0.03 => -30 // 海水 = 0.03 => -30 float foodPoisoningScore = -(waterTypeDef.foodPoisonChance * 1000f); // 健康悪化 float hediffScore = 0f; if (waterTypeDef.hediffs != null) { foreach (var hediff in waterTypeDef.hediffs) { hediffScore -= 100f; } } // 基本点合計メモ // 心情,食中毒,健康,合計(禁欲) // きれい= +10, 0, 0, +10( 0) // 普通 = 0, 0, 0, 0( 0) // 生水 = 0, -10, 0, -10( -10) // 泥水 = -6, -30, 0, -36( -30) // 海水 = -6, -30,-100,-136(-130) // 各種状態によるスコアの変化 // 水質優先モードか否か if (priorQuality) { distScore /= 10f; } return(distScore + thoughtScore + foodPoisoningScore); }
public static Toil FinishDrawWater(TargetIndex drawerIndex) { Toil toil = new Toil(); toil.initAction = delegate { Pawn actor = toil.actor; Job curJob = actor.jobs.curJob; Thing thing = curJob.GetTarget(drawerIndex).Thing; if (thing == null) { actor.jobs.EndCurrentJob(JobCondition.Incompletable); return; } var building = thing as IBuilding_DrinkWater; if (building == null) { actor.jobs.EndCurrentJob(JobCondition.Incompletable); return; } // 生産物の生成 // 地下水脈の水の種類から水アイテムの種類を決定 var waterThingDef = MizuUtility.GetWaterThingDefFromWaterType(building.WaterType); if (waterThingDef == null) { actor.jobs.EndCurrentJob(JobCondition.Incompletable); return; } // 水アイテムの水源情報を得る var compprop = waterThingDef.GetCompProperties <CompProperties_WaterSource>(); if (compprop == null) { actor.jobs.EndCurrentJob(JobCondition.Incompletable); return; } // 地下水脈から水を減らす building.DrawWater(compprop.waterVolume); // 水を生成 var createThing = ThingMaker.MakeThing(waterThingDef); if (createThing == null) { actor.jobs.EndCurrentJob(JobCondition.Incompletable); return; } // 個数設定 createThing.stackCount = 1; // 水汲み記録追加 actor.records.AddTo(MizuDef.Record_WaterDrew, 1); // 床置き指定 if (!GenPlace.TryPlaceThing(createThing, actor.Position, actor.Map, ThingPlaceMode.Near, null)) { Log.Error(string.Concat(new object[] { actor, " could not drop recipe product ", thing, " near ", actor.Position })); } actor.jobs.EndCurrentJob(JobCondition.Succeeded, true); return; }; toil.defaultCompleteMode = ToilCompleteMode.Instant; return(toil); }
public static Toil DrinkFromBuilding(TargetIndex buildingIndex) { int initialTicks = 1; Toil toil = new Toil(); toil.initAction = delegate { var actor = toil.actor; var thing = actor.CurJob.GetTarget(buildingIndex).Thing; var comp = thing.TryGetComp <CompWaterSource>(); var building = thing as IBuilding_DrinkWater; if (actor.needs == null || actor.needs.water() == null || building == null || comp == null || !comp.IsWaterSource) { actor.jobs.EndCurrentJob(JobCondition.Incompletable); return; } var need_water = actor.needs.water(); var waterTypeDef = MizuDef.Dic_WaterTypeDef[comp.WaterType]; // 向きを変更 actor.rotationTracker.FaceCell(actor.Position); // 作業量 actor.jobs.curDriver.ticksLeftThisToil = (int)(comp.BaseDrinkTicks * need_water.WaterWanted); initialTicks = actor.jobs.curDriver.ticksLeftThisToil; if (actor.needs.mood != null) { // 水分摂取による心情変化 foreach (var thoughtDef in MizuUtility.ThoughtsFromGettingWater(actor, thing)) { actor.needs.mood.thoughts.memories.TryGainMemory(thoughtDef); } } // 指定された健康状態になる if (waterTypeDef.hediffs != null) { foreach (var hediff in waterTypeDef.hediffs) { actor.health.AddHediff(HediffMaker.MakeHediff(hediff, actor)); } } // 確率で食中毒 if (Rand.Value < waterTypeDef.foodPoisonChance) { FoodUtility.AddFoodPoisoningHediff(actor, thing, FoodPoisonCause.Unknown); } }; toil.tickAction = delegate { toil.actor.GainComfortFromCellIfPossible(); var need_water = toil.actor.needs.water(); var thing = toil.actor.CurJob.GetTarget(buildingIndex).Thing; var comp = thing.TryGetComp <CompWaterSource>(); var building = thing as IBuilding_DrinkWater; if (thing == null || comp == null || !comp.IsWaterSource || building == null || building.IsEmpty) { toil.actor.jobs.EndCurrentJob(JobCondition.Incompletable); return; } // 徐々に飲む float riseNeedWater = 1 / (float)comp.BaseDrinkTicks; need_water.CurLevel = Mathf.Min(need_water.CurLevel + riseNeedWater, need_water.MaxLevel); building.DrawWater(riseNeedWater * Need_Water.NeedWaterVolumePerDay); }; toil.WithProgressBar(buildingIndex, delegate { return(1f - (float)toil.actor.jobs.curDriver.ticksLeftThisToil / initialTicks); }, false, -0.5f); toil.defaultCompleteMode = ToilCompleteMode.Delay; toil.FailOn((t) => { Pawn actor = toil.actor; var target = actor.CurJob.GetTarget(buildingIndex); if (target.Thing.def.hasInteractionCell) { // 使用場所があるなら使用場所基準 return(target.Thing.InteractionCell.IsForbidden(actor) || !actor.CanReach(target.Thing.InteractionCell, PathEndMode.OnCell, Danger.Deadly)); } else { // 使用場所がないなら設備の場所基準 return(target.Thing.Position.IsForbidden(actor) || !actor.CanReach(target.Thing.Position, PathEndMode.ClosestTouch, Danger.Deadly)); } }); // エフェクト追加 toil.PlaySustainerOrSound(delegate { return(DefDatabase <SoundDef> .GetNamed("Ingest_Beer")); }); return(toil); }
public static float GetWater(Pawn getter, Thing thing, float waterWanted, bool withIngested) { // 摂取しようとしているものが既に消滅している(エラー) // 食事と同時に水分摂取する場合は既に消滅しているので無視する if (!withIngested && thing.Destroyed) { Log.Error(getter + " drank destroyed thing " + thing); return(0f); } // 現在飲めないはずのものを飲もうとしている(エラー) if (!thing.CanDrinkWaterNow()) { Log.Error(getter + " drank CanDrinkWaterNow()=false thing " + thing); return(0f); } if (getter.needs.mood != null) { // 水分摂取による心情変化 foreach (var thoughtDef in MizuUtility.ThoughtsFromGettingWater(getter, thing)) { getter.needs.mood.thoughts.memories.TryGainMemory(thoughtDef); } } // 健康状態の変化 var comp = thing.TryGetComp <CompWaterSource>(); if (comp == null) { Log.Error("comp is null"); return(0.0f); } if (!comp.IsWaterSource && comp.DependIngredients == false) { Log.Error("not watersource"); return(0.0f); } if (comp.SourceType != CompProperties_WaterSource.SourceType.Item) { Log.Error("source type is not item"); return(0.0f); } var waterType = MizuUtility.GetWaterType(thing); var waterTypeDef = MizuDef.Dic_WaterTypeDef[waterType]; // 指定された健康状態になる if (waterTypeDef.hediffs != null) { foreach (var hediff in waterTypeDef.hediffs) { getter.health.AddHediff(HediffMaker.MakeHediff(hediff, getter)); } } // 確率で食中毒 float animalFactor = getter.RaceProps.Humanlike ? 1f : 0.1f; // 動物は1/10に抑える if (Rand.Value < waterTypeDef.foodPoisonChance * animalFactor) { FoodUtility.AddFoodPoisoningHediff(getter, thing); } int drankWaterItemCount; float gotWaterAmount; // 摂取個数と摂取水分量の計算 thing.GetWaterCalculateAmounts(getter, waterWanted, withIngested, out drankWaterItemCount, out gotWaterAmount); if (withIngested) { // 食事の場合は後で個数を計算するのでここでは1個にする gotWaterAmount = comp.WaterAmount; drankWaterItemCount = 1; } // 食事と同時に水分摂取する場合は既に消滅しているので消滅処理をスキップする if (!withIngested && drankWaterItemCount > 0) { if (drankWaterItemCount == thing.stackCount) { // アイテム消費数とスタック数が同じ // →完全消滅 thing.Destroy(DestroyMode.Vanish); } else { // スタック数と異なる // →消費した数だけ減らす thing.SplitOff(drankWaterItemCount); } } return(gotWaterAmount); }
public static Toil DrinkTerrain(TargetIndex cellIndex, int baseDrinkTicksFromTerrain) { // 地形から水を飲む int initialTicks = 1; Toil toil = new Toil(); toil.initAction = delegate { var actor = toil.actor; var cell = actor.CurJob.GetTarget(cellIndex).Cell; var need_water = actor.needs.water(); if (need_water == null) { actor.jobs.EndCurrentJob(JobCondition.Incompletable); return; } var waterType = cell.GetTerrain(actor.Map).ToWaterType(); if (waterType == WaterType.NoWater || waterType == WaterType.Undefined) { actor.jobs.EndCurrentJob(JobCondition.Incompletable); return; } var waterTypeDef = MizuDef.Dic_WaterTypeDef[waterType]; // 向き変更 actor.rotationTracker.FaceCell(actor.Position); // 作業量 actor.jobs.curDriver.ticksLeftThisToil = (int)(baseDrinkTicksFromTerrain * need_water.WaterWanted); initialTicks = actor.jobs.curDriver.ticksLeftThisToil; if (actor.needs.mood != null) { // 水分摂取による心情変化 List <ThoughtDef> thoughtList = new List <ThoughtDef>(); MizuUtility.ThoughtsFromWaterTypeDef(actor, waterTypeDef, true, thoughtList); foreach (var thoughtDef in thoughtList) { actor.needs.mood.thoughts.memories.TryGainMemory(thoughtDef); } } // 指定された健康状態になる if (waterTypeDef.hediffs != null) { foreach (var hediff in waterTypeDef.hediffs) { actor.health.AddHediff(HediffMaker.MakeHediff(hediff, actor)); } } // 確率で食中毒 if (Rand.Value < waterTypeDef.foodPoisonChance) { actor.health.AddHediff(HediffMaker.MakeHediff(HediffDefOf.FoodPoisoning, actor)); if (PawnUtility.ShouldSendNotificationAbout(actor)) { var water = ThingMaker.MakeThing(MizuUtility.GetWaterThingDefFromWaterType(waterType)); string cause = "MizuPoisonedByDirtyWater".Translate().CapitalizeFirst(); string text = "MessageFoodPoisoning".Translate(actor.LabelShort, water.ToString(), cause, actor.Named("PAWN"), water.Named("FOOD")).CapitalizeFirst(); Messages.Message(text, actor, MessageTypeDefOf.NegativeEvent); } } }; toil.tickAction = delegate { toil.actor.GainComfortFromCellIfPossible(); var need_water = toil.actor.needs.water(); var cell = toil.actor.CurJob.GetTarget(cellIndex).Cell; if (need_water == null) { toil.actor.jobs.EndCurrentJob(JobCondition.Incompletable); return; } // 徐々に飲む float riseNeedWater = 1 / (float)baseDrinkTicksFromTerrain; need_water.CurLevel = Mathf.Min(need_water.CurLevel + riseNeedWater, need_water.MaxLevel); }; toil.WithProgressBar(cellIndex, delegate { return(1f - (float)toil.actor.jobs.curDriver.ticksLeftThisToil / initialTicks); }, false, -0.5f); toil.defaultCompleteMode = ToilCompleteMode.Delay; toil.FailOn((t) => { Pawn actor = toil.actor; return(actor.CurJob.targetA.Cell.IsForbidden(actor) || !actor.CanReach(actor.CurJob.targetA.Cell, PathEndMode.OnCell, Danger.Deadly)); }); // エフェクト追加 toil.PlaySustainerOrSound(delegate { return(DefDatabase <SoundDef> .GetNamed("Ingest_Beer")); }); return(toil); }
protected override Job TryGiveJob(Pawn pawn) { // 所持品インスタンスがない if (pawn.inventory == null) { return(null); } // 水分要求がない var need_water = pawn.needs.Water(); if (need_water == null) { return(null); } bool validator(Thing t) { // 食べられるものは携帯飲料としては選ばれない if (t.def.IsIngestible) { return(false); } var comp = t.TryGetComp <CompWaterSource>(); if (comp == null) { return(false); // 水源でないもの× } if (!comp.IsWaterSource) { return(false); // 水源でないもの× } if (comp.SourceType != CompProperties_WaterSource.SourceType.Item) { return(false); // 水アイテムではないもの× } if (comp.WaterAmount * comp.MaxNumToGetAtOnce < Need_Water.MinWaterAmountPerOneDrink) { return(false); // 最低水分量を満たしていないもの× } if (MizuDef.Dic_WaterTypeDef[comp.WaterType].waterPreferability < MinWaterPreferability) { return(false); // 最低の水質を満たしていないもの× } return(true); } // 既に条件を満たしたアイテムを持っているか? foreach (var thing in pawn.inventory.innerContainer) { if (!validator(thing)) { continue; } return(null); } // マップ中の水アイテムの合計水分量が、最低限必要とされる水の量(×入植者の人数)以下しかなければ、 // 個人の所持品には入れない if (pawn.Map.resourceCounter.TotalWater() < pawn.Map.mapPawns.ColonistsSpawnedCount * MinWaterPerColonistToDo) { return(null); } var waterThing = GenClosest.ClosestThing_Regionwise_ReachablePrioritized( pawn.Position, pawn.Map, ThingRequest.ForGroup(ThingRequestGroup.HaulableEver), PathEndMode.ClosestTouch, TraverseParms.For(pawn), 20f, t => { if (!validator(t)) { return(false); // 所持品チェック時と同じ条件を満たしていない× } if (t.IsForbidden(pawn)) { return(false); // 禁止されている× } if (!pawn.CanReserve(t)) { return(false); // 予約不可能× } if (!t.IsSociallyProper(pawn)) { return(false); // 囚人部屋の物× } return(true); }, // スコアの高いものが優先? x => MizuUtility.GetWaterItemScore(pawn, x, 0f, true)); if (waterThing == null) { return(null); } return(new Job(JobDefOf.TakeInventory, waterThing) { count = Mathf.Min(MizuUtility.StackCountForWater(waterThing, NeedTotalWaterAmount), waterThing.stackCount) }); }
public static int WillGetStackCountOf(Pawn getter, Thing thing) { var comp = thing.TryGetComp <CompWaterSource>(); // 水源ではない→摂取数0 if (comp == null || !comp.IsWaterSource) { return(0); } // アイテムではない→摂取数0 if (comp.SourceType != CompProperties_WaterSource.SourceType.Item) { return(0); } // それを一度に摂取できる数と、何個摂取すれば水分が100%になるのか、の小さい方 int wantedWaterItemCount = Math.Min(thing.TryGetComp <CompWaterSource>().MaxNumToGetAtOnce, MizuUtility.StackCountForWater(thing, getter.needs.water().WaterWanted)); // 1個未満なら1個にする if (wantedWaterItemCount < 1) { return(1); } return(wantedWaterItemCount); }