private static Blueprint PlaceEntranceBlueprint() { if (!availableCrates.Any(c => c.def == _DefOf.Carn_Crate_Stall)) { return(null); } ThingDef bannerDef = _DefOf.Carn_SignEntry; Rot4 rot = default(Rot4); IntVec3 bannerSpot = FindRadialPlacementFor(bannerDef, rot, info.bannerCell, 16); if (bannerSpot.IsValid) { info.bannerCell = bannerSpot; if (Prefs.DevMode) { Log.Message("[Carnivale] bannerCell final pass: "******"Couldn't find an optimum place for " + bannerDef + ". Trying random place in carnival area."); bannerSpot = FindRandomPlacementFor(bannerDef, rot); if (bannerSpot.IsValid) { info.bannerCell = bannerSpot; if (Prefs.DevMode) { Log.Message("[Carnivale] bannerCell final pass: "******"Couldn't find any place for " + bannerDef + ". Not retrying."); return(null); }
private static Blueprint PlaceTrashBlueprint() { var signDef = _DefOf.Carn_SignTrash; var noGo = CellRect.CenteredOn(info.bannerCell, 10); IntVec3 trashPos; if (!CellRect.CenteredOn(info.bannerCell, 15).Cells .Where(c => !noGo.Contains(c)) .TryRandomElement(out trashPos)) { if (Prefs.DevMode) { Log.Message("[Carnivale] New trash position calculation failed. Using old method."); } trashPos = info.carnivalArea.ContractedBy(5).FurthestCellFrom(cachedPos.Average(), true, delegate(IntVec3 c) { if (noGo.Contains(c)) { return(false); } return(true); }); } if (!CanPlaceBlueprintAt(trashPos, signDef)) { trashPos = FindRadialPlacementFor(signDef, default(Rot4), trashPos, 10); } if (!trashPos.IsValid) { Log.Error("[Carnivale] Could not find any place for a trash spot. Trash will not be hauled."); info.TrashCentre = IntVec3.Invalid; return(null); } info.TrashCentre = trashPos; RemoveFirstCrateOf(ThingDefOf.WoodLog); CarnUtils.ClearThingsFor(info.map, trashPos, new IntVec2(4, 4), default(Rot4)); return(PlaceBlueprint(signDef, trashPos, default(Rot4), ThingDefOf.WoodLog)); }
private static IEnumerable <Blueprint> PlaceGameBlueprints() { var gameMasters = stallUsers.Where(p => p.TraderKind == null).ToList(); var gameCrates = availableCrates.ListFullCopy().Where(c => c.def.entityDefToBuild != null); var points = new IntVec3[] { info.setupCentre, info.bannerCell }; var gameSpot = CellRect.CenteredOn(points.Average(), 7).FurthestCellFrom(info.carnivalArea.EdgeCells.RandomElement()); ThingDef gameDef; int i = 0; foreach (var crate in gameCrates) { gameDef = (ThingDef)crate.def.entityDefToBuild; gameSpot = FindRadialPlacementFor(gameDef, default(Rot4), gameSpot, 20); if (gameSpot.IsValid) { RemoveFirstCrateOf(crate.def); CarnUtils.ClearThingsFor(info.map, gameSpot, gameDef.size, default(Rot4), 2); if (i < gameMasters.Count) { IntVec3 gameMasterSpot = gameSpot + gameDef.interactionCellOffset + new IntVec3(-1, 0, 1); info.rememberedPositions.Add(gameMasters[i++], gameMasterSpot); } yield return(PlaceBlueprint(gameDef, gameSpot)); } else { Log.Error("Found no place for " + gameDef + ". It will not be built."); } } }
protected List <Pawn> SpawnPawns(IncidentParms parms, int spawnPointSpread) { Map map = (Map)parms.target; if (parms.spawnRotation != Rot4.East) { if (Prefs.DevMode) { Log.Message("[Carnivale] Spawn centre for CarnivalArrives was not precomputed. Resolving now."); } IntVec3 tempSpot = parms.spawnCenter; if (CarnCellFinder.BestCarnivalSpawnSpot(map, out tempSpot)) { parms.spawnCenter = tempSpot; } else if (Prefs.DevMode) { Log.Warning("[Carnivale] Failed to resolve spawn center for CarnivalArrives. Defaulting."); } int feePerColonist = CarnUtils.CalculateFeePerColonist(parms.points); CarnUtils.Info.feePerColonist = -feePerColonist; } PawnGroupMakerParms defaultMakerParms = IncidentParmsUtility.GetDefaultPawnGroupMakerParms(parms); List <Pawn> list = PawnGroupMakerUtility.GeneratePawns(this.PawnGroupKindDef, defaultMakerParms, false).ToList(); foreach (Pawn p in list) { IntVec3 spawnPoint = CellFinder.RandomClosewalkCellNear(parms.spawnCenter, map, spawnPointSpread, null); GenSpawn.Spawn(p, spawnPoint, map); } parms.faction.leader.skills.GetSkill(SkillDefOf.Shooting).levelInt = 9; return(list); }
public override void MapLoaded(Map map) { base.MapLoaded(map); CarnUtils.Cleanup(); }
public override bool TryExecute(IncidentParms parms) { Map map = (Map)parms.target; IntVec3 spawnSpot; int durationDays = Mathf.RoundToInt(this.def.durationDays.RandomInRange); if (Prefs.DevMode) { Log.Message("[Carnivale] Calculating spawn centre:"); } if (!CarnCellFinder.BestCarnivalSpawnSpot(map, out spawnSpot)) { if (Prefs.DevMode) { Log.Warning("[Carnivale] Tried to execute incident CarnivalApproaches, failed to find reachable spawn spot."); } return(false); } if (!FindCarnivalFaction(out parms.faction)) { if (Prefs.DevMode) { Log.Warning("[Carnivale] Tried to execute incident CarnivalApproaches, failed to find valid faction."); } return(false); } // Calculate fe int feePerColonist = CarnUtils.CalculateFeePerColonist(parms.points); // Main dialog node string title = "CarnivalApproachesTitle".Translate(parms.faction.Name); DiaNode initialNode = new DiaNode("CarnivalApproachesInitial".Translate(new object[] { parms.faction.Name, durationDays, feePerColonist + " " + ThingDefOf.Silver.label, map.info.parent.Label == "Colony" ? "your colony" : map.info.parent.Label })); // Accept button DiaOption acceptOption = new DiaOption("CarnivalApproachesAccept".Translate()); acceptOption.action = delegate { // Do accept action parms.faction.AffectGoodwillWith(Faction.OfPlayer, acceptanceBonus); IncidentParms arrivalParms = StorytellerUtility.DefaultParmsNow(Find.Storyteller.def, IncidentCategory.AllyArrival, map); //arrivalParms.forced = true; // forcing not necessary arrivalParms.faction = parms.faction; arrivalParms.spawnCenter = spawnSpot; arrivalParms.points = parms.points; // Do this? // This is so it can be determined that the spawnpoint was precomputed. Rot4 sneakyValueForSpawnpointResolved = Rot4.East; arrivalParms.spawnRotation = sneakyValueForSpawnpointResolved; // This is super cheaty, but there is no other field to pass this to. arrivalParms.raidPodOpenDelay = durationDays; // End cheaty. // Assign fee per colonist to CarnivalInfo CarnUtils.Info.feePerColonist = -feePerColonist; QueuedIncident qi = new QueuedIncident(new FiringIncident(_DefOf.CarnivalArrives, null, arrivalParms), Find.TickManager.TicksGame + GenDate.TicksPerDay); Find.Storyteller.incidentQueue.Add(qi); }; initialNode.options.Add(acceptOption); // Accept thank you message DiaNode acceptedMessage = new DiaNode("CarnivalApproachesAcceptMessage".Translate(new object[] { parms.faction.leader.Name.ToStringFull })); DiaOption ok = new DiaOption("OK".Translate()); ok.resolveTree = true; acceptedMessage.options.Add(ok); acceptOption.link = acceptedMessage; // Reject button DiaOption rejectOption = new DiaOption("CarnivalApproachesReject".Translate()); rejectOption.action = delegate { // Do reject action parms.faction.AffectGoodwillWith(Faction.OfPlayer, rejectionPenalty); }; initialNode.options.Add(rejectOption); // Reject f**k you message (TODO: randomise response) DiaNode rejectedMessage = new DiaNode("CarnivalApproachesRejectMessage".Translate(new object[] { parms.faction.leader.Name.ToStringShort })); DiaOption hangup = new DiaOption("HangUp".Translate()); hangup.resolveTree = true; rejectedMessage.options.Add(hangup); rejectOption.link = rejectedMessage; // Draw dialog Find.WindowStack.Add(new Dialog_NodeTree(initialNode, true, true, title)); return(true); }
public override bool TryExecute(IncidentParms parms) { Map map = (Map)parms.target; // Cheaty: int durationDays = parms.raidPodOpenDelay == 140 ? 3 : parms.raidPodOpenDelay; // End cheaty. // Resolve parms (currently counting on parent class to handle this) if (!base.TryResolveParms(parms)) { if (Prefs.DevMode) { Log.Warning("[Carnivale] Could not execute CarnivalArrives: the spawn point calculated yesterday is probably no longer valid."); } return(false); } // Spawn pawns. Counting on you, IncidentWorker_NeutralGroup. List <Pawn> pawns = this.SpawnPawns(parms, 17); if (pawns.Count < 3) { if (Prefs.DevMode) { Log.Warning("[Carnivale] Could not execute CarnivalArrives: could not generate enough valid pawns."); } return(false); } List <Pawn> vendors = new List <Pawn>(); foreach (Pawn p in pawns) { if (p.TraderKind != null) { // Get list of vendors vendors.Add(p); } if (p.needs != null && p.needs.food != null) { // Also feed the carnies p.needs.food.CurLevel = p.needs.food.MaxLevel; } } string label = "LetterLabelCarnivalArrival".Translate(); string text = "LetterCarnivalArrival".Translate(parms.faction.Name, durationDays); if (vendors.Count > 0) { text += "CarnivalArrivalVendorsList".Translate(); foreach (Pawn vendor in vendors) { text += "\n " + vendor.NameStringShort + ", " + vendor.TraderKind.label.CapitalizeFirst(); } } PawnRelationUtility.Notify_PawnsSeenByPlayer(pawns, ref label, ref text, "LetterRelatedPawnsNeutralGroup".Translate(), true); Find.LetterStack.ReceiveLetter(label, text, LetterDefOf.Good, parms.faction.leader, null); //LordJob_EntertainColony lordJob = new LordJob_EntertainColony(durationDays); //Lord lord = LordMaker.MakeNewLord(parms.faction, lordJob, map, pawns); //CarnivalUtils.Info.ReInitWith(lord, parms.spawnCenter); CarnUtils.MakeNewCarnivalLord(parms.faction, map, parms.spawnCenter, durationDays, pawns); return(true); }
private static IEnumerable <Blueprint> PlaceTentBlueprints() { // main chapiteau ThingDef tentDef; Rot4 rot = info.setupCentre.RotationFacing(info.bannerCell).Opposite; IntVec3 tentSpot; if (availableCrates.Any(c => c.def == _DefOf.Carn_Crate_TentHuge)) { tentDef = _DefOf.Carn_TentChap; tentSpot = FindRadialPlacementFor(tentDef, rot, info.setupCentre, 11); if (tentSpot.IsValid) { RemoveFirstCrateOf(_DefOf.Carn_Crate_TentHuge); CarnUtils.ClearThingsFor(info.map, tentSpot, tentDef.size, rot, 2); yield return(PlaceBlueprint(tentDef, tentSpot, rot)); } else { Log.Error("Could not find placement for " + tentDef + ", which is a major attraction. Tell Xnope to get it together."); } } // lodging tents tentDef = _DefOf.Carn_TentLodge; rot = Rot4.Random; tentSpot = FindRadialPlacementFor(tentDef, rot, info.carnivalArea.ContractedBy(9).FurthestCellFrom(CellsUtil.AverageColonistPosition(info.map)), 7); IntVec3 lineDirection = rot.ToIntVec3(1); // shifted clockwise by 1 int numFailures = 0; bool firstNewPass = true; while (numFailures < 30 && availableCrates.Any(t => t.def == _DefOf.Carn_Crate_TentLodge)) { // Following works as intended iff size.x == size.y if (!firstNewPass) { // try adding tent next in a an adjacent line tentSpot += lineDirection * ((tentDef.size.x + 1)); } if (CanPlaceBlueprintAt(tentSpot, tentDef, rot)) { // bingo firstNewPass = false; RemoveFirstCrateOf(_DefOf.Carn_Crate_TentLodge); CarnUtils.ClearThingsFor(info.map, tentSpot, tentDef.size, rot, 1); yield return(PlaceBlueprint(tentDef, tentSpot, rot)); } else if (numFailures % 3 != 0) { // try different line directions rot.AsByte++; lineDirection = rot.ToIntVec3(1); numFailures++; } else { // Find new placement if next spot and any of its rotations don't work tentSpot = FindRadialPlacementFor(tentDef, rot, tentSpot, (int)info.baseRadius / 2); if (!tentSpot.IsValid) { // suboptimal random placement tentSpot = FindRandomPlacementFor(tentDef, rot, false, 5); } firstNewPass = true; } } if (numFailures == 30 && availableCrates.Any(t => t.def == _DefOf.Carn_Crate_TentLodge)) { Log.Error("Tried too many times to place tents. Some may not be built."); } // manager tent if (!availableCrates.Any(c => c.def == _DefOf.Carn_Crate_TentMan)) { yield break; } rot = Rot4.Random; tentDef = _DefOf.Carn_TentLodgeMan; // Try to place near other tents tentSpot = FindRadialCardinalPlacementFor(tentDef, rot, tentSpot, 10); if (tentSpot.IsValid) { RemoveFirstCrateOf(_DefOf.Carn_Crate_TentMan); CarnUtils.ClearThingsFor(info.map, tentSpot, tentDef.size, rot, 1); yield return(PlaceBlueprint(tentDef, tentSpot, rot)); } else { // suboptimal placement tentSpot = FindRadialPlacementFor(tentDef, rot, info.setupCentre, (int)info.baseRadius / 2); if (tentSpot.IsValid) { RemoveFirstCrateOf(_DefOf.Carn_Crate_TentMan); CarnUtils.ClearThingsFor(info.map, tentSpot, tentDef.size, rot, 1); yield return(PlaceBlueprint(tentDef, tentSpot, rot)); } else { Log.Error("Found no valid placement for manager tent. It will not be placed."); } } }
private static IEnumerable <Blueprint> PlaceStallBlueprints() { ThingDef stallDef = _DefOf.Carn_StallFood; IntVec3 stallSpot = IntVec3.Invalid; Rot4 rot = default(Rot4); foreach (Pawn stallUser in stallUsers.Where(p => p.TraderKind != null)) { // Handle different kinds of vendor stalls if (stallUser.TraderKind == _DefOf.Carn_Trader_Food) { stallDef = _DefOf.Carn_StallFood; } else if (stallUser.TraderKind == _DefOf.Carn_Trader_Surplus) { stallDef = _DefOf.Carn_StallSurplus; } else if (stallUser.TraderKind == _DefOf.Carn_Trader_Curios) { stallDef = _DefOf.Carn_StallCurios; } else { Log.Error("Trader " + stallUser.NameStringShort + " is not a carnival vendor and will get no stall."); continue; } if (stallSpot.IsValid) { if (Rand.Chance(0.8f)) { // Next spot should be close to last spot stallSpot = FindRadialCardinalPlacementFor(stallDef, rot, stallSpot, 10); } else { // Next spot should be random IntVec3[] points = new IntVec3[] { stallSpot, stallSpot, info.carnivalArea.EdgeCells.RandomElement() }; stallSpot = FindRadialPlacementFor(stallDef, rot, points.Average(), 10); } } else { IntVec3[] points = new IntVec3[] { info.setupCentre, info.bannerCell }; stallSpot = FindRadialPlacementFor(stallDef, rot, points.Average(), 10); } // Finally, spawn the f*cker if (stallSpot.IsValid) { RemoveFirstCrateOf(_DefOf.Carn_Crate_Stall); CarnUtils.ClearThingsFor(info.map, stallSpot, stallDef.size, rot, 2); // Add spot to stall user's spot info.rememberedPositions.Add(stallUser, stallSpot); yield return(PlaceBlueprint(stallDef, stallSpot, rot)); } } }
public override IEnumerable <FloatMenuOption> CompFloatMenuOptions(Pawn pawn) { if (Props.useJob == null) { // no useJob specified yield break; } if (parent.Faction != pawn.Faction) { if (!Info.Active) { // carnival is not in town for whatever reason yield break; } else if (!Info.entertainingNow) { // Carnival is closed yield return(new FloatMenuOption(this.FloatMenuOptionLabel + " (Carnival closed)", null)); } else if (!pawn.CanReserve(this.parent)) { // Already reserved yield return(new FloatMenuOption(this.FloatMenuOptionLabel + " (" + "Reserved".Translate() + ")", null)); } else if (Props.useJob == _DefOf.Job_PayEntryFee) { // Pay entry fee yield return(new FloatMenuOption(this.FloatMenuOptionLabel + " (" + Info.feePerColonist + ")", delegate { var silverCount = new ThingCountClass(ThingDefOf.Silver, Info.feePerColonist); var silverStack = CarnUtils.FindClosestThings(pawn, silverCount); if (silverStack != null && pawn.CanReserveAndReach(silverStack, PathEndMode.Touch, pawn.NormalMaxDanger())) { var job = new Job(Props.useJob, silverStack) { expiryInterval = GenDate.TicksPerHour * 10 }; pawn.jobs.TryTakeOrderedJob(job, JobTag.Misc); } })); } else if (!Info.allowedColonists.Contains(pawn)) { // Pawn hasn't payed entry fee yield return(new FloatMenuOption(this.FloatMenuOptionLabel + " (Must pay fee at entrance)", null)); } else if (Props.type.Is(CarnBuildingType.Stall | CarnBuildingType.Attraction)) { // Do use job (mostly for games) yield return(new FloatMenuOption(this.FloatMenuOptionLabel, delegate { if (pawn.CanReserveAndReach(this.parent, PathEndMode.InteractionCell, Danger.None)) { this.TryStartUseJob(pawn); } })); } } else { if (!pawn.CanReserve(this.parent)) { // Already reserved yield return(new FloatMenuOption(this.FloatMenuOptionLabel + " (" + "Reserved".Translate() + ")", null)); } else if (Props.type.Is(CarnBuildingType.Stall | CarnBuildingType.Attraction)) { // Do use job (mostly for games) yield return(new FloatMenuOption(this.FloatMenuOptionLabel, delegate { if (pawn.CanReserveAndReach(this.parent, PathEndMode.InteractionCell, Danger.None)) { this.TryStartUseJob(pawn); } })); } } }
public override void SpawnSetup(Map map, bool respawningAfterLoad) { // Forbid things so colonists can access them after they are hauled to trash if (this.factionInt != Faction.OfPlayer) { var occ = this.OccupiedRect().ExpandedBy(2); var tele = occ.ExpandedBy(1); foreach (var cell in occ) { foreach (var thing in cell.GetThingList(map)) { if (thing.def.EverHaulable) { thing.SetForbidden(true, false); } if (thing is Pawn && (thing.Faction == null || thing.Faction != this.Faction)) { thing.Position = tele.Cells.Where(c => !c.GetThingList(map).Any(t => t is Building)).RandomElement(); } } } } // Build interior if (Props.interiorThings.Any() && !childBuildings.Any(b => Props.interiorThings.Any(t => t.thingDef == b.def))) { this.oldPosition = Position; foreach (ThingPlacement tp in Props.interiorThings) { foreach (IntVec3 offset in tp.placementOffsets) { IntVec3 cell = Position + offset.RotatedBy(Rotation); ThingDef stuff = tp.thingDef.MadeFromStuff ? GenStuff.DefaultStuffFor(tp.thingDef) : null; Building building = ThingMaker.MakeThing(tp.thingDef, stuff) as Building; building.SetFaction(this.Faction); building.Position = cell; building.Rotation = this.Rotation.Opposite; // Give manager a specific bed: if (this.Type.Is(CarnBuildingType.Bedroom | CarnBuildingType.ManagerOnly)) { if (building is Building_Bed && this.Faction != Faction.OfPlayer) { this.Faction.leader.ownership.ClaimBedIfNonMedical((Building_Bed)building); } } childBuildings.Add(building); //Utilities.SpawnThingNoWipe(building, cell, map, Rotation.Opposite, respawningAfterLoad); } } } foreach (var child in childBuildings) { // spawn here if (!child.Spawned) { // offset is necessary for reinstallation IntVec3 offset = child.Position - this.oldPosition; child.Position = this.Position + offset; CarnUtils.SpawnThingNoWipe(child, map, respawningAfterLoad); } } base.SpawnSetup(map, respawningAfterLoad); if (childBuildings.Any()) { // Necessary for reinstalling properly this.oldPosition = this.Position; } }
// OVERRIDE METHODS // public override void Init() { base.Init(); LordToilData_SetupCarnival data = (LordToilData_SetupCarnival)this.data; // Give em a wood for trash sign var log = ThingMaker.MakeThing(ThingDefOf.WoodLog); log.stackCount = 1; GenPlace.TryPlaceThing(log, Info.setupCentre, Map, ThingPlaceMode.Near); // Give chapiteau if (data.TryHaveWorkerCarry(_DefOf.Carn_Crate_TentHuge, 1, CarnUtils.RandomFabricByCheapness()) != 1) { Log.Error("[Carnivale] Could not give " + _DefOf.Carn_Crate_TentHuge + " to carnies of faction " + lord.faction + ". It will not be built."); } // Give lodging tents (currently 8 carnies per lodging tent) int numCarnies = this.lord.ownedPawns.Count - Info.pawnsWithRole[CarnivalRole.Carrier].Count; int numBedTents = numCarnies > 9 ? Mathf.CeilToInt(numCarnies / 8f) : 1; if (data.TryHaveWorkerCarry(_DefOf.Carn_Crate_TentLodge, numBedTents, CarnUtils.RandomFabricByCheapness()) != numBedTents) { Log.Error("[Carnivale] Could not give enough " + _DefOf.Carn_Crate_TentLodge + " to carnies of faction " + lord.faction + ". Some will not be built."); } if (Info.pawnsWithRole[CarnivalRole.Manager].Any()) { if (data.TryHaveWorkerCarry(_DefOf.Carn_Crate_TentMan, 1, CarnUtils.RandomFabricByExpensiveness()) != 1) { Log.Error("[Carnivale] Could not give " + _DefOf.Carn_Crate_TentMan + " to carnies of faction " + lord.faction + ". It will not be built."); } } // Give vendor stalls + entry sign int numStallCrates = Info.pawnsWithRole[CarnivalRole.Vendor].Count + _DefOf.Carn_SignEntry.costList.First().count; data.TryHaveWorkerCarry(_DefOf.Carn_Crate_Stall, numStallCrates, ThingDefOf.WoodLog); // Give game stalls data.TryHaveWorkerCarry(_DefOf.Carn_Crate_GameHighStriker, 1, ThingDefOf.WoodLog); // Place blueprints foreach (Blueprint bp in AIBlueprintsUtility.PlaceCarnivalBlueprints(Info)) { data.blueprints.Add(bp); } // Find spots for carriers to chill + a guard spot var guardSpot = GetCarrierSpots().Average(); // Assign guard spot at carriers Pawn guard = Info.GetBestGuard(); if (guard != null) { //Info.rememberedPositions.Add(guard, guardSpot); Info.guardPositions.Add(guardSpot); // if can feed animals, give em kibble if (!guard.story.WorkTypeIsDisabled(WorkTypeDefOf.Handling)) { Thing kib = ThingMaker.MakeThing(ThingDefOf.Kibble); kib.stackCount = 75; guard.inventory.TryAddItemNotForSale(kib); } } }
public override void Cleanup() { base.Cleanup(); Info.Cleanup(); CarnUtils.Cleanup(); }