Пример #1
0
        private static void EngageMindControl(Player target, Player attacker, DbStaticForm targetForm, LogBox output)
        {
            //Player attacker = playerRepo.Players.FirstOrDefault(p => p.Id == attackerId);
            MindControlProcedures.AddMindControl(attacker, target, targetForm.Id);

            output.LocationLog += $"<br><b>{target.GetFullName()} was partially mind controlled by {attacker.GetFullName()} here.</b>";
            output.AttackerLog += $"<br><b>You have seized the mind of {target.GetFullName()}!  You can now force them into performing certain actions.</b>";
            output.VictimLog   += $"<br><b>You are now being partially mind controlled by {targetForm.FriendlyName}!</b>";

            TFEnergyProcedures.DeleteAllPlayerTFEnergiesOfFormSourceId(target.Id, targetForm.Id);
            // Remove any Self Restore entires.
            RemoveSelfRestore(target);

            // give curse debuff
            if (targetForm.Id == MindControlStatics.MindControl__MovementFormSourceId)
            {
                EffectProcedures.GivePerkToPlayer(MindControlStatics.MindControl__Movement_DebuffEffectSourceId, target);
            }
            else if (targetForm.Id == MindControlStatics.MindControl__StripFormSourceId)
            {
                EffectProcedures.GivePerkToPlayer(MindControlStatics.MindControl__Strip_DebuffEffectSourceId, target);
            }
            else if (targetForm.Id == MindControlStatics.MindControl__MeditateFormSourceId)
            {
                EffectProcedures.GivePerkToPlayer(MindControlStatics.MindControl__Strip_DebuffEffectSourceId, target);
            }
        }
Пример #2
0
        public static (bool, LogBox) AddTFEnergyToPlayer(Player victim, Player attacker, SkillViewModel skill, decimal modifier, decimal victimHealthBefore)
        {
            var output = new LogBox
            {
                AttackerLog = "  ",
                VictimLog   = "  "
            };

            var victimMaxHealth = victim.MaxHealth;  // Remember this before any form change can affect it

            // Move old energy to pool, add pool energy to the current attack
            (var energyBefore, var energyAfter) = AccumulateTFEnergy(victim, attacker, skill, modifier);

            var eventualForm = FormStatics.GetForm(skill.StaticSkill.FormSourceId.Value);

            var energyAmount = $"  [{energyAfter:0.00} / {eventualForm.TFEnergyRequired:0.00} TF energy]<br />";

            output.AttackerLog += energyAmount;
            output.VictimLog   += energyAmount;

            // Return early if victim is already in the target form
            if (victim.FormSourceId == eventualForm.Id && victim.Mobility == eventualForm.MobilityType)
            {
                var noTransformMessage = $"Since {victim.GetFullName()} is already in this form, the spell has no transforming effect.";
                output.AttackerLog += noTransformMessage;
                output.VictimLog   += noTransformMessage;
                return(false, output);
            }

            // Perform any form change logic (inc extra health damage)
            var formChangeLog = new LogBox();

            var(complete, victimHealthAfter) = TFEnergyProcedures.RunFormChangeLogic(victim, attacker.Id, skill, energyAfter, formChangeLog);

            // Add the spell text to the player logs (now that we know the full damage of this hit)
            var percentTransformedBefore = CalculateTransformationProgress(victimHealthBefore, victimMaxHealth, energyBefore, eventualForm);
            var percentTransformedAfter  = CalculateTransformationProgress(victimHealthAfter, victimMaxHealth, energyAfter, eventualForm);

            LogTransformationMessages(victim, attacker, eventualForm, percentTransformedBefore, percentTransformedAfter, output);

            // Append energy overflow damage and form change output from earlier
            output.Add(formChangeLog);

            // Only give XP if attacker and victim are not part of the same covenant
            if (attacker.Covenant == null || attacker.Covenant != victim.Covenant)
            {
                AwardXP(victim, attacker, eventualForm.MobilityType, output);
            }

            return(complete, output);
        }
Пример #3
0
        private static void PerformAnimateTransformation(Player victim, Player attacker, DbStaticForm targetForm, LogBox output)
        {
            var playerRepo = new EFPlayerRepository();
            var dbVictim   = playerRepo.Players.FirstOrDefault(p => p.Id == victim.Id);

            SkillProcedures.UpdateFormSpecificSkillsToPlayer(dbVictim, targetForm.Id);
            DomainRegistry.Repository.Execute(new ChangeForm
            {
                PlayerId     = dbVictim.Id,
                FormSourceId = targetForm.Id
            });

            // wipe out half of the target's mana
            dbVictim.Mana -= dbVictim.MaxMana / 2;
            if (dbVictim.Mana < 0)
            {
                dbVictim.Mana = 0;
            }

            // Remove any Self Restore entires.
            RemoveSelfRestore(dbVictim);

            var targetbuffs = ItemProcedures.GetPlayerBuffs(dbVictim);

            dbVictim = PlayerProcedures.ReadjustMaxes(dbVictim, targetbuffs);


            // take away some of the victim's XP based on the their level
            // target.XP += -2.5M * target.Level;

            playerRepo.SavePlayer(dbVictim);

            output.LocationLog += $"<br><b>{dbVictim.GetFullName()} was completely transformed into a {targetForm.FriendlyName} here.</b>";
            output.AttackerLog += $"<br><b>You fully transformed {dbVictim.GetFullName()} into a {targetForm.FriendlyName}</b>!";
            output.VictimLog   += $"<br><b>You have been fully transformed into a {targetForm.FriendlyName}!</b>";

            // Let the target know they are best friends with the angel plush.
            if (attacker.BotId == AIStatics.MinibossPlushAngelId)
            {
                output.VictimLog += $"<br><br><b>{attacker.GetFullName()}</b> was happy to make you into a new friend!<br>";
            }

            TFEnergyProcedures.DeleteAllPlayerTFEnergiesOfFormSourceId(dbVictim.Id, targetForm.Id);

            StatsProcedures.AddStat(dbVictim.MembershipId, StatsProcedures.Stat__TimesAnimateTFed, 1);
            StatsProcedures.AddStat(attacker.MembershipId, StatsProcedures.Stat__TimesAnimateTFing, 1);
        }
Пример #4
0
        public static (bool, string) Attack(Player attackingPlayer, Player attackedPlayer, SkillViewModel skillBeingUsed, bool timestamp = true)
        {
            var result = "";

            var attacker = PlayerProcedures.GetPlayer(attackingPlayer.Id);
            var victim   = PlayerProcedures.GetPlayer(attackedPlayer.Id);

            if (victim.Mobility != PvPStatics.MobilityFull ||
                attacker.Mobility != PvPStatics.MobilityFull ||
                victim.GameMode == (int)GameModeStatics.GameModes.Invisible ||
                attacker.GameMode == (int)GameModeStatics.GameModes.Invisible)
            {
                return(false, "");
            }

            var complete = false;
            var logs     = new LogBox();

            // all of our checks seem to be okay.  So let's lower the player's mana and action points
            PlayerProcedures.ChangePlayerActionMana(-PvPStatics.AttackCost, 0, -PvPStatics.AttackManaCost, attacker.Id, timestamp);

            PlayerProcedures.LogCombatTimestampsAndAddAttackCount(victim, attacker);

            var attackerFullName = attacker.GetFullName();
            var victimFullName   = victim.GetFullName();

            // if the spell is a curse, give the effect and that's all
            if (skillBeingUsed.StaticSkill.GivesEffectSourceId != null)
            {
                var effectBeingGiven = EffectStatics.GetDbStaticEffect(skillBeingUsed.StaticSkill.GivesEffectSourceId.Value);

                EffectProcedures.GivePerkToPlayer(skillBeingUsed.StaticSkill.GivesEffectSourceId.Value, victim);

                if (attacker.Gender == PvPStatics.GenderMale && !effectBeingGiven.AttackerWhenHit_M.IsNullOrEmpty())
                {
                    logs.AttackerLog += effectBeingGiven.AttackerWhenHit_M;
                }
                else if (attacker.Gender == PvPStatics.GenderFemale && !effectBeingGiven.AttackerWhenHit_F.IsNullOrEmpty())
                {
                    logs.AttackerLog += effectBeingGiven.AttackerWhenHit_F;
                }
                else
                {
                    logs.AttackerLog += effectBeingGiven.AttackerWhenHit;
                }

                if (!String.IsNullOrEmpty(logs.AttackerLog))
                {
                    logs.AttackerLog += "<br><br>";
                }

                logs.LocationLog  = "<span class='playerAttackNotification'>" + attackerFullName + " cursed " + victimFullName + " with " + skillBeingUsed.StaticSkill.FriendlyName + ".</span>";
                logs.AttackerLog += "You cursed " + victimFullName + " with " + skillBeingUsed.StaticSkill.FriendlyName + ".";
                logs.AttackerLog += "  (+1 XP)  ";
                logs.AttackerLog += PlayerProcedures.GiveXP(attacker, 1);
                logs.VictimLog    = effectBeingGiven.MessageWhenHit;
                logs.VictimLog   += "  <span class='playerAttackNotification'>" + attackerFullName + " cursed you with <b>" + skillBeingUsed.StaticSkill.FriendlyName + "</b>.</b></span>  ";
                result            = logs.AttackerLog;
            }

            // the spell is a regular attack
            else
            {
                logs.LocationLog = "<span class='playerAttackNotification'>" + attackerFullName + " cast " + skillBeingUsed.StaticSkill.FriendlyName + " against " + victimFullName + ".</span>";
                logs.AttackerLog = "You cast " + skillBeingUsed.StaticSkill.FriendlyName + " against " + victimFullName + ".  ";
                logs.VictimLog   = "<span class='playerAttackNotification'>" + attackerFullName + " cast " + skillBeingUsed.StaticSkill.FriendlyName + " against you.</span>  ";

                var meBuffs       = ItemProcedures.GetPlayerBuffs(attacker);
                var targetedBuffs = ItemProcedures.GetPlayerBuffs(victim);

                var rand          = new Random(Guid.NewGuid().GetHashCode());
                var basehitChance = rand.NextDouble() * 100;

                var meDmgExtra = meBuffs.SpellExtraHealthDamagePercent();

                var criticalMissPercentChance = PvPStatics.CriticalMissPercentChance - meBuffs.SpellMisfireChanceReduction();

                var criticalPercentChance = meBuffs.ExtraSkillCriticalPercent() + PvPStatics.CriticalHitPercentChance;
                var evasionPercentChance  = targetedBuffs.EvasionPercent() - meBuffs.EvasionNegationPercent();
                var evasionUpgrade        = false;
                var failedAttack          = false;

                // clamp modifiedEvasion at 50% max
                if (evasionPercentChance > 50)
                {
                    evasionPercentChance = 50;
                }

                // critical miss!  damage caster instead
                if (basehitChance < (double)criticalMissPercentChance)
                {
                    // check if there is a health damage aspect to this spell
                    if (skillBeingUsed.StaticSkill.HealthDamageAmount > 0)
                    {
                        var amountToDamage = skillBeingUsed.StaticSkill.HealthDamageAmount *
                                             (1 + meDmgExtra / 100);
                        PlayerProcedures.DamagePlayerHealth(attacker.Id, amountToDamage);
                        logs.AttackerLog += $"Misfire!  Your spell accidentally lowered your own willpower by {amountToDamage:N2}.  ";
                        logs.VictimLog   += $"Misfire!  {GetPronoun_HisHer(attacker.Gender)} spell accidentally lowered {GetPronoun_hisher(attacker.Gender)} own willpower by {amountToDamage:N2}.";
                        result           += logs.AttackerLog;
                    }
                    failedAttack = true;
                }
                // spell is evaded
                else if (basehitChance < (double)criticalMissPercentChance + (double)evasionPercentChance)
                {
                    // Check for a crit to upgrade the miss to a hit
                    var criticalHitChance = rand.NextDouble() * 100;
                    if (criticalHitChance < (double)criticalPercentChance)
                    {
                        evasionUpgrade = true;
                    }
                    else
                    {
                        logs.AttackerLog += victimFullName + " managed to leap out of the way of your spell.";
                        logs.VictimLog   += "You managed to leap out of the way " + attackerFullName + "'s spell.";
                        result            = logs.AttackerLog;
                        failedAttack      = true;
                    }
                }

                // not a  miss, so let's deal some damage, possibly
                if (!failedAttack)
                {
                    decimal criticalModifier = 1;

                    if (evasionUpgrade)
                    {
                        logs.AttackerLog += "<b>Piercing hit!</b>  ";
                        logs.VictimLog   += "<b>Piercing hit!</b>  ";
                    }
                    else if (rand.NextDouble() * 100 < (double)criticalPercentChance)
                    {
                        criticalModifier  = 2;
                        logs.AttackerLog += "<b>Critical hit!</b>  ";
                        logs.VictimLog   += "<b>Critical hit!</b>  ";
                    }

                    var initialVictimHealth = victim.Health;

                    // check if there is a health damage aspect to this spell
                    if (skillBeingUsed.StaticSkill.HealthDamageAmount > 0)
                    {
                        var targetProt = targetedBuffs.SpellHealthDamageResistance();

                        // calculator the modifier as extra attack - defense.      15 - 20 = -5 modifier
                        var willpowerDamageModifierFromBonuses = 1 + ((meDmgExtra - targetProt) / 100.0M);

                        // cap the modifier at at 50 % IF the target is a human
                        if (willpowerDamageModifierFromBonuses < .5M)
                        {
                            willpowerDamageModifierFromBonuses = .5M;
                        }

                        // cap the modifier at 200 % IF the target is a human
                        if (willpowerDamageModifierFromBonuses > 2 && victim.BotId == AIStatics.ActivePlayerBotId)
                        {
                            willpowerDamageModifierFromBonuses = 2;
                        }

                        var totalHealthDamage = skillBeingUsed.StaticSkill.HealthDamageAmount * willpowerDamageModifierFromBonuses * criticalModifier;

                        // make sure damage is never in the negatives (which would heal instead)
                        if (totalHealthDamage < 0)
                        {
                            totalHealthDamage = 0;
                        }

                        PlayerProcedures.DamagePlayerHealth(victim.Id, totalHealthDamage);

                        // even though it's been done in the db, change the player health here as well
                        victim.Health -= totalHealthDamage;


                        logs.AttackerLog += $"Your spell lowered {GetPronoun_hisher(victim.Gender)} willpower by {Math.Round(totalHealthDamage, 2)}.  ";
                        logs.VictimLog   += $"{GetPronoun_HisHer(attacker.Gender)} spell lowered your willpower by {Math.Round(totalHealthDamage, 2)}.  ";
                        result           += logs.AttackerLog;
                    }

                    // if this skill has any TF power, add energy and check for form change
                    if (skillBeingUsed.StaticSkill.TFPointsAmount > 0)
                    {
                        var TFEnergyDmg   = meBuffs.SpellExtraTFEnergyPercent();
                        var TFEnergyArmor = targetedBuffs.SpellTFEnergyDamageResistance();

                        // calculator the modifier as extra attack - defense.
                        var tfEnergyDamageModifierFromBonuses = 1 + ((TFEnergyDmg - TFEnergyArmor) / 100.0M);

                        // cap the modifier at at 50 % IF the target is a human
                        if (tfEnergyDamageModifierFromBonuses < .5M)
                        {
                            tfEnergyDamageModifierFromBonuses = .5M;
                        }

                        // cap the modifier at at 200 % IF the target is a human
                        if (tfEnergyDamageModifierFromBonuses > 2 && victim.BotId == AIStatics.ActivePlayerBotId)
                        {
                            tfEnergyDamageModifierFromBonuses = 2;
                        }

                        var totalTFEnergyModifier = criticalModifier * tfEnergyDamageModifierFromBonuses;

                        LogBox tfEnergyResult;
                        (complete, tfEnergyResult) = TFEnergyProcedures.AddTFEnergyToPlayer(victim, attacker, skillBeingUsed, totalTFEnergyModifier, initialVictimHealth);
                        logs.Add(tfEnergyResult);

                        result = logs.AttackerLog;
                    }
                }
            }

            LocationLogProcedures.AddLocationLog(attacker.dbLocationName, logs.LocationLog);
            PlayerLogProcedures.AddPlayerLog(attacker.Id, logs.AttackerLog, false);
            PlayerLogProcedures.AddPlayerLog(victim.Id, logs.VictimLog, true);

            DomainRegistry.AttackNotificationBroker.Notify(victim.Id, logs.VictimLog);

            // if this is a psycho-on-psycho battle, have a chance for the victim bot to switch targets to the attacker bot
            if (attacker.BotId == AIStatics.PsychopathBotId && victim.BotId == AIStatics.PsychopathBotId)
            {
                var rand         = new Random(Guid.NewGuid().GetHashCode());
                var botAggroRoll = rand.NextDouble();
                if (botAggroRoll < .08)
                {
                    AIDirectiveProcedures.SetAIDirective_Attack(victim.Id, attacker.Id);
                }
            }

            return(complete, result);
        }
Пример #5
0
        private static void PerformInanimateTransformation(Player victim, Player attacker, int skillSourceId, DbStaticForm targetForm, LogBox output)
        {
            SkillProcedures.UpdateFormSpecificSkillsToPlayer(victim, targetForm.Id);
            DomainRegistry.Repository.Execute(new ChangeForm
            {
                PlayerId     = victim.Id,
                FormSourceId = targetForm.Id
            });

            if (targetForm.MobilityType == PvPStatics.MobilityInanimate && victim.BotId != AIStatics.MinibossPlushAngelId) //No reward for monsters that hurt an innocent little plush friend. :(
            {
                StatsProcedures.AddStat(victim.MembershipId, StatsProcedures.Stat__TimesInanimateTFed, 1);
                StatsProcedures.AddStat(attacker.MembershipId, StatsProcedures.Stat__TimesInanimateTFing, 1);
            }
            else if (targetForm.MobilityType == PvPStatics.MobilityPet && victim.BotId != AIStatics.MinibossPlushAngelId) //No reward for monsters that hurt an innocent little plush friend. :(
            {
                StatsProcedures.AddStat(victim.MembershipId, StatsProcedures.Stat__TimesAnimalTFed, 1);
                StatsProcedures.AddStat(attacker.MembershipId, StatsProcedures.Stat__TimesAnimalTFing, 1);
            }

            if (targetForm.MobilityType == PvPStatics.MobilityPet || targetForm.MobilityType == PvPStatics.MobilityInanimate)
            {
                if (victim.BotId == AIStatics.PsychopathBotId)
                {
                    StatsProcedures.AddStat(attacker.MembershipId, StatsProcedures.Stat__PsychopathsDefeated, 1);
                }

                if (victim.BotId == AIStatics.ActivePlayerBotId && attacker.GameMode == (int)GameModeStatics.GameModes.PvP && victim.GameMode == (int)GameModeStatics.GameModes.PvP)
                {
                    StatsProcedures.AddStat(attacker.MembershipId, StatsProcedures.Stat__PvPPlayerNumberTakedowns, 1);
                    StatsProcedures.AddStat(attacker.MembershipId, StatsProcedures.Stat__PvPPlayerLevelTakedowns, victim.Level);
                }
            }

            // extra log stuff for turning into item
            var extra = ItemProcedures.PlayerBecomesItem(victim, targetForm, attacker);

            output.AttackerLog += extra.AttackerLog;
            output.VictimLog   += extra.VictimLog;
            output.LocationLog += extra.LocationLog;

            // give some of the victim's money to the attacker, the amount depending on what mode the victim is in
            var moneygain = victim.Money * .35M;

            PlayerProcedures.GiveMoneyToPlayer(attacker, moneygain);
            PlayerProcedures.GiveMoneyToPlayer(victim, -moneygain / 2);

            var levelDifference = attacker.Level - victim.Level;

            // only give the lump sum XP if the victim is not in the same covenant
            if (attacker.Covenant == null || attacker.Covenant != victim.Covenant)
            {
                var xpGain = 100 - (PvPStatics.XP__EndgameTFCompletionLevelBase * levelDifference);

                if (xpGain < 50)
                {
                    xpGain = 50;
                }
                else if (xpGain > 200)
                {
                    xpGain = 200;
                }

                // give the attacker a nice lump sum for having completed the transformation
                output.AttackerLog += $"  <br>For having sealed your opponent into their new form, you gain an extra <b>{xpGain}</b> XP.";
                output.AttackerLog += PlayerProcedures.GiveXP(attacker, xpGain);
            }

            // exclude PvP score for bots
            if (victim.BotId == AIStatics.ActivePlayerBotId)
            {
                var score = PlayerProcedures.GetPvPScoreFromWin(attacker, victim);

                if (score > 0)
                {
                    output.AttackerLog += PlayerProcedures.GivePlayerPvPScore(attacker, victim, score);
                    output.VictimLog   += PlayerProcedures.RemovePlayerPvPScore(victim, attacker, score);

                    StatsProcedures.AddStat(attacker.MembershipId, StatsProcedures.Stat__DungeonPointsStolen, (float)score);
                }
                else
                {
                    output.AttackerLog += $"  {victim.GetFullName()} unfortunately did not have any dungeon points for you to steal for yourself.";
                }
            }

            // Call out a player for being the monster they are when they defeat the plush angel.
            if (victim.BotId == AIStatics.MinibossPlushAngelId)
            {
                output.AttackerLog += "<br><br>Why did you do that to the poor plush? They just wanted to be a friend!<br>";
                output.LocationLog += $"<br><b>{attacker.GetFullName()}</b> went and bullied <b>{victim.GetFullName()}</b>, like some <b>monster</b>. The angelic plush left some flowers to the 'victor', in hope they would forgive it despite doing no wrong.";

                // Give the dummy a bit of madness for being a bully.
                EffectProcedures.GivePerkToPlayer(198, attacker);
            }

            // Heals the victorious player provided that the target was eligible
            if (attacker.BotId == AIStatics.ActivePlayerBotId)
            {
                // Provide no healing if the victim shared a coven with the attacker
                if (attacker.Covenant != null && attacker.Covenant == victim.Covenant)
                {
                    output.AttackerLog += "  <br>There is no glory to be had in this victory, your willpower & mana are not restored.";
                }
                else
                {
                    // Figure out the modifier to be used
                    double modifier = (levelDifference * 5) / 100;
                    // Cap the modifier to prevent too much / too little healing.
                    if (modifier > 0.3)
                    {
                        modifier = 0.3;
                    }
                    if (modifier < -0.55)
                    {
                        modifier = -0.55;
                    }
                    decimal healingPercent = (decimal)(0.6 + modifier);

                    if (victim.BotId != AIStatics.ActivePlayerBotId)
                    {
                        // The victim is not a player, provide half of the healing.
                        healingPercent /= 2;
                    }

                    // Heal the attacker and restore their Mana
                    var healingTotal      = attacker.MaxHealth * healingPercent;
                    var manaRestoredTotal = attacker.MaxMana * healingPercent;
                    PlayerProcedures.ChangePlayerActionMana(0, healingTotal, manaRestoredTotal, attacker.Id, false);

                    // Remove any Self Restore entires.
                    RemoveSelfRestore(victim);

                    output.AttackerLog += $"<br />Invigorated by your victory and fuelled by the scattered essence that was once your foe, you are healed for {healingTotal:#} willpower and {manaRestoredTotal:#} mana.";
                }
            }

            output.AttackerLog += $"  You collect {Math.Round(moneygain, 0)} Arpeyjis your victim dropped during the transformation.";

            // create inanimate XP for the victim
            InanimateXPProcedures.GetStruggleChance(victim, false);

            // if this victim is a bot, clear out some old stuff that is not needed anymore
            if (victim.BotId < AIStatics.ActivePlayerBotId)
            {
                AIDirectiveProcedures.DeleteAIDirectiveByPlayerId(victim.Id);
                PlayerLogProcedures.ClearPlayerLog(victim.Id);
            }

            TFEnergyProcedures.DeleteAllPlayerTFEnergiesOfFormSourceId(victim.Id, targetForm.Id);

            // if the attacker is a psycho, have them change to a new spell and equip whatever they just earned
            if (attacker.BotId == AIStatics.PsychopathBotId)
            {
                SkillProcedures.DeletePlayerSkill(attacker, skillSourceId);

                if (targetForm.MobilityType == PvPStatics.MobilityInanimate || targetForm.MobilityType == PvPStatics.MobilityPet)
                {
                    if (attacker.MembershipId.IsNullOrEmpty())
                    {
                        // give this bot a random replacement inanimate/pet skill
                        var eligibleSkills = SkillStatics.GetLearnablePsychopathSkills().ToList();
                        var rand           = new Random();
                        var skillToLearn   = eligibleSkills.ElementAt(rand.Next(eligibleSkills.Count));
                        SkillProcedures.GiveSkillToPlayer(attacker.Id, skillToLearn.Id);
                    }
                    else
                    {
                        // Bot is being controlled by a player - re-add the original skill so only the ordering of skills changes
                        SkillProcedures.GiveSkillToPlayer(attacker.Id, skillSourceId);
                    }
                }
            }
        }