コード例 #1
0
ファイル: NPCHandler.cs プロジェクト: osiris123/CypherCore
        void HandleGossipHello(Hello packet)
        {
            Creature unit = GetPlayer().GetNPCIfCanInteractWith(packet.Unit, NPCFlags.Gossip);

            if (unit == null)
            {
                Log.outDebug(LogFilter.Network, "WORLD: HandleGossipHello - {0} not found or you can not interact with him.", packet.Unit.ToString());
                return;
            }

            // set faction visible if needed
            var factionTemplateEntry = CliDB.FactionTemplateStorage.LookupByKey(unit.getFaction());

            if (factionTemplateEntry != null)
            {
                GetPlayer().GetReputationMgr().SetVisible(factionTemplateEntry);
            }

            GetPlayer().RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags.Talk);

            if (unit.IsArmorer() || unit.IsCivilian() || unit.IsQuestGiver() || unit.IsServiceProvider() || unit.IsGuard())
            {
                unit.StopMoving();
            }

            // If spiritguide, no need for gossip menu, just put player into resurrect queue
            if (unit.IsSpiritGuide())
            {
                Battleground bg = GetPlayer().GetBattleground();
                if (bg)
                {
                    bg.AddPlayerToResurrectQueue(unit.GetGUID(), GetPlayer().GetGUID());
                    Global.BattlegroundMgr.SendAreaSpiritHealerQuery(GetPlayer(), bg, unit.GetGUID());
                    return;
                }
            }

            if (!Global.ScriptMgr.OnGossipHello(GetPlayer(), unit))
            {
                GetPlayer().PrepareGossipMenu(unit, unit.GetCreatureTemplate().GossipMenuId, true);
                GetPlayer().SendPreparedGossip(unit);
            }
            unit.GetAI().sGossipHello(GetPlayer());
        }
コード例 #2
0
ファイル: NPCHandler.cs プロジェクト: aufbau1/CypherCore
        void HandleGossipHello(Hello packet)
        {
            Creature unit = GetPlayer().GetNPCIfCanInteractWith(packet.Unit, NPCFlags.Gossip, NPCFlags2.None);

            if (unit == null)
            {
                Log.outDebug(LogFilter.Network, "WORLD: HandleGossipHello - {0} not found or you can not interact with him.", packet.Unit.ToString());
                return;
            }

            // set faction visible if needed
            var factionTemplateEntry = CliDB.FactionTemplateStorage.LookupByKey(unit.GetFaction());

            if (factionTemplateEntry != null)
            {
                GetPlayer().GetReputationMgr().SetVisible(factionTemplateEntry);
            }

            GetPlayer().RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags.Interacting);

            // Stop the npc if moving
            unit.PauseMovement(WorldConfig.GetUIntValue(WorldCfg.CreatureStopForPlayer));
            unit.SetHomePosition(unit.GetPosition());

            // If spiritguide, no need for gossip menu, just put player into resurrect queue
            if (unit.IsSpiritGuide())
            {
                Battleground bg = GetPlayer().GetBattleground();
                if (bg)
                {
                    bg.AddPlayerToResurrectQueue(unit.GetGUID(), GetPlayer().GetGUID());
                    Global.BattlegroundMgr.SendAreaSpiritHealerQuery(GetPlayer(), bg, unit.GetGUID());
                    return;
                }
            }

            _player.PlayerTalkClass.ClearMenus();
            if (!unit.GetAI().GossipHello(_player))
            {
                GetPlayer().PrepareGossipMenu(unit, unit.GetCreatureTemplate().GossipMenuId, true);
                GetPlayer().SendPreparedGossip(unit);
            }
        }
コード例 #3
0
ファイル: ChatHandler.cs プロジェクト: lOlbas/CypherCore
        void HandleTextEmote(CTextEmote packet)
        {
            if (!GetPlayer().IsAlive())
            {
                return;
            }

            if (!CanSpeak())
            {
                string timeStr = Time.secsToTimeString((ulong)(m_muteTime - Time.UnixTime));
                SendNotification(CypherStrings.WaitBeforeSpeaking, timeStr);
                return;
            }

            Global.ScriptMgr.OnPlayerTextEmote(GetPlayer(), (uint)packet.SoundIndex, (uint)packet.EmoteID, packet.Target);

            EmotesTextRecord em = CliDB.EmotesTextStorage.LookupByKey(packet.EmoteID);

            if (em == null)
            {
                return;
            }

            uint emote_anim = em.EmoteId;

            switch ((Emote)emote_anim)
            {
            case Emote.StateSleep:
            case Emote.StateSit:
            case Emote.StateKneel:
            case Emote.OneshotNone:
                break;

            case Emote.StateDance:
            case Emote.StateRead:
                GetPlayer().SetEmoteState((Emote)emote_anim);
                break;

            default:
                // Only allow text-emotes for "dead" entities (feign death included)
                if (GetPlayer().HasUnitState(UnitState.Died))
                {
                    break;
                }
                GetPlayer().HandleEmoteCommand((Emote)emote_anim);
                break;
            }

            STextEmote textEmote = new STextEmote();

            textEmote.SourceGUID        = GetPlayer().GetGUID();
            textEmote.SourceAccountGUID = GetAccountGUID();
            textEmote.TargetGUID        = packet.Target;
            textEmote.EmoteID           = packet.EmoteID;
            textEmote.SoundIndex        = packet.SoundIndex;
            GetPlayer().SendMessageToSetInRange(textEmote, WorldConfig.GetFloatValue(WorldCfg.ListenRangeTextemote), true);

            Unit unit = Global.ObjAccessor.GetUnit(GetPlayer(), packet.Target);

            GetPlayer().UpdateCriteria(CriteriaTypes.DoEmote, (uint)packet.EmoteID, 0, 0, unit);

            // Send scripted event call
            if (unit)
            {
                Creature creature = unit.ToCreature();
                if (creature)
                {
                    creature.GetAI().ReceiveEmote(GetPlayer(), (TextEmotes)packet.EmoteID);
                }
            }
        }
コード例 #4
0
ファイル: NPCHandler.cs プロジェクト: osiris123/CypherCore
        void HandleGossipSelectOption(GossipSelectOption packet)
        {
            if (GetPlayer().PlayerTalkClass.GetGossipMenu().GetItem(packet.GossipIndex) == null)
            {
                return;
            }

            // Prevent cheating on C# scripted menus
            if (GetPlayer().PlayerTalkClass.GetInteractionData().SourceGuid != packet.GossipUnit)
            {
                return;
            }

            Creature   unit = null;
            GameObject go   = null;

            if (packet.GossipUnit.IsCreatureOrVehicle())
            {
                unit = GetPlayer().GetNPCIfCanInteractWith(packet.GossipUnit, NPCFlags.Gossip);
                if (unit == null)
                {
                    Log.outDebug(LogFilter.Network, "WORLD: HandleGossipSelectOption - {0} not found or you can't interact with him.", packet.GossipUnit.ToString());
                    return;
                }
            }
            else if (packet.GossipUnit.IsGameObject())
            {
                go = GetPlayer().GetGameObjectIfCanInteractWith(packet.GossipUnit);
                if (go == null)
                {
                    Log.outDebug(LogFilter.Network, "WORLD: HandleGossipSelectOption - {0} not found or you can't interact with it.", packet.GossipUnit.ToString());
                    return;
                }
            }
            else
            {
                Log.outDebug(LogFilter.Network, "WORLD: HandleGossipSelectOption - unsupported {0}.", packet.GossipUnit.ToString());
                return;
            }

            // remove fake death
            if (GetPlayer().HasUnitState(UnitState.Died))
            {
                GetPlayer().RemoveAurasByType(AuraType.FeignDeath);
            }

            if ((unit && unit.GetScriptId() != unit.LastUsedScriptID) || (go != null && go.GetScriptId() != go.LastUsedScriptID))
            {
                Log.outDebug(LogFilter.Network, "WORLD: HandleGossipSelectOption - Script reloaded while in use, ignoring and set new scipt id");
                if (unit != null)
                {
                    unit.LastUsedScriptID = unit.GetScriptId();
                }

                if (go != null)
                {
                    go.LastUsedScriptID = go.GetScriptId();
                }
                GetPlayer().PlayerTalkClass.SendCloseGossip();
                return;
            }
            if (!string.IsNullOrEmpty(packet.PromotionCode))
            {
                if (unit)
                {
                    unit.GetAI().sGossipSelectCode(GetPlayer(), packet.GossipID, packet.GossipIndex, packet.PromotionCode);
                    if (!Global.ScriptMgr.OnGossipSelectCode(GetPlayer(), unit, GetPlayer().PlayerTalkClass.GetGossipOptionSender(packet.GossipIndex), GetPlayer().PlayerTalkClass.GetGossipOptionAction(packet.GossipIndex), packet.PromotionCode))
                    {
                        GetPlayer().OnGossipSelect(unit, packet.GossipIndex, packet.GossipID);
                    }
                }
                else
                {
                    go.GetAI().GossipSelectCode(GetPlayer(), packet.GossipID, packet.GossipIndex, packet.PromotionCode);
                    Global.ScriptMgr.OnGossipSelectCode(GetPlayer(), go, GetPlayer().PlayerTalkClass.GetGossipOptionSender(packet.GossipIndex), GetPlayer().PlayerTalkClass.GetGossipOptionAction(packet.GossipIndex), packet.PromotionCode);
                }
            }
            else
            {
                if (unit != null)
                {
                    unit.GetAI().sGossipSelect(GetPlayer(), packet.GossipID, packet.GossipIndex);
                    if (!Global.ScriptMgr.OnGossipSelect(GetPlayer(), unit, GetPlayer().PlayerTalkClass.GetGossipOptionSender(packet.GossipIndex), GetPlayer().PlayerTalkClass.GetGossipOptionAction(packet.GossipIndex)))
                    {
                        GetPlayer().OnGossipSelect(unit, packet.GossipIndex, packet.GossipID);
                    }
                }
                else
                {
                    go.GetAI().GossipSelect(GetPlayer(), packet.GossipID, packet.GossipIndex);
                    if (!Global.ScriptMgr.OnGossipSelect(GetPlayer(), go, GetPlayer().PlayerTalkClass.GetGossipOptionSender(packet.GossipIndex), GetPlayer().PlayerTalkClass.GetGossipOptionAction(packet.GossipIndex)))
                    {
                        GetPlayer().OnGossipSelect(go, packet.GossipIndex, packet.GossipID);
                    }
                }
            }
        }
コード例 #5
0
        void HandleQuestgiverChooseReward(QuestGiverChooseReward packet)
        {
            Quest quest = Global.ObjectMgr.GetQuestTemplate(packet.QuestID);

            if (quest == null)
            {
                return;
            }

            // This is Real Item Entry, not slot id as pre 5.x
            if (packet.ItemChoiceID != 0)
            {
                ItemTemplate rewardProto = Global.ObjectMgr.GetItemTemplate(packet.ItemChoiceID);
                if (rewardProto == null)
                {
                    Log.outError(LogFilter.Network, "Error in CMSG_QUESTGIVER_CHOOSE_REWARD: player {0} ({1}) tried to get invalid reward item (Item Entry: {2}) for quest {3} (possible packet-hacking detected)", GetPlayer().GetName(), GetPlayer().GetGUID().ToString(), packet.ItemChoiceID, packet.QuestID);
                    return;
                }

                bool itemValid = false;
                for (uint i = 0; i < quest.GetRewChoiceItemsCount(); ++i)
                {
                    if (quest.RewardChoiceItemId[i] != 0 && quest.RewardChoiceItemId[i] == packet.ItemChoiceID)
                    {
                        itemValid = true;
                        break;
                    }
                }

                if (!itemValid && quest.PackageID != 0)
                {
                    var questPackageItems = Global.DB2Mgr.GetQuestPackageItems(quest.PackageID);
                    if (questPackageItems != null)
                    {
                        foreach (var questPackageItem in questPackageItems)
                        {
                            if (questPackageItem.ItemID != packet.ItemChoiceID)
                            {
                                continue;
                            }

                            if (_player.CanSelectQuestPackageItem(questPackageItem))
                            {
                                itemValid = true;
                                break;
                            }
                        }
                    }

                    if (!itemValid)
                    {
                        var questPackageItems1 = Global.DB2Mgr.GetQuestPackageItemsFallback(quest.PackageID);
                        if (questPackageItems1 != null)
                        {
                            foreach (var questPackageItem in questPackageItems1)
                            {
                                if (questPackageItem.ItemID != packet.ItemChoiceID)
                                {
                                    continue;
                                }

                                itemValid = true;
                                break;
                            }
                        }
                    }
                }

                if (!itemValid)
                {
                    Log.outError(LogFilter.Network, "Error in CMSG_QUESTGIVER_CHOOSE_REWARD: player {0} ({1}) tried to get reward item (Item Entry: {2}) wich is not a reward for quest {3} (possible packet-hacking detected)", GetPlayer().GetName(), GetPlayer().GetGUID().ToString(), packet.ItemChoiceID, packet.QuestID);
                    return;
                }
            }

            WorldObject obj = GetPlayer();

            if (!quest.HasFlag(QuestFlags.AutoComplete))
            {
                obj = Global.ObjAccessor.GetObjectByTypeMask(GetPlayer(), packet.QuestGiverGUID, TypeMask.Unit | TypeMask.GameObject);
                if (!obj || !obj.HasInvolvedQuest(packet.QuestID))
                {
                    return;
                }

                // some kind of WPE protection
                if (!GetPlayer().CanInteractWithQuestGiver(obj))
                {
                    return;
                }
            }

            if ((!GetPlayer().CanSeeStartQuest(quest) && GetPlayer().GetQuestStatus(packet.QuestID) == QuestStatus.None) ||
                (GetPlayer().GetQuestStatus(packet.QuestID) != QuestStatus.Complete && !quest.IsAutoComplete()))
            {
                Log.outError(LogFilter.Network, "Error in QuestStatus.Complete: player {0} ({1}) tried to complete quest {2}, but is not allowed to do so (possible packet-hacking or high latency)",
                             GetPlayer().GetName(), GetPlayer().GetGUID().ToString(), packet.QuestID);
                return;
            }

            if (GetPlayer().CanRewardQuest(quest, packet.ItemChoiceID, true))
            {
                GetPlayer().RewardQuest(quest, packet.ItemChoiceID, obj);

                switch (obj.GetTypeId())
                {
                case TypeId.Unit:
                case TypeId.Player:
                {
                    //For AutoSubmition was added plr case there as it almost same exclute AI script cases.
                    Creature creatureQGiver = obj.ToCreature();
                    if (!creatureQGiver || !Global.ScriptMgr.OnQuestReward(GetPlayer(), creatureQGiver, quest, packet.ItemChoiceID))
                    {
                        // Send next quest
                        Quest nextQuest = GetPlayer().GetNextQuest(packet.QuestGiverGUID, quest);
                        if (nextQuest != null)
                        {
                            // Only send the quest to the player if the conditions are met
                            if (GetPlayer().CanTakeQuest(nextQuest, false))
                            {
                                if (nextQuest.IsAutoAccept() && GetPlayer().CanAddQuest(nextQuest, true))
                                {
                                    GetPlayer().AddQuestAndCheckCompletion(nextQuest, obj);
                                }

                                GetPlayer().PlayerTalkClass.SendQuestGiverQuestDetails(nextQuest, packet.QuestGiverGUID, true, false);
                            }
                        }

                        if (creatureQGiver)
                        {
                            creatureQGiver.GetAI().QuestReward(GetPlayer(), quest, packet.ItemChoiceID);
                        }
                    }
                    break;
                }

                case TypeId.GameObject:
                    GameObject questGiver = obj.ToGameObject();
                    if (!Global.ScriptMgr.OnQuestReward(GetPlayer(), questGiver, quest, packet.ItemChoiceID))
                    {
                        // Send next quest
                        Quest nextQuest = GetPlayer().GetNextQuest(packet.QuestGiverGUID, quest);
                        if (nextQuest != null)
                        {
                            // Only send the quest to the player if the conditions are met
                            if (GetPlayer().CanTakeQuest(nextQuest, false))
                            {
                                if (nextQuest.IsAutoAccept() && GetPlayer().CanAddQuest(nextQuest, true))
                                {
                                    GetPlayer().AddQuestAndCheckCompletion(nextQuest, obj);
                                }

                                GetPlayer().PlayerTalkClass.SendQuestGiverQuestDetails(nextQuest, packet.QuestGiverGUID, true, false);
                            }
                        }

                        questGiver.GetAI().QuestReward(GetPlayer(), quest, packet.ItemChoiceID);
                    }
                    break;

                default:
                    break;
                }
            }
            else
            {
                GetPlayer().PlayerTalkClass.SendQuestGiverOfferReward(quest, packet.QuestGiverGUID, true);
            }
        }
コード例 #6
0
ファイル: QuestHandler.cs プロジェクト: lyosky/CypherCore
        void HandleQuestgiverChooseReward(QuestGiverChooseReward packet)
        {
            Quest quest = Global.ObjectMgr.GetQuestTemplate(packet.QuestID);

            if (quest == null)
            {
                return;
            }

            if (packet.Choice.Item.ItemID != 0)
            {
                switch (packet.Choice.LootItemType)
                {
                case LootItemType.Item:
                    ItemTemplate rewardProto = Global.ObjectMgr.GetItemTemplate(packet.Choice.Item.ItemID);
                    if (rewardProto == null)
                    {
                        Log.outError(LogFilter.Network, "Error in CMSG_QUESTGIVER_CHOOSE_REWARD: player {0} ({1}) tried to get invalid reward item (Item Entry: {2}) for quest {3} (possible packet-hacking detected)", GetPlayer().GetName(), GetPlayer().GetGUID().ToString(), packet.Choice.Item.ItemID, packet.QuestID);
                        return;
                    }

                    bool itemValid = false;
                    for (uint i = 0; i < quest.GetRewChoiceItemsCount(); ++i)
                    {
                        if (quest.RewardChoiceItemId[i] != 0 && quest.RewardChoiceItemType[i] == LootItemType.Item && quest.RewardChoiceItemId[i] == packet.Choice.Item.ItemID)
                        {
                            itemValid = true;
                            break;
                        }
                    }

                    if (!itemValid && quest.PackageID != 0)
                    {
                        var questPackageItems = Global.DB2Mgr.GetQuestPackageItems(quest.PackageID);
                        if (questPackageItems != null)
                        {
                            foreach (var questPackageItem in questPackageItems)
                            {
                                if (questPackageItem.ItemID != packet.Choice.Item.ItemID)
                                {
                                    continue;
                                }

                                if (_player.CanSelectQuestPackageItem(questPackageItem))
                                {
                                    itemValid = true;
                                    break;
                                }
                            }
                        }

                        if (!itemValid)
                        {
                            var questPackageItems1 = Global.DB2Mgr.GetQuestPackageItemsFallback(quest.PackageID);
                            if (questPackageItems1 != null)
                            {
                                foreach (var questPackageItem in questPackageItems1)
                                {
                                    if (questPackageItem.ItemID != packet.Choice.Item.ItemID)
                                    {
                                        continue;
                                    }

                                    itemValid = true;
                                    break;
                                }
                            }
                        }
                    }

                    if (!itemValid)
                    {
                        Log.outError(LogFilter.Network, "Error in CMSG_QUESTGIVER_CHOOSE_REWARD: player {0} ({1}) tried to get reward item (Item Entry: {2}) wich is not a reward for quest {3} (possible packet-hacking detected)", GetPlayer().GetName(), GetPlayer().GetGUID().ToString(), packet.Choice.Item.ItemID, packet.QuestID);
                        return;
                    }
                    break;

                case LootItemType.Currency:
                    if (!CliDB.CurrencyTypesStorage.HasRecord(packet.Choice.Item.ItemID))
                    {
                        Log.outError(LogFilter.Player, $"Error in CMSG_QUESTGIVER_CHOOSE_REWARD: player {_player.GetName()} ({_player.GetGUID()}) tried to get invalid reward currency (Currency ID: {packet.Choice.Item.ItemID}) for quest {packet.QuestID} (possible packet-hacking detected)");
                        return;
                    }

                    bool currencyValid = false;
                    for (uint i = 0; i < quest.GetRewChoiceItemsCount(); ++i)
                    {
                        if (quest.RewardChoiceItemId[i] != 0 && quest.RewardChoiceItemType[i] == LootItemType.Currency && quest.RewardChoiceItemId[i] == packet.Choice.Item.ItemID)
                        {
                            currencyValid = true;
                            break;
                        }
                    }
                    if (!currencyValid)
                    {
                        Log.outError(LogFilter.Player, $"Error in CMSG_QUESTGIVER_CHOOSE_REWARD: player {_player.GetName()} ({_player.GetGUID()}) tried to get reward currency (Currency ID: {packet.Choice.Item.ItemID}) wich is not a reward for quest {packet.QuestID} (possible packet-hacking detected)");
                        return;
                    }
                    break;
                }
            }

            WorldObject obj = GetPlayer();

            if (!quest.HasFlag(QuestFlags.AutoComplete))
            {
                obj = Global.ObjAccessor.GetObjectByTypeMask(GetPlayer(), packet.QuestGiverGUID, TypeMask.Unit | TypeMask.GameObject);
                if (!obj || !obj.HasInvolvedQuest(packet.QuestID))
                {
                    return;
                }

                // some kind of WPE protection
                if (!GetPlayer().CanInteractWithQuestGiver(obj))
                {
                    return;
                }
            }

            if ((!GetPlayer().CanSeeStartQuest(quest) && GetPlayer().GetQuestStatus(packet.QuestID) == QuestStatus.None) ||
                (GetPlayer().GetQuestStatus(packet.QuestID) != QuestStatus.Complete && !quest.IsAutoComplete()))
            {
                Log.outError(LogFilter.Network, "Error in QuestStatus.Complete: player {0} ({1}) tried to complete quest {2}, but is not allowed to do so (possible packet-hacking or high latency)",
                             GetPlayer().GetName(), GetPlayer().GetGUID().ToString(), packet.QuestID);
                return;
            }

            if (GetPlayer().CanRewardQuest(quest, true))                                                            // First, check if player is allowed to turn the quest in (all objectives completed). If not, we send players to the offer reward screen
            {
                if (GetPlayer().CanRewardQuest(quest, packet.Choice.LootItemType, packet.Choice.Item.ItemID, true)) // Then check if player can receive the reward item (if inventory is not full, if player doesn't have too many unique items, and so on). If not, the client will close the gossip window
                {
                    GetPlayer().RewardQuest(quest, packet.Choice.LootItemType, packet.Choice.Item.ItemID, obj);

                    switch (obj.GetTypeId())
                    {
                    case TypeId.Unit:
                    case TypeId.Player:
                    {
                        //For AutoSubmition was added plr case there as it almost same exclute AI script cases.
                        // Send next quest
                        Quest nextQuest = _player.GetNextQuest(packet.QuestGiverGUID, quest);
                        if (nextQuest != null)
                        {
                            // Only send the quest to the player if the conditions are met
                            if (_player.CanTakeQuest(nextQuest, false))
                            {
                                if (nextQuest.IsAutoAccept() && _player.CanAddQuest(nextQuest, true))
                                {
                                    _player.AddQuestAndCheckCompletion(nextQuest, obj);
                                }

                                _player.PlayerTalkClass.SendQuestGiverQuestDetails(nextQuest, packet.QuestGiverGUID, true, false);
                            }
                        }

                        _player.PlayerTalkClass.ClearMenus();
                        Creature creatureQGiver = obj.ToCreature();
                        if (creatureQGiver != null)
                        {
                            creatureQGiver.GetAI().QuestReward(_player, quest, packet.Choice.LootItemType, packet.Choice.Item.ItemID);
                        }
                        break;
                    }

                    case TypeId.GameObject:
                    {
                        GameObject questGiver = obj.ToGameObject();
                        // Send next quest
                        Quest nextQuest = _player.GetNextQuest(packet.QuestGiverGUID, quest);
                        if (nextQuest != null)
                        {
                            // Only send the quest to the player if the conditions are met
                            if (_player.CanTakeQuest(nextQuest, false))
                            {
                                if (nextQuest.IsAutoAccept() && _player.CanAddQuest(nextQuest, true))
                                {
                                    _player.AddQuestAndCheckCompletion(nextQuest, obj);
                                }

                                _player.PlayerTalkClass.SendQuestGiverQuestDetails(nextQuest, packet.QuestGiverGUID, true, false);
                            }
                        }

                        _player.PlayerTalkClass.ClearMenus();
                        questGiver.GetAI().QuestReward(_player, quest, packet.Choice.LootItemType, packet.Choice.Item.ItemID);
                        break;
                    }

                    default:
                        break;
                    }
                }
            }
            else
            {
                GetPlayer().PlayerTalkClass.SendQuestGiverOfferReward(quest, packet.QuestGiverGUID, true);
            }
        }