void WorldSession::HandleWhoOpcode(WorldPacket& recv_data) { DEBUG_LOG("WORLD: Received opcode CMSG_WHO"); // recv_data.hexlike(); uint32 clientcount = 0; uint32 level_min, level_max, racemask, classmask, zones_count, str_count; uint32 zoneids[10]; // 10 is client limit std::string player_name, guild_name; recv_data >> level_min; // maximal player level, default 0 recv_data >> level_max; // minimal player level, default 100 (MAX_LEVEL) recv_data >> player_name; // player name, case sensitive... recv_data >> guild_name; // guild name, case sensitive... recv_data >> racemask; // race mask recv_data >> classmask; // class mask recv_data >> zones_count; // zones count, client limit=10 (2.0.10) if (zones_count > 10) return; // can't be received from real client or broken packet for (uint32 i = 0; i < zones_count; ++i) { uint32 temp; recv_data >> temp; // zone id, 0 if zone is unknown... zoneids[i] = temp; DEBUG_LOG("Zone %u: %u", i, zoneids[i]); } recv_data >> str_count; // user entered strings count, client limit=4 (checked on 2.0.10) if (str_count > 4) return; // can't be received from real client or broken packet DEBUG_LOG("Minlvl %u, maxlvl %u, name %s, guild %s, racemask %u, classmask %u, zones %u, strings %u", level_min, level_max, player_name.c_str(), guild_name.c_str(), racemask, classmask, zones_count, str_count); std::wstring str[4]; // 4 is client limit for (uint32 i = 0; i < str_count; ++i) { std::string temp; recv_data >> temp; // user entered string, it used as universal search pattern(guild+player name)? if (!Utf8toWStr(temp, str[i])) continue; wstrToLower(str[i]); DEBUG_LOG("String %u: %s", i, temp.c_str()); } std::wstring wplayer_name; std::wstring wguild_name; if (!(Utf8toWStr(player_name, wplayer_name) && Utf8toWStr(guild_name, wguild_name))) return; wstrToLower(wplayer_name); wstrToLower(wguild_name); // client send in case not set max level value 100 but mangos support 255 max level, // update it to show GMs with characters after 100 level if (level_max >= MAX_LEVEL) level_max = STRONG_MAX_LEVEL; Team team = _player->GetTeam(); AccountTypes security = GetSecurity(); bool allowTwoSideWhoList = sWorld.getConfig(CONFIG_BOOL_ALLOW_TWO_SIDE_WHO_LIST); AccountTypes gmLevelInWhoList = (AccountTypes)sWorld.getConfig(CONFIG_UINT32_GM_LEVEL_IN_WHO_LIST); WorldPacket data(SMSG_WHO, 50); // guess size data << uint32(clientcount); // clientcount place holder, listed count data << uint32(clientcount); // clientcount place holder, online count // TODO: Guard Player map HashMapHolder<Player>::MapType& m = sObjectAccessor.GetPlayers(); for (HashMapHolder<Player>::MapType::const_iterator itr = m.begin(); itr != m.end(); ++itr) { Player* pl = itr->second; if (security == SEC_PLAYER) { // player can see member of other team only if CONFIG_BOOL_ALLOW_TWO_SIDE_WHO_LIST if (pl->GetTeam() != team && !allowTwoSideWhoList) continue; // player can see MODERATOR, GAME MASTER, ADMINISTRATOR only if CONFIG_GM_IN_WHO_LIST if (pl->GetSession()->GetSecurity() > gmLevelInWhoList) continue; } // do not process players which are not in world if (!pl->IsInWorld()) continue; // check if target is globally visible for player if (!pl->IsVisibleGloballyFor(_player)) continue; // check if target's level is in level range uint32 lvl = pl->getLevel(); if (lvl < level_min || lvl > level_max) continue; // check if class matches classmask uint32 class_ = pl->getClass(); if (!(classmask & (1 << class_))) continue; // check if race matches racemask uint32 race = pl->getRace(); if (!(racemask & (1 << race))) continue; uint32 pzoneid = pl->GetZoneId(); bool z_show = true; for (uint32 i = 0; i < zones_count; ++i) { if (zoneids[i] == pzoneid) { z_show = true; break; } z_show = false; } if (!z_show) continue; std::string pname = pl->GetName(); std::wstring wpname; if (!Utf8toWStr(pname, wpname)) continue; wstrToLower(wpname); if (!(wplayer_name.empty() || wpname.find(wplayer_name) != std::wstring::npos)) continue; std::string gname = sGuildMgr.GetGuildNameById(pl->GetGuildId()); std::wstring wgname; if (!Utf8toWStr(gname, wgname)) continue; wstrToLower(wgname); if (!(wguild_name.empty() || wgname.find(wguild_name) != std::wstring::npos)) continue; std::string aname; if (AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(pzoneid)) aname = areaEntry->area_name[GetSessionDbcLocale()]; bool s_show = true; for (uint32 i = 0; i < str_count; ++i) { if (!str[i].empty()) { if (wgname.find(str[i]) != std::wstring::npos || wpname.find(str[i]) != std::wstring::npos || Utf8FitTo(aname, str[i])) { s_show = true; break; } s_show = false; } } if (!s_show) continue; data << pname; // player name data << gname; // guild name data << uint32(lvl); // player level data << uint32(class_); // player class data << uint32(race); // player race data << uint32(pzoneid); // player zone id // 50 is maximum player count sent to client if ((++clientcount) == 49) break; } uint32 count = m.size(); data.put(0, clientcount); // insert right count, listed count data.put(4, count > 49 ? count : clientcount); // insert right count, online count SendPacket(&data); DEBUG_LOG("WORLD: Send SMSG_WHO Message"); }
bool SmartAIMgr::IsEventValid(SmartScriptHolder &e) { if (e.event.type >= SMART_EVENT_END) { sLog->outErrorDb("SmartAIMgr: EntryOrGuid %d using event(%u) has invalid event type (%u), skipped.", e.entryOrGuid, e.event_id, e.GetEventType()); return false; } // in SMART_SCRIPT_TYPE_TIMED_ACTIONLIST all event types are overriden by core if (e.GetScriptType() != SMART_SCRIPT_TYPE_TIMED_ACTIONLIST && !(SmartAIEventMask[e.event.type][1] & SmartAITypeMask[e.GetScriptType()][1])) { sLog->outErrorDb("SmartAIMgr: EntryOrGuid %d, event type %u can not be used for Script type %u", e.entryOrGuid, e.GetEventType(), e.GetScriptType()); return false; } if (e.action.type >= SMART_ACTION_END) { sLog->outErrorDb("SmartAIMgr: EntryOrGuid %d using event(%u) has invalid action type (%u), skipped.", e.entryOrGuid, e.event_id, e.GetActionType()); return false; } if (e.event.event_phase_mask > SMART_EVENT_PHASE_ALL) { sLog->outErrorDb("SmartAIMgr: EntryOrGuid %d using event(%u) has invalid phase mask (%u), skipped.", e.entryOrGuid, e.event_id, e.event.event_phase_mask); return false; } if (e.GetScriptType() == SMART_SCRIPT_TYPE_TIMED_ACTIONLIST) { e.event.type = SMART_EVENT_UPDATE_OOC;//force default OOC, can change when calling the script! if (!IsMinMaxValid(e, e.event.minMaxRepeat.min, e.event.minMaxRepeat.max)) return false; if (!IsMinMaxValid(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax)) return false; } else { uint32 type = e.event.type; switch (type) { case SMART_EVENT_UPDATE: case SMART_EVENT_UPDATE_IC: case SMART_EVENT_UPDATE_OOC: case SMART_EVENT_HEALT_PCT: case SMART_EVENT_MANA_PCT: case SMART_EVENT_TARGET_HEALTH_PCT: case SMART_EVENT_TARGET_MANA_PCT: case SMART_EVENT_RANGE: case SMART_EVENT_DAMAGED: case SMART_EVENT_DAMAGED_TARGET: case SMART_EVENT_RECEIVE_HEAL: if (!IsMinMaxValid(e, e.event.minMaxRepeat.min, e.event.minMaxRepeat.max)) return false; if (!IsMinMaxValid(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax)) return false; break; case SMART_EVENT_SPELLHIT: case SMART_EVENT_SPELLHIT_TARGET: if (e.event.spellHit.spell) { SpellEntry const* pSpell = sSpellStore.LookupEntry(e.event.spellHit.spell); if (!pSpell) { sLog->outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses non-existent Spell entry %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.event.spellHit.spell); return false; } if (e.event.spellHit.school && (e.event.spellHit.school & pSpell->SchoolMask) != pSpell->SchoolMask) { sLog->outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses Spell entry %u with invalid school mask, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.event.spellHit.spell); return false; } } if (!IsMinMaxValid(e, e.event.spellHit.cooldownMin, e.event.spellHit.cooldownMax)) return false; break; case SMART_EVENT_OOC_LOS: case SMART_EVENT_IC_LOS: if (!IsMinMaxValid(e, e.event.los.cooldownMin, e.event.los.cooldownMax)) return false; break; case SMART_EVENT_RESPAWN: if (e.event.respawn.type == SMART_SCRIPT_RESPAWN_CONDITION_MAP && !sMapStore.LookupEntry(e.event.respawn.map)) { sLog->outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses non-existent Map entry %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.event.respawn.map); return false; } if (e.event.respawn.type == SMART_SCRIPT_RESPAWN_CONDITION_AREA && !GetAreaEntryByAreaID(e.event.respawn.area)) { sLog->outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses non-existent Area entry %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.event.respawn.area); return false; } break; case SMART_EVENT_FRIENDLY_HEALTH: if (!NotNULL(e, e.event.friendlyHealt.radius)) return false; if (!IsMinMaxValid(e, e.event.friendlyHealt.repeatMin, e.event.friendlyHealt.repeatMax)) return false; break; case SMART_EVENT_FRIENDLY_IS_CC: if (!IsMinMaxValid(e, e.event.friendlyCC.repeatMin, e.event.friendlyCC.repeatMax)) return false; break; case SMART_EVENT_FRIENDLY_MISSING_BUFF: { if (!IsSpellValid(e, e.event.missingBuff.spell)) return false; if (!NotNULL(e, e.event.missingBuff.radius)) return false; if (!IsMinMaxValid(e, e.event.missingBuff.repeatMin, e.event.missingBuff.repeatMax)) return false; break; } case SMART_EVENT_KILL: if (!IsMinMaxValid(e, e.event.kill.cooldownMin, e.event.kill.cooldownMax)) return false; if (e.event.kill.creature && !IsCreatureValid(e, e.event.kill.creature)) return false; break; case SMART_EVENT_TARGET_CASTING: case SMART_EVENT_PASSENGER_BOARDED: case SMART_EVENT_PASSENGER_REMOVED: if (!IsMinMaxValid(e, e.event.minMax.repeatMin, e.event.minMax.repeatMax)) return false; break; case SMART_EVENT_SUMMON_DESPAWNED: case SMART_EVENT_SUMMONED_UNIT: if (e.event.summoned.creature && !IsCreatureValid(e, e.event.summoned.creature)) return false; if (!IsMinMaxValid(e, e.event.summoned.cooldownMin, e.event.summoned.cooldownMax)) return false; break; case SMART_EVENT_ACCEPTED_QUEST: case SMART_EVENT_REWARD_QUEST: if (!IsQuestValid(e, e.event.quest.quest)) return false; break; case SMART_EVENT_RECEIVE_EMOTE: { if (e.event.emote.emote && !IsTextEmoteValid(e, e.event.emote.emote)) return false; if (!IsMinMaxValid(e, e.event.emote.cooldownMin, e.event.emote.cooldownMax)) return false; break; } case SMART_EVENT_HAS_AURA: case SMART_EVENT_TARGET_BUFFED: { if (!IsSpellValid(e, e.event.aura.spell)) return false; if (!IsMinMaxValid(e, e.event.aura.repeatMin, e.event.aura.repeatMax)) return false; break; } case SMART_EVENT_TRANSPORT_ADDCREATURE: { if (e.event.transportAddCreature.creature && !IsCreatureValid(e, e.event.transportAddCreature.creature)) return false; break; } case SMART_EVENT_MOVEMENTINFORM: { if (e.event.movementInform.type > NULL_MOTION_TYPE) { sLog->outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses invalid Motion type %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.event.movementInform.type); return false; } break; } case SMART_EVENT_DATA_SET: { if (!IsMinMaxValid(e, e.event.dataSet.cooldownMin, e.event.dataSet.cooldownMax)) return false; break; } case SMART_EVENT_AREATRIGGER_ONTRIGGER: { if (e.event.areatrigger.id && !IsAreaTriggerValid(e, e.event.areatrigger.id)) return false; break; } case SMART_EVENT_TEXT_OVER: //if (e.event.textOver.textGroupID && !IsTextValid(e, e.event.textOver.textGroupID)) return false;// 0 is a valid text group! break; case SMART_EVENT_LINK: { if (e.link && e.link == e.event_id) { sLog->outErrorDb("SmartAIMgr: Entry %d SourceType %u, Event %u, Link Event is linking self (infinite loop), skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id); return false; } break; } case SMART_EVENT_DUMMY_EFFECT: if (!IsSpellValid(e, e.event.dummy.spell)) return false; if (e.event.dummy.effIndex > EFFECT_2) return false; break; case SMART_EVENT_IS_BEHIND_TARGET: if (!IsMinMaxValid(e, e.event.behindTarget.cooldownMin, e.event.behindTarget.cooldownMax)) return false; break; case SMART_EVENT_TIMED_EVENT_TRIGGERED: case SMART_EVENT_INSTANCE_PLAYER_ENTER: case SMART_EVENT_TRANSPORT_RELOCATE: case SMART_EVENT_CHARMED: case SMART_EVENT_CHARMED_TARGET: case SMART_EVENT_CORPSE_REMOVED: case SMART_EVENT_AI_INIT: case SMART_EVENT_TRANSPORT_ADDPLAYER: case SMART_EVENT_TRANSPORT_REMOVE_PLAYER: case SMART_EVENT_AGGRO: case SMART_EVENT_DEATH: case SMART_EVENT_EVADE: case SMART_EVENT_REACHED_HOME: case SMART_EVENT_RESET: case SMART_EVENT_QUEST_ACCEPTED: case SMART_EVENT_QUEST_OBJ_COPLETETION: case SMART_EVENT_QUEST_COMPLETION: case SMART_EVENT_QUEST_REWARDED: case SMART_EVENT_QUEST_FAIL: case SMART_EVENT_JUST_SUMMONED: case SMART_EVENT_WAYPOINT_START: case SMART_EVENT_WAYPOINT_REACHED: case SMART_EVENT_WAYPOINT_PAUSED: case SMART_EVENT_WAYPOINT_RESUMED: case SMART_EVENT_WAYPOINT_STOPPED: case SMART_EVENT_WAYPOINT_ENDED: case SMART_ACTION_PLAYMOVIE: case SMART_EVENT_GOSSIP_SELECT: case SMART_EVENT_GOSSIP_HELLO: case SMART_EVENT_JUST_CREATED: case SMART_EVENT_FOLLOW_COMPLETED: break; default: sLog->outErrorDb("SmartAIMgr: Not handled event_type(%u), Entry %d SourceType %u Event %u Action %u, skipped.", e.GetEventType(), e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType()); return false; } } switch (e.GetActionType()) { case SMART_ACTION_SET_FACTION: if (e.action.faction.factionID && !sFactionStore.LookupEntry(e.action.faction.factionID)) { sLog->outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses non-existent Faction %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.faction.factionID); return false; } break; case SMART_ACTION_MORPH_TO_ENTRY_OR_MODEL: case SMART_ACTION_MOUNT_TO_ENTRY_OR_MODEL: if (e.action.morphOrMount.creature || e.action.morphOrMount.model) { if (e.action.morphOrMount.creature > 0 && !sObjectMgr->GetCreatureTemplate(e.action.morphOrMount.creature)) { sLog->outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses non-existent Creature entry %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.morphOrMount.creature); return false; } if (e.action.morphOrMount.model) { if (e.action.morphOrMount.creature) { sLog->outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u has ModelID set with also set CreatureId, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType()); return false; } else if (!sCreatureDisplayInfoStore.LookupEntry(e.action.morphOrMount.model)) { sLog->outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses non-existent Model id %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.morphOrMount.model); return false; } } } break; case SMART_ACTION_SOUND: if (!IsSoundValid(e, e.action.sound.sound)) return false; if (e.action.sound.range > TEXT_RANGE_WORLD) { sLog->outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses invalid Text Range %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.sound.range); return false; } break; case SMART_ACTION_SET_EMOTE_STATE: case SMART_ACTION_PLAY_EMOTE: if (!IsEmoteValid(e, e.action.emote.emote)) return false; break; case SMART_ACTION_FAIL_QUEST: case SMART_ACTION_ADD_QUEST: if (e.action.quest.quest && !IsQuestValid(e, e.action.quest.quest)) return false; break; case SMART_ACTION_ACTIVATE_TAXI: { if (!sTaxiPathStore.LookupEntry(e.action.taxi.id)) { sLog->outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses invalid Taxi path ID %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.taxi.id); return false; } break; } case SMART_ACTION_RANDOM_EMOTE: if (e.action.randomEmote.emote1 && !IsEmoteValid(e, e.action.randomEmote.emote1)) return false; if (e.action.randomEmote.emote2 && !IsEmoteValid(e, e.action.randomEmote.emote2)) return false; if (e.action.randomEmote.emote3 && !IsEmoteValid(e, e.action.randomEmote.emote3)) return false; if (e.action.randomEmote.emote4 && !IsEmoteValid(e, e.action.randomEmote.emote4)) return false; if (e.action.randomEmote.emote5 && !IsEmoteValid(e, e.action.randomEmote.emote5)) return false; if (e.action.randomEmote.emote6 && !IsEmoteValid(e, e.action.randomEmote.emote6)) return false; break; case SMART_ACTION_ADD_AURA: case SMART_ACTION_CAST: case SMART_ACTION_INVOKER_CAST: if (!IsSpellValid(e, e.action.cast.spell)) return false; break; case SMART_ACTION_CALL_AREAEXPLOREDOREVENTHAPPENS: case SMART_ACTION_CALL_GROUPEVENTHAPPENS: if (Quest const* qid = sObjectMgr->GetQuestTemplate(e.action.quest.quest)) { if (!qid->HasFlag(QUEST_TRINITY_FLAGS_EXPLORATION_OR_EVENT)) { sLog->outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u SpecialFlags for Quest entry %u does not include FLAGS_EXPLORATION_OR_EVENT(2), skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.quest.quest); return false; } } else { sLog->outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses non-existent Quest entry %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.quest.quest); return false; } break; case SMART_ACTION_SEND_CASTCREATUREORGO: if (!IsQuestValid(e, e.action.castCreatureOrGO.quest)) return false; if (!IsSpellValid(e, e.action.castCreatureOrGO.spell)) return false; break; case SMART_ACTION_SET_EVENT_PHASE: if (e.action.setEventPhase.phase >= SMART_EVENT_PHASE_MAX) { sLog->outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u attempts to set phase %u. Phase mask cannot be used past phase %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.setEventPhase.phase, SMART_EVENT_PHASE_MAX-1); return false; } break; case SMART_ACTION_INC_EVENT_PHASE: if (!e.action.incEventPhase.inc && !e.action.incEventPhase.dec) { sLog->outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u is incrementing phase by 0, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType()); return false; } else if (e.action.incEventPhase.inc > SMART_EVENT_PHASE_MAX || e.action.incEventPhase.dec > SMART_EVENT_PHASE_MAX) { sLog->outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u attempts to increment phase by too large value, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType()); return false; } break; case SMART_ACTION_CALL_CASTEDCREATUREORGO: if (!IsCreatureValid(e, e.action.castedCreatureOrGO.creature)) return false; if (!IsSpellValid(e, e.action.castedCreatureOrGO.spell)) return false; break; case SMART_ACTION_REMOVEAURASFROMSPELL: if (!IsSpellValid(e, e.action.removeAura.spell)) return false; break; case SMART_ACTION_RANDOM_PHASE: { if (e.action.randomPhase.phase1 >= SMART_EVENT_PHASE_MAX || e.action.randomPhase.phase2 >= SMART_EVENT_PHASE_MAX || e.action.randomPhase.phase3 >= SMART_EVENT_PHASE_MAX || e.action.randomPhase.phase4 >= SMART_EVENT_PHASE_MAX || e.action.randomPhase.phase5 >= SMART_EVENT_PHASE_MAX || e.action.randomPhase.phase6 >= SMART_EVENT_PHASE_MAX) { sLog->outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u attempts to set invalid phase, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType()); return false; } } break; case SMART_ACTION_RANDOM_PHASE_RANGE: //PhaseMin, PhaseMax { if (e.action.randomPhaseRange.phaseMin >= SMART_EVENT_PHASE_MAX || e.action.randomPhaseRange.phaseMax >= SMART_EVENT_PHASE_MAX) { sLog->outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u attempts to set invalid phase, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType()); return false; } if (!IsMinMaxValid(e, e.action.randomPhaseRange.phaseMin, e.action.randomPhaseRange.phaseMax)) return false; break; } case SMART_ACTION_SUMMON_CREATURE: if (!IsCreatureValid(e, e.action.summonCreature.creature)) return false; if (e.action.summonCreature.type > TEMPSUMMON_MANUAL_DESPAWN) { sLog->outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses incorrect TempSummonType %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.summonCreature.type); return false; } break; case SMART_ACTION_CALL_KILLEDMONSTER: if (!IsCreatureValid(e, e.action.killedMonster.creature)) return false; break; case SMART_ACTION_UPDATE_TEMPLATE: if (e.action.updateTemplate.creature && !IsCreatureValid(e, e.action.updateTemplate.creature)) return false; break; case SMART_ACTION_SET_SHEATH: if (e.action.setSheath.sheath && e.action.setSheath.sheath >= MAX_SHEATH_STATE) { sLog->outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses incorrect Sheath state %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.setSheath.sheath); return false; } break; case SMART_ACTION_SET_REACT_STATE: { if (e.action.react.state > REACT_AGGRESSIVE) { sLog->outErrorDb("SmartAIMgr: Creature %d Event %u Action %u uses invalid React State %u, skipped.", e.entryOrGuid, e.event_id, e.GetActionType(), e.action.react.state); return false; } break; } case SMART_ACTION_SUMMON_GO: if (!IsGameObjectValid(e, e.action.summonGO.entry)) return false; break; case SMART_ACTION_ADD_ITEM: case SMART_ACTION_REMOVE_ITEM: if (!IsItemValid(e, e.action.item.entry)) return false; if (!NotNULL(e, e.action.item.count)) return false; break; case SMART_ACTION_TELEPORT: if (!sMapStore.LookupEntry(e.action.teleport.mapID)) { sLog->outErrorDb("SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses non-existent Map entry %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.teleport.mapID); return false; } break; case SMART_ACTION_INSTALL_AI_TEMPLATE: if (e.action.installTtemplate.id >= SMARTAI_TEMPLATE_END) { sLog->outErrorDb("SmartAIMgr: Creature %d Event %u Action %u uses non-existent AI template id %u, skipped.", e.entryOrGuid, e.event_id, e.GetActionType(), e.action.installTtemplate.id); return false; } break; case SMART_ACTION_WP_STOP: if (e.action.wpStop.quest && !IsQuestValid(e, e.action.wpStop.quest)) return false; break; case SMART_ACTION_WP_START: { if (!sSmartWaypointMgr->GetPath(e.action.wpStart.pathID)) { sLog->outErrorDb("SmartAIMgr: Creature %d Event %u Action %u uses non-existent WaypointPath id %u, skipped.", e.entryOrGuid, e.event_id, e.GetActionType(), e.action.wpStart.pathID); return false; } if (e.action.wpStart.quest && !IsQuestValid(e, e.action.wpStart.quest)) return false; if (e.action.wpStart.reactState > REACT_AGGRESSIVE) { sLog->outErrorDb("SmartAIMgr: Creature %d Event %u Action %u uses invalid React State %u, skipped.", e.entryOrGuid, e.event_id, e.GetActionType(), e.action.wpStart.reactState); return false; } break; } case SMART_ACTION_CREATE_TIMED_EVENT: { if (!IsMinMaxValid(e, e.action.timeEvent.min, e.action.timeEvent.max)) return false; if (!IsMinMaxValid(e, e.action.timeEvent.repeatMin, e.action.timeEvent.repeatMax)) return false; break; } case SMART_ACTION_FOLLOW: case SMART_ACTION_SET_ORIENTATION: case SMART_ACTION_STORE_TARGET_LIST: case SMART_ACTION_EVADE: case SMART_ACTION_FLEE_FOR_ASSIST: case SMART_ACTION_DIE: case SMART_ACTION_SET_IN_COMBAT_WITH_ZONE: case SMART_ACTION_SET_ACTIVE: case SMART_ACTION_STORE_VARIABLE_DECIMAL: case SMART_ACTION_WP_RESUME: case SMART_ACTION_KILL_UNIT: case SMART_ACTION_SET_INVINCIBILITY_HP_LEVEL: case SMART_ACTION_RESET_GOBJECT: case SMART_ACTION_ATTACK_START: case SMART_ACTION_THREAT_ALL_PCT: case SMART_ACTION_THREAT_SINGLE_PCT: case SMART_ACTION_SET_INST_DATA: case SMART_ACTION_SET_INST_DATA64: case SMART_ACTION_AUTO_ATTACK: case SMART_ACTION_ALLOW_COMBAT_MOVEMENT: case SMART_ACTION_CALL_FOR_HELP: case SMART_ACTION_SET_DATA: case SMART_ACTION_MOVE_FORWARD: case SMART_ACTION_SET_VISIBILITY: case SMART_ACTION_WP_PAUSE: case SMART_ACTION_SET_FLY: case SMART_ACTION_SET_RUN: case SMART_ACTION_SET_SWIMM: case SMART_ACTION_FORCE_DESPAWN: case SMART_ACTION_SET_INGAME_PHASE_MASK: case SMART_ACTION_SET_UNIT_FLAG: case SMART_ACTION_REMOVE_UNIT_FLAG: case SMART_ACTION_PLAYMOVIE: case SMART_ACTION_MOVE_TO_POS: case SMART_ACTION_RESPAWN_TARGET: case SMART_ACTION_CLOSE_GOSSIP: case SMART_ACTION_EQUIP: case SMART_ACTION_TRIGGER_TIMED_EVENT: case SMART_ACTION_REMOVE_TIMED_EVENT: case SMART_ACTION_OVERRIDE_SCRIPT_BASE_OBJECT: case SMART_ACTION_RESET_SCRIPT_BASE_OBJECT: case SMART_ACTION_ACTIVATE_GOBJECT: case SMART_ACTION_CALL_SCRIPT_RESET: case SMART_ACTION_ENTER_VEHICLE: case SMART_ACTION_NONE: case SMART_ACTION_CALL_TIMED_ACTIONLIST: case SMART_ACTION_SET_NPC_FLAG: case SMART_ACTION_ADD_NPC_FLAG: case SMART_ACTION_REMOVE_NPC_FLAG: case SMART_ACTION_TALK: case SMART_ACTION_SIMPLE_TALK: case SMART_ACTION_CROSS_CAST: case SMART_ACTION_CALL_RANDOM_TIMED_ACTIONLIST: case SMART_ACTION_CALL_RANDOM_RANGE_TIMED_ACTIONLIST: case SMART_ACTION_RANDOM_MOVE: case SMART_ACTION_SET_UNIT_FIELD_BYTES_1: case SMART_ACTION_REMOVE_UNIT_FIELD_BYTES_1: case SMART_ACTION_INTERRUPT_SPELL: case SMART_ACTION_SEND_GO_CUSTOM_ANIM: break; default: sLog->outErrorDb("SmartAIMgr: Not handled action_type(%u), event_type(%u), Entry %d SourceType %u Event %u, skipped.", e.GetActionType(), e.GetEventType(), e.entryOrGuid, e.GetScriptType(), e.event_id); return false; } return true; }
static bool HandleGroupListCommand(ChatHandler* handler, char const* args) { // Get ALL the variables! Player* playerTarget; uint32 phase = 0; uint64 guidTarget; std::string nameTarget; std::string zoneName; const char* onlineState = ""; // Parse the guid to uint32... uint32 parseGUID = MAKE_NEW_GUID(atol((char*)args), 0, HIGHGUID_PLAYER); // ... and try to extract a player out of it. if (sObjectMgr->GetPlayerNameByGUID(parseGUID, nameTarget)) { playerTarget = sObjectMgr->GetPlayerByLowGUID(parseGUID); guidTarget = parseGUID; } // If not, we return false and end right away. else if (!handler->extractPlayerTarget((char*)args, &playerTarget, &guidTarget, &nameTarget)) return false; // Next, we need a group. So we define a group variable. Group* groupTarget = NULL; // We try to extract a group from an online player. if (playerTarget) groupTarget = playerTarget->GetGroup(); // If not, we extract it from the SQL. if (!groupTarget) { PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_GROUP_MEMBER); stmt->setUInt32(0, guidTarget); PreparedQueryResult resultGroup = CharacterDatabase.Query(stmt); if (resultGroup) groupTarget = sGroupMgr->GetGroupByDbStoreId((*resultGroup)[0].GetUInt32()); } // If both fails, players simply has no party. Return false. if (!groupTarget) { handler->PSendSysMessage(LANG_GROUP_NOT_IN_GROUP, nameTarget.c_str()); handler->SetSentErrorMessage(true); return false; } // We get the group members after successfully detecting a group. Group::MemberSlotList const& members = groupTarget->GetMemberSlots(); // To avoid a cluster f**k, namely trying multiple queries to simply get a group member count... handler->PSendSysMessage(LANG_GROUP_TYPE, (groupTarget->isRaidGroup() ? "raid" : "party"), members.size()); // ... we simply move the group type and member count print after retrieving the slots and simply output it's size. // While rather dirty codestyle-wise, it saves space (if only a little). For each member, we look several informations up. for (Group::MemberSlotList::const_iterator itr = members.begin(); itr != members.end(); ++itr) { // Define temporary variable slot to iterator. Group::MemberSlot const& slot = *itr; // Check for given flag and assign it to that iterator std::string flags; if (slot.flags & MEMBER_FLAG_ASSISTANT) flags = "Assistant"; if (slot.flags & MEMBER_FLAG_MAINTANK) { if (!flags.empty()) flags.append(", "); flags.append("MainTank"); } if (slot.flags & MEMBER_FLAG_MAINASSIST) { if (!flags.empty()) flags.append(", "); flags.append("MainAssist"); } if (flags.empty()) flags = "None"; // Check if iterator is online. If is... Player* p = ObjectAccessor::FindPlayer((*itr).guid); if (p && p->IsInWorld()) { // ... than, it prints information like "is online", where he is, etc... onlineState = "online"; phase = (!p->IsGameMaster() ? p->GetPhaseMask() : -1); uint32 locale = handler->GetSessionDbcLocale(); AreaTableEntry const* area = GetAreaEntryByAreaID(p->GetAreaId()); if (area) { AreaTableEntry const* zone = GetAreaEntryByAreaID(area->zone); if (zone) zoneName = zone->area_name[locale]; } } else { // ... else, everything is set to offline or neutral values. zoneName = "<ERROR>"; onlineState = "Offline"; phase = 0; } // Now we can print those informations for every single member of each group! handler->PSendSysMessage(LANG_GROUP_PLAYER_NAME_GUID, slot.name.c_str(), onlineState, zoneName.c_str(), phase, GUID_LOPART(slot.guid), flags.c_str(), lfg::GetRolesString(slot.roles).c_str()); } // And finish after every iterator is done. return true; }
//show info of player bool ChatHandler::HandlePInfoCommand(const char* args) { Player* target; uint64 target_guid; std::string target_name; uint32 parseGUID = MAKE_NEW_GUID(atol((char*)args), 0, HIGHGUID_PLAYER); if (sObjectMgr->GetPlayerNameByGUID(parseGUID, target_name)) { target = sObjectMgr->GetPlayerByLowGUID(parseGUID); target_guid = parseGUID; } else if (!extractPlayerTarget((char*)args, &target, &target_guid, &target_name)) return false; uint32 accId = 0; uint32 money = 0; uint32 total_player_time = 0; uint8 level = 0; uint32 latency = 0; uint8 race; uint8 Class; int64 muteTime = 0; int64 banTime = -1; uint32 mapId; uint32 areaId; uint32 phase = 0; // get additional information from Player object if (target) { // check online security if (HasLowerSecurity(target, 0)) return false; accId = target->GetSession()->GetAccountId(); money = target->GetMoney(); total_player_time = target->GetTotalPlayedTime(); level = target->getLevel(); latency = target->GetSession()->GetLatency(); race = target->getRace(); Class = target->getClass(); muteTime = target->GetSession()->m_muteTime; mapId = target->GetMapId(); areaId = target->GetAreaId(); phase = target->GetPhaseMask(); } // get additional information from DB else { // check offline security if (HasLowerSecurity(NULL, target_guid)) return false; // 0 1 2 3 4 5 6 7 QueryResult result = CharacterDatabase.PQuery("SELECT totaltime, level, money, account, race, class, map, zone FROM characters " "WHERE guid = '%u'", GUID_LOPART(target_guid)); if (!result) return false; Field* fields = result->Fetch(); total_player_time = fields[0].GetUInt32(); level = fields[1].GetUInt32(); money = fields[2].GetUInt32(); accId = fields[3].GetUInt32(); race = fields[4].GetUInt8(); Class = fields[5].GetUInt8(); mapId = fields[6].GetUInt16(); areaId = fields[7].GetUInt16(); } std::string username = GetSkyfireString(LANG_ERROR); std::string email = GetSkyfireString(LANG_ERROR); std::string last_ip = GetSkyfireString(LANG_ERROR); uint32 security = 0; std::string last_login = GetSkyfireString(LANG_ERROR); QueryResult result = LoginDatabase.PQuery("SELECT a.username, aa.gmlevel, a.email, a.last_ip, a.last_login, a.mutetime " "FROM account a " "LEFT JOIN account_access aa " "ON (a.id = aa.id AND (aa.RealmID = -1 OR aa.RealmID = %u)) " "WHERE a.id = '%u'", realmID, accId); if (result) { Field* fields = result->Fetch(); username = fields[0].GetString(); security = fields[1].GetUInt32(); email = fields[2].GetString(); muteTime = fields[5].GetUInt64(); if (email.empty()) email = "-"; if (!m_session || m_session->GetSecurity() >= AccountTypes(security)) { last_ip = fields[3].GetString(); last_login = fields[4].GetString(); } else { last_ip = "-"; last_login = "******"; } } std::string nameLink = playerLink(target_name); PSendSysMessage(LANG_PINFO_ACCOUNT, (target?"":GetSkyfireString(LANG_OFFLINE)), nameLink.c_str(), GUID_LOPART(target_guid), username.c_str(), accId, email.c_str(), security, last_ip.c_str(), last_login.c_str(), latency); std::string bannedby = "unknown"; std::string banreason = ""; if (QueryResult result2 = LoginDatabase.PQuery("SELECT unbandate, bandate = unbandate, bannedby, banreason FROM account_banned " "WHERE id = '%u' AND active ORDER BY bandate ASC LIMIT 1", accId)) { Field* fields = result2->Fetch(); banTime = fields[1].GetBool() ? 0 : fields[0].GetUInt64(); bannedby = fields[2].GetString(); banreason = fields[3].GetString(); } else if (QueryResult result3 = CharacterDatabase.PQuery("SELECT unbandate, bandate = unbandate, bannedby, banreason FROM character_banned " "WHERE guid = '%u' AND active ORDER BY bandate ASC LIMIT 1", GUID_LOPART(target_guid))) { Field* fields = result3->Fetch(); banTime = fields[1].GetBool() ? 0 : fields[0].GetUInt64(); bannedby = fields[2].GetString(); banreason = fields[3].GetString(); } if (muteTime > 0) PSendSysMessage(LANG_PINFO_MUTE, secsToTimeString(muteTime - time(NULL), true).c_str()); if (banTime >= 0) PSendSysMessage(LANG_PINFO_BAN, banTime > 0 ? secsToTimeString(banTime - time(NULL), true).c_str() : "permanently", bannedby.c_str(), banreason.c_str()); std::string race_s, Class_s; switch (race) { case RACE_HUMAN: race_s = "Human"; break; case RACE_ORC: race_s = "Orc"; break; case RACE_DWARF: race_s = "Dwarf"; break; case RACE_NIGHTELF: race_s = "Night Elf"; break; case RACE_UNDEAD_PLAYER: race_s = "Undead"; break; case RACE_TAUREN: race_s = "Tauren"; break; case RACE_GNOME: race_s = "Gnome"; break; case RACE_TROLL: race_s = "Troll"; break; case RACE_BLOODELF: race_s = "Blood Elf"; break; case RACE_DRAENEI: race_s = "Draenei"; break; case RACE_GOBLIN: race_s = "Goblin"; break; case RACE_WORGEN: race_s = "Worgen"; break; } switch (Class) { case CLASS_WARRIOR: Class_s = "Warrior"; break; case CLASS_PALADIN: Class_s = "Paladin"; break; case CLASS_HUNTER: Class_s = "Hunter"; break; case CLASS_ROGUE: Class_s = "Rogue"; break; case CLASS_PRIEST: Class_s = "Priest"; break; case CLASS_DEATH_KNIGHT: Class_s = "Death Knight"; break; case CLASS_SHAMAN: Class_s = "Shaman"; break; case CLASS_MAGE: Class_s = "Mage"; break; case CLASS_WARLOCK: Class_s = "Warlock"; break; case CLASS_DRUID: Class_s = "Druid"; break; } std::string timeStr = secsToTimeString(total_player_time, true, true); uint32 gold = money /GOLD; uint32 silv = (money % GOLD) / SILVER; uint32 copp = (money % GOLD) % SILVER; PSendSysMessage(LANG_PINFO_LEVEL, race_s.c_str(), Class_s.c_str(), timeStr.c_str(), level, gold, silv, copp); // Add map, zone, subzone and phase to output int locale = GetSessionDbcLocale(); std::string areaName = "<unknown>"; std::string zoneName = ""; MapEntry const* map = sMapStore.LookupEntry(mapId); AreaTableEntry const* area = GetAreaEntryByAreaID(areaId); if (area) { areaName = area->area_name[locale]; AreaTableEntry const* zone = GetAreaEntryByAreaID(area->zone); if (zone) zoneName = zone->area_name[locale]; } if (target) { if (!zoneName.empty()) PSendSysMessage(LANG_PINFO_MAP_ONLINE, map->name[locale], zoneName.c_str(), areaName.c_str(), phase); else PSendSysMessage(LANG_PINFO_MAP_ONLINE, map->name[locale], areaName.c_str(), "<unknown>", phase); } else PSendSysMessage(LANG_PINFO_MAP_OFFLINE, map->name[locale], areaName.c_str()); return true; }
// Get water state on map GridMapLiquidStatus GridMap::getLiquidStatus(float x, float y, float z, uint8 ReqLiquidType, GridMapLiquidData *data) { // Check water type (if no water return) if (!m_liquidFlags && !m_liquidType) return LIQUID_MAP_NO_WATER; // Get cell float cx = MAP_RESOLUTION * (32 - x / SIZE_OF_GRIDS); float cy = MAP_RESOLUTION * (32 - y / SIZE_OF_GRIDS); int x_int = (int)cx & (MAP_RESOLUTION - 1); int y_int = (int)cy & (MAP_RESOLUTION - 1); // Check water type in cell int idx = (x_int>>3)*16 + (y_int>>3); uint8 type = m_liquidFlags ? m_liquidFlags[idx] : m_liquidType; uint32 entry = 0; if (m_liquidEntry) { if (LiquidTypeEntry const* liquidEntry = sLiquidTypeStore.LookupEntry(m_liquidEntry[idx])) { entry = liquidEntry->Id; type &= MAP_LIQUID_TYPE_DARK_WATER; uint32 liqTypeIdx = liquidEntry->Type; if (entry < 21) { if (AreaTableEntry const* area = sAreaStore.LookupEntry(getArea(x, y))) { uint32 overrideLiquid = area->LiquidTypeOverride[liquidEntry->Type]; if (!overrideLiquid && area->zone) { area = GetAreaEntryByAreaID(area->zone); if (area) overrideLiquid = area->LiquidTypeOverride[liquidEntry->Type]; } if (LiquidTypeEntry const* liq = sLiquidTypeStore.LookupEntry(overrideLiquid)) { entry = overrideLiquid; liqTypeIdx = liq->Type; } } } type |= 1 << liqTypeIdx; } } if (type == 0) return LIQUID_MAP_NO_WATER; // Check req liquid type mask if (ReqLiquidType && !(ReqLiquidType & type)) return LIQUID_MAP_NO_WATER; // Check water level: // Check water height map int lx_int = x_int - m_liquid_offY; if (lx_int < 0 || lx_int >= m_liquid_height) return LIQUID_MAP_NO_WATER; int ly_int = y_int - m_liquid_offX; if (ly_int < 0 || ly_int >= m_liquid_width) return LIQUID_MAP_NO_WATER; // Get water level float liquid_level = m_liquid_map ? m_liquid_map[lx_int * m_liquid_width + ly_int] : m_liquidLevel; // Get ground level (sub 0.2 for fix some errors) float ground_level = getHeight(x, y); // Check water level and ground level if (liquid_level < ground_level || z < ground_level - 2) return LIQUID_MAP_NO_WATER; // All ok in water -> store data if (data) { data->entry = entry; data->type_flags = type; data->level = liquid_level; data->depth_level = ground_level; } // For speed check as int values int delta = int((liquid_level - z) * 10); // Get position delta if (delta > 20) // Under water return LIQUID_MAP_UNDER_WATER; if (delta > 0) // In water return LIQUID_MAP_IN_WATER; if (delta > -1) // Walk on water return LIQUID_MAP_WATER_WALK; // Above water return LIQUID_MAP_ABOVE_WATER; }
GridMapLiquidStatus TerrainInfo::getLiquidStatus(float x, float y, float z, uint8 ReqLiquidType, GridMapLiquidData* data) const { GridMapLiquidStatus result = LIQUID_MAP_NO_WATER; VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager(); uint32 liquid_type = 0; float liquid_level = INVALID_HEIGHT_VALUE; float ground_level = GetHeightStatic(x, y, z, true, DEFAULT_WATER_SEARCH); if (vmgr->GetLiquidLevel(GetMapId(), x, y, z, ReqLiquidType, liquid_level, ground_level, liquid_type)) { // DEBUG_LOG("getLiquidStatus(): vmap liquid level: %f ground: %f type: %u", liquid_level, ground_level, liquid_type); // Check water level and ground level if (liquid_level > ground_level && z > ground_level - 2) { // All ok in water -> store data if (data) { // hardcoded in client like this if (GetMapId() == 530 && liquid_type == 2) liquid_type = 15; uint32 liquidFlagType = 0; if (LiquidTypeEntry const* liq = sLiquidTypeStore.LookupEntry(liquid_type)) liquidFlagType = liq->Type; if (liquid_type && liquid_type < 21) { if (AreaTableEntry const* area = GetAreaEntryByAreaFlagAndMap(GetAreaFlag(x, y, z), GetMapId())) { uint32 overrideLiquid = area->LiquidTypeOverride[liquidFlagType]; if (!overrideLiquid && area->zone) { area = GetAreaEntryByAreaID(area->zone); if (area) overrideLiquid = area->LiquidTypeOverride[liquidFlagType]; } if (LiquidTypeEntry const* liq = sLiquidTypeStore.LookupEntry(overrideLiquid)) { liquid_type = overrideLiquid; liquidFlagType = liq->Type; } } } data->level = liquid_level; data->depth_level = ground_level; data->entry = liquid_type; data->type_flags = 1 << liquidFlagType; } // For speed check as int values int delta = int((liquid_level - z) * 10); // Get position delta if (delta > 20) // Under water return LIQUID_MAP_UNDER_WATER; if (delta > 0) // In water return LIQUID_MAP_IN_WATER; if (delta > -1) // Walk on water return LIQUID_MAP_WATER_WALK; result = LIQUID_MAP_ABOVE_WATER; } } else if (GridMapPtr gmap = const_cast<TerrainInfo*>(this)->GetGrid(x, y)) { GridMapLiquidData map_data; GridMapLiquidStatus map_result = gmap->getLiquidStatus(x, y, z, ReqLiquidType, &map_data); // Not override LIQUID_MAP_ABOVE_WATER with LIQUID_MAP_NO_WATER: if (map_result != LIQUID_MAP_NO_WATER && (map_data.level > ground_level)) { if (data) { // hardcoded in client like this if (GetMapId() == 530 && map_data.entry == 2) map_data.entry = 15; *data = map_data; } return map_result; } } return result; }
bool ChatHandler::HandleGPSCommand(const char* args) { WorldObject *obj = NULL; if (*args) { uint64 guid = extractGuidFromLink((char*)args); if (guid) obj = (WorldObject*)ObjectAccessor::GetObjectByTypeMask(*m_session->GetPlayer(),guid,TYPEMASK_UNIT|TYPEMASK_GAMEOBJECT); if (!obj) { SendSysMessage(LANG_PLAYER_NOT_FOUND); SetSentErrorMessage(true); return false; } } else { obj = getSelectedUnit(); if (!obj) { SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE); SetSentErrorMessage(true); return false; } } CellPair cell_val = Trinity::ComputeCellPair(obj->GetPositionX(), obj->GetPositionY()); Cell cell(cell_val); uint32 zone_id, area_id; obj->GetZoneAndAreaId(zone_id,area_id); MapEntry const* mapEntry = sMapStore.LookupEntry(obj->GetMapId()); AreaTableEntry const* zoneEntry = GetAreaEntryByAreaID(zone_id); AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(area_id); float zone_x = obj->GetPositionX(); float zone_y = obj->GetPositionY(); Map2ZoneCoordinates(zone_x,zone_y,zone_id); Map const *map = obj->GetMap(); float ground_z = map->GetHeight(obj->GetPositionX(), obj->GetPositionY(), MAX_HEIGHT); float floor_z = map->GetHeight(obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ()); GridPair p = Trinity::ComputeGridPair(obj->GetPositionX(), obj->GetPositionY()); // 63? WHY? int gx = 63 - p.x_coord; int gy = 63 - p.y_coord; uint32 have_map = Map::ExistMap(obj->GetMapId(),gx,gy) ? 1 : 0; uint32 have_vmap = Map::ExistVMap(obj->GetMapId(),gx,gy) ? 1 : 0; if(have_vmap) { if(map->IsOutdoors(obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ())) PSendSysMessage("You are outdoors"); else PSendSysMessage("You are indoors"); } else PSendSysMessage("no VMAP available for area info"); PSendSysMessage(LANG_MAP_POSITION, obj->GetMapId(), (mapEntry ? mapEntry->name[GetSessionDbcLocale()] : "<unknown>"), zone_id, (zoneEntry ? zoneEntry->area_name[GetSessionDbcLocale()] : "<unknown>"), area_id, (areaEntry ? areaEntry->area_name[GetSessionDbcLocale()] : "<unknown>"), obj->GetPhaseMask(), obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ(), obj->GetOrientation(), cell.GridX(), cell.GridY(), cell.CellX(), cell.CellY(), obj->GetInstanceId(), zone_x, zone_y, ground_z, floor_z, have_map, have_vmap); sLog->outDebug("Player %s GPS call for %s '%s' (%s: %u):", m_session ? GetNameLink().c_str() : GetTrinityString(LANG_CONSOLE_COMMAND), (obj->GetTypeId() == TYPEID_PLAYER ? "player" : "creature"), obj->GetName(), (obj->GetTypeId() == TYPEID_PLAYER ? "GUID" : "Entry"), (obj->GetTypeId() == TYPEID_PLAYER ? obj->GetGUIDLow(): obj->GetEntry())); sLog->outDebug(GetTrinityString(LANG_MAP_POSITION), obj->GetMapId(), (mapEntry ? mapEntry->name[sWorld->GetDefaultDbcLocale()] : "<unknown>"), zone_id, (zoneEntry ? zoneEntry->area_name[sWorld->GetDefaultDbcLocale()] : "<unknown>"), area_id, (areaEntry ? areaEntry->area_name[sWorld->GetDefaultDbcLocale()] : "<unknown>"), obj->GetPhaseMask(), obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ(), obj->GetOrientation(), cell.GridX(), cell.GridY(), cell.CellX(), cell.CellY(), obj->GetInstanceId(), zone_x, zone_y, ground_z, floor_z, have_map, have_vmap); LiquidData liquid_status; ZLiquidStatus res = map->getLiquidStatus(obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ(), MAP_ALL_LIQUIDS, &liquid_status); if (res) { PSendSysMessage(LANG_LIQUID_STATUS, liquid_status.level, liquid_status.depth_level, liquid_status.type, res); } return true; }
void WorldSession::HandleMovementOpcodes(WorldPacket& recvPacket) { uint16 opcode = recvPacket.GetOpcode(); Unit* mover = _player->m_mover; ASSERT(mover != NULL); // there must always be a mover Player* plrMover = mover->ToPlayer(); // ignore, waiting processing in WorldSession::HandleMoveWorldportAckOpcode and WorldSession::HandleMoveTeleportAck if (plrMover && plrMover->IsBeingTeleported()) { recvPacket.rfinish(); // prevent warnings spam return; } // Sometime, client send movement packet after teleporation with position before teleportation, so we ignore 3 first movement packet after teleporation // TODO: find a better way to check that, may be a new CMSG send by client ? /*if (plrMover && plrMover->GetIgnoreMovementCount() && opcode != CMSG_CAST_SPELL) { plrMover->SetIgnoreMovementCount(plrMover->GetIgnoreMovementCount() - 1); recvPacket.rfinish(); // prevent warnings spam return; }*/ /* extract packet */ MovementInfo movementInfo; GetPlayer()->ReadMovementInfo(recvPacket, &movementInfo); if (!mover) return; // prevent tampered movement data if (movementInfo.guid != mover->GetGUID()) { sLog->outDebug(LOG_FILTER_NETWORKIO, "HandleMovementOpcodes: guid error"); return; } if (!movementInfo.pos.IsPositionValid()) { sLog->outDebug(LOG_FILTER_NETWORKIO, "HandleMovementOpcodes: Invalid Position"); return; } /* handle special cases */ if (movementInfo.t_guid) { // transports size limited // (also received at zeppelin leave by some reason with t_* as absolute in continent coordinates, can be safely skipped) if (movementInfo.t_pos.GetPositionX() > 50 || movementInfo.t_pos.GetPositionY() > 50 || movementInfo.t_pos.GetPositionZ() > 50) { recvPacket.rfinish(); // prevent warnings spam return; } if (!WoWSource::IsValidMapCoord(movementInfo.pos.GetPositionX() + movementInfo.t_pos.GetPositionX(), movementInfo.pos.GetPositionY() + movementInfo.t_pos.GetPositionY(), movementInfo.pos.GetPositionZ() + movementInfo.t_pos.GetPositionZ(), movementInfo.pos.GetOrientation() + movementInfo.t_pos.GetOrientation())) { recvPacket.rfinish(); // prevent warnings spam return; } // if we boarded a transport, add us to it if (plrMover) { if (!plrMover->GetTransport()) { // elevators also cause the client to send MOVEMENTFLAG_ONTRANSPORT - just dismount if the guid can be found in the transport list for (MapManager::TransportSet::const_iterator iter = sMapMgr->m_Transports.begin(); iter != sMapMgr->m_Transports.end(); ++iter) { if ((*iter)->GetGUID() == movementInfo.t_guid) { plrMover->m_transport = *iter; (*iter)->AddPassenger(plrMover); break; } } } else if (plrMover->GetTransport()->GetGUID() != movementInfo.t_guid) { bool foundNewTransport = false; plrMover->m_transport->RemovePassenger(plrMover); for (MapManager::TransportSet::const_iterator iter = sMapMgr->m_Transports.begin(); iter != sMapMgr->m_Transports.end(); ++iter) { if ((*iter)->GetGUID() == movementInfo.t_guid) { foundNewTransport = true; plrMover->m_transport = *iter; (*iter)->AddPassenger(plrMover); break; } } if (!foundNewTransport) { plrMover->m_transport = NULL; movementInfo.t_pos.Relocate(0.0f, 0.0f, 0.0f, 0.0f); movementInfo.t_time = 0; movementInfo.t_seat = -1; } } } if (!mover->GetTransport() && !mover->GetVehicle()) { GameObject* go = mover->GetMap()->GetGameObject(movementInfo.t_guid); if (!go || go->GetGoType() != GAMEOBJECT_TYPE_TRANSPORT) movementInfo.t_guid = 0; } } else if (plrMover && plrMover->GetTransport()) // if we were on a transport, leave { plrMover->m_transport->RemovePassenger(plrMover); plrMover->m_transport = NULL; movementInfo.t_pos.Relocate(0.0f, 0.0f, 0.0f, 0.0f); movementInfo.t_time = 0; movementInfo.t_seat = -1; movementInfo.t_guid = 0LL; } // fall damage generation (ignore in flight case that can be triggered also at lags in moment teleportation to another map). if (opcode == CMSG_MOVE_FALL_LAND && plrMover && !plrMover->isInFlight()) { plrMover->HandleFall(movementInfo); } if (plrMover && ((movementInfo.flags & MOVEMENTFLAG_SWIMMING) != 0) != plrMover->IsInWater()) { // now client not include swimming flag in case jumping under water plrMover->SetInWater(!plrMover->IsInWater() || plrMover->GetBaseMap()->IsUnderWater(movementInfo.pos.GetPositionX(), movementInfo.pos.GetPositionY(), movementInfo.pos.GetPositionZ())); } // Hack Fix, clean emotes when moving if (plrMover && plrMover->GetLastPlayedEmote()) plrMover->HandleEmoteCommand(0); //if (plrMover) // sAnticheatMgr->StartHackDetection(plrMover, movementInfo, opcode); uint32 mstime = getMSTime(); if (m_clientTimeDelay == 0) m_clientTimeDelay = mstime - movementInfo.time; movementInfo.time = movementInfo.time + m_clientTimeDelay + MOVEMENT_PACKET_TIME_DELAY; movementInfo.guid = mover->GetGUID(); mover->m_movementInfo = movementInfo; // this is almost never true (not sure why it is sometimes, but it is), normally use mover->IsVehicle() if (mover->GetVehicle()) { mover->SetOrientation(movementInfo.pos.GetOrientation()); return; } mover->UpdatePosition(movementInfo.pos); /* process position-change */ WorldPacket data(SMSG_MOVE_UPDATE, recvPacket.size()); //movementInfo.Alive32 = movementInfo.time; // hack, but it's work in 505 in this way ... mover->WriteMovementInfo(data); mover->SendMessageToSet(&data, _player); if (plrMover) // nothing is charmed, or player charmed { plrMover->UpdateFallInformationIfNeed(movementInfo, opcode); AreaTableEntry const* zone = GetAreaEntryByAreaID(plrMover->GetAreaId()); float depth = zone ? zone->MaxDepth : -500.0f; if (movementInfo.pos.GetPositionZ() < depth) { if (!(plrMover->GetBattleground() && plrMover->GetBattleground()->HandlePlayerUnderMap(_player))) { // NOTE: this is actually called many times while falling // even after the player has been teleported away // TODO: discard movement packets after the player is rooted if (plrMover->isAlive()) { plrMover->EnvironmentalDamage(DAMAGE_FALL_TO_VOID, GetPlayer()->GetMaxHealth()); // player can be alive if GM/etc // change the death state to CORPSE to prevent the death timer from // starting in the next player update if (!plrMover->isAlive()) plrMover->KillPlayer(); } } } } }
// ------------------- void CreatureEventAIMgr::LoadCreatureEventAI_Scripts() { // Drop Existing EventAI List m_CreatureEventAI_Event_Map.clear(); // Gather event data QueryResult* result = WorldDatabase.Query("SELECT id, creature_id, event_type, event_inverse_phase_mask, event_chance, event_flags, " "event_param1, event_param2, event_param3, event_param4, " "action1_type, action1_param1, action1_param2, action1_param3, " "action2_type, action2_param1, action2_param2, action2_param3, " "action3_type, action3_param1, action3_param2, action3_param3 " "FROM creature_ai_scripts"); if (result) { BarGoLink bar(result->GetRowCount()); uint32 Count = 0; do { bar.step(); Field* fields = result->Fetch(); CreatureEventAI_Event temp; temp.event_id = EventAI_Type(fields[0].GetUInt32()); uint32 i = temp.event_id; temp.creature_id = fields[1].GetUInt32(); uint32 creature_id = temp.creature_id; uint32 e_type = fields[2].GetUInt32(); // Report any errors in event if (e_type >= EVENT_T_END) { sLog.outErrorEventAI("Event %u have wrong type (%u), skipping.", i, e_type); continue; } temp.event_type = EventAI_Type(e_type); temp.event_inverse_phase_mask = fields[3].GetUInt32(); temp.event_chance = fields[4].GetUInt8(); temp.event_flags = fields[5].GetUInt8(); temp.raw.param1 = fields[6].GetUInt32(); temp.raw.param2 = fields[7].GetUInt32(); temp.raw.param3 = fields[8].GetUInt32(); temp.raw.param4 = fields[9].GetUInt32(); // Creature does not exist in database if (!sCreatureStorage.LookupEntry<CreatureInfo>(temp.creature_id)) { sLog.outErrorEventAI("Event %u has script for non-existing creature entry (%u), skipping.", i, temp.creature_id); continue; } // No chance of this event occuring if (temp.event_chance == 0) sLog.outErrorEventAI("Event %u has 0 percent chance. Event will never trigger!", i); // Chance above 100, force it to be 100 else if (temp.event_chance > 100) { sLog.outErrorEventAI("Creature %u are using event %u with more than 100 percent chance. Adjusting to 100 percent.", temp.creature_id, i); temp.event_chance = 100; } // Individual event checks switch (temp.event_type) { case EVENT_T_TIMER_IN_COMBAT: case EVENT_T_TIMER_OOC: case EVENT_T_TIMER_GENERIC: if (temp.timer.initialMax < temp.timer.initialMin) sLog.outErrorEventAI("Creature %u are using timed event(%u) with param2 < param1 (InitialMax < InitialMin). Event will never repeat.", temp.creature_id, i); if (temp.timer.repeatMax < temp.timer.repeatMin) sLog.outErrorEventAI("Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i); break; case EVENT_T_HP: case EVENT_T_MANA: case EVENT_T_TARGET_HP: case EVENT_T_TARGET_MANA: if (temp.percent_range.percentMax > 100) sLog.outErrorEventAI("Creature %u are using percentage event(%u) with param2 (MinPercent) > 100. Event will never trigger! ", temp.creature_id, i); if (temp.percent_range.percentMax <= temp.percent_range.percentMin) sLog.outErrorEventAI("Creature %u are using percentage event(%u) with param1 <= param2 (MaxPercent <= MinPercent). Event will never trigger! ", temp.creature_id, i); if (temp.event_flags & EFLAG_REPEATABLE && !temp.percent_range.repeatMin && !temp.percent_range.repeatMax) { sLog.outErrorEventAI("Creature %u has param3 and param4=0 (RepeatMin/RepeatMax) but cannot be repeatable without timers. Removing EFLAG_REPEATABLE for event %u.", temp.creature_id, i); temp.event_flags &= ~EFLAG_REPEATABLE; } break; case EVENT_T_SPELLHIT: if (temp.spell_hit.spellId) { SpellEntry const* pSpell = sSpellStore.LookupEntry(temp.spell_hit.spellId); if (!pSpell) { sLog.outErrorEventAI("Creature %u has nonexistent SpellID(%u) defined in event %u.", temp.creature_id, temp.spell_hit.spellId, i); continue; } if ((temp.spell_hit.schoolMask & pSpell->SchoolMask) != pSpell->SchoolMask) sLog.outErrorEventAI("Creature %u has param1(spellId %u) but param2 is not -1 and not equal to spell's school mask. Event %u can never trigger.", temp.creature_id, temp.spell_hit.schoolMask, i); } if (!temp.spell_hit.schoolMask) sLog.outErrorEventAI("Creature %u is using invalid SpellSchoolMask(%u) defined in event %u.", temp.creature_id, temp.spell_hit.schoolMask, i); if (temp.spell_hit.repeatMax < temp.spell_hit.repeatMin) sLog.outErrorEventAI("Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i); break; case EVENT_T_RANGE: if (temp.range.maxDist < temp.range.minDist) sLog.outErrorEventAI("Creature %u are using event(%u) with param2 < param1 (MaxDist < MinDist). Event will never repeat.", temp.creature_id, i); if (temp.range.repeatMax < temp.range.repeatMin) sLog.outErrorEventAI("Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i); break; case EVENT_T_OOC_LOS: if (temp.ooc_los.repeatMax < temp.ooc_los.repeatMin) sLog.outErrorEventAI("Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i); break; case EVENT_T_SPAWNED: switch (temp.spawned.condition) { case SPAWNED_EVENT_ALWAY: break; case SPAWNED_EVENT_MAP: if (!sMapStore.LookupEntry(temp.spawned.conditionValue1)) sLog.outErrorEventAI("Creature %u are using spawned event(%u) with param1 = %u 'map specific' but map (param2: %u) does not exist. Event will never repeat.", temp.creature_id, i, temp.spawned.condition, temp.spawned.conditionValue1); break; case SPAWNED_EVENT_ZONE: if (!GetAreaEntryByAreaID(temp.spawned.conditionValue1)) sLog.outErrorEventAI("Creature %u are using spawned event(%u) with param1 = %u 'area specific' but area (param2: %u) does not exist. Event will never repeat.", temp.creature_id, i, temp.spawned.condition, temp.spawned.conditionValue1); break; default: sLog.outErrorEventAI("Creature %u are using invalid spawned event %u mode (%u) in param1", temp.creature_id, i, temp.spawned.condition); break; } break; case EVENT_T_FRIENDLY_HP: if (temp.friendly_hp.repeatMax < temp.friendly_hp.repeatMin) sLog.outErrorEventAI("Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i); break; case EVENT_T_FRIENDLY_IS_CC: if (temp.friendly_is_cc.repeatMax < temp.friendly_is_cc.repeatMin) sLog.outErrorEventAI("Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i); break; case EVENT_T_FRIENDLY_MISSING_BUFF: { SpellEntry const* pSpell = sSpellStore.LookupEntry(temp.friendly_buff.spellId); if (!pSpell) { sLog.outErrorEventAI("Creature %u has nonexistent SpellID(%u) defined in event %u.", temp.creature_id, temp.friendly_buff.spellId, i); continue; } if (temp.friendly_buff.radius <= 0) { sLog.outErrorEventAI("Creature %u has wrong radius (%u) for EVENT_T_FRIENDLY_MISSING_BUFF defined in event %u.", temp.creature_id, temp.friendly_buff.radius, i); continue; } if (temp.friendly_buff.repeatMax < temp.friendly_buff.repeatMin) sLog.outErrorEventAI("Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i); break; } case EVENT_T_KILL: if (temp.kill.repeatMax < temp.kill.repeatMin) sLog.outErrorEventAI("Creature %u are using event(%u) with param2 < param1 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i); break; case EVENT_T_TARGET_CASTING: if (temp.target_casting.repeatMax < temp.target_casting.repeatMin) sLog.outErrorEventAI("Creature %u are using event(%u) with param2 < param1 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i); break; case EVENT_T_SUMMONED_UNIT: case EVENT_T_SUMMONED_JUST_DIED: case EVENT_T_SUMMONED_JUST_DESPAWN: if (!sCreatureStorage.LookupEntry<CreatureInfo>(temp.summoned.creatureId)) sLog.outErrorEventAI("Creature %u are using event(%u) with nonexistent creature template id (%u) in param1, skipped.", temp.creature_id, i, temp.summoned.creatureId); if (temp.summoned.repeatMax < temp.summoned.repeatMin) sLog.outErrorEventAI("Creature %u are using event(%u) with param2 < param1 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i); break; case EVENT_T_QUEST_ACCEPT: case EVENT_T_QUEST_COMPLETE: if (!sObjectMgr.GetQuestTemplate(temp.quest.questId)) sLog.outErrorEventAI("Creature %u are using event(%u) with nonexistent quest id (%u) in param1, skipped.", temp.creature_id, i, temp.quest.questId); sLog.outErrorEventAI("Creature %u using not implemented event (%u) in event %u.", temp.creature_id, temp.event_id, i); continue; case EVENT_T_AGGRO: case EVENT_T_DEATH: case EVENT_T_EVADE: case EVENT_T_REACHED_HOME: { if (temp.event_flags & EFLAG_REPEATABLE) { sLog.outErrorEventAI("Creature %u has EFLAG_REPEATABLE set. Event can never be repeatable. Removing flag for event %u.", temp.creature_id, i); temp.event_flags &= ~EFLAG_REPEATABLE; } break; } case EVENT_T_RECEIVE_EMOTE: { if (!sEmotesTextStore.LookupEntry(temp.receive_emote.emoteId)) { sLog.outErrorEventAI("Creature %u using event %u: param1 (EmoteTextId: %u) are not valid.", temp.creature_id, i, temp.receive_emote.emoteId); continue; } if (!PlayerCondition::IsValid(0, ConditionType(temp.receive_emote.condition), temp.receive_emote.conditionValue1, temp.receive_emote.conditionValue2)) { sLog.outErrorEventAI("Creature %u using event %u: param2 (Condition: %u) are not valid.", temp.creature_id, i, temp.receive_emote.condition); continue; } if (!(temp.event_flags & EFLAG_REPEATABLE)) { sLog.outErrorEventAI("Creature %u using event %u: EFLAG_REPEATABLE not set. Event must always be repeatable. Flag applied.", temp.creature_id, i); temp.event_flags |= EFLAG_REPEATABLE; } break; } case EVENT_T_AURA: case EVENT_T_TARGET_AURA: case EVENT_T_MISSING_AURA: case EVENT_T_TARGET_MISSING_AURA: { SpellEntry const* pSpell = sSpellStore.LookupEntry(temp.buffed.spellId); if (!pSpell) { sLog.outErrorEventAI("Creature %u has nonexistent SpellID(%u) defined in event %u.", temp.creature_id, temp.buffed.spellId, i); continue; } if (temp.buffed.amount < 1) { sLog.outErrorEventAI("Creature %u has wrong spell stack size (%u) defined in event %u.", temp.creature_id, temp.buffed.amount, i); continue; } if (temp.buffed.repeatMax < temp.buffed.repeatMin) sLog.outErrorEventAI("Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i); break; } default: sLog.outErrorEventAI("Creature %u using not checked at load event (%u) in event %u. Need check code update?", temp.creature_id, temp.event_id, i); break; } for (uint32 j = 0; j < MAX_ACTIONS; ++j) { uint16 action_type = fields[10+(j*4)].GetUInt16(); if (action_type >= ACTION_T_END) { sLog.outErrorEventAI("Event %u Action %u has incorrect action type (%u), replace by ACTION_T_NONE.", i, j + 1, action_type); temp.action[j].type = ACTION_T_NONE; continue; } CreatureEventAI_Action& action = temp.action[j]; action.type = EventAI_ActionType(action_type); action.raw.param1 = fields[11+(j*4)].GetUInt32(); action.raw.param2 = fields[12+(j*4)].GetUInt32(); action.raw.param3 = fields[13+(j*4)].GetUInt32(); // Report any errors in actions switch (action.type) { case ACTION_T_NONE: break; case ACTION_T_CHANCED_TEXT: // Check first param as chance if (!action.chanced_text.chance) sLog.outErrorEventAI("Event %u Action %u has not set chance param1. Text will not be displayed", i, j + 1); else if (action.chanced_text.chance >= 100) sLog.outErrorEventAI("Event %u Action %u has set chance param1 >= 100. Text will always be displayed", i, j + 1); // no break here to check texts case ACTION_T_TEXT: { bool not_set = false; int firstTextParam = action.type == ACTION_T_TEXT ? 0 : 1; for (int k = firstTextParam; k < 3; ++k) { if (action.text.TextId[k]) { if (k > firstTextParam && not_set) sLog.outErrorEventAI("Event %u Action %u has param%d, but it follow after not set param. Required for randomized text.", i, j + 1, k + 1); if (!action.text.TextId[k]) not_set = true; // range negative else if (action.text.TextId[k] > MIN_CREATURE_AI_TEXT_STRING_ID || action.text.TextId[k] <= MAX_CREATURE_AI_TEXT_STRING_ID) { sLog.outErrorEventAI("Event %u Action %u param%d references out-of-range entry (%i) in texts table.", i, j + 1, k + 1, action.text.TextId[k]); action.text.TextId[k] = 0; } else if (m_CreatureEventAI_TextMap.find(action.text.TextId[k]) == m_CreatureEventAI_TextMap.end()) { sLog.outErrorEventAI("Event %u Action %u param%d references non-existing entry (%i) in texts table.", i, j + 1, k + 1, action.text.TextId[k]); action.text.TextId[k] = 0; } } } break; } case ACTION_T_SET_FACTION: if (action.set_faction.factionId != 0 && !sFactionTemplateStore.LookupEntry(action.set_faction.factionId)) { sLog.outErrorEventAI("Event %u Action %u uses nonexistent FactionId %u.", i, j + 1, action.set_faction.factionId); action.set_faction.factionId = 0; } break; case ACTION_T_MORPH_TO_ENTRY_OR_MODEL: if (action.morph.creatureId != 0 || action.morph.modelId != 0) { if (action.morph.creatureId && !sCreatureStorage.LookupEntry<CreatureInfo>(action.morph.creatureId)) { sLog.outErrorEventAI("Event %u Action %u uses nonexistent Creature entry %u.", i, j + 1, action.morph.creatureId); action.morph.creatureId = 0; } if (action.morph.modelId) { if (action.morph.creatureId) { sLog.outErrorEventAI("Event %u Action %u have unused ModelId %u with also set creature id %u.", i, j + 1, action.morph.modelId, action.morph.creatureId); action.morph.modelId = 0; } else if (!sCreatureDisplayInfoStore.LookupEntry(action.morph.modelId)) { sLog.outErrorEventAI("Event %u Action %u uses nonexistent ModelId %u.", i, j + 1, action.morph.modelId); action.morph.modelId = 0; } } } break; case ACTION_T_SOUND: if (!sSoundEntriesStore.LookupEntry(action.sound.soundId)) sLog.outErrorEventAI("Event %u Action %u uses nonexistent SoundID %u.", i, j + 1, action.sound.soundId); break; case ACTION_T_EMOTE: if (!sEmotesStore.LookupEntry(action.emote.emoteId)) sLog.outErrorEventAI("Event %u Action %u param1 (EmoteId: %u) are not valid.", i, j + 1, action.emote.emoteId); break; case ACTION_T_RANDOM_SOUND: if (!sSoundEntriesStore.LookupEntry(action.random_sound.soundId1)) sLog.outErrorEventAI("Event %u Action %u param1 uses nonexistent SoundID %u.", i, j + 1, action.random_sound.soundId1); if (action.random_sound.soundId2 >= 0 && !sSoundEntriesStore.LookupEntry(action.random_sound.soundId2)) sLog.outErrorEventAI("Event %u Action %u param2 uses nonexistent SoundID %u.", i, j + 1, action.random_sound.soundId2); if (action.random_sound.soundId3 >= 0 && !sSoundEntriesStore.LookupEntry(action.random_sound.soundId3)) sLog.outErrorEventAI("Event %u Action %u param3 uses nonexistent SoundID %u.", i, j + 1, action.random_sound.soundId3); break; case ACTION_T_RANDOM_EMOTE: if (!sEmotesStore.LookupEntry(action.random_emote.emoteId1)) sLog.outErrorEventAI("Event %u Action %u param1 (EmoteId: %u) are not valid.", i, j + 1, action.random_emote.emoteId1); if (action.random_emote.emoteId2 >= 0 && !sEmotesStore.LookupEntry(action.random_emote.emoteId2)) sLog.outErrorEventAI("Event %u Action %u param2 (EmoteId: %u) are not valid.", i, j + 1, action.random_emote.emoteId2); if (action.random_emote.emoteId3 >= 0 && !sEmotesStore.LookupEntry(action.random_emote.emoteId3)) sLog.outErrorEventAI("Event %u Action %u param3 (EmoteId: %u) are not valid.", i, j + 1, action.random_emote.emoteId3); break; case ACTION_T_CAST: { const SpellEntry* spell = sSpellStore.LookupEntry(action.cast.spellId); if (!spell) sLog.outErrorEventAI("Event %u Action %u uses nonexistent SpellID %u.", i, j + 1, action.cast.spellId); /* FIXME: temp.raw.param3 not have event tipes with recovery time in it.... else { if (spell->RecoveryTime > 0 && temp.event_flags & EFLAG_REPEATABLE) { // output as debug for now, also because there's no general rule all spells have RecoveryTime if (temp.event_param3 < spell->RecoveryTime) DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "CreatureEventAI: Event %u Action %u uses SpellID %u but cooldown is longer(%u) than minumum defined in event param3(%u).", i, j+1,action.cast.spellId, spell->RecoveryTime, temp.event_param3); } } */ // Cast is always triggered if target is forced to cast on self if (action.cast.castFlags & CAST_FORCE_TARGET_SELF) action.cast.castFlags |= CAST_TRIGGERED; if (action.cast.target >= TARGET_T_END) sLog.outErrorEventAI("Event %u Action %u uses incorrect Target type", i, j + 1); // Some Advanced target type checks - Can have false positives if (!sLog.HasLogFilter(LOG_FILTER_EVENT_AI_DEV) && spell) { // used TARGET_T_ACTION_INVOKER, but likely should be _INVOKER_OWNER instead if (action.cast.target == TARGET_T_ACTION_INVOKER && (IsSpellHaveEffect(spell, SPELL_EFFECT_QUEST_COMPLETE) || IsSpellHaveEffect(spell, SPELL_EFFECT_DUMMY) || IsSpellHaveEffect(spell, SPELL_EFFECT_KILL_CREDIT_GROUP))) sLog.outErrorEventAI("Event %u Action %u has TARGET_T_ACTION_INVOKER(%u) target type, but should have TARGET_T_ACTION_INVOKER_OWNER(%u).", i, j + 1, TARGET_T_ACTION_INVOKER, TARGET_T_ACTION_INVOKER_OWNER); // Spell that should only target players, but could get any if (spell->HasAttribute(SPELL_ATTR_EX3_TARGET_ONLY_PLAYER) && (action.cast.target == TARGET_T_ACTION_INVOKER || action.cast.target == TARGET_T_HOSTILE_RANDOM || action.cast.target == TARGET_T_HOSTILE_RANDOM_NOT_TOP)) sLog.outErrorEventAI("Event %u Action %u uses Target type %u for a spell (%u) that should only target players. This could be wrong.", i, j + 1, action.cast.target, action.cast.spellId); } break; } case ACTION_T_SUMMON: if (!sCreatureStorage.LookupEntry<CreatureInfo>(action.summon.creatureId)) sLog.outErrorEventAI("Event %u Action %u uses nonexistent creature entry %u.", i, j + 1, action.summon.creatureId); if (action.summon.target >= TARGET_T_END) sLog.outErrorEventAI("Event %u Action %u uses incorrect Target type", i, j + 1); break; case ACTION_T_THREAT_SINGLE_PCT: if (std::abs(action.threat_single_pct.percent) > 100) sLog.outErrorEventAI("Event %u Action %u uses invalid percent value %u.", i, j + 1, action.threat_single_pct.percent); if (action.threat_single_pct.target >= TARGET_T_END) sLog.outErrorEventAI("Event %u Action %u uses incorrect Target type", i, j + 1); break; case ACTION_T_THREAT_ALL_PCT: if (std::abs(action.threat_all_pct.percent) > 100) sLog.outErrorEventAI("Event %u Action %u uses invalid percent value %u.", i, j + 1, action.threat_all_pct.percent); break; case ACTION_T_QUEST_EVENT: if (Quest const* qid = sObjectMgr.GetQuestTemplate(action.quest_event.questId)) { if (!qid->HasSpecialFlag(QUEST_SPECIAL_FLAG_EXPLORATION_OR_EVENT)) sLog.outErrorEventAI("Event %u Action %u. SpecialFlags for quest entry %u does not include |2, Action will not have any effect.", i, j + 1, action.quest_event.questId); } else sLog.outErrorEventAI("Event %u Action %u uses nonexistent Quest entry %u.", i, j + 1, action.quest_event.questId); if (action.quest_event.target >= TARGET_T_END) sLog.outErrorEventAI("Event %u Action %u uses incorrect Target type", i, j + 1); break; case ACTION_T_CAST_EVENT: if (!sCreatureStorage.LookupEntry<CreatureInfo>(action.cast_event.creatureId)) sLog.outErrorEventAI("Event %u Action %u uses nonexistent creature entry %u.", i, j + 1, action.cast_event.creatureId); if (!sSpellStore.LookupEntry(action.cast_event.spellId)) sLog.outErrorEventAI("Event %u Action %u uses nonexistent SpellID %u.", i, j + 1, action.cast_event.spellId); if (action.cast_event.target >= TARGET_T_END) sLog.outErrorEventAI("Event %u Action %u uses incorrect Target type", i, j + 1); break; case ACTION_T_SET_UNIT_FIELD: if (action.set_unit_field.field < OBJECT_END || action.set_unit_field.field >= UNIT_END) sLog.outErrorEventAI("Event %u Action %u param1 (UNIT_FIELD*). Index out of range for intended use.", i, j + 1); if (action.set_unit_field.target >= TARGET_T_END) sLog.outErrorEventAI("Event %u Action %u uses incorrect Target type", i, j + 1); break; case ACTION_T_SET_UNIT_FLAG: case ACTION_T_REMOVE_UNIT_FLAG: if (action.unit_flag.target >= TARGET_T_END) sLog.outErrorEventAI("Event %u Action %u uses incorrect Target type", i, j + 1); break; case ACTION_T_SET_PHASE: if (action.set_phase.phase >= MAX_PHASE) sLog.outErrorEventAI("Event %u Action %u attempts to set phase >= %u. Phase mask cannot be used past phase %u.", i, j + 1, MAX_PHASE, MAX_PHASE - 1); break; case ACTION_T_INC_PHASE: if (action.set_inc_phase.step == 0) sLog.outErrorEventAI("Event %u Action %u is incrementing phase by 0. Was this intended?", i, j + 1); else if (std::abs(action.set_inc_phase.step) > MAX_PHASE - 1) sLog.outErrorEventAI("Event %u Action %u is change phase by too large for any use %i.", i, j + 1, action.set_inc_phase.step); break; case ACTION_T_QUEST_EVENT_ALL: if (Quest const* qid = sObjectMgr.GetQuestTemplate(action.quest_event_all.questId)) { if (!qid->HasSpecialFlag(QUEST_SPECIAL_FLAG_EXPLORATION_OR_EVENT)) sLog.outErrorEventAI("Event %u Action %u. SpecialFlags for quest entry %u does not include |2, Action will not have any effect.", i, j + 1, action.quest_event_all.questId); } else sLog.outErrorEventAI("Event %u Action %u uses nonexistent Quest entry %u.", i, j + 1, action.quest_event_all.questId); break; case ACTION_T_CAST_EVENT_ALL: if (!sCreatureStorage.LookupEntry<CreatureInfo>(action.cast_event_all.creatureId)) sLog.outErrorEventAI("Event %u Action %u uses nonexistent creature entry %u.", i, j + 1, action.cast_event_all.creatureId); if (!sSpellStore.LookupEntry(action.cast_event_all.spellId)) sLog.outErrorEventAI("Event %u Action %u uses nonexistent SpellID %u.", i, j + 1, action.cast_event_all.spellId); break; case ACTION_T_REMOVEAURASFROMSPELL: if (!sSpellStore.LookupEntry(action.remove_aura.spellId)) sLog.outErrorEventAI("Event %u Action %u uses nonexistent SpellID %u.", i, j + 1, action.remove_aura.spellId); if (action.remove_aura.target >= TARGET_T_END) sLog.outErrorEventAI("Event %u Action %u uses incorrect Target type", i, j + 1); break; case ACTION_T_RANDOM_PHASE: // PhaseId1, PhaseId2, PhaseId3 if (action.random_phase.phase1 >= MAX_PHASE) sLog.outErrorEventAI("Event %u Action %u attempts to set phase1 >= %u. Phase mask cannot be used past phase %u.", i, j + 1, MAX_PHASE, MAX_PHASE - 1); if (action.random_phase.phase2 >= MAX_PHASE) sLog.outErrorEventAI("Event %u Action %u attempts to set phase2 >= %u. Phase mask cannot be used past phase %u.", i, j + 1, MAX_PHASE, MAX_PHASE - 1); if (action.random_phase.phase3 >= MAX_PHASE) sLog.outErrorEventAI("Event %u Action %u attempts to set phase3 >= %u. Phase mask cannot be used past phase %u.", i, j + 1, MAX_PHASE, MAX_PHASE - 1); break; case ACTION_T_RANDOM_PHASE_RANGE: // PhaseMin, PhaseMax if (action.random_phase_range.phaseMin >= MAX_PHASE) sLog.outErrorEventAI("Event %u Action %u attempts to set phaseMin >= %u. Phase mask cannot be used past phase %u.", i, j + 1, MAX_PHASE, MAX_PHASE - 1); if (action.random_phase_range.phaseMin >= MAX_PHASE) sLog.outErrorEventAI("Event %u Action %u attempts to set phaseMax >= %u. Phase mask cannot be used past phase %u.", i, j + 1, MAX_PHASE, MAX_PHASE - 1); if (action.random_phase_range.phaseMin >= action.random_phase_range.phaseMax) { sLog.outErrorEventAI("Event %u Action %u attempts to set phaseMax <= phaseMin.", i, j + 1); std::swap(action.random_phase_range.phaseMin, action.random_phase_range.phaseMax); // equal case processed at call } break; case ACTION_T_SUMMON_ID: if (!sCreatureStorage.LookupEntry<CreatureInfo>(action.summon_id.creatureId)) sLog.outErrorEventAI("Event %u Action %u uses nonexistent creature entry %u.", i, j + 1, action.summon_id.creatureId); if (action.summon_id.target >= TARGET_T_END) sLog.outErrorEventAI("Event %u Action %u uses incorrect Target type", i, j + 1); if (m_CreatureEventAI_Summon_Map.find(action.summon_id.spawnId) == m_CreatureEventAI_Summon_Map.end()) sLog.outErrorEventAI("Event %u Action %u summons missing CreatureEventAI_Summon %u", i, j + 1, action.summon_id.spawnId); break; case ACTION_T_KILLED_MONSTER: if (!sCreatureStorage.LookupEntry<CreatureInfo>(action.killed_monster.creatureId)) sLog.outErrorEventAI("Event %u Action %u uses nonexistent creature entry %u.", i, j + 1, action.killed_monster.creatureId); if (action.killed_monster.target >= TARGET_T_END) sLog.outErrorEventAI("Event %u Action %u uses incorrect Target type", i, j + 1); break; case ACTION_T_SET_INST_DATA: if (!(temp.event_flags & EFLAG_DIFFICULTY_ALL)) sLog.outErrorEventAI("Event %u Action %u. Cannot set instance data without difficulty event flags.", i, j + 1); if (action.set_inst_data.value > 4/*SPECIAL*/) sLog.outErrorEventAI("Event %u Action %u attempts to set instance data above encounter state 4. Custom case?", i, j + 1); break; case ACTION_T_SET_INST_DATA64: if (!(temp.event_flags & EFLAG_DIFFICULTY_ALL)) sLog.outErrorEventAI("Event %u Action %u. Cannot set instance data without difficulty event flags.", i, j + 1); if (action.set_inst_data64.target >= TARGET_T_END) sLog.outErrorEventAI("Event %u Action %u uses incorrect Target type", i, j + 1); break; case ACTION_T_UPDATE_TEMPLATE: if (!sCreatureStorage.LookupEntry<CreatureInfo>(action.update_template.creatureId)) sLog.outErrorEventAI("Event %u Action %u uses nonexistent creature entry %u.", i, j + 1, action.update_template.creatureId); break; case ACTION_T_SET_SHEATH: if (action.set_sheath.sheath >= MAX_SHEATH_STATE) { sLog.outErrorEventAI("Event %u Action %u uses wrong sheath state %u.", i, j + 1, action.set_sheath.sheath); action.set_sheath.sheath = SHEATH_STATE_UNARMED; } break; case ACTION_T_SET_INVINCIBILITY_HP_LEVEL: if (action.invincibility_hp_level.is_percent) { if (action.invincibility_hp_level.hp_level > 100) { sLog.outErrorEventAI("Event %u Action %u uses wrong percent value %u.", i, j + 1, action.invincibility_hp_level.hp_level); action.invincibility_hp_level.hp_level = 100; } } break; case ACTION_T_MOUNT_TO_ENTRY_OR_MODEL: if (action.mount.creatureId != 0 || action.mount.modelId != 0) { if (action.mount.creatureId && !sCreatureStorage.LookupEntry<CreatureInfo>(action.mount.creatureId)) { sLog.outErrorEventAI("Event %u Action %u uses nonexistent Creature entry %u.", i, j + 1, action.mount.creatureId); action.morph.creatureId = 0; } if (action.mount.modelId) { if (action.mount.creatureId) { sLog.outErrorEventAI("Event %u Action %u have unused ModelId %u with also set creature id %u.", i, j + 1, action.mount.modelId, action.mount.creatureId); action.mount.modelId = 0; } else if (!sCreatureDisplayInfoStore.LookupEntry(action.mount.modelId)) { sLog.outErrorEventAI("Event %u Action %u uses nonexistent ModelId %u.", i, j + 1, action.mount.modelId); action.mount.modelId = 0; } } } break; case ACTION_T_EVADE: // No Params case ACTION_T_FLEE_FOR_ASSIST: // No Params case ACTION_T_DIE: // No Params case ACTION_T_ZONE_COMBAT_PULSE: // No Params case ACTION_T_FORCE_DESPAWN: // Delay case ACTION_T_AUTO_ATTACK: // AllowAttackState (0 = stop attack, anything else means continue attacking) case ACTION_T_COMBAT_MOVEMENT: // AllowCombatMovement (0 = stop combat based movement, anything else continue attacking) case ACTION_T_RANGED_MOVEMENT: // Distance, Angle case ACTION_T_CALL_FOR_HELP: // Distance break; case ACTION_T_RANDOM_SAY: case ACTION_T_RANDOM_YELL: case ACTION_T_RANDOM_TEXTEMOTE: sLog.outErrorEventAI("Event %u Action %u currently unused ACTION type. Did you forget to update database?", i, j + 1); break; default: sLog.outErrorEventAI("Event %u Action %u have currently not checked at load action type (%u). Need check code update?", i, j + 1, temp.action[j].type); break; } } // Add to list m_CreatureEventAI_Event_Map[creature_id].push_back(temp); ++Count; } while (result->NextRow()); delete result; // post check for (uint32 i = 1; i < sCreatureStorage.GetMaxEntry(); ++i) { if (CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(i)) { bool ainame = strcmp(cInfo->AIName, "EventAI") == 0; bool hasevent = m_CreatureEventAI_Event_Map.find(i) != m_CreatureEventAI_Event_Map.end(); if (ainame && !hasevent) sLog.outErrorEventAI("EventAI not has script for creature entry (%u), but AIName = '%s'.", i, cInfo->AIName); else if (!ainame && hasevent) sLog.outErrorEventAI("EventAI has script for creature entry (%u), but AIName = '%s' instead 'EventAI'.", i, cInfo->AIName); } } CheckUnusedAITexts(); CheckUnusedAISummons(); sLog.outString(); sLog.outString(">> Loaded %u CreatureEventAI scripts", Count); } else { BarGoLink bar(1); bar.step(); sLog.outString(); sLog.outString(">> Loaded 0 CreatureEventAI scripts. DB table `creature_ai_scripts` is empty."); } }
//show info of player bool ChatHandler::HandlePInfoCommand(const char* args) { Player* target; uint64 target_guid; std::string target_name; uint32 parseGUID = MAKE_NEW_GUID(atol((char*)args), 0, HIGHGUID_PLAYER); if (sObjectMgr->GetPlayerNameByGUID(parseGUID, target_name)) { target = sObjectMgr->GetPlayerByLowGUID(parseGUID); target_guid = parseGUID; } else if (!extractPlayerTarget((char*)args, &target, &target_guid, &target_name)) return false; uint32 accId = 0; uint32 money = 0; uint32 total_player_time = 0; uint8 level = 0; uint32 latency = 0; uint8 race; uint8 Class; int64 muteTime = 0; int64 banTime = -1; uint32 mapId; uint32 areaId; uint32 phase = 0; // get additional information from Player object if (target) { // check online security if (HasLowerSecurity(target, 0)) return false; accId = target->GetSession()->GetAccountId(); money = target->GetMoney(); total_player_time = target->GetTotalPlayedTime(); level = target->getLevel(); latency = target->GetSession()->GetLatency(); race = target->getRace(); Class = target->getClass(); muteTime = target->GetSession()->m_muteTime; mapId = target->GetMapId(); areaId = target->GetAreaId(); phase = target->GetPhaseMask(); } // get additional information from DB else { // check offline security if (HasLowerSecurity(NULL, target_guid)) return false; PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_PINFO); stmt->setUInt32(0, GUID_LOPART(target_guid)); PreparedQueryResult result = CharacterDatabase.Query(stmt); if (!result) return false; Field* fields = result->Fetch(); total_player_time = fields[0].GetUInt32(); level = fields[1].GetUInt8(); money = fields[2].GetUInt32(); accId = fields[3].GetUInt32(); race = fields[4].GetUInt8(); Class = fields[5].GetUInt8(); mapId = fields[6].GetUInt16(); areaId = fields[7].GetUInt16(); } std::string username = GetTrinityString(LANG_ERROR); std::string email = GetTrinityString(LANG_ERROR); std::string last_ip = GetTrinityString(LANG_ERROR); uint32 security = 0; std::string last_login = GetTrinityString(LANG_ERROR); PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_PINFO); stmt->setInt32(0, int32(realmID)); stmt->setUInt32(1, accId); PreparedQueryResult result = LoginDatabase.Query(stmt); if (result) { Field* fields = result->Fetch(); username = fields[0].GetString(); security = fields[1].GetUInt8(); email = fields[2].GetString(); muteTime = fields[5].GetUInt64(); if (email.empty()) email = "-"; if (!m_session || m_session->GetSecurity() >= AccountTypes(security)) { last_ip = fields[3].GetString(); last_login = fields[4].GetString(); uint32 ip = inet_addr(last_ip.c_str()); #if TRINITY_ENDIAN == BIGENDIAN EndianConvertReverse(ip); #endif PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_IP2NATION_COUNTRY); stmt->setUInt32(0, ip); PreparedQueryResult result2 = WorldDatabase.Query(stmt); if (result2) { Field* fields2 = result2->Fetch(); last_ip.append(" ("); last_ip.append(fields2[0].GetString()); last_ip.append(")"); } } else { last_ip = "-"; last_login = "******"; } } std::string nameLink = playerLink(target_name); PSendSysMessage(LANG_PINFO_ACCOUNT, (target?"":GetTrinityString(LANG_OFFLINE)), nameLink.c_str(), GUID_LOPART(target_guid), username.c_str(), accId, email.c_str(), security, last_ip.c_str(), last_login.c_str(), latency); std::string bannedby = "unknown"; std::string banreason = ""; stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_PINFO_BANS); stmt->setUInt32(0, accId); PreparedQueryResult result2 = LoginDatabase.Query(stmt); if (!result2) { stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PINFO_BANS); stmt->setUInt32(0, GUID_LOPART(target_guid)); result2 = CharacterDatabase.Query(stmt); } if (result2) { Field* fields = result2->Fetch(); banTime = int64(fields[1].GetBool() ? 0 : fields[0].GetUInt32()); bannedby = fields[2].GetString(); banreason = fields[3].GetString(); } if (muteTime > 0) PSendSysMessage(LANG_PINFO_MUTE, secsToTimeString(muteTime - time(NULL), true).c_str()); if (banTime >= 0) PSendSysMessage(LANG_PINFO_BAN, banTime > 0 ? secsToTimeString(banTime - time(NULL), true).c_str() : "permanently", bannedby.c_str(), banreason.c_str()); std::string race_s, Class_s; switch (race) { case RACE_HUMAN: race_s = "Human"; break; case RACE_ORC: race_s = "Orc"; break; case RACE_DWARF: race_s = "Dwarf"; break; case RACE_NIGHTELF: race_s = "Night Elf"; break; case RACE_UNDEAD_PLAYER: race_s = "Undead"; break; case RACE_TAUREN: race_s = "Tauren"; break; case RACE_GNOME: race_s = "Gnome"; break; case RACE_TROLL: race_s = "Troll"; break; case RACE_BLOODELF: race_s = "Blood Elf"; break; case RACE_DRAENEI: race_s = "Draenei"; break; } switch (Class) { case CLASS_WARRIOR: Class_s = "Warrior"; break; case CLASS_PALADIN: Class_s = "Paladin"; break; case CLASS_HUNTER: Class_s = "Hunter"; break; case CLASS_ROGUE: Class_s = "Rogue"; break; case CLASS_PRIEST: Class_s = "Priest"; break; case CLASS_DEATH_KNIGHT: Class_s = "Death Knight"; break; case CLASS_SHAMAN: Class_s = "Shaman"; break; case CLASS_MAGE: Class_s = "Mage"; break; case CLASS_WARLOCK: Class_s = "Warlock"; break; case CLASS_DRUID: Class_s = "Druid"; break; } std::string timeStr = secsToTimeString(total_player_time, true, true); uint32 gold = money /GOLD; uint32 silv = (money % GOLD) / SILVER; uint32 copp = (money % GOLD) % SILVER; PSendSysMessage(LANG_PINFO_LEVEL, race_s.c_str(), Class_s.c_str(), timeStr.c_str(), level, gold, silv, copp); // Add map, zone, subzone and phase to output int locale = GetSessionDbcLocale(); std::string areaName = "<unknown>"; std::string zoneName = ""; MapEntry const* map = sMapStore.LookupEntry(mapId); AreaTableEntry const* area = GetAreaEntryByAreaID(areaId); if (area) { areaName = area->area_name[locale]; AreaTableEntry const* zone = GetAreaEntryByAreaID(area->zone); if (zone) zoneName = zone->area_name[locale]; } if (target) { if (!zoneName.empty()) PSendSysMessage(LANG_PINFO_MAP_ONLINE, map->name[locale], zoneName.c_str(), areaName.c_str(), phase); else PSendSysMessage(LANG_PINFO_MAP_ONLINE, map->name[locale], areaName.c_str(), "<unknown>", phase); } else PSendSysMessage(LANG_PINFO_MAP_OFFLINE, map->name[locale], areaName.c_str()); return true; }
bool ChatHandler::ExecuteCommandInTable(ChatCommand* table, const char* text, std::string const& fullcmd) { char const* oldtext = text; std::string cmd = ""; while (*text != ' ' && *text != '\0') { cmd += *text; ++text; } while (*text == ' ') ++text; for (uint32 i = 0; table[i].Name != NULL; ++i) { if (!hasStringAbbr(table[i].Name, cmd.c_str())) continue; bool match = false; if (strlen(table[i].Name) > cmd.length()) { for (uint32 j = 0; table[j].Name != NULL; ++j) { if (!hasStringAbbr(table[j].Name, cmd.c_str())) continue; if (strcmp(table[j].Name, cmd.c_str()) == 0) { match = true; break; } } } if (match) continue; // select subcommand from child commands list if (table[i].ChildCommands != NULL) { if (!ExecuteCommandInTable(table[i].ChildCommands, text, fullcmd)) { if (text[0] != '\0') SendSysMessage(LANG_NO_SUBCMD); else SendSysMessage(LANG_CMD_SYNTAX); ShowHelpForCommand(table[i].ChildCommands, text); } return true; } // must be available and have handler if (!table[i].Handler || !isAvailable(table[i])) continue; SetSentErrorMessage(false); // table[i].Name == "" is special case: send original command to handler if ((table[i].Handler)(this, table[i].Name[0] != '\0' ? text : oldtext)) { if (!m_session) // ignore console return true; Player* player = m_session->GetPlayer(); if (!AccountMgr::IsPlayerAccount(m_session->GetSecurity())) { uint64 guid = player->GetTarget(); uint32 areaId = player->GetAreaId(); std::string areaName = "Unknown"; std::string zoneName = "Unknown"; if (AreaTableEntry const* area = GetAreaEntryByAreaID(areaId)) { areaName = area->area_name; if (AreaTableEntry const* zone = GetAreaEntryByAreaID(area->zone)) zoneName = zone->area_name; } sLog->outCommand(m_session->GetAccountId(), "Command: %s [Player: %s (Guid: %u) (Account: %u) X: %f Y: %f Z: %f Map: %u (%s) Area: %u (%s) Zone: %s Selected %s: %s (GUID: %u)]", fullcmd.c_str(), player->GetName().c_str(), GUID_LOPART(player->GetGUID()), m_session->GetAccountId(), player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(), player->GetMapId(), player->GetMap() ? player->GetMap()->GetMapName() : "Unknown", areaId, areaName.c_str(), zoneName.c_str(), GetLogNameForGuid(guid), (player->GetSelectedUnit()) ? player->GetSelectedUnit()->GetName().c_str() : "", GUID_LOPART(guid)); } } // some commands have custom error messages. Don't send the default one in these cases. else if (!HasSentErrorMessage()) { if (!table[i].Help.empty()) SendSysMessage(table[i].Help.c_str()); else SendSysMessage(LANG_CMD_SYNTAX); } return true; } return false; }
void WorldSession::HandleMovementOpcodes(WorldPacket& recvPacket) { uint16 opcode = recvPacket.GetOpcode(); Unit* mover = _player->m_mover; ASSERT(mover != NULL); // there must always be a mover Player* plrMover = mover->ToPlayer(); // ignore, waiting processing in WorldSession::HandleMoveWorldportAckOpcode and WorldSession::HandleMoveTeleportAck if (plrMover && plrMover->IsBeingTeleported()) { recvPacket.rfinish(); // prevent warnings spam return; } /* extract packet */ MovementInfo movementInfo; GetPlayer()->ReadMovementInfo(recvPacket, &movementInfo); // prevent tampered movement data if (movementInfo.guid != mover->GetGUID()) { TC_LOG_ERROR("network", "HandleMovementOpcodes: guid error"); return; } if (!movementInfo.pos.IsPositionValid()) { TC_LOG_ERROR("network", "HandleMovementOpcodes: Invalid Position"); return; } /* handle special cases */ if (movementInfo.transport.guid) { // transports size limited // (also received at zeppelin leave by some reason with t_* as absolute in continent coordinates, can be safely skipped) if (movementInfo.transport.pos.GetPositionX() > 50 || movementInfo.transport.pos.GetPositionY() > 50 || movementInfo.transport.pos.GetPositionZ() > 50) { recvPacket.rfinish(); // prevent warnings spam return; } if (!Trinity::IsValidMapCoord(movementInfo.pos.GetPositionX() + movementInfo.transport.pos.GetPositionX(), movementInfo.pos.GetPositionY() + movementInfo.transport.pos.GetPositionY(), movementInfo.pos.GetPositionZ() + movementInfo.transport.pos.GetPositionZ(), movementInfo.pos.GetOrientation() + movementInfo.transport.pos.GetOrientation())) { recvPacket.rfinish(); // prevent warnings spam return; } // if we boarded a transport, add us to it if (plrMover) { if (!plrMover->GetTransport()) { if (Transport* transport = plrMover->GetMap()->GetTransport(movementInfo.transport.guid)) { plrMover->m_transport = transport; transport->AddPassenger(plrMover); } } else if (plrMover->GetTransport()->GetGUID() != movementInfo.transport.guid) { bool foundNewTransport = false; plrMover->m_transport->RemovePassenger(plrMover); if (Transport* transport = plrMover->GetMap()->GetTransport(movementInfo.transport.guid)) { foundNewTransport = true; plrMover->m_transport = transport; transport->AddPassenger(plrMover); } if (!foundNewTransport) { plrMover->m_transport = NULL; movementInfo.ResetTransport(); } } } if (!mover->GetTransport() && !mover->GetVehicle()) { GameObject* go = mover->GetMap()->GetGameObject(movementInfo.transport.guid); if (!go || go->GetGoType() != GAMEOBJECT_TYPE_TRANSPORT) movementInfo.transport.guid = 0; } } else if (plrMover && plrMover->GetTransport()) // if we were on a transport, leave { plrMover->m_transport->RemovePassenger(plrMover); plrMover->m_transport = NULL; movementInfo.ResetTransport(); } // fall damage generation (ignore in flight case that can be triggered also at lags in moment teleportation to another map). if (opcode == MSG_MOVE_FALL_LAND && plrMover && !plrMover->IsInFlight()) plrMover->HandleFall(movementInfo); if (plrMover && ((movementInfo.flags & MOVEMENTFLAG_SWIMMING) != 0) != plrMover->IsInWater()) { // now client not include swimming flag in case jumping under water plrMover->SetInWater(!plrMover->IsInWater() || plrMover->GetBaseMap()->IsUnderWater(movementInfo.pos.GetPositionX(), movementInfo.pos.GetPositionY(), movementInfo.pos.GetPositionZ())); } uint32 mstime = getMSTime(); /*----------------------*/ if (m_clientTimeDelay == 0) m_clientTimeDelay = mstime - movementInfo.time; /* process position-change */ movementInfo.time = movementInfo.time + m_clientTimeDelay + MOVEMENT_PACKET_TIME_DELAY; movementInfo.guid = mover->GetGUID(); mover->m_movementInfo = movementInfo; /*----------------------*/ /* process position-change */ // this is almost never true (not sure why it is sometimes, but it is), normally use mover->IsVehicle() if (mover->GetVehicle()) { mover->SetOrientation(movementInfo.pos.GetOrientation()); return; } mover->UpdatePosition(movementInfo.pos); WorldPacket data(SMSG_PLAYER_MOVE, recvPacket.size()); mover->WriteMovementInfo(data); mover->SendMessageToSet(&data, _player); if (plrMover) // nothing is charmed, or player charmed { plrMover->UpdateFallInformationIfNeed(movementInfo, opcode); AreaTableEntry const* zone = GetAreaEntryByAreaID(plrMover->GetAreaId()); float depth = zone ? zone->MaxDepth : -500.0f; if (movementInfo.pos.GetPositionZ() < depth) { if (!(plrMover->GetBattleground() && plrMover->GetBattleground()->HandlePlayerUnderMap(_player))) { // NOTE: this is actually called many times while falling // even after the player has been teleported away /// @todo discard movement packets after the player is rooted if (plrMover->IsAlive()) { plrMover->EnvironmentalDamage(DAMAGE_FALL_TO_VOID, GetPlayer()->GetMaxHealth()); // player can be alive if GM/etc // change the death state to CORPSE to prevent the death timer from // starting in the next player update if (!plrMover->IsAlive()) plrMover->KillPlayer(); } } } } }
static bool HandleGPSCommand(ChatHandler* handler, const char *args) { WorldObject* obj = NULL; if (*args) { uint64 guid = handler->extractGuidFromLink((char*)args); if (guid) obj = (WorldObject*)ObjectAccessor::GetObjectByTypeMask(*handler->GetSession()->GetPlayer(), guid, TYPEMASK_UNIT|TYPEMASK_GAMEOBJECT); if (!obj) { handler->SendSysMessage(LANG_PLAYER_NOT_FOUND); handler->SetSentErrorMessage(true); return false; } } else { obj = handler->getSelectedUnit(); if (!obj) { handler->SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE); handler->SetSentErrorMessage(true); return false; } } CellCoord cell_val = Trinity::ComputeCellCoord(obj->GetPositionX(), obj->GetPositionY()); Cell cell(cell_val); uint32 zone_id, area_id; obj->GetZoneAndAreaId(zone_id, area_id); MapEntry const* mapEntry = sMapStore.LookupEntry(obj->GetMapId()); AreaTableEntry const* zoneEntry = GetAreaEntryByAreaID(zone_id); AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(area_id); float zone_x = obj->GetPositionX(); float zone_y = obj->GetPositionY(); Map2ZoneCoordinates(zone_x, zone_y, zone_id); Map const* map = obj->GetMap(); float ground_z = map->GetHeight(obj->GetPositionX(), obj->GetPositionY(), MAX_HEIGHT); float floor_z = map->GetHeight(obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ()); GridCoord p = Trinity::ComputeGridCoord(obj->GetPositionX(), obj->GetPositionY()); // 63? WHY? int gx = 63 - p.x_coord; int gy = 63 - p.y_coord; uint32 have_map = Map::ExistMap(obj->GetMapId(), gx, gy) ? 1 : 0; uint32 have_vmap = Map::ExistVMap(obj->GetMapId(), gx, gy) ? 1 : 0; if (have_vmap) { if (map->IsOutdoors(obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ())) handler->PSendSysMessage("You are outdoors"); else handler->PSendSysMessage("You are indoors"); } else handler->PSendSysMessage("no VMAP available for area info"); handler->PSendSysMessage(LANG_MAP_POSITION, obj->GetMapId(), (mapEntry ? mapEntry->name : "<unknown>"), zone_id, (zoneEntry ? zoneEntry->area_name : "<unknown>"), area_id, (areaEntry ? areaEntry->area_name : "<unknown>"), obj->GetPhaseMask(), obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ(), obj->GetOrientation(), cell.GridX(), cell.GridY(), cell.CellX(), cell.CellY(), obj->GetInstanceId(), zone_x, zone_y, ground_z, floor_z, have_map, have_vmap); LiquidData liquid_status; ZLiquidStatus res = map->getLiquidStatus(obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ(), MAP_ALL_LIQUIDS, &liquid_status); if (res) { handler->PSendSysMessage(LANG_LIQUID_STATUS, liquid_status.level, liquid_status.depth_level, liquid_status.type, res); } return true; }
//teleport at coordinates static bool HandleGoZoneXYCommand(ChatHandler* handler, char const* args) { if (!*args) return false; Player* player = handler->GetSession()->GetPlayer(); char* zoneX = strtok((char*)args, " "); char* zoneY = strtok(NULL, " "); char* tail = strtok(NULL, ""); char* id = handler->extractKeyFromLink(tail, "Harea"); // string or [name] Shift-click form |color|Harea:area_id|h[name]|h|r if (!zoneX || !zoneY) return false; float x = (float)atof(zoneX); float y = (float)atof(zoneY); // prevent accept wrong numeric args if ((x == 0.0f && *zoneX != '0') || (y == 0.0f && *zoneY != '0')) return false; uint32 areaId = id ? (uint32)atoi(id) : player->GetZoneId(); AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(areaId); if (x < 0 || x > 100 || y < 0 || y > 100 || !areaEntry) { handler->PSendSysMessage(LANG_INVALID_ZONE_COORD, x, y, areaId); handler->SetSentErrorMessage(true); return false; } // update to parent zone if exist (client map show only zones without parents) AreaTableEntry const* zoneEntry = areaEntry->zone ? GetAreaEntryByAreaID(areaEntry->zone) : areaEntry; Map const* map = sMapMgr->CreateBaseMap(zoneEntry->mapid); if (map->Instanceable()) { handler->PSendSysMessage(LANG_INVALID_ZONE_MAP, areaEntry->ID, areaEntry->area_name[handler->GetSessionDbcLocale()], map->GetId(), map->GetMapName()); handler->SetSentErrorMessage(true); return false; } Zone2MapCoordinates(x, y, zoneEntry->ID); if (!MapManager::IsValidMapCoord(zoneEntry->mapid, x, y)) { handler->PSendSysMessage(LANG_INVALID_TARGET_COORD, x, y, zoneEntry->mapid); handler->SetSentErrorMessage(true); return false; } // stop flight if need if (player->IsInFlight()) { player->GetMotionMaster()->MovementExpired(); player->CleanupAfterTaxiFlight(); } // save only in non-flight case else player->SaveRecallPosition(); float z = std::max(map->GetHeight(x, y, MAX_HEIGHT), map->GetWaterLevel(x, y)); player->TeleportTo(zoneEntry->mapid, x, y, z, player->GetOrientation()); return true; }
void AntiCheat::DoAntiCheatAction(AntiCheatCheck checkType, std::string reason) { AntiCheatConfig const* config = _FindConfig(checkType); if (!config) return; if (m_lastactiontime.find(checkType) == m_lastactiontime.end()) m_lastactiontime.insert(std::make_pair(checkType, 0)); if (WorldTimer::getMSTime() - m_lastactiontime[checkType] >= sWorld.getConfig(CONFIG_UINT32_ANTICHEAT_ACTION_DELAY) * 1000) { m_lastactiontime[checkType] = WorldTimer::getMSTime(); std::string name = GetPlayer()->GetName(); std::string namechat; MapEntry const* mapEntry = sMapStore.LookupEntry(GetPlayer()->GetMapId()); uint32 zone_id, area_id; GetPlayer()->GetZoneAndAreaId(zone_id,area_id); AreaTableEntry const* zoneEntry = GetAreaEntryByAreaID(zone_id); AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(area_id); char buffer[255]; namechat.clear(); namechat.append(" |cddff0000|Hplayer:"); namechat.append(name); namechat.append("|h["); namechat.append(name); namechat.append("]|h|r "); sprintf(buffer," Map %u (%s), Zone %u (%s) Area |cbbdd0000|Harea:%u|h[%s]|h|r ", GetPlayer()->GetMapId(), (mapEntry ? mapEntry->name[sWorld.GetDefaultDbcLocale()] : "<unknown>" ), zone_id, (zoneEntry ? zoneEntry->area_name[sWorld.GetDefaultDbcLocale()] : "<unknown>" ), area_id, (areaEntry ? areaEntry->area_name[sWorld.GetDefaultDbcLocale()] : "<unknown>" )); if (m_currentspellID) { SpellEntry const *spellInfo = sSpellStore.LookupEntry(m_currentspellID); if (spellInfo) sprintf(buffer,", last spell |cbbee0000|Hspell:%u|h[%s]|h|r ", m_currentspellID, spellInfo->SpellName[sWorld.GetDefaultDbcLocale()]); } namechat.append(buffer); for (int i=0; i < ANTICHEAT_ACTIONS; ++i ) { AntiCheatAction actionID = AntiCheatAction(config->actionType[i]); switch(actionID) { case ANTICHEAT_ACTION_KICK: GetPlayer()->GetSession()->KickPlayer(); break; case ANTICHEAT_ACTION_BAN: sWorld.BanAccount(BAN_CHARACTER, name.c_str(), config->actionParam[i], reason, "AntiCheat"); break; case ANTICHEAT_ACTION_SHEEP: { uint32 sheepAura = 28272; switch (urand(0,6)) { case 0: sheepAura = 118; break; case 1: sheepAura = 28271; break; case 2: sheepAura = 28272; break; case 3: sheepAura = 61025; break; case 4: sheepAura = 61721; break; case 5: sheepAura = 71319; break; default: break; } GetPlayer()->_AddAura(sheepAura,config->actionParam[i]); if (checkType == CHECK_MOVEMENT_FLY || GetPlayer()->HasAuraType(SPELL_AURA_FLY)) { GetPlayer()->CastSpell(GetPlayer(), 55001, true); } } break; case ANTICHEAT_ACTION_STUN: GetPlayer()->_AddAura(13005,config->actionParam[i]); break; case ANTICHEAT_ACTION_SICKNESS: GetPlayer()->_AddAura(15007,config->actionParam[i]); break; case ANTICHEAT_ACTION_ANNOUNCE_GM: sWorld.SendWorldTextWithSecurity(AccountTypes(config->actionParam[i]), config->messageNum, namechat.c_str(), config->description.c_str()); break; case ANTICHEAT_ACTION_ANNOUNCE_ALL: sWorld.SendWorldText(config->messageNum, name.c_str(), config->description.c_str()); break; case ANTICHEAT_ACTION_LOG: case ANTICHEAT_ACTION_NULL: default: break; } } } if (config->actionType[0] != ANTICHEAT_ACTION_NULL) { if (reason == "" ) { sLog.outError("AntiCheat action log: Missing Reason parameter!"); return; } const char* playerName = GetPlayer()->GetName(); if ( !playerName ) { sLog.outError("AntiCheat action log: Player with no name?"); return; } CharacterDatabase.PExecute("REPLACE INTO `anticheat_log` (`guid`, `playername`, `checktype`, `alarm_time`, `action1`, `action2`, `reason`)" "VALUES ('%u','%s','%u',NOW(),'%u','%u','%s')", GetPlayer()->GetObjectGuid().GetCounter(), playerName, checkType, config->actionType[0], config->actionType[1], reason.c_str()); } }
// ------------------- void CreatureEventAIMgr::LoadCreatureEventAI_Scripts() { //Drop Existing EventAI List m_CreatureEventAI_Event_Map.clear(); // Gather event data QueryResult_AutoPtr result = WorldDatabase.Query("SELECT id, creature_id, event_type, event_inverse_phase_mask, event_chance, event_flags, " "event_param1, event_param2, event_param3, event_param4, " "action1_type, action1_param1, action1_param2, action1_param3, " "action2_type, action2_param1, action2_param2, action2_param3, " "action3_type, action3_param1, action3_param2, action3_param3 " "FROM creature_ai_scripts"); if (result) { barGoLink bar(result->GetRowCount()); uint32 Count = 0; do { bar.step(); Field *fields = result->Fetch(); CreatureEventAI_Event temp; temp.event_id = EventAI_Type(fields[0].GetUInt32()); uint32 i = temp.event_id; temp.creature_id = fields[1].GetUInt32(); uint32 creature_id = temp.creature_id; uint32 e_type = fields[2].GetUInt32(); //Report any errors in event if (e_type >= EVENT_T_END) { sLog.outErrorDb("CreatureEventAI: Event %u have wrong type (%u), skipping.", i,e_type); continue; } temp.event_type = EventAI_Type(e_type); temp.event_inverse_phase_mask = fields[3].GetUInt32(); temp.event_chance = fields[4].GetUInt8(); temp.event_flags = fields[5].GetUInt8(); temp.raw.param1 = fields[6].GetUInt32(); temp.raw.param2 = fields[7].GetUInt32(); temp.raw.param3 = fields[8].GetUInt32(); temp.raw.param4 = fields[9].GetUInt32(); //Creature does not exist in database if (!sCreatureStorage.LookupEntry<CreatureInfo>(temp.creature_id)) { sLog.outErrorDb("CreatureEventAI: Event %u has script for non-existing creature entry (%u), skipping.", i, temp.creature_id); continue; } //No chance of this event occuring if (temp.event_chance == 0) sLog.outErrorDb("CreatureEventAI: Event %u has 0 percent chance. Event will never trigger!", i); //Chance above 100, force it to be 100 else if (temp.event_chance > 100) { sLog.outErrorDb("CreatureEventAI: Creature %u are using event %u with more than 100 percent chance. Adjusting to 100 percent.", temp.creature_id, i); temp.event_chance = 100; } //Individual event checks switch (temp.event_type) { case EVENT_T_TIMER: case EVENT_T_TIMER_OOC: if (temp.timer.initialMax < temp.timer.initialMin) sLog.outErrorDb("CreatureEventAI: Creature %u are using timed event(%u) with param2 < param1 (InitialMax < InitialMin). Event will never repeat.", temp.creature_id, i); if (temp.timer.repeatMax < temp.timer.repeatMin) sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i); break; case EVENT_T_HP: case EVENT_T_MANA: case EVENT_T_TARGET_HP: case EVENT_T_TARGET_MANA: if (temp.percent_range.percentMax > 100) sLog.outErrorDb("CreatureEventAI: Creature %u are using percentage event(%u) with param2 (MinPercent) > 100. Event will never trigger! ", temp.creature_id, i); if (temp.percent_range.percentMax <= temp.percent_range.percentMin) sLog.outErrorDb("CreatureEventAI: Creature %u are using percentage event(%u) with param1 <= param2 (MaxPercent <= MinPercent). Event will never trigger! ", temp.creature_id, i); if (temp.event_flags & EFLAG_REPEATABLE && !temp.percent_range.repeatMin && !temp.percent_range.repeatMax) { sLog.outErrorDb("CreatureEventAI: Creature %u has param3 and param4=0 (RepeatMin/RepeatMax) but cannot be repeatable without timers. Removing EFLAG_REPEATABLE for event %u.", temp.creature_id, i); temp.event_flags &= ~EFLAG_REPEATABLE; } break; case EVENT_T_SPELLHIT: if (temp.spell_hit.spellId) { SpellEntry const* pSpell = sSpellStore.LookupEntry(temp.spell_hit.spellId); if (!pSpell) { sLog.outErrorDb("CreatureEventAI: Creature %u has non-existant SpellID(%u) defined in event %u.", temp.creature_id, temp.spell_hit.spellId, i); continue; } if ((temp.spell_hit.schoolMask & pSpell->SchoolMask) != pSpell->SchoolMask) sLog.outErrorDb("CreatureEventAI: Creature %u has param1(spellId %u) but param2 is not -1 and not equal to spell's school mask. Event %u can never trigger.", temp.creature_id, temp.spell_hit.schoolMask, i); } if (!temp.spell_hit.schoolMask) sLog.outErrorDb("CreatureEventAI: Creature %u is using invalid SpellSchoolMask(%u) defined in event %u.", temp.creature_id, temp.spell_hit.schoolMask, i); if (temp.spell_hit.repeatMax < temp.spell_hit.repeatMin) sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i); break; case EVENT_T_RANGE: if (temp.range.maxDist < temp.range.minDist) sLog.outErrorDb("CreatureEventAI: Creature %u are using event(%u) with param2 < param1 (MaxDist < MinDist). Event will never repeat.", temp.creature_id, i); if (temp.range.repeatMax < temp.range.repeatMin) sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i); break; case EVENT_T_OOC_LOS: if (temp.ooc_los.repeatMax < temp.ooc_los.repeatMin) sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i); break; case EVENT_T_SPAWNED: switch(temp.spawned.condition) { case SPAWNED_EVENT_ALWAY: break; case SPAWNED_EVENT_MAP: if (!sMapStore.LookupEntry(temp.spawned.conditionValue1)) sLog.outErrorDb("CreatureEventAI: Creature %u are using spawned event(%u) with param1 = %u 'map specific' but with not existed map (%u) in param2. Event will never repeat.", temp.creature_id, i, temp.spawned.condition, temp.spawned.conditionValue1); break; case SPAWNED_EVENT_ZONE: if (!GetAreaEntryByAreaID(temp.spawned.conditionValue1)) sLog.outErrorDb("CreatureEventAI: Creature %u are using spawned event(%u) with param1 = %u 'area specific' but with not existed area (%u) in param2. Event will never repeat.", temp.creature_id, i, temp.spawned.condition, temp.spawned.conditionValue1); default: sLog.outErrorDb("CreatureEventAI: Creature %u are using invalid spawned event %u mode (%u) in param1", temp.creature_id, i, temp.spawned.condition); break; } break; case EVENT_T_FRIENDLY_HP: if (temp.friendly_hp.repeatMax < temp.friendly_hp.repeatMin) sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i); break; case EVENT_T_FRIENDLY_IS_CC: if (temp.friendly_is_cc.repeatMax < temp.friendly_is_cc.repeatMin) sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i); break; case EVENT_T_FRIENDLY_MISSING_BUFF: { SpellEntry const* pSpell = sSpellStore.LookupEntry(temp.spell_hit.spellId); if (!pSpell) { sLog.outErrorDb("CreatureEventAI: Creature %u has non-existant SpellID(%u) defined in event %u.", temp.creature_id, temp.spell_hit.spellId, i); continue; } if (temp.friendly_buff.repeatMax < temp.friendly_buff.repeatMin) sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i); break; } case EVENT_T_KILL: if (temp.kill.repeatMax < temp.kill.repeatMin) sLog.outErrorDb("CreatureEventAI: Creature %u are using event(%u) with param2 < param1 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i); break; case EVENT_T_TARGET_CASTING: if (temp.target_casting.repeatMax < temp.target_casting.repeatMin) sLog.outErrorDb("CreatureEventAI: Creature %u are using event(%u) with param2 < param1 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i); break; case EVENT_T_SUMMONED_UNIT: if (!sCreatureStorage.LookupEntry<CreatureInfo>(temp.summon_unit.creatureId)) sLog.outErrorDb("CreatureEventAI: Creature %u are using event(%u) with not existed creature template id (%u) in param1, skipped.", temp.creature_id, i, temp.summon_unit.creatureId); if (temp.summon_unit.repeatMax < temp.summon_unit.repeatMin) sLog.outErrorDb("CreatureEventAI: Creature %u are using event(%u) with param2 < param1 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i); break; case EVENT_T_QUEST_ACCEPT: case EVENT_T_QUEST_COMPLETE: if (!objmgr.GetQuestTemplate(temp.quest.questId)) sLog.outErrorDb("CreatureEventAI: Creature %u are using event(%u) with not existed qyest id (%u) in param1, skipped.", temp.creature_id, i, temp.quest.questId); sLog.outErrorDb("CreatureEventAI: Creature %u using not implemented event (%u) in event %u.", temp.creature_id, temp.event_id, i); continue; case EVENT_T_AGGRO: case EVENT_T_DEATH: case EVENT_T_EVADE: case EVENT_T_REACHED_HOME: { if (temp.event_flags & EFLAG_REPEATABLE) { sLog.outErrorDb("CreatureEventAI: Creature %u has EFLAG_REPEATABLE set. Event can never be repeatable. Removing flag for event %u.", temp.creature_id, i); temp.event_flags &= ~EFLAG_REPEATABLE; } break; } case EVENT_T_RECEIVE_EMOTE: { if (!sEmotesTextStore.LookupEntry(temp.receive_emote.emoteId)) { sLog.outErrorDb("CreatureEventAI: Creature %u using event %u: param1 (EmoteTextId: %u) are not valid.",temp.creature_id, i, temp.receive_emote.emoteId); continue; } if (!PlayerCondition::IsValid(ConditionType(temp.receive_emote.condition), temp.receive_emote.conditionValue1, temp.receive_emote.conditionValue2)) { sLog.outErrorDb("CreatureEventAI: Creature %u using event %u: param2 (Condition: %u) are not valid.",temp.creature_id, i, temp.receive_emote.condition); continue; } if (!(temp.event_flags & EFLAG_REPEATABLE)) { sLog.outErrorDb("CreatureEventAI: Creature %u using event %u: EFLAG_REPEATABLE not set. Event must always be repeatable. Flag applied.", temp.creature_id, i); temp.event_flags |= EFLAG_REPEATABLE; } break; } case EVENT_T_BUFFED: case EVENT_T_TARGET_BUFFED: { SpellEntry const* pSpell = sSpellStore.LookupEntry(temp.buffed.spellId); if (!pSpell) { sLog.outErrorDb("CreatureEventAI: Creature %u has non-existant SpellID(%u) defined in event %u.", temp.creature_id, temp.spell_hit.spellId, i); continue; } if (temp.buffed.repeatMax < temp.buffed.repeatMin) sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i); break; } default: sLog.outErrorDb("CreatureEventAI: Creature %u using not checked at load event (%u) in event %u. Need check code update?", temp.creature_id, temp.event_id, i); break; } for (uint32 j = 0; j < MAX_ACTIONS; j++) { uint16 action_type = fields[10+(j*4)].GetUInt16(); if (action_type >= ACTION_T_END) { sLog.outErrorDb("CreatureEventAI: Event %u Action %u has incorrect action type (%u), replace by ACTION_T_NONE.", i, j+1, action_type); temp.action[j].type = ACTION_T_NONE; continue; } CreatureEventAI_Action& action = temp.action[j]; action.type = EventAI_ActionType(action_type); action.raw.param1 = fields[11+(j*4)].GetUInt32(); action.raw.param2 = fields[12+(j*4)].GetUInt32(); action.raw.param3 = fields[13+(j*4)].GetUInt32(); //Report any errors in actions switch (action.type) { case ACTION_T_NONE: break; case ACTION_T_TEXT: { if (action.text.TextId1 < 0) { if (m_CreatureEventAI_TextMap.find(action.text.TextId1) == m_CreatureEventAI_TextMap.end()) sLog.outErrorDb("CreatureEventAI: Event %u Action %u param1 refrences non-existing entry in texts table.", i, j+1); } if (action.text.TextId2 < 0) { if (m_CreatureEventAI_TextMap.find(action.text.TextId2) == m_CreatureEventAI_TextMap.end()) sLog.outErrorDb("CreatureEventAI: Event %u Action %u param2 refrences non-existing entry in texts table.", i, j+1); if (!action.text.TextId1) sLog.outErrorDb("CreatureEventAI: Event %u Action %u has param2, but param1 is not set. Required for randomized text.", i, j+1); } if (action.text.TextId3 < 0) { if (m_CreatureEventAI_TextMap.find(action.text.TextId3) == m_CreatureEventAI_TextMap.end()) sLog.outErrorDb("CreatureEventAI: Event %u Action %u param3 refrences non-existing entry in texts table.", i, j+1); if (!action.text.TextId1 || !action.text.TextId2) sLog.outErrorDb("CreatureEventAI: Event %u Action %u has param3, but param1 and/or param2 is not set. Required for randomized text.", i, j+1); } break; } case ACTION_T_SET_FACTION: if (action.set_faction.factionId !=0 && !sFactionStore.LookupEntry(action.set_faction.factionId)) { sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent FactionId %u.", i, j+1, action.set_faction.factionId); action.set_faction.factionId = 0; } break; case ACTION_T_MORPH_TO_ENTRY_OR_MODEL: if (action.morph.creatureId !=0 || action.morph.modelId !=0) { if (action.morph.creatureId && !sCreatureStorage.LookupEntry<CreatureInfo>(action.morph.creatureId)) { sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant Creature entry %u.", i, j+1, action.morph.creatureId); action.morph.creatureId = 0; } if (action.morph.modelId) { if (action.morph.creatureId) { sLog.outErrorDb("CreatureEventAI: Event %u Action %u have unused ModelId %u with also set creature id %u.", i, j+1, action.morph.modelId,action.morph.creatureId); action.morph.modelId = 0; } else if (!sCreatureDisplayInfoStore.LookupEntry(action.morph.modelId)) { sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant ModelId %u.", i, j+1, action.morph.modelId); action.morph.modelId = 0; } } } break; case ACTION_T_SOUND: if (!sSoundEntriesStore.LookupEntry(action.sound.soundId)) sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant SoundID %u.", i, j+1, action.sound.soundId); break; case ACTION_T_EMOTE: if (!sEmotesStore.LookupEntry(action.emote.emoteId)) sLog.outErrorDb("CreatureEventAI: Event %u Action %u param1 (EmoteId: %u) are not valid.", i, j+1, action.emote.emoteId); break; case ACTION_T_RANDOM_SOUND: if (!sSoundEntriesStore.LookupEntry(action.random_sound.soundId1)) sLog.outErrorDb("CreatureEventAI: Event %u Action %u param1 uses non-existant SoundID %u.", i, j+1, action.random_sound.soundId1); if (action.random_sound.soundId2 >= 0 && !sSoundEntriesStore.LookupEntry(action.random_sound.soundId2)) sLog.outErrorDb("CreatureEventAI: Event %u Action %u param2 uses non-existant SoundID %u.", i, j+1, action.random_sound.soundId2); if (action.random_sound.soundId3 >= 0 && !sSoundEntriesStore.LookupEntry(action.random_sound.soundId3)) sLog.outErrorDb("CreatureEventAI: Event %u Action %u param3 uses non-existant SoundID %u.", i, j+1, action.random_sound.soundId3); break; case ACTION_T_RANDOM_EMOTE: if (!sEmotesStore.LookupEntry(action.random_emote.emoteId1)) sLog.outErrorDb("CreatureEventAI: Event %u Action %u param1 (EmoteId: %u) are not valid.", i, j+1, action.random_emote.emoteId1); if (action.random_emote.emoteId2 >= 0 && !sEmotesStore.LookupEntry(action.random_emote.emoteId2)) sLog.outErrorDb("CreatureEventAI: Event %u Action %u param2 (EmoteId: %u) are not valid.", i, j+1, action.random_emote.emoteId2); if (action.random_emote.emoteId3 >= 0 && !sEmotesStore.LookupEntry(action.random_emote.emoteId3)) sLog.outErrorDb("CreatureEventAI: Event %u Action %u param3 (EmoteId: %u) are not valid.", i, j+1, action.random_emote.emoteId3); break; case ACTION_T_CAST: { const SpellEntry *spell = sSpellStore.LookupEntry(action.cast.spellId); if (!spell) sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent SpellID %u.", i, j+1, action.cast.spellId); /* FIXME: temp.raw.param3 not have event tipes with recovery time in it.... else { if (spell->RecoveryTime > 0 && temp.event_flags & EFLAG_REPEATABLE) { //output as debug for now, also because there's no general rule all spells have RecoveryTime if (temp.event_param3 < spell->RecoveryTime) sLog.outDebug("CreatureEventAI: Event %u Action %u uses SpellID %u but cooldown is longer(%u) than minumum defined in event param3(%u).", i, j+1,action.cast.spellId, spell->RecoveryTime, temp.event_param3); } } */ //Cast is always triggered if target is forced to cast on self if (action.cast.castFlags & CAST_FORCE_TARGET_SELF) action.cast.castFlags |= CAST_TRIGGERED; if (action.cast.target >= TARGET_T_END) sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1); break; } case ACTION_T_SUMMON: if (!sCreatureStorage.LookupEntry<CreatureInfo>(action.summon.creatureId)) sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent creature entry %u.", i, j+1, action.summon.creatureId); if (action.summon.target >= TARGET_T_END) sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1); break; case ACTION_T_THREAT_SINGLE_PCT: if (std::abs(action.threat_single_pct.percent) > 100) sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses invalid percent value %u.", i, j+1, action.threat_single_pct.percent); if (action.threat_single_pct.target >= TARGET_T_END) sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1); break; case ACTION_T_THREAT_ALL_PCT: if (std::abs(action.threat_all_pct.percent) > 100) sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses invalid percent value %u.", i, j+1, action.threat_all_pct.percent); break; case ACTION_T_QUEST_EVENT: if (Quest const* qid = objmgr.GetQuestTemplate(action.quest_event.questId)) { if (!qid->HasFlag(QUEST_TRINITY_FLAGS_EXPLORATION_OR_EVENT)) sLog.outErrorDb("CreatureEventAI: Event %u Action %u. SpecialFlags for quest entry %u does not include |2, Action will not have any effect.", i, j+1, action.quest_event.questId); } else sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent Quest entry %u.", i, j+1, action.quest_event.questId); if (action.quest_event.target >= TARGET_T_END) sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1); break; case ACTION_T_CAST_EVENT: if (!sCreatureStorage.LookupEntry<CreatureInfo>(action.cast_event.creatureId)) sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent creature entry %u.", i, j+1, action.cast_event.creatureId); if (!sSpellStore.LookupEntry(action.cast_event.spellId)) sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent SpellID %u.", i, j+1, action.cast_event.spellId); if (action.cast_event.target >= TARGET_T_END) sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1); break; case ACTION_T_SET_UNIT_FIELD: if (action.set_unit_field.field < OBJECT_END || action.set_unit_field.field >= UNIT_END) sLog.outErrorDb("CreatureEventAI: Event %u Action %u param1 (UNIT_FIELD*). Index out of range for intended use.", i, j+1); if (action.set_unit_field.target >= TARGET_T_END) sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1); break; case ACTION_T_SET_UNIT_FLAG: case ACTION_T_REMOVE_UNIT_FLAG: if (action.unit_flag.target >= TARGET_T_END) sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1); break; case ACTION_T_SET_PHASE: if (action.set_phase.phase >= MAX_PHASE) sLog.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set phase >= %u. Phase mask cannot be used past phase %u.", i, j+1, MAX_PHASE, MAX_PHASE-1); break; case ACTION_T_INC_PHASE: if (action.set_inc_phase.step == 0) sLog.outErrorDb("CreatureEventAI: Event %u Action %u is incrementing phase by 0. Was this intended?", i, j+1); else if (std::abs(action.set_inc_phase.step) > MAX_PHASE-1) sLog.outErrorDb("CreatureEventAI: Event %u Action %u is change phase by too large for any use %i.", i, j+1, action.set_inc_phase.step); break; case ACTION_T_QUEST_EVENT_ALL: if (Quest const* qid = objmgr.GetQuestTemplate(action.quest_event_all.questId)) { if (!qid->HasFlag(QUEST_TRINITY_FLAGS_EXPLORATION_OR_EVENT)) sLog.outErrorDb("CreatureEventAI: Event %u Action %u. SpecialFlags for quest entry %u does not include |2, Action will not have any effect.", i, j+1, action.quest_event_all.questId); } else sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent Quest entry %u.", i, j+1, action.quest_event_all.questId); break; case ACTION_T_CAST_EVENT_ALL: if (!sCreatureStorage.LookupEntry<CreatureInfo>(action.cast_event_all.creatureId)) sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent creature entry %u.", i, j+1, action.cast_event_all.creatureId); if (!sSpellStore.LookupEntry(action.cast_event_all.spellId)) sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent SpellID %u.", i, j+1, action.cast_event_all.spellId); break; case ACTION_T_REMOVEAURASFROMSPELL: if (!sSpellStore.LookupEntry(action.remove_aura.spellId)) sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent SpellID %u.", i, j+1, action.remove_aura.spellId); if (action.remove_aura.target >= TARGET_T_END) sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1); break; case ACTION_T_RANDOM_PHASE: //PhaseId1, PhaseId2, PhaseId3 if (action.random_phase.phase1 >= MAX_PHASE) sLog.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set phase1 >= %u. Phase mask cannot be used past phase %u.", i, j+1, MAX_PHASE, MAX_PHASE-1); if (action.random_phase.phase2 >= MAX_PHASE) sLog.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set phase2 >= %u. Phase mask cannot be used past phase %u.", i, j+1, MAX_PHASE, MAX_PHASE-1); if (action.random_phase.phase3 >= MAX_PHASE) sLog.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set phase3 >= %u. Phase mask cannot be used past phase %u.", i, j+1, MAX_PHASE, MAX_PHASE-1); break; case ACTION_T_RANDOM_PHASE_RANGE: //PhaseMin, PhaseMax if (action.random_phase_range.phaseMin >= MAX_PHASE) sLog.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set phaseMin >= %u. Phase mask cannot be used past phase %u.", i, j+1, MAX_PHASE, MAX_PHASE-1); if (action.random_phase_range.phaseMin >= MAX_PHASE) sLog.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set phaseMax >= %u. Phase mask cannot be used past phase %u.", i, j+1, MAX_PHASE, MAX_PHASE-1); if (action.random_phase_range.phaseMin >= action.random_phase_range.phaseMax) { sLog.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set phaseMax <= phaseMin.", i, j+1); std::swap(action.random_phase_range.phaseMin,action.random_phase_range.phaseMax); // equal case processed at call } break; case ACTION_T_SUMMON_ID: if (!sCreatureStorage.LookupEntry<CreatureInfo>(action.summon_id.creatureId)) sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant creature entry %u.", i, j+1, action.summon_id.creatureId); if (action.summon_id.target >= TARGET_T_END) sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1); if (m_CreatureEventAI_Summon_Map.find(action.summon_id.spawnId) == m_CreatureEventAI_Summon_Map.end()) sLog.outErrorDb("CreatureEventAI: Event %u Action %u summons missing CreatureEventAI_Summon %u", i, j+1, action.summon_id.spawnId); break; case ACTION_T_KILLED_MONSTER: if (!sCreatureStorage.LookupEntry<CreatureInfo>(action.killed_monster.creatureId)) sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant creature entry %u.", i, j+1, action.killed_monster.creatureId); if (action.killed_monster.target >= TARGET_T_END) sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1); break; case ACTION_T_SET_INST_DATA: if (!(temp.event_flags & EFLAG_DIFFICULTY_ALL)) sLog.outErrorDb("CreatureEventAI: Event %u Action %u. Cannot set instance data without difficulty event flags.", i, j+1); if (action.set_inst_data.value > 4/*SPECIAL*/) sLog.outErrorDb("CreatureEventAI: Event %u Action %u attempts to set instance data above encounter state 4. Custom case?", i, j+1); break; case ACTION_T_SET_INST_DATA64: if (!(temp.event_flags & EFLAG_DIFFICULTY_ALL)) sLog.outErrorDb("CreatureEventAI: Event %u Action %u. Cannot set instance data without difficulty event flags.", i, j+1); if (action.set_inst_data64.target >= TARGET_T_END) sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1); break; case ACTION_T_UPDATE_TEMPLATE: if (!sCreatureStorage.LookupEntry<CreatureInfo>(action.update_template.creatureId)) sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant creature entry %u.", i, j+1, action.update_template.creatureId); break; case ACTION_T_SET_SHEATH: if (action.set_sheath.sheath >= MAX_SHEATH_STATE) { sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses wrong sheath state %u.", i, j+1, action.set_sheath.sheath); action.set_sheath.sheath = SHEATH_STATE_UNARMED; } break; case ACTION_T_SET_INVINCIBILITY_HP_LEVEL: if (action.invincibility_hp_level.is_percent) { if (action.invincibility_hp_level.hp_level > 100) { sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses wrong percent value %u.", i, j+1, action.invincibility_hp_level.hp_level); action.invincibility_hp_level.hp_level = 100; } } break; case ACTION_T_EVADE: //No Params case ACTION_T_FLEE_FOR_ASSIST: //No Params case ACTION_T_DIE: //No Params case ACTION_T_ZONE_COMBAT_PULSE: //No Params case ACTION_T_FORCE_DESPAWN: //No Params case ACTION_T_AUTO_ATTACK: //AllowAttackState (0 = stop attack, anything else means continue attacking) case ACTION_T_COMBAT_MOVEMENT: //AllowCombatMovement (0 = stop combat based movement, anything else continue attacking) case ACTION_T_RANGED_MOVEMENT: //Distance, Angle case ACTION_T_CALL_FOR_HELP: //Distance break; case ACTION_T_RANDOM_SAY: case ACTION_T_RANDOM_YELL: case ACTION_T_RANDOM_TEXTEMOTE: sLog.outErrorDb("CreatureEventAI: Event %u Action %u currently unused ACTION type. Did you forget to update database?", i, j+1); break; case ACTION_T_MOVE_RANDOM_POINT: case ACTION_T_SET_STAND_STATE: case ACTION_T_SET_PHASE_MASK: case ACTION_T_SET_VISIBILITY: case ACTION_T_SET_ACTIVE: case ACTION_T_SET_AGGRESSIVE: case ACTION_T_ATTACK_START_PULSE: case ACTION_T_SUMMON_GO: break; default: sLog.outErrorDb("CreatureEventAI: Event %u Action %u have currently not checked at load action type (%u). Need check code update?", i, j+1, temp.action[j].type); break; } } //Add to list m_CreatureEventAI_Event_Map[creature_id].push_back(temp); ++Count; if (CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(temp.creature_id)) { if (!cInfo->AIName || !cInfo->AIName[0]) { //sLog.outErrorDb("CreatureEventAI: Creature Entry %u has EventAI script but its AIName is empty. Set to EventAI as default.", cInfo->Entry); size_t len = strlen("EventAI")+1; const_cast<CreatureInfo*>(cInfo)->AIName = new char[len]; strncpy(const_cast<char*>(cInfo->AIName), "EventAI", len); } if (strcmp(cInfo->AIName, "EventAI")) { //sLog.outErrorDb("CreatureEventAI: Creature Entry %u has EventAI script but it has AIName %s. EventAI script will be overriden.", cInfo->Entry, cInfo->AIName); } if (cInfo->ScriptID) { //sLog.outErrorDb("CreatureEventAI: Creature Entry %u has EventAI script but it also has C++ script. EventAI script will be overriden.", cInfo->Entry); } } } while (result->NextRow()); sLog.outString(); sLog.outString(">> Loaded %u CreatureEventAI scripts", Count); } else { barGoLink bar(1); bar.step(); sLog.outString(); sLog.outString(">> Loaded 0 CreatureEventAI scripts. DB table `creature_ai_scripts` is empty."); } }
bool SmartAIMgr::IsEventValid(SmartScriptHolder& e) { if (e.event.type >= SMART_EVENT_END) { TC_LOG_ERROR("sql.sql", "SmartAIMgr: EntryOrGuid %d using event(%u) has invalid event type (%u), skipped.", e.entryOrGuid, e.event_id, e.GetEventType()); return false; } // in SMART_SCRIPT_TYPE_TIMED_ACTIONLIST all event types are overriden by core if (e.GetScriptType() != SMART_SCRIPT_TYPE_TIMED_ACTIONLIST && !(SmartAIEventMask[e.event.type][1] & SmartAITypeMask[e.GetScriptType()][1])) { TC_LOG_ERROR("sql.sql", "SmartAIMgr: EntryOrGuid %d, event type %u can not be used for Script type %u", e.entryOrGuid, e.GetEventType(), e.GetScriptType()); return false; } if (e.action.type <= 0 || e.action.type >= SMART_ACTION_END) { TC_LOG_ERROR("sql.sql", "SmartAIMgr: EntryOrGuid %d using event(%u) has invalid action type (%u), skipped.", e.entryOrGuid, e.event_id, e.GetActionType()); return false; } if (e.event.event_phase_mask > SMART_EVENT_PHASE_ALL) { TC_LOG_ERROR("sql.sql", "SmartAIMgr: EntryOrGuid %d using event(%u) has invalid phase mask (%u), skipped.", e.entryOrGuid, e.event_id, e.event.event_phase_mask); return false; } if (e.event.event_flags > SMART_EVENT_FLAGS_ALL) { TC_LOG_ERROR("sql.sql", "SmartAIMgr: EntryOrGuid %d using event(%u) has invalid event flags (%u), skipped.", e.entryOrGuid, e.event_id, e.event.event_flags); return false; } if (e.GetScriptType() == SMART_SCRIPT_TYPE_TIMED_ACTIONLIST) { e.event.type = SMART_EVENT_UPDATE_OOC;//force default OOC, can change when calling the script! if (!IsMinMaxValid(e, e.event.minMaxRepeat.min, e.event.minMaxRepeat.max)) return false; if (!IsMinMaxValid(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax)) return false; } else { uint32 type = e.event.type; switch (type) { case SMART_EVENT_UPDATE: case SMART_EVENT_UPDATE_IC: case SMART_EVENT_UPDATE_OOC: case SMART_EVENT_HEALT_PCT: case SMART_EVENT_MANA_PCT: case SMART_EVENT_TARGET_HEALTH_PCT: case SMART_EVENT_TARGET_MANA_PCT: case SMART_EVENT_RANGE: case SMART_EVENT_DAMAGED: case SMART_EVENT_DAMAGED_TARGET: case SMART_EVENT_RECEIVE_HEAL: if (!IsMinMaxValid(e, e.event.minMaxRepeat.min, e.event.minMaxRepeat.max)) return false; if (!IsMinMaxValid(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax)) return false; break; case SMART_EVENT_SPELLHIT: case SMART_EVENT_SPELLHIT_TARGET: if (e.event.spellHit.spell) { SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(e.event.spellHit.spell); if (!spellInfo) { TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses non-existent Spell entry %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.event.spellHit.spell); return false; } if (e.event.spellHit.school && (e.event.spellHit.school & spellInfo->SchoolMask) != spellInfo->SchoolMask) { TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses Spell entry %u with invalid school mask, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.event.spellHit.spell); return false; } } if (!IsMinMaxValid(e, e.event.spellHit.cooldownMin, e.event.spellHit.cooldownMax)) return false; break; case SMART_EVENT_OOC_LOS: case SMART_EVENT_IC_LOS: if (!IsMinMaxValid(e, e.event.los.cooldownMin, e.event.los.cooldownMax)) return false; break; case SMART_EVENT_RESPAWN: if (e.event.respawn.type == SMART_SCRIPT_RESPAWN_CONDITION_MAP && !sMapStore.LookupEntry(e.event.respawn.map)) { TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses non-existent Map entry %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.event.respawn.map); return false; } if (e.event.respawn.type == SMART_SCRIPT_RESPAWN_CONDITION_AREA && !GetAreaEntryByAreaID(e.event.respawn.area)) { TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses non-existent Area entry %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.event.respawn.area); return false; } break; case SMART_EVENT_FRIENDLY_HEALTH: if (!NotNULL(e, e.event.friendlyHealth.radius)) return false; if (!IsMinMaxValid(e, e.event.friendlyHealth.repeatMin, e.event.friendlyHealth.repeatMax)) return false; break; case SMART_EVENT_FRIENDLY_IS_CC: if (!IsMinMaxValid(e, e.event.friendlyCC.repeatMin, e.event.friendlyCC.repeatMax)) return false; break; case SMART_EVENT_FRIENDLY_MISSING_BUFF: { if (!IsSpellValid(e, e.event.missingBuff.spell)) return false; if (!NotNULL(e, e.event.missingBuff.radius)) return false; if (!IsMinMaxValid(e, e.event.missingBuff.repeatMin, e.event.missingBuff.repeatMax)) return false; break; } case SMART_EVENT_KILL: if (!IsMinMaxValid(e, e.event.kill.cooldownMin, e.event.kill.cooldownMax)) return false; if (e.event.kill.creature && !IsCreatureValid(e, e.event.kill.creature)) return false; break; case SMART_EVENT_VICTIM_CASTING: if (e.event.targetCasting.spellId > 0 && !sSpellMgr->GetSpellInfo(e.event.targetCasting.spellId)) { TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses non-existent Spell entry %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.event.spellHit.spell); return false; } if (!IsMinMaxValid(e, e.event.targetCasting.repeatMin, e.event.targetCasting.repeatMax)) return false; break; case SMART_EVENT_PASSENGER_BOARDED: case SMART_EVENT_PASSENGER_REMOVED: if (!IsMinMaxValid(e, e.event.minMax.repeatMin, e.event.minMax.repeatMax)) return false; break; case SMART_EVENT_SUMMON_DESPAWNED: case SMART_EVENT_SUMMONED_UNIT: if (e.event.summoned.creature && !IsCreatureValid(e, e.event.summoned.creature)) return false; if (!IsMinMaxValid(e, e.event.summoned.cooldownMin, e.event.summoned.cooldownMax)) return false; break; case SMART_EVENT_ACCEPTED_QUEST: case SMART_EVENT_REWARD_QUEST: if (e.event.quest.quest && !IsQuestValid(e, e.event.quest.quest)) return false; break; case SMART_EVENT_RECEIVE_EMOTE: { if (e.event.emote.emote && !IsTextEmoteValid(e, e.event.emote.emote)) return false; if (!IsMinMaxValid(e, e.event.emote.cooldownMin, e.event.emote.cooldownMax)) return false; break; } case SMART_EVENT_HAS_AURA: case SMART_EVENT_TARGET_BUFFED: { if (!IsSpellValid(e, e.event.aura.spell)) return false; if (!IsMinMaxValid(e, e.event.aura.repeatMin, e.event.aura.repeatMax)) return false; break; } case SMART_EVENT_TRANSPORT_ADDCREATURE: { if (e.event.transportAddCreature.creature && !IsCreatureValid(e, e.event.transportAddCreature.creature)) return false; break; } case SMART_EVENT_MOVEMENTINFORM: { if (e.event.movementInform.type > NULL_MOTION_TYPE) { TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses invalid Motion type %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.event.movementInform.type); return false; } break; } case SMART_EVENT_DATA_SET: { if (!IsMinMaxValid(e, e.event.dataSet.cooldownMin, e.event.dataSet.cooldownMax)) return false; break; } case SMART_EVENT_AREATRIGGER_ONTRIGGER: { if (e.event.areatrigger.id && !IsAreaTriggerValid(e, e.event.areatrigger.id)) return false; break; } case SMART_EVENT_TEXT_OVER: //if (e.event.textOver.textGroupID && !IsTextValid(e, e.event.textOver.textGroupID)) return false;// 0 is a valid text group! break; case SMART_EVENT_LINK: { if (e.link && e.link == e.event_id) { TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry %d SourceType %u, Event %u, Link Event is linking self (infinite loop), skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id); return false; } break; } case SMART_EVENT_DUMMY_EFFECT: { if (!IsSpellValid(e, e.event.dummy.spell)) return false; if (e.event.dummy.effIndex > EFFECT_2) return false; break; } case SMART_EVENT_IS_BEHIND_TARGET: { if (!IsMinMaxValid(e, e.event.behindTarget.cooldownMin, e.event.behindTarget.cooldownMax)) return false; break; } case SMART_EVENT_GAME_EVENT_START: case SMART_EVENT_GAME_EVENT_END: { GameEventMgr::GameEventDataMap const& events = sGameEventMgr->GetEventMap(); if (e.event.gameEvent.gameEventId >= events.size() || !events[e.event.gameEvent.gameEventId].isValid()) return false; break; } case SMART_EVENT_ACTION_DONE: { if (e.event.doAction.eventId > EVENT_CHARGE) { TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses invalid event id %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.event.doAction.eventId); return false; } break; } case SMART_EVENT_FRIENDLY_HEALTH_PCT: if (!IsMinMaxValid(e, e.event.friendlyHealthPct.repeatMin, e.event.friendlyHealthPct.repeatMax)) return false; if (e.event.friendlyHealthPct.maxHpPct > 100 || e.event.friendlyHealthPct.minHpPct > 100) { TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry %d SourceType %u Event %u Action %u has pct value above 100, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType()); return false; } switch (e.GetTargetType()) { case SMART_TARGET_CREATURE_RANGE: case SMART_TARGET_CREATURE_GUID: case SMART_TARGET_CREATURE_DISTANCE: case SMART_TARGET_CLOSEST_CREATURE: case SMART_TARGET_CLOSEST_PLAYER: case SMART_TARGET_PLAYER_RANGE: case SMART_TARGET_PLAYER_DISTANCE: break; default: TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses invalid target_type %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.GetTargetType()); return false; } break; case SMART_EVENT_GO_STATE_CHANGED: case SMART_EVENT_GO_EVENT_INFORM: case SMART_EVENT_TIMED_EVENT_TRIGGERED: case SMART_EVENT_INSTANCE_PLAYER_ENTER: case SMART_EVENT_TRANSPORT_RELOCATE: case SMART_EVENT_CHARMED: case SMART_EVENT_CHARMED_TARGET: case SMART_EVENT_CORPSE_REMOVED: case SMART_EVENT_AI_INIT: case SMART_EVENT_TRANSPORT_ADDPLAYER: case SMART_EVENT_TRANSPORT_REMOVE_PLAYER: case SMART_EVENT_AGGRO: case SMART_EVENT_DEATH: case SMART_EVENT_EVADE: case SMART_EVENT_REACHED_HOME: case SMART_EVENT_RESET: case SMART_EVENT_QUEST_ACCEPTED: case SMART_EVENT_QUEST_OBJ_COPLETETION: case SMART_EVENT_QUEST_COMPLETION: case SMART_EVENT_QUEST_REWARDED: case SMART_EVENT_QUEST_FAIL: case SMART_EVENT_JUST_SUMMONED: case SMART_EVENT_WAYPOINT_START: case SMART_EVENT_WAYPOINT_REACHED: case SMART_EVENT_WAYPOINT_PAUSED: case SMART_EVENT_WAYPOINT_RESUMED: case SMART_EVENT_WAYPOINT_STOPPED: case SMART_EVENT_WAYPOINT_ENDED: case SMART_EVENT_GOSSIP_SELECT: case SMART_EVENT_GOSSIP_HELLO: case SMART_EVENT_JUST_CREATED: case SMART_EVENT_FOLLOW_COMPLETED: case SMART_EVENT_ON_SPELLCLICK: break; default: TC_LOG_ERROR("sql.sql", "SmartAIMgr: Not handled event_type(%u), Entry %d SourceType %u Event %u Action %u, skipped.", e.GetEventType(), e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType()); return false; } } switch (e.GetActionType()) { case SMART_ACTION_SET_FACTION: if (e.action.faction.factionID && !sFactionTemplateStore.LookupEntry(e.action.faction.factionID)) { TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses non-existent Faction %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.faction.factionID); return false; } break; case SMART_ACTION_MORPH_TO_ENTRY_OR_MODEL: case SMART_ACTION_MOUNT_TO_ENTRY_OR_MODEL: if (e.action.morphOrMount.creature || e.action.morphOrMount.model) { if (e.action.morphOrMount.creature > 0 && !sObjectMgr->GetCreatureTemplate(e.action.morphOrMount.creature)) { TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses non-existent Creature entry %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.morphOrMount.creature); return false; } if (e.action.morphOrMount.model) { if (e.action.morphOrMount.creature) { TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry %d SourceType %u Event %u Action %u has ModelID set with also set CreatureId, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType()); return false; } else if (!sCreatureDisplayInfoStore.LookupEntry(e.action.morphOrMount.model)) { TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses non-existent Model id %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.morphOrMount.model); return false; } } } break; case SMART_ACTION_SOUND: if (!IsSoundValid(e, e.action.sound.sound)) return false; break; case SMART_ACTION_SET_EMOTE_STATE: case SMART_ACTION_PLAY_EMOTE: if (!IsEmoteValid(e, e.action.emote.emote)) return false; break; case SMART_ACTION_FAIL_QUEST: case SMART_ACTION_ADD_QUEST: if (!e.action.quest.quest || !IsQuestValid(e, e.action.quest.quest)) return false; break; case SMART_ACTION_ACTIVATE_TAXI: { if (!sTaxiPathStore.LookupEntry(e.action.taxi.id)) { TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses invalid Taxi path ID %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.taxi.id); return false; } break; } case SMART_ACTION_RANDOM_EMOTE: if (e.action.randomEmote.emote1 && !IsEmoteValid(e, e.action.randomEmote.emote1)) return false; if (e.action.randomEmote.emote2 && !IsEmoteValid(e, e.action.randomEmote.emote2)) return false; if (e.action.randomEmote.emote3 && !IsEmoteValid(e, e.action.randomEmote.emote3)) return false; if (e.action.randomEmote.emote4 && !IsEmoteValid(e, e.action.randomEmote.emote4)) return false; if (e.action.randomEmote.emote5 && !IsEmoteValid(e, e.action.randomEmote.emote5)) return false; if (e.action.randomEmote.emote6 && !IsEmoteValid(e, e.action.randomEmote.emote6)) return false; break; case SMART_ACTION_ADD_AURA: case SMART_ACTION_CAST: case SMART_ACTION_INVOKER_CAST: if (!IsSpellValid(e, e.action.cast.spell)) return false; break; case SMART_ACTION_CALL_AREAEXPLOREDOREVENTHAPPENS: case SMART_ACTION_CALL_GROUPEVENTHAPPENS: if (Quest const* qid = sObjectMgr->GetQuestTemplate(e.action.quest.quest)) { if (!qid->HasSpecialFlag(QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT)) { TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry %d SourceType %u Event %u Action %u SpecialFlags for Quest entry %u does not include FLAGS_EXPLORATION_OR_EVENT(2), skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.quest.quest); return false; } } else { TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses non-existent Quest entry %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.quest.quest); return false; } break; case SMART_ACTION_SET_EVENT_PHASE: if (e.action.setEventPhase.phase >= SMART_EVENT_PHASE_MAX) { TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry %d SourceType %u Event %u Action %u attempts to set phase %u. Phase mask cannot be used past phase %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.setEventPhase.phase, SMART_EVENT_PHASE_MAX-1); return false; } break; case SMART_ACTION_INC_EVENT_PHASE: if (!e.action.incEventPhase.inc && !e.action.incEventPhase.dec) { TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry %d SourceType %u Event %u Action %u is incrementing phase by 0, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType()); return false; } else if (e.action.incEventPhase.inc > SMART_EVENT_PHASE_MAX || e.action.incEventPhase.dec > SMART_EVENT_PHASE_MAX) { TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry %d SourceType %u Event %u Action %u attempts to increment phase by too large value, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType()); return false; } break; case SMART_ACTION_REMOVEAURASFROMSPELL: if (e.action.removeAura.spell != 0 && !IsSpellValid(e, e.action.removeAura.spell)) return false; break; case SMART_ACTION_RANDOM_PHASE: { if (e.action.randomPhase.phase1 >= SMART_EVENT_PHASE_MAX || e.action.randomPhase.phase2 >= SMART_EVENT_PHASE_MAX || e.action.randomPhase.phase3 >= SMART_EVENT_PHASE_MAX || e.action.randomPhase.phase4 >= SMART_EVENT_PHASE_MAX || e.action.randomPhase.phase5 >= SMART_EVENT_PHASE_MAX || e.action.randomPhase.phase6 >= SMART_EVENT_PHASE_MAX) { TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry %d SourceType %u Event %u Action %u attempts to set invalid phase, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType()); return false; } } break; case SMART_ACTION_RANDOM_PHASE_RANGE: //PhaseMin, PhaseMax { if (e.action.randomPhaseRange.phaseMin >= SMART_EVENT_PHASE_MAX || e.action.randomPhaseRange.phaseMax >= SMART_EVENT_PHASE_MAX) { TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry %d SourceType %u Event %u Action %u attempts to set invalid phase, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType()); return false; } if (!IsMinMaxValid(e, e.action.randomPhaseRange.phaseMin, e.action.randomPhaseRange.phaseMax)) return false; break; } case SMART_ACTION_SUMMON_CREATURE: if (!IsCreatureValid(e, e.action.summonCreature.creature)) return false; for (uint32 i = 0; i < sSpellMgr->GetSpellInfoStoreSize(); ++i) { SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(i); if (!spellInfo) continue; for (uint32 j = 0; j < MAX_SPELL_EFFECTS; ++j) { if (spellInfo->Effects[j].Effect == SPELL_EFFECT_SUMMON) { uint32 creatureSummonEntry = spellInfo->Effects[j].MiscValue; if (e.action.summonCreature.creature == creatureSummonEntry) TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry %d SourceType %u Event %u Action %u creature summon: There is a summon spell for creature entry %u (SpellId: %u, effect: %u)", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.summonCreature.creature, spellInfo->Id, j); } } } if (e.action.summonCreature.type < TEMPSUMMON_TIMED_OR_DEAD_DESPAWN || e.action.summonCreature.type > TEMPSUMMON_MANUAL_DESPAWN) { TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses incorrect TempSummonType %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.summonCreature.type); return false; } break; case SMART_ACTION_CALL_KILLEDMONSTER: if (!IsCreatureValid(e, e.action.killedMonster.creature)) return false; for (uint32 i = 0; i < sSpellMgr->GetSpellInfoStoreSize(); ++i) { SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(i); if (!spellInfo) continue; for (uint32 j = 0; j < MAX_SPELL_EFFECTS; ++j) { if (spellInfo->Effects[j].Effect == SPELL_EFFECT_KILL_CREDIT || spellInfo->Effects[j].Effect == SPELL_EFFECT_KILL_CREDIT2) { uint32 killCredit = spellInfo->Effects[j].MiscValue; if (e.action.killedMonster.creature == killCredit) TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry %d SourceType %u Event %u Action %u Kill Credit: %u has already spell kill credit (SpellId: %u effect: %u)", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.killedMonster.creature, spellInfo->Id, j); } } } if (e.GetTargetType() == SMART_TARGET_POSITION) { TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses incorrect TargetType %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.GetTargetType()); return false; } break; case SMART_ACTION_UPDATE_TEMPLATE: if (e.action.updateTemplate.creature && !IsCreatureValid(e, e.action.updateTemplate.creature)) return false; break; case SMART_ACTION_SET_SHEATH: if (e.action.setSheath.sheath && e.action.setSheath.sheath >= MAX_SHEATH_STATE) { TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses incorrect Sheath state %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.setSheath.sheath); return false; } break; case SMART_ACTION_SET_REACT_STATE: { if (e.action.react.state > REACT_AGGRESSIVE) { TC_LOG_ERROR("sql.sql", "SmartAIMgr: Creature %d Event %u Action %u uses invalid React State %u, skipped.", e.entryOrGuid, e.event_id, e.GetActionType(), e.action.react.state); return false; } break; } case SMART_ACTION_SUMMON_GO: if (!IsGameObjectValid(e, e.action.summonGO.entry)) return false; for (uint32 i = 0; i < sSpellMgr->GetSpellInfoStoreSize(); ++i) { SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(i); if (!spellInfo) continue; for (uint32 j = 0; j < MAX_SPELL_EFFECTS; ++j) { if (spellInfo->Effects[j].Effect == SPELL_EFFECT_SUMMON_OBJECT_WILD) { uint32 goSummonEntry = spellInfo->Effects[j].MiscValue; if (e.action.summonGO.entry == goSummonEntry) TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry %d SourceType %u Event %u Action %u gameobject summon: There is a summon spell for gameobject entry %u (SpellId: %u, effect: %u)", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.summonGO.entry, spellInfo->Id, j); } } } break; case SMART_ACTION_ADD_ITEM: case SMART_ACTION_REMOVE_ITEM: if (!IsItemValid(e, e.action.item.entry)) return false; if (!NotNULL(e, e.action.item.count)) return false; break; case SMART_ACTION_TELEPORT: if (!sMapStore.LookupEntry(e.action.teleport.mapID)) { TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses non-existent Map entry %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.teleport.mapID); return false; } break; case SMART_ACTION_INSTALL_AI_TEMPLATE: if (e.action.installTtemplate.id >= SMARTAI_TEMPLATE_END) { TC_LOG_ERROR("sql.sql", "SmartAIMgr: Creature %d Event %u Action %u uses non-existent AI template id %u, skipped.", e.entryOrGuid, e.event_id, e.GetActionType(), e.action.installTtemplate.id); return false; } break; case SMART_ACTION_WP_STOP: if (e.action.wpStop.quest && !IsQuestValid(e, e.action.wpStop.quest)) return false; break; case SMART_ACTION_WP_START: { if (!sSmartWaypointMgr->GetPath(e.action.wpStart.pathID)) { TC_LOG_ERROR("sql.sql", "SmartAIMgr: Creature %d Event %u Action %u uses non-existent WaypointPath id %u, skipped.", e.entryOrGuid, e.event_id, e.GetActionType(), e.action.wpStart.pathID); return false; } if (e.action.wpStart.quest && !IsQuestValid(e, e.action.wpStart.quest)) return false; if (e.action.wpStart.reactState > REACT_AGGRESSIVE) { TC_LOG_ERROR("sql.sql", "SmartAIMgr: Creature %d Event %u Action %u uses invalid React State %u, skipped.", e.entryOrGuid, e.event_id, e.GetActionType(), e.action.wpStart.reactState); return false; } break; } case SMART_ACTION_CREATE_TIMED_EVENT: { if (!IsMinMaxValid(e, e.action.timeEvent.min, e.action.timeEvent.max)) return false; if (!IsMinMaxValid(e, e.action.timeEvent.repeatMin, e.action.timeEvent.repeatMax)) return false; break; } case SMART_ACTION_SET_POWER: case SMART_ACTION_ADD_POWER: case SMART_ACTION_REMOVE_POWER: if (e.action.power.powerType > MAX_POWERS) { TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses non-existent Power %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.power.powerType); return false; } break; case SMART_ACTION_GAME_EVENT_STOP: { uint32 eventId = e.action.gameEventStop.id; GameEventMgr::GameEventDataMap const& events = sGameEventMgr->GetEventMap(); if (eventId < 1 || eventId >= events.size()) { TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry %u SourceType %u Event %u Action %u uses non-existent event, eventId %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.gameEventStop.id); return false; } GameEventData const& eventData = events[eventId]; if (!eventData.isValid()) { TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry %u SourceType %u Event %u Action %u uses non-existent event, eventId %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.gameEventStop.id); return false; } break; } case SMART_ACTION_GAME_EVENT_START: { uint32 eventId = e.action.gameEventStart.id; GameEventMgr::GameEventDataMap const& events = sGameEventMgr->GetEventMap(); if (eventId < 1 || eventId >= events.size()) { TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry %u SourceType %u Event %u Action %u uses non-existent event, eventId %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.gameEventStart.id); return false; } GameEventData const& eventData = events[eventId]; if (!eventData.isValid()) { TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry %u SourceType %u Event %u Action %u uses non-existent event, eventId %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.gameEventStart.id); return false; } break; } case SMART_ACTION_FOLLOW: case SMART_ACTION_SET_ORIENTATION: case SMART_ACTION_STORE_TARGET_LIST: case SMART_ACTION_EVADE: case SMART_ACTION_FLEE_FOR_ASSIST: case SMART_ACTION_DIE: case SMART_ACTION_SET_IN_COMBAT_WITH_ZONE: case SMART_ACTION_SET_ACTIVE: case SMART_ACTION_STORE_VARIABLE_DECIMAL: case SMART_ACTION_WP_RESUME: case SMART_ACTION_KILL_UNIT: case SMART_ACTION_SET_INVINCIBILITY_HP_LEVEL: case SMART_ACTION_RESET_GOBJECT: case SMART_ACTION_ATTACK_START: case SMART_ACTION_THREAT_ALL_PCT: case SMART_ACTION_THREAT_SINGLE_PCT: case SMART_ACTION_SET_INST_DATA: case SMART_ACTION_SET_INST_DATA64: case SMART_ACTION_AUTO_ATTACK: case SMART_ACTION_ALLOW_COMBAT_MOVEMENT: case SMART_ACTION_CALL_FOR_HELP: case SMART_ACTION_SET_DATA: case SMART_ACTION_MOVE_FORWARD: case SMART_ACTION_SET_VISIBILITY: case SMART_ACTION_WP_PAUSE: case SMART_ACTION_SET_FLY: case SMART_ACTION_SET_RUN: case SMART_ACTION_SET_SWIM: case SMART_ACTION_FORCE_DESPAWN: case SMART_ACTION_SET_INGAME_PHASE_MASK: case SMART_ACTION_SET_UNIT_FLAG: case SMART_ACTION_REMOVE_UNIT_FLAG: case SMART_ACTION_PLAYMOVIE: case SMART_ACTION_MOVE_TO_POS: case SMART_ACTION_RESPAWN_TARGET: case SMART_ACTION_CLOSE_GOSSIP: case SMART_ACTION_EQUIP: case SMART_ACTION_TRIGGER_TIMED_EVENT: case SMART_ACTION_REMOVE_TIMED_EVENT: case SMART_ACTION_OVERRIDE_SCRIPT_BASE_OBJECT: case SMART_ACTION_RESET_SCRIPT_BASE_OBJECT: case SMART_ACTION_ACTIVATE_GOBJECT: case SMART_ACTION_CALL_SCRIPT_RESET: case SMART_ACTION_SET_RANGED_MOVEMENT: case SMART_ACTION_CALL_TIMED_ACTIONLIST: case SMART_ACTION_SET_NPC_FLAG: case SMART_ACTION_ADD_NPC_FLAG: case SMART_ACTION_REMOVE_NPC_FLAG: case SMART_ACTION_TALK: case SMART_ACTION_SIMPLE_TALK: case SMART_ACTION_CROSS_CAST: case SMART_ACTION_CALL_RANDOM_TIMED_ACTIONLIST: case SMART_ACTION_CALL_RANDOM_RANGE_TIMED_ACTIONLIST: case SMART_ACTION_RANDOM_MOVE: case SMART_ACTION_SET_UNIT_FIELD_BYTES_1: case SMART_ACTION_REMOVE_UNIT_FIELD_BYTES_1: case SMART_ACTION_INTERRUPT_SPELL: case SMART_ACTION_SEND_GO_CUSTOM_ANIM: case SMART_ACTION_SET_DYNAMIC_FLAG: case SMART_ACTION_ADD_DYNAMIC_FLAG: case SMART_ACTION_REMOVE_DYNAMIC_FLAG: case SMART_ACTION_JUMP_TO_POS: case SMART_ACTION_SEND_GOSSIP_MENU: case SMART_ACTION_GO_SET_LOOT_STATE: case SMART_ACTION_SEND_TARGET_TO_TARGET: case SMART_ACTION_SET_HOME_POS: case SMART_ACTION_SET_HEALTH_REGEN: case SMART_ACTION_SET_ROOT: case SMART_ACTION_SET_GO_FLAG: case SMART_ACTION_ADD_GO_FLAG: case SMART_ACTION_REMOVE_GO_FLAG: case SMART_ACTION_SUMMON_CREATURE_GROUP: break; default: TC_LOG_ERROR("sql.sql", "SmartAIMgr: Not handled action_type(%u), event_type(%u), Entry %d SourceType %u Event %u, skipped.", e.GetActionType(), e.GetEventType(), e.entryOrGuid, e.GetScriptType(), e.event_id); return false; } return true; }