internal static int?PlaceBounty(Player player, Random rand = null)
        {
            // Only place bounties on PvP players
            if (player.GameMode != (int)GameModeStatics.GameModes.PvP)
            {
                return(null);
            }

            var numMappings = MAPPINGS.Count();

            if (numMappings == 0)
            {
                return(null);
            }

            rand = rand ?? new Random();
            var effect = MAPPINGS[rand.Next(numMappings)];

            if (EffectProcedures.PlayerHasEffect(player, effect.EffectSourceId))
            {
                return(null);
            }

            var possibleForms = JokeShopProcedures.Forms(e => e.Category == effect.Category).Select(e => e.FormSourceId).ToArray();
            var numForms      = possibleForms.Count();

            if (numForms == 0)
            {
                return(null);
            }

            EffectProcedures.GivePerkToPlayer(effect.EffectSourceId, player.Id, TURNS_OF_BOUNTY, TURNS_OF_BOUNTY + TURNS_OF_IMMUNITY);

            return(effect.EffectSourceId);
        }
Beispiel #2
0
        public static string ForceAttack(Player attacker, bool strongAttackerAlerts = false, Random rand = null)
        {
            if (attacker.TimesAttackingThisUpdate >= PvPStatics.MaxAttacksPerUpdate)
            {
                return(null);
            }

            if (attacker.GameMode != (int)GameModeStatics.GameModes.PvP)
            {
                return(null);
            }

            rand = rand ?? new Random();
            var candidates = JokeShopProcedures.ActivePlayersInJokeShopApartFrom(attacker)
                             .Where(p => p.GameMode == (int)GameModeStatics.GameModes.PvP &&
                                    JokeShopProcedures.PlayerHasBeenWarned(p)).ToList();

            if (candidates == null || candidates.IsEmpty())
            {
                return(null);
            }

            var victim = candidates[rand.Next(candidates.Count())];

            var spells = SkillProcedures.AvailableSkills(attacker, victim, true);

            if (spells == null || spells.IsEmpty())
            {
                return(null);
            }

            var spellList = spells.ToArray();
            var spell     = spellList[rand.Next(spellList.Count())];

            var message = $"You are compelled to attack {victim.GetFullName()}!";

            PlayerLogProcedures.AddPlayerLog(attacker.Id, message, strongAttackerAlerts);
            PlayerLogProcedures.AddPlayerLog(victim.Id, $"{attacker.GetFullName()} is compelled to attack you!", true);
            LocationLogProcedures.AddLocationLog(attacker.dbLocationName, $"{attacker.GetFullName()} is compelled to attack {victim.GetFullName()}!");

            // Note we do not apply the full gamut of preconditions of a manual attack present in the controller
            var attack = AttackProcedures.AttackSequence(attacker, victim, spell, false);

            if (strongAttackerAlerts)
            {
                PlayerLogProcedures.AddPlayerLog(attacker.Id, attack, true);
            }

            return($"{message}<br />{attack}");
        }
Beispiel #3
0
        public static string Incite(Player player, Random rand = null)
        {
            rand = rand ?? new Random();
            var candidates = JokeShopProcedures.ActivePlayersInJokeShopApartFrom(player)
                             .Where(p => p.GameMode == (int)GameModeStatics.GameModes.PvP &&
                                    JokeShopProcedures.PlayerHasBeenWarned(p)).ToList();

            if (candidates == null || candidates.IsEmpty())
            {
                return(null);
            }

            var attacker = candidates[rand.Next(candidates.Count())];

            if (ForceAttack(attacker, true, rand) == null)
            {
                return(null);
            }

            return($"You incite {attacker.GetFullName()} to attack another player!");
        }
Beispiel #4
0
        public IHttpActionResult Post()
        {
            var allowedIps = new List <string> {
                "127.0.0.1", "::1"
            };
            var owinContext = Request.GetOwinContext();

            if (owinContext == null)
            {
                return(BadRequest());
            }

            if (!allowedIps.Contains(owinContext.Request.RemoteIpAddress))
            {
                return(Unauthorized());
            }

            IPvPWorldStatRepository worldStatRepo = new EFPvPWorldStatRepository();
            var world = worldStatRepo.PvPWorldStats.First();

            if (world.TurnNumber == world.RoundDuration && !world.ChaosMode)
            {
                var round = Int32.Parse(PvPStatics.AlphaRound.Split(' ')[2]);
                var errorSavingLeaderboards = false;
                try
                {
                    DomainRegistry.Repository.Execute(new SavePvPLeaderboards {
                        RoundNumber = round
                    });
                }
                catch (DomainException)
                {
                    errorSavingLeaderboards = true;
                }

                try
                {
                    DomainRegistry.Repository.Execute(new SaveXpLeaderboards {
                        RoundNumber = round
                    });
                }
                catch (DomainException)
                {
                    errorSavingLeaderboards = true;
                }

                try
                {
                    DomainRegistry.Repository.Execute(new SaveItemLeaderboards {
                        RoundNumber = round
                    });
                }
                catch (DomainException)
                {
                    errorSavingLeaderboards = true;
                }

                if (errorSavingLeaderboards)
                {
                    return(InternalServerError());
                }

                // TODO: Set the turn number to 0 and enable chaos mode once we are confident that leaderboards are saving properly, including achievements and badges
                return(Ok());
            }

            // Don't do a turn update if the round is over or we're still in an update
            if (world.TurnNumber >= PvPStatics.RoundDuration || world.WorldIsUpdating)
            {
                return(BadRequest());
            }

            // Don't do a turn update if it hasn't been long enough yet
            var gracePeriodSeconds = 10; // account for delays in fetching the URL
            var secondsElapsed     = DateTime.UtcNow.Subtract(world.LastUpdateTimestamp).TotalSeconds + gracePeriodSeconds;

            if (secondsElapsed < TurnTimesStatics.GetTurnLengthInSeconds())
            {
                return(BadRequest());
            }

            world.TurnNumber++;
            world.WorldIsUpdating     = true;
            world.LastUpdateTimestamp = DateTime.UtcNow;

            // save changes to database
            worldStatRepo.SavePvPWorldStat(world);

            try
            {
                JokeShopProcedures.SetJokeShopActive(world.JokeShop);
                WorldUpdateProcedures.UpdateWorld();
            }
            catch (Exception e)
            {
                return(InternalServerError(e));
            }

            return(Ok());
        }
Beispiel #5
0
        public void Configuration(IAppBuilder app)
        {
            var container       = new Container();
            var httpConfig      = new HttpConfiguration();
            var signalrResolver = new SimpleInjectorSignalRDependencyResolver(container);

            container.RegisterContainer(httpConfig, app.GetDataProtectionProvider);

            // cross owin and simple injector for OnValidateIdentity
            app.CreatePerOwinContext(container.GetInstance <ApplicationUserManager>);

            // Enable the application to use a cookie to store information for the signed in user
            // and to use a cookie to temporarily store information about a user logging in with a third party login provider
            // Configure the sign in cookie
            app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
                LoginPath          = new PathString("/Account/Login"),
                Provider           = new CookieAuthenticationProvider
                {
                    // Enables the application to validate the security stamp when the user logs in.
                    // This is a security feature which is used when you change a password or add an external login to your account.
                    OnValidateIdentity = SecurityStampValidator.OnValidateIdentity <ApplicationUserManager, User>(
                        validateInterval: TimeSpan.FromMinutes(5),
                        regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
                }
            });

            app.MapScopableHubConnection(async(request, connectionId, next) =>
            {
                using (AsyncScopedLifestyle.BeginScope(container))
                {
                    if (container.GetInstance <IHubRequestAccessor>() is HubRequestAccessor hubRequestAccessor)
                    {
                        hubRequestAccessor.Request = request;
                    }

                    if (container.GetInstance <IHubConnectionIdAccessor>() is HubConnectionIdAccessor hubConnectionIdAccessor)
                    {
                        hubConnectionIdAccessor.ConnectionId = connectionId;
                    }

                    await next();
                }
            },
                                         resolver: signalrResolver);

            app.Use(async(context, next) =>
            {
                // check if there is a HttpContext for WebRequestLifestyle to store its scope
                if (HttpContext.Current != null)
                {
                    // capture the owin context for any service dependant on it and store it in the async scoped container
                    // this will use WebRequestLifestyle's cache
                    if (container.GetInstance <IOwinContextAccessor>() is OwinContextAccessor webrequestCallContextOwinContextAccessor)
                    {
                        webrequestCallContextOwinContextAccessor.CurrentContext = context;
                    }
                }

                using (AsyncScopedLifestyle.BeginScope(container))
                {
                    // capture the owin context for any service dependant on it and store it in the async scoped container
                    // this will use AsyncScopedLifestyle's cache
                    if (container.GetInstance <IOwinContextAccessor>() is OwinContextAccessor asyncCallContextOwinContextAccessor)
                    {
                        asyncCallContextOwinContextAccessor.CurrentContext = context;
                    }

                    await next();
                }
            });

            app.UseWebApi(httpConfig);

            AttackProcedures.LoadCovenantOwnersIntoRAM();
            DungeonProcedures.GenerateDungeon();

            // set chaos mode
            IPvPWorldStatRepository repo = new EFPvPWorldStatRepository();
            var data = repo.PvPWorldStats.FirstOrDefault();

            PvPStatics.ChaosMode     = data != null ? data.ChaosMode : false;
            PvPStatics.RoundDuration = data != null ? data.RoundDuration : 5000;

            TurnTimesStatics.ActiveConfiguration = data != null && TurnTimesStatics.IsValidConfiguration(data.TurnTimeConfiguration) ? data.TurnTimeConfiguration : TurnTimesStatics.FiveMinuteTurns;

            PvPStatics.AlphaRound = DomainRegistry.Repository.FindSingle(new GetWorld()).RoundNumber ?? PvPStatics.AlphaRound;

            if (data != null)
            {
                JokeShopProcedures.SetJokeShopActive(data.JokeShop);
            }
        }
        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);
        }
        internal static BountyInfo BountyDetails(Player player, int effectId)
        {
            var effect = MAPPINGS.FirstOrDefault(e => e.EffectSourceId == effectId);

            if (effect == null || !EffectProcedures.PlayerHasEffect(player, effect.EffectSourceId))
            {
                return(null);
            }

            var possibleFormSourceIds = JokeShopProcedures.Forms(e => e.Category == effect.Category).Select(e => e.FormSourceId).ToArray();
            var numFormSourceIds      = possibleFormSourceIds.Count();

            if (numFormSourceIds == 0)
            {
                return(null);
            }

            var playerEffect = EffectProcedures.GetPlayerEffects2(player.Id).FirstOrDefault(e => e.dbEffect.EffectSourceId == effect.EffectSourceId);

            if (playerEffect == null || playerEffect.dbEffect.Duration == 0)
            {
                return(null);
            }

            var duration = playerEffect.dbEffect.Duration;

            if (duration <= 0)
            {
                return(null);
            }

            var turn = PvPStatics.LastGameTurn;

            if (turn == 0)
            {
                // Check server hasn't been restarted mid-game (in which case PvPStatics.LastGameTurn is not accurate)
                var worldStats = DomainRegistry.Repository.FindSingle(new GetWorld());
                turn = worldStats.TurnNumber;
            }

            var expiresTurn  = turn + duration;
            var formIndex    = player.Id + expiresTurn;
            var formSourceId = possibleFormSourceIds[formIndex % possibleFormSourceIds.Count()];

            // Locate the desired form
            IDbStaticFormRepository formsRepo = new EFDbStaticFormRepository();
            var form = formsRepo.DbStaticForms.FirstOrDefault(f => f.Id == formSourceId);

            if (form == null)
            {
                return(null);
            }

            // Calculate the reward that could be claimed right now
            var reward = Math.Min(MAXIMUM_REWARD, BASE_REWARD + (MAXIMUM_REWARD - BASE_REWARD) * duration / TURNS_OF_BOUNTY);

            if (form.MobilityType == PvPStatics.MobilityFull)
            {
                reward /= 2;
            }

            var category = MAPPINGS.Where(m => m.EffectSourceId == effect.EffectSourceId).Select(m => m.Category).FirstOrDefault();

            return(new BountyInfo {
                PlayerName = player.GetFullName(), Form = form, ExpiresTurn = expiresTurn, CurrentReward = reward, Category = category
            });
        }
Beispiel #8
0
        public static string SummonPsychopath(Player player, Random rand = null, int?strengthOverride = null, bool?aggro = null)
        {
            rand = rand ?? new Random();

            var baseStrength = Math.Min(Math.Max(0, (player.Level - 1) / 3), 3);
            var strength     = strengthOverride ?? (baseStrength + Math.Max(0, rand.Next(6) - 1));

            var prefix = "";
            int level;
            int perk;
            int?extraPerk = null;
            var gender    = rand.Next(2);
            int form;

            var turnNumber = PvPWorldStatProcedures.GetWorldTurnNumber();

            if (strength <= 0 || (turnNumber < 300 && !strengthOverride.HasValue))
            {
                strength = 0;
                level    = 1;
                perk     = AIProcedures.PsychopathicForLevelOneEffectSourceId;
                form     = gender == 0 ? AIProcedures.Psycho1MId : AIProcedures.Psycho1FId;
            }
            else if (strength == 1 || (turnNumber < 600 && !strengthOverride.HasValue))
            {
                strength = 1;
                level    = 3;
                prefix   = "Fierce";
                perk     = AIProcedures.PsychopathicForLevelThreeEffectSourceId;
                form     = gender == 0 ? AIProcedures.Psycho3MId : AIProcedures.Psycho3FId;
            }
            else if (strength == 2 || (turnNumber < 900 && !strengthOverride.HasValue))
            {
                strength = 2;
                level    = 5;
                prefix   = "Wrathful";
                perk     = AIProcedures.PsychopathicForLevelFiveEffectSourceId;
                form     = gender == 0 ? AIProcedures.Psycho5MId : AIProcedures.Psycho5FId;
            }
            else if (strength == 3 || (turnNumber < 1500 && !strengthOverride.HasValue))
            {
                strength = 3;
                level    = 7;
                prefix   = "Loathful";
                perk     = AIProcedures.PsychopathicForLevelSevenEffectSourceId;
                form     = gender == 0 ? AIProcedures.Psycho7MId : AIProcedures.Psycho7FId;
            }
            else if (strength == 4 || (turnNumber < 2400 && !strengthOverride.HasValue))
            {
                strength = 4;
                level    = 9;
                prefix   = "Soulless";
                perk     = AIProcedures.PsychopathicForLevelNineEffectSourceId;
                form     = gender == 0 ? AIProcedures.Psycho9MId : AIProcedures.Psycho9FId;
            }
            else if (strength == 5 || (turnNumber < 3600 && !strengthOverride.HasValue))
            {
                strength  = 5;
                level     = 11;
                prefix    = "Ruthless";
                perk      = AIProcedures.PsychopathicForLevelNineEffectSourceId;
                extraPerk = AIProcedures.PsychopathicForLevelThreeEffectSourceId;
                form      = gender == 0 ? AIProcedures.Psycho9MId : AIProcedures.Psycho9FId;
            }
            else
            {
                strength  = 6;
                level     = 13;
                prefix    = "Eternal";
                perk      = AIProcedures.PsychopathicForLevelNineEffectSourceId;
                extraPerk = AIProcedures.PsychopathicForLevelFiveEffectSourceId;
                form      = gender == 0 ? AIProcedures.Psycho9MId : AIProcedures.Psycho9FId;
            }

            var firstName = "Psychopath";
            var lastName  = NameService.GetRandomLastName();

            if (!prefix.IsEmpty())
            {
                firstName = $"{prefix} {firstName}";
            }

            IPlayerRepository playerRepo = new EFPlayerRepository();
            var cmd = new CreatePlayer
            {
                FirstName          = firstName,
                LastName           = lastName,
                Location           = player.dbLocationName,
                FormSourceId       = form,
                Level              = level,
                Health             = 100000,
                MaxHealth          = 100000,
                Mana               = 100000,
                MaxMana            = 100000,
                BotId              = AIStatics.PsychopathBotId,
                UnusedLevelUpPerks = 0,
                XP     = 0,
                Money  = (strength + 1) * 50,
                Gender = gender == 0 ? PvPStatics.GenderMale : PvPStatics.GenderFemale,
            };

            var botId = DomainRegistry.Repository.Execute(cmd);

            // Give spells
            var eligibleSkills = SkillStatics.GetLearnablePsychopathSkills().ToList();

            SkillProcedures.GiveSkillToPlayer(botId, eligibleSkills[rand.Next(eligibleSkills.Count())].Id);

            if (strength >= 5)
            {
                SkillProcedures.GiveSkillToPlayer(botId, PvPStatics.Spell_WeakenId);
            }

            if (strength >= 6)
            {
                var limitedMobilityForms = JokeShopProcedures.AnimateForms().Where(f => f.Category == JokeShopProcedures.LIMITED_MOBILITY).ToArray();

                if (limitedMobilityForms.Any())
                {
                    IDbStaticSkillRepository skillsRepo = new EFDbStaticSkillRepository();

                    var formId        = limitedMobilityForms[rand.Next(limitedMobilityForms.Count())].FormSourceId;
                    var immobileSkill = skillsRepo.DbStaticSkills.FirstOrDefault(spell => spell.FormSourceId == formId);

                    if (immobileSkill != null)
                    {
                        SkillProcedures.GiveSkillToPlayer(botId, immobileSkill.Id);
                    }
                }
            }

            // Give bonuses
            EffectProcedures.GivePerkToPlayer(perk, botId);

            if (extraPerk.HasValue)
            {
                EffectProcedures.GivePerkToPlayer(extraPerk.Value, botId);
            }

            // Give runes
            var quantity = rand.Next(1, 3);

            for (var c = 0; c < quantity; c++)
            {
                var runeId = DomainRegistry.Repository.FindSingle(new GetRandomRuneAtLevel {
                    RuneLevel = strength * 2 + 1, Random = rand
                });
                DomainRegistry.Repository.Execute(new GiveRune {
                    ItemSourceId = runeId, PlayerId = botId
                });
            }

            // Balance stats
            var psychoEF = playerRepo.Players.FirstOrDefault(p => p.Id == botId);

            psychoEF.ReadjustMaxes(ItemProcedures.GetPlayerBuffs(psychoEF));
            playerRepo.SavePlayer(psychoEF);

            // Tell the bot to attack the player
            if (!aggro.HasValue || aggro.Value)
            {
                AIDirectiveProcedures.SetAIDirective_Attack(botId, player.Id);
            }

            PlayerLogProcedures.AddPlayerLog(player.Id, $"<b>You have summoned {firstName} {lastName}!</b>  Beware!  They are not friendly!!", true);
            LocationLogProcedures.AddLocationLog(player.dbLocationName, $"{player.GetFullName()} has summoned <b>{firstName} {lastName}</b>!");

            return("Near the counter is an altar with a leather-bound book resting open upon it.  You take a look and try to read one of the jokes aloud.  It seems to be some consonant-heavy tongue twister that soon leaves you faltering.  You're not quite sure what the set up means, but hope the punchline will be worth it.  As you spurt out the last syllable a puff of red smoke explodes out from the book with an audible bang.  You're not laughing, and that remains the case when you close the book to see a large pentagram seared into the cover.  As the smoke subsides there seems to be a strange neon flicker to the light and a crackling to the air.  You turn sharply to see the psychopath you just summoned readying their attack against you!");
        }
Beispiel #9
0
        public void SendAs(string message, PlayerFormViewModel me)
        {
            string room = Clients.Caller.toRoom;

            me.Player.UpdateOnlineActivityTimestamp();

            if (me.Player.BotId == AIStatics.ActivePlayerBotId && DomainRegistry.Repository.FindSingle(new IsAccountLockedOut {
                userId = me.Player.MembershipId
            }))
            {
                return;
            }

            // Assert player is not banned
            if (me.Player.IsBannedFromGlobalChat && room == "global")
            {
                return;
            }

            if (!message.TrimStart().StartsWith("/") && EffectProcedures.PlayerHasActiveEffect(me.Player.ToDbPlayer(), CharacterPrankProcedures.HUSHED_EFFECT))
            {
                String[] denied = { "/me tries to speak but cannot!",
                                    "/me puffs profusely but doesn't make a sound!",
                                    "/me is unable to utter a single word!",
                                    "/me gesticulates wildly in the hope of communicating!",
                                    "/me is afflicted by a magical field, leaving them completely mute!",
                                    "/me has been hushed!",
                                    "/me tries to mouth some words in the hope you can understand!",
                                    "/me has lost their voice!",
                                    "/me has their lips sealed!",
                                    "/me will be unable to speak until the enchantment cast on them has lapsed!",
                                    "/me is counting down the minutes until they are able to speak again!",
                                    "/me tries to communicate through telepathy!" };
                message = denied[new Random().Next(denied.Count())];
            }

            // Get player picture and name
            var pic        = HtmlHelpers.GetImageURL(me, true).ToString();
            var descriptor = me.Player.GetDescriptor();

            var name = descriptor.Item1;

            pic = string.IsNullOrWhiteSpace(descriptor.Item2) ? pic : descriptor.Item2;

            if (me.Player.BotId == AIStatics.ActivePlayerBotId)
            {
                if (Context.User.IsInRole(PvPStatics.Permissions_Developer))
                {
                    name = name + " (Dev)";
                }
                else if (Context.User.IsInRole(PvPStatics.Permissions_Admin))
                {
                    name = name + " (Admin)";
                }
                else if (Context.User.IsInRole(PvPStatics.Permissions_Moderator))
                {
                    switch (me.Player.MembershipId)
                    {
                    case "d465db1c-ba4f-4347-b666-4dfd1c9a5e33":     //Because Martha wants to be a filthy casual.
                        break;

                    case "08b476c3-d262-45b6-9e6a-7d94b472fefe":
                        break;     //So is this one.

                    default:
                        name = name + " (Mod)";
                        break;
                    }
                }
            }

            // Performs message processing to correctly format any special text
            var output = ChatMessageProcessor.ProcessMessage(new MessageData(name, message));

            if (!string.IsNullOrWhiteSpace(output.Text))
            {
                var colorOut = output.SendPlayerChatColor ? me.Player.ChatColor : "";

                if (me.Player.BotId == AIStatics.ActivePlayerBotId)
                {
                    _chatPersistenceService.TrackMessageSend(me.Player.MembershipId, Context.ConnectionId);
                }

                var model = new
                {
                    User        = name,
                    IsStaff     = me.Player.BotId == AIStatics.ActivePlayerBotId ? ChatStatics.Staff.ContainsKey(me.Player.MembershipId) : false,
                    Color       = colorOut,
                    Pic         = pic,
                    Message     = WebUtility.HtmlEncode(output.Text),
                    MessageType = Enum.GetName(output.MessageType.GetType(), output.MessageType),
                    Timestamp   = DateTime.UtcNow.ToUnixTime(),
                };

                if (me.Player.BotId == AIStatics.ActivePlayerBotId && _chatPersistenceService.HasNameChanged(me.Player.MembershipId, name))
                {
                    _chatPersistenceService.TrackPlayerNameChange(me.Player.MembershipId, name);
                    Clients.Caller.nameChanged(name);
                }

                Clients.Group(room).addNewMessageToPage(model);

                DomainRegistry.Repository.Execute(new CreateChatLog
                {
                    Message     = output.Text,
                    Room        = room,
                    Name        = name,
                    UserId      = me.Player.MembershipId,
                    Color       = me.Player.ChatColor,
                    PortraitUrl = pic
                });
            }

            UpdateUserList(room);

            // NPC dice prank
            if (room == "global" && me.Player.BotId == AIStatics.ActivePlayerBotId &&
                message.StartsWith("/roll") && message.Contains("4d20") &&
                JokeShopProcedures.IsJokeShopActive())
            {
                var rand = new Random();

                if (rand.Next(10) == 0)
                {
                    IPlayerRepository playerRepo = new EFPlayerRepository();
                    var npcIds = playerRepo.Players.Where(p => p.BotId <= AIStatics.PsychopathBotId &&
                                                          p.Mobility == PvPStatics.MobilityFull)
                                 .Select(p => p.Id).ToArray();

                    if (npcIds.Any())
                    {
                        var npcId = npcIds[rand.Next(npcIds.Count())];
                        SendAs(message, PlayerProcedures.GetPlayerFormViewModel(npcId));
                    }
                }
            }
        }
        public static void UpdateWorld()
        {
            var worldStats = DomainRegistry.Repository.FindSingle(new GetWorld());

            var turnNo = worldStats.TurnNumber;

            PvPStatics.LastGameTurn = turnNo;

            if (turnNo < PvPStatics.RoundDuration)
            {
                PvPStatics.AnimateUpdateInProgress = true;

                IServerLogRepository serverLogRepo = new EFServerLogRepository();
                var log = new ServerLog
                {
                    TurnNumber      = turnNo,
                    StartTimestamp  = DateTime.UtcNow,
                    FinishTimestamp = DateTime.UtcNow,
                    Errors          = 0,
                    FullLog         = "",
                    Population      = PlayerProcedures.GetWorldPlayerStats().CurrentOnlinePlayers,
                };
                log.AddLog($"Started new log for turn {turnNo} at <b>{DateTime.UtcNow}</b>.");
                serverLogRepo.SaveServerLog(log);
                var updateTimer = new Stopwatch();
                updateTimer.Start();

                IPlayerRepository playerRepo = new EFPlayerRepository();

                Player lindella = playerRepo.Players.FirstOrDefault(p => p.BotId == AIStatics.LindellaBotId);
                Player wuffie   = playerRepo.Players.FirstOrDefault(p => p.BotId == AIStatics.WuffieBotId);

                #region spawn NPCS
                // make sure the NPCs have been spawned early turn
                if (turnNo <= 3)
                {
                    if (lindella == null)
                    {
                        BossProcedures_Lindella.SpawnLindella();
                        lindella = playerRepo.Players.FirstOrDefault(p => p.BotId == AIStatics.LindellaBotId);
                    }

                    if (wuffie == null)
                    {
                        BossProcedures_PetMerchant.SpawnPetMerchant();
                        wuffie = playerRepo.Players.FirstOrDefault(p => p.BotId == AIStatics.WuffieBotId);
                    }

                    var fae = playerRepo.Players.FirstOrDefault(p => p.BotId == AIStatics.JewdewfaeBotId);
                    if (fae == null)
                    {
                        BossProcedures_Jewdewfae.SpawnFae();
                    }

                    var bartender = playerRepo.Players.FirstOrDefault(p => p.BotId == AIStatics.BartenderBotId);
                    if (bartender == null)
                    {
                        BossProcedures_Bartender.SpawnBartender();
                    }

                    var lorekeeper = playerRepo.Players.FirstOrDefault(p => p.BotId == AIStatics.LoremasterBotId);
                    if (lorekeeper == null)
                    {
                        BossProcedures_Loremaster.SpawnLoremaster();
                    }

                    var soulbinder = playerRepo.Players.FirstOrDefault(p => p.BotId == AIStatics.SoulbinderBotId);
                    if (soulbinder == null)
                    {
                        var id = DomainRegistry.Repository.Execute(new CreatePlayer
                        {
                            FirstName               = "Karin",
                            LastName                = "Kezesul-Adriz the Soulbinder",
                            FormSourceId            = 1000,
                            Location                = "stripclub_office",
                            Level                   = 10,
                            Mobility                = PvPStatics.MobilityFull,
                            Money                   = 0,
                            Gender                  = PvPStatics.GenderFemale,
                            Health                  = 9999,
                            MaxHealth               = 9999,
                            Mana                    = 9999,
                            MaxMana                 = 9999,
                            OnlineActivityTimestamp = DateTime.UtcNow,
                            BotId                   = AIStatics.SoulbinderBotId
                        });

                        var newSoulbinder = playerRepo.Players.FirstOrDefault(p => p.Id == id);
                        newSoulbinder.ReadjustMaxes(ItemProcedures.GetPlayerBuffs(newSoulbinder));
                        playerRepo.SavePlayer(newSoulbinder);
                    }
                }
                #endregion

                log.AddLog(updateTimer.ElapsedMilliseconds + ":  Started loading animate players");

                IEffectRepository effectRepo = new EFEffectRepository();

                log.AddLog(updateTimer.ElapsedMilliseconds + ":  Started loading effects");
                var temporaryEffects = effectRepo.Effects.Where(e => !e.IsPermanent).ToList();
                log.AddLog(updateTimer.ElapsedMilliseconds + ":  Finished loading effects");
                var effectsToDelete = new List <Effect>();

                log.AddLog(updateTimer.ElapsedMilliseconds + ":  Started updating effects");
                var effectsExpiringThisTurn = temporaryEffects.Where(e => e.Duration == 1).ToList();
                var activeOrExpiringEffects = temporaryEffects.Where(e => e.Duration > 0).ToList();

                foreach (var e in temporaryEffects)
                {
                    e.Duration--;
                    e.Cooldown--;

                    if (e.Duration < 0)
                    {
                        e.Duration = 0;
                    }

                    if (e.Cooldown <= 0)
                    {
                        effectsToDelete.Add(e);
                    }
                    else
                    {
                        effectRepo.SaveEffect(e);
                    }
                }
                log.AddLog(updateTimer.ElapsedMilliseconds + ":  Finished updating effects");
                serverLogRepo.SaveServerLog(log);

                log.AddLog(updateTimer.ElapsedMilliseconds + ":  Started running effect-expiry actions");
                try
                {
                    JokeShopProcedures.RunEffectExpiryActions(effectsExpiringThisTurn);
                }
                catch (Exception e)
                {
                    log.Errors++;
                    log.AddLog(FormatExceptionLog(updateTimer.ElapsedMilliseconds, " ERROR running effect-expiry actions", e));
                }
                log.AddLog(updateTimer.ElapsedMilliseconds + ":  Finished running effect-expiry actions");
                serverLogRepo.SaveServerLog(log);

                log.AddLog(updateTimer.ElapsedMilliseconds + ":  Started deleting expired effects");
                foreach (var e in effectsToDelete)
                {
                    effectRepo.DeleteEffect(e.Id);
                }
                log.AddLog(updateTimer.ElapsedMilliseconds + ":  Finished deleting expired effects");
                serverLogRepo.SaveServerLog(log);

                #region playerExtra / protection cooldown loop
                IPlayerExtraRepository playerExtraRepo = new EFPlayerExtraRepository();
                var extrasToIncrement            = playerExtraRepo.PlayerExtras.ToList();
                var extrasToIncrement_SaveList   = new List <PlayerExtra>();
                var extrasToIncrement_DeleteList = new List <PlayerExtra>();
                log.AddLog(updateTimer.ElapsedMilliseconds + ":  Started updating protection change cooldown (" + extrasToIncrement.Count + ")");

                foreach (var e in extrasToIncrement)
                {
                    var owner = PlayerProcedures.GetPlayer(e.PlayerId);
                    if (PlayerProcedures.PlayerIsOffline(owner))
                    {
                        extrasToIncrement_SaveList.Add(e);
                    }
                }

                foreach (var e in extrasToIncrement_SaveList)
                {
                    if (e.ProtectionToggleTurnsRemaining > 0)
                    {
                        e.ProtectionToggleTurnsRemaining--;
                        playerExtraRepo.SavePlayerExtra(e);
                    }
                    else if (e.ProtectionToggleTurnsRemaining <= 0)
                    {
                        extrasToIncrement_DeleteList.Add(e);
                    }
                }

                foreach (var e in extrasToIncrement_DeleteList)
                {
                    playerExtraRepo.DeletePlayerExtra(e.Id);
                }

                #endregion

                log.AddLog(updateTimer.ElapsedMilliseconds + ":  Finished updating protection change cooldown.");


                #region main player loop

                using (var context = new StatsContext())
                {
                    try
                    {
                        context.Database.ExecuteSqlCommand("UPDATE [dbo].[Players] SET TimesAttackingThisUpdate = 0, CleansesMeditatesThisRound = 0, ShoutsRemaining = 1, ActionPoints = ActionPoints + 10, ItemsUsedThisTurn = 0 WHERE Mobility='full'" +

                                                           $"UPDATE [dbo].[Players] SET ActionPoints_Refill = ActionPoints_Refill + (ActionPoints % {TurnTimesStatics.GetActionPointLimit()} / 2) WHERE ActionPoints >= {TurnTimesStatics.GetActionPointLimit()} AND Mobility='full'" +

                                                           $"UPDATE [dbo].[Players] SET ActionPoints = {TurnTimesStatics.GetActionPointLimit()} WHERE ActionPoints > {TurnTimesStatics.GetActionPointLimit()} AND Mobility='full'" +

                                                           $"UPDATE [dbo].[Players] SET ActionPoints_Refill = {TurnTimesStatics.GetActionPointReserveLimit()} WHERE ActionPoints_Refill > {TurnTimesStatics.GetActionPointReserveLimit()} AND Mobility='full'" +

                                                           $"UPDATE [dbo].[Players] SET ActionPoints = ActionPoints + 20, ActionPoints_Refill = ActionPoints_Refill - 20 WHERE ActionPoints <= {TurnTimesStatics.GetActionPointLimit()-20} AND ActionPoints_Refill >= 20 AND Mobility='full'");

                        if (PvPStatics.ChaosMode)
                        {
                            context.Database.ExecuteSqlCommand($"Update [dbo].[Players] SET ActionPoints = {TurnTimesStatics.GetActionPointLimit()}, ActionPoints_Refill = {TurnTimesStatics.GetActionPointReserveLimit()}, Mana = MaxMana");
                        }

                        log.AddLog(updateTimer.ElapsedMilliseconds + ":  ANIMATE SQL UPDATE SUCCEEDED!");
                        serverLogRepo.SaveServerLog(log);
                    }
                    catch (Exception e)
                    {
                        log.Errors++;
                        log.AddLog(FormatExceptionLog(updateTimer.ElapsedMilliseconds, " ANIMATE SQL UPDATE FAILED", e));
                        serverLogRepo.SaveServerLog(log);
                    }
                }

                var timeCutoff    = DateTime.UtcNow.AddHours(-8);
                var playersToSave = playerRepo.Players
                                    .Where(p => p.Mobility == PvPStatics.MobilityFull && p.LastActionTimestamp > timeCutoff).ToList();

                foreach (var player in playersToSave)
                {
                    //Skip regen stuff for those in quests.
                    if (player.InQuest > 0)
                    {
                        continue;
                    }

                    // extra AP condition checks
                    if (player.Covenant > 0)
                    {
                        var playerCov = CovenantDictionary.IdNameFlagLookup.FirstOrDefault(c => c.Key == player.Covenant).Value;

                        // give this player an extra AP refill if they are at their safeground, scaled up by level
                        if (playerCov != null && !playerCov.HomeLocation.IsNullOrEmpty() && player.dbLocationName == playerCov.HomeLocation)
                        {
                            player.ActionPoints_Refill += .25M * playerCov.CovLevel;
                        }

                        // give this player an extra AP refill if they are on a location that their covenane has enchanted
                        var currentLocation = LocationsStatics.LocationList.GetLocation.FirstOrDefault(l => l.dbName == player.dbLocationName);

                        if (currentLocation != null && currentLocation.CovenantController == player.Covenant)
                        {
                            if (currentLocation.TakeoverAmount < 25)
                            {
                                player.ActionPoints_Refill += .05M;
                            }
                            else if (currentLocation.TakeoverAmount <= 50)
                            {
                                player.ActionPoints_Refill += .10M;
                            }
                            else if (currentLocation.TakeoverAmount <= 75)
                            {
                                player.ActionPoints_Refill += .15M;
                            }
                            else if (currentLocation.TakeoverAmount < 100)
                            {
                                player.ActionPoints_Refill += .20M;
                            }
                            else if (currentLocation.TakeoverAmount >= 100)
                            {
                                player.ActionPoints_Refill += .25M;
                            }
                        }

                        // make sure AP reserve stays within maximum amount
                        if (player.ActionPoints_Refill > TurnTimesStatics.GetActionPointReserveLimit())
                        {
                            player.ActionPoints_Refill = TurnTimesStatics.GetActionPointReserveLimit();
                        }
                    }

                    var buffs = ItemProcedures.GetPlayerBuffs(player);
                    player.Health += buffs.HealthRecoveryPerUpdate();
                    player.Mana   += buffs.ManaRecoveryPerUpdate();

                    player.ReadjustMaxes(buffs);

                    playerRepo.SavePlayer(player);
                }

                log.AddLog($"{updateTimer.ElapsedMilliseconds}:  Finished updating animate players ({playersToSave.Count})");
                serverLogRepo.SaveServerLog(log);

                #endregion main player loop

                log.AddLog(updateTimer.ElapsedMilliseconds + ":  Started updating inanimate/animal players");


                using (var context = new StatsContext())
                {
                    try
                    {
                        context.Database.ExecuteSqlCommand("UPDATE [dbo].[Players] SET TimesAttackingThisUpdate = 0, ItemsUsedThisTurn = 0 WHERE (Mobility = 'inanimate' OR Mobility = 'animal') AND BotId = 0");
                        log.AddLog(updateTimer.ElapsedMilliseconds + ":  Finished updating inanimate/animal players");
                    }
                    catch (Exception e)
                    {
                        log.Errors++;
                        log.AddLog(FormatExceptionLog(updateTimer.ElapsedMilliseconds, " ERROR UPDATING INANIMATE/ANIMAL PLAYERS", e));
                    }
                }

                serverLogRepo.SaveServerLog(log);

                #region decrement mind control timers
                log.AddLog(updateTimer.ElapsedMilliseconds + ":  Started mind control cooldown.");

                using (var context = new StatsContext())
                {
                    try
                    {
                        context.Database.ExecuteSqlCommand("UPDATE [dbo].[MindControls] SET TurnsRemaining = TurnsRemaining - 1");
                        context.Database.ExecuteSqlCommand("DELETE FROM [dbo].[MindControls] WHERE TurnsRemaining <= 0");
                        context.Database.ExecuteSqlCommand("UPDATE [dbo].[MindControls] SET TimesUsedThisTurn = 0");

                        log.AddLog(updateTimer.ElapsedMilliseconds + ":  Finished mind control cooldown.");
                    }
                    catch (Exception e)
                    {
                        log.Errors++;
                        log.AddLog(FormatExceptionLog(updateTimer.ElapsedMilliseconds, "MIND CONTROL COOLDOWN UPDATE FAILED", e));
                    }
                }
                #endregion

                serverLogRepo.SaveServerLog(log);

                PvPStatics.AnimateUpdateInProgress = false;

                // bump down the timer on all items that are reuseable consumables
                log.AddLog(updateTimer.ElapsedMilliseconds + ":  Started updating items on cooldown");
                IItemRepository itemsRepo     = new EFItemRepository();
                var             itemsToUpdate = itemsRepo.Items.Where(i => i.TurnsUntilUse > 0).ToList();

                foreach (var item in itemsToUpdate)
                {
                    item.TurnsUntilUse--;
                }

                foreach (var item in itemsToUpdate)
                {
                    itemsRepo.SaveItem(item);
                }
                log.AddLog(updateTimer.ElapsedMilliseconds + ":  Finished updating items on cooldown");
                serverLogRepo.SaveServerLog(log);

                // find the ids for the merchants Lindella and Skaldyr
                var skaldyr      = PlayerProcedures.GetPlayerFromBotId(AIStatics.LoremasterBotId);
                var soulbinderId = PlayerProcedures.GetPlayerFromBotId(AIStatics.SoulbinderBotId).Id;

                // have abandoned items go to Lindella
                if (turnNo % 11 == 3 && lindella.Mobility == PvPStatics.MobilityFull)
                {
                    log.AddLog(updateTimer.ElapsedMilliseconds + ":  Started collecting all abandoned items for Lindella");

                    using (var context = new StatsContext())
                    {
                        try
                        {
                            context.Database.ExecuteSqlCommand($"UPDATE [dbo].[Items] SET OwnerId = {lindella.Id}, dbLocationName = '', PvPEnabled = {(int)GameModeStatics.GameModes.Any}, TimeDropped = '{DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss")}' " +
                                                               $"FROM DbStaticItems WHERE dbLocationName <> '' AND dbLocationName IS NOT NULL AND TimeDropped < DATEADD(hour, -8, GETUTCDATE()) AND OwnerId IS NULL AND DbStaticItems.Id = Items.ItemSourceId AND ItemType != '{PvPStatics.ItemType_Pet}' AND ItemSourceId != {ItemStatics.ItemType_DungeonArtifactItemSourceId};" +
                                                               $"UPDATE [dbo].[Players] SET dbLocationName = '' FROM Items WHERE Items.FormerPlayerId = Players.Id AND Items.OwnerId = {lindella.Id};");



                            log.AddLog(updateTimer.ElapsedMilliseconds + ":  Finished collecting all abandoned items for Lindella");
                        }
                        catch (Exception e)
                        {
                            log.Errors++;
                            log.AddLog(FormatExceptionLog(updateTimer.ElapsedMilliseconds, "ERROR collecting all abandoned items for Lindella", e));
                        }
                    }
                }

                // delete all consumable type items that have been sitting around on the ground for too long
                log.AddLog(updateTimer.ElapsedMilliseconds + ":  Started deleting expired consumables");
                DomainRegistry.Repository.Execute(new DeleteExpiredConsumablesOnGround());
                DomainRegistry.Repository.Execute(new DeleteExpiredConsumablesOnMerchants {
                    LindellaId = lindella.Id, LorekeeperId = skaldyr.Id
                });

                log.AddLog(updateTimer.ElapsedMilliseconds + ":  Finished deleting expired consumables");
                serverLogRepo.SaveServerLog(log);

                log.AddLog(updateTimer.ElapsedMilliseconds + ":  Started deleting expired runes");
                try
                {
                    DomainRegistry.Repository.Execute(new DeleteExpiredRunesOnMerchants());
                }
                catch (Exception e)
                {
                    log.AddLog(FormatExceptionLog(updateTimer.ElapsedMilliseconds, "ERROR deleting expired runes", e));
                    log.Errors++;
                }

                log.AddLog(updateTimer.ElapsedMilliseconds + ":  Finished deleting expired runes");

                serverLogRepo.SaveServerLog(log);

                // allow all items that have been recently equipped to be taken back off
                log.AddLog(updateTimer.ElapsedMilliseconds + ":  Started resetting items that have been recently equipped");
                var recentlyEquipped = itemsRepo.Items.Where(i => i.EquippedThisTurn).ToList();

                foreach (var item in recentlyEquipped)
                {
                    item.EquippedThisTurn = false;
                    itemsRepo.SaveItem(item);
                }
                log.AddLog(updateTimer.ElapsedMilliseconds + ":  Finished resetting items that have been recently equipped");

                #region give covenants money based on territories
                if (turnNo % 6 == 0)
                {
                    log.AddLog(updateTimer.ElapsedMilliseconds + ":  Started giving covenants money from territories");
                    ICovenantRepository covRepo = new EFCovenantRepository();
                    var covs = covRepo.Covenants.Where(c => c.HomeLocation != null && c.HomeLocation != "").ToList();


                    foreach (var c in covs)
                    {
                        var locationControlledSum = CovenantProcedures.GetLocationControlCount(c);
                        var moneyGain             = (decimal)Math.Floor(Convert.ToDouble(locationControlledSum));
                        c.Money += moneyGain;

                        if (moneyGain > 0)
                        {
                            CovenantProcedures.WriteCovenantLog("Your covenant collected " + moneyGain + " Arpeyjis from the locations you have enchanted.", c.Id, false);
                        }
                        covRepo.SaveCovenant(c);
                    }
                    log.AddLog(updateTimer.ElapsedMilliseconds + ":  Finished giving covenants money from territories");
                }
                #endregion

                serverLogRepo.SaveServerLog(log);

                #region drop dungeon artifacts and spawn demons if needed

                try
                {
                    if (turnNo % 7 == 2)
                    {
                        log.AddLog(updateTimer.ElapsedMilliseconds + ":  Starting dungeon item / demon spawning");
                        var dungeonArtifactCount = itemsRepo.Items.Count(i => i.ItemSourceId == ItemStatics.ItemType_DungeonArtifactItemSourceId);
                        for (var x = 0; x < PvPStatics.DungeonArtifact_SpawnLimit - dungeonArtifactCount; x++)
                        {
                            var randDungeon = LocationsStatics.GetRandomLocation_InDungeon();

                            var cmd = new CreateItem
                            {
                                dbLocationName   = randDungeon,
                                OwnerId          = null,
                                EquippedThisTurn = false,
                                IsPermanent      = true,
                                Level            = 0,
                                PvPEnabled       = 2,
                                IsEquipped       = false,
                                TurnsUntilUse    = 0,
                                ItemSourceId     = ItemStatics.ItemType_DungeonArtifactItemSourceId
                            };
                            DomainRegistry.Repository.Execute(cmd);
                        }


                        IEnumerable <Player> demons = playerRepo.Players.Where(i => i.FormSourceId == PvPStatics.DungeonDemonFormSourceId);
                        var dungeonDemonCount       = demons.Count();

                        var randLevel = new Random(Guid.NewGuid().GetHashCode());

                        var demonNames = XmlResourceLoader.Load <List <string> >("TT.Domain.XMLs.DungeonDemonNames.xml");

                        for (var x = 0; x < PvPStatics.DungeonDemon_Limit - dungeonDemonCount; x++)
                        {
                            var randDungeon = LocationsStatics.GetRandomLocation_InDungeon();

                            // pull a random last demon name
                            double maxDemonNameCount = demonNames.Count();
                            var    num           = randLevel.NextDouble();
                            var    demonIndex    = Convert.ToInt32(Math.Floor(num * maxDemonNameCount));
                            var    demonlastName = demonNames.ElementAt(demonIndex);

                            // if there's already a demon with this last name, reroll and try again
                            if (demons.FirstOrDefault(d => d.LastName == demonlastName) != null)
                            {
                                x--;
                                continue;
                            }


                            var levelRoll = randLevel.NextDouble();
                            var level     = (int)Math.Floor(levelRoll * 8 + 3);

                            var cmd = new CreatePlayer
                            {
                                BotId        = AIStatics.DemonBotId,
                                FirstName    = "Spirit of ",
                                LastName     = demonlastName,
                                Mobility     = PvPStatics.MobilityFull,
                                FormSourceId = AIStatics.DungeonDemonFormId,
                                Gender       = PvPStatics.GenderFemale,
                                GameMode     = 2,
                                Health       = 10000,
                                Mana         = 10000,
                                Covenant     = -1,
                                Location     = randDungeon,
                                Level        = level,
                                MaxHealth    = 10000,
                                MaxMana      = 10000,
                            };

                            var id = DomainRegistry.Repository.Execute(cmd);

                            var newDemon = playerRepo.Players.FirstOrDefault(p => p.Id == id);

                            if (cmd.Level <= 5)
                            {
                                ItemProcedures.GiveNewItemToPlayer(newDemon, ItemStatics.SpellbookMediumItemSourceId);
                            }
                            else if (cmd.Level <= 7)
                            {
                                ItemProcedures.GiveNewItemToPlayer(newDemon, ItemStatics.SpellbookLargeItemSourceId);
                            }

                            else if (cmd.Level > 7)
                            {
                                ItemProcedures.GiveNewItemToPlayer(newDemon, ItemStatics.SpellbookGiantItemSourceId);
                            }

                            newDemon.ReadjustMaxes(ItemProcedures.GetPlayerBuffs(newDemon));
                            playerRepo.SavePlayer(newDemon);
                        }
                        log.AddLog(updateTimer.ElapsedMilliseconds + ":  FINISHED dungeon item / demon spawning");
                    }
                }
                catch (Exception e)
                {
                    log.Errors++;
                    log.AddLog(FormatExceptionLog(updateTimer.ElapsedMilliseconds, "ERROR running dungeon actions", e));
                }


                #endregion

                serverLogRepo.SaveServerLog(log);

                #region forcibly terminate duels that have timed out
                log.AddLog(updateTimer.ElapsedMilliseconds + ":  Started duel updates");
                try
                {
                    IDuelRepository duelRepo = new EFDuelRepository();
                    var             duels    = duelRepo.Duels.Where(d => d.Status == DuelProcedures.ACTIVE).ToList();

                    foreach (var d in duels)
                    {
                        // if the duel has timed out, end it forcibly
                        if ((turnNo - d.StartTurn) >= PvPStatics.MaximumDuelTurnLength)
                        {
                            DuelProcedures.EndDuel(d.Id, DuelProcedures.TIMEOUT);
                        }
                    }

                    log.AddLog(updateTimer.ElapsedMilliseconds + ":  Successfully completed duel updates");
                }
                catch (Exception e)
                {
                    log.Errors++;
                    log.AddLog(FormatExceptionLog(updateTimer.ElapsedMilliseconds, "ERROR completing duel updates", e));
                }
                #endregion duel updates

                log.AddLog(updateTimer.ElapsedMilliseconds + ":  Started Lindella actions");
                serverLogRepo.SaveServerLog(log);
                try
                {
                    BossProcedures_Lindella.RunActions(turnNo);
                }
                catch (Exception e)
                {
                    log.Errors++;
                    log.AddLog(FormatExceptionLog(updateTimer.ElapsedMilliseconds, "ERROR running Lindella action", e));
                }
                log.AddLog(updateTimer.ElapsedMilliseconds + ":  Finished Lindella actions");

                log.AddLog(updateTimer.ElapsedMilliseconds + ":  Started Wuffie actions");
                serverLogRepo.SaveServerLog(log);
                try
                {
                    BossProcedures_PetMerchant.RunPetMerchantActions(turnNo);
                }
                catch (Exception e)
                {
                    log.Errors++;
                    log.AddLog(FormatExceptionLog(updateTimer.ElapsedMilliseconds, "ERROR running Wuffie actions", e));
                }
                log.AddLog(updateTimer.ElapsedMilliseconds + ":  Finished Wuffie actions");

                log.AddLog(updateTimer.ElapsedMilliseconds + ":  Started running effect-related actions");
                try
                {
                    JokeShopProcedures.RunEffectActions(activeOrExpiringEffects);
                }
                catch (Exception e)
                {
                    log.Errors++;
                    log.AddLog(FormatExceptionLog(updateTimer.ElapsedMilliseconds, " ERROR running effect-related actions", e));
                }
                log.AddLog(updateTimer.ElapsedMilliseconds + ":  Finished running effect-related actions");
                serverLogRepo.SaveServerLog(log);

                #region furniture
                if (turnNo % 6 == 0)
                {
                    // move some furniture around on the market
                    try
                    {
                        FurnitureProcedures.MoveFurnitureOnMarket();
                    }
                    catch (Exception e)
                    {
                        log.Errors++;
                        log.AddLog(updateTimer.ElapsedMilliseconds + "ERROR MOVING FURNITURE ON MARKET:  " + e);
                    }

                    // move Jewdewfae to a new location if she has been in one place for more than 48 turns, 8 hours
                    try
                    {
                        var fae   = PlayerProcedures.GetPlayerFromBotId(-6);
                        var faeAI = AIDirectiveProcedures.GetAIDirective(fae.Id);

                        // if the turn since her last move has been long enough, relocate her
                        if (turnNo - (int)faeAI.Var2 > 48)
                        {
                            log.AddLog(updateTimer.ElapsedMilliseconds + ":  FORCED JEWDEWFAE TO MOVE.");
                            BossProcedures_Jewdewfae.MoveToNewLocation();
                        }
                    }
                    catch (Exception e)
                    {
                        log.Errors++;
                        log.AddLog(updateTimer.ElapsedMilliseconds + "ERROR TRYING TO MOVE JEWDEWFAE:  " + e);
                    }
                }
                #endregion furniture

                #region bosses

                // DONNA
                if (worldStats.Boss_Donna == AIStatics.ACTIVE)
                {
                    log.AddLog(updateTimer.ElapsedMilliseconds + ":  Started Donna actions");
                    serverLogRepo.SaveServerLog(log);
                    try
                    {
                        BossProcedures_Donna.RunDonnaActions();
                    }
                    catch (Exception e)
                    {
                        log.Errors++;
                        log.AddLog(FormatExceptionLog(updateTimer.ElapsedMilliseconds, "Error running Donna actions", e));
                    }
                    log.AddLog(updateTimer.ElapsedMilliseconds + ":  Finished Donna actions");
                }

                // VALENTINE
                if (worldStats.Boss_Valentine == AIStatics.ACTIVE || PlayerProcedures.GetAnimatePlayerFromBotId(AIStatics.ValentineBotId) != null)
                {
                    log.AddLog(updateTimer.ElapsedMilliseconds + ":  Started Valentine actions");
                    serverLogRepo.SaveServerLog(log);
                    try
                    {
                        BossProcedures_Valentine.RunValentineActions();
                    }
                    catch (Exception e)
                    {
                        log.Errors++;
                        log.AddLog(FormatExceptionLog(updateTimer.ElapsedMilliseconds, "Error running Valentine actions", e));
                    }
                    log.AddLog(updateTimer.ElapsedMilliseconds + ":  Finished Valentine actions");
                }

                // BIMBO
                if (worldStats.Boss_Bimbo == AIStatics.ACTIVE)
                {
                    log.AddLog(updateTimer.ElapsedMilliseconds + ":  Started Bimbo actions");
                    serverLogRepo.SaveServerLog(log);
                    try
                    {
                        BossProcedures_BimboBoss.RunActions(turnNo);
                    }
                    catch (Exception e)
                    {
                        log.Errors++;
                        log.AddLog(FormatExceptionLog(updateTimer.ElapsedMilliseconds, "Error running Bimbo actions", e));
                    }
                    log.AddLog(updateTimer.ElapsedMilliseconds + ":  Finished Bimbo actions");
                }

                // THIEVES
                if (worldStats.Boss_Thief == AIStatics.ACTIVE)
                {
                    log.AddLog(updateTimer.ElapsedMilliseconds + ":  Started Thieves actions");
                    serverLogRepo.SaveServerLog(log);
                    try
                    {
                        BossProcedures_Thieves.RunThievesAction(turnNo);
                    }
                    catch (Exception e)
                    {
                        log.Errors++;
                        log.AddLog(FormatExceptionLog(updateTimer.ElapsedMilliseconds, "Error running Thieves actions", e));
                    }
                    log.AddLog(updateTimer.ElapsedMilliseconds + ":  Finished Thieves actions");
                }

                // SISTERS
                if (worldStats.Boss_Sisters == AIStatics.ACTIVE)
                {
                    log.AddLog(updateTimer.ElapsedMilliseconds + ":  Started Sisters actions");
                    serverLogRepo.SaveServerLog(log);
                    try
                    {
                        BossProcedures_Sisters.RunSistersAction();
                    }
                    catch (Exception e)
                    {
                        log.Errors++;
                        log.AddLog(FormatExceptionLog(updateTimer.ElapsedMilliseconds, "Error running Sisters actions", e));
                    }
                    log.AddLog(updateTimer.ElapsedMilliseconds + ":  Finished Sisters actions");
                }

                // FAEBOSS
                if (worldStats.Boss_Faeboss == AIStatics.ACTIVE)
                {
                    log.AddLog(updateTimer.ElapsedMilliseconds + ":  Started Narcissa actions");
                    serverLogRepo.SaveServerLog(log);
                    try
                    {
                        BossProcedures_FaeBoss.RunTurnLogic();
                    }
                    catch (Exception e)
                    {
                        log.Errors++;
                        log.AddLog(FormatExceptionLog(updateTimer.ElapsedMilliseconds, "Error running Narcissa actions", e));
                    }
                    log.AddLog(updateTimer.ElapsedMilliseconds + ":  Finished Narcissa actions");
                }

                // BIKER GANG BOSS
                if (worldStats.Boss_MotorcycleGang == AIStatics.ACTIVE)
                {
                    log.AddLog(updateTimer.ElapsedMilliseconds + ":  Started BikerBoss actions");
                    serverLogRepo.SaveServerLog(log);
                    try
                    {
                        BossProcedures_MotorcycleGang.RunTurnLogic();
                    }
                    catch (Exception e)
                    {
                        log.Errors++;
                        log.AddLog(updateTimer.ElapsedMilliseconds + ":  BikerBoss ERROR:  " + e);
                    }
                    log.AddLog(updateTimer.ElapsedMilliseconds + ":  Finished BikerBoss actions");
                }

                #endregion bosses

                // psychopaths
                log.AddLog(updateTimer.ElapsedMilliseconds + ":  Started psychopath actions");
                serverLogRepo.SaveServerLog(log);

                var psychoExceptions = AIProcedures.RunPsychopathActions(worldStats);

                foreach (var e in psychoExceptions)
                {
                    log.Errors++;
                    log.AddLog(FormatExceptionLog(updateTimer.ElapsedMilliseconds, "Error running pycho action", e));
                }

                log.AddLog(updateTimer.ElapsedMilliseconds + ":  Finished psychopath actions");

                // minibosses
                log.AddLog(updateTimer.ElapsedMilliseconds + ":  Started miniboss actions");
                serverLogRepo.SaveServerLog(log);

                var minibossExceptions = BossProcedures_Minibosses.RunAll(worldStats.TurnNumber);

                foreach (var e in minibossExceptions)
                {
                    log.Errors++;
                    log.AddLog(FormatExceptionLog(updateTimer.ElapsedMilliseconds, "Error running miniboss action", e));
                }

                serverLogRepo.SaveServerLog(log);
                log.AddLog(updateTimer.ElapsedMilliseconds + ":  Finished miniboss actions");

                log.AddLog(updateTimer.ElapsedMilliseconds + ":  Starting setting update status to done");
                try
                {
                    PvPWorldStatProcedures.UpdateWorldTurnCounter_UpdateDone();
                    log.AddLog(updateTimer.ElapsedMilliseconds + ":  Finished setting update status to done");
                }
                catch (Exception e)
                {
                    log.AddLog(FormatExceptionLog(updateTimer.ElapsedMilliseconds, "ERROR setting update status to done: ", e));
                    log.Errors++;
                }

                serverLogRepo.SaveServerLog(log);

                try
                {
                    log.AddLog(updateTimer.ElapsedMilliseconds + ":  Started stored procedure maintenance");
                    using (var context = new StatsContext())
                    {
                        context.Database.ExecuteSqlCommand(
                            "DELETE FROM [dbo].[LocationLogs] WHERE Timestamp < DATEADD(hour, -1, GETUTCDATE())");
                        context.Database.ExecuteSqlCommand(
                            "DELETE FROM [dbo].[Messages] WHERE Timestamp < DATEADD(hour, -72, GETUTCDATE()) AND DoNotRecycleMe = 0");
                        context.Database.ExecuteSqlCommand("DELETE FROM [dbo].[TFEnergies] WHERE Amount < .5");
                        context.Database.ExecuteSqlCommand(
                            "DELETE FROM [dbo].[PlayerLogs] WHERE Timestamp < DATEADD(hour, -72, GETUTCDATE())");
                        context.Database.ExecuteSqlCommand(
                            "DELETE FROM [dbo].[ChatLogs] WHERE Timestamp < DATEADD(hour, -72, GETUTCDATE())");
                        context.Database.ExecuteSqlCommand(
                            "DELETE FROM [dbo].[AIDirectives] WHERE Timestamp < DATEADD(hour, -72, GETUTCDATE()) AND DoNotRecycleMe = 0");
                        context.Database.ExecuteSqlCommand(
                            "DELETE FROM [dbo].[CovenantLogs] WHERE Timestamp < DATEADD(hour, -72, GETUTCDATE())");
                        context.Database.ExecuteSqlCommand(
                            "DELETE FROM [dbo].[RPClassifiedAds] WHERE RefreshTimestamp < DATEADD(hour, -72, GETUTCDATE())");
                        context.Database.ExecuteSqlCommand(
                            "DELETE FROM [dbo].[TFEnergies] WHERE Timestamp < DATEADD(hour, -72, GETUTCDATE())");
                        context.Database.ExecuteSqlCommand(
                            "DELETE FROM [dbo].[SelfRestoreEnergies] WHERE Timestamp < DATEADD(hour, -4, GETUTCDATE())");

                        // move soulbound items on the ground to the soulbinding NPC.
                        context.Database.ExecuteSqlCommand(
                            $"UPDATE[dbo].[Items] SET OwnerId = {soulbinderId}, dbLocationName = '' WHERE dbLocationName <> '' AND SoulboundToPlayerId IS NOT NULL;");
                    }
                    log.AddLog(updateTimer.ElapsedMilliseconds + ":  Finished stored procedure maintenance");
                }
                catch (Exception e)
                {
                    log.AddLog(FormatExceptionLog(updateTimer.ElapsedMilliseconds, "ERROR running stored procedure maintenance: ", e));
                    log.Errors++;
                }

                serverLogRepo.SaveServerLog(log);


                #region update joke shop
                log.AddLog(updateTimer.ElapsedMilliseconds + ":  Updating joke shop started.");
                try
                {
                    JokeShopProcedures.EjectOfflineCharacters();

                    if (new Random().Next(20) == 0)
                    {
                        LocationsStatics.MoveJokeShop();
                    }

                    ChallengeProcedures.CheckChallenges();
                }
                catch (Exception e)
                {
                    log.Errors++;
                    log.AddLog(FormatExceptionLog(updateTimer.ElapsedMilliseconds, "Updating joke shop FAILED", e));
                }
                log.AddLog(updateTimer.ElapsedMilliseconds + ":  Updating joke shop completed.");
                serverLogRepo.SaveServerLog(log);
                #endregion update joke shop


                #region regenerate dungeon
                if (turnNo % 30 == 7)
                {
                    log.AddLog(updateTimer.ElapsedMilliseconds + ":  Dungeon generation started.");
                    try
                    {
                        DungeonProcedures.GenerateDungeon();
                    }
                    catch (Exception e)
                    {
                        log.Errors++;
                        log.AddLog(FormatExceptionLog(updateTimer.ElapsedMilliseconds, "Dungeon generation FAILED", e));
                    }
                    log.AddLog(updateTimer.ElapsedMilliseconds + ":  Dungeon generation completed.");
                    serverLogRepo.SaveServerLog(log);
                }
                #endregion

                #region move tomb quest
                // Just to keep some things consistent, following the update pattern of the dungeon regen.
                if (turnNo > 6665 && turnNo % 30 == 7)
                {
                    log.AddLog(updateTimer.ElapsedMilliseconds + ":  Updating tomb location started.");
                    try
                    {
                        // Get the quest stuff to start with.
                        int questId           = 39; //Nephthyma's Calling quest ID.
                        IQuestRepository repo = new EFQuestRepository();
                        var questStart        = repo.QuestStarts.FirstOrDefault(q => q.Id == questId);

                        if (questStart != null)
                        {
                            // Pick a random location.
                            string[] locationList   = { "mansion_mausoleum", "gym_laundry", "street_50e9th", "park_shrine" };
                            Random   locationRandom = new Random();
                            int      locationIndex  = locationRandom.Next(locationList.Length);
                            string   location       = locationList[locationIndex];

                            // Set it to the new location.

                            questStart.Location = location;
                            QuestWriterProcedures.SaveQuestStart(questStart);
                        }
                    }
                    catch (Exception e)
                    {
                        log.Errors++;
                        log.AddLog(FormatExceptionLog(updateTimer.ElapsedMilliseconds, "Updating tomb location FAILED", e));
                    }
                    log.AddLog(updateTimer.ElapsedMilliseconds + ":  Updating tomb location completed.");
                    serverLogRepo.SaveServerLog(log);
                }
                #endregion

                log.AddLog(updateTimer.ElapsedMilliseconds + ":  Started deleting unwanted psycho items/pets on Lindella/Wuffie");
                try
                {
                    DomainRegistry.Repository.Execute(new DeleteUnpurchasedPsychoItems());
                }
                catch (Exception e)
                {
                    log.Errors++;
                    log.AddLog(FormatExceptionLog(updateTimer.ElapsedMilliseconds, "ERROR deleting unwanted psycho items", e));
                }
                log.AddLog(updateTimer.ElapsedMilliseconds + ":  Finished deleting unwanted psycho items/pets on Lindella/Wuffie");
                log.FinishTimestamp = DateTime.UtcNow;
                serverLogRepo.SaveServerLog(log);
            }
        }