diff options
49 files changed, 7102 insertions, 6882 deletions
diff --git a/sql/mangos.sql b/sql/mangos.sql index 686c581e9f7..14e084afd5b 100644 --- a/sql/mangos.sql +++ b/sql/mangos.sql @@ -2338,6 +2338,7 @@ INSERT INTO `mangos_string` VALUES (328,'Characters at account %s (Id: %u)',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),
(329,' %s (GUID %u)',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),
(330,'No players found!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),
+(331,'Extended item cost %u not exist',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),
(400,'|cffff0000[System Message]:|rScripts reloaded',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),
(401,'You change security level of %s to %i.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),
(402,'%s changed your security level to %i.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL),
@@ -10485,6 +10486,7 @@ UNLOCK TABLES; DROP TABLE IF EXISTS `quest_template`;
CREATE TABLE `quest_template` (
`entry` mediumint(8) unsigned NOT NULL default '0',
+ `Method` tinyint(3) unsigned NOT NULL default '2',
`ZoneOrSort` smallint(6) NOT NULL default '0',
`SkillOrClass` smallint(6) NOT NULL default '0',
`MinLevel` tinyint(3) unsigned NOT NULL default '0',
diff --git a/sql/updates/19_pack.sql b/sql/updates/19_pack.sql new file mode 100644 index 00000000000..3a43f43a955 --- /dev/null +++ b/sql/updates/19_pack.sql @@ -0,0 +1,4 @@ +ALTER TABLE `quest_template` ADD COLUMN `Method` tinyint(3) unsigned NOT NULL default '2' AFTER `entry`;
+DELETE FROM mangos_string WHERE entry IN (331);
+INSERT INTO mangos_string VALUES
+(331,'Extended item cost %u not exist',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
diff --git a/src/bindings/scripts/docs/EventAI.txt b/src/bindings/scripts/docs/EventAI.txt index af4335655b8..f1f5a1e9f7f 100644 --- a/src/bindings/scripts/docs/EventAI.txt +++ b/src/bindings/scripts/docs/EventAI.txt @@ -1,701 +1,702 @@ -========================================= -Event AI documentation -========================================= - -Scriptdev2 Revision 220 introduces a new database defined AI named EventAI. -This system allows users to create new creature scripts entierly within the Database. -ScriptName must still be set to "mob_eventai" within the MaNGOS database creature_template.scriptname field. - -========================================= -Basic Structure of EventAI -========================================= -Event AI follows a basic if (Event) then do {Action} format. -Below is a the list of current fields within the Eventai_scripts table. - -(Field_Name Discription) -id This value is mearly an incrementing counter of the current Event number. Required for sql queries. -creature_id Creature id which this event should occur on. - -event_type Type of event (See Event Types below) -event_inverse_phase_mask Mask which phases this event should NOT trigger in* -event_chance Percent chance of this event occuring (1 - 100) -event_flags Event flags such as if the event is repeatable (see below) -event_param1 Variable for event (dependant on Event type) -event_param2 -event_param3 -event_param4 - -action1_type First Type of Action to take when event occurs (See Action Types below) -action1_param1 Variables used for Action1 (dependant on Action type) -action1_param2 -action1_param3 - -action2_type Second Type of Action to take when event occurs (See Action Types below) -action2_param1 Variables used for Action2 (dependant on Action type) -action2_param2 -action2_param3 - -action3_type Third Type of Action to take when event occurs (See Action Types below) -action3_param1 Variables used for Action3 (dependant on Action type) -action3_param2 -action3_param3 - -All params are signed 32 bit values (+/- 2147483647). If param specifies time then time is in milliseconds. If param specifies percentage then percentages are value/100 (ex: if param = 500 then that means 500%, -50 = -50%) - -*Phase mask is a bit mask of which phases this event should not trigger in. Example: Phase mask value of 12 (1100) would mean that this event would trigger 0, 1 and all other phases except for 2 and 3 (0 counts as the first phase). - -========================================= -Event Types -========================================= -Below is the list of current Event types that EventAI can handle. -Each event type has its own specific interpretation of the params that accompany it. -Params are always read from Param1, then Param2, then Param3. -Events will not repeat until the creature exits combat unless EFLAG_REPEATABLE is set. Some events such as EVENT_T_AGGRO, EVENT_T_DEATH, EVENT_T_SPAWNED, and EVENT_T_EVADE cannot repeat. - -# Internal Name Pamarm usage Description -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -0 EVENT_T_TIMER InitialMin, InitialMax, RepeatMin, RepeatMax Expires first between (Param1) and (Param2) and then between every (Param3) and (Param4). but only in combat. -1 EVENT_T_TIMER_OOC InitialMin, InitialMax, RepeatMin, RepeatMax Expires first between (Param1) and (Param2) and then between every (Param3) and (Param4). but only out of combat. -2 EVENT_T_HP HPMax%, HPMin%, RepeatMin, RepeatMax Expires when HP is between (Param1) and (Param2). Will repeat every (Param3) and (Param4). -3 EVENT_T_MANA ManaMax%,ManaMin% RepeatMin, RepeatMax Expires once Mana% is between (Param1) and (Param2). Will repeat every (Param3) and (Param4). -4 EVENT_T_AGGRO NONE Expires upon initial aggro (does not repeat). -5 EVENT_T_KILL RepeatMin, RepeatMax Expires upon killing a player. Will repeat between every (Param1) and (Param2). -6 EVENT_T_DEATH NONE Expires upon Death of the Creature. -7 EVENT_T_EVADE NONE Expires upon creature EnterEvadeMode(). -8 EVENT_T_SPELLHIT SpellID, School, RepeatMin, RepeatMax Expires upon Spell hit. If (param1) is set will only expire on that spell. If (param2) will only expire on spells of that school (-1 for all). Will repeat every (Param3) and (Param4) . -9 EVENT_T_RANGE MinDist, MaxDist, RepeatMin, RepeatMax Expires when the highest threat target distance is greater than (Param1) and less than (Param2). Will repeat every (Param3) and (Param4) . -10 EVENT_T_OOC_LOS NoHostile, NoFriendly, RepeatMin, RepeatMax Expires when a Player moves within visible distance to creature. Does not expire for Hostile Players if (Param1) is not 0. Does not expire for Friendly Players if (Param2) is not 0. Will repeat every (Param3) and (Param4) . Does not expire for creatures or pet or when the creature is in combat. -11 EVENT_T_SPAWNED NONE Expires at initial spawn and at creature respawn (useful for setting ranged movement type) -12 EVENT_T_TARGET_HP HPMax%, HPMin%, RepeatMin, RepeatMax Expires when Current Target's HP is between (Param1) and (Param2). Will repeat every (Param3) and (Param4) . -13 EVENT_T_TARGET_CASTING RepeatMin, RepeatatMax Expires when the current target is casting a spell. Will repeat every (Param1) and (Param2) . -14 EVENT_T_FRIENDLY_HP HPDeficit, Radius, RepeatMin, RepeatMax Expires when a friendly unit in radius has at least (param1) hp missing. Will repeat every (Param3) and (Param4) . -15 EVENT_T_FRIENDLY_IS_CC DispelType, Radius, RepeatMin, RepeatMax Expires when a friendly unit is Crowd controlled within the given radius (param2). Will repeat every (Param3) and (Param4) . -16 EVENT_T_MISSING_BUFF SpellId, Radius, RepeatMin, RepeatMax Expires when a friendly unit is missing aura's given by spell (param1) within radius (param2). Will repeat every (Param3) and (Param4) . -17 EVENT_T_SUMMONED_UNIT CreatureId, RepeatMin, RepeatMax Expires after creature with entry = (param1) is spawned or for all spawns if param1 = 0. Will repeat every (Param2) and (Param3) . - -========================================= -Action Types -========================================= -Below is the list of current Action types that EventAI can handle. -Each event type has its own specific interpretation of the params that accompany it. -Params are always read from Param1, then Param2, then Param3. - -(# Internal Name Param usage Discription) -0 ACTION_T_NONE No Action Does Nothing -1 ACTION_T_SAY TextId Says Text -2 ACTION_T_YELL TextId Yells Text -3 ACTION_T_TEXTEMOTE TextId Text Emotes Text -4 ACTION_T_SOUND SoundId Plays Sound -5 ACTION_T_EMOTE EmoteId Does emote -6 ACTION_T_RANDOM_SAY TextId1, TextId2, TextId3 Says random text between 3 params* -7 ACTION_T_RANDOM_YELL TextId1, TextId2, TextId3 Yells random text between 3 params* -8 ACTION_T_RANDOM_TEXTEMOTE TextId1, TextId2, TextId3 Text Emotes random text between 3 params* -9 ACTION_T_RANDOM_SOUND SoundId1, SoundId2, SoundId3 Plays random sound between 3 params* -10 ACTION_T_RANDOM_EMOTE EmoteId1, EmoteId2, EmoteId3 Emotes random emote between 3 params -11 ACTION_T_CAST SpellId, Target, CastFlags Casts spell (param1) on target type (param2). Uses Cast Flags (specified below target types) -12 ACTION_T_SUMMON CreatureID, Target, Duration Summons creature (param1) to attack target (param2) for (param3) duration. Spawns on top of current creature. -13 ACTION_T_THREAT_SINGLE_PCT Threat%, Target Modifies threat by (param1) on target type (param2) -14 ACTION_T_THREAT_ALL_PCT Threat% Modifies threat by (param1) on all targets (using -100% on all will result in full aggro dump) -15 ACTION_T_QUEST_EVENT QuestID, Target Calls AreaExploredOrEventHappens with (param1) for target type (Param2) -16 ACTION_T_QUEST_CASTCREATUREGO CreatureID, SpellId, Target Sends CastCreatureOrGo for CreatureId (param1) with SpellId (param2) for target (param3) -17 ACTION_T_SET_UNIT_FIELD Field_Number, Value, Target Sets Unit Field (param1) to Value (param2) on target type (param3) -18 ACTION_T_SET_UNIT_FLAG Flags, Target Sets flag (flags can be binary OR together to modify multiple flags at once) on for Target type (param2) -19 ACTION_T_REMOVE_UNIT_FLAG Flags, Target Removes flag (flags can be binary OR together to modify multiple flags at once) on for Target type (param2) -20 ACTION_T_AUTO_ATTACK AllowAutoAttack 0 = stop melee attack, anything else means continue attacking/allow melee attacking -21 ACTION_T_COMBAT_MOVEMENT AllowCombatMovement 0 = stop combat based movement, anything else continue/allow combat based movement (targeted movement generator) -22 ACTION_T_SET_PHASE Phase Sets the current phase to (param1) -23 ACTION_T_INC_PHASE Value Increments the phase by (param1). May be negative to decrement phase but should not be 0. -24 ACTION_T_EVADE No Params Forces the creature to evade. Wiping all threat and dropping combat. -25 ACTION_T_FLEE No Params Causes the .creature to flee. Please use this action instead of directly casting this spell so we may change this when a more correct approach is found. -26 ACTION_T_QUEST_EVENT_ALL QuestId Calls GroupEventHappens with (param1). Only used if it's _expected_ event should complete for all players in current party -27 ACTION_T_CASTCREATUREGO_ALL QuestId, SpellId Calls CastedCreatureOrGo for all players on the threat list with QuestID(Param1) and SpellId(Param2) -28 ACTION_T_REMOVEAURASFROMSPELL Target, Spellid Removes all auras on Target caused by Spellid -29 ACTION_T_RANGED_MOVEMENT Distance, Angle Changes the movement generator type to a ranged type. Note: Default melee type can still be done with this. Specify 0 angle and 0 distance. -30 ACTION_T_RANDOM_PHASE PhaseId1, PhaseId2, PhaseId3 Sets the phase to the id between 3 params* -31 ACTION_T_RANDOM_PHASE_RANGE PhaseMin, PhaseMax Sets the phase to a random id (Phase = PhaseMin + rnd % PhaseMin-PhaseMax). PhaseMax must be greater than PhaseMin. -32 ACTION_T_SUMMON CreatureID, Target, SummonID Summons creature (param1) to attack target (param2) at location specified by EventAI_Summons (param3). -33 ACTION_T_KILLED_MONSTER CreatureID, Target Calls KilledMonster (param1) for target of type (param2) -34 ACTION_T_SET_INST_DATA Field, Data Calls ScriptedInstance::SetData with field (param1) and data (param2) -35 ACTION_T_SET_INST_DATA64 Field, Target Calls ScriptedInstance::SetData64 with field (param1) and data (param2) target's GUID. -36 ACTION_T_UPDATE_TEMPLATE TemplateId, Team Changes the creature to a new creature template of (param1) with team = Alliance if (param2) = false or Horde if (param2) = true -37 ACTION_T_DIE No Params Kills the creature -38 ACTION_T_ZONE_COMBAT_PULSE No Params Places all players within the instance into combat with the creature. Only works in combat and only works inside of instances. - -* = Use -1 to specify that if this param is picked to do nothing. Random is constant between actions within an event. So if you have a random Yell and a random Sound they will match up (ex: param2 with param2) - -========================================= -Event Types -========================================= -Note: -COMBAT ONLY - Means that this event will only trigger durring combat. -OUT OF COMBAT ONLY - Means that this event will only trigger while out of combat. -BOTH - This event can trigger both in and out of combat. - -Events that do not have lables on them are events that are directly involved with the in and out of combat state. - ------------------- -0 = EVENT_T_TIMER: ------------------- -Parameter 1: InitialMin - Minumum Time used to calculate Random Initial Expire -Parameter 2: InitialMax - Maximum Time used to calculate Random Initial Expire -Parameter 3: RepeatMin - Minimum Time used to calculate Random Repeat Expire -Parameter 4: RepeatMax - Maximum Time used to calculate Random Repeat Expire - -COMBAT ONLY! - Expires first between (Param1) and (Param2) and then between every (Param3) and (Param4) from then on. -This is commonly used for spells that repeat cast during combat (Simulate Spell Cooldown). - ----------------------- -1 = EVENT_T_TIMER_OOC: ----------------------- -Parameter 1: InitialMin - Minumum Time used to calculate Random Initial Expire -Parameter 2: InitialMax - Maximum Time used to calculate Random Initial Expire -Parameter 3: RepeatMin - Minimum Time used to calculate Random Repeat Expire -Parameter 4: RepeatMax - Maximum Time used to calculate Random Repeat Expire - -OUT OF COMBAT ONLY! - Expires first between (Param1) and (Param2) and then between every (Param3) and (Param4) from then on. -This is commonly used for events that occur and repeat outside of combat. - ---------------- -2 = EVENT_T_HP: ---------------- -Parameter 1: HPMax% - Maximum HP% That this Event will Expire -Parameter 2: HPMin% - Minimum HP% That this Event will Expire -Parameter 3: RepeatMin - Minimum Time used to calculate Random Repeat Expire -Parameter 4: RepeatMax - Maximum Time used to calculate Random Repeat Expire - -BOTH - Expires when HP is between (Param1) and (Param2). Will repeat every (Param3) and (Param4). -This is commonly used for events that trigger at a specific HP% (Such as Heal/Enrage Spells or NPC's that Flee). - ------------------ -3 = EVENT_T_MANA: ------------------ -Parameter 1: ManaMax% - Maximum Mana% That this Event will Expire -Parameter 2: ManaMin% - Minimum Mana% That this Event will Expire -Parameter 3: RepeatMin - Minimum Time used to calculate Random Repeat Expire -Parameter 4: RepeatMax - Maximum Time used to calculate Random Repeat Expire - -BOTH - Expires once Mana% is between (Param1) and (Param2). Will repeat every (Param3) and (Param4). -This is commonly used for events where an NPC low on Mana will do something (Such as stop casting spells and switch to melee). - ------------------- -4 = EVENT_T_AGGRO: ------------------- -This Event Expires upon initial aggro (does not repeat). - ------------------ -5 = EVENT_T_KILL: ------------------ -Parameter 1: RepeatMin - Minimum Time used to calculate Random Repeat Expire -Parameter 2: RepeatMax - Maximum Time used to calculate Random Repeat Expire - -COMBAT ONLY! - Expires upon killing a player. Will repeat every (Param1) and (Param2). -This Event Expires upon killing a player. It is commonly used for NPC's who yell or do something after killing a player. - ------------------- -6 = EVENT_T_DEATH: ------------------- -This Event Expires upon Death of the Scripted NPC. -This is commonly used for NPC's who have a yell on death or cast some kind if summon spell when they die. - ------------------- -7 = EVENT_T_EVADE: ------------------- -This Event Expires upon the creature EnterEvadeMode(). -This is commonly used for NPC's who use phases, allows you to reset their phase to 0 upon evade to prevent possible strange behavior. - ---------------------- -8 = EVENT_T_SPELLHIT: ---------------------- -Parameter 1: SpellID - The Spell ID that will trigger the event to occur (NOTE: If you use Spell School as the trigger set this value to 0) -Parameter 2: School - Spell School to trigger the event (NOTE: If you use a SpellID then set this value to -1) - *See Below for Spell School Bitmask Values* -Parameter 3: RepeatMin - Minimum Time used to calculate Random Repeat Expire -Parameter 4: RepeatMax - Maximum Time used to calculate Random Repeat Expire - -BOTH - Expires upon Spell hit. If (param1) is set will only expire on that spell. If (param2) will only expire on spells of that school. Will repeat every (Param3) and (Param4). -This Event is commonly used for NPC's who can do special things when you cast a spell (Or specific spell) on them. - ------------------- -9 = EVENT_T_RANGE: ------------------- -Parameter 1: MinDist - This Distance is the Minimum Distance between the NPC and it's target to allow this Event to Expire -Parameter 2: MaxDist - This Distance is the Maximum Distance between the NPC and it's target to allow this Event to Expire -Parameter 3: RepeatMin - Minimum Time used to calculate Random Repeat Expire -Parameter 4: RepeatMax - Maximum Time used to calculate Random Repeat Expire - -COMBAT ONLY! - Expires when the highest threat target distance is greater than (Param1) and less than (Param2). Will repeat every (Param3) and (Param4). -This Event is commonly used for NPC's who have Ranged Combat and will Throw/Shoot between a certian distance. - ---------------------- -10 = EVENT_T_OOC_LOS: ---------------------- -Parameter 1: NoHostile - This Value is to Prevent this Action from Expiring When Caused by a Player/Creature Hostile to them (0 = Prevent Event from Expiring, 1 = Allow Event to Expire) -Parameter 2: NoFriendly - This Value is to Prevent this Action from Expiring When Caused by a Player/Creature Friendly to them (0 = Prevent Event from Expiring, 1 = Allow Event to Expire) -Parameter 3: RepeatMin - Minimum Time used to calculate Random Repeat Expire -Parameter 4: RepeatMax - Maximum Time used to calculate Random Repeat Expire - -OUT OF COMBAT ONLY! - Expires when a Player moves within visible distance to the NPC. Does not expire for Hostile Players if (Param1) is not 0. Does not expire for Friendly Players if (Param2) is not 0. Will repeat every (Param3) and (Param4). Does not expire for creatures or pets when they are in combat. -This Event is commonly used for NPC's who Do Something or Say Something when you walk past them Out of Combat. - ---------------------- -11 = EVENT_T_SPAWNED: ---------------------- -Expires at initial spawn and at creature respawn. -This Event is commonly used for setting ranged movement type or Summoning a Pet on Spawn - ------------------------ -12 = EVENT_T_TARGET_HP: ------------------------ -Parameter 1: HPMax% - Maximum HP% That this Event will Expire -Parameter 2: HPMin% - Minimum HP% That this Event will Expire -Parameter 3: RepeatMin - Minimum Time used to calculate Random Repeat Expire -Parameter 4: RepeatMax - Maximum Time used to calculate Random Repeat Expire - -COMBAT ONLY! - Expires when Current NPC's Target's HP is between (Param1) and (Param2). Will repeat every (Param3) and (Param4). -This Event is commonly used for NPC's who have a special ability (Like Execute) that only casts when a Player HP is low. - ----------------------------- -13 = EVENT_T_TARGET_CASTING: ----------------------------- -Parameter 1: RepeatMin - Minimum Time used to calculate Random Repeat Expire -Parameter 2: RepeatMax - Maximum Time used to calculate Random Repeat Expire - -COMBAT ONLY! - Expires when the current target is casting a spell. Will repeat every (Param1) and (Param2). -This event is commonly used for NPC's who will cast a counter spell when their target starts to cast a spell. - -------------------------- -14 = EVENT_T_FRIENDLY_HP: -------------------------- -Parameter 1: HPDeficit - This is the Amount of HP Missing from Full HP to trigger this event (You would need to calculate the amount of HP the event happens and subtract that from Full HP Value to get this number) -Parameter 2: Radius - This is the Range in Yards the NPC will scan for nearby Friendlies (Faction is Friendly To) for the missing amount of HP in Param1. -Parameter 3: RepeatMin - Minimum Time used to calculate Random Repeat Expire -Parameter 4: RepeatMax - Maximum Time used to calculate Random Repeat Expire - -COMBAT ONLY! - Expires when a friendly unit in radius(param2) has at least (param1) hp missing. Will repeat every (Param3) and (Param4). -This is commonly used when an NPC in Combat will heal a nearby Friendly NPC in Combat with a Heal/Renew Spell. - ----------------------------- -15 = EVENT_T_FRIENDLY_IS_CC: ----------------------------- -Parameter 1: DispelType - Dispel Type to trigger the event - *See Below for Dispel Bitmask Values* -Parameter 2: Radius - This is the Range in Yards the NPC will scan for nearby Friendlies being Crowd Controlled -Parameter 3: RepeatMin - Minimum Time used to calculate Random Repeat Expire -Parameter 4: RepeatMax - Maximum Time used to calculate Random Repeat Expire - -COMBAT ONLY! - Expires when a friendly unit is Crowd controlled within the given radius (param2). Will repeat every (Param3) and (Param4). -This is commonly used for NPC's who can come to the resule of other Friendly NPC's if being Crowd Controlled - --------------------------- -16 = EVENT_T_MISSING_BUFF: --------------------------- -Parameter 1: SpellId - This is the SpellID That the Aura Check will look for (If it is missing this Aura) -Parameter 2: Radius - This is the Range in Yards the NPC will scan for nearby Friendlies (Faction is Friendly To) for the missing Aura. -Parameter 3: RepeatMin - Minimum Time used to calculate Random Repeat Expire -Parameter 4: RepeatMax - Maximum Time used to calculate Random Repeat Expire - -BOTH - Expires when a friendly unit is missing aura's given by spell (param1) within radius (param2). Will repeat every (Param3) and (Param4). -This is commonly used for NPC's who watch friendly units for a debuff to end so they can recast it on them again. - ---------------------------- -17 = EVENT_T_SUMMONED_UNIT: ---------------------------- -Parameter 1: CreatureId - The CreatureID that the NPC is watching to spawn to trigger this event -Parameter 2: RepeatMin - Minimum Time used to calculate Random Repeat Expire -Parameter 3: RepeatMax - Maximum Time used to calculate Random Repeat Expire - -BOTH - Expires after creature with entry(Param1) is spawned or for all spawns if param1 = 0. Will repeat every (Param2) and (Param3) . -This is commonly used for NPC's who will do something special once another NPC is summoned. Usually used is Complex Scripts or Special Events. - - -========================================= -Action Types -========================================= - ------------------ -1 = ACTION_T_SAY: ------------------ -Parameter 1: The ID of the text that the NPC should SAY from the Localized_Texts Table. - -This action is pretty straightforward. When activated, the creature will SAY the specified text (Speech Bubble). - ------------------- -2 = ACTION_T_YELL: ------------------- -Parameter 1: The ID of the text that the NPC should YELL from the Localized_Texts Table. - -The creature will YELL the specified text (Red Speech Bubble). - ------------------------ -3 = ACTION_T_TEXTEMOTE: ------------------------ -Parameter 1: The ID of the text that the NPC should TEXTEMOTE from the Localized_Texts Table. - -When activated, the creature will do a text emote using the specified text. A text emote is what you would regularly see by using the /em slash command. -NOTE: The text should be written with the assumption that the creature's name is the first word or words in the sentence (for example, "is doing a text emote" would appear as "XYZ is doing a text emote" for a creature named XYZ). - -------------------- -4 = ACTION_T_SOUND: -------------------- -Parameter 1: The Sound ID to be played. (Sound IDs are contained in the DBC files.) - -The creature will play the specified sound. -This is commonly used for Bosses who Yell and then also have a Voice for the same thing. - -------------------- -5 = ACTION_T_EMOTE: -------------------- -Parameter 1: The Emote ID that the creature should perform. (Emote IDs are also contained in the DBC but they can be found in the mangos source as well). - -The creature will perform a visual emote. Unlike a text emote, a visual emote is one where the creature will actually move or perform a gesture. -This is commonly used for NPC's who may perform a special action (Salute, Roar, ect...). Not all player emotes work for creature models. - ------------------------- -6 = ACTION_T_RANDOM_SAY: ------------------------- -Parameter 1: The ID of the text that the NPC should SAY from the Localized_Texts Table (Random Choice #1). -Parameter 2: The ID of the text that the NPC should SAY from the Localized_Texts Table (Random Choice #2). -Parameter 3: The ID of the text that the NPC should SAY from the Localized_Texts Table (Random Choice #3). - -Similar to the ACTION_T_SAY action, it will choose at random a text entry to SAY. This action will pick a random entry from the three. -NOTE: If you want any of the options if selected to do nothing you set that Param to -1 value. Then if selected it will do nothing. -NOTE: When using Random Select Actions, ALL The Actions using Random in a Single Event (Action 1,2,3) will select the SAME Random Choice when it expires. This can come in handy if you are doing a ACTION_T_RANDOM_YELL for Action1 and then the corresponding sounds for ACTION_T_RANDOM_SOUND in Action2. If it selects Random Choice #2 for the Yell it will Select Random Choice #2 for the sound so you can match them up as required. -This is commonly used for NPC's who have several different Aggro Say's and you would like one of them selected at random. - -------------------------- -7 = ACTION_T_RANDOM_YELL: -------------------------- -Parameter 1: The ID of the text that the NPC should YELL from the Localized_Texts Table (Random Choice #1). -Parameter 2: The ID of the text that the NPC should YELL from the Localized_Texts Table (Random Choice #2). -Parameter 3: The ID of the text that the NPC should YELL from the Localized_Texts Table (Random Choice #3). - -Similar to the ACTION_T_YELL action, it will choose at random a text entry to YELL. - ------------------------------- -8 = ACTION_T_RANDOM_TEXTEMOTE: ------------------------------- -Parameter 1: The ID of the text that the NPC should TEXTEMOTE from the Localized_Texts Table (Random Choice #1). -Parameter 2: The ID of the text that the NPC should TEXTEMOTE from the Localized_Texts Table (Random Choice #2). -Parameter 3: The ID of the text that the NPC should TEXTEMOTE from the Localized_Texts Table (Random Choice #3). - -Similar to the ACTION_T_TEXTEMOTE action, it will choose at random a text entry to TEXTEMOTE. - --------------------------- -9 = ACTION_T_RANDOM_SOUND: --------------------------- -Parameter 1: The Sound ID to be played as Random Choice #1. -Parameter 2: The Sound ID to be played as Random Choice #2. -Parameter 3: The Sound ID to be played as Random Choice #3. - -Similar to the ACTION_T_SOUND action, it will choose at random a sound to play. - ---------------------------- -10 = ACTION_T_RANDOM_EMOTE: ---------------------------- -Parameter 1: The Emote ID to be played as Random Choice #1. -Parameter 2: The Emote ID to be played as Random Choice #2. -Parameter 3: The Emote ID to be played as Random Choice #3. - -Similar to the ACTION_T_EMOTE action, it will choose at random an Emote to Visually Perform. - -------------------- -11 = ACTION_T_CAST: -------------------- -Parameter 1: SpellId - The Spell ID to use for the NPC to cast. The value used in this field needs to be a valid Spell ID. -Parameter 2: Target - The Target Type defining who the creature should cast the spell at. The value in this field needs to be a valid Target Type as specified in the reference tables below. -Parameter 3: CastFlags - See Table Below for Cast Flag Bitmask Values. If you are unsure what to set this value at leave it at 0. - -The creature will cast a spell specified by a spell ID on a target specified by the target type. -This is commonly used for NPC's who cast spells. - ---------------------- -12 = ACTION_T_SUMMON: ---------------------- -Parameter 1: CreatureID - The Creature Template ID to be Summoned. The value here needs to be a valid Creature Template ID. -Parameter 2: Target - The Target Type defining who the Summoned creature will attack once spawned. The value in this field needs to be a valid Target Type as specified in the reference tables below. -Parameter 3: Duration - The duration until the summoned creature should be unsummoned AFTER Combat ends. The value in this field is in milliseconds or 0. - -The NPC will Summon another creature at the same spot as itself that will attack the specified target. -NOTE: Almost all Creature Summons have proper Summon Spells that should be used when possible. This Action is a powerful last resort option only to be used if nothing else works. -NOTE: Using Target Type 0 will cause the Summoned creature to not attack anyone. -NOTE: If Duration is set at 0, then the summoned creature will not despawn until it has died. -This is used as a manual way to force an NPC to Summon. --------------------------------- -13 = ACTION_T_THREAT_SINGLE_PCT: --------------------------------- -Parameter 1: Threat% - Threat percent that should be modified. The value in this field can range from -100 to +100. If it is negative, threat will be taken away and if positive, threat will be added. -Parameter 2: Target - The Target Type defining on whom the threat change should occur. The value in this field needs to be a valid target type as specified in the reference tables below. - -This action will modify the threat of a target in the creature's threat list by the specified percent. -This is commonly used to allow an NPC to adjust the Threat to a single player. - ------------------------------ -14 = ACTION_T_THREAT_ALL_PCT: ------------------------------ -Parameter 1: Threat% - The percent that should be used in modifying everyone's threat in the creature's threat list. The value here can range from -100 to +100. - -This action will modify the threat for everyone in the creature's threat list by the specified percent. -NOTE: Using -100 will cause the creature to reset everyone's threat to 0 so that everyone has the same amount of threat. It will NOT remove anyone from the threat list. -This is commonly used to allow an NPC to drop threat for all players to zero. - --------------------------- -15 = ACTION_T_QUEST_EVENT: --------------------------- -Parameter 1: QuestID - The Quest Template ID. The value here must be a valid quest template ID. Furthermore, the quest should have SpecialFlags | 2 as it would need to be completed by an external event which is the activation of this action. -Parameter 2: Target - The Target Type defining whom the quest should be completed for. The value in this field needs to be a valid target type as specified in the reference tables below. - -This action will satisfy the external completion requirement for the quest for the specified target defined by the target type. -NOTE: This action can only be used with player targets so it must be ensured that the target type will point to a player. -This is commonly used for Quests where only ONE player will gain credit for the quest. - ------------------------------ -16 = ACTION_T_CASTCREATUREGO: ------------------------------ -Parameter 1: CreatureID - The Creature Template ID to be Summoned. The value here needs to be a valid Creature Template ID. -Parameter 2: SpellId - The Spell ID to use to simulate the cast. The value used in this field needs to be a valid Spell ID. -Parameter 3: Target - The Target Type defining whom the quest credit should be given to. The value in this field needs to be a valid target type as specified in the reference tables below. - -This action will call CastedCreatureOrGO() function for the player. It can be used to give quest credit for casting a spell on the creature. -This is commonly used for NPC's who have a special requirement to have a Spell cast on them to complete a quest. - ------------------------------ -17 = ACTION_T_SET_UNIT_FIELD: ------------------------------ -Parameter 1: Field_Number - The index of the Field Number to be changed. Use (http://wiki.udbforums.org/index.php/Character_data) for a list of indeces and what they control. Creatures only contain the OBJECT_FIELD_* and UNIT_FIELD_* fields. They do not contain the PLAYER_FIELD_* fields. -Parameter 2: Value - The new value to be put in the field. -Parameter 3: Target - The Target Type defining for whom the unit field should be changed. The value in this field needs to be a valid target type as specified in the reference tables below. - -When activated, this action can change the target's unit field values. More information on the field value indeces can be found at (http://wiki.udbforums.org/index.php/Character_data) - ----------------------------- -18 = ACTION_T_SET_UNIT_FLAG: ----------------------------- -Parameter 1: Flags - The flag(s) to be set. Multiple flags can be set by using bitwise-OR on them (adding them together). -Parameter 2: Target - The Target Type defining for whom the flags should be changed. The value in this field needs to be a valid Target Type as specified in the reference tables below. - -When activated, this action changes the target's flags by adding (turning on) more flags. For example, this action can make the creature unattackable/unselectable if the right flags are used. - -------------------------------- -19 = ACTION_T_REMOVE_UNIT_FLAG: -------------------------------- -Parameter 1: Flags - The flag(s) to be removed. Multiple flags can be set by using bitwise-OR on them (adding them together). -Parameter 2: Target - The target type defining for whom the flags should be changed. The value in this field needs to be a valid Target Type as specified in the reference tables below. - -When activated, this action changes the target's flags by removing (turning off) flags. For example, this action can make the creature normal after it was unattackable/unselectable if the right flags are used. - --------------------------- -20 = ACTION_T_AUTO_ATTACK: --------------------------- -Parameter 1: AllowAutoAttack - If zero, then the creature will stop its melee attacks. If non-zero, then the creature will either continue its melee attacks (the action would then have no effect) or it will start its melee attacks on the target with the top threat if its melee attacks were previously stopped. - -This action controls whether or not the creature should stop or start the auto melee attack. -NOTE: The ACID Dev Team has conformed to using either 0 or 1 for the Param values (0 = Stop Melee, 1 = Start Melee). -This is commonly used in combination with EVENT_T_RANGE and ACTION_T_COMBAT_MOVEMENT for Ranged Combat for Mages and Spell Casters. - ------------------------------- -21 = ACTION_T_COMBAT_MOVEMENT: ------------------------------- -Parameter 1: If zero, then the creature will stop moving towards its victim (if its victim gets out of melee range) and will be stationary. If non-zero, then the creature will either continue to follow its victim (the action would have no effect) or it will start to follow the target with the top threat if its movement was disabled before. - -This action controls whether or not the creature will always move towards its target. -NOTE: The ACID Dev Team has conformed to using either 0 or 1 for the Param values. (0 = Stop Movement, 1 = Start Movement) -This is commonly used with EVENT_T_RANGE and ACTION_T_AUTO_ATTACK for NPC's who engage in Ranged Comabt (Either Spells or Ranged Attacks) - ------------------------- -22 = ACTION_T_SET_PHASE: ------------------------- -Parameter 1: The new phase to set the creature in. This number must be an integer between 0 and 31. Numbers outside of that range will result in an error. - -When activated, this action sets the creature's event to the specified value. -NOTE: The creature's current Phase is NOT reset at creature evade. You must manually set the phase back to 0 at EVENT_T_RESET. -NOTE: The value used for the Param is the actual Phase Number (Not The Event_Inverse_Phase_Mask) -This is commonly used for complex scripts with several phases and you need to switch to a different phase. - ------------------------- -23 = ACTION_T_INC_PHASE: ------------------------- -Parameter 1: Value - The number of phases to increase or decrease. Use negative values to decrease the current phase. - -When activated, this action will increase (or decrease) the current creature's phase. -NOTE: After increasing or decreasing the phase by this action, the current phase must NOT be lower than 0 or exceed 31. -This can be used instead of ACTION_T_SET_PHASE to change phases in scripts. Just a user friendly option for changing phases. - --------------------- -24 = ACTION_T_EVADE: --------------------- -When activated, the creature will immediately exit out of combat, clear its threat list, and move back to its spawn point. Basically, this action will reset the whole encounter. -NOTE: All Param Values Are 0 for this Action. - -------------------- -25 = ACTION_T_FLEE: -------------------- -When activated, the creature will try to flee from combat. Currently this is done by it casting a fear-like spell on itself called "Run Away". A Better Flee system is in Development but will take time before it is implimented. -NOTE: All Param Values Are 0 for this Action. - ------------------------------- -26 = ACTION_T_QUEST_EVENT_ALL: ------------------------------- -Parameter 1: QuestId - The quest ID to finish for everyone. - -This action does the same thing as the ACTION_T_QUEST_EVENT does but it does it for all players in the creature's threat list. -NOTE: If a player is not in the NPC's threat list for whatever reason, he/she won't get the quest completed. - ---------------------------------- -27 = ACTION_T_CASTCREATUREGO_ALL: ---------------------------------- -Parameter 1: QuestId - The quest template ID. -Parameter 2: SpellId - The spell ID used to simulate the cast. - -This action does the same thing as the ACTION_T_CASTCREATUREGO does but it does it for all players in the creature's threat list. -NOTE: If a player is not in its threat list for whatever reason, he/she won't receive the cast emulation. - ------------------------------------ -28 = ACTION_T_REMOVEAURASFROMSPELL: ------------------------------------ -Parameter 1: Target - The target type defining for whom the unit field should be changed. The value in this field needs to be a valid target type as specified in the reference tables below. -Parameter 2: SpellId - The spell ID whose auras will be removed. - -This action will remove all auras from a specific spell from the target. -This is commonly used for NPC's who have an OOC Aura that is removed at combat start or a similar idea (Like Stealth or Shape Shift) - ------------------------------- -29 = ACTION_T_RANGED_MOVEMENT: ------------------------------- -Parameter 1: Distance - The distance the mob should keep between it and its target. -Parameter 2: Angle - The angle the mob should use. - -This action changes the movement type generator to ranged type using the specified values for angle and distance. -NOTE: Specifying zero angle and distance will make it just melee instead. -This is commonly used for NPC's who always attack at range and you can specify the distance they will maintain from the target. - ---------------------------- -30 = ACTION_T_RANDOM_PHASE: ---------------------------- -Parameter 1: PhaseId1 - A possible random phase choice. -Parameter 2: PhaseId2 - A possible random phase choice. -Parameter 3: PhaseId3 - A possible random phase choice. - -Randomly sets the phase to one from the three parameter choices. -NOTE: Use -1 to specify that if this param is picked to do nothing. Random is constant between actions within an event. So if you have a random Yell and a random Sound they will match up (ex: param2 with param2) -NOTE 2: PLEASE NOTE THAT EACH OF THE PARAM VALUES ARE ACTUAL PHASE NUMBERS NOT THE INVERSE PHASE MASK VALUE. -This is commonly used for Spellcasting NPC's who on Aggro may select at random a school of spells to use for the fight. Use this if you have up to 3 phases used, otherwise use Action 31 for more then 3 phases. - ---------------------------------- -31 = ACTION_T_RANDOM_PHASE_RANGE: ---------------------------------- -Parameter 1: PhaseMin - The minimum of the phase range. -Parameter 2: PhaseMax - The maximum of the phase range. The number here must be greater than PhaseMin. - -Randomly sets the phase between a range of phases controlled by the parameters. Sets the phase to a random id (Phase = PhaseMin + rnd % PhaseMin-PhaseMax). -NOTE: PhaseMax must be greater than PhaseMin. -NOTE 2: PLEASE NOTE THAT EACH OF THE PARAM VALUES ARE ACTUAL PHASE NUMBERS NOT THE INVERSE PHASE MASK VALUE. -This is commonly used for Spellcasting NPC's who on Aggro may select at random a school of spells to use for the fight. Use this if you have MORE then 3 phases used, otherwise use Action 30. - ---------------------- -32 = ACTION_T_SUMMON: ---------------------- -Parameter 1: CreatureID - The creature template ID to be summoned. The value here needs to be a valid creature template ID. -Parameter 2: Target - The target type defining who the summoned creature will attack. The value in this field needs to be a valid target type as specified in the reference tables below. NOTE: Using target type 0 will cause the summoned creature to not attack anyone. -Parameter 3: SummonID - The summon ID from the eventai_summons table controlling the position (and spawntime) where the summoned mob should be spawned at. - -Summons creature (param1) to attack target (param2) at location specified by EventAI_Summons (param3). -NOTE: Param3 Value is the ID Value used for the entry used in EventAI_Summons for this action. You MUST have an EventAI_Summons entry to use this action. -This is commonly used for NPC's who need to Summon a creature at a specific location. (Normally used for complex events) - ------------------------------ -33 = ACTION_T_KILLED_MONSTER: ------------------------------ -Parameter 1: CreatureID - The creature template ID. The value here must be a valid creature template ID. -Parameter 2: Target - The target type defining whom the quest kill count should be given to. The value in this field needs to be a valid target type as specified in the reference tables below. - -When activated, this action will call KilledMonster() function for the player. It can be used to give creature credit for killing a creature. In general if the quest is set to be accompished on different creatures (e.g. "Credit" templates). -NOTE: It can be ANY creature including certain quest specific triggers -This is commonly used for giving the player Quest Credits for NPC kills (Many NPC's may use the same CreatureID for the Kill Credit) - ----------------------------- -34 = ACTION_T_SET_INST_DATA: ----------------------------- -Parameter 1: Field - The field to change in the instance script. Again, this field needs to be a valid field that has been already defined in the instance's script. -Parameter 2: Data - The value to put at that field index. - -Sets data for the instance. Note that this will only work when the creature is inside an instantiable zone that has a valid script (ScriptedInstance) assigned. -NOTE: Param1 Value is located in "def_<instance name>.h" SD2 File and Param2 value is generally found in the "sc_instance.h" file in SD2 -This is commonly used to link an ACID script with a SD2 C++ Script. You make make things happen like opening doors on specific events that happen. ACID Just triggers the C++ Script to function. - ------------------------------- -35 = ACTION_T_SET_INST_DATA64: ------------------------------- -Parameter 1: Field - The field to change in the instance script. Again, this field needs to be a valid field that has been already defined in the instance's script. -Parameter 2: Target - The target type to use to get the GUID that will be stored at the field index. The value in this field needs to be a valid target type as specified in the reference tables below. - -Sets GUID (64 bits) data for the instance based on the target. Note that this will only work when the creature is inside an instantiable zone that has a valid script (ScriptedInstance) assigned. -Calls ScriptedInstance::SetData64 with field (param1) and data (param2) target's GUID. - ------------------------------- -36 = ACTION_T_UPDATE_TEMPLATE: ------------------------------- -Parameter 1: TemplateId - The creature template ID. The value here must be a valid creature template ID. -Parameter 2: Team - Use model_id from team : Alliance(0) or Horde (1). - -This function temporarily changes creature entry to new entry, display is changed, loot is changed, but AI is not changed. At respawn creature will be reverted to original entry. -Changes the creature to a new creature template of (param1) with team = Alliance if (param2) = false or Horde if (param2) = true - ------------------- -37 = ACTION_T_DIE: ------------------- -Kills the creature -This is commonly used if you need to Instakill the creature for one reason or another. - --------------------------------- -38 = ACTION_T_ZONE_COMBAT_PULSE: --------------------------------- -Places all players within the instance into combat with the creature. Only works in combat and only works inside of instances. - - -========================================= -Target Types -========================================= -Below is the list of current Target types that EventAI can handle. -Target types are used by certain actions and may effect actions differently - -(# Internal Name Discription) -0 TARGET_T_SELF Self cast -1 TARGET_T_HOSTILE Our current target (ie: highest aggro) -2 TARGET_T_HOSTILE_SECOND_AGGRO Second highest aggro (generaly used for cleaves and some special attacks) -3 TARGET_T_HOSTILE_LAST_AGGRO Dead last on aggro (no idea what this could be used for) -4 TARGET_T_HOSTILE_RANDOM Just any random target on our threat list -5 TARGET_T_HOSTILE_RANDOM_NOT_TOP Any random target except top threat -6 TARGET_T_ACTION_INVOKER Unit who caused this Event to occur (only works for EVENT_T_AGGRO, EVENT_T_KILL, EVENT_T_DEATH, EVENT_T_SPELLHIT, EVENT_T_OOC_LOS, EVENT_T_FRIENDLY_HP) - -========================================= -Cast Flags -========================================= -Below is the list of current Cast Flags that EventAI's spell casting can handle. -Cast flags are handled bitwise. Bit 0 is Interrupt Previous, bit 1 is triggered, etc. -So for example the number "3" (11 in Binary, selecting first 2 options) would mean that this cast has both CAST_INTURRUPT_PREVIOUS and CAST_TRIGGERED. -Another example: the number "5" (101 in Binary, selecting first and third options) would mean that this cast has CAST_INTURRUPT_PREVIOUS and CAST_FORCE_CAST. - -(bit# Decimal Internal Name Discription) -0 1 CAST_INTURRUPT_PREVIOUS Interrupts any previous spell casting (basicaly makes sure that this spell goes off) -1 2 CAST_TRIGGERED Forces the spell to be instant cast and require no mana/reagents. -2 4 CAST_FORCE_CAST Forces spell to cast even if the target is possibly out of range or the creature is possibly out of mana -3 8 CAST_NO_MELEE_IF_OOM Prevents creature from entering melee if out of mana or out of range -4 16 CAST_FORCE_TARGET_SELF Forces the target to cast this spell on itself - -NOTE: You can add the numbers in the decimal column to combine flags. - For example if you wanted to use CAST_NO_MELEE_IF_OOM(8) and CAST_TRIGGERED(2) you would simply use 10 in the cast flags field (8 + 2 = 10). - -========================================= -Event Flags -========================================= -Below is the list of current Event Flags that EventAI can handle. Event flags are handled bitwise. - -(bit# Decimal Internal Name Discription) -0 1 EFLAG_REPEATABLE Event repeats (Does not repeat if this flag is not set) -1 2 EFLAG_NORMAL Event occurs in Normal instance difficulty (will not occur in Normal if not set) -2 4 EFLAG_HEROIC Event occurs in Heroic instance difficulty (will not occur in Heroic if not set) -3 8 -4 16 -5 32 -6 64 -7 128 EFLAG_DEBUG_ONLY Prevents events from occuring on Release builds of ScriptDev2. Useful for testing new features. - +=========================================
+Event AI documentation
+=========================================
+
+Scriptdev2 Revision 220 introduces a new database defined AI named EventAI.
+This system allows users to create new creature scripts entierly within the Database.
+ScriptName must still be set to "mob_eventai" within the MaNGOS database creature_template.scriptname field.
+
+=========================================
+Basic Structure of EventAI
+=========================================
+Event AI follows a basic if (Event) then do {Action} format.
+Below is a the list of current fields within the Eventai_scripts table.
+
+(Field_Name Discription)
+id This value is mearly an incrementing counter of the current Event number. Required for sql queries.
+creature_id Creature id which this event should occur on.
+
+event_type Type of event (See Event Types below)
+event_inverse_phase_mask Mask which phases this event should NOT trigger in*
+event_chance Percent chance of this event occuring (1 - 100)
+event_flags Event flags such as if the event is repeatable (see below)
+event_param1 Variable for event (dependant on Event type)
+event_param2
+event_param3
+event_param4
+
+action1_type First Type of Action to take when event occurs (See Action Types below)
+action1_param1 Variables used for Action1 (dependant on Action type)
+action1_param2
+action1_param3
+
+action2_type Second Type of Action to take when event occurs (See Action Types below)
+action2_param1 Variables used for Action2 (dependant on Action type)
+action2_param2
+action2_param3
+
+action3_type Third Type of Action to take when event occurs (See Action Types below)
+action3_param1 Variables used for Action3 (dependant on Action type)
+action3_param2
+action3_param3
+
+All params are signed 32 bit values (+/- 2147483647). If param specifies time then time is in milliseconds. If param specifies percentage then percentages are value/100 (ex: if param = 500 then that means 500%, -50 = -50%)
+
+*Phase mask is a bit mask of which phases this event should not trigger in. Example: Phase mask value of 12 (1100) would mean that this event would trigger 0, 1 and all other phases except for 2 and 3 (0 counts as the first phase).
+
+=========================================
+Event Types
+=========================================
+Below is the list of current Event types that EventAI can handle.
+Each event type has its own specific interpretation of the params that accompany it.
+Params are always read from Param1, then Param2, then Param3.
+Events will not repeat until the creature exits combat unless EFLAG_REPEATABLE is set. Some events such as EVENT_T_AGGRO, EVENT_T_DEATH, EVENT_T_SPAWNED, and EVENT_T_EVADE cannot repeat.
+
+# Internal Name Pamarm usage Description
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+0 EVENT_T_TIMER InitialMin, InitialMax, RepeatMin, RepeatMax Expires first between (Param1) and (Param2) and then between every (Param3) and (Param4). but only in combat.
+1 EVENT_T_TIMER_OOC InitialMin, InitialMax, RepeatMin, RepeatMax Expires first between (Param1) and (Param2) and then between every (Param3) and (Param4). but only out of combat.
+2 EVENT_T_HP HPMax%, HPMin%, RepeatMin, RepeatMax Expires when HP is between (Param1) and (Param2). Will repeat every (Param3) and (Param4).
+3 EVENT_T_MANA ManaMax%,ManaMin% RepeatMin, RepeatMax Expires once Mana% is between (Param1) and (Param2). Will repeat every (Param3) and (Param4).
+4 EVENT_T_AGGRO NONE Expires upon initial aggro (does not repeat).
+5 EVENT_T_KILL RepeatMin, RepeatMax Expires upon killing a player. Will repeat between every (Param1) and (Param2).
+6 EVENT_T_DEATH NONE Expires upon Death of the Creature.
+7 EVENT_T_EVADE NONE Expires upon creature EnterEvadeMode().
+8 EVENT_T_SPELLHIT SpellID, School, RepeatMin, RepeatMax Expires upon Spell hit. If (param1) is set will only expire on that spell. If (param2) will only expire on spells of that school (-1 for all). Will repeat every (Param3) and (Param4) .
+9 EVENT_T_RANGE MinDist, MaxDist, RepeatMin, RepeatMax Expires when the highest threat target distance is greater than (Param1) and less than (Param2). Will repeat every (Param3) and (Param4) .
+10 EVENT_T_OOC_LOS NoHostile, NoFriendly, RepeatMin, RepeatMax Expires when a Player moves within visible distance to creature. Does not expire for Hostile Players if (Param1) is not 0. Does not expire for Friendly Players if (Param2) is not 0. Will repeat every (Param3) and (Param4) . Does not expire for creatures or pet or when the creature is in combat.
+11 EVENT_T_SPAWNED NONE Expires at initial spawn and at creature respawn (useful for setting ranged movement type)
+12 EVENT_T_TARGET_HP HPMax%, HPMin%, RepeatMin, RepeatMax Expires when Current Target's HP is between (Param1) and (Param2). Will repeat every (Param3) and (Param4) .
+13 EVENT_T_TARGET_CASTING RepeatMin, RepeatatMax Expires when the current target is casting a spell. Will repeat every (Param1) and (Param2) .
+14 EVENT_T_FRIENDLY_HP HPDeficit, Radius, RepeatMin, RepeatMax Expires when a friendly unit in radius has at least (param1) hp missing. Will repeat every (Param3) and (Param4) .
+15 EVENT_T_FRIENDLY_IS_CC DispelType, Radius, RepeatMin, RepeatMax Expires when a friendly unit is Crowd controlled within the given radius (param2). Will repeat every (Param3) and (Param4) .
+16 EVENT_T_MISSING_BUFF SpellId, Radius, RepeatMin, RepeatMax Expires when a friendly unit is missing aura's given by spell (param1) within radius (param2). Will repeat every (Param3) and (Param4) .
+17 EVENT_T_SUMMONED_UNIT CreatureId, RepeatMin, RepeatMax Expires after creature with entry = (param1) is spawned or for all spawns if param1 = 0. Will repeat every (Param2) and (Param3) .
+
+=========================================
+Action Types
+=========================================
+Below is the list of current Action types that EventAI can handle.
+Each event type has its own specific interpretation of the params that accompany it.
+Params are always read from Param1, then Param2, then Param3.
+
+(# Internal Name Param usage Discription)
+0 ACTION_T_NONE No Action Does Nothing
+1 ACTION_T_SAY TextId Says Text
+2 ACTION_T_YELL TextId Yells Text
+3 ACTION_T_TEXTEMOTE TextId Text Emotes Text
+4 ACTION_T_SOUND SoundId Plays Sound
+5 ACTION_T_EMOTE EmoteId Does emote
+6 ACTION_T_RANDOM_SAY TextId1, TextId2, TextId3 Says random text between 3 params*
+7 ACTION_T_RANDOM_YELL TextId1, TextId2, TextId3 Yells random text between 3 params*
+8 ACTION_T_RANDOM_TEXTEMOTE TextId1, TextId2, TextId3 Text Emotes random text between 3 params*
+9 ACTION_T_RANDOM_SOUND SoundId1, SoundId2, SoundId3 Plays random sound between 3 params*
+10 ACTION_T_RANDOM_EMOTE EmoteId1, EmoteId2, EmoteId3 Emotes random emote between 3 params
+11 ACTION_T_CAST SpellId, Target, CastFlags Casts spell (param1) on target type (param2). Uses Cast Flags (specified below target types)
+12 ACTION_T_SUMMON CreatureID, Target, Duration Summons creature (param1) to attack target (param2) for (param3) duration. Spawns on top of current creature.
+13 ACTION_T_THREAT_SINGLE_PCT Threat%, Target Modifies threat by (param1) on target type (param2)
+14 ACTION_T_THREAT_ALL_PCT Threat% Modifies threat by (param1) on all targets (using -100% on all will result in full aggro dump)
+15 ACTION_T_QUEST_EVENT QuestID, Target Calls AreaExploredOrEventHappens with (param1) for target type (Param2)
+16 ACTION_T_QUEST_CASTCREATUREGO CreatureID, SpellId, Target Sends CastCreatureOrGo for CreatureId (param1) with SpellId (param2) for target (param3)
+17 ACTION_T_SET_UNIT_FIELD Field_Number, Value, Target Sets Unit Field (param1) to Value (param2) on target type (param3)
+18 ACTION_T_SET_UNIT_FLAG Flags, Target Sets flag (flags can be binary OR together to modify multiple flags at once) on for Target type (param2)
+19 ACTION_T_REMOVE_UNIT_FLAG Flags, Target Removes flag (flags can be binary OR together to modify multiple flags at once) on for Target type (param2)
+20 ACTION_T_AUTO_ATTACK AllowAutoAttack 0 = stop melee attack, anything else means continue attacking/allow melee attacking
+21 ACTION_T_COMBAT_MOVEMENT AllowCombatMovement 0 = stop combat based movement, anything else continue/allow combat based movement (targeted movement generator)
+22 ACTION_T_SET_PHASE Phase Sets the current phase to (param1)
+23 ACTION_T_INC_PHASE Value Increments the phase by (param1). May be negative to decrement phase but should not be 0.
+24 ACTION_T_EVADE No Params Forces the creature to evade. Wiping all threat and dropping combat.
+25 ACTION_T_FLEE No Params Causes the .creature to flee. Please use this action instead of directly casting this spell so we may change this when a more correct approach is found.
+26 ACTION_T_QUEST_EVENT_ALL QuestId Calls GroupEventHappens with (param1). Only used if it's _expected_ event should complete for all players in current party
+27 ACTION_T_CASTCREATUREGO_ALL QuestId, SpellId Calls CastedCreatureOrGo for all players on the threat list with QuestID(Param1) and SpellId(Param2)
+28 ACTION_T_REMOVEAURASFROMSPELL Target, Spellid Removes all auras on Target caused by Spellid
+29 ACTION_T_RANGED_MOVEMENT Distance, Angle Changes the movement generator type to a ranged type. Note: Default melee type can still be done with this. Specify 0 angle and 0 distance.
+30 ACTION_T_RANDOM_PHASE PhaseId1, PhaseId2, PhaseId3 Sets the phase to the id between 3 params*
+31 ACTION_T_RANDOM_PHASE_RANGE PhaseMin, PhaseMax Sets the phase to a random id (Phase = PhaseMin + rnd % PhaseMin-PhaseMax). PhaseMax must be greater than PhaseMin.
+32 ACTION_T_SUMMON CreatureID, Target, SummonID Summons creature (param1) to attack target (param2) at location specified by EventAI_Summons (param3).
+33 ACTION_T_KILLED_MONSTER CreatureID, Target Calls KilledMonster (param1) for target of type (param2)
+34 ACTION_T_SET_INST_DATA Field, Data Calls ScriptedInstance::SetData with field (param1) and data (param2)
+35 ACTION_T_SET_INST_DATA64 Field, Target Calls ScriptedInstance::SetData64 with field (param1) and data (param2) target's GUID.
+36 ACTION_T_UPDATE_TEMPLATE TemplateId, Team Changes the creature to a new creature template of (param1) with team = Alliance if (param2) = false or Horde if (param2) = true
+37 ACTION_T_DIE No Params Kills the creature
+38 ACTION_T_ZONE_COMBAT_PULSE No Params Places all players within the instance into combat with the creature. Only works in combat and only works inside of instances.
+
+* = Use -1 to specify that if this param is picked to do nothing. Random is constant between actions within an event. So if you have a random Yell and a random Sound they will match up (ex: param2 with param2)
+
+=========================================
+Event Types
+=========================================
+Note:
+COMBAT ONLY - Means that this event will only trigger durring combat.
+OUT OF COMBAT ONLY - Means that this event will only trigger while out of combat.
+BOTH - This event can trigger both in and out of combat.
+
+Events that do not have lables on them are events that are directly involved with the in and out of combat state.
+
+------------------
+0 = EVENT_T_TIMER:
+------------------
+Parameter 1: InitialMin - Minumum Time used to calculate Random Initial Expire
+Parameter 2: InitialMax - Maximum Time used to calculate Random Initial Expire
+Parameter 3: RepeatMin - Minimum Time used to calculate Random Repeat Expire
+Parameter 4: RepeatMax - Maximum Time used to calculate Random Repeat Expire
+
+COMBAT ONLY! - Expires first between (Param1) and (Param2) and then between every (Param3) and (Param4) from then on.
+This is commonly used for spells that repeat cast during combat (Simulate Spell Cooldown).
+
+----------------------
+1 = EVENT_T_TIMER_OOC:
+----------------------
+Parameter 1: InitialMin - Minumum Time used to calculate Random Initial Expire
+Parameter 2: InitialMax - Maximum Time used to calculate Random Initial Expire
+Parameter 3: RepeatMin - Minimum Time used to calculate Random Repeat Expire
+Parameter 4: RepeatMax - Maximum Time used to calculate Random Repeat Expire
+
+OUT OF COMBAT ONLY! - Expires first between (Param1) and (Param2) and then between every (Param3) and (Param4) from then on.
+This is commonly used for events that occur and repeat outside of combat.
+
+---------------
+2 = EVENT_T_HP:
+---------------
+Parameter 1: HPMax% - Maximum HP% That this Event will Expire
+Parameter 2: HPMin% - Minimum HP% That this Event will Expire
+Parameter 3: RepeatMin - Minimum Time used to calculate Random Repeat Expire
+Parameter 4: RepeatMax - Maximum Time used to calculate Random Repeat Expire
+
+BOTH - Expires when HP is between (Param1) and (Param2). Will repeat every (Param3) and (Param4).
+This is commonly used for events that trigger at a specific HP% (Such as Heal/Enrage Spells or NPC's that Flee).
+
+-----------------
+3 = EVENT_T_MANA:
+-----------------
+Parameter 1: ManaMax% - Maximum Mana% That this Event will Expire
+Parameter 2: ManaMin% - Minimum Mana% That this Event will Expire
+Parameter 3: RepeatMin - Minimum Time used to calculate Random Repeat Expire
+Parameter 4: RepeatMax - Maximum Time used to calculate Random Repeat Expire
+
+BOTH - Expires once Mana% is between (Param1) and (Param2). Will repeat every (Param3) and (Param4).
+This is commonly used for events where an NPC low on Mana will do something (Such as stop casting spells and switch to melee).
+
+------------------
+4 = EVENT_T_AGGRO:
+------------------
+This Event Expires upon initial aggro (does not repeat).
+
+-----------------
+5 = EVENT_T_KILL:
+-----------------
+Parameter 1: RepeatMin - Minimum Time used to calculate Random Repeat Expire
+Parameter 2: RepeatMax - Maximum Time used to calculate Random Repeat Expire
+
+COMBAT ONLY! - Expires upon killing a player. Will repeat every (Param1) and (Param2).
+This Event Expires upon killing a player. It is commonly used for NPC's who yell or do something after killing a player.
+
+------------------
+6 = EVENT_T_DEATH:
+------------------
+This Event Expires upon Death of the Scripted NPC.
+This is commonly used for NPC's who have a yell on death or cast some kind if summon spell when they die.
+
+------------------
+7 = EVENT_T_EVADE:
+------------------
+This Event Expires upon the creature EnterEvadeMode().
+This is commonly used for NPC's who use phases, allows you to reset their phase to 0 upon evade to prevent possible strange behavior.
+
+---------------------
+8 = EVENT_T_SPELLHIT:
+---------------------
+Parameter 1: SpellID - The Spell ID that will trigger the event to occur (NOTE: If you use Spell School as the trigger set this value to 0)
+Parameter 2: School - Spell School to trigger the event (NOTE: If you use a SpellID then set this value to -1) - *See Below for Spell School Bitmask Values*
+Parameter 3: RepeatMin - Minimum Time used to calculate Random Repeat Expire
+Parameter 4: RepeatMax - Maximum Time used to calculate Random Repeat Expire
+
+BOTH - Expires upon Spell hit. If (param1) is set will only expire on that spell. If (param2) will only expire on spells of that school. Will repeat every (Param3) and (Param4).
+This Event is commonly used for NPC's who can do special things when you cast a spell (Or specific spell) on them.
+
+------------------
+9 = EVENT_T_RANGE:
+------------------
+Parameter 1: MinDist - This Distance is the Minimum Distance between the NPC and it's target to allow this Event to Expire
+Parameter 2: MaxDist - This Distance is the Maximum Distance between the NPC and it's target to allow this Event to Expire
+Parameter 3: RepeatMin - Minimum Time used to calculate Random Repeat Expire
+Parameter 4: RepeatMax - Maximum Time used to calculate Random Repeat Expire
+
+COMBAT ONLY! - Expires when the highest threat target distance is greater than (Param1) and less than (Param2). Will repeat every (Param3) and (Param4).
+This Event is commonly used for NPC's who have Ranged Combat and will Throw/Shoot between a certian distance.
+
+---------------------
+10 = EVENT_T_OOC_LOS:
+---------------------
+Parameter 1: NoHostile - This Value is to Prevent this Action from Expiring When Caused by a Player/Creature Hostile to them (0 = Prevent Event from Expiring, 1 = Allow Event to Expire)
+Parameter 2: NoFriendly - This Value is to Prevent this Action from Expiring When Caused by a Player/Creature Friendly to them (0 = Prevent Event from Expiring, 1 = Allow Event to Expire)
+Parameter 3: RepeatMin - Minimum Time used to calculate Random Repeat Expire
+Parameter 4: RepeatMax - Maximum Time used to calculate Random Repeat Expire
+
+OUT OF COMBAT ONLY! - Expires when a Player moves within visible distance to the NPC. Does not expire for Hostile Players if (Param1) is not 0. Does not expire for Friendly Players if (Param2) is not 0. Will repeat every (Param3) and (Param4). Does not expire for creatures or pets when they are in combat.
+This Event is commonly used for NPC's who Do Something or Say Something when you walk past them Out of Combat.
+
+---------------------
+11 = EVENT_T_SPAWNED:
+---------------------
+Expires at initial spawn and at creature respawn.
+This Event is commonly used for setting ranged movement type or Summoning a Pet on Spawn
+
+-----------------------
+12 = EVENT_T_TARGET_HP:
+-----------------------
+Parameter 1: HPMax% - Maximum HP% That this Event will Expire
+Parameter 2: HPMin% - Minimum HP% That this Event will Expire
+Parameter 3: RepeatMin - Minimum Time used to calculate Random Repeat Expire
+Parameter 4: RepeatMax - Maximum Time used to calculate Random Repeat Expire
+
+COMBAT ONLY! - Expires when Current NPC's Target's HP is between (Param1) and (Param2). Will repeat every (Param3) and (Param4).
+This Event is commonly used for NPC's who have a special ability (Like Execute) that only casts when a Player HP is low.
+
+----------------------------
+13 = EVENT_T_TARGET_CASTING:
+----------------------------
+Parameter 1: RepeatMin - Minimum Time used to calculate Random Repeat Expire
+Parameter 2: RepeatMax - Maximum Time used to calculate Random Repeat Expire
+
+COMBAT ONLY! - Expires when the current target is casting a spell. Will repeat every (Param1) and (Param2).
+This event is commonly used for NPC's who will cast a counter spell when their target starts to cast a spell.
+
+-------------------------
+14 = EVENT_T_FRIENDLY_HP:
+-------------------------
+Parameter 1: HPDeficit - This is the Amount of HP Missing from Full HP to trigger this event (You would need to calculate the amount of HP the event happens and subtract that from Full HP Value to get this number)
+Parameter 2: Radius - This is the Range in Yards the NPC will scan for nearby Friendlies (Faction is Friendly To) for the missing amount of HP in Param1.
+Parameter 3: RepeatMin - Minimum Time used to calculate Random Repeat Expire
+Parameter 4: RepeatMax - Maximum Time used to calculate Random Repeat Expire
+
+COMBAT ONLY! - Expires when a friendly unit in radius(param2) has at least (param1) hp missing. Will repeat every (Param3) and (Param4).
+This is commonly used when an NPC in Combat will heal a nearby Friendly NPC in Combat with a Heal/Renew Spell.
+
+----------------------------
+15 = EVENT_T_FRIENDLY_IS_CC:
+----------------------------
+Parameter 1: DispelType - Dispel Type to trigger the event - *See Below for Dispel Bitmask Values*
+Parameter 2: Radius - This is the Range in Yards the NPC will scan for nearby Friendlies being Crowd Controlled
+Parameter 3: RepeatMin - Minimum Time used to calculate Random Repeat Expire
+Parameter 4: RepeatMax - Maximum Time used to calculate Random Repeat Expire
+
+COMBAT ONLY! - Expires when a friendly unit is Crowd controlled within the given radius (param2). Will repeat every (Param3) and (Param4).
+This is commonly used for NPC's who can come to the resule of other Friendly NPC's if being Crowd Controlled
+
+--------------------------
+16 = EVENT_T_MISSING_BUFF:
+--------------------------
+Parameter 1: SpellId - This is the SpellID That the Aura Check will look for (If it is missing this Aura)
+Parameter 2: Radius - This is the Range in Yards the NPC will scan for nearby Friendlies (Faction is Friendly To) for the missing Aura.
+Parameter 3: RepeatMin - Minimum Time used to calculate Random Repeat Expire
+Parameter 4: RepeatMax - Maximum Time used to calculate Random Repeat Expire
+
+BOTH - Expires when a friendly unit is missing aura's given by spell (param1) within radius (param2). Will repeat every (Param3) and (Param4).
+This is commonly used for NPC's who watch friendly units for a debuff to end so they can recast it on them again.
+
+---------------------------
+17 = EVENT_T_SUMMONED_UNIT:
+---------------------------
+Parameter 1: CreatureId - The CreatureID that the NPC is watching to spawn to trigger this event
+Parameter 2: RepeatMin - Minimum Time used to calculate Random Repeat Expire
+Parameter 3: RepeatMax - Maximum Time used to calculate Random Repeat Expire
+
+BOTH - Expires after creature with entry(Param1) is spawned or for all spawns if param1 = 0. Will repeat every (Param2) and (Param3) .
+This is commonly used for NPC's who will do something special once another NPC is summoned. Usually used is Complex Scripts or Special Events.
+
+
+=========================================
+Action Types
+=========================================
+
+-----------------
+1 = ACTION_T_SAY:
+-----------------
+Parameter 1: The ID of the text that the NPC should SAY from the Localized_Texts Table.
+
+This action is pretty straightforward. When activated, the creature will SAY the specified text (Speech Bubble).
+
+------------------
+2 = ACTION_T_YELL:
+------------------
+Parameter 1: The ID of the text that the NPC should YELL from the Localized_Texts Table.
+
+The creature will YELL the specified text (Red Speech Bubble).
+
+-----------------------
+3 = ACTION_T_TEXTEMOTE:
+-----------------------
+Parameter 1: The ID of the text that the NPC should TEXTEMOTE from the Localized_Texts Table.
+
+When activated, the creature will do a text emote using the specified text. A text emote is what you would regularly see by using the /em slash command.
+NOTE: The text should be written with the assumption that the creature's name is the first word or words in the sentence (for example, "is doing a text emote" would appear as "XYZ is doing a text emote" for a creature named XYZ).
+
+-------------------
+4 = ACTION_T_SOUND:
+-------------------
+Parameter 1: The Sound ID to be played. (Sound IDs are contained in the DBC files.)
+
+The creature will play the specified sound.
+This is commonly used for Bosses who Yell and then also have a Voice for the same thing.
+
+-------------------
+5 = ACTION_T_EMOTE:
+-------------------
+Parameter 1: The Emote ID that the creature should perform. (Emote IDs are also contained in the DBC but they can be found in the mangos source as well).
+
+The creature will perform a visual emote. Unlike a text emote, a visual emote is one where the creature will actually move or perform a gesture.
+This is commonly used for NPC's who may perform a special action (Salute, Roar, ect...). Not all player emotes work for creature models.
+
+------------------------
+6 = ACTION_T_RANDOM_SAY:
+------------------------
+Parameter 1: The ID of the text that the NPC should SAY from the Localized_Texts Table (Random Choice #1).
+Parameter 2: The ID of the text that the NPC should SAY from the Localized_Texts Table (Random Choice #2).
+Parameter 3: The ID of the text that the NPC should SAY from the Localized_Texts Table (Random Choice #3).
+
+Similar to the ACTION_T_SAY action, it will choose at random a text entry to SAY. This action will pick a random entry from the three.
+NOTE: If you want any of the options if selected to do nothing you set that Param to -1 value. Then if selected it will do nothing.
+NOTE: When using Random Select Actions, ALL The Actions using Random in a Single Event (Action 1,2,3) will select the SAME Random Choice when it expires. This can come in handy if you are doing a ACTION_T_RANDOM_YELL for Action1 and then the corresponding sounds for ACTION_T_RANDOM_SOUND in Action2. If it selects Random Choice #2 for the Yell it will Select Random Choice #2 for the sound so you can match them up as required.
+This is commonly used for NPC's who have several different Aggro Say's and you would like one of them selected at random.
+
+-------------------------
+7 = ACTION_T_RANDOM_YELL:
+-------------------------
+Parameter 1: The ID of the text that the NPC should YELL from the Localized_Texts Table (Random Choice #1).
+Parameter 2: The ID of the text that the NPC should YELL from the Localized_Texts Table (Random Choice #2).
+Parameter 3: The ID of the text that the NPC should YELL from the Localized_Texts Table (Random Choice #3).
+
+Similar to the ACTION_T_YELL action, it will choose at random a text entry to YELL.
+
+------------------------------
+8 = ACTION_T_RANDOM_TEXTEMOTE:
+------------------------------
+Parameter 1: The ID of the text that the NPC should TEXTEMOTE from the Localized_Texts Table (Random Choice #1).
+Parameter 2: The ID of the text that the NPC should TEXTEMOTE from the Localized_Texts Table (Random Choice #2).
+Parameter 3: The ID of the text that the NPC should TEXTEMOTE from the Localized_Texts Table (Random Choice #3).
+
+Similar to the ACTION_T_TEXTEMOTE action, it will choose at random a text entry to TEXTEMOTE.
+
+--------------------------
+9 = ACTION_T_RANDOM_SOUND:
+--------------------------
+Parameter 1: The Sound ID to be played as Random Choice #1.
+Parameter 2: The Sound ID to be played as Random Choice #2.
+Parameter 3: The Sound ID to be played as Random Choice #3.
+
+Similar to the ACTION_T_SOUND action, it will choose at random a sound to play.
+
+---------------------------
+10 = ACTION_T_RANDOM_EMOTE:
+---------------------------
+Parameter 1: The Emote ID to be played as Random Choice #1.
+Parameter 2: The Emote ID to be played as Random Choice #2.
+Parameter 3: The Emote ID to be played as Random Choice #3.
+
+Similar to the ACTION_T_EMOTE action, it will choose at random an Emote to Visually Perform.
+
+-------------------
+11 = ACTION_T_CAST:
+-------------------
+Parameter 1: SpellId - The Spell ID to use for the NPC to cast. The value used in this field needs to be a valid Spell ID.
+Parameter 2: Target - The Target Type defining who the creature should cast the spell at. The value in this field needs to be a valid Target Type as specified in the reference tables below.
+Parameter 3: CastFlags - See Table Below for Cast Flag Bitmask Values. If you are unsure what to set this value at leave it at 0.
+
+The creature will cast a spell specified by a spell ID on a target specified by the target type.
+This is commonly used for NPC's who cast spells.
+
+---------------------
+12 = ACTION_T_SUMMON:
+---------------------
+Parameter 1: CreatureID - The Creature Template ID to be Summoned. The value here needs to be a valid Creature Template ID.
+Parameter 2: Target - The Target Type defining who the Summoned creature will attack once spawned. The value in this field needs to be a valid Target Type as specified in the reference tables below.
+Parameter 3: Duration - The duration until the summoned creature should be unsummoned AFTER Combat ends. The value in this field is in milliseconds or 0.
+
+The NPC will Summon another creature at the same spot as itself that will attack the specified target.
+NOTE: Almost all Creature Summons have proper Summon Spells that should be used when possible. This Action is a powerful last resort option only to be used if nothing else works.
+NOTE: Using Target Type 0 will cause the Summoned creature to not attack anyone.
+NOTE: If Duration is set at 0, then the summoned creature will not despawn until it has died.
+This is used as a manual way to force an NPC to Summon.
+--------------------------------
+13 = ACTION_T_THREAT_SINGLE_PCT:
+--------------------------------
+Parameter 1: Threat% - Threat percent that should be modified. The value in this field can range from -100 to +100. If it is negative, threat will be taken away and if positive, threat will be added.
+Parameter 2: Target - The Target Type defining on whom the threat change should occur. The value in this field needs to be a valid target type as specified in the reference tables below.
+
+This action will modify the threat of a target in the creature's threat list by the specified percent.
+This is commonly used to allow an NPC to adjust the Threat to a single player.
+
+-----------------------------
+14 = ACTION_T_THREAT_ALL_PCT:
+-----------------------------
+Parameter 1: Threat% - The percent that should be used in modifying everyone's threat in the creature's threat list. The value here can range from -100 to +100.
+
+This action will modify the threat for everyone in the creature's threat list by the specified percent.
+NOTE: Using -100 will cause the creature to reset everyone's threat to 0 so that everyone has the same amount of threat. It will NOT remove anyone from the threat list.
+This is commonly used to allow an NPC to drop threat for all players to zero.
+
+--------------------------
+15 = ACTION_T_QUEST_EVENT:
+--------------------------
+Parameter 1: QuestID - The Quest Template ID. The value here must be a valid quest template ID. Furthermore, the quest should have SpecialFlags | 2 as it would need to be completed by an external event which is the activation of this action.
+Parameter 2: Target - The Target Type defining whom the quest should be completed for. The value in this field needs to be a valid target type as specified in the reference tables below.
+
+This action will satisfy the external completion requirement for the quest for the specified target defined by the target type.
+NOTE: This action can only be used with player targets so it must be ensured that the target type will point to a player.
+This is commonly used for Quests where only ONE player will gain credit for the quest.
+
+-----------------------------
+16 = ACTION_T_CASTCREATUREGO:
+-----------------------------
+Parameter 1: CreatureID - The Creature Template ID to be Summoned. The value here needs to be a valid Creature Template ID.
+Parameter 2: SpellId - The Spell ID to use to simulate the cast. The value used in this field needs to be a valid Spell ID.
+Parameter 3: Target - The Target Type defining whom the quest credit should be given to. The value in this field needs to be a valid target type as specified in the reference tables below.
+
+This action will call CastedCreatureOrGO() function for the player. It can be used to give quest credit for casting a spell on the creature.
+This is commonly used for NPC's who have a special requirement to have a Spell cast on them to complete a quest.
+
+-----------------------------
+17 = ACTION_T_SET_UNIT_FIELD:
+-----------------------------
+Parameter 1: Field_Number - The index of the Field Number to be changed. Use (http://wiki.udbforums.org/index.php/Character_data) for a list of indeces and what they control. Creatures only contain the OBJECT_FIELD_* and UNIT_FIELD_* fields. They do not contain the PLAYER_FIELD_* fields.
+Parameter 2: Value - The new value to be put in the field.
+Parameter 3: Target - The Target Type defining for whom the unit field should be changed. The value in this field needs to be a valid target type as specified in the reference tables below.
+
+When activated, this action can change the target's unit field values. More information on the field value indeces can be found at (http://wiki.udbforums.org/index.php/Character_data)
+
+----------------------------
+18 = ACTION_T_SET_UNIT_FLAG:
+----------------------------
+Parameter 1: Flags - The flag(s) to be set. Multiple flags can be set by using bitwise-OR on them (adding them together).
+Parameter 2: Target - The Target Type defining for whom the flags should be changed. The value in this field needs to be a valid Target Type as specified in the reference tables below.
+
+When activated, this action changes the target's flags by adding (turning on) more flags. For example, this action can make the creature unattackable/unselectable if the right flags are used.
+
+-------------------------------
+19 = ACTION_T_REMOVE_UNIT_FLAG:
+-------------------------------
+Parameter 1: Flags - The flag(s) to be removed. Multiple flags can be set by using bitwise-OR on them (adding them together).
+Parameter 2: Target - The target type defining for whom the flags should be changed. The value in this field needs to be a valid Target Type as specified in the reference tables below.
+
+When activated, this action changes the target's flags by removing (turning off) flags. For example, this action can make the creature normal after it was unattackable/unselectable if the right flags are used.
+
+--------------------------
+20 = ACTION_T_AUTO_ATTACK:
+--------------------------
+Parameter 1: AllowAutoAttack - If zero, then the creature will stop its melee attacks. If non-zero, then the creature will either continue its melee attacks (the action would then have no effect) or it will start its melee attacks on the target with the top threat if its melee attacks were previously stopped.
+
+This action controls whether or not the creature should stop or start the auto melee attack.
+NOTE: The ACID Dev Team has conformed to using either 0 or 1 for the Param values (0 = Stop Melee, 1 = Start Melee).
+This is commonly used in combination with EVENT_T_RANGE and ACTION_T_COMBAT_MOVEMENT for Ranged Combat for Mages and Spell Casters.
+
+------------------------------
+21 = ACTION_T_COMBAT_MOVEMENT:
+------------------------------
+Parameter 1: If zero, then the creature will stop moving towards its victim (if its victim gets out of melee range) and will be stationary. If non-zero, then the creature will either continue to follow its victim (the action would have no effect) or it will start to follow the target with the top threat if its movement was disabled before.
+
+This action controls whether or not the creature will always move towards its target.
+NOTE: The ACID Dev Team has conformed to using either 0 or 1 for the Param values. (0 = Stop Movement, 1 = Start Movement)
+This is commonly used with EVENT_T_RANGE and ACTION_T_AUTO_ATTACK for NPC's who engage in Ranged Comabt (Either Spells or Ranged Attacks)
+
+------------------------
+22 = ACTION_T_SET_PHASE:
+------------------------
+Parameter 1: The new phase to set the creature in. This number must be an integer between 0 and 31. Numbers outside of that range will result in an error.
+
+When activated, this action sets the creature's event to the specified value.
+NOTE: The creature's current Phase is NOT reset at creature evade. You must manually set the phase back to 0 at EVENT_T_RESET.
+NOTE: The value used for the Param is the actual Phase Number (Not The Event_Inverse_Phase_Mask)
+This is commonly used for complex scripts with several phases and you need to switch to a different phase.
+
+------------------------
+23 = ACTION_T_INC_PHASE:
+------------------------
+Parameter 1: Value - The number of phases to increase or decrease. Use negative values to decrease the current phase.
+
+When activated, this action will increase (or decrease) the current creature's phase.
+NOTE: After increasing or decreasing the phase by this action, the current phase must NOT be lower than 0 or exceed 31.
+This can be used instead of ACTION_T_SET_PHASE to change phases in scripts. Just a user friendly option for changing phases.
+
+--------------------
+24 = ACTION_T_EVADE:
+--------------------
+When activated, the creature will immediately exit out of combat, clear its threat list, and move back to its spawn point. Basically, this action will reset the whole encounter.
+NOTE: All Param Values Are 0 for this Action.
+
+-------------------
+25 = ACTION_T_FLEE:
+-------------------
+When activated, the creature will try to flee from combat. Currently this is done by it casting a fear-like spell on itself called "Run Away". A Better Flee system is in Development but will take time before it is implimented.
+NOTE: All Param Values Are 0 for this Action.
+
+------------------------------
+26 = ACTION_T_QUEST_EVENT_ALL:
+------------------------------
+Parameter 1: QuestId - The quest ID to finish for everyone.
+
+This action does the same thing as the ACTION_T_QUEST_EVENT does but it does it for all players in the creature's threat list.
+NOTE: If a player is not in the NPC's threat list for whatever reason, he/she won't get the quest completed.
+
+---------------------------------
+27 = ACTION_T_CASTCREATUREGO_ALL:
+---------------------------------
+Parameter 1: QuestId - The quest template ID.
+Parameter 2: SpellId - The spell ID used to simulate the cast.
+
+This action does the same thing as the ACTION_T_CASTCREATUREGO does but it does it for all players in the creature's threat list.
+NOTE: If a player is not in its threat list for whatever reason, he/she won't receive the cast emulation.
+
+-----------------------------------
+28 = ACTION_T_REMOVEAURASFROMSPELL:
+-----------------------------------
+Parameter 1: Target - The target type defining for whom the unit field should be changed. The value in this field needs to be a valid target type as specified in the reference tables below.
+Parameter 2: SpellId - The spell ID whose auras will be removed.
+
+This action will remove all auras from a specific spell from the target.
+This is commonly used for NPC's who have an OOC Aura that is removed at combat start or a similar idea (Like Stealth or Shape Shift)
+
+------------------------------
+29 = ACTION_T_RANGED_MOVEMENT:
+------------------------------
+Parameter 1: Distance - The distance the mob should keep between it and its target.
+Parameter 2: Angle - The angle the mob should use.
+
+This action changes the movement type generator to ranged type using the specified values for angle and distance.
+NOTE: Specifying zero angle and distance will make it just melee instead.
+This is commonly used for NPC's who always attack at range and you can specify the distance they will maintain from the target.
+
+---------------------------
+30 = ACTION_T_RANDOM_PHASE:
+---------------------------
+Parameter 1: PhaseId1 - A possible random phase choice.
+Parameter 2: PhaseId2 - A possible random phase choice.
+Parameter 3: PhaseId3 - A possible random phase choice.
+
+Randomly sets the phase to one from the three parameter choices.
+NOTE: Use -1 to specify that if this param is picked to do nothing. Random is constant between actions within an event. So if you have a random Yell and a random Sound they will match up (ex: param2 with param2)
+NOTE 2: PLEASE NOTE THAT EACH OF THE PARAM VALUES ARE ACTUAL PHASE NUMBERS NOT THE INVERSE PHASE MASK VALUE.
+This is commonly used for Spellcasting NPC's who on Aggro may select at random a school of spells to use for the fight. Use this if you have up to 3 phases used, otherwise use Action 31 for more then 3 phases.
+
+---------------------------------
+31 = ACTION_T_RANDOM_PHASE_RANGE:
+---------------------------------
+Parameter 1: PhaseMin - The minimum of the phase range.
+Parameter 2: PhaseMax - The maximum of the phase range. The number here must be greater than PhaseMin.
+
+Randomly sets the phase between a range of phases controlled by the parameters. Sets the phase to a random id (Phase = PhaseMin + rnd % PhaseMin-PhaseMax).
+NOTE: PhaseMax must be greater than PhaseMin.
+NOTE 2: PLEASE NOTE THAT EACH OF THE PARAM VALUES ARE ACTUAL PHASE NUMBERS NOT THE INVERSE PHASE MASK VALUE.
+This is commonly used for Spellcasting NPC's who on Aggro may select at random a school of spells to use for the fight. Use this if you have MORE then 3 phases used, otherwise use Action 30.
+
+---------------------
+32 = ACTION_T_SUMMON:
+---------------------
+Parameter 1: CreatureID - The creature template ID to be summoned. The value here needs to be a valid creature template ID.
+Parameter 2: Target - The target type defining who the summoned creature will attack. The value in this field needs to be a valid target type as specified in the reference tables below. NOTE: Using target type 0 will cause the summoned creature to not attack anyone.
+Parameter 3: SummonID - The summon ID from the eventai_summons table controlling the position (and spawntime) where the summoned mob should be spawned at.
+
+Summons creature (param1) to attack target (param2) at location specified by EventAI_Summons (param3).
+NOTE: Param3 Value is the ID Value used for the entry used in EventAI_Summons for this action. You MUST have an EventAI_Summons entry to use this action.
+This is commonly used for NPC's who need to Summon a creature at a specific location. (Normally used for complex events)
+
+-----------------------------
+33 = ACTION_T_KILLED_MONSTER:
+-----------------------------
+Parameter 1: CreatureID - The creature template ID. The value here must be a valid creature template ID.
+Parameter 2: Target - The target type defining whom the quest kill count should be given to. The value in this field needs to be a valid target type as specified in the reference tables below.
+
+When activated, this action will call KilledMonster() function for the player. It can be used to give creature credit for killing a creature. In general if the quest is set to be accompished on different creatures (e.g. "Credit" templates).
+NOTE: It can be ANY creature including certain quest specific triggers
+This is commonly used for giving the player Quest Credits for NPC kills (Many NPC's may use the same CreatureID for the Kill Credit)
+
+----------------------------
+34 = ACTION_T_SET_INST_DATA:
+----------------------------
+Parameter 1: Field - The field to change in the instance script. Again, this field needs to be a valid field that has been already defined in the instance's script.
+Parameter 2: Data - The value to put at that field index.
+
+Sets data for the instance. Note that this will only work when the creature is inside an instantiable zone that has a valid script (ScriptedInstance) assigned.
+NOTE: Param1 Value is located in "def_<instance name>.h" SD2 File and Param2 value is generally found in the "sc_instance.h" file in SD2
+This is commonly used to link an ACID script with a SD2 C++ Script. You make make things happen like opening doors on specific events that happen. ACID Just triggers the C++ Script to function.
+
+------------------------------
+35 = ACTION_T_SET_INST_DATA64:
+------------------------------
+Parameter 1: Field - The field to change in the instance script. Again, this field needs to be a valid field that has been already defined in the instance's script.
+Parameter 2: Target - The target type to use to get the GUID that will be stored at the field index. The value in this field needs to be a valid target type as specified in the reference tables below.
+
+Sets GUID (64 bits) data for the instance based on the target. Note that this will only work when the creature is inside an instantiable zone that has a valid script (ScriptedInstance) assigned.
+Calls ScriptedInstance::SetData64 with field (param1) and data (param2) target's GUID.
+
+------------------------------
+36 = ACTION_T_UPDATE_TEMPLATE:
+------------------------------
+Parameter 1: TemplateId - The creature template ID. The value here must be a valid creature template ID.
+Parameter 2: Team - Use model_id from team : Alliance(0) or Horde (1).
+
+This function temporarily changes creature entry to new entry, display is changed, loot is changed, but AI is not changed. At respawn creature will be reverted to original entry.
+Changes the creature to a new creature template of (param1) with team = Alliance if (param2) = false or Horde if (param2) = true
+
+------------------
+37 = ACTION_T_DIE:
+------------------
+Kills the creature
+This is commonly used if you need to Instakill the creature for one reason or another.
+
+--------------------------------
+38 = ACTION_T_ZONE_COMBAT_PULSE:
+--------------------------------
+Places all players within the instance into combat with the creature. Only works in combat and only works inside of instances.
+
+
+=========================================
+Target Types
+=========================================
+Below is the list of current Target types that EventAI can handle.
+Target types are used by certain actions and may effect actions differently
+
+(# Internal Name Discription)
+0 TARGET_T_SELF Self cast
+1 TARGET_T_HOSTILE Our current target (ie: highest aggro)
+2 TARGET_T_HOSTILE_SECOND_AGGRO Second highest aggro (generaly used for cleaves and some special attacks)
+3 TARGET_T_HOSTILE_LAST_AGGRO Dead last on aggro (no idea what this could be used for)
+4 TARGET_T_HOSTILE_RANDOM Just any random target on our threat list
+5 TARGET_T_HOSTILE_RANDOM_NOT_TOP Any random target except top threat
+6 TARGET_T_ACTION_INVOKER Unit who caused this Event to occur (only works for EVENT_T_AGGRO, EVENT_T_KILL, EVENT_T_DEATH, EVENT_T_SPELLHIT, EVENT_T_OOC_LOS, EVENT_T_FRIENDLY_HP)
+
+=========================================
+Cast Flags
+=========================================
+Below is the list of current Cast Flags that EventAI's spell casting can handle.
+Cast flags are handled bitwise. Bit 0 is Interrupt Previous, bit 1 is triggered, etc.
+So for example the number "3" (11 in Binary, selecting first 2 options) would mean that this cast has both CAST_INTURRUPT_PREVIOUS and CAST_TRIGGERED.
+Another example: the number "5" (101 in Binary, selecting first and third options) would mean that this cast has CAST_INTURRUPT_PREVIOUS and CAST_FORCE_CAST.
+
+(bit# Decimal Internal Name Discription)
+0 1 CAST_INTURRUPT_PREVIOUS Interrupts any previous spell casting (basicaly makes sure that this spell goes off)
+1 2 CAST_TRIGGERED Forces the spell to be instant cast and require no mana/reagents.
+2 4 CAST_FORCE_CAST Forces spell to cast even if the target is possibly out of range or the creature is possibly out of mana
+3 8 CAST_NO_MELEE_IF_OOM Prevents creature from entering melee if out of mana or out of range
+4 16 CAST_FORCE_TARGET_SELF Forces the target to cast this spell on itself
+5 32 CAST_AURA_NOT_PRESENT Only casts the spell on the target if the target does not have the aura from that spell on itself already.
+
+NOTE: You can add the numbers in the decimal column to combine flags.
+ For example if you wanted to use CAST_NO_MELEE_IF_OOM(8) and CAST_TRIGGERED(2) you would simply use 10 in the cast flags field (8 + 2 = 10).
+
+=========================================
+Event Flags
+=========================================
+Below is the list of current Event Flags that EventAI can handle. Event flags are handled bitwise.
+
+(bit# Decimal Internal Name Discription)
+0 1 EFLAG_REPEATABLE Event repeats (Does not repeat if this flag is not set)
+1 2 EFLAG_NORMAL Event occurs in Normal instance difficulty (will not occur in Normal if not set)
+2 4 EFLAG_HEROIC Event occurs in Heroic instance difficulty (will not occur in Heroic if not set)
+3 8
+4 16
+5 32
+6 64
+7 128 EFLAG_DEBUG_ONLY Prevents events from occuring on Release builds of ScriptDev2. Useful for testing new features.
+
NOTE: You can add the numbers in the decimal column to combine flags.
\ No newline at end of file diff --git a/src/bindings/scripts/include/sc_creature.cpp b/src/bindings/scripts/include/sc_creature.cpp index 4c9f7a385a2..8e18b0fc29e 100644 --- a/src/bindings/scripts/include/sc_creature.cpp +++ b/src/bindings/scripts/include/sc_creature.cpp @@ -35,8 +35,8 @@ void ScriptedAI::MoveInLineOfSight(Unit *who) if (!InCombat)
{
- Aggro(who);
InCombat = true;
+ Aggro(who);
}
}
}
@@ -54,8 +54,8 @@ void ScriptedAI::AttackStart(Unit* who) if (!InCombat)
{
- Aggro(who);
InCombat = true;
+ Aggro(who);
}
}
}
@@ -582,8 +582,8 @@ void Scripted_NoMovementAI::MoveInLineOfSight(Unit *who) if (!InCombat)
{
- Aggro(who);
InCombat = true;
+ Aggro(who);
}
}
}
@@ -601,8 +601,8 @@ void Scripted_NoMovementAI::AttackStart(Unit* who) if (!InCombat)
{
- Aggro(who);
InCombat = true;
+ Aggro(who);
}
}
}
diff --git a/src/bindings/scripts/scripts/creature/mob_event_ai.cpp b/src/bindings/scripts/scripts/creature/mob_event_ai.cpp index 4298156af84..ab43165370e 100644 --- a/src/bindings/scripts/scripts/creature/mob_event_ai.cpp +++ b/src/bindings/scripts/scripts/creature/mob_event_ai.cpp @@ -55,8 +55,6 @@ struct MANGOS_DLL_DECL Mob_EventAI : public ScriptedAI case EVENT_T_SPAWNED:
ProcessEvent(*i);
break;
- default:
- break;
}
}
@@ -612,12 +610,18 @@ struct MANGOS_DLL_DECL Mob_EventAI : public ScriptedAI caster = target;
}
- //Interrupt any previous spell
- if (caster->IsNonMeleeSpellCasted(false) && param3 & CAST_INTURRUPT_PREVIOUS)
- caster->InterruptNonMeleeSpells(false);
+ //Allowed to cast only if not casting (unless we interrupt ourself) or if spell is triggered
+ bool canCast = !(caster->IsNonMeleeSpellCasted(false) && (param3 & CAST_TRIGGERED | CAST_INTURRUPT_PREVIOUS));
+
+ // If cast flag CAST_AURA_NOT_PRESENT is active, check if target already has aura on them
+ if(param3 & CAST_AURA_NOT_PRESENT)
+ {
+ for(uint8 i = 0; i < 3; ++i)
+ if(target->HasAura(param1, i))
+ return;
+ }
- //Cast only if not casting or if spell is triggered
- if (param3 & CAST_TRIGGERED || !caster->IsNonMeleeSpellCasted(false))
+ if (canCast)
{
const SpellEntry* tSpell = GetSpellStore()->LookupEntry(param1);
@@ -638,7 +642,14 @@ struct MANGOS_DLL_DECL Mob_EventAI : public ScriptedAI m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim(), AttackDistance, AttackAngle);
}
- }else caster->CastSpell(target, param1, (param3 & CAST_TRIGGERED));
+ }else
+ {
+ //Interrupt any previous spell
+ if (caster->IsNonMeleeSpellCasted(false) && param3 & CAST_INTURRUPT_PREVIOUS)
+ caster->InterruptNonMeleeSpells(false);
+
+ caster->CastSpell(target, param1, (param3 & CAST_TRIGGERED));
+ }
}else if (EAI_ErrorLevel > 0)
error_db_log("SD2: EventAI event %d creature %d attempt to cast spell that doesn't exist %d", EventId, m_creature->GetEntry(), param1);
@@ -996,11 +1007,11 @@ struct MANGOS_DLL_DECL Mob_EventAI : public ScriptedAI error_db_log("SD2: Creature %u using Event %u (Type = %u) has InitialMax < InitialMin. Event disabled.", m_creature->GetEntry(), (*i).Event.event_id, (*i).Event.event_type);
}
break;
- default:
+ //default:
//TODO: enable below code line / verify this is correct to enable events previously disabled (ex. aggro yell), instead of enable this in void Aggro()
//(*i).Enabled = true;
//(*i).Time = 0;
- break;
+ //break;
}
}
}
@@ -1029,8 +1040,6 @@ struct MANGOS_DLL_DECL Mob_EventAI : public ScriptedAI case EVENT_T_EVADE:
ProcessEvent(*i);
break;
- default:
- break;
}
}
}
@@ -1050,8 +1059,6 @@ struct MANGOS_DLL_DECL Mob_EventAI : public ScriptedAI case EVENT_T_DEATH:
ProcessEvent(*i, killer);
break;
- default:
- break;
}
}
}
@@ -1069,8 +1076,6 @@ struct MANGOS_DLL_DECL Mob_EventAI : public ScriptedAI case EVENT_T_KILL:
ProcessEvent(*i, victim);
break;
- default:
- break;
}
}
@@ -1089,8 +1094,6 @@ struct MANGOS_DLL_DECL Mob_EventAI : public ScriptedAI case EVENT_T_SUMMONED_UNIT:
ProcessEvent(*i, pUnit);
break;
- default:
- break;
}
}
}
@@ -1145,8 +1148,8 @@ struct MANGOS_DLL_DECL Mob_EventAI : public ScriptedAI if (!InCombat)
{
- Aggro(who);
InCombat = true;
+ Aggro(who);
}
}
}
@@ -1193,8 +1196,8 @@ struct MANGOS_DLL_DECL Mob_EventAI : public ScriptedAI if (!InCombat)
{
- Aggro(who);
InCombat = true;
+ Aggro(who);
}
}
}
@@ -1215,8 +1218,6 @@ struct MANGOS_DLL_DECL Mob_EventAI : public ScriptedAI ProcessEvent(*i, pUnit);
}
break;
- default:
- break;
}
}
}
diff --git a/src/bindings/scripts/scripts/creature/mob_event_ai.h b/src/bindings/scripts/scripts/creature/mob_event_ai.h index 8c9b06cd5ce..10afd68f736 100644 --- a/src/bindings/scripts/scripts/creature/mob_event_ai.h +++ b/src/bindings/scripts/scripts/creature/mob_event_ai.h @@ -113,6 +113,7 @@ enum CastFlags CAST_FORCE_CAST = 0x04, //Forces cast even if creature is out of mana or out of range
CAST_NO_MELEE_IF_OOM = 0x08, //Prevents creature from entering melee if out of mana or out of range
CAST_FORCE_TARGET_SELF = 0x10, //Forces the target to cast this spell on itself
+ CAST_AURA_NOT_PRESENT = 0x20, //Only casts the spell if the target does not have an aura from the spell
};
enum EventFlags
diff --git a/src/bindings/scripts/scripts/zone/aunchindoun/auchenai_crypts/boss_exarch_maladaar.cpp b/src/bindings/scripts/scripts/zone/aunchindoun/auchenai_crypts/boss_exarch_maladaar.cpp index 529d117aa40..c21804f557f 100644 --- a/src/bindings/scripts/scripts/zone/aunchindoun/auchenai_crypts/boss_exarch_maladaar.cpp +++ b/src/bindings/scripts/scripts/zone/aunchindoun/auchenai_crypts/boss_exarch_maladaar.cpp @@ -104,8 +104,6 @@ struct MANGOS_DLL_DECL mob_stolen_soulAI : public ScriptedAI DoCast(m_creature->getVictim(), SPELL_MOONFIRE);
Class_Timer = 10000;
break;
- default:
- break;
}
}else Class_Timer -= diff;
@@ -204,8 +202,8 @@ struct MANGOS_DLL_DECL boss_exarch_maladaarAI : public ScriptedAI if (!InCombat)
{
- Aggro(who);
InCombat = true;
+ Aggro(who);
}
}
}
diff --git a/src/bindings/scripts/scripts/zone/aunchindoun/mana_tombs/boss_nexusprince_shaffar.cpp b/src/bindings/scripts/scripts/zone/aunchindoun/mana_tombs/boss_nexusprince_shaffar.cpp index 0583638cfc2..4a5e118712a 100644 --- a/src/bindings/scripts/scripts/zone/aunchindoun/mana_tombs/boss_nexusprince_shaffar.cpp +++ b/src/bindings/scripts/scripts/zone/aunchindoun/mana_tombs/boss_nexusprince_shaffar.cpp @@ -106,8 +106,8 @@ struct MANGOS_DLL_DECL boss_nexusprince_shaffarAI : public ScriptedAI if (!InCombat)
{
- Aggro(who);
InCombat = true;
+ Aggro(who);
}
}
}
diff --git a/src/bindings/scripts/scripts/zone/aunchindoun/sethekk_halls/boss_tailonking_ikiss.cpp b/src/bindings/scripts/scripts/zone/aunchindoun/sethekk_halls/boss_tailonking_ikiss.cpp index 0effeedb469..a8712923eac 100644 --- a/src/bindings/scripts/scripts/zone/aunchindoun/sethekk_halls/boss_tailonking_ikiss.cpp +++ b/src/bindings/scripts/scripts/zone/aunchindoun/sethekk_halls/boss_tailonking_ikiss.cpp @@ -115,8 +115,8 @@ struct MANGOS_DLL_DECL boss_talon_king_ikissAI : public ScriptedAI if (!InCombat)
{
- Aggro(who);
InCombat = true;
+ Aggro(who);
}
}
}
diff --git a/src/bindings/scripts/scripts/zone/aunchindoun/shadow_labyrinth/boss_grandmaster_vorpil.cpp b/src/bindings/scripts/scripts/zone/aunchindoun/shadow_labyrinth/boss_grandmaster_vorpil.cpp index 4e1b791b9b6..7fdc0e63824 100644 --- a/src/bindings/scripts/scripts/zone/aunchindoun/shadow_labyrinth/boss_grandmaster_vorpil.cpp +++ b/src/bindings/scripts/scripts/zone/aunchindoun/shadow_labyrinth/boss_grandmaster_vorpil.cpp @@ -125,8 +125,8 @@ struct MANGOS_DLL_DECL boss_grandmaster_vorpilAI : public ScriptedAI if (!InCombat)
{
- Aggro(who);
InCombat = true;
+ Aggro(who);
}
}
}
diff --git a/src/bindings/scripts/scripts/zone/aunchindoun/shadow_labyrinth/boss_murmur.cpp b/src/bindings/scripts/scripts/zone/aunchindoun/shadow_labyrinth/boss_murmur.cpp index 9be5fbfdc99..26e6f423524 100644 --- a/src/bindings/scripts/scripts/zone/aunchindoun/shadow_labyrinth/boss_murmur.cpp +++ b/src/bindings/scripts/scripts/zone/aunchindoun/shadow_labyrinth/boss_murmur.cpp @@ -74,8 +74,8 @@ struct MANGOS_DLL_DECL boss_murmurAI : public ScriptedAI if (!InCombat)
{
- Aggro(who);
InCombat = true;
+ Aggro(who);
}
}
}
@@ -95,8 +95,8 @@ struct MANGOS_DLL_DECL boss_murmurAI : public ScriptedAI if (!InCombat)
{
- Aggro(who);
InCombat = true;
+ Aggro(who);
}
}
}
diff --git a/src/bindings/scripts/scripts/zone/burning_steppes/burning_steppes.cpp b/src/bindings/scripts/scripts/zone/burning_steppes/burning_steppes.cpp index 42e33c67abc..80a1c9d296f 100644 --- a/src/bindings/scripts/scripts/zone/burning_steppes/burning_steppes.cpp +++ b/src/bindings/scripts/scripts/zone/burning_steppes/burning_steppes.cpp @@ -61,8 +61,8 @@ struct MANGOS_DLL_DECL npc_ragged_johnAI : public ScriptedAI if (!InCombat)
{
- Aggro(who);
InCombat = true;
+ Aggro(who);
}
}
}
diff --git a/src/bindings/scripts/scripts/zone/hellfire_citadel/hellfire_ramparts/boss_omor_the_unscarred.cpp b/src/bindings/scripts/scripts/zone/hellfire_citadel/hellfire_ramparts/boss_omor_the_unscarred.cpp index 37eb692fa84..dbb1be4b437 100644 --- a/src/bindings/scripts/scripts/zone/hellfire_citadel/hellfire_ramparts/boss_omor_the_unscarred.cpp +++ b/src/bindings/scripts/scripts/zone/hellfire_citadel/hellfire_ramparts/boss_omor_the_unscarred.cpp @@ -117,8 +117,8 @@ struct MANGOS_DLL_DECL boss_omor_the_unscarredAI : public ScriptedAI if (!InCombat)
{
- Aggro(who);
InCombat = true;
+ Aggro(who);
}
}
}
@@ -138,8 +138,8 @@ struct MANGOS_DLL_DECL boss_omor_the_unscarredAI : public ScriptedAI if (!InCombat)
{
- Aggro(who);
InCombat = true;
+ Aggro(who);
}
}
}
diff --git a/src/bindings/scripts/scripts/zone/hellfire_citadel/hellfire_ramparts/boss_watchkeeper_gargolmar.cpp b/src/bindings/scripts/scripts/zone/hellfire_citadel/hellfire_ramparts/boss_watchkeeper_gargolmar.cpp index f44c384911a..ac6f0d379ed 100644 --- a/src/bindings/scripts/scripts/zone/hellfire_citadel/hellfire_ramparts/boss_watchkeeper_gargolmar.cpp +++ b/src/bindings/scripts/scripts/zone/hellfire_citadel/hellfire_ramparts/boss_watchkeeper_gargolmar.cpp @@ -108,8 +108,8 @@ struct MANGOS_DLL_DECL boss_watchkeeper_gargolmarAI : public ScriptedAI if (!InCombat)
{
- Aggro(who);
InCombat = true;
+ Aggro(who);
}
}
else if (!HasTaunted && m_creature->IsWithinDistInMap(who, 60.0f))
diff --git a/src/bindings/scripts/scripts/zone/hellfire_citadel/shattered_halls/boss_nethekurse.cpp b/src/bindings/scripts/scripts/zone/hellfire_citadel/shattered_halls/boss_nethekurse.cpp index 286913c8881..1638d6070bb 100644 --- a/src/bindings/scripts/scripts/zone/hellfire_citadel/shattered_halls/boss_nethekurse.cpp +++ b/src/bindings/scripts/scripts/zone/hellfire_citadel/shattered_halls/boss_nethekurse.cpp @@ -200,8 +200,8 @@ struct MANGOS_DLL_DECL boss_grand_warlock_nethekurseAI : public ScriptedAI if( !InCombat )
{
- Aggro(who);
InCombat = true;
+ Aggro(who);
}
}
}
@@ -237,8 +237,8 @@ struct MANGOS_DLL_DECL boss_grand_warlock_nethekurseAI : public ScriptedAI if( !InCombat )
{
- Aggro(who);
InCombat = true;
+ Aggro(who);
}
}
}
diff --git a/src/bindings/scripts/scripts/zone/karazhan/boss_prince_malchezaar.cpp b/src/bindings/scripts/scripts/zone/karazhan/boss_prince_malchezaar.cpp index b9f24610e8c..757745d7a15 100644 --- a/src/bindings/scripts/scripts/zone/karazhan/boss_prince_malchezaar.cpp +++ b/src/bindings/scripts/scripts/zone/karazhan/boss_prince_malchezaar.cpp @@ -582,13 +582,13 @@ struct MANGOS_DLL_DECL boss_malchezaarAI : public ScriptedAI if( m_creature->IsWithinDistInMap(m_creature->getVictim(), ATTACK_DISTANCE) && !m_creature->IsNonMeleeSpellCasted(false))
{
//Check for base attack
- if( m_creature->isAttackReady())
+ if( m_creature->isAttackReady() && m_creature->getVictim() )
{
m_creature->AttackerStateUpdate(m_creature->getVictim());
m_creature->resetAttackTimer();
}
//Check for offhand attack
- if( m_creature->isAttackReady(OFF_ATTACK))
+ if( m_creature->isAttackReady(OFF_ATTACK) && m_creature->getVictim() )
{
m_creature->AttackerStateUpdate(m_creature->getVictim(), OFF_ATTACK);
m_creature->resetAttackTimer(OFF_ATTACK);
diff --git a/src/bindings/scripts/scripts/zone/molten_core/instance_molten_core.cpp b/src/bindings/scripts/scripts/zone/molten_core/instance_molten_core.cpp index 53a291affcd..8234fd2ec75 100644 --- a/src/bindings/scripts/scripts/zone/molten_core/instance_molten_core.cpp +++ b/src/bindings/scripts/scripts/zone/molten_core/instance_molten_core.cpp @@ -163,9 +163,6 @@ class MANGOS_DLL_SPEC instance_molten_core : public ScriptedInstance case ID_FLAMEWAKERPRIEST:
FlamewakerPriest = creature->GetGUID();
break;
-
- default:
- return;
}
}
diff --git a/src/bindings/scripts/scripts/zone/netherstorm/netherstorm.cpp b/src/bindings/scripts/scripts/zone/netherstorm/netherstorm.cpp index bb92a16ad25..91af2c5e881 100644 --- a/src/bindings/scripts/scripts/zone/netherstorm/netherstorm.cpp +++ b/src/bindings/scripts/scripts/zone/netherstorm/netherstorm.cpp @@ -264,8 +264,6 @@ struct MANGOS_DLL_DECL npc_manaforge_control_consoleAI : public ScriptedAI }
++Phase;
break;
- default:
- break;
}
} else Event_Timer -= diff;
diff --git a/src/bindings/scripts/scripts/zone/tempest_keep/arcatraz/arcatraz.cpp b/src/bindings/scripts/scripts/zone/tempest_keep/arcatraz/arcatraz.cpp index 4be37887e3d..b7f985f2804 100644 --- a/src/bindings/scripts/scripts/zone/tempest_keep/arcatraz/arcatraz.cpp +++ b/src/bindings/scripts/scripts/zone/tempest_keep/arcatraz/arcatraz.cpp @@ -138,8 +138,8 @@ struct MANGOS_DLL_DECL npc_millhouse_manastormAI : public ScriptedAI if (!InCombat)
{
- Aggro(who);
InCombat = true;
+ Aggro(who);
}
}
}
@@ -153,8 +153,8 @@ struct MANGOS_DLL_DECL npc_millhouse_manastormAI : public ScriptedAI if (!InCombat)
{
- Aggro(who);
InCombat = true;
+ Aggro(who);
}
}
}
@@ -444,8 +444,6 @@ struct MANGOS_DLL_DECL npc_warden_mellicharAI : public ScriptedAI case 7:
pInstance->SetData(TYPE_WARDEN_5,IN_PROGRESS);
break;
- default:
- break;
}
CanSpawn = true;
}
@@ -505,8 +503,6 @@ struct MANGOS_DLL_DECL npc_warden_mellicharAI : public ScriptedAI DoYell(YELL_WELCOME,LANG_UNIVERSAL,NULL);
DoPlaySoundToSet(m_creature,SOUND_WELCOME);
break;
- default:
- break;
}
CanSpawn = false;
++Phase;
@@ -553,8 +549,6 @@ struct MANGOS_DLL_DECL npc_warden_mellicharAI : public ScriptedAI DoPrepareForPhase();
EventProgress_Timer = 15000;
break;
- default:
- break;
}
}
} else EventProgress_Timer -= diff;
diff --git a/src/bindings/scripts/scripts/zone/tempest_keep/arcatraz/boss_harbinger_skyriss.cpp b/src/bindings/scripts/scripts/zone/tempest_keep/arcatraz/boss_harbinger_skyriss.cpp index 739bf237e16..470650db16b 100644 --- a/src/bindings/scripts/scripts/zone/tempest_keep/arcatraz/boss_harbinger_skyriss.cpp +++ b/src/bindings/scripts/scripts/zone/tempest_keep/arcatraz/boss_harbinger_skyriss.cpp @@ -130,8 +130,8 @@ struct MANGOS_DLL_DECL boss_harbinger_skyrissAI : public ScriptedAI if (!InCombat)
{
- Aggro(who);
InCombat = true;
+ Aggro(who);
}
}
}
@@ -148,8 +148,8 @@ struct MANGOS_DLL_DECL boss_harbinger_skyrissAI : public ScriptedAI if( !InCombat )
{
- Aggro(who);
InCombat = true;
+ Aggro(who);
}
}
}
@@ -236,8 +236,6 @@ struct MANGOS_DLL_DECL boss_harbinger_skyrissAI : public ScriptedAI case 3:
Intro = true;
break;
- default:
- break;
}
}else Intro_Timer -=diff;
}
diff --git a/src/bindings/scripts/scripts/zone/tempest_keep/the_eye/boss_kaelthas.cpp b/src/bindings/scripts/scripts/zone/tempest_keep/the_eye/boss_kaelthas.cpp index 78eabcde19f..9f0e0fe7107 100644 --- a/src/bindings/scripts/scripts/zone/tempest_keep/the_eye/boss_kaelthas.cpp +++ b/src/bindings/scripts/scripts/zone/tempest_keep/the_eye/boss_kaelthas.cpp @@ -215,8 +215,8 @@ struct MANGOS_DLL_DECL advisorbase_ai : public ScriptedAI if (!InCombat)
{
- Aggro(who);
InCombat = true;
+ Aggro(who);
}
}
}
@@ -234,8 +234,8 @@ struct MANGOS_DLL_DECL advisorbase_ai : public ScriptedAI if (!InCombat)
{
- Aggro(who);
InCombat = true;
+ Aggro(who);
}
}
}
@@ -863,14 +863,10 @@ struct MANGOS_DLL_DECL boss_kaelthasAI : public ScriptedAI DoYell(SAY_SUMMON_PHOENIX1, LANG_UNIVERSAL, NULL);
DoPlaySoundToSet(m_creature, SOUND_SUMMON_PHOENIX1);
break;
-
case 1:
DoYell(SAY_SUMMON_PHOENIX2, LANG_UNIVERSAL, NULL);
DoPlaySoundToSet(m_creature, SOUND_SUMMON_PHOENIX2);
break;
-
- default:
- break;
}
Phoenix_Timer = 60000;
@@ -1252,8 +1248,8 @@ struct MANGOS_DLL_DECL boss_grand_astromancer_capernianAI : public advisorbase_a if (!InCombat)
{
- Aggro(who);
InCombat = true;
+ Aggro(who);
}
}
}
diff --git a/src/bindings/scripts/scripts/zone/temple_of_ahnqiraj/boss_twinemperors.cpp b/src/bindings/scripts/scripts/zone/temple_of_ahnqiraj/boss_twinemperors.cpp index 16528b6c595..e7bd93f5461 100644 --- a/src/bindings/scripts/scripts/zone/temple_of_ahnqiraj/boss_twinemperors.cpp +++ b/src/bindings/scripts/scripts/zone/temple_of_ahnqiraj/boss_twinemperors.cpp @@ -665,8 +665,8 @@ struct MANGOS_DLL_DECL boss_veklorAI : public boss_twinemperorsAI if (!InCombat)
{
- Aggro(who);
InCombat = true;
+ Aggro(who);
}
}
}
diff --git a/src/game/Chat.cpp b/src/game/Chat.cpp index d9ec554ee0d..e85053a2204 100644 --- a/src/game/Chat.cpp +++ b/src/game/Chat.cpp @@ -34,62 +34,6 @@ bool ChatHandler::load_command_table = true;
-LanguageDesc lang_description[LANGUAGES_COUNT] =
-{
- { LANG_ADDON, 0, 0 },
- { LANG_UNIVERSAL, 0, 0 },
- { LANG_ORCISH, 669, SKILL_LANG_ORCISH },
- { LANG_DARNASSIAN, 671, SKILL_LANG_DARNASSIAN },
- { LANG_TAURAHE, 670, SKILL_LANG_TAURAHE },
- { LANG_DWARVISH, 672, SKILL_LANG_DWARVEN },
- { LANG_COMMON, 668, SKILL_LANG_COMMON },
- { LANG_DEMONIC, 815, SKILL_LANG_DEMON_TONGUE },
- { LANG_TITAN, 816, SKILL_LANG_TITAN },
- { LANG_THALASSIAN, 813, SKILL_LANG_THALASSIAN },
- { LANG_DRACONIC, 814, SKILL_LANG_DRACONIC },
- { LANG_KALIMAG, 817, SKILL_LANG_OLD_TONGUE },
- { LANG_GNOMISH, 7340, SKILL_LANG_GNOMISH },
- { LANG_TROLL, 7341, SKILL_LANG_TROLL },
- { LANG_GUTTERSPEAK, 17737, SKILL_LANG_GUTTERSPEAK },
- { LANG_DRAENEI, 29932, SKILL_LANG_DRAENEI },
- { LANG_ZOMBIE, 0, 0 },
- { LANG_GNOMISH_BINARY, 0, 0 },
- { LANG_GOBLIN_BINARY, 0, 0 }
-};
-
-LanguageDesc const* GetLanguageDescByID(uint32 lang)
-{
- for(int i = 0; i < LANGUAGES_COUNT; ++i)
- {
- if(uint32(lang_description[i].lang_id) == lang)
- return &lang_description[i];
- }
-
- return NULL;
-}
-
-LanguageDesc const* GetLanguageDescBySpell(uint32 spell_id)
-{
- for(int i = 0; i < LANGUAGES_COUNT; ++i)
- {
- if(lang_description[i].spell_id == spell_id)
- return &lang_description[i];
- }
-
- return NULL;
-}
-
-LanguageDesc const* GetLanguageDescBySkill(uint32 skill_id)
-{
- for(int i = 0; i < LANGUAGES_COUNT; ++i)
- {
- if(lang_description[i].skill_id == skill_id)
- return &lang_description[i];
- }
-
- return NULL;
-}
-
ChatCommand * ChatHandler::getCommandTable()
{
static ChatCommand serverCommandTable[] =
diff --git a/src/game/Chat.h b/src/game/Chat.h index 96b813bfe8a..26e3a3a969a 100644 --- a/src/game/Chat.h +++ b/src/game/Chat.h @@ -28,19 +28,6 @@ class Player; class Unit;
struct GameTele;
-struct LanguageDesc
-{
- Language lang_id;
- uint32 spell_id;
- uint32 skill_id;
-};
-
-extern LanguageDesc lang_description[LANGUAGES_COUNT];
-
-LanguageDesc const* GetLanguageDescByID(uint32 lang);
-LanguageDesc const* GetLanguageDescBySpell(uint32 spell_id);
-LanguageDesc const* GetLanguageDescBySkill(uint32 skill_id);
-
class ChatCommand
{
public:
diff --git a/src/game/Creature.cpp b/src/game/Creature.cpp index d0800c9b77f..555bb4db02a 100644 --- a/src/game/Creature.cpp +++ b/src/game/Creature.cpp @@ -62,15 +62,36 @@ TrainerSpell const* TrainerSpellData::Find(uint32 spell_id) const return NULL;
}
+bool VendorItemData::RemoveItem( uint32 item_id )
+{
+ for(VendorItemList::iterator i = m_items.begin(); i != m_items.end(); ++i )
+ {
+ if((*i)->item==item_id)
+ {
+ m_items.erase(i);
+ return true;
+ }
+ }
+ return false;
+}
+
+VendorItem const* VendorItemData::FindItem(uint32 item_id) const
+{
+ for(VendorItemList::const_iterator i = m_items.begin(); i != m_items.end(); ++i )
+ if((*i)->item==item_id)
+ return *i;
+ return NULL;
+}
+
Creature::Creature() :
Unit(), i_AI(NULL),
lootForPickPocketed(false), lootForBody(false), m_groupLootTimer(0), lootingGroupLeaderGUID(0),
-m_itemsLoaded(false), m_lootMoney(0), m_lootRecipient(0),
+m_lootMoney(0), m_lootRecipient(0),
m_deathTimer(0), m_respawnTime(0), m_respawnDelay(25), m_corpseDelay(60), m_respawnradius(0.0f),
m_gossipOptionLoaded(false),m_emoteState(0), m_isPet(false), m_isTotem(false),
m_regenTimer(2000), m_defaultMovementType(IDLE_MOTION_TYPE), m_equipmentId(0),
m_AlreadyCallAssistence(false), m_regenHealth(true), m_AI_locked(false), m_isDeadByDefault(false),
-m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL),m_creatureInfo(NULL)
+m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL),m_creatureInfo(NULL), m_DBTableGuid(0)
{
m_valuesCount = UNIT_END;
@@ -87,7 +108,7 @@ Creature::~Creature() {
CleanupsBeforeDelete();
- m_vendor_items.clear();
+ m_vendorItemCounts.clear();
delete i_AI;
i_AI = NULL;
@@ -503,6 +524,7 @@ bool Creature::Create (uint32 guidlow, Map *map, uint32 Entry, uint32 team, cons m_corpseDelay = sWorld.getConfig(CONFIG_CORPSE_DECAY_NORMAL);
break;
}
+ LoadCreaturesAddon();
}
return bResult;
@@ -673,16 +695,16 @@ void Creature::prepareGossipMenu( Player *pPlayer,uint32 gossipid ) cantalking=false;
break;
case GOSSIP_OPTION_VENDOR:
- // load vendor items if not yet
- LoadGoods();
-
- if(!GetItemCount())
+ {
+ VendorItemData const* vItems = GetVendorItems();
+ if(!vItems || vItems->Empty())
{
sLog.outErrorDb("Creature %u (Entry: %u) have UNIT_NPC_FLAG_VENDOR but have empty trading item list.",
GetGUIDLow(),GetEntry());
cantalking=false;
}
break;
+ }
case GOSSIP_OPTION_TRAINER:
if(!isCanTrainingOf(pPlayer,false))
cantalking=false;
@@ -934,6 +956,9 @@ uint32 Creature::GetGossipTextId(uint32 action, uint32 zoneid) uint32 Creature::GetNpcTextId()
{
+ if (!m_DBTableGuid)
+ return DEFAULT_GOSSIP_MESSAGE;
+
if(uint32 pos = objmgr.GetNpcGossip(m_DBTableGuid))
return pos;
@@ -1046,6 +1071,8 @@ void Creature::SaveToDB() void Creature::SaveToDB(uint32 mapid, uint8 spawnMask)
{
// update in loaded data
+ if (!m_DBTableGuid)
+ m_DBTableGuid = GetGUIDLow();
CreatureData& data = objmgr.NewOrExistCreatureData(m_DBTableGuid);
uint32 displayId = GetNativeDisplayId();
@@ -1237,7 +1264,6 @@ bool Creature::CreateFromProto(uint32 guidlow, uint32 Entry, uint32 team, const Object::_Create(guidlow, Entry, HIGHGUID_UNIT);
- m_DBTableGuid = guidlow;
if(!UpdateEntry(Entry, team, data))
return false;
@@ -1263,7 +1289,7 @@ bool Creature::LoadFromDB(uint32 guid, Map *map) return false;
}
- uint32 stored_guid = guid;
+ m_DBTableGuid = guid;
if (map->GetInstanceId() != 0) guid = objmgr.GenerateLowGuid(HIGHGUID_UNIT);
uint16 team = 0;
@@ -1278,9 +1304,6 @@ bool Creature::LoadFromDB(uint32 guid, Map *map) return false;
}
- m_DBTableGuid = stored_guid;
- LoadCreaturesAddon();
-
m_respawnradius = data->spawndist;
m_respawnDelay = data->spawntimesecs;
@@ -1346,24 +1369,6 @@ void Creature::LoadEquipment(uint32 equip_entry, bool force) }
}
-void Creature::LoadGoods()
-{
- // already loaded;
- if(m_itemsLoaded)
- return;
-
- m_vendor_items.clear();
-
- VendorItemList const* vList = objmgr.GetNpcVendorItemList(GetEntry());
- if(!vList)
- return;
-
- for (VendorItemList::const_iterator _item_iter = vList->begin(); _item_iter != vList->end(); ++_item_iter)
- AddItem( (*_item_iter)->item, (*_item_iter)->maxcount, (*_item_iter)->incrtime, (*_item_iter)->ExtendedCost);
-
- m_itemsLoaded = true;
-}
-
bool Creature::hasQuest(uint32 quest_id) const
{
QuestRelations const& qr = objmgr.mCreatureQuestRelations;
@@ -1388,6 +1393,12 @@ bool Creature::hasInvolvedQuest(uint32 quest_id) const void Creature::DeleteFromDB()
{
+ if (!m_DBTableGuid)
+ {
+ sLog.outDebug("Trying to delete not saved creature!");
+ return;
+ }
+
objmgr.SaveCreatureRespawnTime(m_DBTableGuid,GetInstanceId(),0);
objmgr.DeleteCreatureData(m_DBTableGuid);
@@ -1494,7 +1505,8 @@ void Creature::Respawn() if(getDeathState()==DEAD)
{
- objmgr.SaveCreatureRespawnTime(m_DBTableGuid,GetInstanceId(),0);
+ if (m_DBTableGuid)
+ objmgr.SaveCreatureRespawnTime(m_DBTableGuid,GetInstanceId(),0);
m_respawnTime = time(NULL); // respawn at next tick
}
}
@@ -1716,7 +1728,7 @@ void Creature::CallAssistence() void Creature::SaveRespawnTime()
{
- if(isPet())
+ if(isPet() || !m_DBTableGuid)
return;
if(m_respawnTime > time(NULL)) // dead (no corpse)
@@ -1752,8 +1764,11 @@ bool Creature::IsOutOfThreatArea(Unit* pVictim) const CreatureDataAddon const* Creature::GetCreatureAddon() const
{
- if(CreatureDataAddon const* addon = ObjectMgr::GetCreatureAddon(m_DBTableGuid))
- return addon;
+ if (m_DBTableGuid)
+ {
+ if(CreatureDataAddon const* addon = ObjectMgr::GetCreatureAddon(m_DBTableGuid))
+ return addon;
+ }
// dependent from heroic mode entry
return ObjectMgr::GetCreatureTemplateAddon(GetCreatureInfo()->Entry);
@@ -1956,6 +1971,84 @@ char const* Creature::GetScriptName() const return ObjectMgr::GetCreatureTemplate(GetEntry())->ScriptName;
}
+
+VendorItemData const* Creature::GetVendorItems() const
+{
+ return objmgr.GetNpcVendorItemList(GetEntry());
+}
+
+uint32 Creature::GetVendorItemCurrentCount(VendorItem const* vItem)
+{
+ if(!vItem->maxcount)
+ return vItem->maxcount;
+
+ VendorItemCounts::iterator itr = m_vendorItemCounts.begin();
+ for(; itr != m_vendorItemCounts.end(); ++itr)
+ if(itr->itemId==vItem->item)
+ break;
+
+ if(itr == m_vendorItemCounts.end())
+ return vItem->maxcount;
+
+ VendorItemCount* vCount = &*itr;
+
+ time_t ptime = time(NULL);
+
+ if( vCount->lastIncrementTime + vItem->incrtime <= ptime )
+ {
+ ItemPrototype const* pProto = objmgr.GetItemPrototype(vItem->item);
+
+ uint32 diff = uint32((ptime - vCount->lastIncrementTime)/vItem->incrtime);
+ if((vCount->count + diff * pProto->BuyCount) >= vItem->maxcount )
+ {
+ m_vendorItemCounts.erase(itr);
+ return vItem->maxcount;
+ }
+
+ vCount->count += diff * pProto->BuyCount;
+ vCount->lastIncrementTime = ptime;
+ }
+
+ return vCount->count;
+}
+
+uint32 Creature::UpdateVendorItemCurrentCount(VendorItem const* vItem, uint32 used_count)
+{
+ if(!vItem->maxcount)
+ return 0;
+
+ VendorItemCounts::iterator itr = m_vendorItemCounts.begin();
+ for(; itr != m_vendorItemCounts.end(); ++itr)
+ if(itr->itemId==vItem->item)
+ break;
+
+ if(itr == m_vendorItemCounts.end())
+ {
+ uint32 new_count = vItem->maxcount > used_count ? vItem->maxcount-used_count : 0;
+ m_vendorItemCounts.push_back(VendorItemCount(vItem->item,new_count));
+ return new_count;
+ }
+
+ VendorItemCount* vCount = &*itr;
+
+ time_t ptime = time(NULL);
+
+ if( vCount->lastIncrementTime + vItem->incrtime <= ptime )
+ {
+ ItemPrototype const* pProto = objmgr.GetItemPrototype(vItem->item);
+
+ uint32 diff = uint32((ptime - vCount->lastIncrementTime)/vItem->incrtime);
+ if((vCount->count + diff * pProto->BuyCount) < vItem->maxcount )
+ vCount->count += diff * pProto->BuyCount;
+ else
+ vCount->count = vItem->maxcount;
+ }
+
+ vCount->count = vCount->count > used_count ? vCount->count-used_count : 0;
+ vCount->lastIncrementTime = ptime;
+ return vCount->count;
+}
+
TrainerSpellData const* Creature::GetTrainerSpells() const
{
return objmgr.GetNpcTrainerSpells(GetEntry());
diff --git a/src/game/Creature.h b/src/game/Creature.h index 2ade7c48695..6145b946892 100644 --- a/src/game/Creature.h +++ b/src/game/Creature.h @@ -112,19 +112,6 @@ struct GossipOption std::string Option;
};
-struct CreatureItem
-{
- CreatureItem(uint32 _item, uint32 _maxcount, uint32 _incrtime, uint32 _ExtendedCost)
- : id(_item), count(_maxcount), maxcount(_maxcount), incrtime(_incrtime), ExtendedCost(_ExtendedCost), lastincr((uint32)time(NULL)) {}
-
- uint32 id;
- uint32 count;
- uint32 maxcount;
- uint32 incrtime;
- uint32 lastincr;
- uint32 ExtendedCost;
-};
-
enum CreatureFlagsExtra
{
CREATURE_FLAG_EXTRA_INSTANCE_BIND = 0x00000001, // creature kill bind instance with killer and killer's group
@@ -291,6 +278,56 @@ enum InhabitTypeValues #pragma pack(pop)
#endif
+// Vendors
+struct VendorItem
+{
+ VendorItem(uint32 _item, uint32 _maxcount, uint32 _incrtime, uint32 _ExtendedCost)
+ : item(_item), maxcount(_maxcount), incrtime(_incrtime), ExtendedCost(_ExtendedCost) {}
+
+ uint32 item;
+ uint32 maxcount; // 0 for infinity item amount
+ uint32 incrtime; // time for restore items amount if maxcount != 0
+ uint32 ExtendedCost;
+};
+typedef std::vector<VendorItem*> VendorItemList;
+
+struct VendorItemData
+{
+ VendorItemList m_items;
+
+ VendorItem* GetItem(uint32 slot) const
+ {
+ if(slot>=m_items.size()) return NULL;
+ return m_items[slot];
+ }
+ bool Empty() const { return m_items.empty(); }
+ uint8 GetItemCount() const { return m_items.size(); }
+ void AddItem( uint32 item, uint32 maxcount, uint32 ptime, uint32 ExtendedCost)
+ {
+ m_items.push_back(new VendorItem(item, maxcount, ptime, ExtendedCost));
+ }
+ bool RemoveItem( uint32 item_id );
+ VendorItem const* FindItem(uint32 item_id) const;
+
+ void Clear()
+ {
+ for (VendorItemList::iterator itr = m_items.begin(); itr != m_items.end(); ++itr)
+ delete (*itr);
+ }
+};
+
+struct VendorItemCount
+{
+ explicit VendorItemCount(uint32 _item, uint32 _count)
+ : itemId(_item), count(_count), lastIncrementTime(time(NULL)) {}
+
+ uint32 itemId;
+ uint32 count;
+ time_t lastIncrementTime;
+};
+
+typedef std::list<VendorItemCount> VendorItemCounts;
+
struct TrainerSpell
{
uint32 spell;
@@ -418,41 +455,9 @@ class MANGOS_DLL_SPEC Creature : public Unit uint32 GetCurrentEquipmentId() { return m_equipmentId; }
float GetSpellDamageMod(int32 Rank);
- /*********************************************************/
- /*** VENDOR SYSTEM ***/
- /*********************************************************/
- void LoadGoods(); // must be called before access to vendor items, lazy loading at first call
- void ReloadGoods() { m_itemsLoaded = false; LoadGoods(); }
-
- CreatureItem* GetItem(uint32 slot)
- {
- if(slot>=m_vendor_items.size()) return NULL;
- return &m_vendor_items[slot];
- }
- uint8 GetItemCount() const { return m_vendor_items.size(); }
- void AddItem( uint32 item, uint32 maxcount, uint32 ptime, uint32 ExtendedCost)
- {
- m_vendor_items.push_back(CreatureItem(item, maxcount, ptime, ExtendedCost));
- }
- bool RemoveItem( uint32 item_id )
- {
- for(CreatureItems::iterator i = m_vendor_items.begin(); i != m_vendor_items.end(); ++i )
- {
- if(i->id==item_id)
- {
- m_vendor_items.erase(i);
- return true;
- }
- }
- return false;
- }
- CreatureItem* FindItem(uint32 item_id)
- {
- for(CreatureItems::iterator i = m_vendor_items.begin(); i != m_vendor_items.end(); ++i )
- if(i->id==item_id)
- return &*i;
- return NULL;
- }
+ VendorItemData const* GetVendorItems() const;
+ uint32 GetVendorItemCurrentCount(VendorItem const* vItem);
+ uint32 UpdateVendorItemCurrentCount(VendorItem const* vItem, uint32 used_count);
TrainerSpellData const* GetTrainerSpells() const;
@@ -562,9 +567,7 @@ class MANGOS_DLL_SPEC Creature : public Unit bool InitEntry(uint32 entry, uint32 team=ALLIANCE, const CreatureData* data=NULL);
// vendor items
- typedef std::vector<CreatureItem> CreatureItems;
- CreatureItems m_vendor_items;
- bool m_itemsLoaded; // vendor items loading state
+ VendorItemCounts m_vendorItemCounts;
void _RealtimeSetCreatureInfo();
@@ -592,7 +595,7 @@ class MANGOS_DLL_SPEC Creature : public Unit uint32 m_regenTimer;
MovementGeneratorType m_defaultMovementType;
Cell m_currentCell; // store current cell where creature listed
- uint32 m_DBTableGuid;
+ uint32 m_DBTableGuid; ///< For new or temporary creatures is 0 for saved it is lowguid
uint32 m_equipmentId;
bool m_AlreadyCallAssistence;
diff --git a/src/game/GameObject.cpp b/src/game/GameObject.cpp index c3b8cf3d6e4..dc69b830760 100644 --- a/src/game/GameObject.cpp +++ b/src/game/GameObject.cpp @@ -1,1253 +1,1247 @@ -/* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "Common.h" -#include "QuestDef.h" -#include "GameObject.h" -#include "ObjectMgr.h" -#include "SpellMgr.h" -#include "Spell.h" -#include "UpdateMask.h" -#include "Opcodes.h" -#include "WorldPacket.h" -#include "WorldSession.h" -#include "World.h" -#include "Database/DatabaseEnv.h" -#include "MapManager.h" -#include "LootMgr.h" -#include "GridNotifiers.h" -#include "GridNotifiersImpl.h" -#include "CellImpl.h" -#include "InstanceData.h" -#include "BattleGround.h" -#include "Util.h" - -GameObject::GameObject() : WorldObject() -{ - m_objectType |= TYPEMASK_GAMEOBJECT; - m_objectTypeId = TYPEID_GAMEOBJECT; - // 2.3.2 - 0x58 - m_updateFlag = (UPDATEFLAG_LOWGUID | UPDATEFLAG_HIGHGUID | UPDATEFLAG_HASPOSITION); - - m_valuesCount = GAMEOBJECT_END; - m_respawnTime = 0; - m_respawnDelayTime = 25; - m_lootState = GO_NOT_READY; - m_spawnedByDefault = true; - m_usetimes = 0; - m_spellId = 0; - m_charges = 5; - m_cooldownTime = 0; - m_goInfo = NULL; -} - -GameObject::~GameObject() -{ - if(m_uint32Values) // field array can be not exist if GameOBject not loaded - { - // crash possable at access to deleted GO in Unit::m_gameobj - uint64 owner_guid = GetOwnerGUID(); - if(owner_guid) - { - Unit* owner = ObjectAccessor::GetUnit(*this,owner_guid); - if(owner) - owner->RemoveGameObject(this,false); - else if(!IS_PLAYER_GUID(owner_guid)) - sLog.outError("Delete GameObject (GUID: %u Entry: %u ) that have references in not found creature %u GO list. Crash possable later.",GetGUIDLow(),GetGOInfo()->id,GUID_LOPART(owner_guid)); - } - } -} - -void GameObject::AddToWorld() -{ - ///- Register the gameobject for guid lookup - if(!IsInWorld()) ObjectAccessor::Instance().AddObject(this); - Object::AddToWorld(); -} - -void GameObject::RemoveFromWorld() -{ - ///- Remove the gameobject from the accessor - if(IsInWorld()) ObjectAccessor::Instance().RemoveObject(this); - Object::RemoveFromWorld(); -} - -bool GameObject::Create(uint32 guidlow, uint32 name_id, Map *map, float x, float y, float z, float ang, float rotation0, float rotation1, float rotation2, float rotation3, uint32 animprogress, uint32 go_state) -{ - Relocate(x,y,z,ang); - SetMapId(map->GetId()); - SetInstanceId(map->GetInstanceId()); - - if(!IsPositionValid()) - { - sLog.outError("ERROR: Gameobject (GUID: %u Entry: %u ) not created. Suggested coordinates isn't valid (X: %f Y: %f)",guidlow,name_id,x,y); - return false; - } - - GameObjectInfo const* goinfo = objmgr.GetGameObjectInfo(name_id); - if (!goinfo) - { - sLog.outErrorDb("Gameobject (GUID: %u Entry: %u) not created: it have not exist entry in `gameobject_template`. Map: %u (X: %f Y: %f Z: %f) ang: %f rotation0: %f rotation1: %f rotation2: %f rotation3: %f",guidlow, name_id, map->GetId(), x, y, z, ang, rotation0, rotation1, rotation2, rotation3); - return false; - } - - Object::_Create(guidlow, goinfo->id, HIGHGUID_GAMEOBJECT); - - m_DBTableGuid = guidlow; - m_goInfo = goinfo; - - if (goinfo->type >= MAX_GAMEOBJECT_TYPE) - { - sLog.outErrorDb("Gameobject (GUID: %u Entry: %u) not created: it have not exist GO type '%u' in `gameobject_template`. It's will crash client if created.",guidlow,name_id,goinfo->type); - return false; - } - - SetFloatValue(GAMEOBJECT_POS_X, x); - SetFloatValue(GAMEOBJECT_POS_Y, y); - SetFloatValue(GAMEOBJECT_POS_Z, z); - SetFloatValue(GAMEOBJECT_FACING, ang); //this is not facing angle - - SetFloatValue (GAMEOBJECT_ROTATION, rotation0); - SetFloatValue (GAMEOBJECT_ROTATION+1, rotation1); - SetFloatValue (GAMEOBJECT_ROTATION+2, rotation2); - SetFloatValue (GAMEOBJECT_ROTATION+3, rotation3); - - SetFloatValue(OBJECT_FIELD_SCALE_X, goinfo->size); - - SetUInt32Value(GAMEOBJECT_FACTION, goinfo->faction); - SetUInt32Value(GAMEOBJECT_FLAGS, goinfo->flags); - - SetUInt32Value(OBJECT_FIELD_ENTRY, goinfo->id); - - SetUInt32Value(GAMEOBJECT_DISPLAYID, goinfo->displayId); - - SetGoState(go_state); - SetGoType(GameobjectTypes(goinfo->type)); - - SetGoAnimProgress(animprogress); - - // Spell charges for GAMEOBJECT_TYPE_SPELLCASTER (22) - if (goinfo->type == GAMEOBJECT_TYPE_SPELLCASTER) - m_charges = goinfo->spellcaster.charges; - - //Notify the map's instance data. - //Only works if you create the object in it, not if it is moves to that map. - //Normally non-players do not teleport to other maps. - if(map->IsDungeon() && ((InstanceMap*)map)->GetInstanceData()) - { - ((InstanceMap*)map)->GetInstanceData()->OnObjectCreate(this); - } - - return true; -} - -void GameObject::Update(uint32 /*p_time*/) -{ - if (IS_MO_TRANSPORT(GetGUID())) - { - //((Transport*)this)->Update(p_time); - return; - } - - switch (m_lootState) - { - case GO_NOT_READY: - { - switch(GetGoType()) - { - case GAMEOBJECT_TYPE_TRAP: - { - // Arming Time for GAMEOBJECT_TYPE_TRAP (6) - Unit* owner = GetOwner(); - if (owner && ((Player*)owner)->isInCombat()) - m_cooldownTime = time(NULL) + GetGOInfo()->trap.startDelay; - m_lootState = GO_READY; - break; - } - case GAMEOBJECT_TYPE_FISHINGNODE: - { - // fishing code (bobber ready) - if( time(NULL) > m_respawnTime - FISHING_BOBBER_READY_TIME ) - { - // splash bobber (bobber ready now) - Unit* caster = GetOwner(); - if(caster && caster->GetTypeId()==TYPEID_PLAYER) - { - SetGoState(0); - SetUInt32Value(GAMEOBJECT_FLAGS, 32); - - UpdateData udata; - WorldPacket packet; - BuildValuesUpdateBlockForPlayer(&udata,((Player*)caster)); - udata.BuildPacket(&packet); - ((Player*)caster)->GetSession()->SendPacket(&packet); - - WorldPacket data(SMSG_GAMEOBJECT_CUSTOM_ANIM,8+4); - data << GetGUID(); - data << (uint32)(0); - ((Player*)caster)->SendMessageToSet(&data,true); - } - - m_lootState = GO_READY; // can be succesfully open with some chance - } - return; - } - default: - m_lootState = GO_READY; // for other GOis same switched without delay to GO_READY - break; - } - // NO BREAK for switch (m_lootState) - } - case GO_READY: - { - if (m_respawnTime > 0) // timer on - { - if (m_respawnTime <= time(NULL)) // timer expired - { - m_respawnTime = 0; - m_SkillupList.clear(); - m_usetimes = 0; - - switch (GetGoType()) - { - case GAMEOBJECT_TYPE_FISHINGNODE: // can't fish now - { - Unit* caster = GetOwner(); - if(caster && caster->GetTypeId()==TYPEID_PLAYER) - { - if(caster->m_currentSpells[CURRENT_CHANNELED_SPELL]) - { - caster->m_currentSpells[CURRENT_CHANNELED_SPELL]->SendChannelUpdate(0); - caster->m_currentSpells[CURRENT_CHANNELED_SPELL]->finish(false); - } - - WorldPacket data(SMSG_FISH_NOT_HOOKED,0); - ((Player*)caster)->GetSession()->SendPacket(&data); - } - // can be delete - m_lootState = GO_JUST_DEACTIVATED; - return; - } - case GAMEOBJECT_TYPE_DOOR: - case GAMEOBJECT_TYPE_BUTTON: - //we need to open doors if they are closed (add there another condition if this code breaks some usage, but it need to be here for battlegrounds) - if( !GetGoState() ) - SwitchDoorOrButton(false); - //flags in AB are type_button and we need to add them here so no break! - default: - if(!m_spawnedByDefault) // despawn timer - { - // can be despawned or destroyed - SetLootState(GO_JUST_DEACTIVATED); - return; - } - // respawn timer - MapManager::Instance().GetMap(GetMapId(), this)->Add(this); - break; - } - } - } - - // traps can have time and can not have - GameObjectInfo const* goInfo = GetGOInfo(); - if(goInfo->type == GAMEOBJECT_TYPE_TRAP) - { - // traps - Unit* owner = GetOwner(); - Unit* ok = NULL; // pointer to appropriate target if found any - - if(m_cooldownTime >= time(NULL)) - return; - - bool IsBattleGroundTrap = false; - //FIXME: this is activation radius (in different casting radius that must be selected from spell data) - //TODO: move activated state code (cast itself) to GO_ACTIVATED, in this place only check activating and set state - float radius = goInfo->trap.radius; - if(!radius) - { - if(goInfo->trap.cooldown != 3) // cast in other case (at some triggring/linked go/etc explicit call) - return; - else - { - if(m_respawnTime > 0) - break; - - radius = goInfo->trap.cooldown; // battlegrounds gameobjects has data2 == 0 && data5 == 3 - IsBattleGroundTrap = true; - } - } - - bool NeedDespawn = (goInfo->trap.charges != 0); - - CellPair p(MaNGOS::ComputeCellPair(GetPositionX(),GetPositionY())); - Cell cell(p); - cell.data.Part.reserved = ALL_DISTRICT; - - // Note: this hack with search required until GO casting not implemented - // search unfriendly creature - if(owner && NeedDespawn) // hunter trap - { - MaNGOS::AnyUnfriendlyUnitInObjectRangeCheck u_check(this, owner, radius); - MaNGOS::UnitSearcher<MaNGOS::AnyUnfriendlyUnitInObjectRangeCheck> checker(ok, u_check); - - CellLock<GridReadGuard> cell_lock(cell, p); - - TypeContainerVisitor<MaNGOS::UnitSearcher<MaNGOS::AnyUnfriendlyUnitInObjectRangeCheck>, GridTypeMapContainer > grid_object_checker(checker); - cell_lock->Visit(cell_lock, grid_object_checker, *MapManager::Instance().GetMap(GetMapId(), this)); - - // or unfriendly player/pet - if(!ok) - { - TypeContainerVisitor<MaNGOS::UnitSearcher<MaNGOS::AnyUnfriendlyUnitInObjectRangeCheck>, WorldTypeMapContainer > world_object_checker(checker); - cell_lock->Visit(cell_lock, world_object_checker, *MapManager::Instance().GetMap(GetMapId(), this)); - } - } - else // environmental trap - { - // environmental damage spells already have around enemies targeting but this not help in case not existed GO casting support - - // affect only players - Player* p_ok = NULL; - MaNGOS::AnyPlayerInObjectRangeCheck p_check(this, radius); - MaNGOS::PlayerSearcher<MaNGOS::AnyPlayerInObjectRangeCheck> checker(p_ok, p_check); - - CellLock<GridReadGuard> cell_lock(cell, p); - - TypeContainerVisitor<MaNGOS::PlayerSearcher<MaNGOS::AnyPlayerInObjectRangeCheck>, WorldTypeMapContainer > world_object_checker(checker); - cell_lock->Visit(cell_lock, world_object_checker, *MapManager::Instance().GetMap(GetMapId(), this)); - ok = p_ok; - } - - if (ok) - { - Unit *caster = owner ? owner : ok; - - caster->CastSpell(ok, goInfo->trap.spellId, true); - m_cooldownTime = time(NULL) + 4; // 4 seconds - - if(NeedDespawn) - SetLootState(GO_JUST_DEACTIVATED); // can be despawned or destroyed - - if(IsBattleGroundTrap && ok->GetTypeId() == TYPEID_PLAYER) - { - //BattleGround gameobjects case - if(((Player*)ok)->InBattleGround()) - if(BattleGround *bg = ((Player*)ok)->GetBattleGround()) - bg->HandleTriggerBuff(GetGUID()); - } - } - } - - if (m_charges && m_usetimes >= m_charges) - SetLootState(GO_JUST_DEACTIVATED); // can be despawned or destroyed - - break; - } - case GO_ACTIVATED: - { - switch(GetGoType()) - { - case GAMEOBJECT_TYPE_DOOR: - case GAMEOBJECT_TYPE_BUTTON: - if(GetAutoCloseTime() && (m_cooldownTime < time(NULL))) - { - SwitchDoorOrButton(false); - SetLootState(GO_JUST_DEACTIVATED); - } - break; - } - break; - } - case GO_JUST_DEACTIVATED: - { - //if Gameobject should cast spell, then this, but some GOs (type = 10) should be destroyed - if (GetGoType() == GAMEOBJECT_TYPE_GOOBER) - { - uint32 spellId = GetGOInfo()->goober.spellId; - - if(spellId) - { - std::set<uint32>::iterator it = m_unique_users.begin(); - std::set<uint32>::iterator end = m_unique_users.end(); - for (; it != end; it++) - { - Unit* owner = Unit::GetUnit(*this, uint64(*it)); - if (owner) owner->CastSpell(owner, spellId, false); - } - - m_unique_users.clear(); - m_usetimes = 0; - } - //any return here in case battleground traps - } - - if(GetOwnerGUID()) - { - m_respawnTime = 0; - Delete(); - return; - } - - //burning flags in some battlegrounds, if you find better condition, just add it - if (GetGoAnimProgress() > 0) - { - SendObjectDeSpawnAnim(this->GetGUID()); - //reset flags - SetUInt32Value(GAMEOBJECT_FLAGS, GetGOInfo()->flags); - } - - loot.clear(); - SetLootState(GO_READY); - - if(!m_respawnDelayTime) - return; - - if(!m_spawnedByDefault) - { - m_respawnTime = 0; - return; - } - - m_respawnTime = time(NULL) + m_respawnDelayTime; - - // if option not set then object will be saved at grid unload - if(sWorld.getConfig(CONFIG_SAVE_RESPAWN_TIME_IMMEDIATLY)) - SaveRespawnTime(); - - ObjectAccessor::UpdateObjectVisibility(this); - - break; - } - } -} - -void GameObject::Refresh() -{ - // not refresh despawned not casted GO (despawned casted GO destroyed in all cases anyway) - if(m_respawnTime > 0 && m_spawnedByDefault) - return; - - if(isSpawned()) - MapManager::Instance().GetMap(GetMapId(), this)->Add(this); -} - -void GameObject::AddUniqueUse(Player* player) -{ - AddUse(); - m_unique_users.insert(player->GetGUIDLow()); -} - -void GameObject::Delete() -{ - SendObjectDeSpawnAnim(GetGUID()); - - SetGoState(1); - SetUInt32Value(GAMEOBJECT_FLAGS, GetGOInfo()->flags); - - AddObjectToRemoveList(); -} - -void GameObject::getFishLoot(Loot *fishloot) -{ - fishloot->clear(); - - uint32 subzone = GetAreaId(); - - // if subzone loot exist use it - if(LootTemplates_Fishing.HaveLootFor(subzone)) - fishloot->FillLoot(subzone, LootTemplates_Fishing, NULL); - // else use zone loot - else - fishloot->FillLoot(GetZoneId(), LootTemplates_Fishing, NULL); -} - -void GameObject::SaveToDB() -{ - // this should only be used when the creature has already been loaded - // perferably after adding to map, because mapid may not be valid otherwise - GameObjectData const *data = objmgr.GetGOData(m_DBTableGuid); - if(!data) - { - sLog.outError("GameObject::SaveToDB failed, cannot get gameobject data!"); - return; - } - - SaveToDB(GetMapId(), data->spawnMask); -} - -void GameObject::SaveToDB(uint32 mapid, uint8 spawnMask) -{ - const GameObjectInfo *goI = GetGOInfo(); - - if (!goI) - return; - - // update in loaded data (changing data only in this place) - GameObjectData& data = objmgr.NewGOData(m_DBTableGuid); - - // data->guid = guid don't must be update at save - data.id = GetEntry(); - data.mapid = mapid; - data.posX = GetFloatValue(GAMEOBJECT_POS_X); - data.posY = GetFloatValue(GAMEOBJECT_POS_Y); - data.posZ = GetFloatValue(GAMEOBJECT_POS_Z); - data.orientation = GetFloatValue(GAMEOBJECT_FACING); - data.rotation0 = GetFloatValue(GAMEOBJECT_ROTATION+0); - data.rotation1 = GetFloatValue(GAMEOBJECT_ROTATION+1); - data.rotation2 = GetFloatValue(GAMEOBJECT_ROTATION+2); - data.rotation3 = GetFloatValue(GAMEOBJECT_ROTATION+3); - data.spawntimesecs = m_spawnedByDefault ? m_respawnDelayTime : -(int32)m_respawnDelayTime; - data.animprogress = GetGoAnimProgress(); - data.go_state = GetGoState(); - data.spawnMask = spawnMask; - - // updated in DB - std::ostringstream ss; - ss << "INSERT INTO gameobject VALUES ( " - << m_DBTableGuid << ", " - << GetUInt32Value (OBJECT_FIELD_ENTRY) << ", " - << mapid << ", " - << (uint32)spawnMask << ", " - << GetFloatValue(GAMEOBJECT_POS_X) << ", " - << GetFloatValue(GAMEOBJECT_POS_Y) << ", " - << GetFloatValue(GAMEOBJECT_POS_Z) << ", " - << GetFloatValue(GAMEOBJECT_FACING) << ", " - << GetFloatValue(GAMEOBJECT_ROTATION) << ", " - << GetFloatValue(GAMEOBJECT_ROTATION+1) << ", " - << GetFloatValue(GAMEOBJECT_ROTATION+2) << ", " - << GetFloatValue(GAMEOBJECT_ROTATION+3) << ", " - << m_respawnDelayTime << ", " - << GetGoAnimProgress() << ", " - << GetGoState() << ")"; - - WorldDatabase.BeginTransaction(); - WorldDatabase.PExecuteLog("DELETE FROM gameobject WHERE guid = '%u'", m_DBTableGuid); - WorldDatabase.PExecuteLog( ss.str( ).c_str( ) ); - WorldDatabase.CommitTransaction(); -} - -bool GameObject::LoadFromDB(uint32 guid, Map *map) -{ - GameObjectData const* data = objmgr.GetGOData(guid); - - if( !data ) - { - sLog.outErrorDb("ERROR: Gameobject (GUID: %u) not found in table `gameobject`, can't load. ",guid); - return false; - } - - uint32 entry = data->id; - uint32 map_id = data->mapid; - float x = data->posX; - float y = data->posY; - float z = data->posZ; - float ang = data->orientation; - - float rotation0 = data->rotation0; - float rotation1 = data->rotation1; - float rotation2 = data->rotation2; - float rotation3 = data->rotation3; - - uint32 animprogress = data->animprogress; - uint32 go_state = data->go_state; - - uint32 stored_guid = guid; - if (map->GetInstanceId() != 0) guid = objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT); - - if (!Create(guid,entry, map, x, y, z, ang, rotation0, rotation1, rotation2, rotation3, animprogress, go_state) ) - return false; - - m_DBTableGuid = stored_guid; - - switch(GetGOInfo()->type) - { - case GAMEOBJECT_TYPE_DOOR: - case GAMEOBJECT_TYPE_BUTTON: - /* this code (in comment) isn't correct because in battlegrounds we need despawnable doors and buttons, pls remove - SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NODESPAWN); - m_spawnedByDefault = true; - m_respawnDelayTime = 0; - m_respawnTime = 0; - break;*/ - default: - if(data->spawntimesecs >= 0) - { - m_spawnedByDefault = true; - m_respawnDelayTime = data->spawntimesecs; - m_respawnTime = objmgr.GetGORespawnTime(stored_guid, map->GetInstanceId()); - - // ready to respawn - if(m_respawnTime && m_respawnTime <= time(NULL)) - { - m_respawnTime = 0; - objmgr.SaveGORespawnTime(m_DBTableGuid,GetInstanceId(),0); - } - } - else - { - m_spawnedByDefault = false; - m_respawnDelayTime = -data->spawntimesecs; - m_respawnTime = 0; - } - break; - } - - return true; -} - -void GameObject::DeleteFromDB() -{ - objmgr.SaveGORespawnTime(m_DBTableGuid,GetInstanceId(),0); - objmgr.DeleteGOData(m_DBTableGuid); - WorldDatabase.PExecuteLog("DELETE FROM gameobject WHERE guid = '%u'", m_DBTableGuid); - WorldDatabase.PExecuteLog("DELETE FROM game_event_gameobject WHERE guid = '%u'", m_DBTableGuid); -} - -GameObject* GameObject::GetGameObject(WorldObject& object, uint64 guid) -{ - return ObjectAccessor::GetGameObject(object,guid); -} - -GameObjectInfo const *GameObject::GetGOInfo() const -{ - return m_goInfo; -} - -uint32 GameObject::GetLootId(GameObjectInfo const* ginfo) -{ - if (!ginfo) - return 0; - - switch(ginfo->type) - { - case GAMEOBJECT_TYPE_CHEST: - return ginfo->chest.lootId; - case GAMEOBJECT_TYPE_FISHINGHOLE: - return ginfo->fishinghole.lootId; - case GAMEOBJECT_TYPE_FISHINGNODE: - return ginfo->fishnode.lootId; - default: - return 0; - } -} - -/*********************************************************/ -/*** QUEST SYSTEM ***/ -/*********************************************************/ -bool GameObject::hasQuest(uint32 quest_id) const -{ - QuestRelations const& qr = objmgr.mGOQuestRelations; - for(QuestRelations::const_iterator itr = qr.lower_bound(GetEntry()); itr != qr.upper_bound(GetEntry()); ++itr) - { - if(itr->second==quest_id) - return true; - } - return false; -} - -bool GameObject::hasInvolvedQuest(uint32 quest_id) const -{ - QuestRelations const& qr = objmgr.mGOQuestInvolvedRelations; - for(QuestRelations::const_iterator itr = qr.lower_bound(GetEntry()); itr != qr.upper_bound(GetEntry()); ++itr) - { - if(itr->second==quest_id) - return true; - } - return false; -} - -bool GameObject::IsTransport() const -{ - // If something is marked as a transport, don't transmit an out of range packet for it. - GameObjectInfo const * gInfo = GetGOInfo(); - if(!gInfo) return false; - return gInfo->type == GAMEOBJECT_TYPE_TRANSPORT || gInfo->type == GAMEOBJECT_TYPE_MO_TRANSPORT; -} - -Unit* GameObject::GetOwner() const -{ - return ObjectAccessor::GetUnit(*this, GetOwnerGUID()); -} - -void GameObject::SaveRespawnTime() -{ - if(m_respawnTime > time(NULL) && m_spawnedByDefault) - objmgr.SaveGORespawnTime(m_DBTableGuid,GetInstanceId(),m_respawnTime); -} - -bool GameObject::isVisibleForInState(Player const* u, bool inVisibleList) const -{ - // Not in world - if(!IsInWorld() || !u->IsInWorld()) - return false; - - // Transport always visible at this step implementation - if(IsTransport() && IsInMap(u)) - return true; - - // quick check visibility false cases for non-GM-mode - if(!u->isGameMaster()) - { - // despawned and then not visible for non-GM in GM-mode - if(!isSpawned()) - return false; - - // special invisibility cases - /* TODO: implement trap stealth, take look at spell 2836 - if(GetGOInfo()->type == GAMEOBJECT_TYPE_TRAP && GetGOInfo()->trap.stealthed && u->IsHostileTo(GetOwner())) - { - if(check stuff here) - return false; - }*/ - - // Smuggled Mana Cell required 10 invisibility type detection/state - if(GetEntry()==187039 && ((u->m_detectInvisibilityMask | u->m_invisibilityMask) & (1<<10))==0) - return false; - } - - // check distance - return IsWithinDistInMap(u,World::GetMaxVisibleDistanceForObject() + - (inVisibleList ? World::GetVisibleObjectGreyDistance() : 0.0f) ); -} - -void GameObject::Respawn() -{ - if(m_spawnedByDefault && m_respawnTime > 0) - { - m_respawnTime = time(NULL); - objmgr.SaveGORespawnTime(m_DBTableGuid,GetInstanceId(),0); - } -} - -bool GameObject::ActivateToQuest( Player *pTarget)const -{ - if(!objmgr.IsGameObjectForQuests(GetEntry())) - return false; - - switch(GetGoType()) - { - // scan GO chest with loot including quest items - case GAMEOBJECT_TYPE_CHEST: - { - if(LootTemplates_Gameobject.HaveQuestLootForPlayer(GetLootId(), pTarget)) - return true; - break; - } - case GAMEOBJECT_TYPE_GOOBER: - { - if(pTarget->GetQuestStatus(GetGOInfo()->goober.questId) == QUEST_STATUS_INCOMPLETE) - return true; - break; - } - default: - break; - } - - return false; -} - -void GameObject::TriggeringLinkedGameObject( uint32 trapEntry, Unit* target) -{ - GameObjectInfo const* trapInfo = sGOStorage.LookupEntry<GameObjectInfo>(trapEntry); - if(!trapInfo || trapInfo->type!=GAMEOBJECT_TYPE_TRAP) - return; - - SpellEntry const* trapSpell = sSpellStore.LookupEntry(trapInfo->trap.spellId); - if(!trapSpell) // checked at load already - return; - - float range = GetSpellMaxRange(sSpellRangeStore.LookupEntry(trapSpell->rangeIndex)); - - // search nearest linked GO - GameObject* trapGO = NULL; - { - // using original GO distance - CellPair p(MaNGOS::ComputeCellPair(GetPositionX(), GetPositionY())); - Cell cell(p); - cell.data.Part.reserved = ALL_DISTRICT; - - MaNGOS::NearestGameObjectEntryInObjectRangeCheck go_check(*target,trapEntry,range); - MaNGOS::GameObjectLastSearcher<MaNGOS::NearestGameObjectEntryInObjectRangeCheck> checker(trapGO,go_check); - - TypeContainerVisitor<MaNGOS::GameObjectLastSearcher<MaNGOS::NearestGameObjectEntryInObjectRangeCheck>, GridTypeMapContainer > object_checker(checker); - CellLock<GridReadGuard> cell_lock(cell, p); - cell_lock->Visit(cell_lock, object_checker, *MapManager::Instance().GetMap(GetMapId(), this)); - } - - // found correct GO - // FIXME: when GO casting will be implemented trap must cast spell to target - if(trapGO) - target->CastSpell(target,trapSpell,true); -} - -GameObject* GameObject::LookupFishingHoleAround(float range) -{ - GameObject* ok = NULL; - - CellPair p(MaNGOS::ComputeCellPair(GetPositionX(),GetPositionY())); - Cell cell(p); - cell.data.Part.reserved = ALL_DISTRICT; - MaNGOS::NearestGameObjectFishingHole u_check(*this, range); - MaNGOS::GameObjectSearcher<MaNGOS::NearestGameObjectFishingHole> checker(ok, u_check); - - CellLock<GridReadGuard> cell_lock(cell, p); - - TypeContainerVisitor<MaNGOS::GameObjectSearcher<MaNGOS::NearestGameObjectFishingHole>, GridTypeMapContainer > grid_object_checker(checker); - cell_lock->Visit(cell_lock, grid_object_checker, *MapManager::Instance().GetMap(GetMapId(), this)); - - return ok; -} - -void GameObject::UseDoorOrButton(uint32 time_to_restore) -{ - if(m_lootState != GO_READY) - return; - - if(!time_to_restore) - time_to_restore = GetAutoCloseTime(); - - SwitchDoorOrButton(true); - SetLootState(GO_ACTIVATED); - - m_cooldownTime = time(NULL) + time_to_restore; - -} - -void GameObject::SwitchDoorOrButton(bool activate) -{ - if(activate) - SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_IN_USE); - else - RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_IN_USE); - - if(GetGoState()) //if closed -> open - SetGoState(0); - else //if open -> close - SetGoState(1); -} - -void GameObject::Use(Unit* user) -{ - // by default spell caster is user - Unit* spellCaster = user; - uint32 spellId = 0; - - switch(GetGoType()) - { - case GAMEOBJECT_TYPE_DOOR: //0 - case GAMEOBJECT_TYPE_BUTTON: //1 - //doors/buttons never really despawn, only reset to default state/flags - UseDoorOrButton(); - - // activate script - sWorld.ScriptsStart(sGameObjectScripts, GetDBTableGUIDLow(), spellCaster, this); - return; - - case GAMEOBJECT_TYPE_QUESTGIVER: //2 - { - if(user->GetTypeId()!=TYPEID_PLAYER) - return; - - Player* player = (Player*)user; - - player->PrepareQuestMenu( GetGUID() ); - player->SendPreparedQuest( GetGUID() ); - return; - } - //Sitting: Wooden bench, chairs enzz - case GAMEOBJECT_TYPE_CHAIR: //7 - { - GameObjectInfo const* info = GetGOInfo(); - if(!info) - return; - - if(user->GetTypeId()!=TYPEID_PLAYER) - return; - - Player* player = (Player*)user; - - // a chair may have n slots. we have to calculate their positions and teleport the player to the nearest one - - // check if the db is sane - if(info->chair.slots > 0) - { - float lowestDist = DEFAULT_VISIBILITY_DISTANCE; - - float x_lowest = GetPositionX(); - float y_lowest = GetPositionY(); - - // the object orientation + 1/2 pi - // every slot will be on that straight line - float orthogonalOrientation = GetOrientation()+M_PI*0.5f; - // find nearest slot - for(uint32 i=0; i<info->chair.slots; i++) - { - // the distance between this slot and the center of the go - imagine a 1D space - float relativeDistance = (info->size*i)-(info->size*(info->chair.slots-1)/2.0f); - - float x_i = GetPositionX() + relativeDistance * cos(orthogonalOrientation); - float y_i = GetPositionY() + relativeDistance * sin(orthogonalOrientation); - - // calculate the distance between the player and this slot - float thisDistance = player->GetDistance2d(x_i, y_i); - - /* debug code. It will spawn a npc on each slot to visualize them. - Creature* helper = player->SummonCreature(14496, x_i, y_i, GetPositionZ(), GetOrientation(), TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 10000); - std::ostringstream output; - output << i << ": thisDist: " << thisDistance; - helper->MonsterSay(output.str().c_str(), LANG_UNIVERSAL, 0); - */ - - if(thisDistance <= lowestDist) - { - lowestDist = thisDistance; - x_lowest = x_i; - y_lowest = y_i; - } - } - player->TeleportTo(GetMapId(), x_lowest, y_lowest, GetPositionZ(), GetOrientation(),TELE_TO_NOT_LEAVE_TRANSPORT | TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET); - } - else - { - // fallback, will always work - player->TeleportTo(GetMapId(), GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation(),TELE_TO_NOT_LEAVE_TRANSPORT | TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET); - } - player->SetStandState(PLAYER_STATE_SIT_LOW_CHAIR+info->chair.height); - return; - } - //big gun, its a spell/aura - case GAMEOBJECT_TYPE_GOOBER: //10 - { - GameObjectInfo const* info = GetGOInfo(); - - if(user->GetTypeId()==TYPEID_PLAYER) - { - Player* player = (Player*)user; - - // show page - if(info->goober.pageId) - { - WorldPacket data(SMSG_GAMEOBJECT_PAGETEXT, 8); - data << GetGUID(); - player->GetSession()->SendPacket(&data); - } - - // possible quest objective for active quests - player->CastedCreatureOrGO(info->id, GetGUID(), 0); - } - - // cast this spell later if provided - spellId = info->goober.spellId; - - break; - } - case GAMEOBJECT_TYPE_CAMERA: //13 - { - GameObjectInfo const* info = GetGOInfo(); - if(!info) - return; - - if(user->GetTypeId()!=TYPEID_PLAYER) - return; - - Player* player = (Player*)user; - - if(info->camera.cinematicId) - { - WorldPacket data(SMSG_TRIGGER_CINEMATIC, 4); - data << info->camera.cinematicId; - player->GetSession()->SendPacket(&data); - } - return; - } - //fishing bobber - case GAMEOBJECT_TYPE_FISHINGNODE: //17 - { - if(user->GetTypeId()!=TYPEID_PLAYER) - return; - - Player* player = (Player*)user; - - if(player->GetGUID() != GetOwnerGUID()) - return; - - switch(getLootState()) - { - case GO_READY: // ready for loot - { - // 1) skill must be >= base_zone_skill - // 2) if skill == base_zone_skill => 5% chance - // 3) chance is linear dependence from (base_zone_skill-skill) - - uint32 subzone = GetAreaId(); - - int32 zone_skill = objmgr.GetFishingBaseSkillLevel( subzone ); - if(!zone_skill) - zone_skill = objmgr.GetFishingBaseSkillLevel( GetZoneId() ); - - //provide error, no fishable zone or area should be 0 - if(!zone_skill) - sLog.outErrorDb("Fishable areaId %u are not properly defined in `skill_fishing_base_level`.",subzone); - - int32 skill = player->GetSkillValue(SKILL_FISHING); - int32 chance = skill - zone_skill + 5; - int32 roll = irand(1,100); - - DEBUG_LOG("Fishing check (skill: %i zone min skill: %i chance %i roll: %i",skill,zone_skill,chance,roll); - - if(skill >= zone_skill && chance >= roll) - { - // prevent removing GO at spell cancel - player->RemoveGameObject(this,false); - SetOwnerGUID(player->GetGUID()); - - //fish catched - player->UpdateFishingSkill(); - - GameObject* ok = LookupFishingHoleAround(DEFAULT_VISIBILITY_DISTANCE); - if (ok) - { - player->SendLoot(ok->GetGUID(),LOOT_FISHINGHOLE); - SetLootState(GO_JUST_DEACTIVATED); - } - else - player->SendLoot(GetGUID(),LOOT_FISHING); - } - else - { - // fish escaped, can be deleted now - SetLootState(GO_JUST_DEACTIVATED); - - WorldPacket data(SMSG_FISH_ESCAPED, 0); - player->GetSession()->SendPacket(&data); - } - break; - } - case GO_JUST_DEACTIVATED: // nothing to do, will be deleted at next update - break; - default: - { - SetLootState(GO_JUST_DEACTIVATED); - - WorldPacket data(SMSG_FISH_NOT_HOOKED, 0); - player->GetSession()->SendPacket(&data); - break; - } - } - - if(player->m_currentSpells[CURRENT_CHANNELED_SPELL]) - { - player->m_currentSpells[CURRENT_CHANNELED_SPELL]->SendChannelUpdate(0); - player->m_currentSpells[CURRENT_CHANNELED_SPELL]->finish(); - } - return; - } - - case GAMEOBJECT_TYPE_SUMMONING_RITUAL: //18 - { - if(user->GetTypeId()!=TYPEID_PLAYER) - return; - - Player* player = (Player*)user; - - Unit* caster = GetOwner(); - - GameObjectInfo const* info = GetGOInfo(); - - if( !caster || caster->GetTypeId()!=TYPEID_PLAYER ) - return; - - // accept only use by player from same group for caster except caster itself - if(((Player*)caster)==player || !((Player*)caster)->IsInSameRaidWith(player)) - return; - - AddUniqueUse(player); - - // full amount unique participants including original summoner - if(GetUniqueUseCount() < info->summoningRitual.reqParticipants) - return; - - // in case summoning ritual caster is GO creator - spellCaster = caster; - - if(!caster->m_currentSpells[CURRENT_CHANNELED_SPELL]) - return; - - spellId = info->summoningRitual.spellId; - - // finish spell - caster->m_currentSpells[CURRENT_CHANNELED_SPELL]->SendChannelUpdate(0); - caster->m_currentSpells[CURRENT_CHANNELED_SPELL]->finish(); - - // can be deleted now - SetLootState(GO_JUST_DEACTIVATED); - - // go to end function to spell casting - break; - } - case GAMEOBJECT_TYPE_SPELLCASTER: //22 - { - SetUInt32Value(GAMEOBJECT_FLAGS,2); - - GameObjectInfo const* info = GetGOInfo(); - if(!info) - return; - - if(info->spellcaster.partyOnly) - { - Unit* caster = GetOwner(); - if( !caster || caster->GetTypeId()!=TYPEID_PLAYER ) - return; - - if(user->GetTypeId()!=TYPEID_PLAYER || !((Player*)user)->IsInSameRaidWith((Player*)caster)) - return; - } - - spellId = info->spellcaster.spellId; - - AddUse(); - break; - } - case GAMEOBJECT_TYPE_MEETINGSTONE: //23 - { - GameObjectInfo const* info = GetGOInfo(); - - if(user->GetTypeId()!=TYPEID_PLAYER) - return; - - Player* player = (Player*)user; - - Player* targetPlayer = ObjectAccessor::FindPlayer(player->GetSelection()); - - // accept only use by player from same group for caster except caster itself - if(!targetPlayer || targetPlayer == player || !targetPlayer->IsInSameGroupWith(player)) - return; - - //required lvl checks! - uint8 level = player->getLevel(); - if (level < info->meetingstone.minLevel || level > info->meetingstone.maxLevel) - return; - level = targetPlayer->getLevel(); - if (level < info->meetingstone.minLevel || level > info->meetingstone.maxLevel) - return; - - spellId = 23598; - - break; - } - - case GAMEOBJECT_TYPE_FLAGSTAND: // 24 - { - if(user->GetTypeId()!=TYPEID_PLAYER) - return; - - Player* player = (Player*)user; - - if( player->InBattleGround() && // in battleground - !player->IsMounted() && // not mounted - !player->HasStealthAura() && // not stealthed - !player->HasInvisibilityAura() && // not invisible - player->isAlive()) // live player - { - BattleGround *bg = player->GetBattleGround(); - if(!bg) - return; - // BG flag click - // AB: - // 15001 - // 15002 - // 15003 - // 15004 - // 15005 - bg->EventPlayerClickedOnFlag(player, this); - return; //we don;t need to delete flag ... it is despawned! - } - break; - } - case GAMEOBJECT_TYPE_FLAGDROP: // 26 - { - if(user->GetTypeId()!=TYPEID_PLAYER) - return; - - Player* player = (Player*)user; - - if( player->InBattleGround() && // in battleground - !player->IsMounted() && // not mounted - !player->HasStealthAura() && // not stealthed - !player->HasInvisibilityAura() && // not invisible - !player->HasAura(SPELL_RECENTLY_DROPPED_FLAG, 0) && // can't pickup - player->isAlive()) // live player - { - BattleGround *bg = player->GetBattleGround(); - if(!bg) - return; - // BG flag dropped - // WS: - // 179785 - Silverwing Flag - // 179786 - Warsong Flag - // EotS: - // 184142 - Netherstorm Flag - GameObjectInfo const* info = GetGOInfo(); - if(info) - { - switch(info->id) - { - case 179785: // Silverwing Flag - // check if it's correct bg - if(bg->GetTypeID() == BATTLEGROUND_WS) - bg->EventPlayerClickedOnFlag(player, this); - break; - case 179786: // Warsong Flag - if(bg->GetTypeID() == BATTLEGROUND_WS) - bg->EventPlayerClickedOnFlag(player, this); - break; - case 184142: // Netherstorm Flag - if(bg->GetTypeID() == BATTLEGROUND_EY) - bg->EventPlayerClickedOnFlag(player, this); - break; - } - } - //this cause to call return, all flags must be deleted here!! - spellId = 0; - Delete(); - } - break; - } - default: - sLog.outDebug("Unknown Object Type %u", GetGoType()); - break; - } - - if(!spellId) - return; - - SpellEntry const *spellInfo = sSpellStore.LookupEntry( spellId ); - if(!spellInfo) - { - sLog.outError("WORLD: unknown spell id %u at use action for gameobject (Entry: %u GoType: %u )", spellId,GetEntry(),GetGoType()); - return; - } - - Spell *spell = new Spell(spellCaster, spellInfo, false); - - // spell target is user of GO - SpellCastTargets targets; - targets.setUnitTarget( user ); - - spell->prepare(&targets); -} +/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "Common.h"
+#include "QuestDef.h"
+#include "GameObject.h"
+#include "ObjectMgr.h"
+#include "SpellMgr.h"
+#include "Spell.h"
+#include "UpdateMask.h"
+#include "Opcodes.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "World.h"
+#include "Database/DatabaseEnv.h"
+#include "MapManager.h"
+#include "LootMgr.h"
+#include "GridNotifiers.h"
+#include "GridNotifiersImpl.h"
+#include "CellImpl.h"
+#include "InstanceData.h"
+#include "BattleGround.h"
+#include "Util.h"
+
+GameObject::GameObject() : WorldObject()
+{
+ m_objectType |= TYPEMASK_GAMEOBJECT;
+ m_objectTypeId = TYPEID_GAMEOBJECT;
+ // 2.3.2 - 0x58
+ m_updateFlag = (UPDATEFLAG_LOWGUID | UPDATEFLAG_HIGHGUID | UPDATEFLAG_HASPOSITION);
+
+ m_valuesCount = GAMEOBJECT_END;
+ m_respawnTime = 0;
+ m_respawnDelayTime = 25;
+ m_lootState = GO_NOT_READY;
+ m_spawnedByDefault = true;
+ m_usetimes = 0;
+ m_spellId = 0;
+ m_charges = 5;
+ m_cooldownTime = 0;
+ m_goInfo = NULL;
+
+ m_DBTableGuid = 0;
+}
+
+GameObject::~GameObject()
+{
+ if(m_uint32Values) // field array can be not exist if GameOBject not loaded
+ {
+ // crash possable at access to deleted GO in Unit::m_gameobj
+ uint64 owner_guid = GetOwnerGUID();
+ if(owner_guid)
+ {
+ Unit* owner = ObjectAccessor::GetUnit(*this,owner_guid);
+ if(owner)
+ owner->RemoveGameObject(this,false);
+ else if(!IS_PLAYER_GUID(owner_guid))
+ sLog.outError("Delete GameObject (GUID: %u Entry: %u ) that have references in not found creature %u GO list. Crash possable later.",GetGUIDLow(),GetGOInfo()->id,GUID_LOPART(owner_guid));
+ }
+ }
+}
+
+void GameObject::AddToWorld()
+{
+ ///- Register the gameobject for guid lookup
+ if(!IsInWorld()) ObjectAccessor::Instance().AddObject(this);
+ Object::AddToWorld();
+}
+
+void GameObject::RemoveFromWorld()
+{
+ ///- Remove the gameobject from the accessor
+ if(IsInWorld()) ObjectAccessor::Instance().RemoveObject(this);
+ Object::RemoveFromWorld();
+}
+
+bool GameObject::Create(uint32 guidlow, uint32 name_id, Map *map, float x, float y, float z, float ang, float rotation0, float rotation1, float rotation2, float rotation3, uint32 animprogress, uint32 go_state)
+{
+ Relocate(x,y,z,ang);
+ SetMapId(map->GetId());
+ SetInstanceId(map->GetInstanceId());
+
+ if(!IsPositionValid())
+ {
+ sLog.outError("ERROR: Gameobject (GUID: %u Entry: %u ) not created. Suggested coordinates isn't valid (X: %f Y: %f)",guidlow,name_id,x,y);
+ return false;
+ }
+
+ GameObjectInfo const* goinfo = objmgr.GetGameObjectInfo(name_id);
+ if (!goinfo)
+ {
+ sLog.outErrorDb("Gameobject (GUID: %u Entry: %u) not created: it have not exist entry in `gameobject_template`. Map: %u (X: %f Y: %f Z: %f) ang: %f rotation0: %f rotation1: %f rotation2: %f rotation3: %f",guidlow, name_id, map->GetId(), x, y, z, ang, rotation0, rotation1, rotation2, rotation3);
+ return false;
+ }
+
+ Object::_Create(guidlow, goinfo->id, HIGHGUID_GAMEOBJECT);
+
+ m_goInfo = goinfo;
+
+ if (goinfo->type >= MAX_GAMEOBJECT_TYPE)
+ {
+ sLog.outErrorDb("Gameobject (GUID: %u Entry: %u) not created: it have not exist GO type '%u' in `gameobject_template`. It's will crash client if created.",guidlow,name_id,goinfo->type);
+ return false;
+ }
+
+ SetFloatValue(GAMEOBJECT_POS_X, x);
+ SetFloatValue(GAMEOBJECT_POS_Y, y);
+ SetFloatValue(GAMEOBJECT_POS_Z, z);
+ SetFloatValue(GAMEOBJECT_FACING, ang); //this is not facing angle
+
+ SetFloatValue (GAMEOBJECT_ROTATION, rotation0);
+ SetFloatValue (GAMEOBJECT_ROTATION+1, rotation1);
+ SetFloatValue (GAMEOBJECT_ROTATION+2, rotation2);
+ SetFloatValue (GAMEOBJECT_ROTATION+3, rotation3);
+
+ SetFloatValue(OBJECT_FIELD_SCALE_X, goinfo->size);
+
+ SetUInt32Value(GAMEOBJECT_FACTION, goinfo->faction);
+ SetUInt32Value(GAMEOBJECT_FLAGS, goinfo->flags);
+
+ SetUInt32Value(OBJECT_FIELD_ENTRY, goinfo->id);
+
+ SetUInt32Value(GAMEOBJECT_DISPLAYID, goinfo->displayId);
+
+ SetGoState(go_state);
+ SetGoType(GameobjectTypes(goinfo->type));
+
+ SetGoAnimProgress(animprogress);
+
+ // Spell charges for GAMEOBJECT_TYPE_SPELLCASTER (22)
+ if (goinfo->type == GAMEOBJECT_TYPE_SPELLCASTER)
+ m_charges = goinfo->spellcaster.charges;
+
+ //Notify the map's instance data.
+ //Only works if you create the object in it, not if it is moves to that map.
+ //Normally non-players do not teleport to other maps.
+ if(map->IsDungeon() && ((InstanceMap*)map)->GetInstanceData())
+ {
+ ((InstanceMap*)map)->GetInstanceData()->OnObjectCreate(this);
+ }
+
+ return true;
+}
+
+void GameObject::Update(uint32 /*p_time*/)
+{
+ if (IS_MO_TRANSPORT(GetGUID()))
+ {
+ //((Transport*)this)->Update(p_time);
+ return;
+ }
+
+ switch (m_lootState)
+ {
+ case GO_NOT_READY:
+ {
+ switch(GetGoType())
+ {
+ case GAMEOBJECT_TYPE_TRAP:
+ {
+ // Arming Time for GAMEOBJECT_TYPE_TRAP (6)
+ Unit* owner = GetOwner();
+ if (owner && ((Player*)owner)->isInCombat())
+ m_cooldownTime = time(NULL) + GetGOInfo()->trap.startDelay;
+ m_lootState = GO_READY;
+ break;
+ }
+ case GAMEOBJECT_TYPE_FISHINGNODE:
+ {
+ // fishing code (bobber ready)
+ if( time(NULL) > m_respawnTime - FISHING_BOBBER_READY_TIME )
+ {
+ // splash bobber (bobber ready now)
+ Unit* caster = GetOwner();
+ if(caster && caster->GetTypeId()==TYPEID_PLAYER)
+ {
+ SetGoState(0);
+ SetUInt32Value(GAMEOBJECT_FLAGS, 32);
+
+ UpdateData udata;
+ WorldPacket packet;
+ BuildValuesUpdateBlockForPlayer(&udata,((Player*)caster));
+ udata.BuildPacket(&packet);
+ ((Player*)caster)->GetSession()->SendPacket(&packet);
+
+ WorldPacket data(SMSG_GAMEOBJECT_CUSTOM_ANIM,8+4);
+ data << GetGUID();
+ data << (uint32)(0);
+ ((Player*)caster)->SendMessageToSet(&data,true);
+ }
+
+ m_lootState = GO_READY; // can be succesfully open with some chance
+ }
+ return;
+ }
+ default:
+ m_lootState = GO_READY; // for other GOis same switched without delay to GO_READY
+ break;
+ }
+ // NO BREAK for switch (m_lootState)
+ }
+ case GO_READY:
+ {
+ if (m_respawnTime > 0) // timer on
+ {
+ if (m_respawnTime <= time(NULL)) // timer expired
+ {
+ m_respawnTime = 0;
+ m_SkillupList.clear();
+ m_usetimes = 0;
+
+ switch (GetGoType())
+ {
+ case GAMEOBJECT_TYPE_FISHINGNODE: // can't fish now
+ {
+ Unit* caster = GetOwner();
+ if(caster && caster->GetTypeId()==TYPEID_PLAYER)
+ {
+ if(caster->m_currentSpells[CURRENT_CHANNELED_SPELL])
+ {
+ caster->m_currentSpells[CURRENT_CHANNELED_SPELL]->SendChannelUpdate(0);
+ caster->m_currentSpells[CURRENT_CHANNELED_SPELL]->finish(false);
+ }
+
+ WorldPacket data(SMSG_FISH_NOT_HOOKED,0);
+ ((Player*)caster)->GetSession()->SendPacket(&data);
+ }
+ // can be delete
+ m_lootState = GO_JUST_DEACTIVATED;
+ return;
+ }
+ case GAMEOBJECT_TYPE_DOOR:
+ case GAMEOBJECT_TYPE_BUTTON:
+ //we need to open doors if they are closed (add there another condition if this code breaks some usage, but it need to be here for battlegrounds)
+ if( !GetGoState() )
+ SwitchDoorOrButton(false);
+ //flags in AB are type_button and we need to add them here so no break!
+ default:
+ if(!m_spawnedByDefault) // despawn timer
+ {
+ // can be despawned or destroyed
+ SetLootState(GO_JUST_DEACTIVATED);
+ return;
+ }
+ // respawn timer
+ MapManager::Instance().GetMap(GetMapId(), this)->Add(this);
+ break;
+ }
+ }
+ }
+
+ // traps can have time and can not have
+ GameObjectInfo const* goInfo = GetGOInfo();
+ if(goInfo->type == GAMEOBJECT_TYPE_TRAP)
+ {
+ // traps
+ Unit* owner = GetOwner();
+ Unit* ok = NULL; // pointer to appropriate target if found any
+
+ if(m_cooldownTime >= time(NULL))
+ return;
+
+ bool IsBattleGroundTrap = false;
+ //FIXME: this is activation radius (in different casting radius that must be selected from spell data)
+ //TODO: move activated state code (cast itself) to GO_ACTIVATED, in this place only check activating and set state
+ float radius = goInfo->trap.radius;
+ if(!radius)
+ {
+ if(goInfo->trap.cooldown != 3) // cast in other case (at some triggring/linked go/etc explicit call)
+ return;
+ else
+ {
+ if(m_respawnTime > 0)
+ break;
+
+ radius = goInfo->trap.cooldown; // battlegrounds gameobjects has data2 == 0 && data5 == 3
+ IsBattleGroundTrap = true;
+ }
+ }
+
+ bool NeedDespawn = (goInfo->trap.charges != 0);
+
+ CellPair p(MaNGOS::ComputeCellPair(GetPositionX(),GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+
+ // Note: this hack with search required until GO casting not implemented
+ // search unfriendly creature
+ if(owner && NeedDespawn) // hunter trap
+ {
+ MaNGOS::AnyUnfriendlyUnitInObjectRangeCheck u_check(this, owner, radius);
+ MaNGOS::UnitSearcher<MaNGOS::AnyUnfriendlyUnitInObjectRangeCheck> checker(ok, u_check);
+
+ CellLock<GridReadGuard> cell_lock(cell, p);
+
+ TypeContainerVisitor<MaNGOS::UnitSearcher<MaNGOS::AnyUnfriendlyUnitInObjectRangeCheck>, GridTypeMapContainer > grid_object_checker(checker);
+ cell_lock->Visit(cell_lock, grid_object_checker, *MapManager::Instance().GetMap(GetMapId(), this));
+
+ // or unfriendly player/pet
+ if(!ok)
+ {
+ TypeContainerVisitor<MaNGOS::UnitSearcher<MaNGOS::AnyUnfriendlyUnitInObjectRangeCheck>, WorldTypeMapContainer > world_object_checker(checker);
+ cell_lock->Visit(cell_lock, world_object_checker, *MapManager::Instance().GetMap(GetMapId(), this));
+ }
+ }
+ else // environmental trap
+ {
+ // environmental damage spells already have around enemies targeting but this not help in case not existed GO casting support
+
+ // affect only players
+ Player* p_ok = NULL;
+ MaNGOS::AnyPlayerInObjectRangeCheck p_check(this, radius);
+ MaNGOS::PlayerSearcher<MaNGOS::AnyPlayerInObjectRangeCheck> checker(p_ok, p_check);
+
+ CellLock<GridReadGuard> cell_lock(cell, p);
+
+ TypeContainerVisitor<MaNGOS::PlayerSearcher<MaNGOS::AnyPlayerInObjectRangeCheck>, WorldTypeMapContainer > world_object_checker(checker);
+ cell_lock->Visit(cell_lock, world_object_checker, *MapManager::Instance().GetMap(GetMapId(), this));
+ ok = p_ok;
+ }
+
+ if (ok)
+ {
+ Unit *caster = owner ? owner : ok;
+
+ caster->CastSpell(ok, goInfo->trap.spellId, true);
+ m_cooldownTime = time(NULL) + 4; // 4 seconds
+
+ if(NeedDespawn)
+ SetLootState(GO_JUST_DEACTIVATED); // can be despawned or destroyed
+
+ if(IsBattleGroundTrap && ok->GetTypeId() == TYPEID_PLAYER)
+ {
+ //BattleGround gameobjects case
+ if(((Player*)ok)->InBattleGround())
+ if(BattleGround *bg = ((Player*)ok)->GetBattleGround())
+ bg->HandleTriggerBuff(GetGUID());
+ }
+ }
+ }
+
+ if (m_charges && m_usetimes >= m_charges)
+ SetLootState(GO_JUST_DEACTIVATED); // can be despawned or destroyed
+
+ break;
+ }
+ case GO_ACTIVATED:
+ {
+ switch(GetGoType())
+ {
+ case GAMEOBJECT_TYPE_DOOR:
+ case GAMEOBJECT_TYPE_BUTTON:
+ if(GetAutoCloseTime() && (m_cooldownTime < time(NULL)))
+ {
+ SwitchDoorOrButton(false);
+ SetLootState(GO_JUST_DEACTIVATED);
+ }
+ break;
+ }
+ break;
+ }
+ case GO_JUST_DEACTIVATED:
+ {
+ //if Gameobject should cast spell, then this, but some GOs (type = 10) should be destroyed
+ if (GetGoType() == GAMEOBJECT_TYPE_GOOBER)
+ {
+ uint32 spellId = GetGOInfo()->goober.spellId;
+
+ if(spellId)
+ {
+ std::set<uint32>::iterator it = m_unique_users.begin();
+ std::set<uint32>::iterator end = m_unique_users.end();
+ for (; it != end; it++)
+ {
+ Unit* owner = Unit::GetUnit(*this, uint64(*it));
+ if (owner) owner->CastSpell(owner, spellId, false);
+ }
+
+ m_unique_users.clear();
+ m_usetimes = 0;
+ }
+ //any return here in case battleground traps
+ }
+
+ if(GetOwnerGUID())
+ {
+ m_respawnTime = 0;
+ Delete();
+ return;
+ }
+
+ //burning flags in some battlegrounds, if you find better condition, just add it
+ if (GetGoAnimProgress() > 0)
+ {
+ SendObjectDeSpawnAnim(this->GetGUID());
+ //reset flags
+ SetUInt32Value(GAMEOBJECT_FLAGS, GetGOInfo()->flags);
+ }
+
+ loot.clear();
+ SetLootState(GO_READY);
+
+ if(!m_respawnDelayTime)
+ return;
+
+ if(!m_spawnedByDefault)
+ {
+ m_respawnTime = 0;
+ return;
+ }
+
+ m_respawnTime = time(NULL) + m_respawnDelayTime;
+
+ // if option not set then object will be saved at grid unload
+ if(sWorld.getConfig(CONFIG_SAVE_RESPAWN_TIME_IMMEDIATLY))
+ SaveRespawnTime();
+
+ ObjectAccessor::UpdateObjectVisibility(this);
+
+ break;
+ }
+ }
+}
+
+void GameObject::Refresh()
+{
+ // not refresh despawned not casted GO (despawned casted GO destroyed in all cases anyway)
+ if(m_respawnTime > 0 && m_spawnedByDefault)
+ return;
+
+ if(isSpawned())
+ MapManager::Instance().GetMap(GetMapId(), this)->Add(this);
+}
+
+void GameObject::AddUniqueUse(Player* player)
+{
+ AddUse();
+ m_unique_users.insert(player->GetGUIDLow());
+}
+
+void GameObject::Delete()
+{
+ SendObjectDeSpawnAnim(GetGUID());
+
+ SetGoState(1);
+ SetUInt32Value(GAMEOBJECT_FLAGS, GetGOInfo()->flags);
+
+ AddObjectToRemoveList();
+}
+
+void GameObject::getFishLoot(Loot *fishloot)
+{
+ fishloot->clear();
+
+ uint32 subzone = GetAreaId();
+
+ // if subzone loot exist use it
+ if(LootTemplates_Fishing.HaveLootFor(subzone))
+ fishloot->FillLoot(subzone, LootTemplates_Fishing, NULL);
+ // else use zone loot
+ else
+ fishloot->FillLoot(GetZoneId(), LootTemplates_Fishing, NULL);
+}
+
+void GameObject::SaveToDB()
+{
+ // this should only be used when the gameobject has already been loaded
+ // perferably after adding to map, because mapid may not be valid otherwise
+ GameObjectData const *data = objmgr.GetGOData(m_DBTableGuid);
+ if(!data)
+ {
+ sLog.outError("GameObject::SaveToDB failed, cannot get gameobject data!");
+ return;
+ }
+
+ SaveToDB(GetMapId(), data->spawnMask);
+}
+
+void GameObject::SaveToDB(uint32 mapid, uint8 spawnMask)
+{
+ const GameObjectInfo *goI = GetGOInfo();
+
+ if (!goI)
+ return;
+
+ if (!m_DBTableGuid)
+ m_DBTableGuid = GetGUIDLow();
+ // update in loaded data (changing data only in this place)
+ GameObjectData& data = objmgr.NewGOData(m_DBTableGuid);
+
+ // data->guid = guid don't must be update at save
+ data.id = GetEntry();
+ data.mapid = mapid;
+ data.posX = GetFloatValue(GAMEOBJECT_POS_X);
+ data.posY = GetFloatValue(GAMEOBJECT_POS_Y);
+ data.posZ = GetFloatValue(GAMEOBJECT_POS_Z);
+ data.orientation = GetFloatValue(GAMEOBJECT_FACING);
+ data.rotation0 = GetFloatValue(GAMEOBJECT_ROTATION+0);
+ data.rotation1 = GetFloatValue(GAMEOBJECT_ROTATION+1);
+ data.rotation2 = GetFloatValue(GAMEOBJECT_ROTATION+2);
+ data.rotation3 = GetFloatValue(GAMEOBJECT_ROTATION+3);
+ data.spawntimesecs = m_spawnedByDefault ? m_respawnDelayTime : -(int32)m_respawnDelayTime;
+ data.animprogress = GetGoAnimProgress();
+ data.go_state = GetGoState();
+ data.spawnMask = spawnMask;
+
+ // updated in DB
+ std::ostringstream ss;
+ ss << "INSERT INTO gameobject VALUES ( "
+ << m_DBTableGuid << ", "
+ << GetUInt32Value (OBJECT_FIELD_ENTRY) << ", "
+ << mapid << ", "
+ << (uint32)spawnMask << ", "
+ << GetFloatValue(GAMEOBJECT_POS_X) << ", "
+ << GetFloatValue(GAMEOBJECT_POS_Y) << ", "
+ << GetFloatValue(GAMEOBJECT_POS_Z) << ", "
+ << GetFloatValue(GAMEOBJECT_FACING) << ", "
+ << GetFloatValue(GAMEOBJECT_ROTATION) << ", "
+ << GetFloatValue(GAMEOBJECT_ROTATION+1) << ", "
+ << GetFloatValue(GAMEOBJECT_ROTATION+2) << ", "
+ << GetFloatValue(GAMEOBJECT_ROTATION+3) << ", "
+ << m_respawnDelayTime << ", "
+ << GetGoAnimProgress() << ", "
+ << GetGoState() << ")";
+
+ WorldDatabase.BeginTransaction();
+ WorldDatabase.PExecuteLog("DELETE FROM gameobject WHERE guid = '%u'", m_DBTableGuid);
+ WorldDatabase.PExecuteLog( ss.str( ).c_str( ) );
+ WorldDatabase.CommitTransaction();
+}
+
+bool GameObject::LoadFromDB(uint32 guid, Map *map)
+{
+ GameObjectData const* data = objmgr.GetGOData(guid);
+
+ if( !data )
+ {
+ sLog.outErrorDb("ERROR: Gameobject (GUID: %u) not found in table `gameobject`, can't load. ",guid);
+ return false;
+ }
+
+ uint32 entry = data->id;
+ uint32 map_id = data->mapid;
+ float x = data->posX;
+ float y = data->posY;
+ float z = data->posZ;
+ float ang = data->orientation;
+
+ float rotation0 = data->rotation0;
+ float rotation1 = data->rotation1;
+ float rotation2 = data->rotation2;
+ float rotation3 = data->rotation3;
+
+ uint32 animprogress = data->animprogress;
+ uint32 go_state = data->go_state;
+
+ m_DBTableGuid = guid;
+ if (map->GetInstanceId() != 0) guid = objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT);
+
+ if (!Create(guid,entry, map, x, y, z, ang, rotation0, rotation1, rotation2, rotation3, animprogress, go_state) )
+ return false;
+
+ switch(GetGOInfo()->type)
+ {
+ case GAMEOBJECT_TYPE_DOOR:
+ case GAMEOBJECT_TYPE_BUTTON:
+ /* this code (in comment) isn't correct because in battlegrounds we need despawnable doors and buttons, pls remove
+ SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NODESPAWN);
+ m_spawnedByDefault = true;
+ m_respawnDelayTime = 0;
+ m_respawnTime = 0;
+ break;*/
+ default:
+ if(data->spawntimesecs >= 0)
+ {
+ m_spawnedByDefault = true;
+ m_respawnDelayTime = data->spawntimesecs;
+ m_respawnTime = objmgr.GetGORespawnTime(m_DBTableGuid, map->GetInstanceId());
+
+ // ready to respawn
+ if(m_respawnTime && m_respawnTime <= time(NULL))
+ {
+ m_respawnTime = 0;
+ objmgr.SaveGORespawnTime(m_DBTableGuid,GetInstanceId(),0);
+ }
+ }
+ else
+ {
+ m_spawnedByDefault = false;
+ m_respawnDelayTime = -data->spawntimesecs;
+ m_respawnTime = 0;
+ }
+ break;
+ }
+
+ return true;
+}
+
+void GameObject::DeleteFromDB()
+{
+ objmgr.SaveGORespawnTime(m_DBTableGuid,GetInstanceId(),0);
+ objmgr.DeleteGOData(m_DBTableGuid);
+ WorldDatabase.PExecuteLog("DELETE FROM gameobject WHERE guid = '%u'", m_DBTableGuid);
+ WorldDatabase.PExecuteLog("DELETE FROM game_event_gameobject WHERE guid = '%u'", m_DBTableGuid);
+}
+
+GameObject* GameObject::GetGameObject(WorldObject& object, uint64 guid)
+{
+ return ObjectAccessor::GetGameObject(object,guid);
+}
+
+GameObjectInfo const *GameObject::GetGOInfo() const
+{
+ return m_goInfo;
+}
+
+uint32 GameObject::GetLootId(GameObjectInfo const* ginfo)
+{
+ if (!ginfo)
+ return 0;
+
+ switch(ginfo->type)
+ {
+ case GAMEOBJECT_TYPE_CHEST:
+ return ginfo->chest.lootId;
+ case GAMEOBJECT_TYPE_FISHINGHOLE:
+ return ginfo->fishinghole.lootId;
+ case GAMEOBJECT_TYPE_FISHINGNODE:
+ return ginfo->fishnode.lootId;
+ default:
+ return 0;
+ }
+}
+
+/*********************************************************/
+/*** QUEST SYSTEM ***/
+/*********************************************************/
+bool GameObject::hasQuest(uint32 quest_id) const
+{
+ QuestRelations const& qr = objmgr.mGOQuestRelations;
+ for(QuestRelations::const_iterator itr = qr.lower_bound(GetEntry()); itr != qr.upper_bound(GetEntry()); ++itr)
+ {
+ if(itr->second==quest_id)
+ return true;
+ }
+ return false;
+}
+
+bool GameObject::hasInvolvedQuest(uint32 quest_id) const
+{
+ QuestRelations const& qr = objmgr.mGOQuestInvolvedRelations;
+ for(QuestRelations::const_iterator itr = qr.lower_bound(GetEntry()); itr != qr.upper_bound(GetEntry()); ++itr)
+ {
+ if(itr->second==quest_id)
+ return true;
+ }
+ return false;
+}
+
+bool GameObject::IsTransport() const
+{
+ // If something is marked as a transport, don't transmit an out of range packet for it.
+ GameObjectInfo const * gInfo = GetGOInfo();
+ if(!gInfo) return false;
+ return gInfo->type == GAMEOBJECT_TYPE_TRANSPORT || gInfo->type == GAMEOBJECT_TYPE_MO_TRANSPORT;
+}
+
+Unit* GameObject::GetOwner() const
+{
+ return ObjectAccessor::GetUnit(*this, GetOwnerGUID());
+}
+
+void GameObject::SaveRespawnTime()
+{
+ if(m_respawnTime > time(NULL) && m_spawnedByDefault)
+ objmgr.SaveGORespawnTime(m_DBTableGuid,GetInstanceId(),m_respawnTime);
+}
+
+bool GameObject::isVisibleForInState(Player const* u, bool inVisibleList) const
+{
+ // Not in world
+ if(!IsInWorld() || !u->IsInWorld())
+ return false;
+
+ // Transport always visible at this step implementation
+ if(IsTransport() && IsInMap(u))
+ return true;
+
+ // quick check visibility false cases for non-GM-mode
+ if(!u->isGameMaster())
+ {
+ // despawned and then not visible for non-GM in GM-mode
+ if(!isSpawned())
+ return false;
+
+ // special invisibility cases
+ /* TODO: implement trap stealth, take look at spell 2836
+ if(GetGOInfo()->type == GAMEOBJECT_TYPE_TRAP && GetGOInfo()->trap.stealthed && u->IsHostileTo(GetOwner()))
+ {
+ if(check stuff here)
+ return false;
+ }*/
+
+ // Smuggled Mana Cell required 10 invisibility type detection/state
+ if(GetEntry()==187039 && ((u->m_detectInvisibilityMask | u->m_invisibilityMask) & (1<<10))==0)
+ return false;
+ }
+
+ // check distance
+ return IsWithinDistInMap(u,World::GetMaxVisibleDistanceForObject() +
+ (inVisibleList ? World::GetVisibleObjectGreyDistance() : 0.0f) );
+}
+
+void GameObject::Respawn()
+{
+ if(m_spawnedByDefault && m_respawnTime > 0)
+ {
+ m_respawnTime = time(NULL);
+ objmgr.SaveGORespawnTime(m_DBTableGuid,GetInstanceId(),0);
+ }
+}
+
+bool GameObject::ActivateToQuest( Player *pTarget)const
+{
+ if(!objmgr.IsGameObjectForQuests(GetEntry()))
+ return false;
+
+ switch(GetGoType())
+ {
+ // scan GO chest with loot including quest items
+ case GAMEOBJECT_TYPE_CHEST:
+ {
+ if(LootTemplates_Gameobject.HaveQuestLootForPlayer(GetLootId(), pTarget))
+ return true;
+ break;
+ }
+ case GAMEOBJECT_TYPE_GOOBER:
+ {
+ if(pTarget->GetQuestStatus(GetGOInfo()->goober.questId) == QUEST_STATUS_INCOMPLETE)
+ return true;
+ break;
+ }
+ default:
+ break;
+ }
+
+ return false;
+}
+
+void GameObject::TriggeringLinkedGameObject( uint32 trapEntry, Unit* target)
+{
+ GameObjectInfo const* trapInfo = sGOStorage.LookupEntry<GameObjectInfo>(trapEntry);
+ if(!trapInfo || trapInfo->type!=GAMEOBJECT_TYPE_TRAP)
+ return;
+
+ SpellEntry const* trapSpell = sSpellStore.LookupEntry(trapInfo->trap.spellId);
+ if(!trapSpell) // checked at load already
+ return;
+
+ float range = GetSpellMaxRange(sSpellRangeStore.LookupEntry(trapSpell->rangeIndex));
+
+ // search nearest linked GO
+ GameObject* trapGO = NULL;
+ {
+ // using original GO distance
+ CellPair p(MaNGOS::ComputeCellPair(GetPositionX(), GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+
+ MaNGOS::NearestGameObjectEntryInObjectRangeCheck go_check(*target,trapEntry,range);
+ MaNGOS::GameObjectLastSearcher<MaNGOS::NearestGameObjectEntryInObjectRangeCheck> checker(trapGO,go_check);
+
+ TypeContainerVisitor<MaNGOS::GameObjectLastSearcher<MaNGOS::NearestGameObjectEntryInObjectRangeCheck>, GridTypeMapContainer > object_checker(checker);
+ CellLock<GridReadGuard> cell_lock(cell, p);
+ cell_lock->Visit(cell_lock, object_checker, *MapManager::Instance().GetMap(GetMapId(), this));
+ }
+
+ // found correct GO
+ // FIXME: when GO casting will be implemented trap must cast spell to target
+ if(trapGO)
+ target->CastSpell(target,trapSpell,true);
+}
+
+GameObject* GameObject::LookupFishingHoleAround(float range)
+{
+ GameObject* ok = NULL;
+
+ CellPair p(MaNGOS::ComputeCellPair(GetPositionX(),GetPositionY()));
+ Cell cell(p);
+ cell.data.Part.reserved = ALL_DISTRICT;
+ MaNGOS::NearestGameObjectFishingHole u_check(*this, range);
+ MaNGOS::GameObjectSearcher<MaNGOS::NearestGameObjectFishingHole> checker(ok, u_check);
+
+ CellLock<GridReadGuard> cell_lock(cell, p);
+
+ TypeContainerVisitor<MaNGOS::GameObjectSearcher<MaNGOS::NearestGameObjectFishingHole>, GridTypeMapContainer > grid_object_checker(checker);
+ cell_lock->Visit(cell_lock, grid_object_checker, *MapManager::Instance().GetMap(GetMapId(), this));
+
+ return ok;
+}
+
+void GameObject::UseDoorOrButton(uint32 time_to_restore)
+{
+ if(m_lootState != GO_READY)
+ return;
+
+ if(!time_to_restore)
+ time_to_restore = GetAutoCloseTime();
+
+ SwitchDoorOrButton(true);
+ SetLootState(GO_ACTIVATED);
+
+ m_cooldownTime = time(NULL) + time_to_restore;
+
+}
+
+void GameObject::SwitchDoorOrButton(bool activate)
+{
+ if(activate)
+ SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_IN_USE);
+ else
+ RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_IN_USE);
+
+ if(GetGoState()) //if closed -> open
+ SetGoState(0);
+ else //if open -> close
+ SetGoState(1);
+}
+
+void GameObject::Use(Unit* user)
+{
+ // by default spell caster is user
+ Unit* spellCaster = user;
+ uint32 spellId = 0;
+
+ switch(GetGoType())
+ {
+ case GAMEOBJECT_TYPE_DOOR: //0
+ case GAMEOBJECT_TYPE_BUTTON: //1
+ //doors/buttons never really despawn, only reset to default state/flags
+ UseDoorOrButton();
+
+ // activate script
+ sWorld.ScriptsStart(sGameObjectScripts, GetDBTableGUIDLow(), spellCaster, this);
+ return;
+
+ case GAMEOBJECT_TYPE_QUESTGIVER: //2
+ {
+ if(user->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ Player* player = (Player*)user;
+
+ player->PrepareQuestMenu( GetGUID() );
+ player->SendPreparedQuest( GetGUID() );
+ return;
+ }
+ //Sitting: Wooden bench, chairs enzz
+ case GAMEOBJECT_TYPE_CHAIR: //7
+ {
+ GameObjectInfo const* info = GetGOInfo();
+ if(!info)
+ return;
+
+ if(user->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ Player* player = (Player*)user;
+
+ // a chair may have n slots. we have to calculate their positions and teleport the player to the nearest one
+
+ // check if the db is sane
+ if(info->chair.slots > 0)
+ {
+ float lowestDist = DEFAULT_VISIBILITY_DISTANCE;
+
+ float x_lowest = GetPositionX();
+ float y_lowest = GetPositionY();
+
+ // the object orientation + 1/2 pi
+ // every slot will be on that straight line
+ float orthogonalOrientation = GetOrientation()+M_PI*0.5f;
+ // find nearest slot
+ for(uint32 i=0; i<info->chair.slots; i++)
+ {
+ // the distance between this slot and the center of the go - imagine a 1D space
+ float relativeDistance = (info->size*i)-(info->size*(info->chair.slots-1)/2.0f);
+
+ float x_i = GetPositionX() + relativeDistance * cos(orthogonalOrientation);
+ float y_i = GetPositionY() + relativeDistance * sin(orthogonalOrientation);
+
+ // calculate the distance between the player and this slot
+ float thisDistance = player->GetDistance2d(x_i, y_i);
+
+ /* debug code. It will spawn a npc on each slot to visualize them.
+ Creature* helper = player->SummonCreature(14496, x_i, y_i, GetPositionZ(), GetOrientation(), TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 10000);
+ std::ostringstream output;
+ output << i << ": thisDist: " << thisDistance;
+ helper->MonsterSay(output.str().c_str(), LANG_UNIVERSAL, 0);
+ */
+
+ if(thisDistance <= lowestDist)
+ {
+ lowestDist = thisDistance;
+ x_lowest = x_i;
+ y_lowest = y_i;
+ }
+ }
+ player->TeleportTo(GetMapId(), x_lowest, y_lowest, GetPositionZ(), GetOrientation(),TELE_TO_NOT_LEAVE_TRANSPORT | TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET);
+ }
+ else
+ {
+ // fallback, will always work
+ player->TeleportTo(GetMapId(), GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation(),TELE_TO_NOT_LEAVE_TRANSPORT | TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET);
+ }
+ player->SetStandState(PLAYER_STATE_SIT_LOW_CHAIR+info->chair.height);
+ return;
+ }
+ //big gun, its a spell/aura
+ case GAMEOBJECT_TYPE_GOOBER: //10
+ {
+ GameObjectInfo const* info = GetGOInfo();
+
+ if(user->GetTypeId()==TYPEID_PLAYER)
+ {
+ Player* player = (Player*)user;
+
+ // show page
+ if(info->goober.pageId)
+ {
+ WorldPacket data(SMSG_GAMEOBJECT_PAGETEXT, 8);
+ data << GetGUID();
+ player->GetSession()->SendPacket(&data);
+ }
+
+ // possible quest objective for active quests
+ player->CastedCreatureOrGO(info->id, GetGUID(), 0);
+ }
+
+ // cast this spell later if provided
+ spellId = info->goober.spellId;
+
+ break;
+ }
+ case GAMEOBJECT_TYPE_CAMERA: //13
+ {
+ GameObjectInfo const* info = GetGOInfo();
+ if(!info)
+ return;
+
+ if(user->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ Player* player = (Player*)user;
+
+ if(info->camera.cinematicId)
+ {
+ WorldPacket data(SMSG_TRIGGER_CINEMATIC, 4);
+ data << info->camera.cinematicId;
+ player->GetSession()->SendPacket(&data);
+ }
+ return;
+ }
+ //fishing bobber
+ case GAMEOBJECT_TYPE_FISHINGNODE: //17
+ {
+ if(user->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ Player* player = (Player*)user;
+
+ if(player->GetGUID() != GetOwnerGUID())
+ return;
+
+ switch(getLootState())
+ {
+ case GO_READY: // ready for loot
+ {
+ // 1) skill must be >= base_zone_skill
+ // 2) if skill == base_zone_skill => 5% chance
+ // 3) chance is linear dependence from (base_zone_skill-skill)
+
+ uint32 subzone = GetAreaId();
+
+ int32 zone_skill = objmgr.GetFishingBaseSkillLevel( subzone );
+ if(!zone_skill)
+ zone_skill = objmgr.GetFishingBaseSkillLevel( GetZoneId() );
+
+ //provide error, no fishable zone or area should be 0
+ if(!zone_skill)
+ sLog.outErrorDb("Fishable areaId %u are not properly defined in `skill_fishing_base_level`.",subzone);
+
+ int32 skill = player->GetSkillValue(SKILL_FISHING);
+ int32 chance = skill - zone_skill + 5;
+ int32 roll = irand(1,100);
+
+ DEBUG_LOG("Fishing check (skill: %i zone min skill: %i chance %i roll: %i",skill,zone_skill,chance,roll);
+
+ if(skill >= zone_skill && chance >= roll)
+ {
+ // prevent removing GO at spell cancel
+ player->RemoveGameObject(this,false);
+ SetOwnerGUID(player->GetGUID());
+
+ //fish catched
+ player->UpdateFishingSkill();
+
+ GameObject* ok = LookupFishingHoleAround(DEFAULT_VISIBILITY_DISTANCE);
+ if (ok)
+ {
+ player->SendLoot(ok->GetGUID(),LOOT_FISHINGHOLE);
+ SetLootState(GO_JUST_DEACTIVATED);
+ }
+ else
+ player->SendLoot(GetGUID(),LOOT_FISHING);
+ }
+ else
+ {
+ // fish escaped, can be deleted now
+ SetLootState(GO_JUST_DEACTIVATED);
+
+ WorldPacket data(SMSG_FISH_ESCAPED, 0);
+ player->GetSession()->SendPacket(&data);
+ }
+ break;
+ }
+ case GO_JUST_DEACTIVATED: // nothing to do, will be deleted at next update
+ break;
+ default:
+ {
+ SetLootState(GO_JUST_DEACTIVATED);
+
+ WorldPacket data(SMSG_FISH_NOT_HOOKED, 0);
+ player->GetSession()->SendPacket(&data);
+ break;
+ }
+ }
+
+ if(player->m_currentSpells[CURRENT_CHANNELED_SPELL])
+ {
+ player->m_currentSpells[CURRENT_CHANNELED_SPELL]->SendChannelUpdate(0);
+ player->m_currentSpells[CURRENT_CHANNELED_SPELL]->finish();
+ }
+ return;
+ }
+
+ case GAMEOBJECT_TYPE_SUMMONING_RITUAL: //18
+ {
+ if(user->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ Player* player = (Player*)user;
+
+ Unit* caster = GetOwner();
+
+ GameObjectInfo const* info = GetGOInfo();
+
+ if( !caster || caster->GetTypeId()!=TYPEID_PLAYER )
+ return;
+
+ // accept only use by player from same group for caster except caster itself
+ if(((Player*)caster)==player || !((Player*)caster)->IsInSameRaidWith(player))
+ return;
+
+ AddUniqueUse(player);
+
+ // full amount unique participants including original summoner
+ if(GetUniqueUseCount() < info->summoningRitual.reqParticipants)
+ return;
+
+ // in case summoning ritual caster is GO creator
+ spellCaster = caster;
+
+ if(!caster->m_currentSpells[CURRENT_CHANNELED_SPELL])
+ return;
+
+ spellId = info->summoningRitual.spellId;
+
+ // finish spell
+ caster->m_currentSpells[CURRENT_CHANNELED_SPELL]->SendChannelUpdate(0);
+ caster->m_currentSpells[CURRENT_CHANNELED_SPELL]->finish();
+
+ // can be deleted now
+ SetLootState(GO_JUST_DEACTIVATED);
+
+ // go to end function to spell casting
+ break;
+ }
+ case GAMEOBJECT_TYPE_SPELLCASTER: //22
+ {
+ SetUInt32Value(GAMEOBJECT_FLAGS,2);
+
+ GameObjectInfo const* info = GetGOInfo();
+ if(!info)
+ return;
+
+ if(info->spellcaster.partyOnly)
+ {
+ Unit* caster = GetOwner();
+ if( !caster || caster->GetTypeId()!=TYPEID_PLAYER )
+ return;
+
+ if(user->GetTypeId()!=TYPEID_PLAYER || !((Player*)user)->IsInSameRaidWith((Player*)caster))
+ return;
+ }
+
+ spellId = info->spellcaster.spellId;
+
+ AddUse();
+ break;
+ }
+ case GAMEOBJECT_TYPE_MEETINGSTONE: //23
+ {
+ GameObjectInfo const* info = GetGOInfo();
+
+ if(user->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ Player* player = (Player*)user;
+
+ Player* targetPlayer = ObjectAccessor::FindPlayer(player->GetSelection());
+
+ // accept only use by player from same group for caster except caster itself
+ if(!targetPlayer || targetPlayer == player || !targetPlayer->IsInSameGroupWith(player))
+ return;
+
+ //required lvl checks!
+ uint8 level = player->getLevel();
+ if (level < info->meetingstone.minLevel || level > info->meetingstone.maxLevel)
+ return;
+ level = targetPlayer->getLevel();
+ if (level < info->meetingstone.minLevel || level > info->meetingstone.maxLevel)
+ return;
+
+ spellId = 23598;
+
+ break;
+ }
+
+ case GAMEOBJECT_TYPE_FLAGSTAND: // 24
+ {
+ if(user->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ Player* player = (Player*)user;
+
+ if( player->isAllowUseBattleGroundObject() )
+ {
+ // in battleground check
+ BattleGround *bg = player->GetBattleGround();
+ if(!bg)
+ return;
+ // BG flag click
+ // AB:
+ // 15001
+ // 15002
+ // 15003
+ // 15004
+ // 15005
+ bg->EventPlayerClickedOnFlag(player, this);
+ return; //we don;t need to delete flag ... it is despawned!
+ }
+ break;
+ }
+ case GAMEOBJECT_TYPE_FLAGDROP: // 26
+ {
+ if(user->GetTypeId()!=TYPEID_PLAYER)
+ return;
+
+ Player* player = (Player*)user;
+
+ if( player->isAllowUseBattleGroundObject() )
+ {
+ // in battleground check
+ BattleGround *bg = player->GetBattleGround();
+ if(!bg)
+ return;
+ // BG flag dropped
+ // WS:
+ // 179785 - Silverwing Flag
+ // 179786 - Warsong Flag
+ // EotS:
+ // 184142 - Netherstorm Flag
+ GameObjectInfo const* info = GetGOInfo();
+ if(info)
+ {
+ switch(info->id)
+ {
+ case 179785: // Silverwing Flag
+ // check if it's correct bg
+ if(bg->GetTypeID() == BATTLEGROUND_WS)
+ bg->EventPlayerClickedOnFlag(player, this);
+ break;
+ case 179786: // Warsong Flag
+ if(bg->GetTypeID() == BATTLEGROUND_WS)
+ bg->EventPlayerClickedOnFlag(player, this);
+ break;
+ case 184142: // Netherstorm Flag
+ if(bg->GetTypeID() == BATTLEGROUND_EY)
+ bg->EventPlayerClickedOnFlag(player, this);
+ break;
+ }
+ }
+ //this cause to call return, all flags must be deleted here!!
+ spellId = 0;
+ Delete();
+ }
+ break;
+ }
+ default:
+ sLog.outDebug("Unknown Object Type %u", GetGoType());
+ break;
+ }
+
+ if(!spellId)
+ return;
+
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry( spellId );
+ if(!spellInfo)
+ {
+ sLog.outError("WORLD: unknown spell id %u at use action for gameobject (Entry: %u GoType: %u )", spellId,GetEntry(),GetGoType());
+ return;
+ }
+
+ Spell *spell = new Spell(spellCaster, spellInfo, false);
+
+ // spell target is user of GO
+ SpellCastTargets targets;
+ targets.setUnitTarget( user );
+
+ spell->prepare(&targets);
+}
diff --git a/src/game/GameObject.h b/src/game/GameObject.h index 30f3a815397..bab73fb1968 100644 --- a/src/game/GameObject.h +++ b/src/game/GameObject.h @@ -1,592 +1,592 @@ -/* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef MANGOSSERVER_GAMEOBJECT_H -#define MANGOSSERVER_GAMEOBJECT_H - -#include "Common.h" -#include "SharedDefines.h" -#include "Object.h" -#include "LootMgr.h" -#include "Database/DatabaseEnv.h" - -// GCC have alternative #pragma pack(N) syntax and old gcc version not support pack(push,N), also any gcc version not support it at some platform -#if defined( __GNUC__ ) -#pragma pack(1) -#else -#pragma pack(push,1) -#endif - -// from `gameobject_template` -struct GameObjectInfo -{ - uint32 id; - uint32 type; - uint32 displayId; - char *name; - char *castBarCaption; - uint32 faction; - uint32 flags; - float size; - union // different GO types have different data field - { - //0 GAMEOBJECT_TYPE_DOOR - struct - { - uint32 startOpen; //0 used client side to determine GO_ACTIVATED means open/closed - uint32 lockId; //1 -> Lock.dbc - uint32 autoCloseTime; //2 secs till autoclose = autoCloseTime / 0x10000 - uint32 noDamageImmune; //3 break opening whenever you recieve damage? - uint32 openTextID; //4 can be used to replace castBarCaption? - uint32 closeTextID; //5 - } door; - //1 GAMEOBJECT_TYPE_BUTTON - struct - { - uint32 startOpen; //0 - uint32 lockId; //1 -> Lock.dbc - uint32 autoCloseTime; //2 secs till autoclose = autoCloseTime / 0x10000 - uint32 linkedTrap; //3 - uint32 noDamageImmune; //4 isBattlegroundObject - uint32 large; //5 - uint32 openTextID; //6 can be used to replace castBarCaption? - uint32 closeTextID; //7 - uint32 losOK; //8 - } button; - //2 GAMEOBJECT_TYPE_QUESTGIVER - struct - { - uint32 lockId; //0 -> Lock.dbc - uint32 questList; //1 - uint32 pageMaterial; //2 - uint32 gossipID; //3 - uint32 customAnim; //4 - uint32 noDamageImmune; //5 - uint32 openTextID; //6 can be used to replace castBarCaption? - uint32 losOK; //7 - uint32 allowMounted; //8 - uint32 large; //9 - } questgiver; - //3 GAMEOBJECT_TYPE_CHEST - struct - { - uint32 lockId; //0 -> Lock.dbc - uint32 lootId; //1 - uint32 chestRestockTime; //2 - uint32 consumable; //3 - uint32 minSuccessOpens; //4 - uint32 maxSuccessOpens; //5 - uint32 eventId; //6 lootedEvent - uint32 linkedTrapId; //7 - uint32 questId; //8 not used currently but store quest required for GO activation for player - uint32 level; //9 - uint32 losOK; //10 - uint32 leaveLoot; //11 - uint32 notInCombat; //12 - uint32 logLoot; //13 - uint32 openTextID; //14 can be used to replace castBarCaption? - uint32 groupLootRules; //15 - } chest; - //5 GAMEOBJECT_TYPE_GENERIC - struct - { - uint32 floatingTooltip; //0 - uint32 highlight; //1 - uint32 serverOnly; //2 - uint32 large; //3 - uint32 floatOnWater; //4 - uint32 questID; //5 - } _generic; - //6 GAMEOBJECT_TYPE_TRAP - struct - { - uint32 lockId; //0 -> Lock.dbc - uint32 level; //1 - uint32 radius; //2 radius for trap activation - uint32 spellId; //3 - uint32 charges; //4 need respawn (if > 0) - uint32 cooldown; //5 time in secs - uint32 autoCloseTime; //6 - uint32 startDelay; //7 - uint32 serverOnly; //8 - uint32 stealthed; //9 - uint32 large; //10 - uint32 stealthAffected; //11 - uint32 openTextID; //12 can be used to replace castBarCaption? - uint32 closeTextID; //13 - } trap; - //7 GAMEOBJECT_TYPE_CHAIR - struct - { - uint32 slots; //0 - uint32 height; //1 - uint32 onlyCreatorUse; //2 - } chair; - //8 GAMEOBJECT_TYPE_SPELL_FOCUS - struct - { - uint32 focusId; //0 - uint32 dist; //1 - uint32 linkedTrapId; //2 - uint32 serverOnly; //3 - uint32 questID; //4 - uint32 large; //5 - } spellFocus; - //9 GAMEOBJECT_TYPE_TEXT - struct - { - uint32 pageID; //0 - uint32 language; //1 - uint32 pageMaterial; //2 - uint32 allowMounted; //3 - } text; - //10 GAMEOBJECT_TYPE_GOOBER - struct - { - uint32 lockId; //0 -> Lock.dbc - uint32 questId; //1 - uint32 eventId; //2 - uint32 autoCloseTime; //3 - uint32 customAnim; //4 - uint32 consumable; //5 - uint32 cooldown; //6 - uint32 pageId; //7 - uint32 language; //8 - uint32 pageMaterial; //9 - uint32 spellId; //10 - uint32 noDamageImmune; //11 - uint32 linkedTrapId; //12 - uint32 large; //13 - uint32 openTextID; //14 can be used to replace castBarCaption? - uint32 closeTextID; //15 - uint32 losOK; //16 isBattlegroundObject - uint32 allowMounted; //17 - } goober; - //11 GAMEOBJECT_TYPE_TRANSPORT - struct - { - uint32 pause; //0 - uint32 startOpen; //1 - uint32 autoCloseTime; //2 secs till autoclose = autoCloseTime / 0x10000 - } transport; - //12 GAMEOBJECT_TYPE_AREADAMAGE - struct - { - uint32 lockId; //0 - uint32 radius; //1 - uint32 damageMin; //2 - uint32 damageMax; //3 - uint32 damageSchool; //4 - uint32 autoCloseTime; //5 secs till autoclose = autoCloseTime / 0x10000 - uint32 openTextID; //6 - uint32 closeTextID; //7 - } areadamage; - //13 GAMEOBJECT_TYPE_CAMERA - struct - { - uint32 lockId; //0 -> Lock.dbc - uint32 cinematicId; //1 - uint32 eventID; //2 - uint32 openTextID; //3 can be used to replace castBarCaption? - } camera; - //15 GAMEOBJECT_TYPE_MO_TRANSPORT - struct - { - uint32 taxiPathId; //0 - uint32 moveSpeed; //1 - uint32 accelRate; //2 - uint32 startEventID; //3 - uint32 stopEventID; //4 - uint32 transportPhysics; //5 - uint32 mapID; //6 - } moTransport; - //17 GAMEOBJECT_TYPE_FISHINGNODE - struct - { - uint32 _data0; //0 - uint32 lootId; //1 - } fishnode; - //18 GAMEOBJECT_TYPE_SUMMONING_RITUAL - struct - { - uint32 reqParticipants; //0 - uint32 spellId; //1 - uint32 animSpell; //2 - uint32 ritualPersistent; //3 - uint32 casterTargetSpell; //4 - uint32 casterTargetSpellTargets; //5 - uint32 castersGrouped; //6 - uint32 ritualNoTargetCheck; //7 - } summoningRitual; - //20 GAMEOBJECT_TYPE_AUCTIONHOUSE - struct - { - uint32 actionHouseID; //0 - } auctionhouse; - //21 GAMEOBJECT_TYPE_GUARDPOST - struct - { - uint32 creatureID; //0 - uint32 charges; //1 - } guardpost; - //22 GAMEOBJECT_TYPE_SPELLCASTER - struct - { - uint32 spellId; //0 - uint32 charges; //1 - uint32 partyOnly; //2 - } spellcaster; - //23 GAMEOBJECT_TYPE_MEETINGSTONE - struct - { - uint32 minLevel; //0 - uint32 maxLevel; //1 - uint32 areaID; //2 - } meetingstone; - //24 GAMEOBJECT_TYPE_FLAGSTAND - struct - { - uint32 lockId; //0 - uint32 pickupSpell; //1 - uint32 radius; //2 - uint32 returnAura; //3 - uint32 returnSpell; //4 - uint32 noDamageImmune; //5 - uint32 openTextID; //6 - uint32 losOK; //7 - } flagstand; - //25 GAMEOBJECT_TYPE_FISHINGHOLE // not implemented yet - struct - { - uint32 radius; //0 how close bobber must land for sending loot - uint32 lootId; //1 - uint32 minSuccessOpens; //2 - uint32 maxSuccessOpens; //3 - uint32 lockId; //4 -> Lock.dbc; possibly 1628 for all? - } fishinghole; - //26 GAMEOBJECT_TYPE_FLAGDROP - struct - { - uint32 lockId; //0 - uint32 eventID; //1 - uint32 pickupSpell; //2 - uint32 noDamageImmune; //3 - uint32 openTextID; //4 - } flagdrop; - //27 GAMEOBJECT_TYPE_MINI_GAME - struct - { - uint32 gameType; //0 - } miniGame; - //29 GAMEOBJECT_TYPE_CAPTURE_POINT - struct - { - uint32 radius; //0 - uint32 spell; //1 - uint32 worldState1; //2 - uint32 worldstate2; //3 - uint32 winEventID1; //4 - uint32 winEventID2; //5 - uint32 contestedEventID1; //6 - uint32 contestedEventID2; //7 - uint32 progressEventID1; //8 - uint32 progressEventID2; //9 - uint32 neutralEventID1; //10 - uint32 neutralEventID2; //11 - uint32 neutralPercent; //12 - uint32 worldstate3; //13 - uint32 minSuperiority; //14 - uint32 maxSuperiority; //15 - uint32 minTime; //16 - uint32 maxTime; //17 - uint32 large; //18 - uint32 highlight; //19 - } capturePoint; - //30 GAMEOBJECT_TYPE_AURA_GENERATOR - struct - { - uint32 startOpen; //0 - uint32 radius; //1 - uint32 auraID1; //2 - uint32 conditionID1; //3 - uint32 auraID2; //4 - uint32 conditionID2; //5 - uint32 serverOnly; //6 - } auraGenerator; - //31 GAMEOBJECT_TYPE_DUNGEON_DIFFICULTY - struct - { - uint32 mapID; //0 - uint32 difficulty; //1 - } dungeonDifficulty; - //32 GAMEOBJECT_TYPE_DO_NOT_USE_YET - struct - { - uint32 mapID; //0 - uint32 difficulty; //1 - } doNotUseYet; - //33 GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING - struct - { - uint32 dmgPctState1; //0 - uint32 dmgPctState2; //1 - uint32 state1Name; //2 - uint32 state2Name; //3 - } destructibleBuilding; - - // not use for specific field access (only for output with loop by all filed), also this determinate max union size - struct // GAMEOBJECT_TYPE_SPELLCASTER - { - uint32 data[24]; - } raw; - }; - char *ScriptName; -}; - -struct GameObjectLocale -{ - std::vector<std::string> Name; - std::vector<std::string> CastBarCaption; -}; - -// from `gameobject` -struct GameObjectData -{ - uint32 id; // entry in gamobject_template - uint32 mapid; - float posX; - float posY; - float posZ; - float orientation; - float rotation0; - float rotation1; - float rotation2; - float rotation3; - int32 spawntimesecs; - uint32 animprogress; - uint32 go_state; - uint8 spawnMask; -}; - -// GCC have alternative #pragma pack() syntax and old gcc version not support pack(pop), also any gcc version not support it at some platform -#if defined( __GNUC__ ) -#pragma pack() -#else -#pragma pack(pop) -#endif - -// For containers: [GO_NOT_READY]->GO_READY (close)->GO_ACTIVATED (open) ->GO_JUST_DEACTIVATED->GO_READY -> ... -// For bobber: GO_NOT_READY ->GO_READY (close)->GO_ACTIVATED (open) ->GO_JUST_DEACTIVATED-><deleted> -// For door(closed):[GO_NOT_READY]->GO_READY (close)->GO_ACTIVATED (open) ->GO_JUST_DEACTIVATED->GO_READY(close) -> ... -// For door(open): [GO_NOT_READY]->GO_READY (open) ->GO_ACTIVATED (close)->GO_JUST_DEACTIVATED->GO_READY(open) -> ... -enum LootState -{ - GO_NOT_READY = 0, - GO_READY, // can be ready but despawned, and then not possible activate until spawn - GO_ACTIVATED, - GO_JUST_DEACTIVATED -}; - -class Unit; - -// 5 sec for bobber catch -#define FISHING_BOBBER_READY_TIME 5 - -class MANGOS_DLL_SPEC GameObject : public WorldObject -{ - public: - explicit GameObject(); - ~GameObject(); - - void AddToWorld(); - void RemoveFromWorld(); - - bool Create(uint32 guidlow, uint32 name_id, Map *map, float x, float y, float z, float ang, float rotation0, float rotation1, float rotation2, float rotation3, uint32 animprogress, uint32 go_state); - void Update(uint32 p_time); - static GameObject* GetGameObject(WorldObject& object, uint64 guid); - GameObjectInfo const* GetGOInfo() const; - - bool IsTransport() const; - - void SetOwnerGUID(uint64 owner) - { - m_spawnedByDefault = false; // all object with owner is despawned after delay - SetUInt64Value(OBJECT_FIELD_CREATED_BY, owner); - } - uint64 GetOwnerGUID() const { return GetUInt64Value(OBJECT_FIELD_CREATED_BY); } - Unit* GetOwner() const; - - uint32 GetDBTableGUIDLow() const { return m_DBTableGuid; } - - void Say(const char* text, uint32 language, uint64 TargetGuid) { MonsterSay(text,language,TargetGuid); } - void Yell(const char* text, uint32 language, uint64 TargetGuid) { MonsterYell(text,language,TargetGuid); } - void TextEmote(const char* text, uint64 TargetGuid) { MonsterTextEmote(text,TargetGuid); } - void Whisper(const char* text,uint64 receiver) { MonsterWhisper(text,receiver); } - void Say(int32 textId, uint32 language, uint64 TargetGuid) { MonsterSay(textId,language,TargetGuid); } - void Yell(int32 textId, uint32 language, uint64 TargetGuid) { MonsterYell(textId,language,TargetGuid); } - void TextEmote(int32 textId, uint64 TargetGuid) { MonsterTextEmote(textId,TargetGuid); } - void Whisper(int32 textId, uint64 receiver) { MonsterWhisper(textId,receiver); } - - void SaveToDB(); - void SaveToDB(uint32 mapid, uint8 spawnMask); - bool LoadFromDB(uint32 guid, Map *map); - void DeleteFromDB(); - void SetLootState(LootState s) { m_lootState = s; } - static uint32 GetLootId(GameObjectInfo const* info); - uint32 GetLootId() const { return GetLootId(GetGOInfo()); } - uint32 GetLockId() const - { - switch(GetGoType()) - { - case GAMEOBJECT_TYPE_DOOR: return GetGOInfo()->door.lockId; - case GAMEOBJECT_TYPE_BUTTON: return GetGOInfo()->button.lockId; - case GAMEOBJECT_TYPE_QUESTGIVER: return GetGOInfo()->questgiver.lockId; - case GAMEOBJECT_TYPE_CHEST: return GetGOInfo()->chest.lockId; - case GAMEOBJECT_TYPE_TRAP: return GetGOInfo()->trap.lockId; - case GAMEOBJECT_TYPE_GOOBER: return GetGOInfo()->goober.lockId; - case GAMEOBJECT_TYPE_AREADAMAGE: return GetGOInfo()->areadamage.lockId; - case GAMEOBJECT_TYPE_CAMERA: return GetGOInfo()->camera.lockId; - case GAMEOBJECT_TYPE_FLAGSTAND: return GetGOInfo()->flagstand.lockId; - case GAMEOBJECT_TYPE_FISHINGHOLE:return GetGOInfo()->fishinghole.lockId; - case GAMEOBJECT_TYPE_FLAGDROP: return GetGOInfo()->flagdrop.lockId; - default: return 0; - } - } - - time_t GetRespawnTime() const { return m_respawnTime; } - time_t GetRespawnTimeEx() const - { - time_t now = time(NULL); - if(m_respawnTime > now) - return m_respawnTime; - else - return now; - } - - void SetRespawnTime(int32 respawn) - { - m_respawnTime = respawn > 0 ? time(NULL) + respawn : 0; - m_respawnDelayTime = respawn > 0 ? respawn : 0; - } - void Respawn(); - bool isSpawned() const - { - return m_respawnDelayTime == 0 || - (m_respawnTime > 0 && !m_spawnedByDefault) || - (m_respawnTime == 0 && m_spawnedByDefault); - } - bool isSpawnedByDefault() const { return m_spawnedByDefault; } - uint32 GetRespawnDelay() const { return m_respawnDelayTime; } - void Refresh(); - void Delete(); - void SetSpellId(uint32 id) { m_spellId = id;} - uint32 GetSpellId() const { return m_spellId;} - void getFishLoot(Loot *loot); - GameobjectTypes GetGoType() const { return GameobjectTypes(GetUInt32Value(GAMEOBJECT_TYPE_ID)); } - void SetGoType(GameobjectTypes type) { SetUInt32Value(GAMEOBJECT_TYPE_ID, type); } - uint32 GetGoState() const { return GetUInt32Value(GAMEOBJECT_STATE); } - void SetGoState(uint32 state) { SetUInt32Value(GAMEOBJECT_STATE, state); } - uint32 GetGoArtKit() const { return GetUInt32Value(GAMEOBJECT_ARTKIT); } - void SetGoArtKit(uint32 artkit) { SetUInt32Value(GAMEOBJECT_ARTKIT, artkit); } - uint32 GetGoAnimProgress() const { return GetUInt32Value(GAMEOBJECT_ANIMPROGRESS); } - void SetGoAnimProgress(uint32 animprogress) { SetUInt32Value(GAMEOBJECT_ANIMPROGRESS, animprogress); } - - void Use(Unit* user); - - LootState getLootState() const { return m_lootState; } - - void AddToSkillupList(uint32 PlayerGuidLow) { m_SkillupList.push_back(PlayerGuidLow); } - bool IsInSkillupList(uint32 PlayerGuidLow) const - { - for (std::list<uint32>::const_iterator i = m_SkillupList.begin(); i != m_SkillupList.end(); ++i) - if (*i == PlayerGuidLow) return true; - return false; - } - void ClearSkillupList() { m_SkillupList.clear(); } - - void AddUniqueUse(Player* player); - void AddUse() { ++m_usetimes; } - - uint32 GetUseCount() const { return m_usetimes; } - uint32 GetUniqueUseCount() const { return m_unique_users.size(); } - - void SaveRespawnTime(); - - Loot loot; - - bool hasQuest(uint32 quest_id) const; - bool hasInvolvedQuest(uint32 quest_id) const; - bool ActivateToQuest(Player *pTarget) const; - void UseDoorOrButton(uint32 time_to_restore = 0); // 0 = use `gameobject`.`spawntimesecs` - - uint32 GetLinkedGameObjectEntry() const - { - switch(GetGoType()) - { - case GAMEOBJECT_TYPE_CHEST: return GetGOInfo()->chest.linkedTrapId; - case GAMEOBJECT_TYPE_SPELL_FOCUS: return GetGOInfo()->spellFocus.linkedTrapId; - case GAMEOBJECT_TYPE_GOOBER: return GetGOInfo()->goober.linkedTrapId; - default: return 0; - } - } - - uint32 GetAutoCloseTime() const - { - uint32 autoCloseTime = 0; - switch(GetGoType()) - { - case GAMEOBJECT_TYPE_DOOR: autoCloseTime = GetGOInfo()->door.autoCloseTime; break; - case GAMEOBJECT_TYPE_BUTTON: autoCloseTime = GetGOInfo()->button.autoCloseTime; break; - case GAMEOBJECT_TYPE_TRAP: autoCloseTime = GetGOInfo()->trap.autoCloseTime; break; - case GAMEOBJECT_TYPE_GOOBER: autoCloseTime = GetGOInfo()->goober.autoCloseTime; break; - case GAMEOBJECT_TYPE_TRANSPORT: autoCloseTime = GetGOInfo()->transport.autoCloseTime; break; - case GAMEOBJECT_TYPE_AREADAMAGE: autoCloseTime = GetGOInfo()->areadamage.autoCloseTime; break; - default: break; - } - return autoCloseTime / 0x10000; - } - - void TriggeringLinkedGameObject( uint32 trapEntry, Unit* target); - - bool isVisibleForInState(Player const* u, bool inVisibleList) const; - - GameObject* LookupFishingHoleAround(float range); - - GridReference<GameObject> &GetGridRef() { return m_gridRef; } - protected: - uint32 m_charges; // Spell charges for GAMEOBJECT_TYPE_SPELLCASTER (22) - uint32 m_spellId; - time_t m_respawnTime; // (secs) time of next respawn (or despawn if GO have owner()), - uint32 m_respawnDelayTime; // (secs) if 0 then current GO state no dependent from timer - LootState m_lootState; - bool m_spawnedByDefault; - time_t m_cooldownTime; // used as internal reaction delay time store (not state change reaction). - // For traps this: spell casting cooldown, for doors/buttons: reset time. - std::list<uint32> m_SkillupList; - - std::set<uint32> m_unique_users; - uint32 m_usetimes; - - uint32 m_DBTableGuid; - GameObjectInfo const* m_goInfo; - private: - void SwitchDoorOrButton(bool activate); - - GridReference<GameObject> m_gridRef; -}; -#endif +/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef MANGOSSERVER_GAMEOBJECT_H
+#define MANGOSSERVER_GAMEOBJECT_H
+
+#include "Common.h"
+#include "SharedDefines.h"
+#include "Object.h"
+#include "LootMgr.h"
+#include "Database/DatabaseEnv.h"
+
+// GCC have alternative #pragma pack(N) syntax and old gcc version not support pack(push,N), also any gcc version not support it at some platform
+#if defined( __GNUC__ )
+#pragma pack(1)
+#else
+#pragma pack(push,1)
+#endif
+
+// from `gameobject_template`
+struct GameObjectInfo
+{
+ uint32 id;
+ uint32 type;
+ uint32 displayId;
+ char *name;
+ char *castBarCaption;
+ uint32 faction;
+ uint32 flags;
+ float size;
+ union // different GO types have different data field
+ {
+ //0 GAMEOBJECT_TYPE_DOOR
+ struct
+ {
+ uint32 startOpen; //0 used client side to determine GO_ACTIVATED means open/closed
+ uint32 lockId; //1 -> Lock.dbc
+ uint32 autoCloseTime; //2 secs till autoclose = autoCloseTime / 0x10000
+ uint32 noDamageImmune; //3 break opening whenever you recieve damage?
+ uint32 openTextID; //4 can be used to replace castBarCaption?
+ uint32 closeTextID; //5
+ } door;
+ //1 GAMEOBJECT_TYPE_BUTTON
+ struct
+ {
+ uint32 startOpen; //0
+ uint32 lockId; //1 -> Lock.dbc
+ uint32 autoCloseTime; //2 secs till autoclose = autoCloseTime / 0x10000
+ uint32 linkedTrap; //3
+ uint32 noDamageImmune; //4 isBattlegroundObject
+ uint32 large; //5
+ uint32 openTextID; //6 can be used to replace castBarCaption?
+ uint32 closeTextID; //7
+ uint32 losOK; //8
+ } button;
+ //2 GAMEOBJECT_TYPE_QUESTGIVER
+ struct
+ {
+ uint32 lockId; //0 -> Lock.dbc
+ uint32 questList; //1
+ uint32 pageMaterial; //2
+ uint32 gossipID; //3
+ uint32 customAnim; //4
+ uint32 noDamageImmune; //5
+ uint32 openTextID; //6 can be used to replace castBarCaption?
+ uint32 losOK; //7
+ uint32 allowMounted; //8
+ uint32 large; //9
+ } questgiver;
+ //3 GAMEOBJECT_TYPE_CHEST
+ struct
+ {
+ uint32 lockId; //0 -> Lock.dbc
+ uint32 lootId; //1
+ uint32 chestRestockTime; //2
+ uint32 consumable; //3
+ uint32 minSuccessOpens; //4
+ uint32 maxSuccessOpens; //5
+ uint32 eventId; //6 lootedEvent
+ uint32 linkedTrapId; //7
+ uint32 questId; //8 not used currently but store quest required for GO activation for player
+ uint32 level; //9
+ uint32 losOK; //10
+ uint32 leaveLoot; //11
+ uint32 notInCombat; //12
+ uint32 logLoot; //13
+ uint32 openTextID; //14 can be used to replace castBarCaption?
+ uint32 groupLootRules; //15
+ } chest;
+ //5 GAMEOBJECT_TYPE_GENERIC
+ struct
+ {
+ uint32 floatingTooltip; //0
+ uint32 highlight; //1
+ uint32 serverOnly; //2
+ uint32 large; //3
+ uint32 floatOnWater; //4
+ uint32 questID; //5
+ } _generic;
+ //6 GAMEOBJECT_TYPE_TRAP
+ struct
+ {
+ uint32 lockId; //0 -> Lock.dbc
+ uint32 level; //1
+ uint32 radius; //2 radius for trap activation
+ uint32 spellId; //3
+ uint32 charges; //4 need respawn (if > 0)
+ uint32 cooldown; //5 time in secs
+ uint32 autoCloseTime; //6
+ uint32 startDelay; //7
+ uint32 serverOnly; //8
+ uint32 stealthed; //9
+ uint32 large; //10
+ uint32 stealthAffected; //11
+ uint32 openTextID; //12 can be used to replace castBarCaption?
+ uint32 closeTextID; //13
+ } trap;
+ //7 GAMEOBJECT_TYPE_CHAIR
+ struct
+ {
+ uint32 slots; //0
+ uint32 height; //1
+ uint32 onlyCreatorUse; //2
+ } chair;
+ //8 GAMEOBJECT_TYPE_SPELL_FOCUS
+ struct
+ {
+ uint32 focusId; //0
+ uint32 dist; //1
+ uint32 linkedTrapId; //2
+ uint32 serverOnly; //3
+ uint32 questID; //4
+ uint32 large; //5
+ } spellFocus;
+ //9 GAMEOBJECT_TYPE_TEXT
+ struct
+ {
+ uint32 pageID; //0
+ uint32 language; //1
+ uint32 pageMaterial; //2
+ uint32 allowMounted; //3
+ } text;
+ //10 GAMEOBJECT_TYPE_GOOBER
+ struct
+ {
+ uint32 lockId; //0 -> Lock.dbc
+ uint32 questId; //1
+ uint32 eventId; //2
+ uint32 autoCloseTime; //3
+ uint32 customAnim; //4
+ uint32 consumable; //5
+ uint32 cooldown; //6
+ uint32 pageId; //7
+ uint32 language; //8
+ uint32 pageMaterial; //9
+ uint32 spellId; //10
+ uint32 noDamageImmune; //11
+ uint32 linkedTrapId; //12
+ uint32 large; //13
+ uint32 openTextID; //14 can be used to replace castBarCaption?
+ uint32 closeTextID; //15
+ uint32 losOK; //16 isBattlegroundObject
+ uint32 allowMounted; //17
+ } goober;
+ //11 GAMEOBJECT_TYPE_TRANSPORT
+ struct
+ {
+ uint32 pause; //0
+ uint32 startOpen; //1
+ uint32 autoCloseTime; //2 secs till autoclose = autoCloseTime / 0x10000
+ } transport;
+ //12 GAMEOBJECT_TYPE_AREADAMAGE
+ struct
+ {
+ uint32 lockId; //0
+ uint32 radius; //1
+ uint32 damageMin; //2
+ uint32 damageMax; //3
+ uint32 damageSchool; //4
+ uint32 autoCloseTime; //5 secs till autoclose = autoCloseTime / 0x10000
+ uint32 openTextID; //6
+ uint32 closeTextID; //7
+ } areadamage;
+ //13 GAMEOBJECT_TYPE_CAMERA
+ struct
+ {
+ uint32 lockId; //0 -> Lock.dbc
+ uint32 cinematicId; //1
+ uint32 eventID; //2
+ uint32 openTextID; //3 can be used to replace castBarCaption?
+ } camera;
+ //15 GAMEOBJECT_TYPE_MO_TRANSPORT
+ struct
+ {
+ uint32 taxiPathId; //0
+ uint32 moveSpeed; //1
+ uint32 accelRate; //2
+ uint32 startEventID; //3
+ uint32 stopEventID; //4
+ uint32 transportPhysics; //5
+ uint32 mapID; //6
+ } moTransport;
+ //17 GAMEOBJECT_TYPE_FISHINGNODE
+ struct
+ {
+ uint32 _data0; //0
+ uint32 lootId; //1
+ } fishnode;
+ //18 GAMEOBJECT_TYPE_SUMMONING_RITUAL
+ struct
+ {
+ uint32 reqParticipants; //0
+ uint32 spellId; //1
+ uint32 animSpell; //2
+ uint32 ritualPersistent; //3
+ uint32 casterTargetSpell; //4
+ uint32 casterTargetSpellTargets; //5
+ uint32 castersGrouped; //6
+ uint32 ritualNoTargetCheck; //7
+ } summoningRitual;
+ //20 GAMEOBJECT_TYPE_AUCTIONHOUSE
+ struct
+ {
+ uint32 actionHouseID; //0
+ } auctionhouse;
+ //21 GAMEOBJECT_TYPE_GUARDPOST
+ struct
+ {
+ uint32 creatureID; //0
+ uint32 charges; //1
+ } guardpost;
+ //22 GAMEOBJECT_TYPE_SPELLCASTER
+ struct
+ {
+ uint32 spellId; //0
+ uint32 charges; //1
+ uint32 partyOnly; //2
+ } spellcaster;
+ //23 GAMEOBJECT_TYPE_MEETINGSTONE
+ struct
+ {
+ uint32 minLevel; //0
+ uint32 maxLevel; //1
+ uint32 areaID; //2
+ } meetingstone;
+ //24 GAMEOBJECT_TYPE_FLAGSTAND
+ struct
+ {
+ uint32 lockId; //0
+ uint32 pickupSpell; //1
+ uint32 radius; //2
+ uint32 returnAura; //3
+ uint32 returnSpell; //4
+ uint32 noDamageImmune; //5
+ uint32 openTextID; //6
+ uint32 losOK; //7
+ } flagstand;
+ //25 GAMEOBJECT_TYPE_FISHINGHOLE // not implemented yet
+ struct
+ {
+ uint32 radius; //0 how close bobber must land for sending loot
+ uint32 lootId; //1
+ uint32 minSuccessOpens; //2
+ uint32 maxSuccessOpens; //3
+ uint32 lockId; //4 -> Lock.dbc; possibly 1628 for all?
+ } fishinghole;
+ //26 GAMEOBJECT_TYPE_FLAGDROP
+ struct
+ {
+ uint32 lockId; //0
+ uint32 eventID; //1
+ uint32 pickupSpell; //2
+ uint32 noDamageImmune; //3
+ uint32 openTextID; //4
+ } flagdrop;
+ //27 GAMEOBJECT_TYPE_MINI_GAME
+ struct
+ {
+ uint32 gameType; //0
+ } miniGame;
+ //29 GAMEOBJECT_TYPE_CAPTURE_POINT
+ struct
+ {
+ uint32 radius; //0
+ uint32 spell; //1
+ uint32 worldState1; //2
+ uint32 worldstate2; //3
+ uint32 winEventID1; //4
+ uint32 winEventID2; //5
+ uint32 contestedEventID1; //6
+ uint32 contestedEventID2; //7
+ uint32 progressEventID1; //8
+ uint32 progressEventID2; //9
+ uint32 neutralEventID1; //10
+ uint32 neutralEventID2; //11
+ uint32 neutralPercent; //12
+ uint32 worldstate3; //13
+ uint32 minSuperiority; //14
+ uint32 maxSuperiority; //15
+ uint32 minTime; //16
+ uint32 maxTime; //17
+ uint32 large; //18
+ uint32 highlight; //19
+ } capturePoint;
+ //30 GAMEOBJECT_TYPE_AURA_GENERATOR
+ struct
+ {
+ uint32 startOpen; //0
+ uint32 radius; //1
+ uint32 auraID1; //2
+ uint32 conditionID1; //3
+ uint32 auraID2; //4
+ uint32 conditionID2; //5
+ uint32 serverOnly; //6
+ } auraGenerator;
+ //31 GAMEOBJECT_TYPE_DUNGEON_DIFFICULTY
+ struct
+ {
+ uint32 mapID; //0
+ uint32 difficulty; //1
+ } dungeonDifficulty;
+ //32 GAMEOBJECT_TYPE_DO_NOT_USE_YET
+ struct
+ {
+ uint32 mapID; //0
+ uint32 difficulty; //1
+ } doNotUseYet;
+ //33 GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING
+ struct
+ {
+ uint32 dmgPctState1; //0
+ uint32 dmgPctState2; //1
+ uint32 state1Name; //2
+ uint32 state2Name; //3
+ } destructibleBuilding;
+
+ // not use for specific field access (only for output with loop by all filed), also this determinate max union size
+ struct // GAMEOBJECT_TYPE_SPELLCASTER
+ {
+ uint32 data[24];
+ } raw;
+ };
+ char *ScriptName;
+};
+
+struct GameObjectLocale
+{
+ std::vector<std::string> Name;
+ std::vector<std::string> CastBarCaption;
+};
+
+// from `gameobject`
+struct GameObjectData
+{
+ uint32 id; // entry in gamobject_template
+ uint32 mapid;
+ float posX;
+ float posY;
+ float posZ;
+ float orientation;
+ float rotation0;
+ float rotation1;
+ float rotation2;
+ float rotation3;
+ int32 spawntimesecs;
+ uint32 animprogress;
+ uint32 go_state;
+ uint8 spawnMask;
+};
+
+// GCC have alternative #pragma pack() syntax and old gcc version not support pack(pop), also any gcc version not support it at some platform
+#if defined( __GNUC__ )
+#pragma pack()
+#else
+#pragma pack(pop)
+#endif
+
+// For containers: [GO_NOT_READY]->GO_READY (close)->GO_ACTIVATED (open) ->GO_JUST_DEACTIVATED->GO_READY -> ...
+// For bobber: GO_NOT_READY ->GO_READY (close)->GO_ACTIVATED (open) ->GO_JUST_DEACTIVATED-><deleted>
+// For door(closed):[GO_NOT_READY]->GO_READY (close)->GO_ACTIVATED (open) ->GO_JUST_DEACTIVATED->GO_READY(close) -> ...
+// For door(open): [GO_NOT_READY]->GO_READY (open) ->GO_ACTIVATED (close)->GO_JUST_DEACTIVATED->GO_READY(open) -> ...
+enum LootState
+{
+ GO_NOT_READY = 0,
+ GO_READY, // can be ready but despawned, and then not possible activate until spawn
+ GO_ACTIVATED,
+ GO_JUST_DEACTIVATED
+};
+
+class Unit;
+
+// 5 sec for bobber catch
+#define FISHING_BOBBER_READY_TIME 5
+
+class MANGOS_DLL_SPEC GameObject : public WorldObject
+{
+ public:
+ explicit GameObject();
+ ~GameObject();
+
+ void AddToWorld();
+ void RemoveFromWorld();
+
+ bool Create(uint32 guidlow, uint32 name_id, Map *map, float x, float y, float z, float ang, float rotation0, float rotation1, float rotation2, float rotation3, uint32 animprogress, uint32 go_state);
+ void Update(uint32 p_time);
+ static GameObject* GetGameObject(WorldObject& object, uint64 guid);
+ GameObjectInfo const* GetGOInfo() const;
+
+ bool IsTransport() const;
+
+ void SetOwnerGUID(uint64 owner)
+ {
+ m_spawnedByDefault = false; // all object with owner is despawned after delay
+ SetUInt64Value(OBJECT_FIELD_CREATED_BY, owner);
+ }
+ uint64 GetOwnerGUID() const { return GetUInt64Value(OBJECT_FIELD_CREATED_BY); }
+ Unit* GetOwner() const;
+
+ uint32 GetDBTableGUIDLow() const { return m_DBTableGuid; }
+
+ void Say(const char* text, uint32 language, uint64 TargetGuid) { MonsterSay(text,language,TargetGuid); }
+ void Yell(const char* text, uint32 language, uint64 TargetGuid) { MonsterYell(text,language,TargetGuid); }
+ void TextEmote(const char* text, uint64 TargetGuid) { MonsterTextEmote(text,TargetGuid); }
+ void Whisper(const char* text,uint64 receiver) { MonsterWhisper(text,receiver); }
+ void Say(int32 textId, uint32 language, uint64 TargetGuid) { MonsterSay(textId,language,TargetGuid); }
+ void Yell(int32 textId, uint32 language, uint64 TargetGuid) { MonsterYell(textId,language,TargetGuid); }
+ void TextEmote(int32 textId, uint64 TargetGuid) { MonsterTextEmote(textId,TargetGuid); }
+ void Whisper(int32 textId, uint64 receiver) { MonsterWhisper(textId,receiver); }
+
+ void SaveToDB();
+ void SaveToDB(uint32 mapid, uint8 spawnMask);
+ bool LoadFromDB(uint32 guid, Map *map);
+ void DeleteFromDB();
+ void SetLootState(LootState s) { m_lootState = s; }
+ static uint32 GetLootId(GameObjectInfo const* info);
+ uint32 GetLootId() const { return GetLootId(GetGOInfo()); }
+ uint32 GetLockId() const
+ {
+ switch(GetGoType())
+ {
+ case GAMEOBJECT_TYPE_DOOR: return GetGOInfo()->door.lockId;
+ case GAMEOBJECT_TYPE_BUTTON: return GetGOInfo()->button.lockId;
+ case GAMEOBJECT_TYPE_QUESTGIVER: return GetGOInfo()->questgiver.lockId;
+ case GAMEOBJECT_TYPE_CHEST: return GetGOInfo()->chest.lockId;
+ case GAMEOBJECT_TYPE_TRAP: return GetGOInfo()->trap.lockId;
+ case GAMEOBJECT_TYPE_GOOBER: return GetGOInfo()->goober.lockId;
+ case GAMEOBJECT_TYPE_AREADAMAGE: return GetGOInfo()->areadamage.lockId;
+ case GAMEOBJECT_TYPE_CAMERA: return GetGOInfo()->camera.lockId;
+ case GAMEOBJECT_TYPE_FLAGSTAND: return GetGOInfo()->flagstand.lockId;
+ case GAMEOBJECT_TYPE_FISHINGHOLE:return GetGOInfo()->fishinghole.lockId;
+ case GAMEOBJECT_TYPE_FLAGDROP: return GetGOInfo()->flagdrop.lockId;
+ default: return 0;
+ }
+ }
+
+ time_t GetRespawnTime() const { return m_respawnTime; }
+ time_t GetRespawnTimeEx() const
+ {
+ time_t now = time(NULL);
+ if(m_respawnTime > now)
+ return m_respawnTime;
+ else
+ return now;
+ }
+
+ void SetRespawnTime(int32 respawn)
+ {
+ m_respawnTime = respawn > 0 ? time(NULL) + respawn : 0;
+ m_respawnDelayTime = respawn > 0 ? respawn : 0;
+ }
+ void Respawn();
+ bool isSpawned() const
+ {
+ return m_respawnDelayTime == 0 ||
+ (m_respawnTime > 0 && !m_spawnedByDefault) ||
+ (m_respawnTime == 0 && m_spawnedByDefault);
+ }
+ bool isSpawnedByDefault() const { return m_spawnedByDefault; }
+ uint32 GetRespawnDelay() const { return m_respawnDelayTime; }
+ void Refresh();
+ void Delete();
+ void SetSpellId(uint32 id) { m_spellId = id;}
+ uint32 GetSpellId() const { return m_spellId;}
+ void getFishLoot(Loot *loot);
+ GameobjectTypes GetGoType() const { return GameobjectTypes(GetUInt32Value(GAMEOBJECT_TYPE_ID)); }
+ void SetGoType(GameobjectTypes type) { SetUInt32Value(GAMEOBJECT_TYPE_ID, type); }
+ uint32 GetGoState() const { return GetUInt32Value(GAMEOBJECT_STATE); }
+ void SetGoState(uint32 state) { SetUInt32Value(GAMEOBJECT_STATE, state); }
+ uint32 GetGoArtKit() const { return GetUInt32Value(GAMEOBJECT_ARTKIT); }
+ void SetGoArtKit(uint32 artkit) { SetUInt32Value(GAMEOBJECT_ARTKIT, artkit); }
+ uint32 GetGoAnimProgress() const { return GetUInt32Value(GAMEOBJECT_ANIMPROGRESS); }
+ void SetGoAnimProgress(uint32 animprogress) { SetUInt32Value(GAMEOBJECT_ANIMPROGRESS, animprogress); }
+
+ void Use(Unit* user);
+
+ LootState getLootState() const { return m_lootState; }
+
+ void AddToSkillupList(uint32 PlayerGuidLow) { m_SkillupList.push_back(PlayerGuidLow); }
+ bool IsInSkillupList(uint32 PlayerGuidLow) const
+ {
+ for (std::list<uint32>::const_iterator i = m_SkillupList.begin(); i != m_SkillupList.end(); ++i)
+ if (*i == PlayerGuidLow) return true;
+ return false;
+ }
+ void ClearSkillupList() { m_SkillupList.clear(); }
+
+ void AddUniqueUse(Player* player);
+ void AddUse() { ++m_usetimes; }
+
+ uint32 GetUseCount() const { return m_usetimes; }
+ uint32 GetUniqueUseCount() const { return m_unique_users.size(); }
+
+ void SaveRespawnTime();
+
+ Loot loot;
+
+ bool hasQuest(uint32 quest_id) const;
+ bool hasInvolvedQuest(uint32 quest_id) const;
+ bool ActivateToQuest(Player *pTarget) const;
+ void UseDoorOrButton(uint32 time_to_restore = 0); // 0 = use `gameobject`.`spawntimesecs`
+
+ uint32 GetLinkedGameObjectEntry() const
+ {
+ switch(GetGoType())
+ {
+ case GAMEOBJECT_TYPE_CHEST: return GetGOInfo()->chest.linkedTrapId;
+ case GAMEOBJECT_TYPE_SPELL_FOCUS: return GetGOInfo()->spellFocus.linkedTrapId;
+ case GAMEOBJECT_TYPE_GOOBER: return GetGOInfo()->goober.linkedTrapId;
+ default: return 0;
+ }
+ }
+
+ uint32 GetAutoCloseTime() const
+ {
+ uint32 autoCloseTime = 0;
+ switch(GetGoType())
+ {
+ case GAMEOBJECT_TYPE_DOOR: autoCloseTime = GetGOInfo()->door.autoCloseTime; break;
+ case GAMEOBJECT_TYPE_BUTTON: autoCloseTime = GetGOInfo()->button.autoCloseTime; break;
+ case GAMEOBJECT_TYPE_TRAP: autoCloseTime = GetGOInfo()->trap.autoCloseTime; break;
+ case GAMEOBJECT_TYPE_GOOBER: autoCloseTime = GetGOInfo()->goober.autoCloseTime; break;
+ case GAMEOBJECT_TYPE_TRANSPORT: autoCloseTime = GetGOInfo()->transport.autoCloseTime; break;
+ case GAMEOBJECT_TYPE_AREADAMAGE: autoCloseTime = GetGOInfo()->areadamage.autoCloseTime; break;
+ default: break;
+ }
+ return autoCloseTime / 0x10000;
+ }
+
+ void TriggeringLinkedGameObject( uint32 trapEntry, Unit* target);
+
+ bool isVisibleForInState(Player const* u, bool inVisibleList) const;
+
+ GameObject* LookupFishingHoleAround(float range);
+
+ GridReference<GameObject> &GetGridRef() { return m_gridRef; }
+ protected:
+ uint32 m_charges; // Spell charges for GAMEOBJECT_TYPE_SPELLCASTER (22)
+ uint32 m_spellId;
+ time_t m_respawnTime; // (secs) time of next respawn (or despawn if GO have owner()),
+ uint32 m_respawnDelayTime; // (secs) if 0 then current GO state no dependent from timer
+ LootState m_lootState;
+ bool m_spawnedByDefault;
+ time_t m_cooldownTime; // used as internal reaction delay time store (not state change reaction).
+ // For traps this: spell casting cooldown, for doors/buttons: reset time.
+ std::list<uint32> m_SkillupList;
+
+ std::set<uint32> m_unique_users;
+ uint32 m_usetimes;
+
+ uint32 m_DBTableGuid; ///< For new or temporary gameobjects is 0 for saved it is lowguid
+ GameObjectInfo const* m_goInfo;
+ private:
+ void SwitchDoorOrButton(bool activate);
+
+ GridReference<GameObject> m_gridRef;
+};
+#endif
diff --git a/src/game/GossipDef.cpp b/src/game/GossipDef.cpp index 08f35c8618b..645cd962146 100644 --- a/src/game/GossipDef.cpp +++ b/src/game/GossipDef.cpp @@ -391,7 +391,7 @@ void PlayerMenu::SendQuestGiverStatus( uint8 questStatus, uint64 npcGUID ) data << uint8(questStatus);
pSession->SendPacket( &data );
- //sLog.outDebug( "WORLD: Sent SMSG_QUESTGIVER_STATUS NPC Guid=%u, status=%u",GUID_LOPART(npcGUID),questStatus);
+ sLog.outDebug( "WORLD: Sent SMSG_QUESTGIVER_STATUS NPC Guid=%u, status=%u",GUID_LOPART(npcGUID),questStatus);
}
void PlayerMenu::SendQuestGiverQuestDetails( Quest const *pQuest, uint64 npcGUID, bool ActivateAccept )
@@ -477,7 +477,7 @@ void PlayerMenu::SendQuestGiverQuestDetails( Quest const *pQuest, uint64 npcGUID }
pSession->SendPacket( &data );
- //sLog.outDebug("WORLD: Sent SMSG_QUESTGIVER_QUEST_DETAILS NPCGuid=%u, questid=%u",GUID_LOPART(npcGUID),pQuest->GetQuestId());
+ sLog.outDebug("WORLD: Sent SMSG_QUESTGIVER_QUEST_DETAILS NPCGuid=%u, questid=%u",GUID_LOPART(npcGUID),pQuest->GetQuestId());
}
void PlayerMenu::SendQuestQueryResponse( Quest const *pQuest )
@@ -515,7 +515,7 @@ void PlayerMenu::SendQuestQueryResponse( Quest const *pQuest ) WorldPacket data( SMSG_QUEST_QUERY_RESPONSE, 100 ); // guess size
data << uint32(pQuest->GetQuestId());
- data << uint32(pQuest->GetMinLevel()); // not MinLevel. Accepted values: 0, 1 or 2 Possible theory for future dev: 0==cannot in quest log, 1==can in quest log session only(removed on log out), 2==can in quest log always (save to db)
+ data << uint32(pQuest->GetQuestMethod()); // Accepted values: 0, 1 or 2. 0==IsAutoComplete() (skip objectives/details)
data << uint32(pQuest->GetQuestLevel()); // may be 0
data << uint32(pQuest->GetZoneOrSort()); // zone or sort to display in quest log
@@ -597,7 +597,7 @@ void PlayerMenu::SendQuestQueryResponse( Quest const *pQuest ) data << ObjectiveText[iI];
pSession->SendPacket( &data );
- //sLog.outDebug( "WORLD: Sent SMSG_QUEST_QUERY_RESPONSE questid=%u",pQuest->GetQuestId() );
+ sLog.outDebug( "WORLD: Sent SMSG_QUEST_QUERY_RESPONSE questid=%u",pQuest->GetQuestId() );
}
void PlayerMenu::SendQuestGiverOfferReward( Quest const* pQuest, uint64 npcGUID, bool EnbleNext )
@@ -679,7 +679,7 @@ void PlayerMenu::SendQuestGiverOfferReward( Quest const* pQuest, uint64 npcGUID, data << uint32(pQuest->GetRewSpellCast()); // casted spell
data << uint32(0); // Honor points reward, not implemented
pSession->SendPacket( &data );
- //sLog.outDebug( "WORLD: Sent SMSG_QUESTGIVER_OFFER_REWARD NPCGuid=%u, questid=%u",GUID_LOPART(npcGUID),pQuest->GetQuestId() );
+ sLog.outDebug( "WORLD: Sent SMSG_QUESTGIVER_OFFER_REWARD NPCGuid=%u, questid=%u",GUID_LOPART(npcGUID),pQuest->GetQuestId() );
}
void PlayerMenu::SendQuestGiverRequestItems( Quest const *pQuest, uint64 npcGUID, bool Completable, bool CloseOnCancel )
@@ -758,5 +758,5 @@ void PlayerMenu::SendQuestGiverRequestItems( Quest const *pQuest, uint64 npcGUID data << uint32(0x04) << uint32(0x08) << uint32(0x10);
pSession->SendPacket( &data );
- //sLog.outDebug( "WORLD: Sent SMSG_QUESTGIVER_REQUEST_ITEMS NPCGuid=%u, questid=%u",GUID_LOPART(npcGUID),pQuest->GetQuestId() );
+ sLog.outDebug( "WORLD: Sent SMSG_QUESTGIVER_REQUEST_ITEMS NPCGuid=%u, questid=%u",GUID_LOPART(npcGUID),pQuest->GetQuestId() );
}
diff --git a/src/game/ItemHandler.cpp b/src/game/ItemHandler.cpp index de687ad6a09..46a179fd78b 100644 --- a/src/game/ItemHandler.cpp +++ b/src/game/ItemHandler.cpp @@ -1,1221 +1,1211 @@ -/* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "Common.h" -#include "WorldPacket.h" -#include "WorldSession.h" -#include "World.h" -#include "Opcodes.h" -#include "Log.h" -#include "ObjectMgr.h" -#include "Player.h" -#include "Item.h" -#include "UpdateData.h" -#include "ObjectAccessor.h" - -void WorldSession::HandleSplitItemOpcode( WorldPacket & recv_data ) -{ - CHECK_PACKET_SIZE(recv_data,1+1+1+1+1); - - //sLog.outDebug("WORLD: CMSG_SPLIT_ITEM"); - uint8 srcbag, srcslot, dstbag, dstslot, count; - - recv_data >> srcbag >> srcslot >> dstbag >> dstslot >> count; - //sLog.outDebug("STORAGE: receive srcbag = %u, srcslot = %u, dstbag = %u, dstslot = %u, count = %u", srcbag, srcslot, dstbag, dstslot, count); - - uint16 src = ( (srcbag << 8) | srcslot ); - uint16 dst = ( (dstbag << 8) | dstslot ); - - if(src==dst) - return; - - if (count==0) - return; //check count - if zero it's fake packet - - _player->SplitItem( src, dst, count ); -} - -void WorldSession::HandleSwapInvItemOpcode( WorldPacket & recv_data ) -{ - CHECK_PACKET_SIZE(recv_data,1+1); - - //sLog.outDebug("WORLD: CMSG_SWAP_INV_ITEM"); - uint8 srcslot, dstslot; - - recv_data >> srcslot >> dstslot; - //sLog.outDebug("STORAGE: receive srcslot = %u, dstslot = %u", srcslot, dstslot); - - // prevent attempt swap same item to current position generated by client at special checting sequence - if(srcslot==dstslot) - return; - - uint16 src = ( (INVENTORY_SLOT_BAG_0 << 8) | srcslot ); - uint16 dst = ( (INVENTORY_SLOT_BAG_0 << 8) | dstslot ); - - _player->SwapItem( src, dst ); -} - -void WorldSession::HandleAutoEquipItemSlotOpcode( WorldPacket & recv_data ) -{ - CHECK_PACKET_SIZE(recv_data,8+1); - uint64 itemguid; - uint8 dstslot; - recv_data >> itemguid >> dstslot; - - // cheating attempt, client should never send opcode in that case - if(!Player::IsEquipmentPos(INVENTORY_SLOT_BAG_0, dstslot)) - return; - - Item* item = _player->GetItemByGuid(itemguid); - uint16 dstpos = dstslot | (INVENTORY_SLOT_BAG_0 << 8); - - if(!item || item->GetPos() == dstpos) - return; - - _player->SwapItem(item->GetPos(), dstpos); -} - -void WorldSession::HandleSwapItem( WorldPacket & recv_data ) -{ - CHECK_PACKET_SIZE(recv_data,1+1+1+1); - - //sLog.outDebug("WORLD: CMSG_SWAP_ITEM"); - uint8 dstbag, dstslot, srcbag, srcslot; - - recv_data >> dstbag >> dstslot >> srcbag >> srcslot ; - //sLog.outDebug("STORAGE: receive srcbag = %u, srcslot = %u, dstbag = %u, dstslot = %u", srcbag, srcslot, dstbag, dstslot); - - uint16 src = ( (srcbag << 8) | srcslot ); - uint16 dst = ( (dstbag << 8) | dstslot ); - - // prevent attempt swap same item to current position generated by client at special checting sequence - if(src==dst) - return; - - _player->SwapItem( src, dst ); -} - -void WorldSession::HandleAutoEquipItemOpcode( WorldPacket & recv_data ) -{ - CHECK_PACKET_SIZE(recv_data,1+1); - - //sLog.outDebug("WORLD: CMSG_AUTOEQUIP_ITEM"); - uint8 srcbag, srcslot; - - recv_data >> srcbag >> srcslot; - //sLog.outDebug("STORAGE: receive srcbag = %u, srcslot = %u", srcbag, srcslot); - - Item *pSrcItem = _player->GetItemByPos( srcbag, srcslot ); - if( !pSrcItem ) - return; // only at cheat - - if(pSrcItem->m_lootGenerated) // prevent swap looting item - { - //best error message found for attempting to swap while looting - _player->SendEquipError( EQUIP_ERR_CANT_DO_RIGHT_NOW, pSrcItem, NULL ); - return; - } - - uint16 dest; - uint8 msg = _player->CanEquipItem( NULL_SLOT, dest, pSrcItem, !pSrcItem->IsBag() ); - if( msg != EQUIP_ERR_OK ) - { - _player->SendEquipError( msg, pSrcItem, NULL ); - return; - } - - uint16 src = pSrcItem->GetPos(); - if(dest==src) // prevent equip in same slot, only at cheat - return; - - Item *pDstItem = _player->GetItemByPos( dest ); - if( !pDstItem ) // empty slot, simple case - { - _player->RemoveItem( srcbag, srcslot, true ); - _player->EquipItem( dest, pSrcItem, true ); - _player->AutoUnequipOffhandIfNeed(); - } - else // have currently equipped item, not simple case - { - uint8 dstbag = pDstItem->GetBagSlot(); - uint8 dstslot = pDstItem->GetSlot(); - - msg = _player->CanUnequipItem( dest, !pSrcItem->IsBag() ); - if( msg != EQUIP_ERR_OK ) - { - _player->SendEquipError( msg, pDstItem, NULL ); - return; - } - - // check dest->src move possibility - ItemPosCountVec sSrc; - uint16 eSrc; - if( _player->IsInventoryPos( src ) ) - { - msg = _player->CanStoreItem( srcbag, srcslot, sSrc, pDstItem, true ); - if( msg != EQUIP_ERR_OK ) - msg = _player->CanStoreItem( srcbag, NULL_SLOT, sSrc, pDstItem, true ); - if( msg != EQUIP_ERR_OK ) - msg = _player->CanStoreItem( NULL_BAG, NULL_SLOT, sSrc, pDstItem, true ); - } - else if( _player->IsBankPos( src ) ) - { - msg = _player->CanBankItem( srcbag, srcslot, sSrc, pDstItem, true ); - if( msg != EQUIP_ERR_OK ) - msg = _player->CanBankItem( srcbag, NULL_SLOT, sSrc, pDstItem, true ); - if( msg != EQUIP_ERR_OK ) - msg = _player->CanBankItem( NULL_BAG, NULL_SLOT, sSrc, pDstItem, true ); - } - else if( _player->IsEquipmentPos( src ) ) - { - msg = _player->CanEquipItem( srcslot, eSrc, pDstItem, true); - if( msg == EQUIP_ERR_OK ) - msg = _player->CanUnequipItem( eSrc, true); - } - - if( msg != EQUIP_ERR_OK ) - { - _player->SendEquipError( msg, pDstItem, pSrcItem ); - return; - } - - // now do moves, remove... - _player->RemoveItem(dstbag, dstslot, false); - _player->RemoveItem(srcbag, srcslot, false); - - // add to dest - _player->EquipItem(dest, pSrcItem, true); - - // add to src - if( _player->IsInventoryPos( src ) ) - _player->StoreItem(sSrc, pDstItem, true); - else if( _player->IsBankPos( src ) ) - _player->BankItem(sSrc, pDstItem, true); - else if( _player->IsEquipmentPos( src ) ) - _player->EquipItem(eSrc, pDstItem, true); - - _player->AutoUnequipOffhandIfNeed(); - } -} - -void WorldSession::HandleDestroyItemOpcode( WorldPacket & recv_data ) -{ - CHECK_PACKET_SIZE(recv_data,1+1+1+1+1+1); - - //sLog.outDebug("WORLD: CMSG_DESTROYITEM"); - uint8 bag, slot, count, data1, data2, data3; - - recv_data >> bag >> slot >> count >> data1 >> data2 >> data3; - //sLog.outDebug("STORAGE: receive bag = %u, slot = %u, count = %u", bag, slot, count); - - uint16 pos = (bag << 8) | slot; - - // prevent drop unequipable items (in combat, for example) and non-empty bags - if(_player->IsEquipmentPos(pos) || _player->IsBagPos(pos)) - { - uint8 msg = _player->CanUnequipItem( pos, false ); - if( msg != EQUIP_ERR_OK ) - { - _player->SendEquipError( msg, _player->GetItemByPos(pos), NULL ); - return; - } - } - - Item *pItem = _player->GetItemByPos( bag, slot ); - if(!pItem) - { - _player->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL ); - return; - } - - if(count) - { - uint32 i_count = count; - _player->DestroyItemCount( pItem, i_count, true ); - } - else - _player->DestroyItem( bag, slot, true ); -} - -// Only _static_ data send in this packet !!! -void WorldSession::HandleItemQuerySingleOpcode( WorldPacket & recv_data ) -{ - CHECK_PACKET_SIZE(recv_data, 4); - - //sLog.outDebug("WORLD: CMSG_ITEM_QUERY_SINGLE"); - uint32 item; - recv_data >> item; - - sLog.outDetail("STORAGE: Item Query = %u", item); - - ItemPrototype const *pProto = objmgr.GetItemPrototype( item ); - if( pProto ) - { - std::string Name = pProto->Name1; - std::string Description = pProto->Description; - - int loc_idx = GetSessionDbLocaleIndex(); - if ( loc_idx >= 0 ) - { - ItemLocale const *il = objmgr.GetItemLocale(pProto->ItemId); - if (il) - { - if (il->Name.size() > loc_idx && !il->Name[loc_idx].empty()) - Name = il->Name[loc_idx]; - if (il->Description.size() > loc_idx && !il->Description[loc_idx].empty()) - Description = il->Description[loc_idx]; - } - } - // guess size - WorldPacket data( SMSG_ITEM_QUERY_SINGLE_RESPONSE, 600); - data << pProto->ItemId; - data << pProto->Class; - data << pProto->SubClass; - data << uint32(-1); // new 2.0.3, not exist in wdb cache? - data << Name; - data << uint8(0x00); //pProto->Name2; // blizz not send name there, just uint8(0x00); <-- \0 = empty string = empty name... - data << uint8(0x00); //pProto->Name3; // blizz not send name there, just uint8(0x00); - data << uint8(0x00); //pProto->Name4; // blizz not send name there, just uint8(0x00); - data << pProto->DisplayInfoID; - data << pProto->Quality; - data << pProto->Flags; - data << pProto->BuyPrice; - data << pProto->SellPrice; - data << pProto->InventoryType; - data << pProto->AllowableClass; - data << pProto->AllowableRace; - data << pProto->ItemLevel; - data << pProto->RequiredLevel; - data << pProto->RequiredSkill; - data << pProto->RequiredSkillRank; - data << pProto->RequiredSpell; - data << pProto->RequiredHonorRank; - data << pProto->RequiredCityRank; - data << pProto->RequiredReputationFaction; - data << pProto->RequiredReputationRank; - data << pProto->MaxCount; - data << pProto->Stackable; - data << pProto->ContainerSlots; - for(int i = 0; i < 10; i++) - { - data << pProto->ItemStat[i].ItemStatType; - data << pProto->ItemStat[i].ItemStatValue; - } - for(int i = 0; i < 5; i++) - { - data << pProto->Damage[i].DamageMin; - data << pProto->Damage[i].DamageMax; - data << pProto->Damage[i].DamageType; - } - data << pProto->Armor; - data << pProto->HolyRes; - data << pProto->FireRes; - data << pProto->NatureRes; - data << pProto->FrostRes; - data << pProto->ShadowRes; - data << pProto->ArcaneRes; - data << pProto->Delay; - data << pProto->Ammo_type; - - data << (float)pProto->RangedModRange; - for(int s = 0; s < 5; s++) - { - // send DBC data for cooldowns in same way as it used in Spell::SendSpellCooldown - // use `item_template` or if not set then only use spell cooldowns - SpellEntry const* spell = sSpellStore.LookupEntry(pProto->Spells[s].SpellId); - if(spell) - { - bool db_data = pProto->Spells[s].SpellCooldown >= 0 || pProto->Spells[s].SpellCategoryCooldown >= 0; - - data << pProto->Spells[s].SpellId; - data << pProto->Spells[s].SpellTrigger; - data << uint32(-abs(pProto->Spells[s].SpellCharges)); - - if(db_data) - { - data << uint32(pProto->Spells[s].SpellCooldown); - data << uint32(pProto->Spells[s].SpellCategory); - data << uint32(pProto->Spells[s].SpellCategoryCooldown); - } - else - { - data << uint32(spell->RecoveryTime); - data << uint32(spell->Category); - data << uint32(spell->CategoryRecoveryTime); - } - } - else - { - data << uint32(0); - data << uint32(0); - data << uint32(0); - data << uint32(-1); - data << uint32(0); - data << uint32(-1); - } - } - data << pProto->Bonding; - data << Description; - data << pProto->PageText; - data << pProto->LanguageID; - data << pProto->PageMaterial; - data << pProto->StartQuest; - data << pProto->LockID; - data << pProto->Material; - data << pProto->Sheath; - data << pProto->RandomProperty; - data << pProto->RandomSuffix; - data << pProto->Block; - data << pProto->ItemSet; - data << pProto->MaxDurability; - data << pProto->Area; - data << pProto->Map; // Added in 1.12.x & 2.0.1 client branch - data << pProto->BagFamily; - data << pProto->TotemCategory; - for(int s = 0; s < 3; s++) - { - data << pProto->Socket[s].Color; - data << pProto->Socket[s].Content; - } - data << pProto->socketBonus; - data << pProto->GemProperties; - data << pProto->RequiredDisenchantSkill; - data << pProto->ArmorDamageModifier; - data << uint32(0); // added in 2.4.2.8209, duration (seconds) - SendPacket( &data ); - } - else - { - sLog.outDebug( "WORLD: CMSG_ITEM_QUERY_SINGLE - NO item INFO! (ENTRY: %u)", item ); - WorldPacket data( SMSG_ITEM_QUERY_SINGLE_RESPONSE, 4); - data << uint32(item | 0x80000000); - SendPacket( &data ); - } -} - -void WorldSession::HandleReadItem( WorldPacket & recv_data ) -{ - CHECK_PACKET_SIZE(recv_data,1+1); - - //sLog.outDebug( "WORLD: CMSG_READ_ITEM"); - - uint8 bag, slot; - recv_data >> bag >> slot; - - //sLog.outDetail("STORAGE: Read bag = %u, slot = %u", bag, slot); - Item *pItem = _player->GetItemByPos( bag, slot ); - - if( pItem && pItem->GetProto()->PageText ) - { - WorldPacket data; - - uint8 msg = _player->CanUseItem( pItem ); - if( msg == EQUIP_ERR_OK ) - { - data.Initialize (SMSG_READ_ITEM_OK, 8); - sLog.outDetail("STORAGE: Item page sent"); - } - else - { - data.Initialize( SMSG_READ_ITEM_FAILED, 8 ); - sLog.outDetail("STORAGE: Unable to read item"); - _player->SendEquipError( msg, pItem, NULL ); - } - data << pItem->GetGUID(); - SendPacket(&data); - } - else - _player->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL ); -} - -void WorldSession::HandlePageQuerySkippedOpcode( WorldPacket & recv_data ) -{ - CHECK_PACKET_SIZE(recv_data,4+8); - - sLog.outDebug( "WORLD: Received CMSG_PAGE_TEXT_QUERY" ); - - uint32 itemid; - uint64 guid; - - recv_data >> itemid >> guid; - - sLog.outDetail( "Packet Info: itemid: %u guidlow: %u guidentry: %u guidhigh: %u", - itemid, GUID_LOPART(guid), GUID_ENPART(guid), GUID_HIPART(guid)); -} - -void WorldSession::HandleSellItemOpcode( WorldPacket & recv_data ) -{ - CHECK_PACKET_SIZE(recv_data,8+8+1); - - sLog.outDebug( "WORLD: Received CMSG_SELL_ITEM" ); - uint64 vendorguid, itemguid; - uint8 _count; - - recv_data >> vendorguid >> itemguid >> _count; - - // prevent possible overflow, as mangos uses uint32 for item count - uint32 count = _count; - - if(!itemguid) - return; - - Creature *pCreature = ObjectAccessor::GetNPCIfCanInteractWith(*_player, vendorguid,UNIT_NPC_FLAG_VENDOR); - if (!pCreature) - { - sLog.outDebug( "WORLD: HandleSellItemOpcode - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(vendorguid)) ); - _player->SendSellError( SELL_ERR_CANT_FIND_VENDOR, NULL, itemguid, 0); - return; - } - - // remove fake death - if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); - - Item *pItem = _player->GetItemByGuid( itemguid ); - if( pItem ) - { - // prevent sell not owner item - if(_player->GetGUID()!=pItem->GetOwnerGUID()) - { - _player->SendSellError( SELL_ERR_CANT_SELL_ITEM, pCreature, itemguid, 0); - return; - } - - // prevent sell non empty bag by drag-and-drop at vendor's item list - if(pItem->IsBag() && !((Bag*)pItem)->IsEmpty()) - { - _player->SendSellError( SELL_ERR_CANT_SELL_ITEM, pCreature, itemguid, 0); - return; - } - - // prevent sell currently looted item - if(_player->GetLootGUID()==pItem->GetGUID()) - { - _player->SendSellError( SELL_ERR_CANT_SELL_ITEM, pCreature, itemguid, 0); - return; - } - - // special case at auto sell (sell all) - if(count==0) - { - count = pItem->GetCount(); - } - else - { - // prevent sell more items that exist in stack (possable only not from client) - if(count > pItem->GetCount()) - { - _player->SendSellError( SELL_ERR_CANT_SELL_ITEM, pCreature, itemguid, 0); - return; - } - } - - ItemPrototype const *pProto = pItem->GetProto(); - if( pProto ) - { - if( pProto->SellPrice > 0 ) - { - if(count < pItem->GetCount()) // need split items - { - Item *pNewItem = pItem->CloneItem( count, _player ); - if (!pNewItem) - { - sLog.outError("WORLD: HandleSellItemOpcode - could not create clone of item %u; count = %u", pItem->GetEntry(), count ); - _player->SendSellError( SELL_ERR_CANT_SELL_ITEM, pCreature, itemguid, 0); - return; - } - - pItem->SetCount( pItem->GetCount() - count ); - _player->ItemRemovedQuestCheck( pItem->GetEntry(), count ); - if( _player->IsInWorld() ) - pItem->SendUpdateToPlayer( _player ); - pItem->SetState(ITEM_CHANGED, _player); - - _player->AddItemToBuyBackSlot( pNewItem ); - if( _player->IsInWorld() ) - pNewItem->SendUpdateToPlayer( _player ); - } - else - { - _player->ItemRemovedQuestCheck( pItem->GetEntry(), pItem->GetCount()); - _player->RemoveItem( pItem->GetBagSlot(), pItem->GetSlot(), true); - pItem->RemoveFromUpdateQueueOf(_player); - _player->AddItemToBuyBackSlot( pItem ); - } - - _player->ModifyMoney( pProto->SellPrice * count ); - } - else - _player->SendSellError( SELL_ERR_CANT_SELL_ITEM, pCreature, itemguid, 0); - return; - } - } - _player->SendSellError( SELL_ERR_CANT_FIND_ITEM, pCreature, itemguid, 0); - return; -} - -void WorldSession::HandleBuybackItem(WorldPacket & recv_data) -{ - CHECK_PACKET_SIZE(recv_data,8+4); - - sLog.outDebug( "WORLD: Received CMSG_BUYBACK_ITEM" ); - uint64 vendorguid; - uint32 slot; - - recv_data >> vendorguid >> slot; - - Creature *pCreature = ObjectAccessor::GetNPCIfCanInteractWith(*_player, vendorguid,UNIT_NPC_FLAG_VENDOR); - if (!pCreature) - { - sLog.outDebug( "WORLD: HandleBuybackItem - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(vendorguid)) ); - _player->SendSellError( SELL_ERR_CANT_FIND_VENDOR, NULL, 0, 0); - return; - } - - // remove fake death - if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); - - Item *pItem = _player->GetItemFromBuyBackSlot( slot ); - if( pItem ) - { - uint32 price = _player->GetUInt32Value( PLAYER_FIELD_BUYBACK_PRICE_1 + slot - BUYBACK_SLOT_START ); - if( _player->GetMoney() < price ) - { - _player->SendBuyError( BUY_ERR_NOT_ENOUGHT_MONEY, pCreature, pItem->GetEntry(), 0); - return; - } - - ItemPosCountVec dest; - uint8 msg = _player->CanStoreItem( NULL_BAG, NULL_SLOT, dest, pItem, false ); - if( msg == EQUIP_ERR_OK ) - { - _player->ModifyMoney( -(int32)price ); - _player->RemoveItemFromBuyBackSlot( slot, false ); - _player->ItemAddedQuestCheck( pItem->GetEntry(), pItem->GetCount()); - _player->StoreItem( dest, pItem, true ); - } - else - _player->SendEquipError( msg, pItem, NULL ); - return; - } - else - _player->SendBuyError( BUY_ERR_CANT_FIND_ITEM, pCreature, 0, 0); -} - -void WorldSession::HandleBuyItemInSlotOpcode( WorldPacket & recv_data ) -{ - CHECK_PACKET_SIZE(recv_data,8+4+8+1+1); - - sLog.outDebug( "WORLD: Received CMSG_BUY_ITEM_IN_SLOT" ); - uint64 vendorguid, bagguid; - uint32 item; - uint8 slot, count; - - recv_data >> vendorguid >> item >> bagguid >> slot >> count; - - GetPlayer()->BuyItemFromVendor(vendorguid,item,count,bagguid,slot); -} - -void WorldSession::HandleBuyItemOpcode( WorldPacket & recv_data ) -{ - CHECK_PACKET_SIZE(recv_data,8+4+1+1); - - sLog.outDebug( "WORLD: Received CMSG_BUY_ITEM" ); - uint64 vendorguid; - uint32 item; - uint8 count, unk1; - - recv_data >> vendorguid >> item >> count >> unk1; - - GetPlayer()->BuyItemFromVendor(vendorguid,item,count,NULL_BAG,NULL_SLOT); -} - -void WorldSession::HandleListInventoryOpcode( WorldPacket & recv_data ) -{ - CHECK_PACKET_SIZE(recv_data,8); - - uint64 guid; - - recv_data >> guid; - - if(!GetPlayer()->isAlive()) - return; - - sLog.outDebug( "WORLD: Recvd CMSG_LIST_INVENTORY" ); - - SendListInventory( guid ); -} - -void WorldSession::SendListInventory( uint64 vendorguid ) -{ - sLog.outDebug( "WORLD: Sent SMSG_LIST_INVENTORY" ); - - Creature *pCreature = ObjectAccessor::GetNPCIfCanInteractWith(*_player, vendorguid,UNIT_NPC_FLAG_VENDOR); - if (!pCreature) - { - sLog.outDebug( "WORLD: SendListInventory - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(vendorguid)) ); - _player->SendSellError( SELL_ERR_CANT_FIND_VENDOR, NULL, 0, 0); - return; - } - - // remove fake death - if(GetPlayer()->hasUnitState(UNIT_STAT_DIED)) - GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH); - - // Stop the npc if moving - pCreature->StopMoving(); - // load vendor items if not yet - pCreature->LoadGoods(); - - uint8 numitems = pCreature->GetItemCount(); - uint8 count = 0; - uint32 ptime = time(NULL); - uint32 diff; - - WorldPacket data( SMSG_LIST_INVENTORY, (8+1+numitems*8*4) ); - data << uint64(vendorguid); - data << uint8(numitems); - - float discountMod = _player->GetReputationPriceDiscount(pCreature); - - ItemPrototype const *pProto; - for(int i = 0; i < numitems; i++ ) - { - CreatureItem* crItem = pCreature->GetItem(i); - if( crItem ) - { - pProto = objmgr.GetItemPrototype(crItem->id); - if( pProto ) - { - if((pProto->AllowableClass & _player->getClassMask()) == 0 && pProto->Bonding == BIND_WHEN_PICKED_UP && !_player->isGameMaster()) - continue; - ++count; - if( crItem->incrtime != 0 && (crItem->lastincr + crItem->incrtime <= ptime) ) - { - diff = uint32((ptime - crItem->lastincr)/crItem->incrtime); - if( (crItem->count + diff * pProto->BuyCount) <= crItem->maxcount ) - crItem->count += diff * pProto->BuyCount; - else - crItem->count = crItem->maxcount; - crItem->lastincr = ptime; - } - data << uint32(count); - data << uint32(crItem->id); - data << uint32(pProto->DisplayInfoID); - data << uint32(crItem->maxcount <= 0 ? 0xFFFFFFFF : crItem->count); - - uint32 price = pProto->BuyPrice; - - // reputation discount - price = uint32(floor(pProto->BuyPrice * discountMod)); - - data << uint32(price); - data << uint32(pProto->MaxDurability); - data << uint32(pProto->BuyCount); - data << uint32(crItem->ExtendedCost); - } - } - } - - if ( count == 0 || data.size() != 8 + 1 + size_t(count) * 8 * 4 ) - return; - - data.put<uint8>(8, count); - SendPacket( &data ); -} - -void WorldSession::HandleAutoStoreBagItemOpcode( WorldPacket & recv_data ) -{ - CHECK_PACKET_SIZE(recv_data,1+1+1); - - //sLog.outDebug("WORLD: CMSG_AUTOSTORE_BAG_ITEM"); - uint8 srcbag, srcslot, dstbag; - - recv_data >> srcbag >> srcslot >> dstbag; - //sLog.outDebug("STORAGE: receive srcbag = %u, srcslot = %u, dstbag = %u", srcbag, srcslot, dstbag); - - Item *pItem = _player->GetItemByPos( srcbag, srcslot ); - if( !pItem ) - return; - - uint16 src = pItem->GetPos(); - - // check unequip potability for equipped items and bank bags - if(_player->IsEquipmentPos ( src ) || _player->IsBagPos ( src )) - { - uint8 msg = _player->CanUnequipItem( src, !_player->IsBagPos ( src )); - if(msg != EQUIP_ERR_OK) - { - _player->SendEquipError( msg, pItem, NULL ); - return; - } - } - - ItemPosCountVec dest; - uint8 msg = _player->CanStoreItem( dstbag, NULL_SLOT, dest, pItem, false ); - if( msg != EQUIP_ERR_OK ) - { - _player->SendEquipError( msg, pItem, NULL ); - return; - } - - // no-op: placed in same slot - if(dest.size()==1 && dest[0].pos==src) - { - // just remove grey item state - _player->SendEquipError( EQUIP_ERR_NONE, pItem, NULL ); - return; - } - - _player->RemoveItem(srcbag, srcslot, true ); - _player->StoreItem( dest, pItem, true ); -} - -void WorldSession::HandleBuyBankSlotOpcode(WorldPacket& /*recvPacket*/) -{ - sLog.outDebug("WORLD: CMSG_BUY_BANK_SLOT"); - - uint32 slot = _player->GetByteValue(PLAYER_BYTES_2, 2); - - // next slot - ++slot; - - sLog.outDetail("PLAYER: Buy bank bag slot, slot number = %u", slot); - - BankBagSlotPricesEntry const* slotEntry = sBankBagSlotPricesStore.LookupEntry(slot); - - if(!slotEntry) - return; - - uint32 price = slotEntry->price; - - if (_player->GetMoney() < price) - return; - - _player->SetByteValue(PLAYER_BYTES_2, 2, slot); - _player->ModifyMoney(-int32(price)); -} - -void WorldSession::HandleAutoBankItemOpcode(WorldPacket& recvPacket) -{ - CHECK_PACKET_SIZE(recvPacket,1+1); - - sLog.outDebug("WORLD: CMSG_AUTOBANK_ITEM"); - uint8 srcbag, srcslot; - - recvPacket >> srcbag >> srcslot; - sLog.outDebug("STORAGE: receive srcbag = %u, srcslot = %u", srcbag, srcslot); - - Item *pItem = _player->GetItemByPos( srcbag, srcslot ); - if( !pItem ) - return; - - ItemPosCountVec dest; - uint8 msg = _player->CanBankItem( NULL_BAG, NULL_SLOT, dest, pItem, false ); - if( msg != EQUIP_ERR_OK ) - { - _player->SendEquipError( msg, pItem, NULL ); - return; - } - - _player->RemoveItem(srcbag, srcslot, true); - _player->BankItem( dest, pItem, true ); -} - -void WorldSession::HandleAutoStoreBankItemOpcode(WorldPacket& recvPacket) -{ - CHECK_PACKET_SIZE(recvPacket,1+1); - - sLog.outDebug("WORLD: CMSG_AUTOSTORE_BANK_ITEM"); - uint8 srcbag, srcslot; - - recvPacket >> srcbag >> srcslot; - sLog.outDebug("STORAGE: receive srcbag = %u, srcslot = %u", srcbag, srcslot); - - Item *pItem = _player->GetItemByPos( srcbag, srcslot ); - if( !pItem ) - return; - - if(_player->IsBankPos(srcbag, srcslot)) // moving from bank to inventory - { - ItemPosCountVec dest; - uint8 msg = _player->CanStoreItem( NULL_BAG, NULL_SLOT, dest, pItem, false ); - if( msg != EQUIP_ERR_OK ) - { - _player->SendEquipError( msg, pItem, NULL ); - return; - } - - _player->RemoveItem(srcbag, srcslot, true); - _player->StoreItem( dest, pItem, true ); - } - else // moving from inventory to bank - { - ItemPosCountVec dest; - uint8 msg = _player->CanBankItem( NULL_BAG, NULL_SLOT, dest, pItem, false ); - if( msg != EQUIP_ERR_OK ) - { - _player->SendEquipError( msg, pItem, NULL ); - return; - } - - _player->RemoveItem(srcbag, srcslot, true); - _player->BankItem( dest, pItem, true ); - } -} - -void WorldSession::HandleSetAmmoOpcode(WorldPacket & recv_data) -{ - CHECK_PACKET_SIZE(recv_data,4); - - if(!GetPlayer()->isAlive()) - { - GetPlayer()->SendEquipError( EQUIP_ERR_YOU_ARE_DEAD, NULL, NULL ); - return; - } - - sLog.outDebug("WORLD: CMSG_SET_AMMO"); - uint32 item; - - recv_data >> item; - - if(!item) - GetPlayer()->RemoveAmmo(); - else - GetPlayer()->SetAmmo(item); -} - -void WorldSession::SendEnchantmentLog(uint64 Target, uint64 Caster,uint32 ItemID,uint32 SpellID) -{ - WorldPacket data(SMSG_ENCHANTMENTLOG, (8+8+4+4+1)); // last check 2.0.10 - data << Target; - data << Caster; - data << ItemID; - data << SpellID; - data << uint8(0); - SendPacket(&data); -} - -void WorldSession::SendItemEnchantTimeUpdate(uint64 Playerguid, uint64 Itemguid,uint32 slot,uint32 Duration) -{ - // last check 2.0.10 - WorldPacket data(SMSG_ITEM_ENCHANT_TIME_UPDATE, (8+4+4+8)); - data << uint64(Itemguid); - data << uint32(slot); - data << uint32(Duration); - data << uint64(Playerguid); - SendPacket(&data); -} - -void WorldSession::HandleItemNameQueryOpcode(WorldPacket & recv_data) -{ - CHECK_PACKET_SIZE(recv_data,4); - - uint32 itemid; - recv_data >> itemid; - sLog.outDebug("WORLD: CMSG_ITEM_NAME_QUERY %u", itemid); - ItemPrototype const *pProto = objmgr.GetItemPrototype( itemid ); - if( pProto ) - { - std::string Name; - Name = pProto->Name1; - - int loc_idx = GetSessionDbLocaleIndex(); - if (loc_idx >= 0) - { - ItemLocale const *il = objmgr.GetItemLocale(pProto->ItemId); - if (il) - { - if (il->Name.size() > loc_idx && !il->Name[loc_idx].empty()) - Name = il->Name[loc_idx]; - } - } - // guess size - WorldPacket data(SMSG_ITEM_NAME_QUERY_RESPONSE, (4+10)); - data << uint32(pProto->ItemId); - data << Name; - data << uint32(pProto->InventoryType); - SendPacket(&data); - return; - } - else - sLog.outDebug("WORLD: CMSG_ITEM_NAME_QUERY for item %u failed (unknown item)", itemid); -} - -void WorldSession::HandleWrapItemOpcode(WorldPacket& recv_data) -{ - CHECK_PACKET_SIZE(recv_data,1+1+1+1); - - sLog.outDebug("Received opcode CMSG_WRAP_ITEM"); - - uint8 gift_bag, gift_slot, item_bag, item_slot; - //recv_data.hexlike(); - - recv_data >> gift_bag >> gift_slot; // paper - recv_data >> item_bag >> item_slot; // item - - sLog.outDebug("WRAP: receive gift_bag = %u, gift_slot = %u, item_bag = %u, item_slot = %u", gift_bag, gift_slot, item_bag, item_slot); - - Item *gift = _player->GetItemByPos( gift_bag, gift_slot ); - if(!gift) - { - _player->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, gift, NULL ); - return; - } - - if(!gift->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPER))// cheating: non-wrapper wrapper - { - _player->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, gift, NULL ); - return; - } - - Item *item = _player->GetItemByPos( item_bag, item_slot ); - - if( !item ) - { - _player->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, item, NULL ); - return; - } - - if(item==gift) // not possable with pacjket from real client - { - _player->SendEquipError( EQUIP_ERR_WRAPPED_CANT_BE_WRAPPED, item, NULL ); - return; - } - - if(item->IsEquipped()) - { - _player->SendEquipError( EQUIP_ERR_EQUIPPED_CANT_BE_WRAPPED, item, NULL ); - return; - } - - if(item->GetUInt64Value(ITEM_FIELD_GIFTCREATOR)) // HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPED); - { - _player->SendEquipError( EQUIP_ERR_WRAPPED_CANT_BE_WRAPPED, item, NULL ); - return; - } - - if(item->IsBag()) - { - _player->SendEquipError( EQUIP_ERR_BAGS_CANT_BE_WRAPPED, item, NULL ); - return; - } - - if(item->IsSoulBound()) - { - _player->SendEquipError( EQUIP_ERR_BOUND_CANT_BE_WRAPPED, item, NULL ); - return; - } - - if(item->GetMaxStackCount() != 1) - { - _player->SendEquipError( EQUIP_ERR_STACKABLE_CANT_BE_WRAPPED, item, NULL ); - return; - } - - // maybe not correct check (it is better than nothing) - if(item->GetProto()->MaxCount>0) - { - _player->SendEquipError( EQUIP_ERR_UNIQUE_CANT_BE_WRAPPED, item, NULL ); - return; - } - - CharacterDatabase.BeginTransaction(); - CharacterDatabase.PExecute("INSERT INTO character_gifts VALUES ('%u', '%u', '%u', '%u')", GUID_LOPART(item->GetOwnerGUID()), item->GetGUIDLow(), item->GetEntry(), item->GetUInt32Value(ITEM_FIELD_FLAGS)); - item->SetUInt32Value(OBJECT_FIELD_ENTRY, gift->GetUInt32Value(OBJECT_FIELD_ENTRY)); - - switch (item->GetEntry()) - { - case 5042: item->SetUInt32Value(OBJECT_FIELD_ENTRY, 5043); break; - case 5048: item->SetUInt32Value(OBJECT_FIELD_ENTRY, 5044); break; - case 17303: item->SetUInt32Value(OBJECT_FIELD_ENTRY, 17302); break; - case 17304: item->SetUInt32Value(OBJECT_FIELD_ENTRY, 17305); break; - case 17307: item->SetUInt32Value(OBJECT_FIELD_ENTRY, 17308); break; - case 21830: item->SetUInt32Value(OBJECT_FIELD_ENTRY, 21831); break; - } - item->SetUInt64Value(ITEM_FIELD_GIFTCREATOR, _player->GetGUID()); - item->SetUInt32Value(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPED); - item->SetState(ITEM_CHANGED, _player); - - if(item->GetState()==ITEM_NEW) // save new item, to have alway for `character_gifts` record in `item_instance` - { - // after save it will be impossible to remove the item from the queue - item->RemoveFromUpdateQueueOf(_player); - item->SaveToDB(); // item gave inventory record unchanged and can be save standalone - } - CharacterDatabase.CommitTransaction(); - - uint32 count = 1; - _player->DestroyItemCount(gift, count, true); -} - -void WorldSession::HandleSocketOpcode(WorldPacket& recv_data) -{ - sLog.outDebug("WORLD: CMSG_SOCKET_GEMS"); - - CHECK_PACKET_SIZE(recv_data,8*4); - - uint64 guids[4]; - uint32 GemEnchants[3], OldEnchants[3]; - Item *Gems[3]; - bool SocketBonusActivated, SocketBonusToBeActivated; - - for(int i = 0; i < 4; i++) - recv_data >> guids[i]; - - if(!guids[0]) - return; - - //cheat -> tried to socket same gem multiple times - if((guids[1] && (guids[1] == guids[2] || guids[1] == guids[3])) || (guids[2] && (guids[2] == guids[3]))) - return; - - Item *itemTarget = _player->GetItemByGuid(guids[0]); - if(!itemTarget) //missing item to socket - return; - - //this slot is excepted when applying / removing meta gem bonus - uint8 slot = itemTarget->IsEquipped() ? itemTarget->GetSlot() : NULL_SLOT; - - for(int i = 0; i < 3; i++) - Gems[i] = guids[i + 1] ? _player->GetItemByGuid(guids[i + 1]) : NULL; - - GemPropertiesEntry const *GemProps[3]; - for(int i = 0; i < 3; ++i) //get geminfo from dbc storage - { - GemProps[i] = (Gems[i]) ? sGemPropertiesStore.LookupEntry(Gems[i]->GetProto()->GemProperties) : NULL; - } - - for(int i = 0; i < 3; ++i) //check for hack maybe - { - // tried to put gem in socket where no socket exists / tried to put normal gem in meta socket - // tried to put meta gem in normal socket - if( GemProps[i] && ( !itemTarget->GetProto()->Socket[i].Color || - itemTarget->GetProto()->Socket[i].Color == SOCKET_COLOR_META && GemProps[i]->color != SOCKET_COLOR_META || - itemTarget->GetProto()->Socket[i].Color != SOCKET_COLOR_META && GemProps[i]->color == SOCKET_COLOR_META ) ) - return; - } - - for(int i = 0; i < 3; ++i) //get new and old enchantments - { - GemEnchants[i] = (GemProps[i]) ? GemProps[i]->spellitemenchantement : 0; - OldEnchants[i] = itemTarget->GetEnchantmentId(EnchantmentSlot(SOCK_ENCHANTMENT_SLOT+i)); - } - - // check unique-equipped conditions - for(int i = 0; i < 3; ++i) - { - if (Gems[i] && (Gems[i]->GetProto()->Flags & ITEM_FLAGS_UNIQUE_EQUIPPED)) - { - // for equipped item check all equipment for duplicate equipped gems - if(itemTarget->IsEquipped()) - { - if(GetPlayer()->GetItemOrItemWithGemEquipped(Gems[i]->GetEntry())) - { - _player->SendEquipError( EQUIP_ERR_ITEM_UNIQUE_EQUIPABLE, itemTarget, NULL ); - return; - } - } - - // continue check for case when attempt add 2 similar unique equipped gems in one item. - for (int j = 0; j < 3; ++j) - { - if ((i != j) && (Gems[j]) && (Gems[i]->GetProto()->ItemId == Gems[j]->GetProto()->ItemId)) - { - _player->SendEquipError( EQUIP_ERR_ITEM_UNIQUE_EQUIPPABLE_SOCKETED, itemTarget, NULL ); - return; - } - } - for (int j = 0; j < 3; ++j) - { - if (OldEnchants[j]) - { - SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(OldEnchants[j]); - if(!enchantEntry) - continue; - - if ((enchantEntry->GemID == Gems[i]->GetProto()->ItemId) && (i != j)) - { - _player->SendEquipError( EQUIP_ERR_ITEM_UNIQUE_EQUIPPABLE_SOCKETED, itemTarget, NULL ); - return; - } - } - } - } - } - - SocketBonusActivated = itemTarget->GemsFitSockets(); //save state of socketbonus - _player->ToggleMetaGemsActive(slot, false); //turn off all metagems (except for the target item) - - //if a meta gem is being equipped, all information has to be written to the item before testing if the conditions for the gem are met - - //remove ALL enchants - for(uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+3; ++enchant_slot) - _player->ApplyEnchantment(itemTarget,EnchantmentSlot(enchant_slot),false); - - for(int i = 0; i < 3; ++i) - { - if(GemEnchants[i]) - { - itemTarget->SetEnchantment(EnchantmentSlot(SOCK_ENCHANTMENT_SLOT+i), GemEnchants[i],0,0); - if(Item* guidItem = _player->GetItemByGuid(guids[i + 1])) - _player->DestroyItem(guidItem->GetBagSlot(), guidItem->GetSlot(), true ); - } - } - - for(uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+3; ++enchant_slot) - _player->ApplyEnchantment(itemTarget,EnchantmentSlot(enchant_slot),true); - - SocketBonusToBeActivated = itemTarget->GemsFitSockets();//current socketbonus state - if(SocketBonusActivated ^ SocketBonusToBeActivated) //if there was a change... - { - _player->ApplyEnchantment(itemTarget,BONUS_ENCHANTMENT_SLOT,false); - itemTarget->SetEnchantment(BONUS_ENCHANTMENT_SLOT, (SocketBonusToBeActivated ? itemTarget->GetProto()->socketBonus : 0), 0, 0); - _player->ApplyEnchantment(itemTarget,BONUS_ENCHANTMENT_SLOT,true); - //it is not displayed, client has an inbuilt system to determine if the bonus is activated - } - - _player->ToggleMetaGemsActive(slot, true); //turn on all metagems (except for target item) -} - -void WorldSession::HandleCancelTempItemEnchantmentOpcode(WorldPacket& recv_data) -{ - sLog.outDebug("WORLD: CMSG_CANCEL_TEMP_ENCHANTMENT"); - - CHECK_PACKET_SIZE(recv_data,4); - - uint32 eslot; - - recv_data >> eslot; - - // apply only to equipped item - if(!Player::IsEquipmentPos(INVENTORY_SLOT_BAG_0,eslot)) - return; - - Item* item = GetPlayer()->GetItemByPos(INVENTORY_SLOT_BAG_0, eslot); - - if(!item) - return; - - if(!item->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT)) - return; - - GetPlayer()->ApplyEnchantment(item,TEMP_ENCHANTMENT_SLOT,false); - item->ClearEnchantment(TEMP_ENCHANTMENT_SLOT); -} +/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "Common.h"
+#include "WorldPacket.h"
+#include "WorldSession.h"
+#include "World.h"
+#include "Opcodes.h"
+#include "Log.h"
+#include "ObjectMgr.h"
+#include "Player.h"
+#include "Item.h"
+#include "UpdateData.h"
+#include "ObjectAccessor.h"
+
+void WorldSession::HandleSplitItemOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,1+1+1+1+1);
+
+ //sLog.outDebug("WORLD: CMSG_SPLIT_ITEM");
+ uint8 srcbag, srcslot, dstbag, dstslot, count;
+
+ recv_data >> srcbag >> srcslot >> dstbag >> dstslot >> count;
+ //sLog.outDebug("STORAGE: receive srcbag = %u, srcslot = %u, dstbag = %u, dstslot = %u, count = %u", srcbag, srcslot, dstbag, dstslot, count);
+
+ uint16 src = ( (srcbag << 8) | srcslot );
+ uint16 dst = ( (dstbag << 8) | dstslot );
+
+ if(src==dst)
+ return;
+
+ if (count==0)
+ return; //check count - if zero it's fake packet
+
+ _player->SplitItem( src, dst, count );
+}
+
+void WorldSession::HandleSwapInvItemOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,1+1);
+
+ //sLog.outDebug("WORLD: CMSG_SWAP_INV_ITEM");
+ uint8 srcslot, dstslot;
+
+ recv_data >> srcslot >> dstslot;
+ //sLog.outDebug("STORAGE: receive srcslot = %u, dstslot = %u", srcslot, dstslot);
+
+ // prevent attempt swap same item to current position generated by client at special checting sequence
+ if(srcslot==dstslot)
+ return;
+
+ uint16 src = ( (INVENTORY_SLOT_BAG_0 << 8) | srcslot );
+ uint16 dst = ( (INVENTORY_SLOT_BAG_0 << 8) | dstslot );
+
+ _player->SwapItem( src, dst );
+}
+
+void WorldSession::HandleAutoEquipItemSlotOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8+1);
+ uint64 itemguid;
+ uint8 dstslot;
+ recv_data >> itemguid >> dstslot;
+
+ // cheating attempt, client should never send opcode in that case
+ if(!Player::IsEquipmentPos(INVENTORY_SLOT_BAG_0, dstslot))
+ return;
+
+ Item* item = _player->GetItemByGuid(itemguid);
+ uint16 dstpos = dstslot | (INVENTORY_SLOT_BAG_0 << 8);
+
+ if(!item || item->GetPos() == dstpos)
+ return;
+
+ _player->SwapItem(item->GetPos(), dstpos);
+}
+
+void WorldSession::HandleSwapItem( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,1+1+1+1);
+
+ //sLog.outDebug("WORLD: CMSG_SWAP_ITEM");
+ uint8 dstbag, dstslot, srcbag, srcslot;
+
+ recv_data >> dstbag >> dstslot >> srcbag >> srcslot ;
+ //sLog.outDebug("STORAGE: receive srcbag = %u, srcslot = %u, dstbag = %u, dstslot = %u", srcbag, srcslot, dstbag, dstslot);
+
+ uint16 src = ( (srcbag << 8) | srcslot );
+ uint16 dst = ( (dstbag << 8) | dstslot );
+
+ // prevent attempt swap same item to current position generated by client at special checting sequence
+ if(src==dst)
+ return;
+
+ _player->SwapItem( src, dst );
+}
+
+void WorldSession::HandleAutoEquipItemOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,1+1);
+
+ //sLog.outDebug("WORLD: CMSG_AUTOEQUIP_ITEM");
+ uint8 srcbag, srcslot;
+
+ recv_data >> srcbag >> srcslot;
+ //sLog.outDebug("STORAGE: receive srcbag = %u, srcslot = %u", srcbag, srcslot);
+
+ Item *pSrcItem = _player->GetItemByPos( srcbag, srcslot );
+ if( !pSrcItem )
+ return; // only at cheat
+
+ if(pSrcItem->m_lootGenerated) // prevent swap looting item
+ {
+ //best error message found for attempting to swap while looting
+ _player->SendEquipError( EQUIP_ERR_CANT_DO_RIGHT_NOW, pSrcItem, NULL );
+ return;
+ }
+
+ uint16 dest;
+ uint8 msg = _player->CanEquipItem( NULL_SLOT, dest, pSrcItem, !pSrcItem->IsBag() );
+ if( msg != EQUIP_ERR_OK )
+ {
+ _player->SendEquipError( msg, pSrcItem, NULL );
+ return;
+ }
+
+ uint16 src = pSrcItem->GetPos();
+ if(dest==src) // prevent equip in same slot, only at cheat
+ return;
+
+ Item *pDstItem = _player->GetItemByPos( dest );
+ if( !pDstItem ) // empty slot, simple case
+ {
+ _player->RemoveItem( srcbag, srcslot, true );
+ _player->EquipItem( dest, pSrcItem, true );
+ _player->AutoUnequipOffhandIfNeed();
+ }
+ else // have currently equipped item, not simple case
+ {
+ uint8 dstbag = pDstItem->GetBagSlot();
+ uint8 dstslot = pDstItem->GetSlot();
+
+ msg = _player->CanUnequipItem( dest, !pSrcItem->IsBag() );
+ if( msg != EQUIP_ERR_OK )
+ {
+ _player->SendEquipError( msg, pDstItem, NULL );
+ return;
+ }
+
+ // check dest->src move possibility
+ ItemPosCountVec sSrc;
+ uint16 eSrc;
+ if( _player->IsInventoryPos( src ) )
+ {
+ msg = _player->CanStoreItem( srcbag, srcslot, sSrc, pDstItem, true );
+ if( msg != EQUIP_ERR_OK )
+ msg = _player->CanStoreItem( srcbag, NULL_SLOT, sSrc, pDstItem, true );
+ if( msg != EQUIP_ERR_OK )
+ msg = _player->CanStoreItem( NULL_BAG, NULL_SLOT, sSrc, pDstItem, true );
+ }
+ else if( _player->IsBankPos( src ) )
+ {
+ msg = _player->CanBankItem( srcbag, srcslot, sSrc, pDstItem, true );
+ if( msg != EQUIP_ERR_OK )
+ msg = _player->CanBankItem( srcbag, NULL_SLOT, sSrc, pDstItem, true );
+ if( msg != EQUIP_ERR_OK )
+ msg = _player->CanBankItem( NULL_BAG, NULL_SLOT, sSrc, pDstItem, true );
+ }
+ else if( _player->IsEquipmentPos( src ) )
+ {
+ msg = _player->CanEquipItem( srcslot, eSrc, pDstItem, true);
+ if( msg == EQUIP_ERR_OK )
+ msg = _player->CanUnequipItem( eSrc, true);
+ }
+
+ if( msg != EQUIP_ERR_OK )
+ {
+ _player->SendEquipError( msg, pDstItem, pSrcItem );
+ return;
+ }
+
+ // now do moves, remove...
+ _player->RemoveItem(dstbag, dstslot, false);
+ _player->RemoveItem(srcbag, srcslot, false);
+
+ // add to dest
+ _player->EquipItem(dest, pSrcItem, true);
+
+ // add to src
+ if( _player->IsInventoryPos( src ) )
+ _player->StoreItem(sSrc, pDstItem, true);
+ else if( _player->IsBankPos( src ) )
+ _player->BankItem(sSrc, pDstItem, true);
+ else if( _player->IsEquipmentPos( src ) )
+ _player->EquipItem(eSrc, pDstItem, true);
+
+ _player->AutoUnequipOffhandIfNeed();
+ }
+}
+
+void WorldSession::HandleDestroyItemOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,1+1+1+1+1+1);
+
+ //sLog.outDebug("WORLD: CMSG_DESTROYITEM");
+ uint8 bag, slot, count, data1, data2, data3;
+
+ recv_data >> bag >> slot >> count >> data1 >> data2 >> data3;
+ //sLog.outDebug("STORAGE: receive bag = %u, slot = %u, count = %u", bag, slot, count);
+
+ uint16 pos = (bag << 8) | slot;
+
+ // prevent drop unequipable items (in combat, for example) and non-empty bags
+ if(_player->IsEquipmentPos(pos) || _player->IsBagPos(pos))
+ {
+ uint8 msg = _player->CanUnequipItem( pos, false );
+ if( msg != EQUIP_ERR_OK )
+ {
+ _player->SendEquipError( msg, _player->GetItemByPos(pos), NULL );
+ return;
+ }
+ }
+
+ Item *pItem = _player->GetItemByPos( bag, slot );
+ if(!pItem)
+ {
+ _player->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL );
+ return;
+ }
+
+ if(count)
+ {
+ uint32 i_count = count;
+ _player->DestroyItemCount( pItem, i_count, true );
+ }
+ else
+ _player->DestroyItem( bag, slot, true );
+}
+
+// Only _static_ data send in this packet !!!
+void WorldSession::HandleItemQuerySingleOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data, 4);
+
+ //sLog.outDebug("WORLD: CMSG_ITEM_QUERY_SINGLE");
+ uint32 item;
+ recv_data >> item;
+
+ sLog.outDetail("STORAGE: Item Query = %u", item);
+
+ ItemPrototype const *pProto = objmgr.GetItemPrototype( item );
+ if( pProto )
+ {
+ std::string Name = pProto->Name1;
+ std::string Description = pProto->Description;
+
+ int loc_idx = GetSessionDbLocaleIndex();
+ if ( loc_idx >= 0 )
+ {
+ ItemLocale const *il = objmgr.GetItemLocale(pProto->ItemId);
+ if (il)
+ {
+ if (il->Name.size() > loc_idx && !il->Name[loc_idx].empty())
+ Name = il->Name[loc_idx];
+ if (il->Description.size() > loc_idx && !il->Description[loc_idx].empty())
+ Description = il->Description[loc_idx];
+ }
+ }
+ // guess size
+ WorldPacket data( SMSG_ITEM_QUERY_SINGLE_RESPONSE, 600);
+ data << pProto->ItemId;
+ data << pProto->Class;
+ data << pProto->SubClass;
+ data << uint32(-1); // new 2.0.3, not exist in wdb cache?
+ data << Name;
+ data << uint8(0x00); //pProto->Name2; // blizz not send name there, just uint8(0x00); <-- \0 = empty string = empty name...
+ data << uint8(0x00); //pProto->Name3; // blizz not send name there, just uint8(0x00);
+ data << uint8(0x00); //pProto->Name4; // blizz not send name there, just uint8(0x00);
+ data << pProto->DisplayInfoID;
+ data << pProto->Quality;
+ data << pProto->Flags;
+ data << pProto->BuyPrice;
+ data << pProto->SellPrice;
+ data << pProto->InventoryType;
+ data << pProto->AllowableClass;
+ data << pProto->AllowableRace;
+ data << pProto->ItemLevel;
+ data << pProto->RequiredLevel;
+ data << pProto->RequiredSkill;
+ data << pProto->RequiredSkillRank;
+ data << pProto->RequiredSpell;
+ data << pProto->RequiredHonorRank;
+ data << pProto->RequiredCityRank;
+ data << pProto->RequiredReputationFaction;
+ data << pProto->RequiredReputationRank;
+ data << pProto->MaxCount;
+ data << pProto->Stackable;
+ data << pProto->ContainerSlots;
+ for(int i = 0; i < 10; i++)
+ {
+ data << pProto->ItemStat[i].ItemStatType;
+ data << pProto->ItemStat[i].ItemStatValue;
+ }
+ for(int i = 0; i < 5; i++)
+ {
+ data << pProto->Damage[i].DamageMin;
+ data << pProto->Damage[i].DamageMax;
+ data << pProto->Damage[i].DamageType;
+ }
+ data << pProto->Armor;
+ data << pProto->HolyRes;
+ data << pProto->FireRes;
+ data << pProto->NatureRes;
+ data << pProto->FrostRes;
+ data << pProto->ShadowRes;
+ data << pProto->ArcaneRes;
+ data << pProto->Delay;
+ data << pProto->Ammo_type;
+
+ data << (float)pProto->RangedModRange;
+ for(int s = 0; s < 5; s++)
+ {
+ // send DBC data for cooldowns in same way as it used in Spell::SendSpellCooldown
+ // use `item_template` or if not set then only use spell cooldowns
+ SpellEntry const* spell = sSpellStore.LookupEntry(pProto->Spells[s].SpellId);
+ if(spell)
+ {
+ bool db_data = pProto->Spells[s].SpellCooldown >= 0 || pProto->Spells[s].SpellCategoryCooldown >= 0;
+
+ data << pProto->Spells[s].SpellId;
+ data << pProto->Spells[s].SpellTrigger;
+ data << uint32(-abs(pProto->Spells[s].SpellCharges));
+
+ if(db_data)
+ {
+ data << uint32(pProto->Spells[s].SpellCooldown);
+ data << uint32(pProto->Spells[s].SpellCategory);
+ data << uint32(pProto->Spells[s].SpellCategoryCooldown);
+ }
+ else
+ {
+ data << uint32(spell->RecoveryTime);
+ data << uint32(spell->Category);
+ data << uint32(spell->CategoryRecoveryTime);
+ }
+ }
+ else
+ {
+ data << uint32(0);
+ data << uint32(0);
+ data << uint32(0);
+ data << uint32(-1);
+ data << uint32(0);
+ data << uint32(-1);
+ }
+ }
+ data << pProto->Bonding;
+ data << Description;
+ data << pProto->PageText;
+ data << pProto->LanguageID;
+ data << pProto->PageMaterial;
+ data << pProto->StartQuest;
+ data << pProto->LockID;
+ data << pProto->Material;
+ data << pProto->Sheath;
+ data << pProto->RandomProperty;
+ data << pProto->RandomSuffix;
+ data << pProto->Block;
+ data << pProto->ItemSet;
+ data << pProto->MaxDurability;
+ data << pProto->Area;
+ data << pProto->Map; // Added in 1.12.x & 2.0.1 client branch
+ data << pProto->BagFamily;
+ data << pProto->TotemCategory;
+ for(int s = 0; s < 3; s++)
+ {
+ data << pProto->Socket[s].Color;
+ data << pProto->Socket[s].Content;
+ }
+ data << pProto->socketBonus;
+ data << pProto->GemProperties;
+ data << pProto->RequiredDisenchantSkill;
+ data << pProto->ArmorDamageModifier;
+ data << uint32(0); // added in 2.4.2.8209, duration (seconds)
+ SendPacket( &data );
+ }
+ else
+ {
+ sLog.outDebug( "WORLD: CMSG_ITEM_QUERY_SINGLE - NO item INFO! (ENTRY: %u)", item );
+ WorldPacket data( SMSG_ITEM_QUERY_SINGLE_RESPONSE, 4);
+ data << uint32(item | 0x80000000);
+ SendPacket( &data );
+ }
+}
+
+void WorldSession::HandleReadItem( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,1+1);
+
+ //sLog.outDebug( "WORLD: CMSG_READ_ITEM");
+
+ uint8 bag, slot;
+ recv_data >> bag >> slot;
+
+ //sLog.outDetail("STORAGE: Read bag = %u, slot = %u", bag, slot);
+ Item *pItem = _player->GetItemByPos( bag, slot );
+
+ if( pItem && pItem->GetProto()->PageText )
+ {
+ WorldPacket data;
+
+ uint8 msg = _player->CanUseItem( pItem );
+ if( msg == EQUIP_ERR_OK )
+ {
+ data.Initialize (SMSG_READ_ITEM_OK, 8);
+ sLog.outDetail("STORAGE: Item page sent");
+ }
+ else
+ {
+ data.Initialize( SMSG_READ_ITEM_FAILED, 8 );
+ sLog.outDetail("STORAGE: Unable to read item");
+ _player->SendEquipError( msg, pItem, NULL );
+ }
+ data << pItem->GetGUID();
+ SendPacket(&data);
+ }
+ else
+ _player->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL );
+}
+
+void WorldSession::HandlePageQuerySkippedOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,4+8);
+
+ sLog.outDebug( "WORLD: Received CMSG_PAGE_TEXT_QUERY" );
+
+ uint32 itemid;
+ uint64 guid;
+
+ recv_data >> itemid >> guid;
+
+ sLog.outDetail( "Packet Info: itemid: %u guidlow: %u guidentry: %u guidhigh: %u",
+ itemid, GUID_LOPART(guid), GUID_ENPART(guid), GUID_HIPART(guid));
+}
+
+void WorldSession::HandleSellItemOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8+8+1);
+
+ sLog.outDebug( "WORLD: Received CMSG_SELL_ITEM" );
+ uint64 vendorguid, itemguid;
+ uint8 _count;
+
+ recv_data >> vendorguid >> itemguid >> _count;
+
+ // prevent possible overflow, as mangos uses uint32 for item count
+ uint32 count = _count;
+
+ if(!itemguid)
+ return;
+
+ Creature *pCreature = ObjectAccessor::GetNPCIfCanInteractWith(*_player, vendorguid,UNIT_NPC_FLAG_VENDOR);
+ if (!pCreature)
+ {
+ sLog.outDebug( "WORLD: HandleSellItemOpcode - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(vendorguid)) );
+ _player->SendSellError( SELL_ERR_CANT_FIND_VENDOR, NULL, itemguid, 0);
+ return;
+ }
+
+ // remove fake death
+ if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
+ GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
+
+ Item *pItem = _player->GetItemByGuid( itemguid );
+ if( pItem )
+ {
+ // prevent sell not owner item
+ if(_player->GetGUID()!=pItem->GetOwnerGUID())
+ {
+ _player->SendSellError( SELL_ERR_CANT_SELL_ITEM, pCreature, itemguid, 0);
+ return;
+ }
+
+ // prevent sell non empty bag by drag-and-drop at vendor's item list
+ if(pItem->IsBag() && !((Bag*)pItem)->IsEmpty())
+ {
+ _player->SendSellError( SELL_ERR_CANT_SELL_ITEM, pCreature, itemguid, 0);
+ return;
+ }
+
+ // prevent sell currently looted item
+ if(_player->GetLootGUID()==pItem->GetGUID())
+ {
+ _player->SendSellError( SELL_ERR_CANT_SELL_ITEM, pCreature, itemguid, 0);
+ return;
+ }
+
+ // special case at auto sell (sell all)
+ if(count==0)
+ {
+ count = pItem->GetCount();
+ }
+ else
+ {
+ // prevent sell more items that exist in stack (possable only not from client)
+ if(count > pItem->GetCount())
+ {
+ _player->SendSellError( SELL_ERR_CANT_SELL_ITEM, pCreature, itemguid, 0);
+ return;
+ }
+ }
+
+ ItemPrototype const *pProto = pItem->GetProto();
+ if( pProto )
+ {
+ if( pProto->SellPrice > 0 )
+ {
+ if(count < pItem->GetCount()) // need split items
+ {
+ Item *pNewItem = pItem->CloneItem( count, _player );
+ if (!pNewItem)
+ {
+ sLog.outError("WORLD: HandleSellItemOpcode - could not create clone of item %u; count = %u", pItem->GetEntry(), count );
+ _player->SendSellError( SELL_ERR_CANT_SELL_ITEM, pCreature, itemguid, 0);
+ return;
+ }
+
+ pItem->SetCount( pItem->GetCount() - count );
+ _player->ItemRemovedQuestCheck( pItem->GetEntry(), count );
+ if( _player->IsInWorld() )
+ pItem->SendUpdateToPlayer( _player );
+ pItem->SetState(ITEM_CHANGED, _player);
+
+ _player->AddItemToBuyBackSlot( pNewItem );
+ if( _player->IsInWorld() )
+ pNewItem->SendUpdateToPlayer( _player );
+ }
+ else
+ {
+ _player->ItemRemovedQuestCheck( pItem->GetEntry(), pItem->GetCount());
+ _player->RemoveItem( pItem->GetBagSlot(), pItem->GetSlot(), true);
+ pItem->RemoveFromUpdateQueueOf(_player);
+ _player->AddItemToBuyBackSlot( pItem );
+ }
+
+ _player->ModifyMoney( pProto->SellPrice * count );
+ }
+ else
+ _player->SendSellError( SELL_ERR_CANT_SELL_ITEM, pCreature, itemguid, 0);
+ return;
+ }
+ }
+ _player->SendSellError( SELL_ERR_CANT_FIND_ITEM, pCreature, itemguid, 0);
+ return;
+}
+
+void WorldSession::HandleBuybackItem(WorldPacket & recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data,8+4);
+
+ sLog.outDebug( "WORLD: Received CMSG_BUYBACK_ITEM" );
+ uint64 vendorguid;
+ uint32 slot;
+
+ recv_data >> vendorguid >> slot;
+
+ Creature *pCreature = ObjectAccessor::GetNPCIfCanInteractWith(*_player, vendorguid,UNIT_NPC_FLAG_VENDOR);
+ if (!pCreature)
+ {
+ sLog.outDebug( "WORLD: HandleBuybackItem - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(vendorguid)) );
+ _player->SendSellError( SELL_ERR_CANT_FIND_VENDOR, NULL, 0, 0);
+ return;
+ }
+
+ // remove fake death
+ if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
+ GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
+
+ Item *pItem = _player->GetItemFromBuyBackSlot( slot );
+ if( pItem )
+ {
+ uint32 price = _player->GetUInt32Value( PLAYER_FIELD_BUYBACK_PRICE_1 + slot - BUYBACK_SLOT_START );
+ if( _player->GetMoney() < price )
+ {
+ _player->SendBuyError( BUY_ERR_NOT_ENOUGHT_MONEY, pCreature, pItem->GetEntry(), 0);
+ return;
+ }
+
+ ItemPosCountVec dest;
+ uint8 msg = _player->CanStoreItem( NULL_BAG, NULL_SLOT, dest, pItem, false );
+ if( msg == EQUIP_ERR_OK )
+ {
+ _player->ModifyMoney( -(int32)price );
+ _player->RemoveItemFromBuyBackSlot( slot, false );
+ _player->ItemAddedQuestCheck( pItem->GetEntry(), pItem->GetCount());
+ _player->StoreItem( dest, pItem, true );
+ }
+ else
+ _player->SendEquipError( msg, pItem, NULL );
+ return;
+ }
+ else
+ _player->SendBuyError( BUY_ERR_CANT_FIND_ITEM, pCreature, 0, 0);
+}
+
+void WorldSession::HandleBuyItemInSlotOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8+4+8+1+1);
+
+ sLog.outDebug( "WORLD: Received CMSG_BUY_ITEM_IN_SLOT" );
+ uint64 vendorguid, bagguid;
+ uint32 item;
+ uint8 slot, count;
+
+ recv_data >> vendorguid >> item >> bagguid >> slot >> count;
+
+ GetPlayer()->BuyItemFromVendor(vendorguid,item,count,bagguid,slot);
+}
+
+void WorldSession::HandleBuyItemOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8+4+1+1);
+
+ sLog.outDebug( "WORLD: Received CMSG_BUY_ITEM" );
+ uint64 vendorguid;
+ uint32 item;
+ uint8 count, unk1;
+
+ recv_data >> vendorguid >> item >> count >> unk1;
+
+ GetPlayer()->BuyItemFromVendor(vendorguid,item,count,NULL_BAG,NULL_SLOT);
+}
+
+void WorldSession::HandleListInventoryOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,8);
+
+ uint64 guid;
+
+ recv_data >> guid;
+
+ if(!GetPlayer()->isAlive())
+ return;
+
+ sLog.outDebug( "WORLD: Recvd CMSG_LIST_INVENTORY" );
+
+ SendListInventory( guid );
+}
+
+void WorldSession::SendListInventory( uint64 vendorguid )
+{
+ sLog.outDebug( "WORLD: Sent SMSG_LIST_INVENTORY" );
+
+ Creature *pCreature = ObjectAccessor::GetNPCIfCanInteractWith(*_player, vendorguid,UNIT_NPC_FLAG_VENDOR);
+ if (!pCreature)
+ {
+ sLog.outDebug( "WORLD: SendListInventory - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(vendorguid)) );
+ _player->SendSellError( SELL_ERR_CANT_FIND_VENDOR, NULL, 0, 0);
+ return;
+ }
+
+ // remove fake death
+ if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
+ GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
+
+ // Stop the npc if moving
+ pCreature->StopMoving();
+
+ VendorItemData const* vItems = pCreature->GetVendorItems();
+ if(!vItems)
+ {
+ _player->SendSellError( SELL_ERR_CANT_FIND_VENDOR, NULL, 0, 0);
+ return;
+ }
+
+ uint8 numitems = vItems->GetItemCount();
+ uint8 count = 0;
+
+ WorldPacket data( SMSG_LIST_INVENTORY, (8+1+numitems*8*4) );
+ data << uint64(vendorguid);
+ data << uint8(numitems);
+
+ float discountMod = _player->GetReputationPriceDiscount(pCreature);
+
+ for(int i = 0; i < numitems; i++ )
+ {
+ if(VendorItem const* crItem = vItems->GetItem(i))
+ {
+ if(ItemPrototype const *pProto = objmgr.GetItemPrototype(crItem->item))
+ {
+ if((pProto->AllowableClass & _player->getClassMask()) == 0 && pProto->Bonding == BIND_WHEN_PICKED_UP && !_player->isGameMaster())
+ continue;
+
+ ++count;
+
+ // reputation discount
+ uint32 price = uint32(floor(pProto->BuyPrice * discountMod));
+
+ data << uint32(count);
+ data << uint32(crItem->item);
+ data << uint32(pProto->DisplayInfoID);
+ data << uint32(crItem->maxcount <= 0 ? 0xFFFFFFFF : pCreature->GetVendorItemCurrentCount(crItem));
+ data << uint32(price);
+ data << uint32(pProto->MaxDurability);
+ data << uint32(pProto->BuyCount);
+ data << uint32(crItem->ExtendedCost);
+ }
+ }
+ }
+
+ if ( count == 0 || data.size() != 8 + 1 + size_t(count) * 8 * 4 )
+ return;
+
+ data.put<uint8>(8, count);
+ SendPacket( &data );
+}
+
+void WorldSession::HandleAutoStoreBagItemOpcode( WorldPacket & recv_data )
+{
+ CHECK_PACKET_SIZE(recv_data,1+1+1);
+
+ //sLog.outDebug("WORLD: CMSG_AUTOSTORE_BAG_ITEM");
+ uint8 srcbag, srcslot, dstbag;
+
+ recv_data >> srcbag >> srcslot >> dstbag;
+ //sLog.outDebug("STORAGE: receive srcbag = %u, srcslot = %u, dstbag = %u", srcbag, srcslot, dstbag);
+
+ Item *pItem = _player->GetItemByPos( srcbag, srcslot );
+ if( !pItem )
+ return;
+
+ uint16 src = pItem->GetPos();
+
+ // check unequip potability for equipped items and bank bags
+ if(_player->IsEquipmentPos ( src ) || _player->IsBagPos ( src ))
+ {
+ uint8 msg = _player->CanUnequipItem( src, !_player->IsBagPos ( src ));
+ if(msg != EQUIP_ERR_OK)
+ {
+ _player->SendEquipError( msg, pItem, NULL );
+ return;
+ }
+ }
+
+ ItemPosCountVec dest;
+ uint8 msg = _player->CanStoreItem( dstbag, NULL_SLOT, dest, pItem, false );
+ if( msg != EQUIP_ERR_OK )
+ {
+ _player->SendEquipError( msg, pItem, NULL );
+ return;
+ }
+
+ // no-op: placed in same slot
+ if(dest.size()==1 && dest[0].pos==src)
+ {
+ // just remove grey item state
+ _player->SendEquipError( EQUIP_ERR_NONE, pItem, NULL );
+ return;
+ }
+
+ _player->RemoveItem(srcbag, srcslot, true );
+ _player->StoreItem( dest, pItem, true );
+}
+
+void WorldSession::HandleBuyBankSlotOpcode(WorldPacket& /*recvPacket*/)
+{
+ sLog.outDebug("WORLD: CMSG_BUY_BANK_SLOT");
+
+ uint32 slot = _player->GetByteValue(PLAYER_BYTES_2, 2);
+
+ // next slot
+ ++slot;
+
+ sLog.outDetail("PLAYER: Buy bank bag slot, slot number = %u", slot);
+
+ BankBagSlotPricesEntry const* slotEntry = sBankBagSlotPricesStore.LookupEntry(slot);
+
+ if(!slotEntry)
+ return;
+
+ uint32 price = slotEntry->price;
+
+ if (_player->GetMoney() < price)
+ return;
+
+ _player->SetByteValue(PLAYER_BYTES_2, 2, slot);
+ _player->ModifyMoney(-int32(price));
+}
+
+void WorldSession::HandleAutoBankItemOpcode(WorldPacket& recvPacket)
+{
+ CHECK_PACKET_SIZE(recvPacket,1+1);
+
+ sLog.outDebug("WORLD: CMSG_AUTOBANK_ITEM");
+ uint8 srcbag, srcslot;
+
+ recvPacket >> srcbag >> srcslot;
+ sLog.outDebug("STORAGE: receive srcbag = %u, srcslot = %u", srcbag, srcslot);
+
+ Item *pItem = _player->GetItemByPos( srcbag, srcslot );
+ if( !pItem )
+ return;
+
+ ItemPosCountVec dest;
+ uint8 msg = _player->CanBankItem( NULL_BAG, NULL_SLOT, dest, pItem, false );
+ if( msg != EQUIP_ERR_OK )
+ {
+ _player->SendEquipError( msg, pItem, NULL );
+ return;
+ }
+
+ _player->RemoveItem(srcbag, srcslot, true);
+ _player->BankItem( dest, pItem, true );
+}
+
+void WorldSession::HandleAutoStoreBankItemOpcode(WorldPacket& recvPacket)
+{
+ CHECK_PACKET_SIZE(recvPacket,1+1);
+
+ sLog.outDebug("WORLD: CMSG_AUTOSTORE_BANK_ITEM");
+ uint8 srcbag, srcslot;
+
+ recvPacket >> srcbag >> srcslot;
+ sLog.outDebug("STORAGE: receive srcbag = %u, srcslot = %u", srcbag, srcslot);
+
+ Item *pItem = _player->GetItemByPos( srcbag, srcslot );
+ if( !pItem )
+ return;
+
+ if(_player->IsBankPos(srcbag, srcslot)) // moving from bank to inventory
+ {
+ ItemPosCountVec dest;
+ uint8 msg = _player->CanStoreItem( NULL_BAG, NULL_SLOT, dest, pItem, false );
+ if( msg != EQUIP_ERR_OK )
+ {
+ _player->SendEquipError( msg, pItem, NULL );
+ return;
+ }
+
+ _player->RemoveItem(srcbag, srcslot, true);
+ _player->StoreItem( dest, pItem, true );
+ }
+ else // moving from inventory to bank
+ {
+ ItemPosCountVec dest;
+ uint8 msg = _player->CanBankItem( NULL_BAG, NULL_SLOT, dest, pItem, false );
+ if( msg != EQUIP_ERR_OK )
+ {
+ _player->SendEquipError( msg, pItem, NULL );
+ return;
+ }
+
+ _player->RemoveItem(srcbag, srcslot, true);
+ _player->BankItem( dest, pItem, true );
+ }
+}
+
+void WorldSession::HandleSetAmmoOpcode(WorldPacket & recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data,4);
+
+ if(!GetPlayer()->isAlive())
+ {
+ GetPlayer()->SendEquipError( EQUIP_ERR_YOU_ARE_DEAD, NULL, NULL );
+ return;
+ }
+
+ sLog.outDebug("WORLD: CMSG_SET_AMMO");
+ uint32 item;
+
+ recv_data >> item;
+
+ if(!item)
+ GetPlayer()->RemoveAmmo();
+ else
+ GetPlayer()->SetAmmo(item);
+}
+
+void WorldSession::SendEnchantmentLog(uint64 Target, uint64 Caster,uint32 ItemID,uint32 SpellID)
+{
+ WorldPacket data(SMSG_ENCHANTMENTLOG, (8+8+4+4+1)); // last check 2.0.10
+ data << Target;
+ data << Caster;
+ data << ItemID;
+ data << SpellID;
+ data << uint8(0);
+ SendPacket(&data);
+}
+
+void WorldSession::SendItemEnchantTimeUpdate(uint64 Playerguid, uint64 Itemguid,uint32 slot,uint32 Duration)
+{
+ // last check 2.0.10
+ WorldPacket data(SMSG_ITEM_ENCHANT_TIME_UPDATE, (8+4+4+8));
+ data << uint64(Itemguid);
+ data << uint32(slot);
+ data << uint32(Duration);
+ data << uint64(Playerguid);
+ SendPacket(&data);
+}
+
+void WorldSession::HandleItemNameQueryOpcode(WorldPacket & recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data,4);
+
+ uint32 itemid;
+ recv_data >> itemid;
+ sLog.outDebug("WORLD: CMSG_ITEM_NAME_QUERY %u", itemid);
+ ItemPrototype const *pProto = objmgr.GetItemPrototype( itemid );
+ if( pProto )
+ {
+ std::string Name;
+ Name = pProto->Name1;
+
+ int loc_idx = GetSessionDbLocaleIndex();
+ if (loc_idx >= 0)
+ {
+ ItemLocale const *il = objmgr.GetItemLocale(pProto->ItemId);
+ if (il)
+ {
+ if (il->Name.size() > loc_idx && !il->Name[loc_idx].empty())
+ Name = il->Name[loc_idx];
+ }
+ }
+ // guess size
+ WorldPacket data(SMSG_ITEM_NAME_QUERY_RESPONSE, (4+10));
+ data << uint32(pProto->ItemId);
+ data << Name;
+ data << uint32(pProto->InventoryType);
+ SendPacket(&data);
+ return;
+ }
+ else
+ sLog.outDebug("WORLD: CMSG_ITEM_NAME_QUERY for item %u failed (unknown item)", itemid);
+}
+
+void WorldSession::HandleWrapItemOpcode(WorldPacket& recv_data)
+{
+ CHECK_PACKET_SIZE(recv_data,1+1+1+1);
+
+ sLog.outDebug("Received opcode CMSG_WRAP_ITEM");
+
+ uint8 gift_bag, gift_slot, item_bag, item_slot;
+ //recv_data.hexlike();
+
+ recv_data >> gift_bag >> gift_slot; // paper
+ recv_data >> item_bag >> item_slot; // item
+
+ sLog.outDebug("WRAP: receive gift_bag = %u, gift_slot = %u, item_bag = %u, item_slot = %u", gift_bag, gift_slot, item_bag, item_slot);
+
+ Item *gift = _player->GetItemByPos( gift_bag, gift_slot );
+ if(!gift)
+ {
+ _player->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, gift, NULL );
+ return;
+ }
+
+ if(!gift->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPER))// cheating: non-wrapper wrapper
+ {
+ _player->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, gift, NULL );
+ return;
+ }
+
+ Item *item = _player->GetItemByPos( item_bag, item_slot );
+
+ if( !item )
+ {
+ _player->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, item, NULL );
+ return;
+ }
+
+ if(item==gift) // not possable with pacjket from real client
+ {
+ _player->SendEquipError( EQUIP_ERR_WRAPPED_CANT_BE_WRAPPED, item, NULL );
+ return;
+ }
+
+ if(item->IsEquipped())
+ {
+ _player->SendEquipError( EQUIP_ERR_EQUIPPED_CANT_BE_WRAPPED, item, NULL );
+ return;
+ }
+
+ if(item->GetUInt64Value(ITEM_FIELD_GIFTCREATOR)) // HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPED);
+ {
+ _player->SendEquipError( EQUIP_ERR_WRAPPED_CANT_BE_WRAPPED, item, NULL );
+ return;
+ }
+
+ if(item->IsBag())
+ {
+ _player->SendEquipError( EQUIP_ERR_BAGS_CANT_BE_WRAPPED, item, NULL );
+ return;
+ }
+
+ if(item->IsSoulBound())
+ {
+ _player->SendEquipError( EQUIP_ERR_BOUND_CANT_BE_WRAPPED, item, NULL );
+ return;
+ }
+
+ if(item->GetMaxStackCount() != 1)
+ {
+ _player->SendEquipError( EQUIP_ERR_STACKABLE_CANT_BE_WRAPPED, item, NULL );
+ return;
+ }
+
+ // maybe not correct check (it is better than nothing)
+ if(item->GetProto()->MaxCount>0)
+ {
+ _player->SendEquipError( EQUIP_ERR_UNIQUE_CANT_BE_WRAPPED, item, NULL );
+ return;
+ }
+
+ CharacterDatabase.BeginTransaction();
+ CharacterDatabase.PExecute("INSERT INTO character_gifts VALUES ('%u', '%u', '%u', '%u')", GUID_LOPART(item->GetOwnerGUID()), item->GetGUIDLow(), item->GetEntry(), item->GetUInt32Value(ITEM_FIELD_FLAGS));
+ item->SetUInt32Value(OBJECT_FIELD_ENTRY, gift->GetUInt32Value(OBJECT_FIELD_ENTRY));
+
+ switch (item->GetEntry())
+ {
+ case 5042: item->SetUInt32Value(OBJECT_FIELD_ENTRY, 5043); break;
+ case 5048: item->SetUInt32Value(OBJECT_FIELD_ENTRY, 5044); break;
+ case 17303: item->SetUInt32Value(OBJECT_FIELD_ENTRY, 17302); break;
+ case 17304: item->SetUInt32Value(OBJECT_FIELD_ENTRY, 17305); break;
+ case 17307: item->SetUInt32Value(OBJECT_FIELD_ENTRY, 17308); break;
+ case 21830: item->SetUInt32Value(OBJECT_FIELD_ENTRY, 21831); break;
+ }
+ item->SetUInt64Value(ITEM_FIELD_GIFTCREATOR, _player->GetGUID());
+ item->SetUInt32Value(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPED);
+ item->SetState(ITEM_CHANGED, _player);
+
+ if(item->GetState()==ITEM_NEW) // save new item, to have alway for `character_gifts` record in `item_instance`
+ {
+ // after save it will be impossible to remove the item from the queue
+ item->RemoveFromUpdateQueueOf(_player);
+ item->SaveToDB(); // item gave inventory record unchanged and can be save standalone
+ }
+ CharacterDatabase.CommitTransaction();
+
+ uint32 count = 1;
+ _player->DestroyItemCount(gift, count, true);
+}
+
+void WorldSession::HandleSocketOpcode(WorldPacket& recv_data)
+{
+ sLog.outDebug("WORLD: CMSG_SOCKET_GEMS");
+
+ CHECK_PACKET_SIZE(recv_data,8*4);
+
+ uint64 guids[4];
+ uint32 GemEnchants[3], OldEnchants[3];
+ Item *Gems[3];
+ bool SocketBonusActivated, SocketBonusToBeActivated;
+
+ for(int i = 0; i < 4; i++)
+ recv_data >> guids[i];
+
+ if(!guids[0])
+ return;
+
+ //cheat -> tried to socket same gem multiple times
+ if((guids[1] && (guids[1] == guids[2] || guids[1] == guids[3])) || (guids[2] && (guids[2] == guids[3])))
+ return;
+
+ Item *itemTarget = _player->GetItemByGuid(guids[0]);
+ if(!itemTarget) //missing item to socket
+ return;
+
+ //this slot is excepted when applying / removing meta gem bonus
+ uint8 slot = itemTarget->IsEquipped() ? itemTarget->GetSlot() : NULL_SLOT;
+
+ for(int i = 0; i < 3; i++)
+ Gems[i] = guids[i + 1] ? _player->GetItemByGuid(guids[i + 1]) : NULL;
+
+ GemPropertiesEntry const *GemProps[3];
+ for(int i = 0; i < 3; ++i) //get geminfo from dbc storage
+ {
+ GemProps[i] = (Gems[i]) ? sGemPropertiesStore.LookupEntry(Gems[i]->GetProto()->GemProperties) : NULL;
+ }
+
+ for(int i = 0; i < 3; ++i) //check for hack maybe
+ {
+ // tried to put gem in socket where no socket exists / tried to put normal gem in meta socket
+ // tried to put meta gem in normal socket
+ if( GemProps[i] && ( !itemTarget->GetProto()->Socket[i].Color ||
+ itemTarget->GetProto()->Socket[i].Color == SOCKET_COLOR_META && GemProps[i]->color != SOCKET_COLOR_META ||
+ itemTarget->GetProto()->Socket[i].Color != SOCKET_COLOR_META && GemProps[i]->color == SOCKET_COLOR_META ) )
+ return;
+ }
+
+ for(int i = 0; i < 3; ++i) //get new and old enchantments
+ {
+ GemEnchants[i] = (GemProps[i]) ? GemProps[i]->spellitemenchantement : 0;
+ OldEnchants[i] = itemTarget->GetEnchantmentId(EnchantmentSlot(SOCK_ENCHANTMENT_SLOT+i));
+ }
+
+ // check unique-equipped conditions
+ for(int i = 0; i < 3; ++i)
+ {
+ if (Gems[i] && (Gems[i]->GetProto()->Flags & ITEM_FLAGS_UNIQUE_EQUIPPED))
+ {
+ // for equipped item check all equipment for duplicate equipped gems
+ if(itemTarget->IsEquipped())
+ {
+ if(GetPlayer()->GetItemOrItemWithGemEquipped(Gems[i]->GetEntry()))
+ {
+ _player->SendEquipError( EQUIP_ERR_ITEM_UNIQUE_EQUIPABLE, itemTarget, NULL );
+ return;
+ }
+ }
+
+ // continue check for case when attempt add 2 similar unique equipped gems in one item.
+ for (int j = 0; j < 3; ++j)
+ {
+ if ((i != j) && (Gems[j]) && (Gems[i]->GetProto()->ItemId == Gems[j]->GetProto()->ItemId))
+ {
+ _player->SendEquipError( EQUIP_ERR_ITEM_UNIQUE_EQUIPPABLE_SOCKETED, itemTarget, NULL );
+ return;
+ }
+ }
+ for (int j = 0; j < 3; ++j)
+ {
+ if (OldEnchants[j])
+ {
+ SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(OldEnchants[j]);
+ if(!enchantEntry)
+ continue;
+
+ if ((enchantEntry->GemID == Gems[i]->GetProto()->ItemId) && (i != j))
+ {
+ _player->SendEquipError( EQUIP_ERR_ITEM_UNIQUE_EQUIPPABLE_SOCKETED, itemTarget, NULL );
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ SocketBonusActivated = itemTarget->GemsFitSockets(); //save state of socketbonus
+ _player->ToggleMetaGemsActive(slot, false); //turn off all metagems (except for the target item)
+
+ //if a meta gem is being equipped, all information has to be written to the item before testing if the conditions for the gem are met
+
+ //remove ALL enchants
+ for(uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+3; ++enchant_slot)
+ _player->ApplyEnchantment(itemTarget,EnchantmentSlot(enchant_slot),false);
+
+ for(int i = 0; i < 3; ++i)
+ {
+ if(GemEnchants[i])
+ {
+ itemTarget->SetEnchantment(EnchantmentSlot(SOCK_ENCHANTMENT_SLOT+i), GemEnchants[i],0,0);
+ if(Item* guidItem = _player->GetItemByGuid(guids[i + 1]))
+ _player->DestroyItem(guidItem->GetBagSlot(), guidItem->GetSlot(), true );
+ }
+ }
+
+ for(uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+3; ++enchant_slot)
+ _player->ApplyEnchantment(itemTarget,EnchantmentSlot(enchant_slot),true);
+
+ SocketBonusToBeActivated = itemTarget->GemsFitSockets();//current socketbonus state
+ if(SocketBonusActivated ^ SocketBonusToBeActivated) //if there was a change...
+ {
+ _player->ApplyEnchantment(itemTarget,BONUS_ENCHANTMENT_SLOT,false);
+ itemTarget->SetEnchantment(BONUS_ENCHANTMENT_SLOT, (SocketBonusToBeActivated ? itemTarget->GetProto()->socketBonus : 0), 0, 0);
+ _player->ApplyEnchantment(itemTarget,BONUS_ENCHANTMENT_SLOT,true);
+ //it is not displayed, client has an inbuilt system to determine if the bonus is activated
+ }
+
+ _player->ToggleMetaGemsActive(slot, true); //turn on all metagems (except for target item)
+}
+
+void WorldSession::HandleCancelTempItemEnchantmentOpcode(WorldPacket& recv_data)
+{
+ sLog.outDebug("WORLD: CMSG_CANCEL_TEMP_ENCHANTMENT");
+
+ CHECK_PACKET_SIZE(recv_data,4);
+
+ uint32 eslot;
+
+ recv_data >> eslot;
+
+ // apply only to equipped item
+ if(!Player::IsEquipmentPos(INVENTORY_SLOT_BAG_0,eslot))
+ return;
+
+ Item* item = GetPlayer()->GetItemByPos(INVENTORY_SLOT_BAG_0, eslot);
+
+ if(!item)
+ return;
+
+ if(!item->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT))
+ return;
+
+ GetPlayer()->ApplyEnchantment(item,TEMP_ENCHANTMENT_SLOT,false);
+ item->ClearEnchantment(TEMP_ENCHANTMENT_SLOT);
+}
diff --git a/src/game/Language.h b/src/game/Language.h index ef27a9a0cd6..dcd76059e2c 100644 --- a/src/game/Language.h +++ b/src/game/Language.h @@ -303,6 +303,7 @@ enum MangosStrings LANG_LOOKUP_PLAYER_ACCOUNT = 328,
LANG_LOOKUP_PLAYER_CHARACTER = 329,
LANG_NO_PLAYERS_FOUND = 330,
+ LANG_EXTENDED_COST_NOT_EXIST = 331,
// Room for more level 2
diff --git a/src/game/Level2.cpp b/src/game/Level2.cpp index d3457d19493..d2911057ee3 100644 --- a/src/game/Level2.cpp +++ b/src/game/Level2.cpp @@ -1237,14 +1237,6 @@ bool ChatHandler::HandleAddVendorItemCommand(const char* args) if (!*args)
return false;
- Creature* vendor = getSelectedCreature();
- if (!vendor || !vendor->isVendor())
- {
- SendSysMessage(LANG_COMMAND_VENDORSELECTION);
- SetSentErrorMessage(true);
- return false;
- }
-
char* pitem = extractKeyFromLink((char*)args,"Hitem");
if (!pitem)
{
@@ -1252,6 +1244,7 @@ bool ChatHandler::HandleAddVendorItemCommand(const char* args) SetSentErrorMessage(true);
return false;
}
+
uint32 itemId = atol(pitem);
char* fmaxcount = strtok(NULL, " "); //add maxcount, default: 0
@@ -1267,41 +1260,20 @@ bool ChatHandler::HandleAddVendorItemCommand(const char* args) char* fextendedcost = strtok(NULL, " "); //add ExtendedCost, default: 0
uint32 extendedcost = fextendedcost ? atol(fextendedcost) : 0;
- ItemPrototype const *pProto = objmgr.GetItemPrototype(itemId);
- if(!pProto)
- {
- PSendSysMessage(LANG_ITEM_NOT_FOUND, itemId);
- SetSentErrorMessage(true);
- return false;
- }
-
- if(extendedcost && !sItemExtendedCostStore.LookupEntry(extendedcost))
- {
- PSendSysMessage(LANG_BAD_VALUE, extendedcost);
- SetSentErrorMessage(true);
- return false;
- }
+ Creature* vendor = getSelectedCreature();
- // load vendor items if not yet
- vendor->LoadGoods();
+ uint32 vendor_entry = vendor ? vendor->GetEntry() : 0;
- if(vendor->FindItem(itemId))
+ if(!objmgr.IsVendorItemValid(vendor_entry,itemId,maxcount,incrtime,extendedcost,m_session->GetPlayer()))
{
- PSendSysMessage(LANG_ITEM_ALREADY_IN_LIST,itemId);
SetSentErrorMessage(true);
return false;
}
- if (vendor->GetItemCount() >= MAX_VENDOR_ITEMS)
- {
- SendSysMessage(LANG_COMMAND_ADDVENDORITEMITEMS);
- SetSentErrorMessage(true);
- return false;
- }
+ objmgr.AddVendorItem(vendor_entry,itemId,maxcount,incrtime,extendedcost);
+
+ ItemPrototype const* pProto = objmgr.GetItemPrototype(itemId);
- // add to DB and to current ingame vendor
- WorldDatabase.PExecuteLog("INSERT INTO npc_vendor (entry,item,maxcount,incrtime,extendedcost) VALUES('%u','%u','%u','%u','%u')",vendor->GetEntry(), itemId, maxcount,incrtime,extendedcost);
- vendor->AddItem(itemId,maxcount,incrtime,extendedcost);
PSendSysMessage(LANG_ITEM_ADDED_TO_LIST,itemId,pProto->Name1,maxcount,incrtime,extendedcost);
return true;
}
@@ -1329,25 +1301,16 @@ bool ChatHandler::HandleDelVendorItemCommand(const char* args) }
uint32 itemId = atol(pitem);
- ItemPrototype const *pProto = objmgr.GetItemPrototype(itemId);
- if(!pProto)
- {
- PSendSysMessage(LANG_ITEM_NOT_FOUND, itemId);
- SetSentErrorMessage(true);
- return false;
- }
-
- // load vendor items if not yet
- vendor->LoadGoods();
- if (!vendor->RemoveItem(itemId))
+ if(!objmgr.RemoveVendorItem(vendor->GetEntry(),itemId))
{
PSendSysMessage(LANG_ITEM_NOT_IN_LIST,itemId);
SetSentErrorMessage(true);
return false;
}
- WorldDatabase.PExecuteLog("DELETE FROM npc_vendor WHERE entry='%u' AND item='%u'",vendor->GetEntry(), itemId);
+ ItemPrototype const* pProto = objmgr.GetItemPrototype(itemId);
+
PSendSysMessage(LANG_ITEM_DELETED_FROM_LIST,itemId,pProto->Name1);
return true;
}
diff --git a/src/game/ObjectMgr.cpp b/src/game/ObjectMgr.cpp index a6cbb0e653d..1bbca47d3b5 100644 --- a/src/game/ObjectMgr.cpp +++ b/src/game/ObjectMgr.cpp @@ -70,6 +70,40 @@ bool normalizePlayerName(std::string& name) return true;
}
+LanguageDesc lang_description[LANGUAGES_COUNT] =
+{
+ { LANG_ADDON, 0, 0 },
+ { LANG_UNIVERSAL, 0, 0 },
+ { LANG_ORCISH, 669, SKILL_LANG_ORCISH },
+ { LANG_DARNASSIAN, 671, SKILL_LANG_DARNASSIAN },
+ { LANG_TAURAHE, 670, SKILL_LANG_TAURAHE },
+ { LANG_DWARVISH, 672, SKILL_LANG_DWARVEN },
+ { LANG_COMMON, 668, SKILL_LANG_COMMON },
+ { LANG_DEMONIC, 815, SKILL_LANG_DEMON_TONGUE },
+ { LANG_TITAN, 816, SKILL_LANG_TITAN },
+ { LANG_THALASSIAN, 813, SKILL_LANG_THALASSIAN },
+ { LANG_DRACONIC, 814, SKILL_LANG_DRACONIC },
+ { LANG_KALIMAG, 817, SKILL_LANG_OLD_TONGUE },
+ { LANG_GNOMISH, 7340, SKILL_LANG_GNOMISH },
+ { LANG_TROLL, 7341, SKILL_LANG_TROLL },
+ { LANG_GUTTERSPEAK, 17737, SKILL_LANG_GUTTERSPEAK },
+ { LANG_DRAENEI, 29932, SKILL_LANG_DRAENEI },
+ { LANG_ZOMBIE, 0, 0 },
+ { LANG_GNOMISH_BINARY, 0, 0 },
+ { LANG_GOBLIN_BINARY, 0, 0 }
+};
+
+LanguageDesc const* GetLanguageDescByID(uint32 lang)
+{
+ for(int i = 0; i < LANGUAGES_COUNT; ++i)
+ {
+ if(uint32(lang_description[i].lang_id) == lang)
+ return &lang_description[i];
+ }
+
+ return NULL;
+}
+
ObjectMgr::ObjectMgr()
{
m_hiCharGuid = 1;
@@ -134,8 +168,7 @@ ObjectMgr::~ObjectMgr() delete itr->second;
for (CacheVendorItemMap::iterator itr = m_mCacheVendorItemMap.begin(); itr != m_mCacheVendorItemMap.end(); ++itr)
- for (VendorItemList::iterator itr2 = itr->second.begin(); itr2 != itr->second.end(); ++itr2)
- delete (*itr2);
+ itr->second.Clear();
for (CacheTrainerSpellMap::iterator itr = m_mCacheTrainerSpellMap.begin(); itr != m_mCacheTrainerSpellMap.end(); ++itr)
itr->second.Clear();
@@ -2697,35 +2730,35 @@ void ObjectMgr::LoadQuests() mExclusiveQuestGroups.clear();
- // 0 1 2 3 4 5 6 7
- QueryResult *result = WorldDatabase.Query("SELECT entry, ZoneOrSort, SkillOrClass, MinLevel, QuestLevel, Type, RequiredRaces, RequiredSkillValue,"
- // 8 9 10 11 12 13 14 15
+ // 0 1 2 3 4 5 6 7 8
+ QueryResult *result = WorldDatabase.Query("SELECT entry, Method, ZoneOrSort, SkillOrClass, MinLevel, QuestLevel, Type, RequiredRaces, RequiredSkillValue,"
+ // 9 10 11 12 13 14 15 16
"RepObjectiveFaction, RepObjectiveValue, RequiredMinRepFaction, RequiredMinRepValue, RequiredMaxRepFaction, RequiredMaxRepValue, SuggestedPlayers, LimitTime,"
- // 16 17 18 19 20 21 22 23 24 25
+ // 17 18 19 20 21 22 23 24 25 26
"QuestFlags, SpecialFlags, CharTitleId, PrevQuestId, NextQuestId, ExclusiveGroup, NextQuestInChain, SrcItemId, SrcItemCount, SrcSpell,"
- // 26 27 28 29 30 31 32 33 34 35
+ // 27 28 29 30 31 32 33 34 35 36
"Title, Details, Objectives, OfferRewardText, RequestItemsText, EndText, ObjectiveText1, ObjectiveText2, ObjectiveText3, ObjectiveText4,"
- // 36 37 38 39 40 41 42 43
+ // 37 38 39 40 41 42 43 44
"ReqItemId1, ReqItemId2, ReqItemId3, ReqItemId4, ReqItemCount1, ReqItemCount2, ReqItemCount3, ReqItemCount4,"
- // 44 45 46 47 48 49 50 51 52 53 54 55
+ // 45 46 47 48 49 50 51 52 53 54 54 55
"ReqSourceId1, ReqSourceId2, ReqSourceId3, ReqSourceId4, ReqSourceCount1, ReqSourceCount2, ReqSourceCount3, ReqSourceCount4, ReqSourceRef1, ReqSourceRef2, ReqSourceRef3, ReqSourceRef4,"
- // 56 57 58 59 60 61 62 63
+ // 57 58 59 60 61 62 63 64
"ReqCreatureOrGOId1, ReqCreatureOrGOId2, ReqCreatureOrGOId3, ReqCreatureOrGOId4, ReqCreatureOrGOCount1, ReqCreatureOrGOCount2, ReqCreatureOrGOCount3, ReqCreatureOrGOCount4,"
- // 64 65 66 67
+ // 65 66 67 68
"ReqSpellCast1, ReqSpellCast2, ReqSpellCast3, ReqSpellCast4,"
- // 68 69 70 71 72 73
+ // 69 70 71 72 73 74
"RewChoiceItemId1, RewChoiceItemId2, RewChoiceItemId3, RewChoiceItemId4, RewChoiceItemId5, RewChoiceItemId6,"
- // 74 75 76 77 78 79
+ // 75 76 77 78 79 80
"RewChoiceItemCount1, RewChoiceItemCount2, RewChoiceItemCount3, RewChoiceItemCount4, RewChoiceItemCount5, RewChoiceItemCount6,"
- // 80 81 82 83 84 85 86 87
+ // 81 82 83 84 85 86 87 88
"RewItemId1, RewItemId2, RewItemId3, RewItemId4, RewItemCount1, RewItemCount2, RewItemCount3, RewItemCount4,"
- // 88 89 90 91 92 93 94 95 96 97
+ // 89 90 91 92 93 94 95 96 97 98
"RewRepFaction1, RewRepFaction2, RewRepFaction3, RewRepFaction4, RewRepFaction5, RewRepValue1, RewRepValue2, RewRepValue3, RewRepValue4, RewRepValue5,"
- // 98 99 100 101 102 103 104 105 106 107
+ // 99 100 101 102 103 104 105 106 107 108
"RewOrReqMoney, RewMoneyMaxLevel, RewSpell, RewSpellCast, RewMailTemplateId, RewMailDelaySecs, PointMapId, PointX, PointY, PointOpt,"
- // 108 109 110 111 112 113 114 115 116 117
+ // 109 110 111 112 113 114 115 116 117 118
"DetailsEmote1, DetailsEmote2, DetailsEmote3, DetailsEmote4,IncompleteEmote, CompleteEmote, OfferRewardEmote1, OfferRewardEmote2, OfferRewardEmote3, OfferRewardEmote4,"
- // 118 119
+ // 119 120
"StartScript, CompleteScript"
" FROM quest_template");
if(result == NULL)
@@ -2761,6 +2794,11 @@ void ObjectMgr::LoadQuests() // additional quest integrity checks (GO, creature_template and item_template must be loaded already)
+ if( qinfo->GetQuestMethod() >= 3 )
+ {
+ sLog.outErrorDb("Quest %u has `Method` = %u, expected values are 0, 1 or 2.",qinfo->GetQuestId(),qinfo->GetQuestMethod());
+ }
+
if (qinfo->QuestFlags & ~QUEST_MANGOS_FLAGS_DB_ALLOWED)
{
sLog.outErrorDb("Quest %u has `SpecialFlags` = %u > max allowed value. Correct `SpecialFlags` to value <= %u",
@@ -6148,14 +6186,14 @@ bool ObjectMgr::LoadMangosStrings(DatabaseType& db, char const* table, int32 min if(entry==0)
{
- sLog.outString("Table `%s` contain reserved entry 0, ignored.",table);
+ sLog.outErrorDb("Table `%s` contain reserved entry 0, ignored.",table);
continue;
}
else if(entry < min_value || entry > max_value)
{
int32 start = min_value > 0 ? min_value : max_value;
int32 end = min_value > 0 ? max_value : min_value;
- sLog.outString("Table `%s` contain entry %i out of allowed range (%d - %d), ignored.",table,entry,start,end);
+ sLog.outErrorDb("Table `%s` contain entry %i out of allowed range (%d - %d), ignored.",table,entry,start,end);
continue;
}
@@ -6163,7 +6201,7 @@ bool ObjectMgr::LoadMangosStrings(DatabaseType& db, char const* table, int32 min if(data.Content.size() > 0)
{
- sLog.outString("Table `%s` contain data for already loaded entry %i (from another table?), ignored.",table,entry);
+ sLog.outErrorDb("Table `%s` contain data for already loaded entry %i (from another table?), ignored.",table,entry);
continue;
}
@@ -6656,22 +6694,30 @@ void ObjectMgr::LoadTrainerSpell() barGoLink bar( result->GetRowCount() );
- uint32 count = 0,entry,spell;
+ uint32 count = 0;
do
{
bar.step();
Field* fields = result->Fetch();
- entry = fields[0].GetUInt32();
- spell = fields[1].GetUInt32();
+ uint32 entry = fields[0].GetUInt32();
+ uint32 spell = fields[1].GetUInt32();
+
+ CreatureInfo const* cInfo = GetCreatureTemplate(entry);
- if(!GetCreatureTemplate(entry))
+ if(!cInfo)
{
sLog.outErrorDb("Table `npc_trainer` have entry for not existed creature template (Entry: %u), ignore", entry);
continue;
}
+ if(!(cInfo->npcflag & UNIT_NPC_FLAG_TRAINER))
+ {
+ sLog.outErrorDb("Table `npc_trainer` have data for not creature template (Entry: %u) without trainer flag, ignore", entry);
+ continue;
+ }
+
SpellEntry const *spellinfo = sSpellStore.LookupEntry(spell);
if(!spellinfo)
{
@@ -6715,10 +6761,7 @@ void ObjectMgr::LoadVendors() {
// For reload case
for (CacheVendorItemMap::iterator itr = m_mCacheVendorItemMap.begin(); itr != m_mCacheVendorItemMap.end(); ++itr)
- {
- for (VendorItemList::iterator itr2 = itr->second.begin(); itr2 != itr->second.end(); ++itr2)
- delete (*itr2);
- }
+ itr->second.Clear();
m_mCacheVendorItemMap.clear();
QueryResult *result = WorldDatabase.PQuery("SELECT entry, item, maxcount, incrtime, ExtendedCost FROM npc_vendor");
@@ -6736,48 +6779,23 @@ void ObjectMgr::LoadVendors() barGoLink bar( result->GetRowCount() );
uint32 count = 0;
- uint32 entry, item_id, ExtendedCost;
do
{
bar.step();
Field* fields = result->Fetch();
- entry = fields[0].GetUInt32();
- if(!GetCreatureTemplate(entry))
- {
- sLog.outErrorDb("Table `npc_vendor` have data for not existed creature template (Entry: %u), ignore", entry);
- continue;
- }
-
- item_id = fields[1].GetUInt32();
- if(!GetItemPrototype(item_id))
- {
- sLog.outErrorDb("Table `npc_vendor` for Vendor (Entry: %u) have in item list non-existed item (%u), ignore",entry,item_id);
- continue;
- }
+ uint32 entry = fields[0].GetUInt32();
+ uint32 item_id = fields[1].GetUInt32();
+ uint32 maxcount = fields[2].GetUInt32();
+ uint32 incrtime = fields[3].GetUInt32();
+ uint32 ExtendedCost = fields[4].GetUInt32();
- ExtendedCost = fields[4].GetUInt32();
- if(ExtendedCost && !sItemExtendedCostStore.LookupEntry(ExtendedCost))
- {
- sLog.outErrorDb("Table `npc_vendor` have Item (Entry: %u) with wrong ExtendedCost (%u) for vendor (%u), ignore",item_id,ExtendedCost,entry);
+ if(!IsVendorItemValid(entry,item_id,maxcount,incrtime,ExtendedCost))
continue;
- }
- VendorItemList& vList = m_mCacheVendorItemMap[entry];
+ VendorItemData& vList = m_mCacheVendorItemMap[entry];
- if(vList.size() >= MAX_VENDOR_ITEMS)
- {
- sLog.outErrorDb( "Table `npc_vendor` has too many items (%u >= %i) for vendor (Entry: %u), ignore", vList.size(), MAX_VENDOR_ITEMS, entry);
- continue;
- }
-
- VendorItem* pVendorItem = new VendorItem();
- pVendorItem->item = item_id;
- pVendorItem->maxcount = fields[2].GetUInt32();
- pVendorItem->incrtime = fields[3].GetUInt32();
- pVendorItem->ExtendedCost = ExtendedCost;
-
- vList.push_back(pVendorItem);
+ vList.AddItem(item_id,maxcount,incrtime,ExtendedCost);
++count;
} while (result->NextRow());
@@ -6838,6 +6856,109 @@ void ObjectMgr::LoadNpcTextId() sLog.outString( ">> Loaded %d NpcTextId ", count );
}
+void ObjectMgr::AddVendorItem( uint32 entry,uint32 item, uint32 maxcount, uint32 incrtime, uint32 extendedcost )
+{
+ VendorItemData& vList = m_mCacheVendorItemMap[entry];
+ vList.AddItem(item,maxcount,incrtime,extendedcost);
+
+ WorldDatabase.PExecuteLog("INSERT INTO npc_vendor (entry,item,maxcount,incrtime,extendedcost) VALUES('%u','%u','%u','%u','%u')",entry, item, maxcount,incrtime,extendedcost);
+}
+
+bool ObjectMgr::RemoveVendorItem( uint32 entry,uint32 item )
+{
+ CacheVendorItemMap::iterator iter = m_mCacheVendorItemMap.find(entry);
+ if(iter == m_mCacheVendorItemMap.end())
+ return false;
+
+ if(!iter->second.FindItem(item))
+ return false;
+
+ iter->second.RemoveItem(item);
+ WorldDatabase.PExecuteLog("DELETE FROM npc_vendor WHERE entry='%u' AND item='%u'",entry, item);
+ return true;
+}
+
+bool ObjectMgr::IsVendorItemValid( uint32 vendor_entry, uint32 item_id, uint32 maxcount, uint32 incrtime, uint32 ExtendedCost, Player* pl ) const
+{
+ CreatureInfo const* cInfo = GetCreatureTemplate(vendor_entry);
+ if(!cInfo)
+ {
+ if(pl)
+ ChatHandler(pl).SendSysMessage(LANG_COMMAND_VENDORSELECTION);
+ else
+ sLog.outErrorDb("Table `npc_vendor` have data for not existed creature template (Entry: %u), ignore", vendor_entry);
+ return false;
+ }
+
+ if(!(cInfo->npcflag & UNIT_NPC_FLAG_VENDOR))
+ {
+ if(pl)
+ ChatHandler(pl).SendSysMessage(LANG_COMMAND_VENDORSELECTION);
+ else
+ sLog.outErrorDb("Table `npc_vendor` have data for not creature template (Entry: %u) without vendor flag, ignore", vendor_entry);
+ return false;
+ }
+
+ if(!GetItemPrototype(item_id))
+ {
+ if(pl)
+ ChatHandler(pl).PSendSysMessage(LANG_ITEM_NOT_FOUND, item_id);
+ else
+ sLog.outErrorDb("Table `npc_vendor` for Vendor (Entry: %u) have in item list non-existed item (%u), ignore",vendor_entry,item_id);
+ return false;
+ }
+
+ if(ExtendedCost && !sItemExtendedCostStore.LookupEntry(ExtendedCost))
+ {
+ if(pl)
+ ChatHandler(pl).PSendSysMessage(LANG_EXTENDED_COST_NOT_EXIST,ExtendedCost);
+ else
+ sLog.outErrorDb("Table `npc_vendor` have Item (Entry: %u) with wrong ExtendedCost (%u) for vendor (%u), ignore",item_id,ExtendedCost,vendor_entry);
+ return false;
+ }
+
+ if(maxcount > 0 && incrtime == 0)
+ {
+ if(pl)
+ ChatHandler(pl).PSendSysMessage("MaxCount!=0 (%u) but IncrTime==0", maxcount);
+ else
+ sLog.outErrorDb( "Table `npc_vendor` has `maxcount` (%u) for item %u of vendor (Entry: %u) but `incrtime`=0, ignore", maxcount, item_id, vendor_entry);
+ return false;
+ }
+ else if(maxcount==0 && incrtime > 0)
+ {
+ if(pl)
+ ChatHandler(pl).PSendSysMessage("MaxCount==0 but IncrTime<>=0");
+ else
+ sLog.outErrorDb( "Table `npc_vendor` has `maxcount`=0 for item %u of vendor (Entry: %u) but `incrtime`<>0, ignore", item_id, vendor_entry);
+ return false;
+ }
+
+ VendorItemData const* vItems = GetNpcVendorItemList(vendor_entry);
+ if(!vItems)
+ return true; // later checks for non-empty lists
+
+ if(vItems->FindItem(item_id))
+ {
+ if(pl)
+ ChatHandler(pl).PSendSysMessage(LANG_ITEM_ALREADY_IN_LIST,item_id);
+ else
+ sLog.outErrorDb( "Table `npc_vendor` has duplicate items %u for vendor (Entry: %u), ignore", item_id, vendor_entry);
+ return false;
+ }
+
+ if(vItems->GetItemCount() >= MAX_VENDOR_ITEMS)
+ {
+ if(pl)
+ ChatHandler(pl).SendSysMessage(LANG_COMMAND_ADDVENDORITEMITEMS);
+ else
+ sLog.outErrorDb( "Table `npc_vendor` has too many items (%u >= %i) for vendor (Entry: %u), ignore", vItems->GetItemCount(), MAX_VENDOR_ITEMS, vendor_entry);
+ return false;
+ }
+
+ return true;
+}
+
// Functions for scripting access
const char* GetAreaTriggerScriptNameById(uint32 id)
{
@@ -6848,7 +6969,7 @@ bool LoadMangosStrings(DatabaseType& db, char const* table,int32 start_value, in {
if(start_value >= 0 || start_value <= end_value) // start/end reversed for negative values
{
- sLog.outError("Table '%s' attempt loaded with invalid range (%d - %d), use (%d - %d) instead.",table,start_value,end_value,-1,std::numeric_limits<int32>::min());
+ sLog.outErrorDb("Table '%s' attempt loaded with invalid range (%d - %d), use (%d - %d) instead.",table,start_value,end_value,-1,std::numeric_limits<int32>::min());
start_value = -1;
end_value = std::numeric_limits<int32>::min();
}
diff --git a/src/game/ObjectMgr.h b/src/game/ObjectMgr.h index c9baf762ac3..5bb3aafde72 100644 --- a/src/game/ObjectMgr.h +++ b/src/game/ObjectMgr.h @@ -228,18 +228,8 @@ struct PlayerCondition // NPC gossip text id
typedef HM_NAMESPACE::hash_map<uint32, uint32> CacheNpcTextIdMap;
-// Vendors
-struct VendorItem
-{
- uint32 item;
- uint32 maxcount;
- uint32 incrtime;
- uint32 ExtendedCost;
-};
-typedef std::vector<VendorItem*> VendorItemList;
-
-typedef HM_NAMESPACE::hash_map<uint32, VendorItemList> CacheVendorItemMap;
+typedef HM_NAMESPACE::hash_map<uint32, VendorItemData> CacheVendorItemMap;
typedef HM_NAMESPACE::hash_map<uint32, TrainerSpellData> CacheTrainerSpellMap;
enum SkillRangeType
@@ -258,6 +248,16 @@ SkillRangeType GetSkillRangeType(SkillLineEntry const *pSkill, bool racial); bool normalizePlayerName(std::string& name);
+struct MANGOS_DLL_SPEC LanguageDesc
+{
+ Language lang_id;
+ uint32 spell_id;
+ uint32 skill_id;
+};
+
+extern LanguageDesc lang_description[LANGUAGES_COUNT];
+MANGOS_DLL_SPEC LanguageDesc const* GetLanguageDescByID(uint32 lang);
+
class PlayerDumpReader;
class ObjectMgr
@@ -732,7 +732,7 @@ class ObjectMgr return &iter->second;
}
- VendorItemList const* GetNpcVendorItemList(uint32 entry) const
+ VendorItemData const* GetNpcVendorItemList(uint32 entry) const
{
CacheVendorItemMap::const_iterator iter = m_mCacheVendorItemMap.find(entry);
if(iter == m_mCacheVendorItemMap.end())
@@ -740,6 +740,9 @@ class ObjectMgr return &iter->second;
}
+ void AddVendorItem(uint32 entry,uint32 item, uint32 maxcount, uint32 incrtime, uint32 ExtendedCost);
+ bool RemoveVendorItem(uint32 entry,uint32 item);
+ bool IsVendorItemValid( uint32 vendor_entry, uint32 item, uint32 maxcount, uint32 ptime, uint32 ExtendedCost, Player* pl = NULL ) const;
protected:
uint32 m_auctionid;
uint32 m_mailid;
diff --git a/src/game/Pet.cpp b/src/game/Pet.cpp index c80bbc8f673..3d8eaf17182 100644 --- a/src/game/Pet.cpp +++ b/src/game/Pet.cpp @@ -1,1750 +1,1750 @@ -/* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "Common.h" -#include "Database/DatabaseEnv.h" -#include "Log.h" -#include "WorldSession.h" -#include "WorldPacket.h" -#include "ObjectMgr.h" -#include "SpellMgr.h" -#include "Pet.h" -#include "MapManager.h" -#include "Formulas.h" -#include "SpellAuras.h" -#include "CreatureAI.h" -#include "Unit.h" -#include "Util.h" - -char const* petTypeSuffix[MAX_PET_TYPE] = -{ - "'s Minion", // SUMMON_PET - "'s Pet", // HUNTER_PET - "'s Guardian", // GUARDIAN_PET - "'s Companion" // MINI_PET -}; - -//numbers represent minutes * 100 while happy (you get 100 loyalty points per min while happy) -uint32 const LevelUpLoyalty[6] = -{ - 5500, - 11500, - 17000, - 23500, - 31000, - 39500, -}; - -uint32 const LevelStartLoyalty[6] = -{ - 2000, - 4500, - 7000, - 10000, - 13500, - 17500, -}; - -Pet::Pet(PetType type) : Creature() -{ - m_isPet = true; - m_name = "Pet"; - m_petType = type; - - m_removed = false; - m_regenTimer = 4000; - m_happinessTimer = 7500; - m_loyaltyTimer = 12000; - m_duration = 0; - m_bonusdamage = 0; - - m_loyaltyPoints = 0; - m_TrainingPoints = 0; - m_resetTalentsCost = 0; - m_resetTalentsTime = 0; - - m_auraUpdateMask = 0; - - // pets always have a charminfo, even if they are not actually charmed - CharmInfo* charmInfo = InitCharmInfo(this); - - if(type == MINI_PET) // always passive - charmInfo->SetReactState(REACT_PASSIVE); - else if(type == GUARDIAN_PET) // always aggressive - charmInfo->SetReactState(REACT_AGGRESSIVE); - - m_spells.clear(); - m_Auras.clear(); - m_CreatureSpellCooldowns.clear(); - m_CreatureCategoryCooldowns.clear(); - m_autospells.clear(); - m_declinedname = NULL; -} - -Pet::~Pet() -{ - if(m_uint32Values) // only for fully created Object - { - for (PetSpellMap::iterator i = m_spells.begin(); i != m_spells.end(); ++i) - delete i->second; - ObjectAccessor::Instance().RemoveObject(this); - } - - delete m_declinedname; -} - -void Pet::AddToWorld() -{ - ///- Register the pet for guid lookup - if(!IsInWorld()) ObjectAccessor::Instance().AddObject(this); - Unit::AddToWorld(); -} - -void Pet::RemoveFromWorld() -{ - ///- Remove the pet from the accessor - if(IsInWorld()) ObjectAccessor::Instance().RemoveObject(this); - ///- Don't call the function for Creature, normal mobs + totems go in a different storage - Unit::RemoveFromWorld(); -} - -bool Pet::LoadPetFromDB( Unit* owner, uint32 petentry, uint32 petnumber, bool current ) -{ - uint32 ownerid = owner->GetGUIDLow(); - - QueryResult *result; - - if(petnumber) - // known petnumber entry 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 - result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, loyaltypoints, loyalty, trainpoint, slot, name, renamed, curhealth, curmana, curhappiness, abdata, TeachSpelldata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType FROM character_pet WHERE owner = '%u' AND id = '%u'",ownerid, petnumber); - else if(current) - // current pet (slot 0) 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 - result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, loyaltypoints, loyalty, trainpoint, slot, name, renamed, curhealth, curmana, curhappiness, abdata, TeachSpelldata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType FROM character_pet WHERE owner = '%u' AND slot = '0'",ownerid ); - else if(petentry) - // known petentry entry (unique for summoned pet, but non unique for hunter pet (only from current or not stabled pets) - // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 - result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, loyaltypoints, loyalty, trainpoint, slot, name, renamed, curhealth, curmana, curhappiness, abdata, TeachSpelldata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType FROM character_pet WHERE owner = '%u' AND entry = '%u' AND (slot = '0' OR slot = '3') ",ownerid, petentry ); - else - // any current or other non-stabled pet (for hunter "call pet") - // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 - result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, loyaltypoints, loyalty, trainpoint, slot, name, renamed, curhealth, curmana, curhappiness, abdata, TeachSpelldata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType FROM character_pet WHERE owner = '%u' AND (slot = '0' OR slot = '3') ",ownerid); - - if(!result) - return false; - - Field *fields = result->Fetch(); - - // update for case of current pet "slot = 0" - petentry = fields[1].GetUInt32(); - if(!petentry) - { - delete result; - return false; - } - - uint32 summon_spell_id = fields[21].GetUInt32(); - SpellEntry const* spellInfo = sSpellStore.LookupEntry(summon_spell_id); - - bool is_temporary_summoned = spellInfo && GetSpellDuration(spellInfo) > 0; - - // check temporary summoned pets like mage water elemental - if(current && is_temporary_summoned) - { - delete result; - return false; - } - - Map *map = owner->GetMap(); - uint32 guid=objmgr.GenerateLowGuid(HIGHGUID_PET); - uint32 pet_number = fields[0].GetUInt32(); - if(!Create(guid, map, petentry, pet_number)) - { - delete result; - return false; - } - - float px, py, pz; - owner->GetClosePoint(px, py, pz,GetObjectSize(),PET_FOLLOW_DIST,PET_FOLLOW_ANGLE); - - Relocate(px, py, pz, owner->GetOrientation()); - - if(!IsPositionValid()) - { - sLog.outError("ERROR: Pet (guidlow %d, entry %d) not loaded. Suggested coordinates isn't valid (X: %d Y: ^%d)", GetGUIDLow(), GetEntry(), GetPositionX(), GetPositionY()); - delete result; - return false; - } - - setPetType(PetType(fields[22].GetUInt8())); - SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE,owner->getFaction()); - SetUInt32Value(UNIT_CREATED_BY_SPELL, summon_spell_id); - - CreatureInfo const *cinfo = GetCreatureInfo(); - if(cinfo->type == CREATURE_TYPE_CRITTER) - { - AIM_Initialize(); - map->Add((Creature*)this); - delete result; - return true; - } - if(getPetType()==HUNTER_PET || getPetType()==SUMMON_PET && cinfo->type == CREATURE_TYPE_DEMON && owner->getClass() == CLASS_WARLOCK) - m_charmInfo->SetPetNumber(pet_number, true); - else - m_charmInfo->SetPetNumber(pet_number, false); - SetUInt64Value(UNIT_FIELD_SUMMONEDBY, owner->GetGUID()); - SetDisplayId(fields[3].GetUInt32()); - SetNativeDisplayId(fields[3].GetUInt32()); - uint32 petlevel=fields[4].GetUInt32(); - SetUInt32Value(UNIT_NPC_FLAGS , 0); - SetName(fields[11].GetString()); - - switch(getPetType()) - { - - case SUMMON_PET: - petlevel=owner->getLevel(); - - SetUInt32Value(UNIT_FIELD_BYTES_0,2048); - SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE); - // this enables popup window (pet dismiss, cancel) - break; - case HUNTER_PET: - SetUInt32Value(UNIT_FIELD_BYTES_0, 0x02020100); - SetByteValue(UNIT_FIELD_BYTES_1, 1, fields[8].GetUInt32()); - SetByteValue(UNIT_FIELD_BYTES_2, 0, SHEATH_STATE_MELEE ); - SetByteValue(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_UNK3 | UNIT_BYTE2_FLAG_AURAS | UNIT_BYTE2_FLAG_UNK5 ); - - if(fields[12].GetBool()) - SetByteValue(UNIT_FIELD_BYTES_2, 2, UNIT_RENAME_NOT_ALLOWED); - else - SetByteValue(UNIT_FIELD_BYTES_2, 2, UNIT_RENAME_ALLOWED); - - SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE); - // this enables popup window (pet abandon, cancel) - SetTP(fields[9].GetInt32()); - SetMaxPower(POWER_HAPPINESS,GetCreatePowers(POWER_HAPPINESS)); - SetPower( POWER_HAPPINESS,fields[15].GetUInt32()); - setPowerType(POWER_FOCUS); - break; - default: - sLog.outError("Pet have incorrect type (%u) for pet loading.",getPetType()); - } - InitStatsForLevel( petlevel); - SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, time(NULL)); - SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, fields[5].GetUInt32()); - SetUInt64Value(UNIT_FIELD_CREATEDBY, owner->GetGUID()); - - m_charmInfo->SetReactState( ReactStates( fields[6].GetUInt8() )); - m_loyaltyPoints = fields[7].GetInt32(); - - uint32 savedhealth = fields[13].GetUInt32(); - uint32 savedmana = fields[14].GetUInt32(); - - // set current pet as current - if(fields[10].GetUInt32() != 0) - { - CharacterDatabase.BeginTransaction(); - CharacterDatabase.PExecute("UPDATE character_pet SET slot = '3' WHERE owner = '%u' AND slot = '0' AND id <> '%u'",ownerid, m_charmInfo->GetPetNumber()); - CharacterDatabase.PExecute("UPDATE character_pet SET slot = '0' WHERE owner = '%u' AND id = '%u'",ownerid, m_charmInfo->GetPetNumber()); - CharacterDatabase.CommitTransaction(); - } - - if(!is_temporary_summoned) - { - // permanent controlled pets store state in DB - Tokens tokens = StrSplit(fields[16].GetString(), " "); - - if(tokens.size() != 20) - { - delete result; - return false; - } - - int index; - Tokens::iterator iter; - for(iter = tokens.begin(), index = 0; index < 10; ++iter, ++index ) - { - m_charmInfo->GetActionBarEntry(index)->Type = atol((*iter).c_str()); - ++iter; - m_charmInfo->GetActionBarEntry(index)->SpellOrAction = atol((*iter).c_str()); - } - - //init teach spells - tokens = StrSplit(fields[17].GetString(), " "); - for (iter = tokens.begin(), index = 0; index < 4; ++iter, ++index) - { - uint32 tmp = atol((*iter).c_str()); - - ++iter; - - if(tmp) - AddTeachSpell(tmp, atol((*iter).c_str())); - else - break; - } - } - - // since last save (in seconds) - uint32 timediff = (time(NULL) - fields[18].GetUInt32()); - - delete result; - - //load spells/cooldowns/auras - SetCanModifyStats(true); - _LoadAuras(timediff); - - //init AB - if(is_temporary_summoned) - { - // Temporary summoned pets always have initial spell list at load - InitPetCreateSpells(); - } - else - { - LearnPetPassives(); - CastPetAuras(current); - } - - if(getPetType() == SUMMON_PET && !current) //all (?) summon pets come with full health when called, but not when they are current - { - SetHealth(GetMaxHealth()); - SetPower(POWER_MANA, GetMaxPower(POWER_MANA)); - } - else - { - SetHealth(savedhealth > GetMaxHealth() ? GetMaxHealth() : savedhealth); - SetPower(POWER_MANA, savedmana > GetMaxPower(POWER_MANA) ? GetMaxPower(POWER_MANA) : savedmana); - } - - AIM_Initialize(); - map->Add((Creature*)this); - - // Spells should be loaded after pet is added to map, because in CanCast is check on it - _LoadSpells(); - _LoadSpellCooldowns(); - - owner->SetPet(this); // in DB stored only full controlled creature - sLog.outDebug("New Pet has guid %u", GetGUIDLow()); - - if(owner->GetTypeId() == TYPEID_PLAYER) - { - ((Player*)owner)->PetSpellInitialize(); - if(((Player*)owner)->GetGroup()) - ((Player*)owner)->SetGroupUpdateFlag(GROUP_UPDATE_PET); - } - - if(owner->GetTypeId() == TYPEID_PLAYER && getPetType() == HUNTER_PET) - { - result = CharacterDatabase.PQuery("SELECT genitive, dative, accusative, instrumental, prepositional FROM character_pet_declinedname WHERE owner = '%u' AND id = '%u'",owner->GetGUIDLow(),GetCharmInfo()->GetPetNumber()); - - if(result) - { - if(m_declinedname) - delete m_declinedname; - - m_declinedname = new DeclinedName; - Field *fields = result->Fetch(); - for(int i = 0; i < MAX_DECLINED_NAME_CASES; ++i) - { - m_declinedname->name[i] = fields[i].GetCppString(); - } - } - } - - return true; -} - -void Pet::SavePetToDB(PetSaveMode mode) -{ - if(!GetEntry()) - return; - - // save only fully controlled creature - if(!isControlled()) - return; - - uint32 curhealth = GetHealth(); - uint32 curmana = GetPower(POWER_MANA); - - switch(mode) - { - case PET_SAVE_IN_STABLE_SLOT_1: - case PET_SAVE_IN_STABLE_SLOT_2: - case PET_SAVE_NOT_IN_SLOT: - { - RemoveAllAuras(); - - //only alive hunter pets get auras saved, the others don't - if(!(getPetType() == HUNTER_PET && isAlive())) - m_Auras.clear(); - } - default: - break; - } - - _SaveSpells(); - _SaveSpellCooldowns(); - _SaveAuras(); - - switch(mode) - { - case PET_SAVE_AS_CURRENT: - case PET_SAVE_IN_STABLE_SLOT_1: - case PET_SAVE_IN_STABLE_SLOT_2: - case PET_SAVE_NOT_IN_SLOT: - { - uint32 loyalty =1; - if(getPetType()!=HUNTER_PET) - loyalty = GetLoyaltyLevel(); - - uint32 owner = GUID_LOPART(GetOwnerGUID()); - std::string name = m_name; - CharacterDatabase.escape_string(name); - CharacterDatabase.BeginTransaction(); - // remove current data - CharacterDatabase.PExecute("DELETE FROM character_pet WHERE owner = '%u' AND id = '%u'", owner,m_charmInfo->GetPetNumber() ); - - // prevent duplicate using slot (except PET_SAVE_NOT_IN_SLOT) - if(mode!=PET_SAVE_NOT_IN_SLOT) - CharacterDatabase.PExecute("UPDATE character_pet SET slot = 3 WHERE owner = '%u' AND slot = '%u'", owner, uint32(mode) ); - - // prevent existence another hunter pet in PET_SAVE_AS_CURRENT and PET_SAVE_NOT_IN_SLOT - if(getPetType()==HUNTER_PET && (mode==PET_SAVE_AS_CURRENT||mode==PET_SAVE_NOT_IN_SLOT)) - CharacterDatabase.PExecute("DELETE FROM character_pet WHERE owner = '%u' AND (slot = '0' OR slot = '3')", owner ); - // save pet - std::ostringstream ss; - ss << "INSERT INTO character_pet ( id, entry, owner, modelid, level, exp, Reactstate, loyaltypoints, loyalty, trainpoint, slot, name, renamed, curhealth, curmana, curhappiness, abdata,TeachSpelldata,savetime,resettalents_cost,resettalents_time,CreatedBySpell,PetType) " - << "VALUES (" - << m_charmInfo->GetPetNumber() << ", " - << GetEntry() << ", " - << owner << ", " - << GetNativeDisplayId() << ", " - << getLevel() << ", " - << GetUInt32Value(UNIT_FIELD_PETEXPERIENCE) << ", " - << uint32(m_charmInfo->GetReactState()) << ", " - << m_loyaltyPoints << ", " - << GetLoyaltyLevel() << ", " - << m_TrainingPoints << ", " - << uint32(mode) << ", '" - << name.c_str() << "', " - << uint32((GetByteValue(UNIT_FIELD_BYTES_2, 2) == UNIT_RENAME_ALLOWED)?0:1) << ", " - << (curhealth<1?1:curhealth) << ", " - << curmana << ", " - << GetPower(POWER_HAPPINESS) << ", '"; - - for(uint32 i = 0; i < 10; i++) - ss << uint32(m_charmInfo->GetActionBarEntry(i)->Type) << " " << uint32(m_charmInfo->GetActionBarEntry(i)->SpellOrAction) << " "; - ss << "', '"; - - //save spells the pet can teach to it's Master - { - int i = 0; - for(TeachSpellMap::iterator itr = m_teachspells.begin(); i < 4 && itr != m_teachspells.end(); ++i, ++itr) - ss << itr->first << " " << itr->second << " "; - for(; i < 4; ++i) - ss << uint32(0) << " " << uint32(0) << " "; - } - - ss << "', " - << time(NULL) << ", " - << uint32(m_resetTalentsCost) << ", " - << uint64(m_resetTalentsTime) << ", " - << GetUInt32Value(UNIT_CREATED_BY_SPELL) << ", " - << uint32(getPetType()) << ")"; - - CharacterDatabase.Execute( ss.str().c_str() ); - - CharacterDatabase.CommitTransaction(); - break; - } - case PET_SAVE_AS_DELETED: - { - RemoveAllAuras(); - uint32 owner = GUID_LOPART(GetOwnerGUID()); - DeleteFromDB(m_charmInfo->GetPetNumber()); - break; - } - default: - sLog.outError("Unknown pet save/remove mode: %d",mode); - } -} - -void Pet::DeleteFromDB(uint32 guidlow) -{ - CharacterDatabase.PExecute("DELETE FROM character_pet WHERE id = '%u'", guidlow); - CharacterDatabase.PExecute("DELETE FROM character_pet_declinedname WHERE id = '%u'", guidlow); - CharacterDatabase.PExecute("DELETE FROM pet_aura WHERE guid = '%u'", guidlow); - CharacterDatabase.PExecute("DELETE FROM pet_spell WHERE guid = '%u'", guidlow); - CharacterDatabase.PExecute("DELETE FROM pet_spell_cooldown WHERE guid = '%u'", guidlow); -} - -void Pet::setDeathState(DeathState s) // overwrite virtual Creature::setDeathState and Unit::setDeathState -{ - Creature::setDeathState(s); - if(getDeathState()==CORPSE) - { - //remove summoned pet (no corpse) - if(getPetType()==SUMMON_PET) - Remove(PET_SAVE_NOT_IN_SLOT); - // other will despawn at corpse desppawning (Pet::Update code) - else - { - // pet corpse non lootable and non skinnable - SetUInt32Value( UNIT_DYNAMIC_FLAGS, 0x00 ); - RemoveFlag (UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE); - - //lose happiness when died and not in BG/Arena - MapEntry const* mapEntry = sMapStore.LookupEntry(GetMapId()); - if(!mapEntry || (mapEntry->map_type != MAP_ARENA && mapEntry->map_type != MAP_BATTLEGROUND)) - ModifyPower(POWER_HAPPINESS, -HAPPINESS_LEVEL_SIZE); - - SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_ROTATE); - } - } - else if(getDeathState()==ALIVE) - { - RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_ROTATE); - CastPetAuras(true); - } -} - -void Pet::Update(uint32 diff) -{ - if(m_removed) // pet already removed, just wait in remove queue, no updates - return; - - switch( m_deathState ) - { - case CORPSE: - { - if( m_deathTimer <= diff ) - { - assert(getPetType()!=SUMMON_PET && "Must be already removed."); - Remove(PET_SAVE_NOT_IN_SLOT); //hunters' pets never get removed because of death, NEVER! - return; - } - break; - } - case ALIVE: - { - // unsummon pet that lost owner - Unit* owner = GetOwner(); - if(!owner || !IsWithinDistInMap(owner, OWNER_MAX_DISTANCE) || isControlled() && !owner->GetPetGUID()) - { - Remove(PET_SAVE_NOT_IN_SLOT, true); - return; - } - - if(isControlled()) - { - if( owner->GetPetGUID() != GetGUID() ) - { - Remove(getPetType()==HUNTER_PET?PET_SAVE_AS_DELETED:PET_SAVE_NOT_IN_SLOT); - return; - } - } - - if(m_duration > 0) - { - if(m_duration > diff) - m_duration -= diff; - else - { - Remove(getPetType() != SUMMON_PET ? PET_SAVE_AS_DELETED:PET_SAVE_NOT_IN_SLOT); - return; - } - } - - if(getPetType() != HUNTER_PET) - break; - - //regenerate Focus - if(m_regenTimer <= diff) - { - RegenerateFocus(); - m_regenTimer = 4000; - } - else - m_regenTimer -= diff; - - if(m_happinessTimer <= diff) - { - LooseHappiness(); - m_happinessTimer = 7500; - } - else - m_happinessTimer -= diff; - - if(m_loyaltyTimer <= diff) - { - TickLoyaltyChange(); - m_loyaltyTimer = 12000; - } - else - m_loyaltyTimer -= diff; - - break; - } - default: - break; - } - Creature::Update(diff); -} - -void Pet::RegenerateFocus() -{ - uint32 curValue = GetPower(POWER_FOCUS); - uint32 maxValue = GetMaxPower(POWER_FOCUS); - - if (curValue >= maxValue) - return; - - float addvalue = 24 * sWorld.getRate(RATE_POWER_FOCUS); - - AuraList const& ModPowerRegenPCTAuras = GetAurasByType(SPELL_AURA_MOD_POWER_REGEN_PERCENT); - for(AuraList::const_iterator i = ModPowerRegenPCTAuras.begin(); i != ModPowerRegenPCTAuras.end(); ++i) - if ((*i)->GetModifier()->m_miscvalue == POWER_FOCUS) - addvalue *= ((*i)->GetModifier()->m_amount + 100) / 100.0f; - - ModifyPower(POWER_FOCUS, (int32)addvalue); -} - -void Pet::LooseHappiness() -{ - uint32 curValue = GetPower(POWER_HAPPINESS); - if (curValue <= 0) - return; - int32 addvalue = (140 >> GetLoyaltyLevel()) * 125; //value is 70/35/17/8/4 (per min) * 1000 / 8 (timer 7.5 secs) - if(isInCombat()) //we know in combat happiness fades faster, multiplier guess - addvalue = int32(addvalue * 1.5); - ModifyPower(POWER_HAPPINESS, -addvalue); -} - -void Pet::ModifyLoyalty(int32 addvalue) -{ - uint32 loyaltylevel = GetLoyaltyLevel(); - - if(addvalue > 0) //only gain influenced, not loss - addvalue = int32((float)addvalue * sWorld.getRate(RATE_LOYALTY)); - - if(loyaltylevel >= BEST_FRIEND && (addvalue + m_loyaltyPoints) > int32(GetMaxLoyaltyPoints(loyaltylevel))) - return; - - m_loyaltyPoints += addvalue; - - if(m_loyaltyPoints < 0) - { - if(loyaltylevel > REBELLIOUS) - { - //level down - --loyaltylevel; - SetLoyaltyLevel(LoyaltyLevel(loyaltylevel)); - m_loyaltyPoints = GetStartLoyaltyPoints(loyaltylevel); - SetTP(m_TrainingPoints - int32(getLevel())); - } - else - { - m_loyaltyPoints = 0; - Unit* owner = GetOwner(); - if(owner && owner->GetTypeId() == TYPEID_PLAYER) - { - WorldPacket data(SMSG_PET_BROKEN, 0); - ((Player*)owner)->GetSession()->SendPacket(&data); - - //run away - ((Player*)owner)->RemovePet(this,PET_SAVE_AS_DELETED); - } - } - } - //level up - else if(m_loyaltyPoints > int32(GetMaxLoyaltyPoints(loyaltylevel))) - { - ++loyaltylevel; - SetLoyaltyLevel(LoyaltyLevel(loyaltylevel)); - m_loyaltyPoints = GetStartLoyaltyPoints(loyaltylevel); - SetTP(m_TrainingPoints + getLevel()); - } -} - -void Pet::TickLoyaltyChange() -{ - int32 addvalue; - - switch(GetHappinessState()) - { - case HAPPY: addvalue = 20; break; - case CONTENT: addvalue = 10; break; - case UNHAPPY: addvalue = -20; break; - default: - return; - } - ModifyLoyalty(addvalue); -} - -void Pet::KillLoyaltyBonus(uint32 level) -{ - if(level > 100) - return; - - //at lower levels gain is faster | the lower loyalty the more loyalty is gained - uint32 bonus = uint32(((100 - level) / 10) + (6 - GetLoyaltyLevel())); - ModifyLoyalty(bonus); -} - -HappinessState Pet::GetHappinessState() -{ - if(GetPower(POWER_HAPPINESS) < HAPPINESS_LEVEL_SIZE) - return UNHAPPY; - else if(GetPower(POWER_HAPPINESS) >= HAPPINESS_LEVEL_SIZE * 2) - return HAPPY; - else - return CONTENT; -} - -void Pet::SetLoyaltyLevel(LoyaltyLevel level) -{ - SetByteValue(UNIT_FIELD_BYTES_1, 1, level); -} - -bool Pet::CanTakeMoreActiveSpells(uint32 spellid) -{ - uint8 activecount = 1; - uint32 chainstartstore[ACTIVE_SPELLS_MAX]; - - if(IsPassiveSpell(spellid)) - return true; - - chainstartstore[0] = spellmgr.GetFirstSpellInChain(spellid); - - for (PetSpellMap::iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr) - { - if(IsPassiveSpell(itr->first)) - continue; - - uint32 chainstart = spellmgr.GetFirstSpellInChain(itr->first); - - uint8 x; - - for(x = 0; x < activecount; x++) - { - if(chainstart == chainstartstore[x]) - break; - } - - if(x == activecount) //spellchain not yet saved -> add active count - { - ++activecount; - if(activecount > ACTIVE_SPELLS_MAX) - return false; - chainstartstore[x] = chainstart; - } - } - return true; -} - -bool Pet::HasTPForSpell(uint32 spellid) -{ - int32 neededtrainp = GetTPForSpell(spellid); - if((m_TrainingPoints - neededtrainp < 0 || neededtrainp < 0) && neededtrainp != 0) - return false; - return true; -} - -int32 Pet::GetTPForSpell(uint32 spellid) -{ - uint32 basetrainp = 0; - - SkillLineAbilityMap::const_iterator lower = spellmgr.GetBeginSkillLineAbilityMap(spellid); - SkillLineAbilityMap::const_iterator upper = spellmgr.GetEndSkillLineAbilityMap(spellid); - for(SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_spell_idx) - { - if(!_spell_idx->second->reqtrainpoints) - return 0; - - basetrainp = _spell_idx->second->reqtrainpoints; - break; - } - - uint32 spenttrainp = 0; - uint32 chainstart = spellmgr.GetFirstSpellInChain(spellid); - - for (PetSpellMap::iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr) - { - if(itr->second->state == PETSPELL_REMOVED) - continue; - - if(spellmgr.GetFirstSpellInChain(itr->first) == chainstart) - { - SkillLineAbilityMap::const_iterator _lower = spellmgr.GetBeginSkillLineAbilityMap(itr->first); - SkillLineAbilityMap::const_iterator _upper = spellmgr.GetEndSkillLineAbilityMap(itr->first); - - for(SkillLineAbilityMap::const_iterator _spell_idx2 = _lower; _spell_idx2 != _upper; ++_spell_idx2) - { - if(_spell_idx2->second->reqtrainpoints > spenttrainp) - { - spenttrainp = _spell_idx2->second->reqtrainpoints; - break; - } - } - } - } - - return int32(basetrainp) - int32(spenttrainp); -} - -uint32 Pet::GetMaxLoyaltyPoints(uint32 level) -{ - return LevelUpLoyalty[level - 1]; -} - -uint32 Pet::GetStartLoyaltyPoints(uint32 level) -{ - return LevelStartLoyalty[level - 1]; -} - -void Pet::SetTP(int32 TP) -{ - m_TrainingPoints = TP; - SetUInt32Value(UNIT_TRAINING_POINTS, (uint32)GetDispTP()); -} - -int32 Pet::GetDispTP() -{ - if(getPetType()!= HUNTER_PET) - return(0); - if(m_TrainingPoints < 0) - return -m_TrainingPoints; - else - return -(m_TrainingPoints + 1); -} - -void Pet::Remove(PetSaveMode mode, bool returnreagent) -{ - Unit* owner = GetOwner(); - - if(owner) - { - if(owner->GetTypeId()==TYPEID_PLAYER) - { - ((Player*)owner)->RemovePet(this,mode,returnreagent); - return; - } - - // only if current pet in slot - if(owner->GetPetGUID()==GetGUID()) - owner->SetPet(0); - } - - CleanupsBeforeDelete(); - AddObjectToRemoveList(); - m_removed = true; -} - -void Pet::GivePetXP(uint32 xp) -{ - if(getPetType() != HUNTER_PET) - return; - - if ( xp < 1 ) - return; - - if(!isAlive()) - return; - - uint32 level = getLevel(); - - // XP to money conversion processed in Player::RewardQuest - if(level >= sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)) - return; - - uint32 curXP = GetUInt32Value(UNIT_FIELD_PETEXPERIENCE); - uint32 nextLvlXP = GetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP); - uint32 newXP = curXP + xp; - - if(newXP >= nextLvlXP && level+1 > GetOwner()->getLevel()) - { - SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, nextLvlXP-1); - return; - } - - while( newXP >= nextLvlXP && level < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL) ) - { - newXP -= nextLvlXP; - - SetLevel( level + 1 ); - SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, uint32((MaNGOS::XP::xp_to_level(level+1))/4)); - - level = getLevel(); - nextLvlXP = GetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP); - GivePetLevel(level); - } - - SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, newXP); - - if(getPetType() == HUNTER_PET) - KillLoyaltyBonus(level); -} - -void Pet::GivePetLevel(uint32 level) -{ - if(!level) - return; - - InitStatsForLevel( level); - - SetTP(m_TrainingPoints + (GetLoyaltyLevel() - 1)); -} - -bool Pet::CreateBaseAtCreature(Creature* creature) -{ - if(!creature) - { - sLog.outError("CRITICAL ERROR: NULL pointer parsed into CreateBaseAtCreature()"); - return false; - } - uint32 guid=objmgr.GenerateLowGuid(HIGHGUID_PET); - - sLog.outBasic("SetInstanceID()"); - SetInstanceId(creature->GetInstanceId()); - - sLog.outBasic("Create pet"); - uint32 pet_number = objmgr.GeneratePetNumber(); - if(!Create(guid, creature->GetMap(), creature->GetEntry(), pet_number)) - return false; - - Relocate(creature->GetPositionX(), creature->GetPositionY(), creature->GetPositionZ(), creature->GetOrientation()); - - if(!IsPositionValid()) - { - sLog.outError("ERROR: Pet (guidlow %d, entry %d) not created base at creature. Suggested coordinates isn't valid (X: %d Y: ^%d)", GetGUIDLow(), GetEntry(), GetPositionX(), GetPositionY()); - return false; - } - - CreatureInfo const *cinfo = GetCreatureInfo(); - if(!cinfo) - { - sLog.outError("ERROR: CreateBaseAtCreature() failed, creatureInfo is missing!"); - return false; - } - - if(cinfo->type == CREATURE_TYPE_CRITTER) - { - setPetType(MINI_PET); - return true; - } - SetDisplayId(creature->GetDisplayId()); - SetNativeDisplayId(creature->GetNativeDisplayId()); - SetMaxPower(POWER_HAPPINESS,GetCreatePowers(POWER_HAPPINESS)); - SetPower( POWER_HAPPINESS,166500); - setPowerType(POWER_FOCUS); - SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP,0); - SetUInt32Value(UNIT_FIELD_PETEXPERIENCE,0); - SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, uint32((MaNGOS::XP::xp_to_level(creature->getLevel()))/4)); - SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE); - SetUInt32Value(UNIT_NPC_FLAGS , 0); - - CreatureFamilyEntry const* cFamily = sCreatureFamilyStore.LookupEntry(creature->GetCreatureInfo()->family); - if( char* familyname = cFamily->Name[sWorld.GetDefaultDbcLocale()] ) - SetName(familyname); - else - SetName(creature->GetName()); - - m_loyaltyPoints = 1000; - if(cinfo->type == CREATURE_TYPE_BEAST) - { - SetUInt32Value(UNIT_FIELD_BYTES_0, 0x02020100); - SetByteValue(UNIT_FIELD_BYTES_2, 0, SHEATH_STATE_MELEE ); - SetByteValue(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_UNK3 | UNIT_BYTE2_FLAG_AURAS | UNIT_BYTE2_FLAG_UNK5 ); - SetByteValue(UNIT_FIELD_BYTES_2, 2, UNIT_RENAME_ALLOWED); - - SetUInt32Value(UNIT_MOD_CAST_SPEED, creature->GetUInt32Value(UNIT_MOD_CAST_SPEED) ); - SetLoyaltyLevel(REBELLIOUS); - } - return true; -} - -bool Pet::InitStatsForLevel(uint32 petlevel) -{ - CreatureInfo const *cinfo = GetCreatureInfo(); - assert(cinfo); - - Unit* owner = GetOwner(); - if(!owner) - { - sLog.outError("ERROR: attempt to summon pet (Entry %u) without owner! Attempt terminated.", cinfo->Entry); - return false; - } - - uint32 creature_ID = (getPetType() == HUNTER_PET) ? 1 : cinfo->Entry; - - SetLevel( petlevel); - - SetMeleeDamageSchool(SpellSchools(cinfo->dmgschool)); - - SetModifierValue(UNIT_MOD_ARMOR, BASE_VALUE, float(petlevel*50)); - - SetAttackTime(BASE_ATTACK, BASE_ATTACK_TIME); - SetAttackTime(OFF_ATTACK, BASE_ATTACK_TIME); - SetAttackTime(RANGED_ATTACK, BASE_ATTACK_TIME); - - SetFloatValue(UNIT_MOD_CAST_SPEED, 1.0); - - CreatureFamilyEntry const* cFamily = sCreatureFamilyStore.LookupEntry(cinfo->family); - if(cFamily && cFamily->minScale > 0.0f) - { - float scale; - if (getLevel() >= cFamily->maxScaleLevel) - scale = cFamily->maxScale; - else if (getLevel() <= cFamily->minScaleLevel) - scale = cFamily->minScale; - else - scale = cFamily->minScale + (getLevel() - cFamily->minScaleLevel) / cFamily->maxScaleLevel * (cFamily->maxScale - cFamily->minScale); - - SetFloatValue(OBJECT_FIELD_SCALE_X, scale); - } - m_bonusdamage = 0; - - int32 createResistance[MAX_SPELL_SCHOOL] = {0,0,0,0,0,0,0}; - - if(cinfo && getPetType() != HUNTER_PET) - { - createResistance[SPELL_SCHOOL_HOLY] = cinfo->resistance1; - createResistance[SPELL_SCHOOL_FIRE] = cinfo->resistance2; - createResistance[SPELL_SCHOOL_NATURE] = cinfo->resistance3; - createResistance[SPELL_SCHOOL_FROST] = cinfo->resistance4; - createResistance[SPELL_SCHOOL_SHADOW] = cinfo->resistance5; - createResistance[SPELL_SCHOOL_ARCANE] = cinfo->resistance6; - } - - switch(getPetType()) - { - case SUMMON_PET: - { - if(owner->GetTypeId() == TYPEID_PLAYER) - { - switch(owner->getClass()) - { - case CLASS_WARLOCK: - { - - //the damage bonus used for pets is either fire or shadow damage, whatever is higher - uint32 fire = owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + SPELL_SCHOOL_FIRE); - uint32 shadow = owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + SPELL_SCHOOL_SHADOW); - uint32 val = (fire > shadow) ? fire : shadow; - - SetBonusDamage(int32 (val * 0.15f)); - //bonusAP += val * 0.57; - break; - } - case CLASS_MAGE: - { - //40% damage bonus of mage's frost damage - float val = owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + SPELL_SCHOOL_FROST) * 0.4; - if(val < 0) - val = 0; - SetBonusDamage( int32(val)); - break; - } - default: - break; - } - } - - SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, float(petlevel - (petlevel / 4)) ); - SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, float(petlevel + (petlevel / 4)) ); - - //SetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE, float(cinfo->attackpower)); - - PetLevelInfo const* pInfo = objmgr.GetPetLevelInfo(creature_ID, petlevel); - if(pInfo) // exist in DB - { - SetCreateHealth(pInfo->health); - SetCreateMana(pInfo->mana); - - if(pInfo->armor > 0) - SetModifierValue(UNIT_MOD_ARMOR, BASE_VALUE, float(pInfo->armor)); - - for(int stat = 0; stat < MAX_STATS; ++stat) - { - SetCreateStat(Stats(stat),float(pInfo->stats[stat])); - } - } - else // not exist in DB, use some default fake data - { - sLog.outErrorDb("Summoned pet (Entry: %u) not have pet stats data in DB",cinfo->Entry); - - // remove elite bonuses included in DB values - SetCreateHealth(uint32(((float(cinfo->maxhealth) / cinfo->maxlevel) / (1 + 2 * cinfo->rank)) * petlevel) ); - SetCreateMana( uint32(((float(cinfo->maxmana) / cinfo->maxlevel) / (1 + 2 * cinfo->rank)) * petlevel) ); - - SetCreateStat(STAT_STRENGTH,22); - SetCreateStat(STAT_AGILITY,22); - SetCreateStat(STAT_STAMINA,25); - SetCreateStat(STAT_INTELLECT,28); - SetCreateStat(STAT_SPIRIT,27); - } - break; - } - case HUNTER_PET: - { - SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, uint32((MaNGOS::XP::xp_to_level(petlevel))/4)); - - //these formula may not be correct; however, it is designed to be close to what it should be - //this makes dps 0.5 of pets level - SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, float(petlevel - (petlevel / 4)) ); - //damage range is then petlevel / 2 - SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, float(petlevel + (petlevel / 4)) ); - //damage is increased afterwards as strength and pet scaling modify attack power - - //stored standard pet stats are entry 1 in pet_levelinfo - PetLevelInfo const* pInfo = objmgr.GetPetLevelInfo(creature_ID, petlevel); - if(pInfo) // exist in DB - { - SetCreateHealth(pInfo->health); - SetModifierValue(UNIT_MOD_ARMOR, BASE_VALUE, float(pInfo->armor)); - //SetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE, float(cinfo->attackpower)); - - for( int i = STAT_STRENGTH; i < MAX_STATS; i++) - { - SetCreateStat(Stats(i), float(pInfo->stats[i])); - } - } - else // not exist in DB, use some default fake data - { - sLog.outErrorDb("Hunter pet levelstats missing in DB"); - - // remove elite bonuses included in DB values - SetCreateHealth( uint32(((float(cinfo->maxhealth) / cinfo->maxlevel) / (1 + 2 * cinfo->rank)) * petlevel) ); - - SetCreateStat(STAT_STRENGTH,22); - SetCreateStat(STAT_AGILITY,22); - SetCreateStat(STAT_STAMINA,25); - SetCreateStat(STAT_INTELLECT,28); - SetCreateStat(STAT_SPIRIT,27); - } - break; - } - case GUARDIAN_PET: - SetUInt32Value(UNIT_FIELD_PETEXPERIENCE,0); - SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP,1000); - - SetCreateMana( 28 + 10*petlevel ); - SetCreateHealth( 28 + 30*petlevel ); - - // FIXME: this is wrong formula, possible each guardian pet have own damage formula - //these formula may not be correct; however, it is designed to be close to what it should be - //this makes dps 0.5 of pets level - SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, float(petlevel - (petlevel / 4)) ); - //damage range is then petlevel / 2 - SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, float(petlevel + (petlevel / 4)) ); - break; - default: - sLog.outError("Pet have incorrect type (%u) for levelup.",getPetType()); break; - } - - for (int i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i) - SetModifierValue(UnitMods(UNIT_MOD_RESISTANCE_START + i), BASE_VALUE, float(createResistance[i]) ); - - UpdateAllStats(); - - SetHealth(GetMaxHealth()); - SetPower(POWER_MANA, GetMaxPower(POWER_MANA)); - - return true; -} - -bool Pet::HaveInDiet(ItemPrototype const* item) const -{ - if (!item->FoodType) - return false; - - CreatureInfo const* cInfo = GetCreatureInfo(); - if(!cInfo) - return false; - - CreatureFamilyEntry const* cFamily = sCreatureFamilyStore.LookupEntry(cInfo->family); - if(!cFamily) - return false; - - uint32 diet = cFamily->petFoodMask; - uint32 FoodMask = 1 << (item->FoodType-1); - return diet & FoodMask; -} - -uint32 Pet::GetCurrentFoodBenefitLevel(uint32 itemlevel) -{ - // -5 or greater food level - if(getLevel() <= itemlevel +5) //possible to feed level 60 pet with level 55 level food for full effect - return 35000; - // -10..-6 - else if(getLevel() <= itemlevel + 10) //pure guess, but sounds good - return 17000; - // -14..-11 - else if(getLevel() <= itemlevel + 14) //level 55 food gets green on 70, makes sense to me - return 8000; - // -15 or less - else - return 0; //food too low level -} - -void Pet::_LoadSpellCooldowns() -{ - m_CreatureSpellCooldowns.clear(); - m_CreatureCategoryCooldowns.clear(); - - QueryResult *result = CharacterDatabase.PQuery("SELECT spell,time FROM pet_spell_cooldown WHERE guid = '%u'",m_charmInfo->GetPetNumber()); - - if(result) - { - time_t curTime = time(NULL); - - WorldPacket data(SMSG_SPELL_COOLDOWN, (8+1+result->GetRowCount()*8)); - data << GetGUID(); - data << uint8(0x0); - - do - { - Field *fields = result->Fetch(); - - uint32 spell_id = fields[0].GetUInt32(); - time_t db_time = (time_t)fields[1].GetUInt64(); - - if(!sSpellStore.LookupEntry(spell_id)) - { - sLog.outError("Pet %u have unknown spell %u in `pet_spell_cooldown`, skipping.",m_charmInfo->GetPetNumber(),spell_id); - continue; - } - - // skip outdated cooldown - if(db_time <= curTime) - continue; - - data << uint32(spell_id); - data << uint32(uint32(db_time-curTime)*1000); // in m.secs - - _AddCreatureSpellCooldown(spell_id,db_time); - - sLog.outDebug("Pet (Number: %u) spell %u cooldown loaded (%u secs).",m_charmInfo->GetPetNumber(),spell_id,uint32(db_time-curTime)); - } - while( result->NextRow() ); - - delete result; - - if(!m_CreatureSpellCooldowns.empty() && GetOwner()) - { - ((Player*)GetOwner())->GetSession()->SendPacket(&data); - } - } -} - -void Pet::_SaveSpellCooldowns() -{ - CharacterDatabase.PExecute("DELETE FROM pet_spell_cooldown WHERE guid = '%u'", m_charmInfo->GetPetNumber()); - - time_t curTime = time(NULL); - - // remove oudated and save active - for(CreatureSpellCooldowns::iterator itr = m_CreatureSpellCooldowns.begin();itr != m_CreatureSpellCooldowns.end();) - { - if(itr->second <= curTime) - m_CreatureSpellCooldowns.erase(itr++); - else - { - CharacterDatabase.PExecute("INSERT INTO pet_spell_cooldown (guid,spell,time) VALUES ('%u', '%u', '" I64FMTD "')", m_charmInfo->GetPetNumber(), itr->first, uint64(itr->second)); - ++itr; - } - } -} - -void Pet::_LoadSpells() -{ - QueryResult *result = CharacterDatabase.PQuery("SELECT spell,slot,active FROM pet_spell WHERE guid = '%u'",m_charmInfo->GetPetNumber()); - - if(result) - { - do - { - Field *fields = result->Fetch(); - - addSpell(fields[0].GetUInt16(), fields[2].GetUInt16(), PETSPELL_UNCHANGED, fields[1].GetUInt16()); - } - while( result->NextRow() ); - - delete result; - } -} - -void Pet::_SaveSpells() -{ - for (PetSpellMap::const_iterator itr = m_spells.begin(), next = m_spells.begin(); itr != m_spells.end(); itr = next) - { - ++next; - if (itr->second->type == PETSPELL_FAMILY) continue; // prevent saving family passives to DB - if (itr->second->state == PETSPELL_REMOVED || itr->second->state == PETSPELL_CHANGED) - CharacterDatabase.PExecute("DELETE FROM pet_spell WHERE guid = '%u' and spell = '%u'", m_charmInfo->GetPetNumber(), itr->first); - if (itr->second->state == PETSPELL_NEW || itr->second->state == PETSPELL_CHANGED) - CharacterDatabase.PExecute("INSERT INTO pet_spell (guid,spell,slot,active) VALUES ('%u', '%u', '%u','%u')", m_charmInfo->GetPetNumber(), itr->first, itr->second->slotId,itr->second->active); - - if (itr->second->state == PETSPELL_REMOVED) - _removeSpell(itr->first); - else - itr->second->state = PETSPELL_UNCHANGED; - } -} - -void Pet::_LoadAuras(uint32 timediff) -{ - m_Auras.clear(); - for (int i = 0; i < TOTAL_AURAS; i++) - m_modAuras[i].clear(); - - // all aura related fields - for(int i = UNIT_FIELD_AURA; i <= UNIT_FIELD_AURASTATE; ++i) - SetUInt32Value(i, 0); - - QueryResult *result = CharacterDatabase.PQuery("SELECT caster_guid,spell,effect_index,amount,maxduration,remaintime,remaincharges FROM pet_aura WHERE guid = '%u'",m_charmInfo->GetPetNumber()); - - if(result) - { - do - { - Field *fields = result->Fetch(); - uint64 caster_guid = fields[0].GetUInt64(); - uint32 spellid = fields[1].GetUInt32(); - uint32 effindex = fields[2].GetUInt32(); - int32 damage = (int32)fields[3].GetUInt32(); - int32 maxduration = (int32)fields[4].GetUInt32(); - int32 remaintime = (int32)fields[5].GetUInt32(); - int32 remaincharges = (int32)fields[6].GetUInt32(); - - SpellEntry const* spellproto = sSpellStore.LookupEntry(spellid); - if(!spellproto) - { - sLog.outError("Unknown aura (spellid %u, effindex %u), ignore.",spellid,effindex); - continue; - } - - if(effindex >= 3) - { - sLog.outError("Invalid effect index (spellid %u, effindex %u), ignore.",spellid,effindex); - continue; - } - - // negative effects should continue counting down after logout - if (remaintime != -1 && !IsPositiveEffect(spellid, effindex)) - { - if(remaintime <= int32(timediff)) - continue; - - remaintime -= timediff; - } - - // prevent wrong values of remaincharges - if(spellproto->procCharges) - { - if(remaincharges <= 0 || remaincharges > spellproto->procCharges) - remaincharges = spellproto->procCharges; - } - else - remaincharges = -1; - - Aura* aura = CreateAura(spellproto, effindex, NULL, this, NULL); - - if(!damage) - damage = aura->GetModifier()->m_amount; - aura->SetLoadedState(caster_guid,damage,maxduration,remaintime,remaincharges); - AddAura(aura); - } - while( result->NextRow() ); - - delete result; - } -} - -void Pet::_SaveAuras() -{ - CharacterDatabase.PExecute("DELETE FROM pet_aura WHERE guid = '%u'",m_charmInfo->GetPetNumber()); - - AuraMap const& auras = GetAuras(); - for(AuraMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr) - { - // skip all auras from spell that apply at cast SPELL_AURA_MOD_SHAPESHIFT or pet area auras. - SpellEntry const *spellInfo = itr->second->GetSpellProto(); - uint8 i; - for (i = 0; i < 3; i++) - if (spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_STEALTH || - spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AREA_AURA_OWNER || - spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AREA_AURA_PET ) - break; - - if (i == 3 && !itr->second->IsPassive()) - CharacterDatabase.PExecute("INSERT INTO pet_aura (guid,caster_guid,spell,effect_index,amount,maxduration,remaintime,remaincharges) " - "VALUES ('%u', '" I64FMTD "', '%u', '%u', '%d', '%d', '%d', '%d')", - m_charmInfo->GetPetNumber(), itr->second->GetCasterGUID(),(uint32)(*itr).second->GetId(), (uint32)(*itr).second->GetEffIndex(),(*itr).second->GetModifier()->m_amount,int((*itr).second->GetAuraMaxDuration()),int((*itr).second->GetAuraDuration()),int((*itr).second->m_procCharges)); - } -} - -bool Pet::addSpell(uint16 spell_id, uint16 active, PetSpellState state, uint16 slot_id, PetSpellType type) -{ - SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell_id); - if (!spellInfo) - { - // do pet spell book cleanup - if(state == PETSPELL_UNCHANGED) // spell load case - { - sLog.outError("Pet::addSpell: Non-existed in SpellStore spell #%u request, deleting for all pets in `pet_spell`.",spell_id); - CharacterDatabase.PExecute("DELETE FROM pet_spell WHERE spell = '%u'",spell_id); - } - else - sLog.outError("Pet::addSpell: Non-existed in SpellStore spell #%u request.",spell_id); - - return false; - } - - PetSpellMap::iterator itr = m_spells.find(spell_id); - if (itr != m_spells.end()) - { - if (itr->second->state == PETSPELL_REMOVED) - { - delete itr->second; - m_spells.erase(itr); - state = PETSPELL_CHANGED; - } - else if (state == PETSPELL_UNCHANGED && itr->second->state != PETSPELL_UNCHANGED) - { - // can be in case spell loading but learned at some previous spell loading - itr->second->state = PETSPELL_UNCHANGED; - return false; - } - else - return false; - } - - uint32 oldspell_id = 0; - - PetSpell *newspell = new PetSpell; - newspell->state = state; - newspell->type = type; - - if(active == ACT_DECIDE) //active was not used before, so we save it's autocast/passive state here - { - if(IsPassiveSpell(spell_id)) - newspell->active = ACT_PASSIVE; - else - newspell->active = ACT_DISABLED; - } - else - newspell->active = active; - - uint32 chainstart = spellmgr.GetFirstSpellInChain(spell_id); - - for (PetSpellMap::iterator itr = m_spells.begin(); itr != m_spells.end(); itr++) - { - if(itr->second->state == PETSPELL_REMOVED) continue; - - if(spellmgr.GetFirstSpellInChain(itr->first) == chainstart) - { - slot_id = itr->second->slotId; - newspell->active = itr->second->active; - - if(newspell->active == ACT_ENABLED) - ToggleAutocast(itr->first, false); - - oldspell_id = itr->first; - removeSpell(itr->first); - } - } - - uint16 tmpslot=slot_id; - - if (tmpslot == 0xffff) - { - uint16 maxid = 0; - PetSpellMap::iterator itr; - for (itr = m_spells.begin(); itr != m_spells.end(); ++itr) - { - if(itr->second->state == PETSPELL_REMOVED) continue; - if (itr->second->slotId > maxid) maxid = itr->second->slotId; - } - tmpslot = maxid + 1; - } - - newspell->slotId = tmpslot; - m_spells[spell_id] = newspell; - - if (IsPassiveSpell(spell_id)) - CastSpell(this, spell_id, true); - else if(state == PETSPELL_NEW) - m_charmInfo->AddSpellToAB(oldspell_id, spell_id); - - if(newspell->active == ACT_ENABLED) - ToggleAutocast(spell_id, true); - - return true; -} - -bool Pet::learnSpell(uint16 spell_id) -{ - // prevent duplicated entires in spell book - if (!addSpell(spell_id)) - return false; - - Unit* owner = GetOwner(); - if(owner->GetTypeId()==TYPEID_PLAYER) - ((Player*)owner)->PetSpellInitialize(); - return true; -} - -void Pet::removeSpell(uint16 spell_id) -{ - PetSpellMap::iterator itr = m_spells.find(spell_id); - if (itr == m_spells.end()) - return; - - if(itr->second->state == PETSPELL_REMOVED) - return; - - if(itr->second->state == PETSPELL_NEW) - { - delete itr->second; - m_spells.erase(itr); - } - else - itr->second->state = PETSPELL_REMOVED; - - RemoveAurasDueToSpell(spell_id); -} - -bool Pet::_removeSpell(uint16 spell_id) -{ - PetSpellMap::iterator itr = m_spells.find(spell_id); - if (itr != m_spells.end()) - { - delete itr->second; - m_spells.erase(itr); - return true; - } - return false; -} - -void Pet::InitPetCreateSpells() -{ - m_charmInfo->InitPetActionBar(); - - m_spells.clear(); - int32 usedtrainpoints = 0, petspellid; - PetCreateSpellEntry const* CreateSpells = objmgr.GetPetCreateSpellEntry(GetEntry()); - if(CreateSpells) - { - for(uint8 i = 0; i < 4; i++) - { - if(!CreateSpells->spellid[i]) - break; - - SpellEntry const *learn_spellproto = sSpellStore.LookupEntry(CreateSpells->spellid[i]); - if(!learn_spellproto) - continue; - - if(learn_spellproto->Effect[0] == SPELL_EFFECT_LEARN_SPELL || learn_spellproto->Effect[0] == SPELL_EFFECT_LEARN_PET_SPELL) - { - petspellid = learn_spellproto->EffectTriggerSpell[0]; - Unit* owner = GetOwner(); - if(owner->GetTypeId() == TYPEID_PLAYER && !((Player*)owner)->HasSpell(learn_spellproto->Id)) - { - if(IsPassiveSpell(petspellid)) //learn passive skills when tamed, not sure if thats right - ((Player*)owner)->learnSpell(learn_spellproto->Id); - else - AddTeachSpell(learn_spellproto->EffectTriggerSpell[0], learn_spellproto->Id); - } - } - else - petspellid = learn_spellproto->Id; - - addSpell(petspellid); - - SkillLineAbilityMap::const_iterator lower = spellmgr.GetBeginSkillLineAbilityMap(learn_spellproto->EffectTriggerSpell[0]); - SkillLineAbilityMap::const_iterator upper = spellmgr.GetEndSkillLineAbilityMap(learn_spellproto->EffectTriggerSpell[0]); - - for(SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_spell_idx) - { - usedtrainpoints += _spell_idx->second->reqtrainpoints; - break; - } - } - } - - LearnPetPassives(); - - CastPetAuras(false); - - SetTP(-usedtrainpoints); -} - -void Pet::CheckLearning(uint32 spellid) -{ - //charmed case -> prevent crash - if(GetTypeId() == TYPEID_PLAYER || getPetType() != HUNTER_PET) - return; - - Unit* owner = GetOwner(); - - if(m_teachspells.empty() || !owner || owner->GetTypeId() != TYPEID_PLAYER) - return; - - TeachSpellMap::iterator itr = m_teachspells.find(spellid); - if(itr == m_teachspells.end()) - return; - - if(urand(0, 100) < 10) - { - ((Player*)owner)->learnSpell(itr->second); - m_teachspells.erase(itr); - } -} - -uint32 Pet::resetTalentsCost() const -{ - uint32 days = (sWorld.GetGameTime() - m_resetTalentsTime)/DAY; - - // The first time reset costs 10 silver; after 1 day cost is reset to 10 silver - if(m_resetTalentsCost < 10*SILVER || days > 0) - return 10*SILVER; - // then 50 silver - else if(m_resetTalentsCost < 50*SILVER) - return 50*SILVER; - // then 1 gold - else if(m_resetTalentsCost < 1*GOLD) - return 1*GOLD; - // then increasing at a rate of 1 gold; cap 10 gold - else - return (m_resetTalentsCost + 1*GOLD > 10*GOLD ? 10*GOLD : m_resetTalentsCost + 1*GOLD); -} - -void Pet::ToggleAutocast(uint32 spellid, bool apply) -{ - if(IsPassiveSpell(spellid)) - return; - - PetSpellMap::const_iterator itr = m_spells.find((uint16)spellid); - - int i; - - if(apply) - { - for (i = 0; i < m_autospells.size() && m_autospells[i] != spellid; i++); - if (i == m_autospells.size()) - { - m_autospells.push_back(spellid); - itr->second->active = ACT_ENABLED; - itr->second->state = PETSPELL_CHANGED; - } - } - else - { - AutoSpellList::iterator itr2 = m_autospells.begin(); - for (i = 0; i < m_autospells.size() && m_autospells[i] != spellid; i++, itr2++); - if (i < m_autospells.size()) - { - m_autospells.erase(itr2); - itr->second->active = ACT_DISABLED; - itr->second->state = PETSPELL_CHANGED; - } - } -} - -bool Pet::Create(uint32 guidlow, Map *map, uint32 Entry, uint32 pet_number) -{ - SetMapId(map->GetId()); - SetInstanceId(map->GetInstanceId()); - - Object::_Create(guidlow, pet_number, HIGHGUID_PET); - - m_DBTableGuid = guidlow; - m_originalEntry = Entry; - - if(!InitEntry(Entry)) - return false; - - SetByteValue(UNIT_FIELD_BYTES_2, 0, SHEATH_STATE_MELEE ); - SetByteValue(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_UNK3 | UNIT_BYTE2_FLAG_AURAS | UNIT_BYTE2_FLAG_UNK5 ); - - if(getPetType() == MINI_PET) // always non-attackable - SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - - return true; -} - -bool Pet::HasSpell(uint32 spell) const -{ - return (m_spells.find(spell) != m_spells.end()); -} - -// Get all passive spells in our skill line -void Pet::LearnPetPassives() -{ - CreatureInfo const* cInfo = GetCreatureInfo(); - if(!cInfo) - return; - - CreatureFamilyEntry const* cFamily = sCreatureFamilyStore.LookupEntry(cInfo->family); - if(!cFamily) - return; - - PetFamilySpellsStore::const_iterator petStore = sPetFamilySpellsStore.find(cFamily->ID); - if(petStore != sPetFamilySpellsStore.end()) - { - for(PetFamilySpellsSet::const_iterator petSet = petStore->second.begin(); petSet != petStore->second.end(); ++petSet) - addSpell(*petSet, ACT_DECIDE, PETSPELL_NEW, 0xffff, PETSPELL_FAMILY); - } -} - -void Pet::CastPetAuras(bool current) -{ - Unit* owner = GetOwner(); - if(!owner) - return; - - if(getPetType() != HUNTER_PET && (getPetType() != SUMMON_PET || owner->getClass() != CLASS_WARLOCK)) - return; - - for(PetAuraSet::iterator itr = owner->m_petAuras.begin(); itr != owner->m_petAuras.end(); ) - { - PetAura const* pa = *itr; - ++itr; - - if(!current && pa->IsRemovedOnChangePet()) - owner->RemovePetAura(pa); - else - CastPetAura(pa); - } -} - -void Pet::CastPetAura(PetAura const* aura) -{ - uint16 auraId = aura->GetAura(GetEntry()); - if(!auraId) - return; - - if(auraId == 35696) // Demonic Knowledge - { - int32 basePoints = aura->GetDamage() * (GetStat(STAT_STAMINA) + GetStat(STAT_INTELLECT)) / 100; - CastCustomSpell(this,auraId,&basePoints, NULL, NULL, true ); - } - else - CastSpell(this, auraId, true); -} +/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "Common.h"
+#include "Database/DatabaseEnv.h"
+#include "Log.h"
+#include "WorldSession.h"
+#include "WorldPacket.h"
+#include "ObjectMgr.h"
+#include "SpellMgr.h"
+#include "Pet.h"
+#include "MapManager.h"
+#include "Formulas.h"
+#include "SpellAuras.h"
+#include "CreatureAI.h"
+#include "Unit.h"
+#include "Util.h"
+
+char const* petTypeSuffix[MAX_PET_TYPE] =
+{
+ "'s Minion", // SUMMON_PET
+ "'s Pet", // HUNTER_PET
+ "'s Guardian", // GUARDIAN_PET
+ "'s Companion" // MINI_PET
+};
+
+//numbers represent minutes * 100 while happy (you get 100 loyalty points per min while happy)
+uint32 const LevelUpLoyalty[6] =
+{
+ 5500,
+ 11500,
+ 17000,
+ 23500,
+ 31000,
+ 39500,
+};
+
+uint32 const LevelStartLoyalty[6] =
+{
+ 2000,
+ 4500,
+ 7000,
+ 10000,
+ 13500,
+ 17500,
+};
+
+Pet::Pet(PetType type) : Creature()
+{
+ m_isPet = true;
+ m_name = "Pet";
+ m_petType = type;
+
+ m_removed = false;
+ m_regenTimer = 4000;
+ m_happinessTimer = 7500;
+ m_loyaltyTimer = 12000;
+ m_duration = 0;
+ m_bonusdamage = 0;
+
+ m_loyaltyPoints = 0;
+ m_TrainingPoints = 0;
+ m_resetTalentsCost = 0;
+ m_resetTalentsTime = 0;
+
+ m_auraUpdateMask = 0;
+
+ // pets always have a charminfo, even if they are not actually charmed
+ CharmInfo* charmInfo = InitCharmInfo(this);
+
+ if(type == MINI_PET) // always passive
+ charmInfo->SetReactState(REACT_PASSIVE);
+ else if(type == GUARDIAN_PET) // always aggressive
+ charmInfo->SetReactState(REACT_AGGRESSIVE);
+
+ m_spells.clear();
+ m_Auras.clear();
+ m_CreatureSpellCooldowns.clear();
+ m_CreatureCategoryCooldowns.clear();
+ m_autospells.clear();
+ m_declinedname = NULL;
+}
+
+Pet::~Pet()
+{
+ if(m_uint32Values) // only for fully created Object
+ {
+ for (PetSpellMap::iterator i = m_spells.begin(); i != m_spells.end(); ++i)
+ delete i->second;
+ ObjectAccessor::Instance().RemoveObject(this);
+ }
+
+ delete m_declinedname;
+}
+
+void Pet::AddToWorld()
+{
+ ///- Register the pet for guid lookup
+ if(!IsInWorld()) ObjectAccessor::Instance().AddObject(this);
+ Unit::AddToWorld();
+}
+
+void Pet::RemoveFromWorld()
+{
+ ///- Remove the pet from the accessor
+ if(IsInWorld()) ObjectAccessor::Instance().RemoveObject(this);
+ ///- Don't call the function for Creature, normal mobs + totems go in a different storage
+ Unit::RemoveFromWorld();
+}
+
+bool Pet::LoadPetFromDB( Unit* owner, uint32 petentry, uint32 petnumber, bool current )
+{
+ uint32 ownerid = owner->GetGUIDLow();
+
+ QueryResult *result;
+
+ if(petnumber)
+ // known petnumber entry 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
+ result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, loyaltypoints, loyalty, trainpoint, slot, name, renamed, curhealth, curmana, curhappiness, abdata, TeachSpelldata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType FROM character_pet WHERE owner = '%u' AND id = '%u'",ownerid, petnumber);
+ else if(current)
+ // current pet (slot 0) 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
+ result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, loyaltypoints, loyalty, trainpoint, slot, name, renamed, curhealth, curmana, curhappiness, abdata, TeachSpelldata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType FROM character_pet WHERE owner = '%u' AND slot = '0'",ownerid );
+ else if(petentry)
+ // known petentry entry (unique for summoned pet, but non unique for hunter pet (only from current or not stabled pets)
+ // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
+ result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, loyaltypoints, loyalty, trainpoint, slot, name, renamed, curhealth, curmana, curhappiness, abdata, TeachSpelldata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType FROM character_pet WHERE owner = '%u' AND entry = '%u' AND (slot = '0' OR slot = '3') ",ownerid, petentry );
+ else
+ // any current or other non-stabled pet (for hunter "call pet")
+ // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
+ result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, loyaltypoints, loyalty, trainpoint, slot, name, renamed, curhealth, curmana, curhappiness, abdata, TeachSpelldata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType FROM character_pet WHERE owner = '%u' AND (slot = '0' OR slot = '3') ",ownerid);
+
+ if(!result)
+ return false;
+
+ Field *fields = result->Fetch();
+
+ // update for case of current pet "slot = 0"
+ petentry = fields[1].GetUInt32();
+ if(!petentry)
+ {
+ delete result;
+ return false;
+ }
+
+ uint32 summon_spell_id = fields[21].GetUInt32();
+ SpellEntry const* spellInfo = sSpellStore.LookupEntry(summon_spell_id);
+
+ bool is_temporary_summoned = spellInfo && GetSpellDuration(spellInfo) > 0;
+
+ // check temporary summoned pets like mage water elemental
+ if(current && is_temporary_summoned)
+ {
+ delete result;
+ return false;
+ }
+
+ Map *map = owner->GetMap();
+ uint32 guid=objmgr.GenerateLowGuid(HIGHGUID_PET);
+ uint32 pet_number = fields[0].GetUInt32();
+ if(!Create(guid, map, petentry, pet_number))
+ {
+ delete result;
+ return false;
+ }
+
+ float px, py, pz;
+ owner->GetClosePoint(px, py, pz,GetObjectSize(),PET_FOLLOW_DIST,PET_FOLLOW_ANGLE);
+
+ Relocate(px, py, pz, owner->GetOrientation());
+
+ if(!IsPositionValid())
+ {
+ sLog.outError("ERROR: Pet (guidlow %d, entry %d) not loaded. Suggested coordinates isn't valid (X: %d Y: ^%d)", GetGUIDLow(), GetEntry(), GetPositionX(), GetPositionY());
+ delete result;
+ return false;
+ }
+
+ setPetType(PetType(fields[22].GetUInt8()));
+ SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE,owner->getFaction());
+ SetUInt32Value(UNIT_CREATED_BY_SPELL, summon_spell_id);
+
+ CreatureInfo const *cinfo = GetCreatureInfo();
+ if(cinfo->type == CREATURE_TYPE_CRITTER)
+ {
+ AIM_Initialize();
+ map->Add((Creature*)this);
+ delete result;
+ return true;
+ }
+ if(getPetType()==HUNTER_PET || getPetType()==SUMMON_PET && cinfo->type == CREATURE_TYPE_DEMON && owner->getClass() == CLASS_WARLOCK)
+ m_charmInfo->SetPetNumber(pet_number, true);
+ else
+ m_charmInfo->SetPetNumber(pet_number, false);
+ SetUInt64Value(UNIT_FIELD_SUMMONEDBY, owner->GetGUID());
+ SetDisplayId(fields[3].GetUInt32());
+ SetNativeDisplayId(fields[3].GetUInt32());
+ uint32 petlevel=fields[4].GetUInt32();
+ SetUInt32Value(UNIT_NPC_FLAGS , 0);
+ SetName(fields[11].GetString());
+
+ switch(getPetType())
+ {
+
+ case SUMMON_PET:
+ petlevel=owner->getLevel();
+
+ SetUInt32Value(UNIT_FIELD_BYTES_0,2048);
+ SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE);
+ // this enables popup window (pet dismiss, cancel)
+ break;
+ case HUNTER_PET:
+ SetUInt32Value(UNIT_FIELD_BYTES_0, 0x02020100);
+ SetByteValue(UNIT_FIELD_BYTES_1, 1, fields[8].GetUInt32());
+ SetByteValue(UNIT_FIELD_BYTES_2, 0, SHEATH_STATE_MELEE );
+ SetByteValue(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_UNK3 | UNIT_BYTE2_FLAG_AURAS | UNIT_BYTE2_FLAG_UNK5 );
+
+ if(fields[12].GetBool())
+ SetByteValue(UNIT_FIELD_BYTES_2, 2, UNIT_RENAME_NOT_ALLOWED);
+ else
+ SetByteValue(UNIT_FIELD_BYTES_2, 2, UNIT_RENAME_ALLOWED);
+
+ SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE);
+ // this enables popup window (pet abandon, cancel)
+ SetTP(fields[9].GetInt32());
+ SetMaxPower(POWER_HAPPINESS,GetCreatePowers(POWER_HAPPINESS));
+ SetPower( POWER_HAPPINESS,fields[15].GetUInt32());
+ setPowerType(POWER_FOCUS);
+ break;
+ default:
+ sLog.outError("Pet have incorrect type (%u) for pet loading.",getPetType());
+ }
+ InitStatsForLevel( petlevel);
+ SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, time(NULL));
+ SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, fields[5].GetUInt32());
+ SetUInt64Value(UNIT_FIELD_CREATEDBY, owner->GetGUID());
+
+ m_charmInfo->SetReactState( ReactStates( fields[6].GetUInt8() ));
+ m_loyaltyPoints = fields[7].GetInt32();
+
+ uint32 savedhealth = fields[13].GetUInt32();
+ uint32 savedmana = fields[14].GetUInt32();
+
+ // set current pet as current
+ if(fields[10].GetUInt32() != 0)
+ {
+ CharacterDatabase.BeginTransaction();
+ CharacterDatabase.PExecute("UPDATE character_pet SET slot = '3' WHERE owner = '%u' AND slot = '0' AND id <> '%u'",ownerid, m_charmInfo->GetPetNumber());
+ CharacterDatabase.PExecute("UPDATE character_pet SET slot = '0' WHERE owner = '%u' AND id = '%u'",ownerid, m_charmInfo->GetPetNumber());
+ CharacterDatabase.CommitTransaction();
+ }
+
+ if(!is_temporary_summoned)
+ {
+ // permanent controlled pets store state in DB
+ Tokens tokens = StrSplit(fields[16].GetString(), " ");
+
+ if(tokens.size() != 20)
+ {
+ delete result;
+ return false;
+ }
+
+ int index;
+ Tokens::iterator iter;
+ for(iter = tokens.begin(), index = 0; index < 10; ++iter, ++index )
+ {
+ m_charmInfo->GetActionBarEntry(index)->Type = atol((*iter).c_str());
+ ++iter;
+ m_charmInfo->GetActionBarEntry(index)->SpellOrAction = atol((*iter).c_str());
+ }
+
+ //init teach spells
+ tokens = StrSplit(fields[17].GetString(), " ");
+ for (iter = tokens.begin(), index = 0; index < 4; ++iter, ++index)
+ {
+ uint32 tmp = atol((*iter).c_str());
+
+ ++iter;
+
+ if(tmp)
+ AddTeachSpell(tmp, atol((*iter).c_str()));
+ else
+ break;
+ }
+ }
+
+ // since last save (in seconds)
+ uint32 timediff = (time(NULL) - fields[18].GetUInt32());
+
+ delete result;
+
+ //load spells/cooldowns/auras
+ SetCanModifyStats(true);
+ _LoadAuras(timediff);
+
+ //init AB
+ if(is_temporary_summoned)
+ {
+ // Temporary summoned pets always have initial spell list at load
+ InitPetCreateSpells();
+ }
+ else
+ {
+ LearnPetPassives();
+ CastPetAuras(current);
+ }
+
+ if(getPetType() == SUMMON_PET && !current) //all (?) summon pets come with full health when called, but not when they are current
+ {
+ SetHealth(GetMaxHealth());
+ SetPower(POWER_MANA, GetMaxPower(POWER_MANA));
+ }
+ else
+ {
+ SetHealth(savedhealth > GetMaxHealth() ? GetMaxHealth() : savedhealth);
+ SetPower(POWER_MANA, savedmana > GetMaxPower(POWER_MANA) ? GetMaxPower(POWER_MANA) : savedmana);
+ }
+
+ AIM_Initialize();
+ map->Add((Creature*)this);
+
+ // Spells should be loaded after pet is added to map, because in CanCast is check on it
+ _LoadSpells();
+ _LoadSpellCooldowns();
+
+ owner->SetPet(this); // in DB stored only full controlled creature
+ sLog.outDebug("New Pet has guid %u", GetGUIDLow());
+
+ if(owner->GetTypeId() == TYPEID_PLAYER)
+ {
+ ((Player*)owner)->PetSpellInitialize();
+ if(((Player*)owner)->GetGroup())
+ ((Player*)owner)->SetGroupUpdateFlag(GROUP_UPDATE_PET);
+ }
+
+ if(owner->GetTypeId() == TYPEID_PLAYER && getPetType() == HUNTER_PET)
+ {
+ result = CharacterDatabase.PQuery("SELECT genitive, dative, accusative, instrumental, prepositional FROM character_pet_declinedname WHERE owner = '%u' AND id = '%u'",owner->GetGUIDLow(),GetCharmInfo()->GetPetNumber());
+
+ if(result)
+ {
+ if(m_declinedname)
+ delete m_declinedname;
+
+ m_declinedname = new DeclinedName;
+ Field *fields = result->Fetch();
+ for(int i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
+ {
+ m_declinedname->name[i] = fields[i].GetCppString();
+ }
+ }
+ }
+
+ return true;
+}
+
+void Pet::SavePetToDB(PetSaveMode mode)
+{
+ if(!GetEntry())
+ return;
+
+ // save only fully controlled creature
+ if(!isControlled())
+ return;
+
+ uint32 curhealth = GetHealth();
+ uint32 curmana = GetPower(POWER_MANA);
+
+ switch(mode)
+ {
+ case PET_SAVE_IN_STABLE_SLOT_1:
+ case PET_SAVE_IN_STABLE_SLOT_2:
+ case PET_SAVE_NOT_IN_SLOT:
+ {
+ RemoveAllAuras();
+
+ //only alive hunter pets get auras saved, the others don't
+ if(!(getPetType() == HUNTER_PET && isAlive()))
+ m_Auras.clear();
+ }
+ default:
+ break;
+ }
+
+ _SaveSpells();
+ _SaveSpellCooldowns();
+ _SaveAuras();
+
+ switch(mode)
+ {
+ case PET_SAVE_AS_CURRENT:
+ case PET_SAVE_IN_STABLE_SLOT_1:
+ case PET_SAVE_IN_STABLE_SLOT_2:
+ case PET_SAVE_NOT_IN_SLOT:
+ {
+ uint32 loyalty =1;
+ if(getPetType()!=HUNTER_PET)
+ loyalty = GetLoyaltyLevel();
+
+ uint32 owner = GUID_LOPART(GetOwnerGUID());
+ std::string name = m_name;
+ CharacterDatabase.escape_string(name);
+ CharacterDatabase.BeginTransaction();
+ // remove current data
+ CharacterDatabase.PExecute("DELETE FROM character_pet WHERE owner = '%u' AND id = '%u'", owner,m_charmInfo->GetPetNumber() );
+
+ // prevent duplicate using slot (except PET_SAVE_NOT_IN_SLOT)
+ if(mode!=PET_SAVE_NOT_IN_SLOT)
+ CharacterDatabase.PExecute("UPDATE character_pet SET slot = 3 WHERE owner = '%u' AND slot = '%u'", owner, uint32(mode) );
+
+ // prevent existence another hunter pet in PET_SAVE_AS_CURRENT and PET_SAVE_NOT_IN_SLOT
+ if(getPetType()==HUNTER_PET && (mode==PET_SAVE_AS_CURRENT||mode==PET_SAVE_NOT_IN_SLOT))
+ CharacterDatabase.PExecute("DELETE FROM character_pet WHERE owner = '%u' AND (slot = '0' OR slot = '3')", owner );
+ // save pet
+ std::ostringstream ss;
+ ss << "INSERT INTO character_pet ( id, entry, owner, modelid, level, exp, Reactstate, loyaltypoints, loyalty, trainpoint, slot, name, renamed, curhealth, curmana, curhappiness, abdata,TeachSpelldata,savetime,resettalents_cost,resettalents_time,CreatedBySpell,PetType) "
+ << "VALUES ("
+ << m_charmInfo->GetPetNumber() << ", "
+ << GetEntry() << ", "
+ << owner << ", "
+ << GetNativeDisplayId() << ", "
+ << getLevel() << ", "
+ << GetUInt32Value(UNIT_FIELD_PETEXPERIENCE) << ", "
+ << uint32(m_charmInfo->GetReactState()) << ", "
+ << m_loyaltyPoints << ", "
+ << GetLoyaltyLevel() << ", "
+ << m_TrainingPoints << ", "
+ << uint32(mode) << ", '"
+ << name.c_str() << "', "
+ << uint32((GetByteValue(UNIT_FIELD_BYTES_2, 2) == UNIT_RENAME_ALLOWED)?0:1) << ", "
+ << (curhealth<1?1:curhealth) << ", "
+ << curmana << ", "
+ << GetPower(POWER_HAPPINESS) << ", '";
+
+ for(uint32 i = 0; i < 10; i++)
+ ss << uint32(m_charmInfo->GetActionBarEntry(i)->Type) << " " << uint32(m_charmInfo->GetActionBarEntry(i)->SpellOrAction) << " ";
+ ss << "', '";
+
+ //save spells the pet can teach to it's Master
+ {
+ int i = 0;
+ for(TeachSpellMap::iterator itr = m_teachspells.begin(); i < 4 && itr != m_teachspells.end(); ++i, ++itr)
+ ss << itr->first << " " << itr->second << " ";
+ for(; i < 4; ++i)
+ ss << uint32(0) << " " << uint32(0) << " ";
+ }
+
+ ss << "', "
+ << time(NULL) << ", "
+ << uint32(m_resetTalentsCost) << ", "
+ << uint64(m_resetTalentsTime) << ", "
+ << GetUInt32Value(UNIT_CREATED_BY_SPELL) << ", "
+ << uint32(getPetType()) << ")";
+
+ CharacterDatabase.Execute( ss.str().c_str() );
+
+ CharacterDatabase.CommitTransaction();
+ break;
+ }
+ case PET_SAVE_AS_DELETED:
+ {
+ RemoveAllAuras();
+ uint32 owner = GUID_LOPART(GetOwnerGUID());
+ DeleteFromDB(m_charmInfo->GetPetNumber());
+ break;
+ }
+ default:
+ sLog.outError("Unknown pet save/remove mode: %d",mode);
+ }
+}
+
+void Pet::DeleteFromDB(uint32 guidlow)
+{
+ CharacterDatabase.PExecute("DELETE FROM character_pet WHERE id = '%u'", guidlow);
+ CharacterDatabase.PExecute("DELETE FROM character_pet_declinedname WHERE id = '%u'", guidlow);
+ CharacterDatabase.PExecute("DELETE FROM pet_aura WHERE guid = '%u'", guidlow);
+ CharacterDatabase.PExecute("DELETE FROM pet_spell WHERE guid = '%u'", guidlow);
+ CharacterDatabase.PExecute("DELETE FROM pet_spell_cooldown WHERE guid = '%u'", guidlow);
+}
+
+void Pet::setDeathState(DeathState s) // overwrite virtual Creature::setDeathState and Unit::setDeathState
+{
+ Creature::setDeathState(s);
+ if(getDeathState()==CORPSE)
+ {
+ //remove summoned pet (no corpse)
+ if(getPetType()==SUMMON_PET)
+ Remove(PET_SAVE_NOT_IN_SLOT);
+ // other will despawn at corpse desppawning (Pet::Update code)
+ else
+ {
+ // pet corpse non lootable and non skinnable
+ SetUInt32Value( UNIT_DYNAMIC_FLAGS, 0x00 );
+ RemoveFlag (UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE);
+
+ //lose happiness when died and not in BG/Arena
+ MapEntry const* mapEntry = sMapStore.LookupEntry(GetMapId());
+ if(!mapEntry || (mapEntry->map_type != MAP_ARENA && mapEntry->map_type != MAP_BATTLEGROUND))
+ ModifyPower(POWER_HAPPINESS, -HAPPINESS_LEVEL_SIZE);
+
+ SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_ROTATE);
+ }
+ }
+ else if(getDeathState()==ALIVE)
+ {
+ RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_ROTATE);
+ CastPetAuras(true);
+ }
+}
+
+void Pet::Update(uint32 diff)
+{
+ if(m_removed) // pet already removed, just wait in remove queue, no updates
+ return;
+
+ switch( m_deathState )
+ {
+ case CORPSE:
+ {
+ if( m_deathTimer <= diff )
+ {
+ assert(getPetType()!=SUMMON_PET && "Must be already removed.");
+ Remove(PET_SAVE_NOT_IN_SLOT); //hunters' pets never get removed because of death, NEVER!
+ return;
+ }
+ break;
+ }
+ case ALIVE:
+ {
+ // unsummon pet that lost owner
+ Unit* owner = GetOwner();
+ if(!owner || !IsWithinDistInMap(owner, OWNER_MAX_DISTANCE) || isControlled() && !owner->GetPetGUID())
+ {
+ Remove(PET_SAVE_NOT_IN_SLOT, true);
+ return;
+ }
+
+ if(isControlled())
+ {
+ if( owner->GetPetGUID() != GetGUID() )
+ {
+ Remove(getPetType()==HUNTER_PET?PET_SAVE_AS_DELETED:PET_SAVE_NOT_IN_SLOT);
+ return;
+ }
+ }
+
+ if(m_duration > 0)
+ {
+ if(m_duration > diff)
+ m_duration -= diff;
+ else
+ {
+ Remove(getPetType() != SUMMON_PET ? PET_SAVE_AS_DELETED:PET_SAVE_NOT_IN_SLOT);
+ return;
+ }
+ }
+
+ if(getPetType() != HUNTER_PET)
+ break;
+
+ //regenerate Focus
+ if(m_regenTimer <= diff)
+ {
+ RegenerateFocus();
+ m_regenTimer = 4000;
+ }
+ else
+ m_regenTimer -= diff;
+
+ if(m_happinessTimer <= diff)
+ {
+ LooseHappiness();
+ m_happinessTimer = 7500;
+ }
+ else
+ m_happinessTimer -= diff;
+
+ if(m_loyaltyTimer <= diff)
+ {
+ TickLoyaltyChange();
+ m_loyaltyTimer = 12000;
+ }
+ else
+ m_loyaltyTimer -= diff;
+
+ break;
+ }
+ default:
+ break;
+ }
+ Creature::Update(diff);
+}
+
+void Pet::RegenerateFocus()
+{
+ uint32 curValue = GetPower(POWER_FOCUS);
+ uint32 maxValue = GetMaxPower(POWER_FOCUS);
+
+ if (curValue >= maxValue)
+ return;
+
+ float addvalue = 24 * sWorld.getRate(RATE_POWER_FOCUS);
+
+ AuraList const& ModPowerRegenPCTAuras = GetAurasByType(SPELL_AURA_MOD_POWER_REGEN_PERCENT);
+ for(AuraList::const_iterator i = ModPowerRegenPCTAuras.begin(); i != ModPowerRegenPCTAuras.end(); ++i)
+ if ((*i)->GetModifier()->m_miscvalue == POWER_FOCUS)
+ addvalue *= ((*i)->GetModifier()->m_amount + 100) / 100.0f;
+
+ ModifyPower(POWER_FOCUS, (int32)addvalue);
+}
+
+void Pet::LooseHappiness()
+{
+ uint32 curValue = GetPower(POWER_HAPPINESS);
+ if (curValue <= 0)
+ return;
+ int32 addvalue = (140 >> GetLoyaltyLevel()) * 125; //value is 70/35/17/8/4 (per min) * 1000 / 8 (timer 7.5 secs)
+ if(isInCombat()) //we know in combat happiness fades faster, multiplier guess
+ addvalue = int32(addvalue * 1.5);
+ ModifyPower(POWER_HAPPINESS, -addvalue);
+}
+
+void Pet::ModifyLoyalty(int32 addvalue)
+{
+ uint32 loyaltylevel = GetLoyaltyLevel();
+
+ if(addvalue > 0) //only gain influenced, not loss
+ addvalue = int32((float)addvalue * sWorld.getRate(RATE_LOYALTY));
+
+ if(loyaltylevel >= BEST_FRIEND && (addvalue + m_loyaltyPoints) > int32(GetMaxLoyaltyPoints(loyaltylevel)))
+ return;
+
+ m_loyaltyPoints += addvalue;
+
+ if(m_loyaltyPoints < 0)
+ {
+ if(loyaltylevel > REBELLIOUS)
+ {
+ //level down
+ --loyaltylevel;
+ SetLoyaltyLevel(LoyaltyLevel(loyaltylevel));
+ m_loyaltyPoints = GetStartLoyaltyPoints(loyaltylevel);
+ SetTP(m_TrainingPoints - int32(getLevel()));
+ }
+ else
+ {
+ m_loyaltyPoints = 0;
+ Unit* owner = GetOwner();
+ if(owner && owner->GetTypeId() == TYPEID_PLAYER)
+ {
+ WorldPacket data(SMSG_PET_BROKEN, 0);
+ ((Player*)owner)->GetSession()->SendPacket(&data);
+
+ //run away
+ ((Player*)owner)->RemovePet(this,PET_SAVE_AS_DELETED);
+ }
+ }
+ }
+ //level up
+ else if(m_loyaltyPoints > int32(GetMaxLoyaltyPoints(loyaltylevel)))
+ {
+ ++loyaltylevel;
+ SetLoyaltyLevel(LoyaltyLevel(loyaltylevel));
+ m_loyaltyPoints = GetStartLoyaltyPoints(loyaltylevel);
+ SetTP(m_TrainingPoints + getLevel());
+ }
+}
+
+void Pet::TickLoyaltyChange()
+{
+ int32 addvalue;
+
+ switch(GetHappinessState())
+ {
+ case HAPPY: addvalue = 20; break;
+ case CONTENT: addvalue = 10; break;
+ case UNHAPPY: addvalue = -20; break;
+ default:
+ return;
+ }
+ ModifyLoyalty(addvalue);
+}
+
+void Pet::KillLoyaltyBonus(uint32 level)
+{
+ if(level > 100)
+ return;
+
+ //at lower levels gain is faster | the lower loyalty the more loyalty is gained
+ uint32 bonus = uint32(((100 - level) / 10) + (6 - GetLoyaltyLevel()));
+ ModifyLoyalty(bonus);
+}
+
+HappinessState Pet::GetHappinessState()
+{
+ if(GetPower(POWER_HAPPINESS) < HAPPINESS_LEVEL_SIZE)
+ return UNHAPPY;
+ else if(GetPower(POWER_HAPPINESS) >= HAPPINESS_LEVEL_SIZE * 2)
+ return HAPPY;
+ else
+ return CONTENT;
+}
+
+void Pet::SetLoyaltyLevel(LoyaltyLevel level)
+{
+ SetByteValue(UNIT_FIELD_BYTES_1, 1, level);
+}
+
+bool Pet::CanTakeMoreActiveSpells(uint32 spellid)
+{
+ uint8 activecount = 1;
+ uint32 chainstartstore[ACTIVE_SPELLS_MAX];
+
+ if(IsPassiveSpell(spellid))
+ return true;
+
+ chainstartstore[0] = spellmgr.GetFirstSpellInChain(spellid);
+
+ for (PetSpellMap::iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr)
+ {
+ if(IsPassiveSpell(itr->first))
+ continue;
+
+ uint32 chainstart = spellmgr.GetFirstSpellInChain(itr->first);
+
+ uint8 x;
+
+ for(x = 0; x < activecount; x++)
+ {
+ if(chainstart == chainstartstore[x])
+ break;
+ }
+
+ if(x == activecount) //spellchain not yet saved -> add active count
+ {
+ ++activecount;
+ if(activecount > ACTIVE_SPELLS_MAX)
+ return false;
+ chainstartstore[x] = chainstart;
+ }
+ }
+ return true;
+}
+
+bool Pet::HasTPForSpell(uint32 spellid)
+{
+ int32 neededtrainp = GetTPForSpell(spellid);
+ if((m_TrainingPoints - neededtrainp < 0 || neededtrainp < 0) && neededtrainp != 0)
+ return false;
+ return true;
+}
+
+int32 Pet::GetTPForSpell(uint32 spellid)
+{
+ uint32 basetrainp = 0;
+
+ SkillLineAbilityMap::const_iterator lower = spellmgr.GetBeginSkillLineAbilityMap(spellid);
+ SkillLineAbilityMap::const_iterator upper = spellmgr.GetEndSkillLineAbilityMap(spellid);
+ for(SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_spell_idx)
+ {
+ if(!_spell_idx->second->reqtrainpoints)
+ return 0;
+
+ basetrainp = _spell_idx->second->reqtrainpoints;
+ break;
+ }
+
+ uint32 spenttrainp = 0;
+ uint32 chainstart = spellmgr.GetFirstSpellInChain(spellid);
+
+ for (PetSpellMap::iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr)
+ {
+ if(itr->second->state == PETSPELL_REMOVED)
+ continue;
+
+ if(spellmgr.GetFirstSpellInChain(itr->first) == chainstart)
+ {
+ SkillLineAbilityMap::const_iterator _lower = spellmgr.GetBeginSkillLineAbilityMap(itr->first);
+ SkillLineAbilityMap::const_iterator _upper = spellmgr.GetEndSkillLineAbilityMap(itr->first);
+
+ for(SkillLineAbilityMap::const_iterator _spell_idx2 = _lower; _spell_idx2 != _upper; ++_spell_idx2)
+ {
+ if(_spell_idx2->second->reqtrainpoints > spenttrainp)
+ {
+ spenttrainp = _spell_idx2->second->reqtrainpoints;
+ break;
+ }
+ }
+ }
+ }
+
+ return int32(basetrainp) - int32(spenttrainp);
+}
+
+uint32 Pet::GetMaxLoyaltyPoints(uint32 level)
+{
+ return LevelUpLoyalty[level - 1];
+}
+
+uint32 Pet::GetStartLoyaltyPoints(uint32 level)
+{
+ return LevelStartLoyalty[level - 1];
+}
+
+void Pet::SetTP(int32 TP)
+{
+ m_TrainingPoints = TP;
+ SetUInt32Value(UNIT_TRAINING_POINTS, (uint32)GetDispTP());
+}
+
+int32 Pet::GetDispTP()
+{
+ if(getPetType()!= HUNTER_PET)
+ return(0);
+ if(m_TrainingPoints < 0)
+ return -m_TrainingPoints;
+ else
+ return -(m_TrainingPoints + 1);
+}
+
+void Pet::Remove(PetSaveMode mode, bool returnreagent)
+{
+ Unit* owner = GetOwner();
+
+ if(owner)
+ {
+ if(owner->GetTypeId()==TYPEID_PLAYER)
+ {
+ ((Player*)owner)->RemovePet(this,mode,returnreagent);
+ return;
+ }
+
+ // only if current pet in slot
+ if(owner->GetPetGUID()==GetGUID())
+ owner->SetPet(0);
+ }
+
+ CleanupsBeforeDelete();
+ AddObjectToRemoveList();
+ m_removed = true;
+}
+
+void Pet::GivePetXP(uint32 xp)
+{
+ if(getPetType() != HUNTER_PET)
+ return;
+
+ if ( xp < 1 )
+ return;
+
+ if(!isAlive())
+ return;
+
+ uint32 level = getLevel();
+
+ // XP to money conversion processed in Player::RewardQuest
+ if(level >= sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
+ return;
+
+ uint32 curXP = GetUInt32Value(UNIT_FIELD_PETEXPERIENCE);
+ uint32 nextLvlXP = GetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP);
+ uint32 newXP = curXP + xp;
+
+ if(newXP >= nextLvlXP && level+1 > GetOwner()->getLevel())
+ {
+ SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, nextLvlXP-1);
+ return;
+ }
+
+ while( newXP >= nextLvlXP && level < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL) )
+ {
+ newXP -= nextLvlXP;
+
+ SetLevel( level + 1 );
+ SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, uint32((MaNGOS::XP::xp_to_level(level+1))/4));
+
+ level = getLevel();
+ nextLvlXP = GetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP);
+ GivePetLevel(level);
+ }
+
+ SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, newXP);
+
+ if(getPetType() == HUNTER_PET)
+ KillLoyaltyBonus(level);
+}
+
+void Pet::GivePetLevel(uint32 level)
+{
+ if(!level)
+ return;
+
+ InitStatsForLevel( level);
+
+ SetTP(m_TrainingPoints + (GetLoyaltyLevel() - 1));
+}
+
+bool Pet::CreateBaseAtCreature(Creature* creature)
+{
+ if(!creature)
+ {
+ sLog.outError("CRITICAL ERROR: NULL pointer parsed into CreateBaseAtCreature()");
+ return false;
+ }
+ uint32 guid=objmgr.GenerateLowGuid(HIGHGUID_PET);
+
+ sLog.outBasic("SetInstanceID()");
+ SetInstanceId(creature->GetInstanceId());
+
+ sLog.outBasic("Create pet");
+ uint32 pet_number = objmgr.GeneratePetNumber();
+ if(!Create(guid, creature->GetMap(), creature->GetEntry(), pet_number))
+ return false;
+
+ Relocate(creature->GetPositionX(), creature->GetPositionY(), creature->GetPositionZ(), creature->GetOrientation());
+
+ if(!IsPositionValid())
+ {
+ sLog.outError("ERROR: Pet (guidlow %d, entry %d) not created base at creature. Suggested coordinates isn't valid (X: %d Y: ^%d)", GetGUIDLow(), GetEntry(), GetPositionX(), GetPositionY());
+ return false;
+ }
+
+ CreatureInfo const *cinfo = GetCreatureInfo();
+ if(!cinfo)
+ {
+ sLog.outError("ERROR: CreateBaseAtCreature() failed, creatureInfo is missing!");
+ return false;
+ }
+
+ if(cinfo->type == CREATURE_TYPE_CRITTER)
+ {
+ setPetType(MINI_PET);
+ return true;
+ }
+ SetDisplayId(creature->GetDisplayId());
+ SetNativeDisplayId(creature->GetNativeDisplayId());
+ SetMaxPower(POWER_HAPPINESS,GetCreatePowers(POWER_HAPPINESS));
+ SetPower( POWER_HAPPINESS,166500);
+ setPowerType(POWER_FOCUS);
+ SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP,0);
+ SetUInt32Value(UNIT_FIELD_PETEXPERIENCE,0);
+ SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, uint32((MaNGOS::XP::xp_to_level(creature->getLevel()))/4));
+ SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE);
+ SetUInt32Value(UNIT_NPC_FLAGS , 0);
+
+ CreatureFamilyEntry const* cFamily = sCreatureFamilyStore.LookupEntry(creature->GetCreatureInfo()->family);
+ if( char* familyname = cFamily->Name[sWorld.GetDefaultDbcLocale()] )
+ SetName(familyname);
+ else
+ SetName(creature->GetName());
+
+ m_loyaltyPoints = 1000;
+ if(cinfo->type == CREATURE_TYPE_BEAST)
+ {
+ SetUInt32Value(UNIT_FIELD_BYTES_0, 0x02020100);
+ SetByteValue(UNIT_FIELD_BYTES_2, 0, SHEATH_STATE_MELEE );
+ SetByteValue(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_UNK3 | UNIT_BYTE2_FLAG_AURAS | UNIT_BYTE2_FLAG_UNK5 );
+ SetByteValue(UNIT_FIELD_BYTES_2, 2, UNIT_RENAME_ALLOWED);
+
+ SetUInt32Value(UNIT_MOD_CAST_SPEED, creature->GetUInt32Value(UNIT_MOD_CAST_SPEED) );
+ SetLoyaltyLevel(REBELLIOUS);
+ }
+ return true;
+}
+
+bool Pet::InitStatsForLevel(uint32 petlevel)
+{
+ CreatureInfo const *cinfo = GetCreatureInfo();
+ assert(cinfo);
+
+ Unit* owner = GetOwner();
+ if(!owner)
+ {
+ sLog.outError("ERROR: attempt to summon pet (Entry %u) without owner! Attempt terminated.", cinfo->Entry);
+ return false;
+ }
+
+ uint32 creature_ID = (getPetType() == HUNTER_PET) ? 1 : cinfo->Entry;
+
+ SetLevel( petlevel);
+
+ SetMeleeDamageSchool(SpellSchools(cinfo->dmgschool));
+
+ SetModifierValue(UNIT_MOD_ARMOR, BASE_VALUE, float(petlevel*50));
+
+ SetAttackTime(BASE_ATTACK, BASE_ATTACK_TIME);
+ SetAttackTime(OFF_ATTACK, BASE_ATTACK_TIME);
+ SetAttackTime(RANGED_ATTACK, BASE_ATTACK_TIME);
+
+ SetFloatValue(UNIT_MOD_CAST_SPEED, 1.0);
+
+ CreatureFamilyEntry const* cFamily = sCreatureFamilyStore.LookupEntry(cinfo->family);
+ if(cFamily && cFamily->minScale > 0.0f)
+ {
+ float scale;
+ if (getLevel() >= cFamily->maxScaleLevel)
+ scale = cFamily->maxScale;
+ else if (getLevel() <= cFamily->minScaleLevel)
+ scale = cFamily->minScale;
+ else
+ scale = cFamily->minScale + (getLevel() - cFamily->minScaleLevel) / cFamily->maxScaleLevel * (cFamily->maxScale - cFamily->minScale);
+
+ SetFloatValue(OBJECT_FIELD_SCALE_X, scale);
+ }
+ m_bonusdamage = 0;
+
+ int32 createResistance[MAX_SPELL_SCHOOL] = {0,0,0,0,0,0,0};
+
+ if(cinfo && getPetType() != HUNTER_PET)
+ {
+ createResistance[SPELL_SCHOOL_HOLY] = cinfo->resistance1;
+ createResistance[SPELL_SCHOOL_FIRE] = cinfo->resistance2;
+ createResistance[SPELL_SCHOOL_NATURE] = cinfo->resistance3;
+ createResistance[SPELL_SCHOOL_FROST] = cinfo->resistance4;
+ createResistance[SPELL_SCHOOL_SHADOW] = cinfo->resistance5;
+ createResistance[SPELL_SCHOOL_ARCANE] = cinfo->resistance6;
+ }
+
+ switch(getPetType())
+ {
+ case SUMMON_PET:
+ {
+ if(owner->GetTypeId() == TYPEID_PLAYER)
+ {
+ switch(owner->getClass())
+ {
+ case CLASS_WARLOCK:
+ {
+
+ //the damage bonus used for pets is either fire or shadow damage, whatever is higher
+ uint32 fire = owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + SPELL_SCHOOL_FIRE);
+ uint32 shadow = owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + SPELL_SCHOOL_SHADOW);
+ uint32 val = (fire > shadow) ? fire : shadow;
+
+ SetBonusDamage(int32 (val * 0.15f));
+ //bonusAP += val * 0.57;
+ break;
+ }
+ case CLASS_MAGE:
+ {
+ //40% damage bonus of mage's frost damage
+ float val = owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + SPELL_SCHOOL_FROST) * 0.4;
+ if(val < 0)
+ val = 0;
+ SetBonusDamage( int32(val));
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, float(petlevel - (petlevel / 4)) );
+ SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, float(petlevel + (petlevel / 4)) );
+
+ //SetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE, float(cinfo->attackpower));
+
+ PetLevelInfo const* pInfo = objmgr.GetPetLevelInfo(creature_ID, petlevel);
+ if(pInfo) // exist in DB
+ {
+ SetCreateHealth(pInfo->health);
+ SetCreateMana(pInfo->mana);
+
+ if(pInfo->armor > 0)
+ SetModifierValue(UNIT_MOD_ARMOR, BASE_VALUE, float(pInfo->armor));
+
+ for(int stat = 0; stat < MAX_STATS; ++stat)
+ {
+ SetCreateStat(Stats(stat),float(pInfo->stats[stat]));
+ }
+ }
+ else // not exist in DB, use some default fake data
+ {
+ sLog.outErrorDb("Summoned pet (Entry: %u) not have pet stats data in DB",cinfo->Entry);
+
+ // remove elite bonuses included in DB values
+ SetCreateHealth(uint32(((float(cinfo->maxhealth) / cinfo->maxlevel) / (1 + 2 * cinfo->rank)) * petlevel) );
+ SetCreateMana( uint32(((float(cinfo->maxmana) / cinfo->maxlevel) / (1 + 2 * cinfo->rank)) * petlevel) );
+
+ SetCreateStat(STAT_STRENGTH,22);
+ SetCreateStat(STAT_AGILITY,22);
+ SetCreateStat(STAT_STAMINA,25);
+ SetCreateStat(STAT_INTELLECT,28);
+ SetCreateStat(STAT_SPIRIT,27);
+ }
+ break;
+ }
+ case HUNTER_PET:
+ {
+ SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, uint32((MaNGOS::XP::xp_to_level(petlevel))/4));
+
+ //these formula may not be correct; however, it is designed to be close to what it should be
+ //this makes dps 0.5 of pets level
+ SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, float(petlevel - (petlevel / 4)) );
+ //damage range is then petlevel / 2
+ SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, float(petlevel + (petlevel / 4)) );
+ //damage is increased afterwards as strength and pet scaling modify attack power
+
+ //stored standard pet stats are entry 1 in pet_levelinfo
+ PetLevelInfo const* pInfo = objmgr.GetPetLevelInfo(creature_ID, petlevel);
+ if(pInfo) // exist in DB
+ {
+ SetCreateHealth(pInfo->health);
+ SetModifierValue(UNIT_MOD_ARMOR, BASE_VALUE, float(pInfo->armor));
+ //SetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE, float(cinfo->attackpower));
+
+ for( int i = STAT_STRENGTH; i < MAX_STATS; i++)
+ {
+ SetCreateStat(Stats(i), float(pInfo->stats[i]));
+ }
+ }
+ else // not exist in DB, use some default fake data
+ {
+ sLog.outErrorDb("Hunter pet levelstats missing in DB");
+
+ // remove elite bonuses included in DB values
+ SetCreateHealth( uint32(((float(cinfo->maxhealth) / cinfo->maxlevel) / (1 + 2 * cinfo->rank)) * petlevel) );
+
+ SetCreateStat(STAT_STRENGTH,22);
+ SetCreateStat(STAT_AGILITY,22);
+ SetCreateStat(STAT_STAMINA,25);
+ SetCreateStat(STAT_INTELLECT,28);
+ SetCreateStat(STAT_SPIRIT,27);
+ }
+ break;
+ }
+ case GUARDIAN_PET:
+ SetUInt32Value(UNIT_FIELD_PETEXPERIENCE,0);
+ SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP,1000);
+
+ SetCreateMana( 28 + 10*petlevel );
+ SetCreateHealth( 28 + 30*petlevel );
+
+ // FIXME: this is wrong formula, possible each guardian pet have own damage formula
+ //these formula may not be correct; however, it is designed to be close to what it should be
+ //this makes dps 0.5 of pets level
+ SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, float(petlevel - (petlevel / 4)) );
+ //damage range is then petlevel / 2
+ SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, float(petlevel + (petlevel / 4)) );
+ break;
+ default:
+ sLog.outError("Pet have incorrect type (%u) for levelup.",getPetType()); break;
+ }
+
+ for (int i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i)
+ SetModifierValue(UnitMods(UNIT_MOD_RESISTANCE_START + i), BASE_VALUE, float(createResistance[i]) );
+
+ UpdateAllStats();
+
+ SetHealth(GetMaxHealth());
+ SetPower(POWER_MANA, GetMaxPower(POWER_MANA));
+
+ return true;
+}
+
+bool Pet::HaveInDiet(ItemPrototype const* item) const
+{
+ if (!item->FoodType)
+ return false;
+
+ CreatureInfo const* cInfo = GetCreatureInfo();
+ if(!cInfo)
+ return false;
+
+ CreatureFamilyEntry const* cFamily = sCreatureFamilyStore.LookupEntry(cInfo->family);
+ if(!cFamily)
+ return false;
+
+ uint32 diet = cFamily->petFoodMask;
+ uint32 FoodMask = 1 << (item->FoodType-1);
+ return diet & FoodMask;
+}
+
+uint32 Pet::GetCurrentFoodBenefitLevel(uint32 itemlevel)
+{
+ // -5 or greater food level
+ if(getLevel() <= itemlevel +5) //possible to feed level 60 pet with level 55 level food for full effect
+ return 35000;
+ // -10..-6
+ else if(getLevel() <= itemlevel + 10) //pure guess, but sounds good
+ return 17000;
+ // -14..-11
+ else if(getLevel() <= itemlevel + 14) //level 55 food gets green on 70, makes sense to me
+ return 8000;
+ // -15 or less
+ else
+ return 0; //food too low level
+}
+
+void Pet::_LoadSpellCooldowns()
+{
+ m_CreatureSpellCooldowns.clear();
+ m_CreatureCategoryCooldowns.clear();
+
+ QueryResult *result = CharacterDatabase.PQuery("SELECT spell,time FROM pet_spell_cooldown WHERE guid = '%u'",m_charmInfo->GetPetNumber());
+
+ if(result)
+ {
+ time_t curTime = time(NULL);
+
+ WorldPacket data(SMSG_SPELL_COOLDOWN, (8+1+result->GetRowCount()*8));
+ data << GetGUID();
+ data << uint8(0x0);
+
+ do
+ {
+ Field *fields = result->Fetch();
+
+ uint32 spell_id = fields[0].GetUInt32();
+ time_t db_time = (time_t)fields[1].GetUInt64();
+
+ if(!sSpellStore.LookupEntry(spell_id))
+ {
+ sLog.outError("Pet %u have unknown spell %u in `pet_spell_cooldown`, skipping.",m_charmInfo->GetPetNumber(),spell_id);
+ continue;
+ }
+
+ // skip outdated cooldown
+ if(db_time <= curTime)
+ continue;
+
+ data << uint32(spell_id);
+ data << uint32(uint32(db_time-curTime)*1000); // in m.secs
+
+ _AddCreatureSpellCooldown(spell_id,db_time);
+
+ sLog.outDebug("Pet (Number: %u) spell %u cooldown loaded (%u secs).",m_charmInfo->GetPetNumber(),spell_id,uint32(db_time-curTime));
+ }
+ while( result->NextRow() );
+
+ delete result;
+
+ if(!m_CreatureSpellCooldowns.empty() && GetOwner())
+ {
+ ((Player*)GetOwner())->GetSession()->SendPacket(&data);
+ }
+ }
+}
+
+void Pet::_SaveSpellCooldowns()
+{
+ CharacterDatabase.PExecute("DELETE FROM pet_spell_cooldown WHERE guid = '%u'", m_charmInfo->GetPetNumber());
+
+ time_t curTime = time(NULL);
+
+ // remove oudated and save active
+ for(CreatureSpellCooldowns::iterator itr = m_CreatureSpellCooldowns.begin();itr != m_CreatureSpellCooldowns.end();)
+ {
+ if(itr->second <= curTime)
+ m_CreatureSpellCooldowns.erase(itr++);
+ else
+ {
+ CharacterDatabase.PExecute("INSERT INTO pet_spell_cooldown (guid,spell,time) VALUES ('%u', '%u', '" I64FMTD "')", m_charmInfo->GetPetNumber(), itr->first, uint64(itr->second));
+ ++itr;
+ }
+ }
+}
+
+void Pet::_LoadSpells()
+{
+ QueryResult *result = CharacterDatabase.PQuery("SELECT spell,slot,active FROM pet_spell WHERE guid = '%u'",m_charmInfo->GetPetNumber());
+
+ if(result)
+ {
+ do
+ {
+ Field *fields = result->Fetch();
+
+ addSpell(fields[0].GetUInt16(), fields[2].GetUInt16(), PETSPELL_UNCHANGED, fields[1].GetUInt16());
+ }
+ while( result->NextRow() );
+
+ delete result;
+ }
+}
+
+void Pet::_SaveSpells()
+{
+ for (PetSpellMap::const_iterator itr = m_spells.begin(), next = m_spells.begin(); itr != m_spells.end(); itr = next)
+ {
+ ++next;
+ if (itr->second->type == PETSPELL_FAMILY) continue; // prevent saving family passives to DB
+ if (itr->second->state == PETSPELL_REMOVED || itr->second->state == PETSPELL_CHANGED)
+ CharacterDatabase.PExecute("DELETE FROM pet_spell WHERE guid = '%u' and spell = '%u'", m_charmInfo->GetPetNumber(), itr->first);
+ if (itr->second->state == PETSPELL_NEW || itr->second->state == PETSPELL_CHANGED)
+ CharacterDatabase.PExecute("INSERT INTO pet_spell (guid,spell,slot,active) VALUES ('%u', '%u', '%u','%u')", m_charmInfo->GetPetNumber(), itr->first, itr->second->slotId,itr->second->active);
+
+ if (itr->second->state == PETSPELL_REMOVED)
+ _removeSpell(itr->first);
+ else
+ itr->second->state = PETSPELL_UNCHANGED;
+ }
+}
+
+void Pet::_LoadAuras(uint32 timediff)
+{
+ m_Auras.clear();
+ for (int i = 0; i < TOTAL_AURAS; i++)
+ m_modAuras[i].clear();
+
+ // all aura related fields
+ for(int i = UNIT_FIELD_AURA; i <= UNIT_FIELD_AURASTATE; ++i)
+ SetUInt32Value(i, 0);
+
+ QueryResult *result = CharacterDatabase.PQuery("SELECT caster_guid,spell,effect_index,amount,maxduration,remaintime,remaincharges FROM pet_aura WHERE guid = '%u'",m_charmInfo->GetPetNumber());
+
+ if(result)
+ {
+ do
+ {
+ Field *fields = result->Fetch();
+ uint64 caster_guid = fields[0].GetUInt64();
+ uint32 spellid = fields[1].GetUInt32();
+ uint32 effindex = fields[2].GetUInt32();
+ int32 damage = (int32)fields[3].GetUInt32();
+ int32 maxduration = (int32)fields[4].GetUInt32();
+ int32 remaintime = (int32)fields[5].GetUInt32();
+ int32 remaincharges = (int32)fields[6].GetUInt32();
+
+ SpellEntry const* spellproto = sSpellStore.LookupEntry(spellid);
+ if(!spellproto)
+ {
+ sLog.outError("Unknown aura (spellid %u, effindex %u), ignore.",spellid,effindex);
+ continue;
+ }
+
+ if(effindex >= 3)
+ {
+ sLog.outError("Invalid effect index (spellid %u, effindex %u), ignore.",spellid,effindex);
+ continue;
+ }
+
+ // negative effects should continue counting down after logout
+ if (remaintime != -1 && !IsPositiveEffect(spellid, effindex))
+ {
+ if(remaintime <= int32(timediff))
+ continue;
+
+ remaintime -= timediff;
+ }
+
+ // prevent wrong values of remaincharges
+ if(spellproto->procCharges)
+ {
+ if(remaincharges <= 0 || remaincharges > spellproto->procCharges)
+ remaincharges = spellproto->procCharges;
+ }
+ else
+ remaincharges = -1;
+
+ Aura* aura = CreateAura(spellproto, effindex, NULL, this, NULL);
+
+ if(!damage)
+ damage = aura->GetModifier()->m_amount;
+ aura->SetLoadedState(caster_guid,damage,maxduration,remaintime,remaincharges);
+ AddAura(aura);
+ }
+ while( result->NextRow() );
+
+ delete result;
+ }
+}
+
+void Pet::_SaveAuras()
+{
+ CharacterDatabase.PExecute("DELETE FROM pet_aura WHERE guid = '%u'",m_charmInfo->GetPetNumber());
+
+ AuraMap const& auras = GetAuras();
+ for(AuraMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr)
+ {
+ // skip all auras from spell that apply at cast SPELL_AURA_MOD_SHAPESHIFT or pet area auras.
+ SpellEntry const *spellInfo = itr->second->GetSpellProto();
+ uint8 i;
+ for (i = 0; i < 3; i++)
+ if (spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_STEALTH ||
+ spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AREA_AURA_OWNER ||
+ spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AREA_AURA_PET )
+ break;
+
+ if (i == 3 && !itr->second->IsPassive())
+ CharacterDatabase.PExecute("INSERT INTO pet_aura (guid,caster_guid,spell,effect_index,amount,maxduration,remaintime,remaincharges) "
+ "VALUES ('%u', '" I64FMTD "', '%u', '%u', '%d', '%d', '%d', '%d')",
+ m_charmInfo->GetPetNumber(), itr->second->GetCasterGUID(),(uint32)(*itr).second->GetId(), (uint32)(*itr).second->GetEffIndex(),(*itr).second->GetModifier()->m_amount,int((*itr).second->GetAuraMaxDuration()),int((*itr).second->GetAuraDuration()),int((*itr).second->m_procCharges));
+ }
+}
+
+bool Pet::addSpell(uint16 spell_id, uint16 active, PetSpellState state, uint16 slot_id, PetSpellType type)
+{
+ SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell_id);
+ if (!spellInfo)
+ {
+ // do pet spell book cleanup
+ if(state == PETSPELL_UNCHANGED) // spell load case
+ {
+ sLog.outError("Pet::addSpell: Non-existed in SpellStore spell #%u request, deleting for all pets in `pet_spell`.",spell_id);
+ CharacterDatabase.PExecute("DELETE FROM pet_spell WHERE spell = '%u'",spell_id);
+ }
+ else
+ sLog.outError("Pet::addSpell: Non-existed in SpellStore spell #%u request.",spell_id);
+
+ return false;
+ }
+
+ PetSpellMap::iterator itr = m_spells.find(spell_id);
+ if (itr != m_spells.end())
+ {
+ if (itr->second->state == PETSPELL_REMOVED)
+ {
+ delete itr->second;
+ m_spells.erase(itr);
+ state = PETSPELL_CHANGED;
+ }
+ else if (state == PETSPELL_UNCHANGED && itr->second->state != PETSPELL_UNCHANGED)
+ {
+ // can be in case spell loading but learned at some previous spell loading
+ itr->second->state = PETSPELL_UNCHANGED;
+ return false;
+ }
+ else
+ return false;
+ }
+
+ uint32 oldspell_id = 0;
+
+ PetSpell *newspell = new PetSpell;
+ newspell->state = state;
+ newspell->type = type;
+
+ if(active == ACT_DECIDE) //active was not used before, so we save it's autocast/passive state here
+ {
+ if(IsPassiveSpell(spell_id))
+ newspell->active = ACT_PASSIVE;
+ else
+ newspell->active = ACT_DISABLED;
+ }
+ else
+ newspell->active = active;
+
+ uint32 chainstart = spellmgr.GetFirstSpellInChain(spell_id);
+
+ for (PetSpellMap::iterator itr = m_spells.begin(); itr != m_spells.end(); itr++)
+ {
+ if(itr->second->state == PETSPELL_REMOVED) continue;
+
+ if(spellmgr.GetFirstSpellInChain(itr->first) == chainstart)
+ {
+ slot_id = itr->second->slotId;
+ newspell->active = itr->second->active;
+
+ if(newspell->active == ACT_ENABLED)
+ ToggleAutocast(itr->first, false);
+
+ oldspell_id = itr->first;
+ removeSpell(itr->first);
+ }
+ }
+
+ uint16 tmpslot=slot_id;
+
+ if (tmpslot == 0xffff)
+ {
+ uint16 maxid = 0;
+ PetSpellMap::iterator itr;
+ for (itr = m_spells.begin(); itr != m_spells.end(); ++itr)
+ {
+ if(itr->second->state == PETSPELL_REMOVED) continue;
+ if (itr->second->slotId > maxid) maxid = itr->second->slotId;
+ }
+ tmpslot = maxid + 1;
+ }
+
+ newspell->slotId = tmpslot;
+ m_spells[spell_id] = newspell;
+
+ if (IsPassiveSpell(spell_id))
+ CastSpell(this, spell_id, true);
+ else if(state == PETSPELL_NEW)
+ m_charmInfo->AddSpellToAB(oldspell_id, spell_id);
+
+ if(newspell->active == ACT_ENABLED)
+ ToggleAutocast(spell_id, true);
+
+ return true;
+}
+
+bool Pet::learnSpell(uint16 spell_id)
+{
+ // prevent duplicated entires in spell book
+ if (!addSpell(spell_id))
+ return false;
+
+ Unit* owner = GetOwner();
+ if(owner->GetTypeId()==TYPEID_PLAYER)
+ ((Player*)owner)->PetSpellInitialize();
+ return true;
+}
+
+void Pet::removeSpell(uint16 spell_id)
+{
+ PetSpellMap::iterator itr = m_spells.find(spell_id);
+ if (itr == m_spells.end())
+ return;
+
+ if(itr->second->state == PETSPELL_REMOVED)
+ return;
+
+ if(itr->second->state == PETSPELL_NEW)
+ {
+ delete itr->second;
+ m_spells.erase(itr);
+ }
+ else
+ itr->second->state = PETSPELL_REMOVED;
+
+ RemoveAurasDueToSpell(spell_id);
+}
+
+bool Pet::_removeSpell(uint16 spell_id)
+{
+ PetSpellMap::iterator itr = m_spells.find(spell_id);
+ if (itr != m_spells.end())
+ {
+ delete itr->second;
+ m_spells.erase(itr);
+ return true;
+ }
+ return false;
+}
+
+void Pet::InitPetCreateSpells()
+{
+ m_charmInfo->InitPetActionBar();
+
+ m_spells.clear();
+ int32 usedtrainpoints = 0, petspellid;
+ PetCreateSpellEntry const* CreateSpells = objmgr.GetPetCreateSpellEntry(GetEntry());
+ if(CreateSpells)
+ {
+ for(uint8 i = 0; i < 4; i++)
+ {
+ if(!CreateSpells->spellid[i])
+ break;
+
+ SpellEntry const *learn_spellproto = sSpellStore.LookupEntry(CreateSpells->spellid[i]);
+ if(!learn_spellproto)
+ continue;
+
+ if(learn_spellproto->Effect[0] == SPELL_EFFECT_LEARN_SPELL || learn_spellproto->Effect[0] == SPELL_EFFECT_LEARN_PET_SPELL)
+ {
+ petspellid = learn_spellproto->EffectTriggerSpell[0];
+ Unit* owner = GetOwner();
+ if(owner->GetTypeId() == TYPEID_PLAYER && !((Player*)owner)->HasSpell(learn_spellproto->Id))
+ {
+ if(IsPassiveSpell(petspellid)) //learn passive skills when tamed, not sure if thats right
+ ((Player*)owner)->learnSpell(learn_spellproto->Id);
+ else
+ AddTeachSpell(learn_spellproto->EffectTriggerSpell[0], learn_spellproto->Id);
+ }
+ }
+ else
+ petspellid = learn_spellproto->Id;
+
+ addSpell(petspellid);
+
+ SkillLineAbilityMap::const_iterator lower = spellmgr.GetBeginSkillLineAbilityMap(learn_spellproto->EffectTriggerSpell[0]);
+ SkillLineAbilityMap::const_iterator upper = spellmgr.GetEndSkillLineAbilityMap(learn_spellproto->EffectTriggerSpell[0]);
+
+ for(SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_spell_idx)
+ {
+ usedtrainpoints += _spell_idx->second->reqtrainpoints;
+ break;
+ }
+ }
+ }
+
+ LearnPetPassives();
+
+ CastPetAuras(false);
+
+ SetTP(-usedtrainpoints);
+}
+
+void Pet::CheckLearning(uint32 spellid)
+{
+ //charmed case -> prevent crash
+ if(GetTypeId() == TYPEID_PLAYER || getPetType() != HUNTER_PET)
+ return;
+
+ Unit* owner = GetOwner();
+
+ if(m_teachspells.empty() || !owner || owner->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ TeachSpellMap::iterator itr = m_teachspells.find(spellid);
+ if(itr == m_teachspells.end())
+ return;
+
+ if(urand(0, 100) < 10)
+ {
+ ((Player*)owner)->learnSpell(itr->second);
+ m_teachspells.erase(itr);
+ }
+}
+
+uint32 Pet::resetTalentsCost() const
+{
+ uint32 days = (sWorld.GetGameTime() - m_resetTalentsTime)/DAY;
+
+ // The first time reset costs 10 silver; after 1 day cost is reset to 10 silver
+ if(m_resetTalentsCost < 10*SILVER || days > 0)
+ return 10*SILVER;
+ // then 50 silver
+ else if(m_resetTalentsCost < 50*SILVER)
+ return 50*SILVER;
+ // then 1 gold
+ else if(m_resetTalentsCost < 1*GOLD)
+ return 1*GOLD;
+ // then increasing at a rate of 1 gold; cap 10 gold
+ else
+ return (m_resetTalentsCost + 1*GOLD > 10*GOLD ? 10*GOLD : m_resetTalentsCost + 1*GOLD);
+}
+
+void Pet::ToggleAutocast(uint32 spellid, bool apply)
+{
+ if(IsPassiveSpell(spellid))
+ return;
+
+ PetSpellMap::const_iterator itr = m_spells.find((uint16)spellid);
+
+ int i;
+
+ if(apply)
+ {
+ for (i = 0; i < m_autospells.size() && m_autospells[i] != spellid; i++);
+ if (i == m_autospells.size())
+ {
+ m_autospells.push_back(spellid);
+ itr->second->active = ACT_ENABLED;
+ itr->second->state = PETSPELL_CHANGED;
+ }
+ }
+ else
+ {
+ AutoSpellList::iterator itr2 = m_autospells.begin();
+ for (i = 0; i < m_autospells.size() && m_autospells[i] != spellid; i++, itr2++);
+ if (i < m_autospells.size())
+ {
+ m_autospells.erase(itr2);
+ itr->second->active = ACT_DISABLED;
+ itr->second->state = PETSPELL_CHANGED;
+ }
+ }
+}
+
+bool Pet::Create(uint32 guidlow, Map *map, uint32 Entry, uint32 pet_number)
+{
+ SetMapId(map->GetId());
+ SetInstanceId(map->GetInstanceId());
+
+ Object::_Create(guidlow, pet_number, HIGHGUID_PET);
+
+ m_DBTableGuid = guidlow;
+ m_originalEntry = Entry;
+
+ if(!InitEntry(Entry))
+ return false;
+
+ SetByteValue(UNIT_FIELD_BYTES_2, 0, SHEATH_STATE_MELEE );
+ SetByteValue(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_UNK3 | UNIT_BYTE2_FLAG_AURAS | UNIT_BYTE2_FLAG_UNK5 );
+
+ if(getPetType() == MINI_PET) // always non-attackable
+ SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
+
+ return true;
+}
+
+bool Pet::HasSpell(uint32 spell) const
+{
+ return (m_spells.find(spell) != m_spells.end());
+}
+
+// Get all passive spells in our skill line
+void Pet::LearnPetPassives()
+{
+ CreatureInfo const* cInfo = GetCreatureInfo();
+ if(!cInfo)
+ return;
+
+ CreatureFamilyEntry const* cFamily = sCreatureFamilyStore.LookupEntry(cInfo->family);
+ if(!cFamily)
+ return;
+
+ PetFamilySpellsStore::const_iterator petStore = sPetFamilySpellsStore.find(cFamily->ID);
+ if(petStore != sPetFamilySpellsStore.end())
+ {
+ for(PetFamilySpellsSet::const_iterator petSet = petStore->second.begin(); petSet != petStore->second.end(); ++petSet)
+ addSpell(*petSet, ACT_DECIDE, PETSPELL_NEW, 0xffff, PETSPELL_FAMILY);
+ }
+}
+
+void Pet::CastPetAuras(bool current)
+{
+ Unit* owner = GetOwner();
+ if(!owner)
+ return;
+
+ if(getPetType() != HUNTER_PET && (getPetType() != SUMMON_PET || owner->getClass() != CLASS_WARLOCK))
+ return;
+
+ for(PetAuraSet::iterator itr = owner->m_petAuras.begin(); itr != owner->m_petAuras.end(); )
+ {
+ PetAura const* pa = *itr;
+ ++itr;
+
+ if(!current && pa->IsRemovedOnChangePet())
+ owner->RemovePetAura(pa);
+ else
+ CastPetAura(pa);
+ }
+}
+
+void Pet::CastPetAura(PetAura const* aura)
+{
+ uint16 auraId = aura->GetAura(GetEntry());
+ if(!auraId)
+ return;
+
+ if(auraId == 35696) // Demonic Knowledge
+ {
+ int32 basePoints = int32(aura->GetDamage() * (GetStat(STAT_STAMINA) + GetStat(STAT_INTELLECT)) / 100);
+ CastCustomSpell(this,auraId,&basePoints, NULL, NULL, true );
+ }
+ else
+ CastSpell(this, auraId, true);
+}
diff --git a/src/game/Player.cpp b/src/game/Player.cpp index 1d032265a80..0da7f36c3eb 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -5794,7 +5794,7 @@ void Player::RewardReputation(Unit *pVictim, float rate) if(!pVictim || pVictim->GetTypeId() == TYPEID_PLAYER)
return;
- ReputationOnKillEntry const* Rep = objmgr.GetReputationOnKilEntry(pVictim->GetEntry());
+ ReputationOnKillEntry const* Rep = objmgr.GetReputationOnKilEntry(((Creature*)pVictim)->GetCreatureInfo()->Entry);
if(!Rep)
return;
@@ -16380,20 +16380,28 @@ bool Player::BuyItemFromVendor(uint64 vendorguid, uint32 item, uint8 count, uint return false;
}
- // load vendor items if not yet
- pCreature->LoadGoods();
+ VendorItemData const* vItems = pCreature->GetVendorItems();
+ if(!vItems || vItems->Empty())
+ {
+ SendBuyError( BUY_ERR_CANT_FIND_ITEM, pCreature, item, 0);
+ return false;
+ }
- CreatureItem* crItem = pCreature->FindItem(item);
+ VendorItem const* crItem = vItems->FindItem(item);
if(!crItem)
{
SendBuyError( BUY_ERR_CANT_FIND_ITEM, pCreature, item, 0);
return false;
}
- if( crItem->maxcount != 0 && crItem->count < count )
+ // check current item amount if it limited
+ if( crItem->maxcount != 0 )
{
- SendBuyError( BUY_ERR_ITEM_ALREADY_SOLD, pCreature, item, 0);
- return false;
+ if(pCreature->GetVendorItemCurrentCount(crItem) < pProto->BuyCount * count )
+ {
+ SendBuyError( BUY_ERR_ITEM_ALREADY_SOLD, pCreature, item, 0);
+ return false;
+ }
}
if( uint32(GetReputationRank(pProto->RequiredReputationFaction)) < pProto->RequiredReputationRank)
@@ -16508,17 +16516,16 @@ bool Player::BuyItemFromVendor(uint64 vendorguid, uint32 item, uint8 count, uint if(Item *it = StoreNewItem( dest, item, true ))
{
- if( crItem->maxcount != 0 )
- crItem->count -= pProto->BuyCount * count;
+ uint32 new_count = pCreature->UpdateVendorItemCurrentCount(crItem,pProto->BuyCount * count);
WorldPacket data(SMSG_BUY_ITEM, (8+4+4+4));
data << pCreature->GetGUID();
- data << (uint32)crItem->id; // entry
- data << (uint32)crItem->count;
+ data << (uint32)crItem->item;
+ data << (uint32)(crItem->maxcount > 0 ? new_count : 0xFFFFFFFF);
data << (uint32)count;
GetSession()->SendPacket(&data);
- SendNewItem(it, count, true, false, false);
+ SendNewItem(it, pProto->BuyCount*count, true, false, false);
}
}
else if( IsEquipmentPos( bag, slot ) )
@@ -16548,17 +16555,16 @@ bool Player::BuyItemFromVendor(uint64 vendorguid, uint32 item, uint8 count, uint if(Item *it = EquipNewItem( dest, item, pProto->BuyCount * count, true ))
{
- if( crItem->maxcount != 0 )
- crItem->count -= pProto->BuyCount * count;
+ uint32 new_count = pCreature->UpdateVendorItemCurrentCount(crItem,pProto->BuyCount * count);
WorldPacket data(SMSG_BUY_ITEM, (8+4+4+4));
data << pCreature->GetGUID();
- data << (uint32)crItem->id; // entry
- data << (uint32)crItem->count;
+ data << (uint32)crItem->item;
+ data << (uint32)(crItem->maxcount > 0 ? new_count : 0xFFFFFFFF);
data << (uint32)count;
GetSession()->SendPacket(&data);
- SendNewItem(it, count, true, false, false);
+ SendNewItem(it, pProto->BuyCount*count, true, false, false);
AutoUnequipOffhandIfNeed();
}
@@ -16569,7 +16575,7 @@ bool Player::BuyItemFromVendor(uint64 vendorguid, uint32 item, uint8 count, uint return false;
}
- return crItem->maxcount!=0?true:false;
+ return crItem->maxcount!=0;
}
uint32 Player::GetMaxPersonalArenaRatingRequirement()
@@ -17174,7 +17180,7 @@ void Player::SendInitialPacketsBeforeAddToMap() // set fly flag if in fly form or taxi flight to prevent visually drop at ground in showup moment
if(HasAuraType(SPELL_AURA_MOD_INCREASE_FLIGHT_SPEED) || isInFlight())
- SetUnitMovementFlags(GetUnitMovementFlags() | MOVEMENTFLAG_FLYING2);
+ AddUnitMovementFlag(MOVEMENTFLAG_FLYING2);
}
void Player::SendInitialPacketsAfterAddToMap()
@@ -18167,3 +18173,14 @@ bool ItemPosCount::isContainedIn(ItemPosCountVec const& vec) const return false;
}
+
+bool Player::isAllowUseBattleGroundObject()
+{
+ return ( //InBattleGround() && // in battleground - not need, check in other cases
+ !IsMounted() && // not mounted
+ !HasStealthAura() && // not stealthed
+ !HasInvisibilityAura() && // not invisible
+ !HasAura(SPELL_RECENTLY_DROPPED_FLAG, 0) && // can't pickup
+ isAlive() // live player
+ );
+}
diff --git a/src/game/Player.h b/src/game/Player.h index 9a4362c04ba..33ccc79e307 100644 --- a/src/game/Player.h +++ b/src/game/Player.h @@ -1898,6 +1898,7 @@ class MANGOS_DLL_SPEC Player : public Unit void ClearAfkReports() { m_bgAfkReporter.clear(); }
bool GetBGAccessByLevel(uint32 bgTypeId) const;
+ bool isAllowUseBattleGroundObject();
/*********************************************************/
/*** REST SYSTEM ***/
diff --git a/src/game/PointMovementGenerator.cpp b/src/game/PointMovementGenerator.cpp index c784c799351..b03263c8bb6 100644 --- a/src/game/PointMovementGenerator.cpp +++ b/src/game/PointMovementGenerator.cpp @@ -1,73 +1,76 @@ -/* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "PointMovementGenerator.h" -#include "Errors.h" -#include "Creature.h" -#include "CreatureAI.h" -#include "MapManager.h" -#include "DestinationHolderImp.h" - -//----- Point Movement Generator -template<class T> -void PointMovementGenerator<T>::Initialize(T &unit) -{ - unit.StopMoving(); - Traveller<T> traveller(unit); - i_destinationHolder.SetDestination(traveller,i_x,i_y,i_z); -} - -template<class T> -bool PointMovementGenerator<T>::Update(T &unit, const uint32 &diff) -{ - if(!&unit) - return false; - - if(unit.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNDED)) - return true; - - Traveller<T> traveller(unit); - - i_destinationHolder.UpdateTraveller(traveller, diff, false); - - if(i_destinationHolder.HasArrived()) - { - unit.StopMoving(); - MovementInform(unit); - return false; - } - - return true; -} - -template<class T> -void PointMovementGenerator<T>::MovementInform(T &unit) -{ -} - -template <> void PointMovementGenerator<Creature>::MovementInform(Creature &unit) -{ - unit.AI()->MovementInform(POINT_MOTION_TYPE, id); -} - -template void PointMovementGenerator<Player>::Initialize(Player&); -template bool PointMovementGenerator<Player>::Update(Player &, const uint32 &diff); -template void PointMovementGenerator<Player>::MovementInform(Player&); - -template void PointMovementGenerator<Creature>::Initialize(Creature&); -template bool PointMovementGenerator<Creature>::Update(Creature&, const uint32 &diff); +/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "PointMovementGenerator.h"
+#include "Errors.h"
+#include "Creature.h"
+#include "CreatureAI.h"
+#include "MapManager.h"
+#include "DestinationHolderImp.h"
+
+//----- Point Movement Generator
+template<class T>
+void PointMovementGenerator<T>::Initialize(T &unit)
+{
+ unit.StopMoving();
+ Traveller<T> traveller(unit);
+ i_destinationHolder.SetDestination(traveller,i_x,i_y,i_z);
+
+ if (unit.GetTypeId() == TYPEID_UNIT && ((Creature*)&unit)->canFly())
+ unit.AddUnitMovementFlag(MOVEMENTFLAG_FLYING2);
+}
+
+template<class T>
+bool PointMovementGenerator<T>::Update(T &unit, const uint32 &diff)
+{
+ if(!&unit)
+ return false;
+
+ if(unit.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNDED))
+ return true;
+
+ Traveller<T> traveller(unit);
+
+ i_destinationHolder.UpdateTraveller(traveller, diff, false);
+
+ if(i_destinationHolder.HasArrived())
+ {
+ unit.StopMoving();
+ MovementInform(unit);
+ return false;
+ }
+
+ return true;
+}
+
+template<class T>
+void PointMovementGenerator<T>::MovementInform(T &unit)
+{
+}
+
+template <> void PointMovementGenerator<Creature>::MovementInform(Creature &unit)
+{
+ unit.AI()->MovementInform(POINT_MOTION_TYPE, id);
+}
+
+template void PointMovementGenerator<Player>::Initialize(Player&);
+template bool PointMovementGenerator<Player>::Update(Player &, const uint32 &diff);
+template void PointMovementGenerator<Player>::MovementInform(Player&);
+
+template void PointMovementGenerator<Creature>::Initialize(Creature&);
+template bool PointMovementGenerator<Creature>::Update(Creature&, const uint32 &diff);
diff --git a/src/game/QuestDef.cpp b/src/game/QuestDef.cpp index c92bb9b7b6a..d18c931440c 100644 --- a/src/game/QuestDef.cpp +++ b/src/game/QuestDef.cpp @@ -1,199 +1,200 @@ -/* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "QuestDef.h" -#include "Player.h" -#include "World.h" - -Quest::Quest(Field * questRecord) -{ - QuestId = questRecord[0].GetUInt32(); - ZoneOrSort = questRecord[1].GetInt32(); - SkillOrClass = questRecord[2].GetInt32(); - MinLevel = questRecord[3].GetUInt32(); - QuestLevel = questRecord[4].GetUInt32(); - Type = questRecord[5].GetUInt32(); - RequiredRaces = questRecord[6].GetUInt32(); - RequiredSkillValue = questRecord[7].GetUInt32(); - RepObjectiveFaction = questRecord[8].GetUInt32(); - RepObjectiveValue = questRecord[9].GetInt32(); - RequiredMinRepFaction = questRecord[10].GetUInt32(); - RequiredMinRepValue = questRecord[11].GetInt32(); - RequiredMaxRepFaction = questRecord[12].GetUInt32(); - RequiredMaxRepValue = questRecord[13].GetInt32(); - SuggestedPlayers = questRecord[14].GetUInt32(); - LimitTime = questRecord[15].GetUInt32(); - QuestFlags = questRecord[16].GetUInt16(); - uint32 SpecialFlags = questRecord[17].GetUInt16(); - CharTitleId = questRecord[18].GetUInt32(); - PrevQuestId = questRecord[19].GetInt32(); - NextQuestId = questRecord[20].GetInt32(); - ExclusiveGroup = questRecord[21].GetInt32(); - NextQuestInChain = questRecord[22].GetUInt32(); - SrcItemId = questRecord[23].GetUInt32(); - SrcItemCount = questRecord[24].GetUInt32(); - SrcSpell = questRecord[25].GetUInt32(); - Title = questRecord[26].GetCppString(); - Details = questRecord[27].GetCppString(); - Objectives = questRecord[28].GetCppString(); - OfferRewardText = questRecord[29].GetCppString(); - RequestItemsText = questRecord[30].GetCppString(); - EndText = questRecord[31].GetCppString(); - - for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i) - ObjectiveText[i] = questRecord[32+i].GetCppString(); - - for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i) - ReqItemId[i] = questRecord[36+i].GetUInt32(); - - for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i) - ReqItemCount[i] = questRecord[40+i].GetUInt32(); - - for (int i = 0; i < QUEST_SOURCE_ITEM_IDS_COUNT; ++i) - ReqSourceId[i] = questRecord[44+i].GetUInt32(); - - for (int i = 0; i < QUEST_SOURCE_ITEM_IDS_COUNT; ++i) - ReqSourceCount[i] = questRecord[48+i].GetUInt32(); - - for (int i = 0; i < QUEST_SOURCE_ITEM_IDS_COUNT; ++i) - ReqSourceRef[i] = questRecord[52+i].GetUInt32(); - - for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i) - ReqCreatureOrGOId[i] = questRecord[56+i].GetInt32(); - - for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i) - ReqCreatureOrGOCount[i] = questRecord[60+i].GetUInt32(); - - for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i) - ReqSpell[i] = questRecord[64+i].GetUInt32(); - - for (int i = 0; i < QUEST_REWARD_CHOICES_COUNT; ++i) - RewChoiceItemId[i] = questRecord[68+i].GetUInt32(); - - for (int i = 0; i < QUEST_REWARD_CHOICES_COUNT; ++i) - RewChoiceItemCount[i] = questRecord[74+i].GetUInt32(); - - for (int i = 0; i < QUEST_REWARDS_COUNT; ++i) - RewItemId[i] = questRecord[80+i].GetUInt32(); - - for (int i = 0; i < QUEST_REWARDS_COUNT; ++i) - RewItemCount[i] = questRecord[84+i].GetUInt32(); - - for (int i = 0; i < QUEST_REPUTATIONS_COUNT; ++i) - RewRepFaction[i] = questRecord[88+i].GetUInt32(); - - for (int i = 0; i < QUEST_REPUTATIONS_COUNT; ++i) - RewRepValue[i] = questRecord[93+i].GetInt32(); - - RewOrReqMoney = questRecord[98].GetInt32(); - RewMoneyMaxLevel = questRecord[99].GetUInt32(); - RewSpell = questRecord[100].GetUInt32(); - RewSpellCast = questRecord[101].GetUInt32(); - RewMailTemplateId = questRecord[102].GetUInt32(); - RewMailDelaySecs = questRecord[103].GetUInt32(); - PointMapId = questRecord[104].GetUInt32(); - PointX = questRecord[105].GetFloat(); - PointY = questRecord[106].GetFloat(); - PointOpt = questRecord[107].GetUInt32(); - - for (int i = 0; i < QUEST_EMOTE_COUNT; ++i) - DetailsEmote[i] = questRecord[108+i].GetUInt32(); - - IncompleteEmote = questRecord[112].GetUInt32(); - CompleteEmote = questRecord[113].GetUInt32(); - - for (int i = 0; i < QUEST_EMOTE_COUNT; ++i) - OfferRewardEmote[i] = questRecord[114+i].GetInt32(); - - QuestStartScript = questRecord[118].GetUInt32(); - QuestCompleteScript = questRecord[119].GetUInt32(); - - QuestFlags |= SpecialFlags << 16; - - m_reqitemscount = 0; - m_reqCreatureOrGOcount = 0; - m_rewitemscount = 0; - m_rewchoiceitemscount = 0; - - for (int i=0; i < QUEST_OBJECTIVES_COUNT; i++) - { - if ( ReqItemId[i] ) - ++m_reqitemscount; - if ( ReqCreatureOrGOId[i] ) - ++m_reqCreatureOrGOcount; - } - - for (int i=0; i < QUEST_REWARDS_COUNT; i++) - { - if ( RewItemId[i] ) - ++m_rewitemscount; - } - - for (int i=0; i < QUEST_REWARD_CHOICES_COUNT; i++) - { - if (RewChoiceItemId[i]) - ++m_rewchoiceitemscount; - } -} - -uint32 Quest::XPValue( Player *pPlayer ) const -{ - if( pPlayer ) - { - if( RewMoneyMaxLevel > 0 ) - { - uint32 pLevel = pPlayer->getLevel(); - uint32 qLevel = QuestLevel; - float fullxp = 0; - if (qLevel >= 65) - fullxp = RewMoneyMaxLevel / 6.0f; - else if (qLevel == 64) - fullxp = RewMoneyMaxLevel / 4.8f; - else if (qLevel == 63) - fullxp = RewMoneyMaxLevel / 3.6f; - else if (qLevel == 62) - fullxp = RewMoneyMaxLevel / 2.4f; - else if (qLevel == 61) - fullxp = RewMoneyMaxLevel / 1.2f; - else if (qLevel > 0 && qLevel <= 60) - fullxp = RewMoneyMaxLevel / 0.6f; - - if( pLevel <= qLevel + 5 ) - return (uint32)fullxp; - else if( pLevel == qLevel + 6 ) - return (uint32)(fullxp * 0.8f); - else if( pLevel == qLevel + 7 ) - return (uint32)(fullxp * 0.6f); - else if( pLevel == qLevel + 8 ) - return (uint32)(fullxp * 0.4f); - else if( pLevel == qLevel + 9 ) - return (uint32)(fullxp * 0.2f); - else - return (uint32)(fullxp * 0.1f); - } - } - return 0; -} - -int32 Quest::GetRewOrReqMoney() const -{ - if(RewOrReqMoney <=0) - return RewOrReqMoney; - - return int32(RewOrReqMoney * sWorld.getRate(RATE_DROP_MONEY)); -} +/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "QuestDef.h"
+#include "Player.h"
+#include "World.h"
+
+Quest::Quest(Field * questRecord)
+{
+ QuestId = questRecord[0].GetUInt32();
+ QuestMethod = questRecord[1].GetUInt32();
+ ZoneOrSort = questRecord[2].GetInt32();
+ SkillOrClass = questRecord[3].GetInt32();
+ MinLevel = questRecord[4].GetUInt32();
+ QuestLevel = questRecord[5].GetUInt32();
+ Type = questRecord[6].GetUInt32();
+ RequiredRaces = questRecord[7].GetUInt32();
+ RequiredSkillValue = questRecord[8].GetUInt32();
+ RepObjectiveFaction = questRecord[9].GetUInt32();
+ RepObjectiveValue = questRecord[10].GetInt32();
+ RequiredMinRepFaction = questRecord[11].GetUInt32();
+ RequiredMinRepValue = questRecord[12].GetInt32();
+ RequiredMaxRepFaction = questRecord[13].GetUInt32();
+ RequiredMaxRepValue = questRecord[14].GetInt32();
+ SuggestedPlayers = questRecord[15].GetUInt32();
+ LimitTime = questRecord[16].GetUInt32();
+ QuestFlags = questRecord[17].GetUInt16();
+ uint32 SpecialFlags = questRecord[18].GetUInt16();
+ CharTitleId = questRecord[19].GetUInt32();
+ PrevQuestId = questRecord[20].GetInt32();
+ NextQuestId = questRecord[21].GetInt32();
+ ExclusiveGroup = questRecord[22].GetInt32();
+ NextQuestInChain = questRecord[23].GetUInt32();
+ SrcItemId = questRecord[24].GetUInt32();
+ SrcItemCount = questRecord[25].GetUInt32();
+ SrcSpell = questRecord[26].GetUInt32();
+ Title = questRecord[27].GetCppString();
+ Details = questRecord[28].GetCppString();
+ Objectives = questRecord[29].GetCppString();
+ OfferRewardText = questRecord[30].GetCppString();
+ RequestItemsText = questRecord[31].GetCppString();
+ EndText = questRecord[32].GetCppString();
+
+ for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i)
+ ObjectiveText[i] = questRecord[33+i].GetCppString();
+
+ for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i)
+ ReqItemId[i] = questRecord[37+i].GetUInt32();
+
+ for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i)
+ ReqItemCount[i] = questRecord[41+i].GetUInt32();
+
+ for (int i = 0; i < QUEST_SOURCE_ITEM_IDS_COUNT; ++i)
+ ReqSourceId[i] = questRecord[45+i].GetUInt32();
+
+ for (int i = 0; i < QUEST_SOURCE_ITEM_IDS_COUNT; ++i)
+ ReqSourceCount[i] = questRecord[49+i].GetUInt32();
+
+ for (int i = 0; i < QUEST_SOURCE_ITEM_IDS_COUNT; ++i)
+ ReqSourceRef[i] = questRecord[53+i].GetUInt32();
+
+ for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i)
+ ReqCreatureOrGOId[i] = questRecord[57+i].GetInt32();
+
+ for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i)
+ ReqCreatureOrGOCount[i] = questRecord[61+i].GetUInt32();
+
+ for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i)
+ ReqSpell[i] = questRecord[65+i].GetUInt32();
+
+ for (int i = 0; i < QUEST_REWARD_CHOICES_COUNT; ++i)
+ RewChoiceItemId[i] = questRecord[69+i].GetUInt32();
+
+ for (int i = 0; i < QUEST_REWARD_CHOICES_COUNT; ++i)
+ RewChoiceItemCount[i] = questRecord[75+i].GetUInt32();
+
+ for (int i = 0; i < QUEST_REWARDS_COUNT; ++i)
+ RewItemId[i] = questRecord[81+i].GetUInt32();
+
+ for (int i = 0; i < QUEST_REWARDS_COUNT; ++i)
+ RewItemCount[i] = questRecord[85+i].GetUInt32();
+
+ for (int i = 0; i < QUEST_REPUTATIONS_COUNT; ++i)
+ RewRepFaction[i] = questRecord[89+i].GetUInt32();
+
+ for (int i = 0; i < QUEST_REPUTATIONS_COUNT; ++i)
+ RewRepValue[i] = questRecord[94+i].GetInt32();
+
+ RewOrReqMoney = questRecord[99].GetInt32();
+ RewMoneyMaxLevel = questRecord[100].GetUInt32();
+ RewSpell = questRecord[101].GetUInt32();
+ RewSpellCast = questRecord[102].GetUInt32();
+ RewMailTemplateId = questRecord[103].GetUInt32();
+ RewMailDelaySecs = questRecord[104].GetUInt32();
+ PointMapId = questRecord[105].GetUInt32();
+ PointX = questRecord[106].GetFloat();
+ PointY = questRecord[107].GetFloat();
+ PointOpt = questRecord[108].GetUInt32();
+
+ for (int i = 0; i < QUEST_EMOTE_COUNT; ++i)
+ DetailsEmote[i] = questRecord[109+i].GetUInt32();
+
+ IncompleteEmote = questRecord[113].GetUInt32();
+ CompleteEmote = questRecord[114].GetUInt32();
+
+ for (int i = 0; i < QUEST_EMOTE_COUNT; ++i)
+ OfferRewardEmote[i] = questRecord[115+i].GetInt32();
+
+ QuestStartScript = questRecord[119].GetUInt32();
+ QuestCompleteScript = questRecord[120].GetUInt32();
+
+ QuestFlags |= SpecialFlags << 16;
+
+ m_reqitemscount = 0;
+ m_reqCreatureOrGOcount = 0;
+ m_rewitemscount = 0;
+ m_rewchoiceitemscount = 0;
+
+ for (int i=0; i < QUEST_OBJECTIVES_COUNT; i++)
+ {
+ if ( ReqItemId[i] )
+ ++m_reqitemscount;
+ if ( ReqCreatureOrGOId[i] )
+ ++m_reqCreatureOrGOcount;
+ }
+
+ for (int i=0; i < QUEST_REWARDS_COUNT; i++)
+ {
+ if ( RewItemId[i] )
+ ++m_rewitemscount;
+ }
+
+ for (int i=0; i < QUEST_REWARD_CHOICES_COUNT; i++)
+ {
+ if (RewChoiceItemId[i])
+ ++m_rewchoiceitemscount;
+ }
+}
+
+uint32 Quest::XPValue( Player *pPlayer ) const
+{
+ if( pPlayer )
+ {
+ if( RewMoneyMaxLevel > 0 )
+ {
+ uint32 pLevel = pPlayer->getLevel();
+ uint32 qLevel = QuestLevel;
+ float fullxp = 0;
+ if (qLevel >= 65)
+ fullxp = RewMoneyMaxLevel / 6.0f;
+ else if (qLevel == 64)
+ fullxp = RewMoneyMaxLevel / 4.8f;
+ else if (qLevel == 63)
+ fullxp = RewMoneyMaxLevel / 3.6f;
+ else if (qLevel == 62)
+ fullxp = RewMoneyMaxLevel / 2.4f;
+ else if (qLevel == 61)
+ fullxp = RewMoneyMaxLevel / 1.2f;
+ else if (qLevel > 0 && qLevel <= 60)
+ fullxp = RewMoneyMaxLevel / 0.6f;
+
+ if( pLevel <= qLevel + 5 )
+ return (uint32)fullxp;
+ else if( pLevel == qLevel + 6 )
+ return (uint32)(fullxp * 0.8f);
+ else if( pLevel == qLevel + 7 )
+ return (uint32)(fullxp * 0.6f);
+ else if( pLevel == qLevel + 8 )
+ return (uint32)(fullxp * 0.4f);
+ else if( pLevel == qLevel + 9 )
+ return (uint32)(fullxp * 0.2f);
+ else
+ return (uint32)(fullxp * 0.1f);
+ }
+ }
+ return 0;
+}
+
+int32 Quest::GetRewOrReqMoney() const
+{
+ if(RewOrReqMoney <=0)
+ return RewOrReqMoney;
+
+ return int32(RewOrReqMoney * sWorld.getRate(RATE_DROP_MONEY));
+}
diff --git a/src/game/QuestDef.h b/src/game/QuestDef.h index ddd24bf57f0..fda013337f0 100644 --- a/src/game/QuestDef.h +++ b/src/game/QuestDef.h @@ -1,330 +1,332 @@ -/* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef MANGOSSERVER_QUEST_H -#define MANGOSSERVER_QUEST_H - -#include "Platform/Define.h" -#include "Database/DatabaseEnv.h" - -#include <string> -#include <vector> - -class Player; - -class ObjectMgr; - -#define MAX_QUEST_LOG_SIZE 25 - -#define QUEST_OBJECTIVES_COUNT 4 -#define QUEST_SOURCE_ITEM_IDS_COUNT 4 -#define QUEST_REWARD_CHOICES_COUNT 6 -#define QUEST_REWARDS_COUNT 4 -#define QUEST_DEPLINK_COUNT 10 -#define QUEST_REPUTATIONS_COUNT 5 -#define QUEST_EMOTE_COUNT 4 - -enum QuestFailedReasons -{ - INVALIDREASON_DONT_HAVE_REQ = 0, - INVALIDREASON_QUEST_FAILED_LOW_LEVEL = 1, //You are not high enough level for that quest. - INVALIDREASON_QUEST_FAILED_WRONG_RACE = 6, //That quest is not available to your race. - INVALIDREASON_QUEST_ALREADY_DONE = 7, //You have completed that quest. - INVALIDREASON_QUEST_ONLY_ONE_TIMED = 12, //You can only be on one timed quest at a time. - INVALIDREASON_QUEST_ALREADY_ON = 13, //You are already on that quest - INVALIDREASON_QUEST_FAILED_EXPANSION = 16, //This quest requires an expansion enabled account. - INVALIDREASON_QUEST_ALREADY_ON2 = 18, //You are already on that quest - INVALIDREASON_QUEST_FAILED_MISSING_ITEMS = 21, //You don't have the required items with you. Check storage. - INVALIDREASON_QUEST_FAILED_NOT_ENOUGH_MONEY = 23, //You don't have enough money for that quest. - INVALIDREASON_DAILY_QUESTS_REMAINING = 26, //You have already completed 10 daily quests today - INVALIDREASON_QUEST_FAILED_CAIS = 27, //You cannot complete quests once you have reached tired time -}; - -enum QuestShareMessages -{ - QUEST_PARTY_MSG_SHARING_QUEST = 0, - QUEST_PARTY_MSG_CANT_TAKE_QUEST = 1, - QUEST_PARTY_MSG_ACCEPT_QUEST = 2, - QUEST_PARTY_MSG_REFUSE_QUEST = 3, - QUEST_PARTY_MSG_TOO_FAR = 4, - QUEST_PARTY_MSG_BUSY = 5, - QUEST_PARTY_MSG_LOG_FULL = 6, - QUEST_PARTY_MSG_HAVE_QUEST = 7, - QUEST_PARTY_MSG_FINISH_QUEST = 8, -}; - -enum __QuestTradeSkill -{ - QUEST_TRSKILL_NONE = 0, - QUEST_TRSKILL_ALCHEMY = 1, - QUEST_TRSKILL_BLACKSMITHING = 2, - QUEST_TRSKILL_COOKING = 3, - QUEST_TRSKILL_ENCHANTING = 4, - QUEST_TRSKILL_ENGINEERING = 5, - QUEST_TRSKILL_FIRSTAID = 6, - QUEST_TRSKILL_HERBALISM = 7, - QUEST_TRSKILL_LEATHERWORKING = 8, - QUEST_TRSKILL_POISONS = 9, - QUEST_TRSKILL_TAILORING = 10, - QUEST_TRSKILL_MINING = 11, - QUEST_TRSKILL_FISHING = 12, - QUEST_TRSKILL_SKINNING = 13, - QUEST_TRSKILL_JEWELCRAFTING = 14, -}; - -enum QuestStatus -{ - QUEST_STATUS_NONE = 0, - QUEST_STATUS_COMPLETE = 1, - QUEST_STATUS_UNAVAILABLE = 2, - QUEST_STATUS_INCOMPLETE = 3, - QUEST_STATUS_AVAILABLE = 4, - MAX_QUEST_STATUS -}; - -enum __QuestGiverStatus -{ - DIALOG_STATUS_NONE = 0, - DIALOG_STATUS_UNAVAILABLE = 1, - DIALOG_STATUS_CHAT = 2, - DIALOG_STATUS_INCOMPLETE = 3, - DIALOG_STATUS_REWARD_REP = 4, - DIALOG_STATUS_AVAILABLE_REP = 5, - DIALOG_STATUS_AVAILABLE = 6, - DIALOG_STATUS_REWARD2 = 7, // not yellow dot on minimap - DIALOG_STATUS_REWARD = 8 // yellow dot on minimap -}; - -enum __QuestFlags -{ - // Flags used at server and sended to client - QUEST_FLAGS_STAY_ALIVE = 1, // Not used currently - QUEST_FLAGS_EVENT = 2, // Not used currently - QUEST_FLAGS_EXPLORATION = 4, // Not used currently - QUEST_FLAGS_SHARABLE = 8, // Can be shared: Player::CanShareQuest() - //QUEST_FLAGS_NONE2 = 16, // Not used currently - QUEST_FLAGS_EPIC = 32, // Not used currently: Unsure of content - QUEST_FLAGS_RAID = 64, // Not used currently - QUEST_FLAGS_TBC = 128, // Not used currently: Available if TBC expension enabled only - QUEST_FLAGS_UNK2 = 256, // Not used currently: _DELIVER_MORE Quest needs more than normal _q-item_ drops from mobs - QUEST_FLAGS_HIDDEN_REWARDS = 512, // Items and money rewarded only sent in SMSG_QUESTGIVER_OFFER_REWARD (not in SMSG_QUESTGIVER_QUEST_DETAILS or in client quest log(SMSG_QUEST_QUERY_RESPONSE)) - QUEST_FLAGS_AUTO_REWARDED = 1024, // These quests are automatically rewarded on quest complete and they will never appear in quest log client side. - QUEST_FLAGS_TBC_RACES = 2048, // Not used currently: Bloodelf/draenei starting zone quests - QUEST_FLAGS_DAILY = 4096, // Used to know quest is Daily one - - // Mangos flags for set SpecialFlags in DB if required but used only at server - QUEST_MANGOS_FLAGS_REPEATABLE = 0x010000, // Set by 1 in SpecialFlags from DB - QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT = 0x020000, // Set by 2 in SpecialFlags from DB (if reequired area explore, spell SPELL_EFFECT_QUEST_COMPLETE casting, table `*_script` command SCRIPT_COMMAND_QUEST_EXPLORED use, set from script DLL) - QUEST_MANGOS_FLAGS_DB_ALLOWED = 0xFFFF | QUEST_MANGOS_FLAGS_REPEATABLE | QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT, - - // Mangos flags for internal use only - QUEST_MANGOS_FLAGS_DELIVER = 0x040000, // Internal flag computed only - QUEST_MANGOS_FLAGS_SPEAKTO = 0x080000, // Internal flag computed only - QUEST_MANGOS_FLAGS_KILL_OR_CAST = 0x100000, // Internal flag computed only - QUEST_MANGOS_FLAGS_TIMED = 0x200000, // Internal flag computed only -}; - -struct QuestLocale -{ - QuestLocale() { ObjectiveText.resize(QUEST_OBJECTIVES_COUNT); } - - std::vector<std::string> Title; - std::vector<std::string> Details; - std::vector<std::string> Objectives; - std::vector<std::string> OfferRewardText; - std::vector<std::string> RequestItemsText; - std::vector<std::string> EndText; - std::vector< std::vector<std::string> > ObjectiveText; -}; - -// This Quest class provides a convenient way to access a few pretotaled (cached) quest details, -// all base quest information, and any utility functions such as generating the amount of -// xp to give -class Quest -{ - friend class ObjectMgr; - public: - Quest(Field * questRecord); - uint32 XPValue( Player *pPlayer ) const; - - bool HasFlag( uint32 flag ) const { return ( QuestFlags & flag ) != 0; } - void SetFlag( uint32 flag ) { QuestFlags |= flag; } - - // table data accessors: - uint32 GetQuestId() const { return QuestId; } - int32 GetZoneOrSort() const { return ZoneOrSort; } - int32 GetSkillOrClass() const { return SkillOrClass; } - uint32 GetMinLevel() const { return MinLevel; } - uint32 GetQuestLevel() const { return QuestLevel; } - uint32 GetType() const { return Type; } - uint32 GetRequiredRaces() const { return RequiredRaces; } - uint32 GetRequiredSkillValue() const { return RequiredSkillValue; } - uint32 GetRepObjectiveFaction() const { return RepObjectiveFaction; } - int32 GetRepObjectiveValue() const { return RepObjectiveValue; } - uint32 GetRequiredMinRepFaction() const { return RequiredMinRepFaction; } - int32 GetRequiredMinRepValue() const { return RequiredMinRepValue; } - uint32 GetRequiredMaxRepFaction() const { return RequiredMaxRepFaction; } - int32 GetRequiredMaxRepValue() const { return RequiredMaxRepValue; } - uint32 GetSuggestedPlayers() const { return SuggestedPlayers; } - uint32 GetLimitTime() const { return LimitTime; } - int32 GetPrevQuestId() const { return PrevQuestId; } - int32 GetNextQuestId() const { return NextQuestId; } - int32 GetExclusiveGroup() const { return ExclusiveGroup; } - uint32 GetNextQuestInChain() const { return NextQuestInChain; } - uint32 GetCharTitleId() const { return CharTitleId; } - uint32 GetSrcItemId() const { return SrcItemId; } - uint32 GetSrcItemCount() const { return SrcItemCount; } - uint32 GetSrcSpell() const { return SrcSpell; } - std::string GetTitle() const { return Title; } - std::string GetDetails() const { return Details; } - std::string GetObjectives() const { return Objectives; } - std::string GetOfferRewardText() const { return OfferRewardText; } - std::string GetRequestItemsText() const { return RequestItemsText; } - std::string GetEndText() const { return EndText; } - int32 GetRewOrReqMoney() const; - uint32 GetRewMoneyMaxLevel() const { return RewMoneyMaxLevel; } - // use in XP calculation at client - uint32 GetRewSpell() const { return RewSpell; } - uint32 GetRewSpellCast() const { return RewSpellCast; } - uint32 GetRewMailTemplateId() const { return RewMailTemplateId; } - uint32 GetRewMailDelaySecs() const { return RewMailDelaySecs; } - uint32 GetPointMapId() const { return PointMapId; } - float GetPointX() const { return PointX; } - float GetPointY() const { return PointY; } - uint32 GetPointOpt() const { return PointOpt; } - uint32 GetIncompleteEmote() const { return IncompleteEmote; } - uint32 GetCompleteEmote() const { return CompleteEmote; } - uint32 GetQuestStartScript() const { return QuestStartScript; } - uint32 GetQuestCompleteScript() const { return QuestCompleteScript; } - bool IsRepeatable() const { return QuestFlags & QUEST_MANGOS_FLAGS_REPEATABLE; } - bool IsAutoComplete() const { return Objectives.empty(); } - uint32 GetFlags() const { return QuestFlags; } - bool IsDaily() const { return QuestFlags & QUEST_FLAGS_DAILY; } - - // multiple values - std::string ObjectiveText[QUEST_OBJECTIVES_COUNT]; - uint32 ReqItemId[QUEST_OBJECTIVES_COUNT]; - uint32 ReqItemCount[QUEST_OBJECTIVES_COUNT]; - uint32 ReqSourceId[QUEST_SOURCE_ITEM_IDS_COUNT]; - uint32 ReqSourceCount[QUEST_SOURCE_ITEM_IDS_COUNT]; - uint32 ReqSourceRef[QUEST_SOURCE_ITEM_IDS_COUNT]; - int32 ReqCreatureOrGOId[QUEST_OBJECTIVES_COUNT]; // >0 Creature <0 Gameobject - uint32 ReqCreatureOrGOCount[QUEST_OBJECTIVES_COUNT]; - uint32 ReqSpell[QUEST_OBJECTIVES_COUNT]; - uint32 RewChoiceItemId[QUEST_REWARD_CHOICES_COUNT]; - uint32 RewChoiceItemCount[QUEST_REWARD_CHOICES_COUNT]; - uint32 RewItemId[QUEST_REWARDS_COUNT]; - uint32 RewItemCount[QUEST_REWARDS_COUNT]; - uint32 RewRepFaction[QUEST_REPUTATIONS_COUNT]; - int32 RewRepValue[QUEST_REPUTATIONS_COUNT]; - uint32 DetailsEmote[QUEST_EMOTE_COUNT]; - uint32 OfferRewardEmote[QUEST_EMOTE_COUNT]; - - uint32 GetReqItemsCount() const { return m_reqitemscount; } - uint32 GetReqCreatureOrGOcount() const { return m_reqCreatureOrGOcount; } - uint32 GetRewChoiceItemsCount() const { return m_rewchoiceitemscount; } - uint32 GetRewItemsCount() const { return m_rewitemscount; } - - typedef std::vector<int32> PrevQuests; - PrevQuests prevQuests; - typedef std::vector<uint32> PrevChainQuests; - PrevChainQuests prevChainQuests; - - // cached data - private: - uint32 m_reqitemscount; - uint32 m_reqCreatureOrGOcount; - uint32 m_rewchoiceitemscount; - uint32 m_rewitemscount; - - // table data - protected: - uint32 QuestId; - int32 ZoneOrSort; - int32 SkillOrClass; - uint32 MinLevel; - uint32 QuestLevel; - uint32 Type; - uint32 RequiredRaces; - uint32 RequiredSkillValue; - uint32 RepObjectiveFaction; - int32 RepObjectiveValue; - uint32 RequiredMinRepFaction; - int32 RequiredMinRepValue; - uint32 RequiredMaxRepFaction; - int32 RequiredMaxRepValue; - uint32 SuggestedPlayers; - uint32 LimitTime; - uint32 QuestFlags; - uint32 CharTitleId; - int32 PrevQuestId; - int32 NextQuestId; - int32 ExclusiveGroup; - uint32 NextQuestInChain; - uint32 SrcItemId; - uint32 SrcItemCount; - uint32 SrcSpell; - std::string Title; - std::string Details; - std::string Objectives; - std::string OfferRewardText; - std::string RequestItemsText; - std::string EndText; - int32 RewOrReqMoney; - uint32 RewMoneyMaxLevel; - uint32 RewSpell; - uint32 RewSpellCast; - uint32 RewMailTemplateId; - uint32 RewMailDelaySecs; - uint32 PointMapId; - float PointX; - float PointY; - uint32 PointOpt; - uint32 IncompleteEmote; - uint32 CompleteEmote; - uint32 QuestStartScript; - uint32 QuestCompleteScript; -}; - -enum QuestUpdateState -{ - QUEST_UNCHANGED = 0, - QUEST_CHANGED = 1, - QUEST_NEW = 2 -}; - -struct QuestStatusData -{ - QuestStatusData() - : m_status(QUEST_STATUS_NONE),m_rewarded(false), - m_explored(false), m_timer(0), uState(QUEST_NEW) - { - memset(m_itemcount, 0, QUEST_OBJECTIVES_COUNT * sizeof(uint32)); - memset(m_creatureOrGOcount, 0, QUEST_OBJECTIVES_COUNT * sizeof(uint32)); - } - - QuestStatus m_status; - bool m_rewarded; - bool m_explored; - uint32 m_timer; - QuestUpdateState uState; - - uint32 m_itemcount[ QUEST_OBJECTIVES_COUNT ]; - uint32 m_creatureOrGOcount[ QUEST_OBJECTIVES_COUNT ]; -}; -#endif +/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef MANGOSSERVER_QUEST_H
+#define MANGOSSERVER_QUEST_H
+
+#include "Platform/Define.h"
+#include "Database/DatabaseEnv.h"
+
+#include <string>
+#include <vector>
+
+class Player;
+
+class ObjectMgr;
+
+#define MAX_QUEST_LOG_SIZE 25
+
+#define QUEST_OBJECTIVES_COUNT 4
+#define QUEST_SOURCE_ITEM_IDS_COUNT 4
+#define QUEST_REWARD_CHOICES_COUNT 6
+#define QUEST_REWARDS_COUNT 4
+#define QUEST_DEPLINK_COUNT 10
+#define QUEST_REPUTATIONS_COUNT 5
+#define QUEST_EMOTE_COUNT 4
+
+enum QuestFailedReasons
+{
+ INVALIDREASON_DONT_HAVE_REQ = 0,
+ INVALIDREASON_QUEST_FAILED_LOW_LEVEL = 1, //You are not high enough level for that quest.
+ INVALIDREASON_QUEST_FAILED_WRONG_RACE = 6, //That quest is not available to your race.
+ INVALIDREASON_QUEST_ALREADY_DONE = 7, //You have completed that quest.
+ INVALIDREASON_QUEST_ONLY_ONE_TIMED = 12, //You can only be on one timed quest at a time.
+ INVALIDREASON_QUEST_ALREADY_ON = 13, //You are already on that quest
+ INVALIDREASON_QUEST_FAILED_EXPANSION = 16, //This quest requires an expansion enabled account.
+ INVALIDREASON_QUEST_ALREADY_ON2 = 18, //You are already on that quest
+ INVALIDREASON_QUEST_FAILED_MISSING_ITEMS = 21, //You don't have the required items with you. Check storage.
+ INVALIDREASON_QUEST_FAILED_NOT_ENOUGH_MONEY = 23, //You don't have enough money for that quest.
+ INVALIDREASON_DAILY_QUESTS_REMAINING = 26, //You have already completed 10 daily quests today
+ INVALIDREASON_QUEST_FAILED_CAIS = 27, //You cannot complete quests once you have reached tired time
+};
+
+enum QuestShareMessages
+{
+ QUEST_PARTY_MSG_SHARING_QUEST = 0,
+ QUEST_PARTY_MSG_CANT_TAKE_QUEST = 1,
+ QUEST_PARTY_MSG_ACCEPT_QUEST = 2,
+ QUEST_PARTY_MSG_REFUSE_QUEST = 3,
+ QUEST_PARTY_MSG_TOO_FAR = 4,
+ QUEST_PARTY_MSG_BUSY = 5,
+ QUEST_PARTY_MSG_LOG_FULL = 6,
+ QUEST_PARTY_MSG_HAVE_QUEST = 7,
+ QUEST_PARTY_MSG_FINISH_QUEST = 8,
+};
+
+enum __QuestTradeSkill
+{
+ QUEST_TRSKILL_NONE = 0,
+ QUEST_TRSKILL_ALCHEMY = 1,
+ QUEST_TRSKILL_BLACKSMITHING = 2,
+ QUEST_TRSKILL_COOKING = 3,
+ QUEST_TRSKILL_ENCHANTING = 4,
+ QUEST_TRSKILL_ENGINEERING = 5,
+ QUEST_TRSKILL_FIRSTAID = 6,
+ QUEST_TRSKILL_HERBALISM = 7,
+ QUEST_TRSKILL_LEATHERWORKING = 8,
+ QUEST_TRSKILL_POISONS = 9,
+ QUEST_TRSKILL_TAILORING = 10,
+ QUEST_TRSKILL_MINING = 11,
+ QUEST_TRSKILL_FISHING = 12,
+ QUEST_TRSKILL_SKINNING = 13,
+ QUEST_TRSKILL_JEWELCRAFTING = 14,
+};
+
+enum QuestStatus
+{
+ QUEST_STATUS_NONE = 0,
+ QUEST_STATUS_COMPLETE = 1,
+ QUEST_STATUS_UNAVAILABLE = 2,
+ QUEST_STATUS_INCOMPLETE = 3,
+ QUEST_STATUS_AVAILABLE = 4,
+ MAX_QUEST_STATUS
+};
+
+enum __QuestGiverStatus
+{
+ DIALOG_STATUS_NONE = 0,
+ DIALOG_STATUS_UNAVAILABLE = 1,
+ DIALOG_STATUS_CHAT = 2,
+ DIALOG_STATUS_INCOMPLETE = 3,
+ DIALOG_STATUS_REWARD_REP = 4,
+ DIALOG_STATUS_AVAILABLE_REP = 5,
+ DIALOG_STATUS_AVAILABLE = 6,
+ DIALOG_STATUS_REWARD2 = 7, // not yellow dot on minimap
+ DIALOG_STATUS_REWARD = 8 // yellow dot on minimap
+};
+
+enum __QuestFlags
+{
+ // Flags used at server and sended to client
+ QUEST_FLAGS_STAY_ALIVE = 1, // Not used currently
+ QUEST_FLAGS_PARTY_ACCEPT = 2, // Not used currently. If player in party, all players that can accept this quest will receive confirmation box to accept quest CMSG_QUEST_CONFIRM_ACCEPT/SMSG_QUEST_CONFIRM_ACCEPT
+ QUEST_FLAGS_EXPLORATION = 4, // Not used currently
+ QUEST_FLAGS_SHARABLE = 8, // Can be shared: Player::CanShareQuest()
+ //QUEST_FLAGS_NONE2 = 16, // Not used currently
+ QUEST_FLAGS_EPIC = 32, // Not used currently: Unsure of content
+ QUEST_FLAGS_RAID = 64, // Not used currently
+ QUEST_FLAGS_TBC = 128, // Not used currently: Available if TBC expension enabled only
+ QUEST_FLAGS_UNK2 = 256, // Not used currently: _DELIVER_MORE Quest needs more than normal _q-item_ drops from mobs
+ QUEST_FLAGS_HIDDEN_REWARDS = 512, // Items and money rewarded only sent in SMSG_QUESTGIVER_OFFER_REWARD (not in SMSG_QUESTGIVER_QUEST_DETAILS or in client quest log(SMSG_QUEST_QUERY_RESPONSE))
+ QUEST_FLAGS_AUTO_REWARDED = 1024, // These quests are automatically rewarded on quest complete and they will never appear in quest log client side.
+ QUEST_FLAGS_TBC_RACES = 2048, // Not used currently: Bloodelf/draenei starting zone quests
+ QUEST_FLAGS_DAILY = 4096, // Used to know quest is Daily one
+
+ // Mangos flags for set SpecialFlags in DB if required but used only at server
+ QUEST_MANGOS_FLAGS_REPEATABLE = 0x010000, // Set by 1 in SpecialFlags from DB
+ QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT = 0x020000, // Set by 2 in SpecialFlags from DB (if reequired area explore, spell SPELL_EFFECT_QUEST_COMPLETE casting, table `*_script` command SCRIPT_COMMAND_QUEST_EXPLORED use, set from script DLL)
+ QUEST_MANGOS_FLAGS_DB_ALLOWED = 0xFFFF | QUEST_MANGOS_FLAGS_REPEATABLE | QUEST_MANGOS_FLAGS_EXPLORATION_OR_EVENT,
+
+ // Mangos flags for internal use only
+ QUEST_MANGOS_FLAGS_DELIVER = 0x040000, // Internal flag computed only
+ QUEST_MANGOS_FLAGS_SPEAKTO = 0x080000, // Internal flag computed only
+ QUEST_MANGOS_FLAGS_KILL_OR_CAST = 0x100000, // Internal flag computed only
+ QUEST_MANGOS_FLAGS_TIMED = 0x200000, // Internal flag computed only
+};
+
+struct QuestLocale
+{
+ QuestLocale() { ObjectiveText.resize(QUEST_OBJECTIVES_COUNT); }
+
+ std::vector<std::string> Title;
+ std::vector<std::string> Details;
+ std::vector<std::string> Objectives;
+ std::vector<std::string> OfferRewardText;
+ std::vector<std::string> RequestItemsText;
+ std::vector<std::string> EndText;
+ std::vector< std::vector<std::string> > ObjectiveText;
+};
+
+// This Quest class provides a convenient way to access a few pretotaled (cached) quest details,
+// all base quest information, and any utility functions such as generating the amount of
+// xp to give
+class Quest
+{
+ friend class ObjectMgr;
+ public:
+ Quest(Field * questRecord);
+ uint32 XPValue( Player *pPlayer ) const;
+
+ bool HasFlag( uint32 flag ) const { return ( QuestFlags & flag ) != 0; }
+ void SetFlag( uint32 flag ) { QuestFlags |= flag; }
+
+ // table data accessors:
+ uint32 GetQuestId() const { return QuestId; }
+ uint32 GetQuestMethod() const { return QuestMethod; }
+ int32 GetZoneOrSort() const { return ZoneOrSort; }
+ int32 GetSkillOrClass() const { return SkillOrClass; }
+ uint32 GetMinLevel() const { return MinLevel; }
+ uint32 GetQuestLevel() const { return QuestLevel; }
+ uint32 GetType() const { return Type; }
+ uint32 GetRequiredRaces() const { return RequiredRaces; }
+ uint32 GetRequiredSkillValue() const { return RequiredSkillValue; }
+ uint32 GetRepObjectiveFaction() const { return RepObjectiveFaction; }
+ int32 GetRepObjectiveValue() const { return RepObjectiveValue; }
+ uint32 GetRequiredMinRepFaction() const { return RequiredMinRepFaction; }
+ int32 GetRequiredMinRepValue() const { return RequiredMinRepValue; }
+ uint32 GetRequiredMaxRepFaction() const { return RequiredMaxRepFaction; }
+ int32 GetRequiredMaxRepValue() const { return RequiredMaxRepValue; }
+ uint32 GetSuggestedPlayers() const { return SuggestedPlayers; }
+ uint32 GetLimitTime() const { return LimitTime; }
+ int32 GetPrevQuestId() const { return PrevQuestId; }
+ int32 GetNextQuestId() const { return NextQuestId; }
+ int32 GetExclusiveGroup() const { return ExclusiveGroup; }
+ uint32 GetNextQuestInChain() const { return NextQuestInChain; }
+ uint32 GetCharTitleId() const { return CharTitleId; }
+ uint32 GetSrcItemId() const { return SrcItemId; }
+ uint32 GetSrcItemCount() const { return SrcItemCount; }
+ uint32 GetSrcSpell() const { return SrcSpell; }
+ std::string GetTitle() const { return Title; }
+ std::string GetDetails() const { return Details; }
+ std::string GetObjectives() const { return Objectives; }
+ std::string GetOfferRewardText() const { return OfferRewardText; }
+ std::string GetRequestItemsText() const { return RequestItemsText; }
+ std::string GetEndText() const { return EndText; }
+ int32 GetRewOrReqMoney() const;
+ uint32 GetRewMoneyMaxLevel() const { return RewMoneyMaxLevel; }
+ // use in XP calculation at client
+ uint32 GetRewSpell() const { return RewSpell; }
+ uint32 GetRewSpellCast() const { return RewSpellCast; }
+ uint32 GetRewMailTemplateId() const { return RewMailTemplateId; }
+ uint32 GetRewMailDelaySecs() const { return RewMailDelaySecs; }
+ uint32 GetPointMapId() const { return PointMapId; }
+ float GetPointX() const { return PointX; }
+ float GetPointY() const { return PointY; }
+ uint32 GetPointOpt() const { return PointOpt; }
+ uint32 GetIncompleteEmote() const { return IncompleteEmote; }
+ uint32 GetCompleteEmote() const { return CompleteEmote; }
+ uint32 GetQuestStartScript() const { return QuestStartScript; }
+ uint32 GetQuestCompleteScript() const { return QuestCompleteScript; }
+ bool IsRepeatable() const { return QuestFlags & QUEST_MANGOS_FLAGS_REPEATABLE; }
+ bool IsAutoComplete() const { return QuestMethod ? false : true; }
+ uint32 GetFlags() const { return QuestFlags; }
+ bool IsDaily() const { return QuestFlags & QUEST_FLAGS_DAILY; }
+
+ // multiple values
+ std::string ObjectiveText[QUEST_OBJECTIVES_COUNT];
+ uint32 ReqItemId[QUEST_OBJECTIVES_COUNT];
+ uint32 ReqItemCount[QUEST_OBJECTIVES_COUNT];
+ uint32 ReqSourceId[QUEST_SOURCE_ITEM_IDS_COUNT];
+ uint32 ReqSourceCount[QUEST_SOURCE_ITEM_IDS_COUNT];
+ uint32 ReqSourceRef[QUEST_SOURCE_ITEM_IDS_COUNT];
+ int32 ReqCreatureOrGOId[QUEST_OBJECTIVES_COUNT]; // >0 Creature <0 Gameobject
+ uint32 ReqCreatureOrGOCount[QUEST_OBJECTIVES_COUNT];
+ uint32 ReqSpell[QUEST_OBJECTIVES_COUNT];
+ uint32 RewChoiceItemId[QUEST_REWARD_CHOICES_COUNT];
+ uint32 RewChoiceItemCount[QUEST_REWARD_CHOICES_COUNT];
+ uint32 RewItemId[QUEST_REWARDS_COUNT];
+ uint32 RewItemCount[QUEST_REWARDS_COUNT];
+ uint32 RewRepFaction[QUEST_REPUTATIONS_COUNT];
+ int32 RewRepValue[QUEST_REPUTATIONS_COUNT];
+ uint32 DetailsEmote[QUEST_EMOTE_COUNT];
+ uint32 OfferRewardEmote[QUEST_EMOTE_COUNT];
+
+ uint32 GetReqItemsCount() const { return m_reqitemscount; }
+ uint32 GetReqCreatureOrGOcount() const { return m_reqCreatureOrGOcount; }
+ uint32 GetRewChoiceItemsCount() const { return m_rewchoiceitemscount; }
+ uint32 GetRewItemsCount() const { return m_rewitemscount; }
+
+ typedef std::vector<int32> PrevQuests;
+ PrevQuests prevQuests;
+ typedef std::vector<uint32> PrevChainQuests;
+ PrevChainQuests prevChainQuests;
+
+ // cached data
+ private:
+ uint32 m_reqitemscount;
+ uint32 m_reqCreatureOrGOcount;
+ uint32 m_rewchoiceitemscount;
+ uint32 m_rewitemscount;
+
+ // table data
+ protected:
+ uint32 QuestId;
+ uint32 QuestMethod;
+ int32 ZoneOrSort;
+ int32 SkillOrClass;
+ uint32 MinLevel;
+ uint32 QuestLevel;
+ uint32 Type;
+ uint32 RequiredRaces;
+ uint32 RequiredSkillValue;
+ uint32 RepObjectiveFaction;
+ int32 RepObjectiveValue;
+ uint32 RequiredMinRepFaction;
+ int32 RequiredMinRepValue;
+ uint32 RequiredMaxRepFaction;
+ int32 RequiredMaxRepValue;
+ uint32 SuggestedPlayers;
+ uint32 LimitTime;
+ uint32 QuestFlags;
+ uint32 CharTitleId;
+ int32 PrevQuestId;
+ int32 NextQuestId;
+ int32 ExclusiveGroup;
+ uint32 NextQuestInChain;
+ uint32 SrcItemId;
+ uint32 SrcItemCount;
+ uint32 SrcSpell;
+ std::string Title;
+ std::string Details;
+ std::string Objectives;
+ std::string OfferRewardText;
+ std::string RequestItemsText;
+ std::string EndText;
+ int32 RewOrReqMoney;
+ uint32 RewMoneyMaxLevel;
+ uint32 RewSpell;
+ uint32 RewSpellCast;
+ uint32 RewMailTemplateId;
+ uint32 RewMailDelaySecs;
+ uint32 PointMapId;
+ float PointX;
+ float PointY;
+ uint32 PointOpt;
+ uint32 IncompleteEmote;
+ uint32 CompleteEmote;
+ uint32 QuestStartScript;
+ uint32 QuestCompleteScript;
+};
+
+enum QuestUpdateState
+{
+ QUEST_UNCHANGED = 0,
+ QUEST_CHANGED = 1,
+ QUEST_NEW = 2
+};
+
+struct QuestStatusData
+{
+ QuestStatusData()
+ : m_status(QUEST_STATUS_NONE),m_rewarded(false),
+ m_explored(false), m_timer(0), uState(QUEST_NEW)
+ {
+ memset(m_itemcount, 0, QUEST_OBJECTIVES_COUNT * sizeof(uint32));
+ memset(m_creatureOrGOcount, 0, QUEST_OBJECTIVES_COUNT * sizeof(uint32));
+ }
+
+ QuestStatus m_status;
+ bool m_rewarded;
+ bool m_explored;
+ uint32 m_timer;
+ QuestUpdateState uState;
+
+ uint32 m_itemcount[ QUEST_OBJECTIVES_COUNT ];
+ uint32 m_creatureOrGOcount[ QUEST_OBJECTIVES_COUNT ];
+};
+#endif
diff --git a/src/game/RandomMovementGenerator.cpp b/src/game/RandomMovementGenerator.cpp index 1022a0699f6..754e76d8267 100644 --- a/src/game/RandomMovementGenerator.cpp +++ b/src/game/RandomMovementGenerator.cpp @@ -1,159 +1,163 @@ -/* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "Creature.h" -#include "MapManager.h" -#include "RandomMovementGenerator.h" -#include "DestinationHolderImp.h" -#include "Map.h" -#include "Util.h" - -#define RUNNING_CHANCE_RANDOMMV 20 //will be "1 / RUNNING_CHANCE_RANDOMMV" - -template<> -void -RandomMovementGenerator<Creature>::_setRandomLocation(Creature &creature) -{ - float X,Y,Z,z,nx,ny,nz,wander_distance,ori,dist; - - creature.GetRespawnCoord(X, Y, Z); - creature.GetRespawnCoord(X, Y, Z, &ori, &wander_distance); - - z = creature.GetPositionZ(); - uint32 mapid=creature.GetMapId(); - Map const* map = MapManager::Instance().GetBaseMap(mapid); - - // For 2D/3D system selection - bool is_land_ok = creature.canWalk(); - bool is_water_ok = creature.canSwim(); - bool is_air_ok = creature.canFly(); - - const float angle = rand_norm()*(M_PI*2); - const float range = rand_norm()*wander_distance; - const float distanceX = range * cos(angle); - const float distanceY = range * sin(angle); - - nx = X + distanceX; - ny = Y + distanceY; - dist = distanceX*distanceX + distanceY*distanceY; - - if (is_air_ok) // 3D system above ground and above water (flying mode) - { - const float distanceZ = rand_norm() * sqrtf(dist)/2; // Limit height change - nz = Z + distanceZ; - float tz = map->GetHeight(nx, ny, nz-2.0f, false); // Map check only, vmap needed here but need to alter vmaps checks for height. - float wz = map->GetWaterLevel(nx, ny); - if (tz >= nz || wz >= nz) - return; // Problem here, we must fly above the ground and water, not under. Let's try on next tick - } - //else if (is_water_ok) // 3D system under water and above ground (swimming mode) - else // 2D only - { - dist = dist>=100.0f ? 10.0f : sqrtf(dist); // 10.0 is the max that vmap high can check (MAX_CAN_FALL_DISTANCE) - // The fastest way to get an accurate result 90% of the time. - // Better result can be obtained like 99% accuracy with a ray light, but the cost is too high and the code is too long. - nz = map->GetHeight(nx,ny,Z+dist-2.0f,false); // Map check - if (fabs(nz-Z)>dist) - { - nz = map->GetHeight(nx,ny,Z-2.0f,true); // Vmap Horizontal or above - if (fabs(nz-Z)>dist) - { - nz = map->GetHeight(nx,ny,Z+dist-2.0f,true); // Vmap Higher - if (fabs(nz-Z)>dist) - return; // let's forget this bad coords where a z cannot be find and retry at next tick - } - } - } - - Traveller<Creature> traveller(creature); - creature.SetOrientation(creature.GetAngle(nx,ny)); - i_destinationHolder.SetDestination(traveller, nx, ny, nz); - creature.addUnitState(UNIT_STAT_ROAMING); - if (is_air_ok) - { - i_nextMoveTime.Reset(i_destinationHolder.GetTotalTravelTime()); - creature.SetUnitMovementFlags(MOVEMENTFLAG_FLYING2); - } - //else if (is_water_ok) // Swimming mode to be done with more than this check - else - { - i_nextMoveTime.Reset(urand(500+i_destinationHolder.GetTotalTravelTime(),5000+i_destinationHolder.GetTotalTravelTime())); - creature.SetUnitMovementFlags(MOVEMENTFLAG_WALK_MODE); - } -} - -template<> -void -RandomMovementGenerator<Creature>::Initialize(Creature &creature) -{ - if(!creature.isAlive()) - return; - - if (creature.canFly()) - creature.SetUnitMovementFlags(MOVEMENTFLAG_FLYING2); - else - creature.SetUnitMovementFlags(irand(0,RUNNING_CHANCE_RANDOMMV) > 0 ? MOVEMENTFLAG_WALK_MODE : MOVEMENTFLAG_NONE ); - _setRandomLocation(creature); -} - -template<> -void -RandomMovementGenerator<Creature>::Reset(Creature &creature) -{ - Initialize(creature); -} - -template<> -bool -RandomMovementGenerator<Creature>::Update(Creature &creature, const uint32 &diff) -{ - if(creature.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNDED | UNIT_STAT_DISTRACTED)) - { - i_nextMoveTime.Update(i_nextMoveTime.GetExpiry()); // Expire the timer - creature.clearUnitState(UNIT_STAT_ROAMING); - return true; - } - - i_nextMoveTime.Update(diff); - - if(i_destinationHolder.HasArrived() && !creature.IsStopped() && !creature.canFly()) - creature.clearUnitState(UNIT_STAT_ROAMING); - - if(!i_destinationHolder.HasArrived() && creature.IsStopped()) - creature.addUnitState(UNIT_STAT_ROAMING); - - CreatureTraveller traveller(creature); - - if( i_destinationHolder.UpdateTraveller(traveller, diff, false, true) ) - { - if(i_nextMoveTime.Passed()) - { - if (creature.canFly()) - creature.SetUnitMovementFlags(MOVEMENTFLAG_FLYING2); - else - creature.SetUnitMovementFlags(irand(0,RUNNING_CHANCE_RANDOMMV) > 0 ? MOVEMENTFLAG_WALK_MODE : MOVEMENTFLAG_NONE); - _setRandomLocation(creature); - } - else if(creature.isPet() && creature.GetOwner() && creature.GetDistance(creature.GetOwner()) > PET_FOLLOW_DIST+2.5f) - { - creature.SetUnitMovementFlags(MOVEMENTFLAG_NONE); - _setRandomLocation(creature); - } - } - return true; -} +/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "Creature.h"
+#include "MapManager.h"
+#include "RandomMovementGenerator.h"
+#include "DestinationHolderImp.h"
+#include "Map.h"
+#include "Util.h"
+
+#define RUNNING_CHANCE_RANDOMMV 20 //will be "1 / RUNNING_CHANCE_RANDOMMV"
+
+template<>
+void
+RandomMovementGenerator<Creature>::_setRandomLocation(Creature &creature)
+{
+ float X,Y,Z,z,nx,ny,nz,wander_distance,ori,dist;
+
+ creature.GetRespawnCoord(X, Y, Z, &ori, &wander_distance);
+
+ z = creature.GetPositionZ();
+ uint32 mapid=creature.GetMapId();
+ Map const* map = MapManager::Instance().GetBaseMap(mapid);
+
+ // For 2D/3D system selection
+ bool is_land_ok = creature.canWalk();
+ bool is_water_ok = creature.canSwim();
+ bool is_air_ok = creature.canFly();
+
+ const float angle = rand_norm()*(M_PI*2);
+ const float range = rand_norm()*wander_distance;
+ const float distanceX = range * cos(angle);
+ const float distanceY = range * sin(angle);
+
+ nx = X + distanceX;
+ ny = Y + distanceY;
+
+ // prevent invalid coordinates generation
+ MaNGOS::NormalizeMapCoord(nx);
+ MaNGOS::NormalizeMapCoord(ny);
+
+ dist = distanceX*distanceX + distanceY*distanceY;
+
+ if (is_air_ok) // 3D system above ground and above water (flying mode)
+ {
+ const float distanceZ = rand_norm() * sqrtf(dist)/2; // Limit height change
+ nz = Z + distanceZ;
+ float tz = map->GetHeight(nx, ny, nz-2.0f, false); // Map check only, vmap needed here but need to alter vmaps checks for height.
+ float wz = map->GetWaterLevel(nx, ny);
+ if (tz >= nz || wz >= nz)
+ return; // Problem here, we must fly above the ground and water, not under. Let's try on next tick
+ }
+ //else if (is_water_ok) // 3D system under water and above ground (swimming mode)
+ else // 2D only
+ {
+ dist = dist>=100.0f ? 10.0f : sqrtf(dist); // 10.0 is the max that vmap high can check (MAX_CAN_FALL_DISTANCE)
+ // The fastest way to get an accurate result 90% of the time.
+ // Better result can be obtained like 99% accuracy with a ray light, but the cost is too high and the code is too long.
+ nz = map->GetHeight(nx,ny,Z+dist-2.0f,false); // Map check
+ if (fabs(nz-Z)>dist)
+ {
+ nz = map->GetHeight(nx,ny,Z-2.0f,true); // Vmap Horizontal or above
+ if (fabs(nz-Z)>dist)
+ {
+ nz = map->GetHeight(nx,ny,Z+dist-2.0f,true); // Vmap Higher
+ if (fabs(nz-Z)>dist)
+ return; // let's forget this bad coords where a z cannot be find and retry at next tick
+ }
+ }
+ }
+
+ Traveller<Creature> traveller(creature);
+ creature.SetOrientation(creature.GetAngle(nx,ny));
+ i_destinationHolder.SetDestination(traveller, nx, ny, nz);
+ creature.addUnitState(UNIT_STAT_ROAMING);
+ if (is_air_ok)
+ {
+ i_nextMoveTime.Reset(i_destinationHolder.GetTotalTravelTime());
+ creature.AddUnitMovementFlag(MOVEMENTFLAG_FLYING2);
+ }
+ //else if (is_water_ok) // Swimming mode to be done with more than this check
+ else
+ {
+ i_nextMoveTime.Reset(urand(500+i_destinationHolder.GetTotalTravelTime(),5000+i_destinationHolder.GetTotalTravelTime()));
+ creature.SetUnitMovementFlags(MOVEMENTFLAG_WALK_MODE);
+ }
+}
+
+template<>
+void
+RandomMovementGenerator<Creature>::Initialize(Creature &creature)
+{
+ if(!creature.isAlive())
+ return;
+
+ if (creature.canFly())
+ creature.AddUnitMovementFlag(MOVEMENTFLAG_FLYING2);
+ else
+ creature.SetUnitMovementFlags(irand(0,RUNNING_CHANCE_RANDOMMV) > 0 ? MOVEMENTFLAG_WALK_MODE : MOVEMENTFLAG_NONE );
+ _setRandomLocation(creature);
+}
+
+template<>
+void
+RandomMovementGenerator<Creature>::Reset(Creature &creature)
+{
+ Initialize(creature);
+}
+
+template<>
+bool
+RandomMovementGenerator<Creature>::Update(Creature &creature, const uint32 &diff)
+{
+ if(creature.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNDED | UNIT_STAT_DISTRACTED))
+ {
+ i_nextMoveTime.Update(i_nextMoveTime.GetExpiry()); // Expire the timer
+ creature.clearUnitState(UNIT_STAT_ROAMING);
+ return true;
+ }
+
+ i_nextMoveTime.Update(diff);
+
+ if(i_destinationHolder.HasArrived() && !creature.IsStopped() && !creature.canFly())
+ creature.clearUnitState(UNIT_STAT_ROAMING);
+
+ if(!i_destinationHolder.HasArrived() && creature.IsStopped())
+ creature.addUnitState(UNIT_STAT_ROAMING);
+
+ CreatureTraveller traveller(creature);
+
+ if( i_destinationHolder.UpdateTraveller(traveller, diff, false, true) )
+ {
+ if(i_nextMoveTime.Passed())
+ {
+ if (creature.canFly())
+ creature.AddUnitMovementFlag(MOVEMENTFLAG_FLYING2);
+ else
+ creature.SetUnitMovementFlags(irand(0,RUNNING_CHANCE_RANDOMMV) > 0 ? MOVEMENTFLAG_WALK_MODE : MOVEMENTFLAG_NONE);
+ _setRandomLocation(creature);
+ }
+ else if(creature.isPet() && creature.GetOwner() && creature.GetDistance(creature.GetOwner()) > PET_FOLLOW_DIST+2.5f)
+ {
+ creature.SetUnitMovementFlags(MOVEMENTFLAG_NONE);
+ _setRandomLocation(creature);
+ }
+ }
+ return true;
+}
diff --git a/src/game/Spell.cpp b/src/game/Spell.cpp index 7cefcc1de8f..3be0b6989b2 100644 --- a/src/game/Spell.cpp +++ b/src/game/Spell.cpp @@ -3681,6 +3681,11 @@ uint8 Spell::CanCast(bool strict) (!m_targets.getItemTarget() || !m_targets.getItemTarget()->GetProto()->LockID || m_targets.getItemTarget()->GetOwner() != m_caster ) )
return SPELL_FAILED_BAD_TARGETS;
+ // In BattleGround players can use only flags and banners
+ if( ((Player*)m_caster)->InBattleGround() &&
+ !((Player*)m_caster)->isAllowUseBattleGroundObject() )
+ return SPELL_FAILED_TRY_AGAIN;
+
// get the lock entry
LockEntry const *lockInfo = NULL;
if (GameObject* go=m_targets.getGOTarget())
diff --git a/src/game/SpellEffects.cpp b/src/game/SpellEffects.cpp index fd6410e55ac..9816f683b05 100644 --- a/src/game/SpellEffects.cpp +++ b/src/game/SpellEffects.cpp @@ -52,6 +52,8 @@ #include "Language.h"
#include "SocialMgr.h"
#include "Util.h"
+#include "TemporarySummon.h"
+
pEffect SpellEffects[TOTAL_SPELL_EFFECTS]=
{
@@ -1006,10 +1008,78 @@ void Spell::EffectDummy(uint32 i) m_caster->CastSpell(m_caster,42337,true,NULL);
return;
}
+ case 37573: //Temporal Phase Modulator
+ {
+ if(!unitTarget)
+ return;
+
+ TemporarySummon* tempSummon = dynamic_cast<TemporarySummon*>(unitTarget);
+ if(!tempSummon)
+ return;
+
+ uint32 health = tempSummon->GetHealth();
+ const uint32 entry_list[6] = {21821, 21820, 21817};
+
+ float x = tempSummon->GetPositionX();
+ float y = tempSummon->GetPositionY();
+ float z = tempSummon->GetPositionZ();
+ float o = tempSummon->GetOrientation();
+
+ tempSummon->UnSummon();
+
+ Creature* pCreature = m_caster->SummonCreature(entry_list[urand(0, 2)], x, y, z, o,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,180000);
+ if (!pCreature)
+ return;
+
+ pCreature->SetHealth(health);
+
+ if(pCreature->AI())
+ pCreature->AI()->AttackStart(m_caster);
+
+ return;
+ }
+ case 34665: //Administer Antidote
+ {
+ if(!unitTarget || m_caster->GetTypeId() != TYPEID_PLAYER )
+ return;
+
+ if(!unitTarget)
+ return;
+
+ TemporarySummon* tempSummon = dynamic_cast<TemporarySummon*>(unitTarget);
+ if(!tempSummon)
+ return;
+
+ uint32 health = tempSummon->GetHealth();
+
+ float x = tempSummon->GetPositionX();
+ float y = tempSummon->GetPositionY();
+ float z = tempSummon->GetPositionZ();
+ float o = tempSummon->GetOrientation();
+ tempSummon->UnSummon();
+
+ Creature* pCreature = m_caster->SummonCreature(16992, x, y, z, o,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,180000);
+ if (!pCreature)
+ return;
+
+ pCreature->SetHealth(health);
+ ((Player*)m_caster)->KilledMonster(16992,pCreature->GetGUID());
+
+ if (pCreature->AI())
+ pCreature->AI()->AttackStart(m_caster);
+
+ return;
+ }
+ case 44997: // Converting Sentry
+ {
+ //Converted Sentry Credit
+ m_caster->CastSpell(m_caster, 45009, true);
+ return;
+ }
case 45030: // Impale Emissary
{
// Emissary of Hate Credit
- m_caster->CastSpell(m_caster,45088,true);
+ m_caster->CastSpell(m_caster, 45088, true);
return;
}
case 50243: // Teach Language
@@ -1102,6 +1172,16 @@ void Spell::EffectDummy(uint32 i) }
return;
}
+ case 32826:
+ {
+ if ( unitTarget && unitTarget->GetTypeId() == TYPEID_UNIT )
+ {
+ //Polymorph Cast Visual Rank 1
+ const uint32 spell_list[6] = {32813, 32816, 32817, 32818, 32819, 32820};
+ unitTarget->CastSpell( unitTarget, spell_list[urand(0, 5)], true);
+ }
+ return;
+ }
}
break;
case SPELLFAMILY_WARRIOR:
@@ -2760,27 +2840,26 @@ void Spell::EffectOpenLock(uint32 /*i*/) if( goInfo->type == GAMEOBJECT_TYPE_BUTTON && goInfo->button.noDamageImmune ||
goInfo->type == GAMEOBJECT_TYPE_GOOBER && goInfo->goober.losOK )
{
- if(BattleGround *bg = player->GetBattleGround())// in battleground
+ //isAllowUseBattleGroundObject() already called in CanCast()
+ // in battleground check
+ if(BattleGround *bg = player->GetBattleGround())
{
- if( !player->IsMounted() && // not mounted
- !player->HasStealthAura() && // not stealthed
- !player->HasInvisibilityAura() && // not invisible
- player->isAlive() ) // live player
- {
- // check if it's correct bg
- if(bg && bg->GetTypeID() == BATTLEGROUND_AB)
- bg->EventPlayerClickedOnFlag(player, gameObjTarget);
-
- return;
- }
+ // check if it's correct bg
+ if(bg && bg->GetTypeID() == BATTLEGROUND_AB)
+ bg->EventPlayerClickedOnFlag(player, gameObjTarget);
+ return;
}
}
else if (goInfo->type == GAMEOBJECT_TYPE_FLAGSTAND)
{
+ //isAllowUseBattleGroundObject() already called in CanCast()
+ // in battleground check
if(BattleGround *bg = player->GetBattleGround())
+ {
if(bg->GetTypeID() == BATTLEGROUND_EY)
bg->EventPlayerClickedOnFlag(player, gameObjTarget);
- return;
+ return;
+ }
}
lockId = gameObjTarget->GetLockId();
guid = gameObjTarget->GetGUID();
@@ -4751,6 +4830,16 @@ void Spell::EffectScriptEffect(uint32 effIndex) unitTarget->CastSpell(unitTarget, spellId, true);
break;
}
+ //5,000 Gold
+ case 46642:
+ {
+ if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ ((Player*)unitTarget)->ModifyMoney(50000000);
+
+ break;
+ }
}
if( m_spellInfo->SpellFamilyName == SPELLFAMILY_PALADIN )
diff --git a/src/game/TargetedMovementGenerator.cpp b/src/game/TargetedMovementGenerator.cpp index bba7426938d..9d465877a2a 100644 --- a/src/game/TargetedMovementGenerator.cpp +++ b/src/game/TargetedMovementGenerator.cpp @@ -1,202 +1,211 @@ -/* - * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "ByteBuffer.h" -#include "TargetedMovementGenerator.h" -#include "Errors.h" -#include "Creature.h" -#include "MapManager.h" -#include "DestinationHolderImp.h" -#include "World.h" - -#define SMALL_ALPHA 0.05f - -#include <cmath> -/* -struct StackCleaner -{ - Creature &i_creature; - StackCleaner(Creature &creature) : i_creature(creature) {} - void Done(void) { i_creature.StopMoving(); } - ~StackCleaner() - { - i_creature->Clear(); - } -}; -*/ - -template<class T> -void -TargetedMovementGenerator<T>::_setTargetLocation(T &owner) -{ - if( !i_target.isValid() || !&owner ) - return; - - if( owner.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNDED | UNIT_STAT_DISTRACTED) ) - return; - - // prevent redundant micro-movement for pets, other followers. - if(i_offset && i_target->IsWithinDistInMap(&owner,2*i_offset)) - return; - - float x, y, z; - if(!i_offset) - { - // to nearest contact position - i_target->GetContactPoint( &owner, x, y, z ); - } - else - { - // to at i_offset distance from target and i_angle from target facing - i_target->GetClosePoint(x,y,z,owner.GetObjectSize(),i_offset,i_angle); - } - - /* - We MUST not check the distance difference and avoid setting the new location for smaller distances. - By that we risk having far too many GetContactPoint() calls freezing the whole system. - In TargetedMovementGenerator<T>::Update() we check the distance to the target and at - some range we calculate a new position. The calculation takes some processor cycles due to vmaps. - If the distance to the target it too large to ignore, - but the distance to the new contact point is short enough to be ignored, - we will calculate a new contact point each update loop, but will never move to it. - The system will freeze. - ralf - - //We don't update Mob Movement, if the difference between New destination and last destination is < BothObjectSize - float bothObjectSize = i_target->GetObjectSize() + owner.GetObjectSize() + CONTACT_DISTANCE; - if( i_destinationHolder.HasDestination() && i_destinationHolder.GetDestinationDiff(x,y,z) < bothObjectSize ) - return; - */ - Traveller<T> traveller(owner); - i_destinationHolder.SetDestination(traveller, x, y, z); - owner.addUnitState(UNIT_STAT_CHASE); -} - -template<class T> -void -TargetedMovementGenerator<T>::Initialize(T &owner) -{ - if(!&owner) - return; - owner.RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE); - _setTargetLocation(owner); -} - -template<class T> -void -TargetedMovementGenerator<T>::Finalize(T &owner) -{ - owner.clearUnitState(UNIT_STAT_CHASE); -} - -template<class T> -void -TargetedMovementGenerator<T>::Reset(T &owner) -{ - Initialize(owner); -} - -template<class T> -bool -TargetedMovementGenerator<T>::Update(T &owner, const uint32 & time_diff) -{ - if(!i_target.isValid()) - return false; - - if( !&owner || !owner.isAlive()) - return true; - - if( owner.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNDED | UNIT_STAT_FLEEING | UNIT_STAT_DISTRACTED) ) - return true; - - // prevent movement while casting spells with cast time or channel time - if ( owner.IsNonMeleeSpellCasted(false, false, true)) - { - if (!owner.IsStopped()) - owner.StopMoving(); - return true; - } - - // prevent crash after creature killed pet - if (!owner.hasUnitState(UNIT_STAT_FOLLOW) && owner.getVictim() != i_target.getTarget()) - return true; - - Traveller<T> traveller(owner); - - if( !i_destinationHolder.HasDestination() ) - _setTargetLocation(owner); - if( owner.IsStopped() && !i_destinationHolder.HasArrived() ) - { - owner.addUnitState(UNIT_STAT_CHASE); - i_destinationHolder.StartTravel(traveller); - return true; - } - - if (i_destinationHolder.UpdateTraveller(traveller, time_diff, false)) - { - // put targeted movement generators on a higher priority - if (owner.GetObjectSize()) - i_destinationHolder.ResetUpdate(50); - - float dist = i_target->GetObjectSize() + owner.GetObjectSize() + sWorld.getRate(RATE_TARGET_POS_RECALCULATION_RANGE); - - //More distance let have better performance, less distance let have more sensitive reaction at target move. - - // try to counter precision differences - if( i_destinationHolder.GetDistance2dFromDestSq(*i_target.getTarget()) >= dist * dist) - { - owner.SetInFront(i_target.getTarget()); // Set new Angle For Map:: - _setTargetLocation(owner); //Calculate New Dest and Send data To Player - } - // Update the Angle of the target only for Map::, no need to send packet for player - else if ( !i_angle && !owner.HasInArc( 0.01f, i_target.getTarget() ) ) - owner.SetInFront(i_target.getTarget()); - - if(( owner.IsStopped() && !i_destinationHolder.HasArrived() ) || i_recalculateTravel ) - { - i_recalculateTravel = false; - //Angle update will take place into owner.StopMoving() - owner.SetInFront(i_target.getTarget()); - - owner.StopMoving(); - if(owner.canReachWithAttack(i_target.getTarget()) && !owner.hasUnitState(UNIT_STAT_FOLLOW)) - owner.Attack(i_target.getTarget(),true); - } - } - return true; -} - -template<class T> -Unit* -TargetedMovementGenerator<T>::GetTarget() const -{ - return i_target.getTarget(); -} - -template void TargetedMovementGenerator<Player>::_setTargetLocation(Player &); -template void TargetedMovementGenerator<Creature>::_setTargetLocation(Creature &); -template void TargetedMovementGenerator<Player>::Initialize(Player &); -template void TargetedMovementGenerator<Creature>::Initialize(Creature &); -template void TargetedMovementGenerator<Player>::Finalize(Player &); -template void TargetedMovementGenerator<Creature>::Finalize(Creature &); -template void TargetedMovementGenerator<Player>::Reset(Player &); -template void TargetedMovementGenerator<Creature>::Reset(Creature &); -template bool TargetedMovementGenerator<Player>::Update(Player &, const uint32 &); -template bool TargetedMovementGenerator<Creature>::Update(Creature &, const uint32 &); -template Unit* TargetedMovementGenerator<Player>::GetTarget() const; -template Unit* TargetedMovementGenerator<Creature>::GetTarget() const; +/*
+ * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "ByteBuffer.h"
+#include "TargetedMovementGenerator.h"
+#include "Errors.h"
+#include "Creature.h"
+#include "MapManager.h"
+#include "DestinationHolderImp.h"
+#include "World.h"
+
+#define SMALL_ALPHA 0.05f
+
+#include <cmath>
+/*
+struct StackCleaner
+{
+ Creature &i_creature;
+ StackCleaner(Creature &creature) : i_creature(creature) {}
+ void Done(void) { i_creature.StopMoving(); }
+ ~StackCleaner()
+ {
+ i_creature->Clear();
+ }
+};
+*/
+
+template<class T>
+void
+TargetedMovementGenerator<T>::_setTargetLocation(T &owner)
+{
+ if( !i_target.isValid() || !&owner )
+ return;
+
+ if( owner.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNDED | UNIT_STAT_DISTRACTED) )
+ return;
+
+ // prevent redundant micro-movement for pets, other followers.
+ if(i_offset && i_target->IsWithinDistInMap(&owner,2*i_offset))
+ return;
+
+ float x, y, z;
+ if(!i_offset)
+ {
+ // to nearest contact position
+ i_target->GetContactPoint( &owner, x, y, z );
+ }
+ else
+ {
+ // to at i_offset distance from target and i_angle from target facing
+ i_target->GetClosePoint(x,y,z,owner.GetObjectSize(),i_offset,i_angle);
+ }
+
+ /*
+ We MUST not check the distance difference and avoid setting the new location for smaller distances.
+ By that we risk having far too many GetContactPoint() calls freezing the whole system.
+ In TargetedMovementGenerator<T>::Update() we check the distance to the target and at
+ some range we calculate a new position. The calculation takes some processor cycles due to vmaps.
+ If the distance to the target it too large to ignore,
+ but the distance to the new contact point is short enough to be ignored,
+ we will calculate a new contact point each update loop, but will never move to it.
+ The system will freeze.
+ ralf
+
+ //We don't update Mob Movement, if the difference between New destination and last destination is < BothObjectSize
+ float bothObjectSize = i_target->GetObjectSize() + owner.GetObjectSize() + CONTACT_DISTANCE;
+ if( i_destinationHolder.HasDestination() && i_destinationHolder.GetDestinationDiff(x,y,z) < bothObjectSize )
+ return;
+ */
+ Traveller<T> traveller(owner);
+ i_destinationHolder.SetDestination(traveller, x, y, z);
+ owner.addUnitState(UNIT_STAT_CHASE);
+ if (owner.GetTypeId() == TYPEID_UNIT && ((Creature*)&owner)->canFly())
+ owner.AddUnitMovementFlag(MOVEMENTFLAG_FLYING2);
+}
+
+template<class T>
+void
+TargetedMovementGenerator<T>::Initialize(T &owner)
+{
+ if(!&owner)
+ return;
+ owner.RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
+
+ if (owner.GetTypeId() == TYPEID_UNIT && ((Creature*)&owner)->canFly())
+ owner.AddUnitMovementFlag(MOVEMENTFLAG_FLYING2);
+
+ _setTargetLocation(owner);
+}
+
+template<class T>
+void
+TargetedMovementGenerator<T>::Finalize(T &owner)
+{
+ owner.clearUnitState(UNIT_STAT_CHASE);
+}
+
+template<class T>
+void
+TargetedMovementGenerator<T>::Reset(T &owner)
+{
+ Initialize(owner);
+}
+
+template<class T>
+bool
+TargetedMovementGenerator<T>::Update(T &owner, const uint32 & time_diff)
+{
+ if(!i_target.isValid())
+ return false;
+
+ if( !&owner || !owner.isAlive())
+ return true;
+
+ if( owner.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNDED | UNIT_STAT_FLEEING | UNIT_STAT_DISTRACTED) )
+ return true;
+
+ // prevent movement while casting spells with cast time or channel time
+ if ( owner.IsNonMeleeSpellCasted(false, false, true))
+ {
+ if (!owner.IsStopped())
+ owner.StopMoving();
+ return true;
+ }
+
+ // prevent crash after creature killed pet
+ if (!owner.hasUnitState(UNIT_STAT_FOLLOW) && owner.getVictim() != i_target.getTarget())
+ return true;
+
+ Traveller<T> traveller(owner);
+
+ if( !i_destinationHolder.HasDestination() )
+ _setTargetLocation(owner);
+ if( owner.IsStopped() && !i_destinationHolder.HasArrived() )
+ {
+ owner.addUnitState(UNIT_STAT_CHASE);
+ if (owner.GetTypeId() == TYPEID_UNIT && ((Creature*)&owner)->canFly())
+ owner.AddUnitMovementFlag(MOVEMENTFLAG_FLYING2);
+
+ i_destinationHolder.StartTravel(traveller);
+ return true;
+ }
+
+ if (i_destinationHolder.UpdateTraveller(traveller, time_diff, false))
+ {
+ // put targeted movement generators on a higher priority
+ if (owner.GetObjectSize())
+ i_destinationHolder.ResetUpdate(50);
+
+ float dist = i_target->GetObjectSize() + owner.GetObjectSize() + sWorld.getRate(RATE_TARGET_POS_RECALCULATION_RANGE);
+
+ //More distance let have better performance, less distance let have more sensitive reaction at target move.
+
+ // try to counter precision differences
+ if( i_destinationHolder.GetDistance2dFromDestSq(*i_target.getTarget()) >= dist * dist)
+ {
+ owner.SetInFront(i_target.getTarget()); // Set new Angle For Map::
+ _setTargetLocation(owner); //Calculate New Dest and Send data To Player
+ }
+ // Update the Angle of the target only for Map::, no need to send packet for player
+ else if ( !i_angle && !owner.HasInArc( 0.01f, i_target.getTarget() ) )
+ owner.SetInFront(i_target.getTarget());
+
+ if(( owner.IsStopped() && !i_destinationHolder.HasArrived() ) || i_recalculateTravel )
+ {
+ i_recalculateTravel = false;
+ //Angle update will take place into owner.StopMoving()
+ owner.SetInFront(i_target.getTarget());
+
+ owner.StopMoving();
+ if(owner.canReachWithAttack(i_target.getTarget()) && !owner.hasUnitState(UNIT_STAT_FOLLOW))
+ owner.Attack(i_target.getTarget(),true);
+ }
+ }
+ return true;
+}
+
+template<class T>
+Unit*
+TargetedMovementGenerator<T>::GetTarget() const
+{
+ return i_target.getTarget();
+}
+
+template void TargetedMovementGenerator<Player>::_setTargetLocation(Player &);
+template void TargetedMovementGenerator<Creature>::_setTargetLocation(Creature &);
+template void TargetedMovementGenerator<Player>::Initialize(Player &);
+template void TargetedMovementGenerator<Creature>::Initialize(Creature &);
+template void TargetedMovementGenerator<Player>::Finalize(Player &);
+template void TargetedMovementGenerator<Creature>::Finalize(Creature &);
+template void TargetedMovementGenerator<Player>::Reset(Player &);
+template void TargetedMovementGenerator<Creature>::Reset(Creature &);
+template bool TargetedMovementGenerator<Player>::Update(Player &, const uint32 &);
+template bool TargetedMovementGenerator<Creature>::Update(Creature &, const uint32 &);
+template Unit* TargetedMovementGenerator<Player>::GetTarget() const;
+template Unit* TargetedMovementGenerator<Creature>::GetTarget() const;
diff --git a/src/game/WaypointMovementGenerator.cpp b/src/game/WaypointMovementGenerator.cpp index 48f739fa6a1..b998b61fa8a 100644 --- a/src/game/WaypointMovementGenerator.cpp +++ b/src/game/WaypointMovementGenerator.cpp @@ -112,7 +112,7 @@ WaypointMovementGenerator<Creature>::Update(Creature &creature, const uint32 &di // Now we re-set destination to same node and start travel
creature.addUnitState(UNIT_STAT_ROAMING);
if (creature.canFly())
- creature.SetUnitMovementFlags(MOVEMENTFLAG_FLYING2);
+ creature.AddUnitMovementFlag(MOVEMENTFLAG_FLYING2);
const WaypointNode &node = i_path->at(i_currentNode);
i_destinationHolder.SetDestination(traveller, node.x, node.y, node.z);
i_nextMoveTime.Reset(i_destinationHolder.GetTotalTravelTime());
@@ -176,7 +176,7 @@ WaypointMovementGenerator<Creature>::Update(Creature &creature, const uint32 &di {
creature.addUnitState(UNIT_STAT_ROAMING);
if (creature.canFly())
- creature.SetUnitMovementFlags(MOVEMENTFLAG_FLYING2);
+ creature.AddUnitMovementFlag(MOVEMENTFLAG_FLYING2);
const WaypointNode &node = i_path->at(i_currentNode);
i_destinationHolder.SetDestination(traveller, node.x, node.y, node.z);
i_nextMoveTime.Reset(i_destinationHolder.GetTotalTravelTime());
diff --git a/src/trinitycore/TrinityCore.rc b/src/trinitycore/TrinityCore.rc index 4b121534c49..80206f29567 100644 --- a/src/trinitycore/TrinityCore.rc +++ b/src/trinitycore/TrinityCore.rc @@ -52,8 +52,8 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_SYS_DEFAULT // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,2,6721,676 - PRODUCTVERSION 0,2,6721,676 + FILEVERSION 0,3,6731,680 + PRODUCTVERSION 0,3,6731,680 FILEFLAGSMASK 0x17L #ifdef _DEBUG FILEFLAGS 0x1L @@ -69,12 +69,12 @@ BEGIN BLOCK "080004b0" BEGIN VALUE "FileDescription", "TrinityCore" - VALUE "FileVersion", "0, 2, 6721, 676" + VALUE "FileVersion", "0, 3, 6731, 680" VALUE "InternalName", "TrinityCore" VALUE "LegalCopyright", "Copyright (C) 2008" VALUE "OriginalFilename", "TrinityCore.exe" VALUE "ProductName", "TrinityCore" - VALUE "ProductVersion", "0, 2, 6721, 676" + VALUE "ProductVersion", "0, 3, 6731, 680" END END BLOCK "VarFileInfo" diff --git a/src/trinityrealm/TrinityRealm.rc b/src/trinityrealm/TrinityRealm.rc index b2a9d6a8f8b..385908d7e38 100644 --- a/src/trinityrealm/TrinityRealm.rc +++ b/src/trinityrealm/TrinityRealm.rc @@ -52,8 +52,8 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_SYS_DEFAULT // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,2,6721,676 - PRODUCTVERSION 0,2,6721,676 + FILEVERSION 0,3,6731,680 + PRODUCTVERSION 0,3,6731,680 FILEFLAGSMASK 0x17L #ifdef _DEBUG FILEFLAGS 0x1L @@ -69,12 +69,12 @@ BEGIN BLOCK "080004b0" BEGIN VALUE "FileDescription", "TrinityRealm" - VALUE "FileVersion", "0, 2, 6721, 676" + VALUE "FileVersion", "0, 3, 6731, 680" VALUE "InternalName", "TrinityRealm" VALUE "LegalCopyright", "Copyright (C) 2008" VALUE "OriginalFilename", "TrinityRealm.exe" VALUE "ProductName", "TrinityRealm" - VALUE "ProductVersion", "0, 2, 6721, 676" + VALUE "ProductVersion", "0, 3, 6731, 680" END END BLOCK "VarFileInfo" diff --git a/win/VC90/ACE_vc9.vcproj b/win/VC90/ACE_vc9.vcproj index 990149db7da..e25dfb517dd 100644 --- a/win/VC90/ACE_vc9.vcproj +++ b/win/VC90/ACE_vc9.vcproj @@ -49,7 +49,7 @@ />
<Tool
Name="VCCLCompilerTool"
- AdditionalOptions="/MP /wd 4748"
+ AdditionalOptions="/MP /wd 4748 /wd 4244"
Optimization="0"
AdditionalIncludeDirectories="..\..\dep\ACE_wrappers"
PreprocessorDefinitions="ACE_BUILD_DLL;_DEBUG;WIN32;_WINDOWS;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE"
@@ -138,7 +138,7 @@ />
<Tool
Name="VCCLCompilerTool"
- AdditionalOptions="/MP /wd 4748"
+ AdditionalOptions="/MP /wd 4748 /wd 4244"
Optimization="0"
AdditionalIncludeDirectories="..\..\dep\ACE_wrappers"
PreprocessorDefinitions="ACE_BUILD_DLL;_DEBUG;WIN32;_WINDOWS;_CRT_NONSTDC_NO_WARNINGS;_AMD64_;_WIN64;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE"
@@ -228,7 +228,7 @@ />
<Tool
Name="VCCLCompilerTool"
- AdditionalOptions="/MP /wd 4748"
+ AdditionalOptions="/MP /wd 4748 /wd 4244"
Optimization="2"
AdditionalIncludeDirectories="..\..\dep\ACE_wrappers"
PreprocessorDefinitions="ACE_BUILD_DLL;NDEBUG;WIN32;_WINDOWS;_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE"
@@ -316,7 +316,7 @@ />
<Tool
Name="VCCLCompilerTool"
- AdditionalOptions="/MP /wd 4748"
+ AdditionalOptions="/MP /wd 4748 /wd 4244"
Optimization="2"
AdditionalIncludeDirectories="..\..\dep\ACE_wrappers"
PreprocessorDefinitions="ACE_BUILD_DLL;NDEBUG;WIN32;_WINDOWS;_CRT_NONSTDC_NO_WARNINGS;_AMD64_;_WIN64;_CRT_SECURE_NO_WARNINGS;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE"
@@ -2853,11 +2853,11 @@ >
</File>
<File
- RelativePath="..\..\dep\ACE_wrappers\ace\OS_Errno.h"
+ RelativePath="..\..\dep\ACE_wrappers\ace\os_include\os_errno.h"
>
</File>
<File
- RelativePath="..\..\dep\ACE_wrappers\ace\os_include\os_errno.h"
+ RelativePath="..\..\dep\ACE_wrappers\ace\OS_Errno.h"
>
</File>
<File
@@ -3237,11 +3237,11 @@ >
</File>
<File
- RelativePath="..\..\dep\ACE_wrappers\ace\OS_String.h"
+ RelativePath="..\..\dep\ACE_wrappers\ace\os_include\os_string.h"
>
</File>
<File
- RelativePath="..\..\dep\ACE_wrappers\ace\os_include\os_string.h"
+ RelativePath="..\..\dep\ACE_wrappers\ace\OS_String.h"
>
</File>
<File
@@ -3285,11 +3285,11 @@ >
</File>
<File
- RelativePath="..\..\dep\ACE_wrappers\ace\os_include\sys\os_time.h"
+ RelativePath="..\..\dep\ACE_wrappers\ace\os_include\os_time.h"
>
</File>
<File
- RelativePath="..\..\dep\ACE_wrappers\ace\os_include\os_time.h"
+ RelativePath="..\..\dep\ACE_wrappers\ace\os_include\sys\os_time.h"
>
</File>
<File
|