// Determine if the scav is well-suited for its mission. In vanilla this will ensure the trader has the region's special. // We do the same for traders here. private static void ScavengerAbstractAI_UpdateMissionAppropriateGear(On.ScavengerAbstractAI.orig_UpdateMissionAppropriateGear orig, ScavengerAbstractAI self) { orig(self); if (self.squad == null || self.squad.missionType != ScavengerAbstractAI.ScavengerSquad.MissionID.Trade) { return; } World world = self.world; if (world == null || world.singleRoomWorld || world.region == null || !TryGetSpecialScavItem(world.region.name, out AbstractPhysicalObject.AbstractObjectType specialItem)) { return; } self.missionAppropriateGear = false; for (int j = 0; j < self.parent.stuckObjects.Count; j++) { if (self.parent.stuckObjects[j] is AbstractPhysicalObject.CreatureGripStick && self.parent.stuckObjects[j].A == self.parent && self.parent.stuckObjects[j].B.type == specialItem) { self.missionAppropriateGear = true; return; } } }
// Hook me :) // Returns null on failure to instantiate // Instantiates an item of type inside ~~mind of scav~~ probably in offscreen den where scav is private static AbstractPhysicalObject InstantiateCustomScavItemAbstract(AbstractPhysicalObject.AbstractObjectType type, ScavengerAbstractAI self) { EntityID id = self.world.game.GetNewID(); switch (type) { // Commented out : safe default OR doesn't really work //case AbstractPhysicalObject.AbstractObjectType.Creature: //case AbstractPhysicalObject.AbstractObjectType.Rock: case AbstractPhysicalObject.AbstractObjectType.Spear: return(new AbstractSpear(self.world, null, self.parent.pos, id, self.IsSpearExplosive((!self.world.game.IsStorySession) ? 0 : (self.world.game.GetStorySession.saveState.cycleNumber + ((self.world.game.StoryCharacter != 2) ? 0 : 60))))); //case AbstractPhysicalObject.AbstractObjectType.FlareBomb: case AbstractPhysicalObject.AbstractObjectType.VultureMask: return(new VultureMask.AbstractVultureMask(self.world, null, self.parent.pos, id, id.RandomSeed, false)); //case AbstractPhysicalObject.AbstractObjectType.PuffBall: //case AbstractPhysicalObject.AbstractObjectType.DangleFruit: //case AbstractPhysicalObject.AbstractObjectType.Oracle: case AbstractPhysicalObject.AbstractObjectType.PebblesPearl: return(new PebblesPearl.AbstractPebblesPearl(self.world, null, self.parent.pos, id, -1, -1, null, id.RandomSeed % 3, id.RandomSeed % 3)); //case AbstractPhysicalObject.AbstractObjectType.SLOracleSwarmer: //case AbstractPhysicalObject.AbstractObjectType.SSOracleSwarmer: case AbstractPhysicalObject.AbstractObjectType.DataPearl: return(new DataPearl.AbstractDataPearl(self.world, AbstractPhysicalObject.AbstractObjectType.DataPearl, null, self.parent.pos, id, -1, -1, null, DataPearl.AbstractDataPearl.DataPearlType.Misc)); case AbstractPhysicalObject.AbstractObjectType.SeedCob: // Fails miserably return(new SeedCob.AbstractSeedCob(self.world, null, self.parent.pos, id, -1, -1, false, null)); case AbstractPhysicalObject.AbstractObjectType.WaterNut: return(new WaterNut.AbstractWaterNut(self.world, null, self.parent.pos, id, -1, -1, null, false)); //case AbstractPhysicalObject.AbstractObjectType.JellyFish: //case AbstractPhysicalObject.AbstractObjectType.Lantern: //case AbstractPhysicalObject.AbstractObjectType.KarmaFlower: //case AbstractPhysicalObject.AbstractObjectType.Mushroom: //case AbstractPhysicalObject.AbstractObjectType.VoidSpawn: //case AbstractPhysicalObject.AbstractObjectType.FirecrackerPlant: //case AbstractPhysicalObject.AbstractObjectType.SlimeMold: //case AbstractPhysicalObject.AbstractObjectType.FlyLure: //case AbstractPhysicalObject.AbstractObjectType.ScavengerBomb: case AbstractPhysicalObject.AbstractObjectType.SporePlant: return(new SporePlant.AbstractSporePlant(self.world, null, self.parent.pos, id, -1, -1, null, false, true)); //case AbstractPhysicalObject.AbstractObjectType.AttachedBee: case AbstractPhysicalObject.AbstractObjectType.EggBugEgg: int seed = UnityEngine.Random.seed; UnityEngine.Random.seed = id.RandomSeed; float hue = Mathf.Lerp(-0.15f, 0.1f, Custom.ClampedRandomVariation(0.5f, 0.5f, 2f)); UnityEngine.Random.seed = seed; return(new EggBugEgg.AbstractBugEgg(self.world, null, self.parent.pos, id, hue)); //case AbstractPhysicalObject.AbstractObjectType.NeedleEgg: //case AbstractPhysicalObject.AbstractObjectType.DartMaggot: case AbstractPhysicalObject.AbstractObjectType.BubbleGrass: return(new BubbleGrass.AbstractBubbleGrass(self.world, null, self.parent.pos, id, 1f, -1, -1, null)); //case AbstractPhysicalObject.AbstractObjectType.NSHSwarmer: case AbstractPhysicalObject.AbstractObjectType.OverseerCarcass: return(new OverseerCarcass.AbstractOverseerCarcass(self.world, null, self.parent.pos, id, new Color(0.1f, 0.1f, 0.1f), 0)); default: try { if (AbstractConsumable.IsTypeConsumable(type)) { return(new AbstractConsumable(self.parent.world, type, null, self.parent.pos, self.world.game.GetNewID(), -1, -1, null)); } else { return(new AbstractPhysicalObject(self.parent.world, type, null, self.parent.pos, self.world.game.GetNewID())); } } catch (Exception) { return(null); // This is nicer to manage if modders hook this method to spawn their own things. } } }
// Called on scav spawn while offscreen // If region has special && roll random chance of special on gerup: equip special // Causes scav to drop a rock or spear if inventory full. If inventory full of other stuff, skip special. private static void ScavengerAbstractAI_InitGearUp(On.ScavengerAbstractAI.orig_InitGearUp orig, ScavengerAbstractAI self) { orig(self); World world = self.world; if (world == null || world.singleRoomWorld || world.region == null || !TryGetSpecialScavItem(world.region.name, out AbstractPhysicalObject.AbstractObjectType specialItem)) { return; } if (UnityEngine.Random.value < GetChanceOfSpecialItemOnGear(world.region.name)) { // Find free grasp // Gearup normally fills 3 -> 2 -> 1 -> 0 'indexes' // Find min int grasp = int.MaxValue; for (int j = self.parent.stuckObjects.Count - 1; j >= 0; j--) { if (self.parent.stuckObjects[j] is AbstractPhysicalObject.CreatureGripStick stick && self.parent.stuckObjects[j].A == self.parent) { if (stick.grasp < grasp) { grasp = stick.grasp; } } } if (grasp == int.MaxValue) { grasp = 3; } else { grasp--; // Next } if (grasp < 0) // Full { // going through objects last to first, drop a rock or spear for (int k = self.parent.stuckObjects.Count - 1; k >= 0; k--) { if (self.parent.stuckObjects[k] is AbstractPhysicalObject.CreatureGripStick && self.parent.stuckObjects[k].A == self.parent && (self.parent.stuckObjects[k].B.type == AbstractPhysicalObject.AbstractObjectType.Rock || (self.parent.stuckObjects[k].B.type == AbstractPhysicalObject.AbstractObjectType.Spear && !(self.parent.stuckObjects[k].B as AbstractSpear).explosive))) { grasp = (self.parent.stuckObjects[k] as AbstractPhysicalObject.CreatureGripStick).grasp; self.DropAndDestroy(self.parent.stuckObjects[k]); break; } } } if (grasp >= 0) { CustomWorldMod.Log($"Scavenger will spawn with [{specialItem}]", false, CustomWorldMod.DebugLevel.MEDIUM); try { AbstractPhysicalObject abstractPhysicalObject = InstantiateCustomScavItemAbstract(specialItem, self); if (abstractPhysicalObject == null) { throw new Exception("Cannot instantiate item of type " + specialItem.ToString()); } self.world.GetAbstractRoom(self.parent.pos).AddEntity(abstractPhysicalObject); new AbstractPhysicalObject.CreatureGripStick(self.parent, abstractPhysicalObject, grasp, true); } catch (Exception e) { CustomWorldMod.Log($"Error at InitGearUp for [{specialItem}]. {e}", true); } } } }
// Called on re-gear for Trade-mission scavs. If the region has a special item assigned, re-gear with that. private static AbstractPhysicalObject ScavengerAbstractAI_TradeItem(On.ScavengerAbstractAI.orig_TradeItem orig, ScavengerAbstractAI self, bool main) { if (main) { World world = self.world; if (world != null && !world.singleRoomWorld && world.region != null && TryGetSpecialScavItem(world.region.name, out AbstractPhysicalObject.AbstractObjectType specialItem)) { try { AbstractPhysicalObject abstractPhysicalObject = InstantiateCustomScavItemAbstract(specialItem, self); if (abstractPhysicalObject == null) { throw new Exception("Cannot instantiate item of type " + specialItem.ToString()); } return(abstractPhysicalObject); } catch (Exception e) { CustomWorldMod.Log($"Error at TradeItem for [{specialItem}]. {e}", true); } } } return(orig(self, main)); }
public void ctor(World world, patch_CreatureTemplate creatureTemplate, patch_Creature realizedCreature, WorldCoordinate pos, EntityID ID) { orig_ctor(world, creatureTemplate, realizedCreature, pos, ID); CreatureTemplate.Type type = creatureTemplate.TopAncestor().type; if (creatureTemplate.AI) { type = creatureTemplate.type; switch (type) { case CreatureTemplate.Type.Scavenger: abstractAI = new ScavengerAbstractAI(world, this); goto IL_2F4; case CreatureTemplate.Type.Overseer: abstractAI = new OverseerAbstractAI(world, this); goto IL_2F4; default: switch (type) { case CreatureTemplate.Type.Vulture: break; default: if (type != CreatureTemplate.Type.MirosBird) { abstractAI = new AbstractCreatureAI(world, this); goto IL_2F4; } abstractAI = new MirosBirdAbstractAI(world, this); goto IL_2F4; case CreatureTemplate.Type.CicadaA: case CreatureTemplate.Type.CicadaB: abstractAI = new CicadaAbstractAI(world, this); goto IL_2F4; case CreatureTemplate.Type.BigEel: abstractAI = new BigEelAbstractAI(world, this); goto IL_2F4; case CreatureTemplate.Type.Deer: abstractAI = new DeerAbstractAI(world, this); goto IL_2F4; case (CreatureTemplate.Type)patch_CreatureTemplate.Type.WalkerBeast: abstractAI = new WalkerBeastAbstractAI(world, this); goto IL_2F4; } break; case CreatureTemplate.Type.SmallNeedleWorm: case CreatureTemplate.Type.BigNeedleWorm: abstractAI = new NeedleWormAbstractAI(world, this); goto IL_2F4; case CreatureTemplate.Type.DropBug: abstractAI = new DropBugAbstractAI(world, this); goto IL_2F4; case CreatureTemplate.Type.KingVulture: break; } abstractAI = new VultureAbstractAI(world, this); } IL_2F4: if (pos.abstractNode > -1 && pos.abstractNode < Room.nodes.Length && Room.nodes[pos.abstractNode].type == AbstractRoomNode.Type.Den && !pos.TileDefined) { if (Room.offScreenDen) { remainInDenCounter = 1; } else { remainInDenCounter = UnityEngine.Random.Range(100, 1000); } if (abstractAI != null) { abstractAI.denPosition = new WorldCoordinate?(pos); } spawnDen = pos; } if (creatureTemplate.type == CreatureTemplate.Type.TentaclePlant || creatureTemplate.type == CreatureTemplate.Type.PoleMimic) { remainInDenCounter = 0; } }