private static IEnumerable <InstinctData> MaidActions(IPlayerRepository playerRepo, IEnumerable <InstinctData> mcPlayers, Random rand)
        {
            // Maids - find places to clean
            var mcMaids = mcPlayers.Where(p => JokeShopProcedures.MAIDS.Any(maidForm => p.FormSourceId == maidForm)).ToList();

            foreach (var maid in mcMaids)
            {
                var maidPlayer = playerRepo.Players.FirstOrDefault(p => p.Id == maid.Id);
                var maidLoc    = LocationsStatics.LocationList.GetLocation.FirstOrDefault(l => l.dbName == maidPlayer.dbLocationName);

                if (maidLoc != null)
                {
                    string nextLoc;
                    string newContract = "";

                    string[] cleaningContracts = { "coffee_shop", "tavern", "oldoak_apartments", "mansion", "ranch_inside", "castle", "salon" };
                    if (cleaningContracts.Contains(maidLoc.Region))
                    {
                        nextLoc = LocationsStatics.GetRandomLocation_InRegion(maidLoc.Region);
                    }
                    else
                    {
                        newContract = "One of the local facilities has just signed a new contract with you!  ";
                        nextLoc     = LocationsStatics.GetRandomLocation_InRegion(cleaningContracts[rand.Next(cleaningContracts.Count())]);
                    }

                    var stoppedAt = EnvironmentPrankProcedures.MovePlayer(maidPlayer, nextLoc, 20, timestamp: false);

                    if (stoppedAt == nextLoc || maidPlayer.dbLocationName == nextLoc)
                    {
                        var      here       = LocationsStatics.GetConnectionName(nextLoc);
                        string[] activities = { "dusting away the cobwebs", "vaccuuming the floor", "washing up the dishes", "doing the laundry", "serving refreshments", "sweeping the trash", "handing out freshly baked cupcakes" };
                        var      activity   = activities[rand.Next(activities.Count())];

                        PlayerLogProcedures.AddPlayerLog(maidPlayer.Id, $"{newContract}You arrive at <b>{here}</b> and start {activity}!", true);
                        LocationLogProcedures.AddLocationLog(nextLoc, $"{maidPlayer.GetFullName()} arrives here and starts <b>{activity}</b>.");
                    }
                    else if (stoppedAt != null)
                    {
                        var here = LocationsStatics.GetConnectionName(stoppedAt);
                        PlayerLogProcedures.AddPlayerLog(maidPlayer.Id, $"{newContract}You quickly head to your new job but only get as far as <b>{here}</b>.", true);
                    }
                }

                mcPlayers = mcPlayers.Where(p => p.Id != maid.Id);
            }

            return(mcPlayers);
        }
        private static (IEnumerable <InstinctData> mcPlayers, IEnumerable <InstinctData> freePlayers) SheepActions(IPlayerRepository playerRepo, IEnumerable <InstinctData> mcPlayers, IEnumerable <InstinctData> freePlayers, Random rand)
        {
            // Sheep - find another sheep and follow it
            var mcSheep = mcPlayers.Where(p => JokeShopProcedures.SHEEP.Any(sheepForm => p.FormSourceId == sheepForm)).ToList();

            if (!mcSheep.IsEmpty())
            {
                var freeSheep       = freePlayers.Where(p => JokeShopProcedures.SHEEP.Any(sheepForm => p.FormSourceId == sheepForm)).ToList();
                var flockToPlayer   = -1;
                var flockToLocation = "";

                if (!freeSheep.IsEmpty())
                {
                    var target = freeSheep[rand.Next(freeSheep.Count())];
                    flockToPlayer   = target.Id;
                    flockToLocation = target.dbLocationName;
                }
                else
                {
                    flockToLocation = LocationsStatics.GetRandomLocationNotInDungeonOr(LocationsStatics.JOKE_SHOP);
                }

                foreach (var sheep in mcSheep)
                {
                    var sheepPlayer = playerRepo.Players.FirstOrDefault(p => p.Id == sheep.Id);
                    var stoppedAt   = EnvironmentPrankProcedures.MovePlayer(sheepPlayer, flockToLocation, 15, timestamp: false, callback: (p, loc) =>
                    {
                        if (rand.Next(3) == 0)
                        {
                            LocationLogProcedures.AddLocationLog(loc, $"{p.GetFullName()} bleated here:  <b>Baaaaa!</b>");
                        }
                    });

                    if (stoppedAt == flockToLocation)
                    {
                        if (flockToPlayer >= 0)
                        {
                            PlayerLogProcedures.AddPlayerLog(flockToPlayer, $"{sheepPlayer.GetFullName()} bleated at you:  <b>Baaaaa!</b>", true);
                        }

                        LocationLogProcedures.AddLocationLog(stoppedAt, $"{sheepPlayer.GetFullName()} bleated here:  <b>Baaaaa!</b>");
                    }

                    if (stoppedAt != null)
                    {
                        var here = LocationsStatics.GetConnectionName(stoppedAt);
                        PlayerLogProcedures.AddPlayerLog(sheepPlayer.Id, $"You followed your flock to <b>{here}</b>.", true);
                    }
                }

                // Don't consider affected players for any more actions this turn
                mcPlayers = mcPlayers.Where(p => mcSheep.All(s => p.Id != s.Id));

                if (flockToPlayer >= 0)
                {
                    freePlayers = freePlayers.Where(p => p.Id != flockToPlayer);
                }
            }

            return(mcPlayers, freePlayers);
        }
        private static IEnumerable <InstinctData> GhostActions(IPlayerRepository playerRepo, IEnumerable <InstinctData> mcPlayers, Random rand)
        {
            // Ghosts - haunt old buildings
            var mcGhosts = mcPlayers.Where(p => JokeShopProcedures.GHOSTS.Any(ghostForm => p.FormSourceId == ghostForm)).ToList();

            foreach (var ghost in mcGhosts)
            {
                var ghostPlayer = playerRepo.Players.FirstOrDefault(p => p.Id == ghost.Id);
                var ghostLoc    = LocationsStatics.LocationList.GetLocation.FirstOrDefault(l => l.dbName == ghostPlayer.dbLocationName);

                if (ghostLoc != null)
                {
                    string nextLoc;

                    string[] haunts = { "mansion", "castle" };
                    if (haunts.Contains(ghostLoc.Region))
                    {
                        nextLoc = LocationsStatics.GetRandomLocation_InRegion(ghostLoc.Region);
                    }
                    else
                    {
                        nextLoc = LocationsStatics.GetRandomLocation_InRegion(haunts[rand.Next(haunts.Count())]);
                    }

                    // Check whether we can haunt the current tile
                    // An enhancement would be to allow ghosts to move through walls
                    var stoppedAt = EnvironmentPrankProcedures.MovePlayer(ghostPlayer, nextLoc, 20, timestamp: false);

                    var canHaunt = stoppedAt == nextLoc || ghostPlayer.dbLocationName == nextLoc;

                    if (!canHaunt && stoppedAt != null)
                    {
                        var stoppedAtTile = LocationsStatics.LocationList.GetLocation.FirstOrDefault(l => l.dbName == stoppedAt);
                        canHaunt = haunts.Contains(stoppedAtTile.Region);
                    }

                    if (canHaunt)
                    {
                        var here   = LocationsStatics.GetConnectionName(nextLoc);
                        var cutoff = DateTime.UtcNow.AddMinutes(-TurnTimesStatics.GetOfflineAfterXMinutes());

                        var candidates = playerRepo.Players
                                         .Where(p => p.dbLocationName == nextLoc &&
                                                p.LastActionTimestamp >= cutoff &&
                                                p.Id != ghostPlayer.Id &&
                                                p.Mobility == PvPStatics.MobilityFull &&
                                                p.InDuel <= 0 &&
                                                p.InQuest <= 0 &&
                                                p.BotId == AIStatics.ActivePlayerBotId)
                                         .ToList();

                        if (candidates.Any())
                        {
                            var      victim = candidates[rand.Next(candidates.Count())];
                            string[] calls  = { "Boo!", "Whooo!" };
                            var      call   = calls[rand.Next(calls.Count())];

                            PlayerLogProcedures.AddPlayerLog(ghostPlayer.Id, $"After entering {here} you tap {victim.GetFullName()} on the shoulder and shout <b>\"{call}\"</b>!", true);
                            PlayerLogProcedures.AddPlayerLog(victim.Id, $"{ghostPlayer.GetFullName()} taps you on the shoulder and shouts <b>\"{call}\"</b>!", true);
                            LocationLogProcedures.AddLocationLog(nextLoc, $"{ghostPlayer.GetFullName()} shouts <b>\"{call}\"</b> at {victim.GetFullName()}.");
                        }
                        else
                        {
                            string[] activities = { "rattles some chains", "wails like a banshee", "floats through a wall", "lights a candle", "makes the lights flicker", "fades into the background", "knocks a book off a shelf", "melts into a pool of ectoplasm", "sends a a chill through the air" };
                            var      activity   = activities[rand.Next(activities.Count())];

                            PlayerLogProcedures.AddPlayerLog(ghostPlayer.Id, $"You begin haunting {here}!", true);
                            LocationLogProcedures.AddLocationLog(nextLoc, $"<b>{ghostPlayer.GetFullName()} {activity}</b>.");
                        }
                    }
                    else if (stoppedAt != null)
                    {
                        var here = LocationsStatics.GetConnectionName(stoppedAt);
                        PlayerLogProcedures.AddPlayerLog(ghostPlayer.Id, $"The spirits call you to <b>{here}</b>.", true);
                    }
                }

                mcPlayers = mcPlayers.Where(p => p.Id != ghost.Id);
            }

            return(mcPlayers);
        }
        private static (IEnumerable <InstinctData> mcPlayers, IEnumerable <InstinctData> freePlayers) CatActions(IPlayerRepository playerRepo, IEnumerable <InstinctData> mcPlayers, IEnumerable <InstinctData> freePlayers, Random rand)
        {
            // Cats - chase rodents
            var mcCats2 = mcPlayers.Where(p => JokeShopProcedures.CATS_AND_NEKOS.Any(catForm => p.FormSourceId == catForm)).ToList();

            if (!mcCats2.IsEmpty())
            {
                var mcRodents   = mcPlayers.Where(p => JokeShopProcedures.RODENTS.Any(rodentForm => p.FormSourceId == rodentForm)).ToList();
                var freeRodents = freePlayers.Where(p => JokeShopProcedures.RODENTS.Any(rodentForm => p.FormSourceId == rodentForm)).ToList();

                while (!mcCats2.IsEmpty() && mcRodents.Count() + freeRodents.Count() > 0)
                {
                    // Find a cat
                    var catIndex = rand.Next(mcCats2.Count());
                    var cat      = mcCats2[catIndex];
                    mcCats2.RemoveAt(catIndex);
                    mcPlayers = mcPlayers.Where(p => p.Id != cat.Id);

                    int    rodentId;
                    string rodentLoc;

                    // Find a rodent for them to chase
                    if (!mcRodents.IsEmpty())
                    {
                        var rodentIndex = rand.Next(mcRodents.Count());
                        var rodent      = mcRodents[rodentIndex];
                        mcRodents.RemoveAt(rodentIndex);

                        rodentId  = rodent.Id;
                        rodentLoc = rodent.dbLocationName;
                        mcPlayers = mcPlayers.Where(p => p.Id != rodentId);
                    }
                    else  // !freeRodents.IsEmpty()
                    {
                        var rodentIndex = rand.Next(freeRodents.Count);
                        var rodent      = freeRodents[catIndex];
                        freeRodents.RemoveAt(rodentIndex);

                        rodentId    = rodent.Id;
                        rodentLoc   = rodent.dbLocationName;
                        freePlayers = freePlayers.Where(p => p.Id != rodentId);
                    }

                    var catPlayer    = playerRepo.Players.FirstOrDefault(p => p.Id == cat.Id);
                    var rodentPlayer = playerRepo.Players.FirstOrDefault(p => p.Id == rodentId);

                    // Move cat
                    var stoppedAt = EnvironmentPrankProcedures.MovePlayer(catPlayer, rodentLoc, 15, timestamp: false, callback: (p, loc) =>
                    {
                        var roll = rand.Next(4);
                        if (roll == 0)
                        {
                            LocationLogProcedures.AddLocationLog(loc, $"{catPlayer.GetFullName()} stealthily prowls the area, on the hunt for {rodentPlayer.GetFullName()}");
                        }
                    });

                    if (stoppedAt == rodentLoc)
                    {
                        var here = LocationsStatics.GetConnectionName(stoppedAt);
                        LocationLogProcedures.AddLocationLog(stoppedAt, $"{catPlayer.GetFullName()} lunges at a rodent, but {rodentPlayer.GetFullName()} is too quick and evades the cat's attack!</b>");

                        PlayerLogProcedures.AddPlayerLog(rodentId, $"{catPlayer.GetFullName()} jumps out at you, but you leap from their paws and deprive them of an easy snack!", true);
                        PlayerLogProcedures.AddPlayerLog(catPlayer.Id, $"You prowl to <b>{here}</b>, creep up on {rodentPlayer.GetFullName()} and pounce!  But they're too quick and evade your clutches!", true);
                    }
                    else if (stoppedAt != null)  // Cat moved, but didn't get to the rodent's location
                    {
                        var here = LocationsStatics.GetConnectionName(stoppedAt);
                        PlayerLogProcedures.AddPlayerLog(catPlayer.Id, $"You prowl to <b>{here}</b>, being careful not to get too close to your prey as you prepare for the hunt...", true);
                    }
                    else if (cat.dbLocationName == rodentLoc)
                    {
                        PlayerLogProcedures.AddPlayerLog(rodentId, $"{catPlayer.GetFullName()} hides in a bush, stealthily watching you, getting ready to pounce...", true);
                        PlayerLogProcedures.AddPlayerLog(catPlayer.Id, $"You slink behind a bush and focus your eyes on {rodentPlayer.GetFullName()}, getting ready to strike...", true);
                    }
                }
            }

            return(mcPlayers, freePlayers);
        }
        private static (IEnumerable <InstinctData> mcPlayers, IEnumerable <InstinctData> freePlayers) DogActions(IPlayerRepository playerRepo, IEnumerable <InstinctData> mcPlayers, IEnumerable <InstinctData> freePlayers, Random rand)
        {
            // Dogs - chase cats, possiby up a tree
            var mcDogs = mcPlayers.Where(p => JokeShopProcedures.DOGS.Any(dogForm => p.FormSourceId == dogForm)).ToList();

            if (!mcDogs.IsEmpty())
            {
                var mcCats   = mcPlayers.Where(p => JokeShopProcedures.CATS_AND_NEKOS.Any(catForm => p.FormSourceId == catForm)).ToList();
                var freeCats = freePlayers.Where(p => JokeShopProcedures.CATS_AND_NEKOS.Any(catForm => p.FormSourceId == catForm)).ToList();

                while (!mcDogs.IsEmpty() && mcCats.Count() + freeCats.Count() > 0)
                {
                    // Find a dog
                    var dogIndex = rand.Next(mcDogs.Count());
                    var dog      = mcDogs[dogIndex];
                    mcDogs.RemoveAt(dogIndex);
                    mcPlayers = mcPlayers.Where(p => p.Id != dog.Id);

                    int    catId;
                    string catLoc;
                    bool   catIsMindControlled;

                    // Find a cat for them to chase
                    if (!mcCats.IsEmpty())
                    {
                        var catIndex = rand.Next(mcCats.Count());
                        var cat      = mcCats[catIndex];
                        mcCats.RemoveAt(catIndex);

                        catId  = cat.Id;
                        catLoc = cat.dbLocationName;
                        catIsMindControlled = true;
                        mcPlayers           = mcPlayers.Where(p => p.Id != catId);
                    }
                    else  // !freeCats.IsEmpty()
                    {
                        var catIndex = rand.Next(freeCats.Count);
                        var cat      = freeCats[catIndex];
                        freeCats.RemoveAt(catIndex);

                        catId  = cat.Id;
                        catLoc = cat.dbLocationName;
                        catIsMindControlled = false;
                        freePlayers         = freePlayers.Where(p => p.Id != catId);
                    }

                    var dogPlayer = playerRepo.Players.FirstOrDefault(p => p.Id == dog.Id);
                    var catPlayer = playerRepo.Players.FirstOrDefault(p => p.Id == catId);

                    // Move dog
                    var stoppedAt = EnvironmentPrankProcedures.MovePlayer(dogPlayer, catLoc, 15, timestamp: false, callback: (p, loc) =>
                    {
                        var roll = rand.Next(4);
                        if (roll == 0)
                        {
                            LocationLogProcedures.AddLocationLog(loc, $"{dogPlayer.GetFullName()} barked here as they catch the scent of a cat, {catPlayer.GetFullName()}:  <b>Woof woof!</b>");
                        }
                        else if (roll == 1)
                        {
                            LocationLogProcedures.AddLocationLog(loc, $"{dogPlayer.GetFullName()} growled here as they get closer to {catPlayer.GetFullName()}, the cat:  <b>Grrrrrrrrr!</b>");
                        }
                    });

                    // Dog has arrived at the cat's location
                    if (stoppedAt == catLoc)
                    {
                        var here = LocationsStatics.GetConnectionName(stoppedAt);
                        LocationLogProcedures.AddLocationLog(stoppedAt, $"{dogPlayer.GetFullName()} barked at {catPlayer.GetFullName()}:  <b>Woof woof!</b>");

                        // If cat is mind controlled we can send them up a tree
                        if (catIsMindControlled)
                        {
                            var treeLoc = mcPlayers.FirstOrDefault(p => JokeShopProcedures.TREES.Any(treeForm => p.FormSourceId == treeForm))?.dbLocationName;

                            if (treeLoc == null)
                            {
                                treeLoc = freePlayers.FirstOrDefault(p => JokeShopProcedures.TREES.Any(treeForm => p.FormSourceId == treeForm))?.dbLocationName;
                            }

                            if (treeLoc == null)
                            {
                                treeLoc = "forest_ancestor_tree";
                            }

                            var catStoppedAt = EnvironmentPrankProcedures.MovePlayer(catPlayer, treeLoc, 15, timestamp: false, callback: (p, loc) =>
                            {
                                var roll = rand.Next(3);
                                if (roll == 0)
                                {
                                    LocationLogProcedures.AddLocationLog(loc, $"<b>Meoooww!</b> yowls {catPlayer.GetFullName()} as they quickly flee from {dogPlayer.GetFullName()}, the dog who is chasing them.");
                                }
                            });

                            if (catStoppedAt == treeLoc)
                            {
                                LocationLogProcedures.AddLocationLog(catLoc, $"{catPlayer.GetFullName()} runs to hide from {dogPlayer.GetFullName()}!");
                                LocationLogProcedures.AddLocationLog(treeLoc, $"{catPlayer.GetFullName()} leaps into a tree to hide from {dogPlayer.GetFullName()}, the dog who is chasing them.");
                                PlayerLogProcedures.AddPlayerLog(catId, $"<b>Woof woof!</b>  {dogPlayer.GetFullName()} barks at you, and you run off to the nearest tree!", true);
                                PlayerLogProcedures.AddPlayerLog(dogPlayer.Id, $"You chased a cat to <b>{here}</b> and started barking at {catPlayer.GetFullName()}:  <b>Woof woof!</b>.  They run off to the nearest tree to hide from you!", true);
                            }
                            else if (catStoppedAt == null)
                            {
                                PlayerLogProcedures.AddPlayerLog(catId, $"<b>Woof woof!</b>  {dogPlayer.GetFullName()} barks at you, but you can't seem to escape!", true);
                                PlayerLogProcedures.AddPlayerLog(dogPlayer.Id, $"You chased a cat to <b>{here}</b> and started barking at {catPlayer.GetFullName()}:  <b>Woof woof!</b>.", true);
                            }
                            else
                            {
                                LocationLogProcedures.AddLocationLog(catLoc, $"{catPlayer.GetFullName()} runs to hide from {dogPlayer.GetFullName()}!");
                                PlayerLogProcedures.AddPlayerLog(catId, $"<b>Woof woof!</b>  {dogPlayer.GetFullName()} barks at you, and you run away to try and hide!", true);
                                PlayerLogProcedures.AddPlayerLog(dogPlayer.Id, $"You chased a cat to <b>{here}</b> and started barking at {catPlayer.GetFullName()}:  <b>Woof woof!</b>.  They run off to try and hide from you!", true);
                            }
                        }
                        else  // Cat not mind controlled, dog can only bark
                        {
                            PlayerLogProcedures.AddPlayerLog(catId, $"{dogPlayer.GetFullName()} barked at you:  <b>Woof woof!</b>", true);
                            PlayerLogProcedures.AddPlayerLog(dogPlayer.Id, $"You chased a cat to <b>{here}</b> and started barking at {catPlayer.GetFullName()}:  <b>Woof woof!</b>", true);
                        }
                    }
                    else if (stoppedAt != null)  // Dog moved, but didn't get to the cat's location
                    {
                        var here = LocationsStatics.GetConnectionName(stoppedAt);
                        PlayerLogProcedures.AddPlayerLog(dogPlayer.Id, $"You caught scent of a cat and ran to <b>{here}</b>", true);
                    }
                    else if (dog.dbLocationName == catLoc)
                    {
                        PlayerLogProcedures.AddPlayerLog(catId, $"{dogPlayer.GetFullName()} barked at you:  <b>Woof woof!</b>", true);
                        PlayerLogProcedures.AddPlayerLog(dogPlayer.Id, $"You barked at {catPlayer.GetFullName()}:  <b>Woof woof!</b>", true);
                    }
                }
            }

            return(mcPlayers, freePlayers);
        }
        public static string RunAction(Player victim, JokeShopActionViewModel input)
        {
            switch (input.Action)
            {
            case JokeShopActions.None:
                break;

            case JokeShopActions.WarnPlayer:
                return(JokeShopProcedures.EnsurePlayerIsWarned(victim, duration: input.EffectDuration, cooldown: input.EffectCooldown));

            case JokeShopActions.RemindPlayer:
                return(JokeShopProcedures.EnsurePlayerIsWarnedTwice(victim, duration: input.EffectDuration, cooldown: input.EffectCooldown));

            case JokeShopActions.BanPlayer:
                return(JokeShopProcedures.BanCharacter(victim, duration: input.EffectDuration, cooldown: input.EffectCooldown));

            case JokeShopActions.UnbanPlayer:
                return(RemoveEffect(victim, JokeShopProcedures.BANNED_FROM_JOKE_SHOP_EFFECT, "Lifted Joke Shop ban on player"));

            case JokeShopActions.EjectPlayer:
                return(JokeShopProcedures.EjectCharacter(victim));

            case JokeShopActions.EjectOfflinePlayers:
                JokeShopProcedures.EjectOfflineCharacters();
                return("Ejected offline players");

            case JokeShopActions.EjectAllPlayers:
                return(JokeShopProcedures.EmptyJokeShopOnto(LocationsStatics.GetRandomLocationNotInDungeonOr(LocationsStatics.JOKE_SHOP)));

            case JokeShopActions.MildPrank:
                return(JokeShopProcedures.MildPrank(victim));

            case JokeShopActions.MischievousPrank:
                return(JokeShopProcedures.MischievousPrank(victim));

            case JokeShopActions.MeanPrank:
                return(JokeShopProcedures.MeanPrank(victim));

            case JokeShopActions.Search:
                return(JokeShopProcedures.Search(victim));

            case JokeShopActions.Cleanse:
                return(JokeShopProcedures.Cleanse(victim));

            case JokeShopActions.Meditate:
                return(JokeShopProcedures.Meditate(victim));

            case JokeShopActions.SelfRestore:
                return(JokeShopProcedures.SelfRestore(victim));

            case JokeShopActions.Activate:
                JokeShopProcedures.SetJokeShopActive(true);
                return("Joke shop activated");

            case JokeShopActions.Deactivate:
                JokeShopProcedures.SetJokeShopActive(false);
                return("Joke shop deactivated");

            case JokeShopActions.Relocate:
                LocationsStatics.MoveJokeShop();
                return("Joke Shop moved");

            case JokeShopActions.AnimateSafetyNet:
                return(JokeShopProcedures.Restore(victim));

            case JokeShopActions.BlowWhistle:
                AIDirectiveProcedures.DeaggroPsychopathsOnPlayer(victim);
                return("Whistle blown");

            case JokeShopActions.DiceGame:
                return(NovelPrankProcedures.DiceGame(victim));

            case JokeShopActions.RandomShout:
                return(NovelPrankProcedures.RandomShout(victim));

            case JokeShopActions.CombatRadar:
                return(NovelPrankProcedures.LocatePlayerInCombat(victim));

            case JokeShopActions.RareFind:
                return(EnvironmentPrankProcedures.RareFind(victim));

            case JokeShopActions.SummonPsychopath:
                return(NovelPrankProcedures.SummonPsychopath(victim, aggro: input.PsychoAggro));

            case JokeShopActions.SummonEvilTwin:
                return(NovelPrankProcedures.SummonDoppelganger(victim, aggro: input.PsychoAggro));

            case JokeShopActions.OpenPsychoNip:
                return(NovelPrankProcedures.OpenPsychoNip(victim));

            case JokeShopActions.SummonLvl1Psychopath:
                return(NovelPrankProcedures.SummonPsychopath(victim, strengthOverride: 0, aggro: input.PsychoAggro));

            case JokeShopActions.SummonLvl3Psychopath:
                return(NovelPrankProcedures.SummonPsychopath(victim, strengthOverride: 1, aggro: input.PsychoAggro));

            case JokeShopActions.SummonLvl5Psychopath:
                return(NovelPrankProcedures.SummonPsychopath(victim, strengthOverride: 2, aggro: input.PsychoAggro));

            case JokeShopActions.SummonLvl7Psychopath:
                return(NovelPrankProcedures.SummonPsychopath(victim, strengthOverride: 3, aggro: input.PsychoAggro));

            case JokeShopActions.SummonLvl9Psychopath:
                return(NovelPrankProcedures.SummonPsychopath(victim, strengthOverride: 4, aggro: input.PsychoAggro));

            case JokeShopActions.SummonLvl11Psychopath:
                return(NovelPrankProcedures.SummonPsychopath(victim, strengthOverride: 5, aggro: input.PsychoAggro));

            case JokeShopActions.SummonLvl13Psychopath:
                return(NovelPrankProcedures.SummonPsychopath(victim, strengthOverride: 6, aggro: input.PsychoAggro));

            case JokeShopActions.PlaceBounty:
                return(NovelPrankProcedures.PlaceBountyOnPlayersHead(victim));

            case JokeShopActions.AwardChallenge:
            {
                var minDuration = input.MinChallengeDuration ?? 1;
                var maxDuration = input.MaxChallengeDuration ?? 480;
                var penalties   = (bool?)null;
                return(NovelPrankProcedures.AwardChallenge(victim, minDuration, maxDuration, penalties));
            }

            case JokeShopActions.ClearChallenge:
                foreach (var challengeType in ChallengeProcedures.CHALLENGE_TYPES)
                {
                    RemoveEffect(victim, challengeType.EffectSourceId);
                }
                return("Challenge cleared");

            case JokeShopActions.CurrentChallenge:
                return(NovelPrankProcedures.DescribeChallenge(victim, ChallengeProcedures.CurrentChallenge(victim)));

            case JokeShopActions.ChallengeProgress:
                return(ChallengeProgress(victim));

            case JokeShopActions.CheckChallenge:
                ChallengeProcedures.CheckChallenge(victim, false);
                return("Challenge checked");

            case JokeShopActions.ForceAttack:
                return(NovelPrankProcedures.ForceAttack(victim));

            case JokeShopActions.Incite:
                return(NovelPrankProcedures.Incite(victim));

            case JokeShopActions.FillInventory:
                return(EnvironmentPrankProcedures.FillInventory(victim, overflow: false));

            case JokeShopActions.LearnSpell:
                return(EnvironmentPrankProcedures.LearnSpell(victim));

            case JokeShopActions.UnlearnSpell:
                return(EnvironmentPrankProcedures.UnlearnSpell(victim));

            case JokeShopActions.BlockAttacks:
                return(EnvironmentPrankProcedures.BlockAttacks(victim));

            case JokeShopActions.BlockCleanses:
                return(EnvironmentPrankProcedures.BlockCleanseMeditates(victim));

            case JokeShopActions.BlockItemUses:
                return(EnvironmentPrankProcedures.BlockItemUses(victim));

            case JokeShopActions.ResetCombatTimer:
                return(EnvironmentPrankProcedures.ResetCombatTimer(victim));

            case JokeShopActions.ResetActivityTimer:
                EnvironmentPrankProcedures.ResetActivityTimer(victim);
                return("Activity timer reset");

            case JokeShopActions.LiftRandomCurse:
                return(CharacterPrankProcedures.LiftRandomCurse(victim));

            case JokeShopActions.Boost:
                return(CharacterPrankProcedures.GiveRandomEffect(victim, CharacterPrankProcedures.BOOST_EFFECTS, duration: input.EffectDuration, cooldown: input.EffectCooldown));

            case JokeShopActions.DisciplineBoost:
                return(GiveEffect(victim, input, CharacterPrankProcedures.DISCIPLINE_BOOST));

            case JokeShopActions.PerceptionBoost:
                return(GiveEffect(victim, input, CharacterPrankProcedures.PERCEPTION_BOOST));

            case JokeShopActions.CharismaBoost:
                return(GiveEffect(victim, input, CharacterPrankProcedures.CHARISMA_BOOST));

            case JokeShopActions.FortitudeBoost:
                return(GiveEffect(victim, input, CharacterPrankProcedures.FORTITUDE_BOOST));

            case JokeShopActions.AgilityBoost:
                return(GiveEffect(victim, input, CharacterPrankProcedures.AGILITY_BOOST));

            case JokeShopActions.RestorationBoost:
                return(GiveEffect(victim, input, CharacterPrankProcedures.RESTORATION_BOOST));

            case JokeShopActions.MagickaBoost:
                return(GiveEffect(victim, input, CharacterPrankProcedures.MAGICKA_BOOST));

            case JokeShopActions.RegenerationBoost:
                return(GiveEffect(victim, input, CharacterPrankProcedures.REGENERATION_BOOST));

            case JokeShopActions.LuckBoost:
                return(GiveEffect(victim, input, CharacterPrankProcedures.LUCK_BOOST));

            case JokeShopActions.InventoryBoost:
                return(GiveEffect(victim, input, CharacterPrankProcedures.INVENTORY_BOOST));

            case JokeShopActions.MobilityBoost:
                return(GiveEffect(victim, input, CharacterPrankProcedures.MOBILITY_BOOST));

            case JokeShopActions.Penalty:
                return(CharacterPrankProcedures.GiveRandomEffect(victim, CharacterPrankProcedures.PENALTY_EFFECTS, duration: input.EffectDuration, cooldown: input.EffectCooldown));

            case JokeShopActions.DisciplinePenalty:
                return(GiveEffect(victim, input, CharacterPrankProcedures.DISCIPLINE_PENALTY));

            case JokeShopActions.PerceptionPenalty:
                return(GiveEffect(victim, input, CharacterPrankProcedures.PERCEPTION_PENALTY));

            case JokeShopActions.CharismaPenalty:
                return(GiveEffect(victim, input, CharacterPrankProcedures.CHARISMA_PENALTY));

            case JokeShopActions.FortitudePenalty:
                return(GiveEffect(victim, input, CharacterPrankProcedures.FORTITUDE_PENALTY));

            case JokeShopActions.AgilityPenalty:
                return(GiveEffect(victim, input, CharacterPrankProcedures.AGILITY_PENALTY));

            case JokeShopActions.RestorationPenalty:
                return(GiveEffect(victim, input, CharacterPrankProcedures.RESTORATION_PENALTY));

            case JokeShopActions.MagickaPenalty:
                return(GiveEffect(victim, input, CharacterPrankProcedures.MAGICKA_PENALTY));

            case JokeShopActions.RegenerationPenalty:
                return(GiveEffect(victim, input, CharacterPrankProcedures.REGENERATION_PENALTY));

            case JokeShopActions.LuckPenalty:
                return(GiveEffect(victim, input, CharacterPrankProcedures.LUCK_PENALTY));

            case JokeShopActions.InventoryPenalty:
                return(GiveEffect(victim, input, CharacterPrankProcedures.INVENTORY_PENALTY));

            case JokeShopActions.MobilityPenalty:
                return(GiveEffect(victim, input, CharacterPrankProcedures.MOBILITY_PENALTY));

            case JokeShopActions.Blind:
                return(GiveEffect(victim, input, CharacterPrankProcedures.BLINDED_EFFECT));

            case JokeShopActions.Dizzy:
                return(GiveEffect(victim, input, CharacterPrankProcedures.DIZZY_EFFECT));

            case JokeShopActions.Hush:
                return(GiveEffect(victim, input, CharacterPrankProcedures.HUSHED_EFFECT));

            case JokeShopActions.SneakLow:
                return(GiveEffect(victim, input, CharacterPrankProcedures.SNEAK_REVEAL_1));

            case JokeShopActions.SneakMedium:
                return(GiveEffect(victim, input, CharacterPrankProcedures.SNEAK_REVEAL_2));

            case JokeShopActions.SneakHigh:
                return(GiveEffect(victim, input, CharacterPrankProcedures.SNEAK_REVEAL_3));

            case JokeShopActions.MakeInvisible:
                return(CharacterPrankProcedures.MakeInvisible(victim, duration: input.EffectDuration, cooldown: input.EffectCooldown));

            case JokeShopActions.UndoInvisible:
                RemoveEffect(victim, JokeShopProcedures.INVISIBILITY_EFFECT);
                CharacterPrankProcedures.UndoInvisible(victim);
                return("Triggered undo invisible");

            case JokeShopActions.UndoInvisibleItems:
                CharacterPrankProcedures.EnsureItemsAreVisible();
                return("Invisible items fixed");

            case JokeShopActions.MakePsychotic:
                return(CharacterPrankProcedures.MakePsychotic(victim, duration: input.EffectDuration, cooldown: input.EffectCooldown));

            case JokeShopActions.UndoPsychotic:
                RemoveEffect(victim, JokeShopProcedures.PSYCHOTIC_EFFECT);
                return(CharacterPrankProcedures.UndoPsychotic(victim.Id));

            case JokeShopActions.Instinctive:
                return(GiveEffect(victim, input, JokeShopProcedures.INSTINCT_EFFECT));

            case JokeShopActions.UndoInstinctive:
                return(RemoveEffect(victim, JokeShopProcedures.INSTINCT_EFFECT, "Instinctive removed"));

            case JokeShopActions.AutoRestore:
                return(GiveEffect(victim, input, JokeShopProcedures.AUTO_RESTORE_EFFECT));

            case JokeShopActions.ClearAutoRestore:
                return(RemoveEffect(victim, JokeShopProcedures.AUTO_RESTORE_EFFECT, "Player will no longer autorestore.<br><b>Important:</b>  If they are a lost item they will be trapped in limbo and require you to give them a form change in order to escape!"));

            case JokeShopActions.TeleportToOverworld:
                return(EnvironmentPrankProcedures.TeleportToOverworld(victim, root: false, curse: false));

            case JokeShopActions.TeleportToDungeon:
                return(EnvironmentPrankProcedures.TeleportToDungeon(victim, meanness: 0));

            case JokeShopActions.TeleportToFriendlyNPC:
                return(EnvironmentPrankProcedures.TeleportToFriendlyNPC(victim));

            case JokeShopActions.TeleportToHostileNPC:
                return(EnvironmentPrankProcedures.TeleportToHostileNPC(victim, attack: false));

            case JokeShopActions.TeleportToBar:
                return(EnvironmentPrankProcedures.TeleportToBar(victim, root: false));

            case JokeShopActions.TeleportToQuest:
                return(EnvironmentPrankProcedures.TeleportToQuest(victim));

            case JokeShopActions.RunAway:
                return(EnvironmentPrankProcedures.RunAway(victim));

            case JokeShopActions.WanderAimlessly:
                return(EnvironmentPrankProcedures.WanderAimlessly(victim));

            case JokeShopActions.AnimateTransform:
                return(CharacterPrankProcedures.AnimateTransform(victim));

            case JokeShopActions.ImmobileTransform:
                return(CharacterPrankProcedures.ImmobileTransform(victim, temporary: false));

            case JokeShopActions.InanimateTransform:
                return(CharacterPrankProcedures.InanimateTransform(victim, temporary: false));

            case JokeShopActions.LostItemTransform:
                return(CharacterPrankProcedures.InanimateTransform(victim, temporary: true));

            case JokeShopActions.MobileInanimateTransform:
                return(CharacterPrankProcedures.MobileInanimateTransform(victim));

            case JokeShopActions.TGTransform:
                return(CharacterPrankProcedures.TGTransform(victim));

            case JokeShopActions.BodySwap:
                return(CharacterPrankProcedures.BodySwap(victim, clone: false));

            case JokeShopActions.Clone:
                return(CharacterPrankProcedures.BodySwap(victim, clone: true));

            case JokeShopActions.UndoTemporaryForm:
                CharacterPrankProcedures.UndoTemporaryForm(victim.Id);
                return(RemoveEffect(victim, JokeShopProcedures.AUTO_RESTORE_EFFECT, "Triggered undo of temporary form"));

            case JokeShopActions.RestoreBaseForm:
                return(CharacterPrankProcedures.RestoreBaseForm(victim));

            case JokeShopActions.RestoreName:
                return(CharacterPrankProcedures.RestoreName(victim));

            case JokeShopActions.IdentityChange:
                return(CharacterPrankProcedures.IdentityChange(victim));

            case JokeShopActions.TransformToMindControlledForm:
                return(CharacterPrankProcedures.TransformToMindControlledForm(victim));

            case JokeShopActions.ChangeBaseForm:
                return(CharacterPrankProcedures.ChangeBaseForm(victim));

            case JokeShopActions.SetBaseFormToRegular:
                return(CharacterPrankProcedures.SetBaseFormToRegular(victim));

            case JokeShopActions.SetBaseFormToCurrent:
                return(CharacterPrankProcedures.SetBaseFormToCurrent(victim));

            case JokeShopActions.BossPrank:
                return(CharacterPrankProcedures.BossPrank(victim));

            case JokeShopActions.Update:
                // Unreachable = case should have been handled by earlier code
                break;
            }

            return(null);
        }