// 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;
        }
    }