diff --git a/sql/mangos.sql b/sql/mangos.sql index 829a7586d..a75345530 100644 --- a/sql/mangos.sql +++ b/sql/mangos.sql @@ -22,7 +22,7 @@ DROP TABLE IF EXISTS `db_version`; CREATE TABLE `db_version` ( `version` varchar(120) default NULL, - `required_7349_01_mangos_spell_area` bit(1) default NULL + `required_7382_01_mangos_creature_template` bit(1) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Used DB version notes'; -- @@ -811,6 +811,8 @@ CREATE TABLE `creature_template` ( `AIName` char(64) NOT NULL default '', `MovementType` tinyint(3) unsigned NOT NULL default '0', `InhabitType` tinyint(3) unsigned NOT NULL default '3', + `unk16` float NOT NULL default '1', + `unk17` float NOT NULL default '1', `RacialLeader` tinyint(3) unsigned NOT NULL default '0', `RegenHealth` tinyint(3) unsigned NOT NULL default '1', `equipment_id` mediumint(8) unsigned NOT NULL default '0', @@ -827,7 +829,7 @@ CREATE TABLE `creature_template` ( LOCK TABLES `creature_template` WRITE; /*!40000 ALTER TABLE `creature_template` DISABLE KEYS */; INSERT INTO `creature_template` VALUES -(1,1,10045,0,10045,0,'Waypoint(Only GM can see it)','Visual',NULL,1,1,64,64,0,0,0,35,35,0,0.91,1,0,14,15,0,100,2000,2200,4096,0,0,0,0,0,0,1.76,2.42,100,8,5242886,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'',0,3,0,1,0,0,0x82,''); +(1,1,10045,0,10045,0,'Waypoint(Only GM can see it)','Visual',NULL,1,1,64,64,0,0,0,35,35,0,0.91,1,0,14,15,0,100,2000,2200,4096,0,0,0,0,0,0,1.76,2.42,100,8,5242886,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'',0,3,1.0,1.0,0,1,0,0,0x82,''); /*!40000 ALTER TABLE `creature_template` ENABLE KEYS */; UNLOCK TABLES; @@ -1470,36 +1472,6 @@ CREATE TABLE `instance_template` ( LOCK TABLES `instance_template` WRITE; /*!40000 ALTER TABLE `instance_template` DISABLE KEYS */; -INSERT INTO `instance_template` VALUES -(33,0,22,30,10,10,7200,NULL,NULL,NULL,NULL,''), -(34,0,24,32,10,10,7200,NULL,NULL,NULL,NULL,''), -(36,0,15,20,10,10,7200,NULL,NULL,NULL,NULL,''), -(43,0,15,21,10,10,7200,NULL,NULL,NULL,NULL,''), -(47,0,29,38,10,10,7200,NULL,NULL,NULL,NULL,''), -(48,0,24,32,10,10,7200,NULL,NULL,NULL,NULL,''), -(70,0,35,47,10,10,7200,NULL,NULL,NULL,NULL,''), -(90,0,29,38,10,10,7200,NULL,NULL,NULL,NULL,''), -(109,0,45,55,10,10,7200,NULL,NULL,NULL,NULL,''), -(129,0,37,46,10,10,7200,NULL,NULL,NULL,NULL,''), -(189,0,34,45,10,10,7200,NULL,NULL,NULL,NULL,''), -(209,0,44,54,10,10,7200,NULL,NULL,NULL,NULL,''), -(229,0,58,0,10,10,120000,78.5083,-225.044,49.839,5.1,''), -(230,0,52,0,5,5,7200,NULL,NULL,NULL,NULL,''), -(249,0,60,0,40,40,432000,NULL,NULL,NULL,NULL,''), -(289,0,57,0,5,5,7200,NULL,NULL,NULL,NULL,''), -(309,0,60,0,20,20,259200,NULL,NULL,NULL,NULL,''), -(329,0,58,60,5,5,7200,NULL,NULL,NULL,NULL,''), -(349,0,46,55,10,10,7200,NULL,NULL,NULL,NULL,''), -(389,0,13,18,10,10,7200,NULL,NULL,NULL,NULL,''), -(409,0,60,0,40,40,604800,NULL,NULL,NULL,NULL,''), -(429,0,55,60,5,5,7200,NULL,NULL,NULL,NULL,''), -(469,0,60,0,40,40,604800,NULL,NULL,NULL,NULL,''), -(509,0,60,0,20,20,259200,NULL,NULL,NULL,NULL,''), -(531,0,60,0,40,40,604800,NULL,NULL,NULL,NULL,''), -(533,0,80,0,10,25,0,NULL,NULL,NULL,NULL,''), -(615,0,80,0,10,25,0,NULL,NULL,NULL,NULL,''), -(616,0,80,0,10,25,0,NULL,NULL,NULL,NULL,''), -(624,0,80,0,10,25,0,NULL,NULL,NULL,NULL,''); /*!40000 ALTER TABLE `instance_template` ENABLE KEYS */; UNLOCK TABLES; @@ -13047,10 +13019,6 @@ CREATE TABLE `quest_template` ( `ReqSourceCount2` smallint(5) unsigned NOT NULL default '0', `ReqSourceCount3` smallint(5) unsigned NOT NULL default '0', `ReqSourceCount4` smallint(5) unsigned NOT NULL default '0', - `ReqSourceRef1` tinyint(3) unsigned NOT NULL default '0', - `ReqSourceRef2` tinyint(3) unsigned NOT NULL default '0', - `ReqSourceRef3` tinyint(3) unsigned NOT NULL default '0', - `ReqSourceRef4` tinyint(3) unsigned NOT NULL default '0', `ReqCreatureOrGOId1` mediumint(9) NOT NULL default '0', `ReqCreatureOrGOId2` mediumint(9) NOT NULL default '0', `ReqCreatureOrGOId3` mediumint(9) NOT NULL default '0', @@ -13299,7 +13267,7 @@ CREATE TABLE `spell_area` ( `quest_start` mediumint(8) unsigned NOT NULL default '0', `quest_start_active` tinyint(1) unsigned NOT NULL default '0', `quest_end` mediumint(8) unsigned NOT NULL default '0', - `aura_spell` mediumint(8) unsigned NOT NULL default '0', + `aura_spell` mediumint(8) NOT NULL default '0', `racemask` mediumint(8) unsigned NOT NULL default '0', `gender` tinyint(1) unsigned NOT NULL default '2', `autocast` tinyint(1) unsigned NOT NULL default '0', diff --git a/sql/updates/7369_01_mangos_quest_template.sql b/sql/updates/7369_01_mangos_quest_template.sql new file mode 100644 index 000000000..734ebe584 --- /dev/null +++ b/sql/updates/7369_01_mangos_quest_template.sql @@ -0,0 +1,7 @@ +ALTER TABLE db_version CHANGE COLUMN required_7349_01_mangos_spell_area required_7369_01_mangos_quest_template bit; + +ALTER TABLE quest_template + DROP COLUMN ReqSourceRef1, + DROP COLUMN ReqSourceRef2, + DROP COLUMN ReqSourceRef3, + DROP COLUMN ReqSourceRef4; \ No newline at end of file diff --git a/sql/updates/7376_01_mangos_spell_area.sql b/sql/updates/7376_01_mangos_spell_area.sql new file mode 100644 index 000000000..5a3aadd68 --- /dev/null +++ b/sql/updates/7376_01_mangos_spell_area.sql @@ -0,0 +1,4 @@ +ALTER TABLE db_version CHANGE COLUMN required_7369_01_mangos_quest_template required_7376_01_mangos_spell_area bit; + +ALTER TABLE spell_area + CHANGE COLUMN `aura_spell` `aura_spell` mediumint(8) NOT NULL default '0'; diff --git a/sql/updates/7382_01_mangos_creature_template.sql b/sql/updates/7382_01_mangos_creature_template.sql new file mode 100644 index 000000000..9616f20a7 --- /dev/null +++ b/sql/updates/7382_01_mangos_creature_template.sql @@ -0,0 +1,5 @@ +ALTER TABLE db_version CHANGE COLUMN required_7376_01_mangos_spell_area required_7382_01_mangos_creature_template bit; + +ALTER TABLE creature_template + ADD COLUMN unk16 float NOT NULL default '1' AFTER InhabitType, + ADD COLUMN unk17 float NOT NULL default '1' AFTER unk16; diff --git a/sql/updates/Makefile.am b/sql/updates/Makefile.am index 6f9ec4dd4..138a68cf7 100644 --- a/sql/updates/Makefile.am +++ b/sql/updates/Makefile.am @@ -188,6 +188,9 @@ pkgdata_DATA = \ 7331_01_mangos_command.sql \ 7332_01_mangos_command.sql \ 7349_01_mangos_spell_area.sql \ + 7369_01_mangos_quest_template.sql \ + 7376_01_mangos_spell_area.sql \ + 7382_01_mangos_creature_template.sql \ README ## Additional files to include when running 'make dist' @@ -356,4 +359,7 @@ EXTRA_DIST = \ 7331_01_mangos_command.sql \ 7332_01_mangos_command.sql \ 7349_01_mangos_spell_area.sql \ + 7369_01_mangos_quest_template.sql \ + 7376_01_mangos_spell_area.sql \ + 7382_01_mangos_creature_template.sql \ README diff --git a/src/game/AchievementMgr.cpp b/src/game/AchievementMgr.cpp index 52adc738a..b06a2f1c7 100644 --- a/src/game/AchievementMgr.cpp +++ b/src/game/AchievementMgr.cpp @@ -27,6 +27,7 @@ #include "GameEvent.h" #include "World.h" #include "SpellMgr.h" +#include "ArenaTeam.h" #include "ProgressBar.h" #include "GridNotifiersImpl.h" #include "CellImpl.h" @@ -374,6 +375,18 @@ void AchievementMgr::CheckAllAchievementCriteria() UpdateAchievementCriteria(AchievementCriteriaTypes(i)); } +static const uint32 achievIdByArenaSlot[MAX_ARENA_SLOT] = { 1057, 1107, 1108 }; +static const uint32 achievIdForDangeon[][4] = +{ + // ach_cr_id,is_dungeon,is_raid,is_heroic_dungeon + { 321, true, true, true }, + { 916, false, true, false }, + { 917, false, true, false }, + { 918, true, false, false }, + { 2219, false, false, true }, + { 0, false, false, false } +}; + /** * this function will be called whenever the user might have done a criteria relevant action */ @@ -406,24 +419,55 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui switch (type) { - case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL: - SetCriteriaProgress(achievementCriteria, GetPlayer()->getLevel()); + // std. case: increment at 1 + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST: + // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case + if(!miscvalue1) + continue; + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); break; - case ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT: - SetCriteriaProgress(achievementCriteria, GetPlayer()->GetByteValue(PLAYER_BYTES_2, 2)+1); + // std case: increment at miscvalue1 + case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TALENTS: + case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD: + case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TRAVELLING: + case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_AT_BARBER: + case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL: + case ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY: + // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case + if(!miscvalue1) + continue; + SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_ACCUMULATE); break; + // std case: high value at miscvalue1 + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID: + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_SOLD: /* FIXME: for online player only currently */ + // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case + if(!miscvalue1) + continue; + SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_HIGHEST); + break; + + // specialized cases + case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE: // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case if(!miscvalue1) continue; if(achievementCriteria->kill_creature.creatureID != miscvalue1) continue; - SetCriteriaProgress(achievementCriteria, miscvalue2, true); + SetCriteriaProgress(achievementCriteria, miscvalue2, PROGRESS_ACCUMULATE); + break; + case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL: + SetCriteriaProgress(achievementCriteria, GetPlayer()->getLevel()); break; case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL: if(uint32 skillvalue = GetPlayer()->GetBaseSkillValue(achievementCriteria->reach_skill_level.skillID)) SetCriteriaProgress(achievementCriteria, skillvalue); break; + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT: + if(m_completedAchievements.find(achievementCriteria->complete_achievement.linkedAchievement) != m_completedAchievements.end()) + SetCriteriaProgress(achievementCriteria, 1); + break; case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT: { uint32 counter =0; @@ -445,27 +489,13 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui SetCriteriaProgress(achievementCriteria, counter); break; } - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST: - // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case - if(!miscvalue1) - continue; - SetCriteriaProgress(achievementCriteria, miscvalue1, true); - break; case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND: // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case if(!miscvalue1) continue; if(GetPlayer()->GetMapId() != achievementCriteria->complete_battleground.mapID) continue; - SetCriteriaProgress(achievementCriteria, miscvalue1, true); - break; - case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL: - if(GetPlayer()->HasSpell(achievementCriteria->learn_spell.spellID)) - SetCriteriaProgress(achievementCriteria, 1); - break; - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT: - if(m_completedAchievements.find(achievementCriteria->complete_achievement.linkedAchievement) != m_completedAchievements.end()) - SetCriteriaProgress(achievementCriteria, 1); + SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_ACCUMULATE); break; case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP: // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case @@ -473,21 +503,99 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui continue; if(GetPlayer()->GetMapId() != achievementCriteria->death_at_map.mapID) continue; - SetCriteriaProgress(achievementCriteria, 1, true); + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); break; + case ACHIEVEMENT_CRITERIA_TYPE_DEATH: + { + // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case + if(!miscvalue1) + continue; + // skip wrong arena achievements, if not achievIdByArenaSlot then normal totla death counter + bool notfit = false; + for(int i = 0; i < MAX_ARENA_SLOT; ++i) + { + if(achievIdByArenaSlot[i] == achievement->ID) + { + BattleGround* bg = GetPlayer()->GetBattleGround(); + if(!bg || ArenaTeam::GetSlotByType(bg->GetArenaType())!=i) + notfit = true; + + break; + } + } + if(notfit) + continue; + + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + break; + } + case ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON: + { + // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case + if(!miscvalue1) + continue; + + Map const* map = GetPlayer()->GetMap(); + if(!map->IsDungeon()) + continue; + + // search case + bool found = false; + for(int i = 0; achievIdForDangeon[i][0]; ++i) + { + if(achievIdForDangeon[i][0] == achievement->ID) + { + if(map->IsRaid()) + { + // if raid accepted (ignore difficulty) + if(!achievIdForDangeon[i][2]) + break; // for + } + else if(GetPlayer()->GetDifficulty()==DIFFICULTY_NORMAL) + { + // dungeon in normal mode accepted + if(!achievIdForDangeon[i][1]) + break; // for + } + else + { + // dungeon in heroic mode accepted + if(!achievIdForDangeon[i][3]) + break; // for + } + + found = true; + break; // for + } + } + if(!found) + continue; + + //FIXME: work only for instances where max==min for players + if(((InstanceMap*)map)->GetMaxPlayers() != achievementCriteria->death_in_dungeon.manLimit) + continue; + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + break; + + } case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE: // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case if(!miscvalue1) continue; if(miscvalue1 != achievementCriteria->killed_by_creature.creatureEntry) continue; - SetCriteriaProgress(achievementCriteria, 1, true); + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); break; case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER: // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case if(!miscvalue1) continue; - SetCriteriaProgress(achievementCriteria, 1, true); + + // if team check required: must kill by opposition faction + if(achievement->ID==318 && miscvalue2==GetPlayer()->GetTeam()) + continue; + + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); break; case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING: { @@ -506,17 +614,32 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui SetCriteriaProgress(achievementCriteria, miscvalue1); break; } + case ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM: + // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case + if(!miscvalue1) + continue; + if(miscvalue2 != achievementCriteria->death_from.type) + continue; + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + break; case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST: if(GetPlayer()->GetQuestRewardStatus(achievementCriteria->complete_quest.questID)) SetCriteriaProgress(achievementCriteria, 1); break; - case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM: - // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case - if(!miscvalue1) + case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET: + case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2: + if (!miscvalue1 || miscvalue1 != achievementCriteria->be_spell_target.spellID) continue; - if(achievementCriteria->use_item.itemID != miscvalue1) + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + break; + case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL: + if (!miscvalue1 || miscvalue1 != achievementCriteria->cast_spell.spellID) continue; - SetCriteriaProgress(achievementCriteria, 1, true); + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + break; + case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL: + if(GetPlayer()->HasSpell(achievementCriteria->learn_spell.spellID)) + SetCriteriaProgress(achievementCriteria, 1); break; case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM: // speedup for non-login case @@ -524,25 +647,107 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui continue; SetCriteriaProgress(achievementCriteria, GetPlayer()->GetItemCount(achievementCriteria->own_item.itemID, true)); break; + case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM: + // AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case + if(!miscvalue1) + continue; + if(achievementCriteria->use_item.itemID != miscvalue1) + continue; + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + break; case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM: // You _have_ to loot that item, just owning it when logging in does _not_ count! if(!miscvalue1) continue; if(miscvalue1 != achievementCriteria->own_item.itemID) continue; - SetCriteriaProgress(achievementCriteria, miscvalue2, true); + SetCriteriaProgress(achievementCriteria, miscvalue2, PROGRESS_ACCUMULATE); break; - case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET: - case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2: - if (!miscvalue1 || miscvalue1 != achievementCriteria->be_spell_target.spellID) + case ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA: + { + WorldMapOverlayEntry const* worldOverlayEntry = sWorldMapOverlayStore.LookupEntry(achievementCriteria->explore_area.areaReference); + if(!worldOverlayEntry) + break; + + int32 exploreFlag = GetAreaFlagByAreaID(worldOverlayEntry->areatableID); + if(exploreFlag < 0) + break; + + uint32 playerIndexOffset = uint32(exploreFlag) / 32; + uint32 mask = 1<< (uint32(exploreFlag) % 32); + + if(GetPlayer()->GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + playerIndexOffset) & mask) + SetCriteriaProgress(achievementCriteria, 1); + break; + } + case ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT: + SetCriteriaProgress(achievementCriteria, GetPlayer()->GetByteValue(PLAYER_BYTES_2, 2)+1); + break; + case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION: + { + int32 reputation = GetPlayer()->GetReputation(achievementCriteria->gain_reputation.factionID); + if (reputation > 0) + SetCriteriaProgress(achievementCriteria, reputation); + break; + } + case ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION: + { + uint32 counter = 0; + const FactionStateList factionStateList = GetPlayer()->GetFactionStateList(); + for (FactionStateList::const_iterator iter = factionStateList.begin(); iter!= factionStateList.end(); ++iter) + { + if(GetPlayer()->ReputationToRank(iter->second.Standing) >= REP_EXALTED) + ++counter; + } + SetCriteriaProgress(achievementCriteria, counter); + break; + } + case ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP: + { + // skip for login case + if(!miscvalue1) continue; - SetCriteriaProgress(achievementCriteria, 1, true); + SetCriteriaProgress(achievementCriteria, 1); break; - case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL: - if (!miscvalue1 || miscvalue1 != achievementCriteria->cast_spell.spellID) + } + case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT: + case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT: + { + // miscvalue1 = itemid + // miscvalue2 = diced value + if(!miscvalue1) continue; - SetCriteriaProgress(achievementCriteria, 1, true); + if(miscvalue2 != achievementCriteria->roll_greed_on_loot.rollValue) + continue; + ItemPrototype const *pProto = objmgr.GetItemPrototype( miscvalue1 ); + + uint32 requiredItemLevel = 0; + if (achievementCriteria->ID == 2412 || achievementCriteria->ID == 2358) + requiredItemLevel = 185; + + if(!pProto || pProto->ItemLevel GetSpellMap().begin(); + spellIter != GetPlayer()->GetSpellMap().end(); + ++spellIter) + { + for(SkillLineAbilityMap::const_iterator skillIter = spellmgr.GetBeginSkillLineAbilityMap(spellIter->first); + skillIter != spellmgr.GetEndSkillLineAbilityMap(spellIter->first); + ++skillIter) + { + if(skillIter->second->skillId == achievementCriteria->learn_skilline_spell.skillLine) + spellCount++; + } + } + SetCriteriaProgress(achievementCriteria, spellCount); + break; + } case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2: { if (!miscvalue1 || miscvalue1 != achievementCriteria->cast_spell.spellID) @@ -564,162 +769,72 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui continue; } - SetCriteriaProgress(achievementCriteria, 1, true); + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); break; } - case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS: - { - uint32 spellCount = 0; - for (PlayerSpellMap::const_iterator spellIter = GetPlayer()->GetSpellMap().begin(); - spellIter != GetPlayer()->GetSpellMap().end(); - ++spellIter) - { - for(SkillLineAbilityMap::const_iterator skillIter = spellmgr.GetBeginSkillLineAbilityMap(spellIter->first); - skillIter != spellmgr.GetEndSkillLineAbilityMap(spellIter->first); - ++skillIter) - { - if(skillIter->second->skillId == achievementCriteria->learn_skilline_spell.skillLine) - spellCount++; - } - } - SetCriteriaProgress(achievementCriteria, spellCount); + // std case: not exist in DBC, not triggered in code as result + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALTH: + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_SPELLPOWER: + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_ARMOR: + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_POWER: + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_STAT: + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_RATING: break; - } - case ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP: - { - // skip for login case - if(!miscvalue1) - continue; - SetCriteriaProgress(achievementCriteria, 1); - break; - } - case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION: - { - int32 reputation = GetPlayer()->GetReputation(achievementCriteria->gain_reputation.factionID); - if (reputation > 0) - SetCriteriaProgress(achievementCriteria, reputation); - break; - } - case ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION: - { - uint32 counter = 0; - const FactionStateList factionStateList = GetPlayer()->GetFactionStateList(); - for (FactionStateList::const_iterator iter = factionStateList.begin(); iter!= factionStateList.end(); ++iter) - { - if(GetPlayer()->ReputationToRank(iter->second.Standing) >= REP_EXALTED) - ++counter; - } - SetCriteriaProgress(achievementCriteria, counter); - break; - } - case ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA: - { - WorldMapOverlayEntry const* worldOverlayEntry = sWorldMapOverlayStore.LookupEntry(achievementCriteria->explore_area.areaReference); - if(!worldOverlayEntry) - break; - - int32 exploreFlag = GetAreaFlagByAreaID(worldOverlayEntry->areatableID); - if(exploreFlag < 0) - break; - - uint32 playerIndexOffset = uint32(exploreFlag) / 32; - uint32 mask = 1<< (uint32(exploreFlag) % 32); - - if(GetPlayer()->GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + playerIndexOffset) & mask) - SetCriteriaProgress(achievementCriteria, 1); - break; - } - case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT: - case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT: - { - // miscvalue1 = itemid - // miscvalue2 = diced value - if(!miscvalue1) - continue; - if(miscvalue2 != achievementCriteria->roll_greed_on_loot.rollValue) - continue; - ItemPrototype const *pProto = objmgr.GetItemPrototype( miscvalue1 ); - - uint32 requiredItemLevel = 0; - if (achievementCriteria->ID == 2412 || achievementCriteria->ID == 2358) - requiredItemLevel = 185; - - if(!pProto || pProto->ItemLevel requiredType) { - case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL: - { - if (achievIdByClass[GetPlayer()->getClass()] == achievement->ID || - achievIdByRace[GetPlayer()->getRace()] == achievement->ID) - return progress->counter >= achievementCriteria->reach_level.level; - return false; - } - case ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT: - return progress->counter >= achievementCriteria->buy_bank_slot.numberOfSlots; case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE: return progress->counter >= achievementCriteria->kill_creature.creatureCount; - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT: - return progress->counter >= 1; + case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL: + { + // skip wrong class achievements + for(int i = 1; i < MAX_CLASSES; ++i) + if(achievIdByClass[i] == achievement->ID && i != GetPlayer()->getClass()) + return false; + + // skip wrong race achievements + for(int i = 1; i < MAX_RACES; ++i) + if(achievIdByRace[i] == achievement->ID && i != GetPlayer()->getRace()) + return false; + + // appropriate class/race or not class/race specific + return progress->counter >= achievementCriteria->reach_level.level; + } case ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL: return progress->counter >= achievementCriteria->reach_skill_level.skillLevel; + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT: + return progress->counter >= 1; case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT: return progress->counter >= achievementCriteria->complete_quest_count.totalQuestCount; case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE: return progress->counter >= achievementCriteria->complete_quests_in_zone.questCount; case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST: return progress->counter >= achievementCriteria->complete_daily_quest.questCount; - case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL: - return progress->counter >= 1; case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING: return progress->counter >= achievementCriteria->fall_without_dying.fallHeight; case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST: return progress->counter >= 1; - case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM: - return progress->counter >= achievementCriteria->use_item.itemCount; - case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM: - return progress->counter >= achievementCriteria->own_item.itemCount; - case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM: - return progress->counter >= achievementCriteria->loot_item.itemCount; case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET: case ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2: return progress->counter >= achievementCriteria->be_spell_target.spellCount; case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL: case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2: return progress->counter >= achievementCriteria->cast_spell.castCount; - case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS: - return progress->counter >= achievementCriteria->learn_skilline_spell.spellCount; - case ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP: - return progress->counter >= achievementCriteria->visit_barber.numberOfVisits; + case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL: + return progress->counter >= 1; + case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM: + return progress->counter >= achievementCriteria->own_item.itemCount; + case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM: + return progress->counter >= achievementCriteria->use_item.itemCount; + case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM: + return progress->counter >= achievementCriteria->loot_item.itemCount; + case ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA: + return progress->counter >= 1; + case ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT: + return progress->counter >= achievementCriteria->buy_bank_slot.numberOfSlots; case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION: return progress->counter >= achievementCriteria->gain_reputation.reputationAmount; case ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION: return progress->counter >= achievementCriteria->gain_exalted_reputation.numberOfExaltedFactions; - case ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA: - return progress->counter >= 1; - case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT: - case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT: - return progress->counter >= achievementCriteria->roll_greed_on_loot.count; + case ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP: + return progress->counter >= achievementCriteria->visit_barber.numberOfVisits; + case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT: + case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT: + return progress->counter >= achievementCriteria->roll_greed_on_loot.count; + case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD: + return progress->counter >= achievementCriteria->quest_reward_money.goldInCopper; + case ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY: + return progress->counter >= achievementCriteria->loot_money.goldInCopper; + case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS: + return progress->counter >= achievementCriteria->learn_skilline_spell.spellCount; // handle all statistic-only criteria here case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND: case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP: + case ACHIEVEMENT_CRITERIA_TYPE_DEATH: + case ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON: case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE: case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER: + case ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM: + case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TALENTS: + case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_AT_BARBER: + case ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL: + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID: + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_SOLD: + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALTH: + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_SPELLPOWER: + case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_ARMOR: return false; } return false; @@ -858,9 +996,9 @@ AchievementCompletionState AchievementMgr::GetAchievementCompletionState(Achieve return ACHIEVEMENT_COMPLETED_COMPLETED_NOT_STORED; } -void AchievementMgr::SetCriteriaProgress(AchievementCriteriaEntry const* entry, uint32 newValue, bool relative) +void AchievementMgr::SetCriteriaProgress(AchievementCriteriaEntry const* entry, uint32 changeValue, ProgressType ptype) { - sLog.outDetail("AchievementMgr::SetCriteriaProgress(%u, %u)", entry->ID, newValue); + sLog.outDetail("AchievementMgr::SetCriteriaProgress(%u, %u) for (GUID:%u)", entry->ID, changeValue); CriteriaProgress *progress = NULL; CriteriaProgressMap::iterator iter = m_criteriaProgress.find(entry->ID); @@ -868,18 +1006,30 @@ void AchievementMgr::SetCriteriaProgress(AchievementCriteriaEntry const* entry, if(iter == m_criteriaProgress.end()) { // not create record for 0 counter - if(newValue == 0) + if(changeValue == 0) return; progress = &m_criteriaProgress[entry->ID]; - progress->counter = newValue; + progress->counter = changeValue; progress->date = time(NULL); } else { progress = &iter->second; - if(relative) - newValue += progress->counter; + + uint32 newValue; + switch(ptype) + { + case PROGRESS_SET: + newValue = changeValue; + break; + case PROGRESS_ACCUMULATE: + newValue = progress->counter + changeValue; + break; + case PROGRESS_HIGHEST: + newValue = progress->counter < changeValue ? changeValue : progress->counter; + break; + } // not update (not mark as changed) if counter will have same value if(progress->counter == newValue) diff --git a/src/game/AchievementMgr.h b/src/game/AchievementMgr.h index 7c3b90cc1..af637bcc2 100644 --- a/src/game/AchievementMgr.h +++ b/src/game/AchievementMgr.h @@ -104,9 +104,10 @@ class AchievementMgr Player* GetPlayer() { return m_player;} private: + enum ProgressType { PROGRESS_SET, PROGRESS_ACCUMULATE, PROGRESS_HIGHEST }; void SendAchievementEarned(AchievementEntry const* achievement); void SendCriteriaUpdate(uint32 id, CriteriaProgress const* progress); - void SetCriteriaProgress(AchievementCriteriaEntry const* entry, uint32 newValue, bool relative=false); + void SetCriteriaProgress(AchievementCriteriaEntry const* entry, uint32 changeValue, ProgressType ptype = PROGRESS_SET); void CompletedCriteria(AchievementCriteriaEntry const* entry); void CompletedAchievement(AchievementEntry const* entry); bool IsCompletedCriteria(AchievementCriteriaEntry const* entry); diff --git a/src/game/AuctionHouseHandler.cpp b/src/game/AuctionHouseHandler.cpp index ac9ef7131..d12018fb1 100644 --- a/src/game/AuctionHouseHandler.cpp +++ b/src/game/AuctionHouseHandler.cpp @@ -358,6 +358,7 @@ void WorldSession::HandleAuctionPlaceBid( WorldPacket & recv_data ) } auction->bidder = pl->GetGUIDLow(); auction->bid = price; + GetPlayer()->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID, price); // after this update we should save player's money ... CharacterDatabase.PExecute("UPDATE auctionhouse SET buyguid = '%u',lastbid = '%u' WHERE id = '%u'", auction->bidder, auction->bid, auction->Id); @@ -381,6 +382,7 @@ void WorldSession::HandleAuctionPlaceBid( WorldPacket & recv_data ) } auction->bidder = pl->GetGUIDLow(); auction->bid = auction->buyout; + GetPlayer()->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID, auction->buyout); auctionmgr.SendAuctionSalePendingMail( auction ); auctionmgr.SendAuctionSuccessfulMail( auction ); diff --git a/src/game/AuctionHouseMgr.cpp b/src/game/AuctionHouseMgr.cpp index b5370c19b..84457c570 100644 --- a/src/game/AuctionHouseMgr.cpp +++ b/src/game/AuctionHouseMgr.cpp @@ -223,6 +223,8 @@ void AuctionHouseMgr::SendAuctionSuccessfulMail( AuctionEntry * auction ) if (owner) { + //FIXME: what do if owner offline + owner->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_SOLD, auction->bid); //send auction owner notification, bidder must be current! owner->GetSession()->SendAuctionOwnerNotification( auction ); } @@ -630,29 +632,29 @@ bool AuctionEntry::BuildAuctionInfo(WorldPacket & data) const sLog.outError("auction to item, that doesn't exist !!!!"); return false; } - data << (uint32) Id; - data << (uint32) pItem->GetEntry(); + data << uint32(Id); + data << uint32(pItem->GetEntry()); for (uint8 i = 0; i < MAX_INSPECTED_ENCHANTMENT_SLOT; i++) { - data << (uint32) pItem->GetEnchantmentId(EnchantmentSlot(i)); - data << (uint32) pItem->GetEnchantmentDuration(EnchantmentSlot(i)); - data << (uint32) pItem->GetEnchantmentCharges(EnchantmentSlot(i)); + data << uint32(pItem->GetEnchantmentId(EnchantmentSlot(i))); + data << uint32(pItem->GetEnchantmentDuration(EnchantmentSlot(i))); + data << uint32(pItem->GetEnchantmentCharges(EnchantmentSlot(i))); } - data << (uint32) pItem->GetItemRandomPropertyId(); //random item property id - data << (uint32) pItem->GetItemSuffixFactor(); //SuffixFactor - data << (uint32) pItem->GetCount(); //item->count - data << (uint32) pItem->GetSpellCharges(); //item->charge FFFFFFF - data << (uint32) 0; //Unknown - data << (uint64) owner; //Auction->owner - data << (uint32) startbid; //Auction->startbid (not sure if useful) - data << (uint32) (bid ? GetAuctionOutBid() : 0); + data << uint32(pItem->GetItemRandomPropertyId()); //random item property id + data << uint32(pItem->GetItemSuffixFactor()); //SuffixFactor + data << uint32(pItem->GetCount()); //item->count + data << uint32(pItem->GetSpellCharges()); //item->charge FFFFFFF + data << uint32(0); //Unknown + data << uint64(owner); //Auction->owner + data << uint32(startbid); //Auction->startbid (not sure if useful) + data << uint32(bid ? GetAuctionOutBid() : 0); //minimal outbid - data << (uint32) buyout; //auction->buyout - data << (uint32) (expire_time - time(NULL))* 1000; //time left - data << (uint64) bidder; //auction->bidder current - data << (uint32) bid; //current bid + data << uint32(buyout); //auction->buyout + data << uint32((expire_time-time(NULL))*IN_MILISECONDS);//time left + data << uint64(bidder) ; //auction->bidder current + data << uint32(bid); //current bid return true; } diff --git a/src/game/BattleGround.cpp b/src/game/BattleGround.cpp index 530ec6b3e..3cb669f59 100644 --- a/src/game/BattleGround.cpp +++ b/src/game/BattleGround.cpp @@ -36,7 +36,7 @@ BattleGround::BattleGround() { m_TypeID = BattleGroundTypeId(0); m_InstanceID = 0; - m_Status = 0; + m_Status = STATUS_NONE; m_EndTime = 0; m_LastResurrectTime = 0; m_QueueId = QUEUE_ID_MAX_LEVEL_19; @@ -1514,3 +1514,8 @@ void BattleGround::SetBgRaid( uint32 TeamID, Group *bg_raid ) if(bg_raid) bg_raid->SetBattlegroundGroup(this); old_raid = bg_raid; } + +WorldSafeLocsEntry const* BattleGround::GetClosestGraveYard( Player* player ) +{ + return objmgr.GetClosestGraveYard( player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(), player->GetMapId(), player->GetTeam() ); +} \ No newline at end of file diff --git a/src/game/BattleGround.h b/src/game/BattleGround.h index 696ef84b5..5dd56aa11 100644 --- a/src/game/BattleGround.h +++ b/src/game/BattleGround.h @@ -111,11 +111,11 @@ const uint32 Buff_Entries[3] = { BG_OBJECTID_SPEEDBUFF_ENTRY, BG_OBJECTID_REGENB enum BattleGroundStatus { - STATUS_NONE = 0, - STATUS_WAIT_QUEUE = 1, - STATUS_WAIT_JOIN = 2, - STATUS_IN_PROGRESS = 3, - STATUS_WAIT_LEAVE = 4 // custom + STATUS_NONE = 0, // first status, should mean bg is not instance + STATUS_WAIT_QUEUE = 1, // means bg is empty and waiting for queue + STATUS_WAIT_JOIN = 2, // this means, that BG has already started and it is waiting for more players + STATUS_IN_PROGRESS = 3, // means bg is running + STATUS_WAIT_LEAVE = 4 // means some faction has won BG and it is ending }; struct BattleGroundPlayer @@ -273,7 +273,7 @@ class BattleGround BattleGroundTypeId GetTypeID() const { return m_TypeID; } BGQueueIdBasedOnLevel GetQueueId() const { return m_QueueId; } uint32 GetInstanceID() const { return m_InstanceID; } - uint32 GetStatus() const { return m_Status; } + BattleGroundStatus GetStatus() const { return m_Status; } uint32 GetStartTime() const { return m_StartTime; } uint32 GetEndTime() const { return m_EndTime; } uint32 GetLastResurrectTime() const { return m_LastResurrectTime; } @@ -286,7 +286,7 @@ class BattleGround uint32 GetMaxPlayersPerTeam() const { return m_MaxPlayersPerTeam; } uint32 GetMinPlayersPerTeam() const { return m_MinPlayersPerTeam; } - int GetStartDelayTime() const { return m_StartDelayTime; } + int32 GetStartDelayTime() const { return m_StartDelayTime; } uint8 GetArenaType() const { return m_ArenaType; } uint8 GetWinner() const { return m_Winner; } uint32 GetBattlemasterEntry() const; @@ -302,7 +302,7 @@ class BattleGround this->SetLevelRange((ID + 1) * 10 + diff, (ID + 2) * 10 - ((diff + 1) % 2)); } void SetInstanceID(uint32 InstanceID) { m_InstanceID = InstanceID; } - void SetStatus(uint32 Status) { m_Status = Status; } + void SetStatus(BattleGroundStatus Status) { m_Status = Status; } void SetStartTime(uint32 Time) { m_StartTime = Time; } void SetEndTime(uint32 Time) { m_EndTime = Time; } void SetLastResurrectTime(uint32 Time) { m_LastResurrectTime = Time; } @@ -426,7 +426,7 @@ class BattleGround virtual void EventPlayerCapturedFlag(Player* /*player*/) {} /* Death related */ - virtual WorldSafeLocsEntry const* GetClosestGraveYard(float /*x*/, float /*y*/, float /*z*/, uint32 /*team*/) { return NULL; } + virtual WorldSafeLocsEntry const* GetClosestGraveYard(Player* player); virtual void AddPlayer(Player *plr); // must be implemented in BG subclass @@ -489,7 +489,7 @@ class BattleGround /* Battleground */ BattleGroundTypeId m_TypeID; uint32 m_InstanceID; //BattleGround Instance's GUID! - uint32 m_Status; + BattleGroundStatus m_Status; uint32 m_StartTime; uint32 m_EndTime; uint32 m_LastResurrectTime; @@ -497,9 +497,6 @@ class BattleGround uint8 m_ArenaType; // 2=2v2, 3=3v3, 5=5v5 bool m_InBGFreeSlotQueue; // used to make sure that BG is only once inserted into the BattleGroundMgr.BGFreeSlotQueue[bgTypeId] deque bool m_SetDeleteThis; // used for safe deletion of the bg after end / all players leave - // this variable is not used .... it can be found in many other ways... but to store it in BG object instance is useless - //uint8 m_BattleGroundType; // 3=BG, 4=arena - //instead of uint8 (in previous line) is bool used bool m_IsArena; uint8 m_Winner; // 0=alliance, 1=horde, 2=none int32 m_StartDelayTime; @@ -537,7 +534,7 @@ class BattleGround uint32 m_MinPlayersPerTeam; uint32 m_MinPlayers; - /* Location */ + /* Start location */ uint32 m_MapId; float m_TeamStartLocX[BG_TEAMS_COUNT]; float m_TeamStartLocY[BG_TEAMS_COUNT]; diff --git a/src/game/BattleGroundAB.cpp b/src/game/BattleGroundAB.cpp index 9c6c040e0..282e25298 100644 --- a/src/game/BattleGroundAB.cpp +++ b/src/game/BattleGroundAB.cpp @@ -409,20 +409,21 @@ void BattleGroundAB::_NodeDeOccupied(uint8 node) if( !ghost_list.empty() ) { WorldSafeLocsEntry const *ClosestGrave = NULL; - Player *plr; for (std::vector::const_iterator itr = ghost_list.begin(); itr != ghost_list.end(); ++itr) { - plr = objmgr.GetPlayer(*itr); - if( !plr ) + Player* plr = objmgr.GetPlayer(*itr); + if (!plr) continue; - if( !ClosestGrave ) - ClosestGrave = GetClosestGraveYard(plr->GetPositionX(), plr->GetPositionY(), plr->GetPositionZ(), plr->GetTeam()); - plr->TeleportTo(GetMapId(), ClosestGrave->x, ClosestGrave->y, ClosestGrave->z, plr->GetOrientation()); + if (!ClosestGrave) // cache + ClosestGrave = GetClosestGraveYard(plr); + + if (ClosestGrave) + plr->TeleportTo(GetMapId(), ClosestGrave->x, ClosestGrave->y, ClosestGrave->z, plr->GetOrientation()); } } - if( m_BgCreatures[node] ) + if( m_BgCreatures[node] ) DelCreature(node); // buff object isn't despawned @@ -603,9 +604,9 @@ void BattleGroundAB::Reset() DelCreature(i); } -WorldSafeLocsEntry const* BattleGroundAB::GetClosestGraveYard(float x, float y, float /*z*/, uint32 team) +WorldSafeLocsEntry const* BattleGroundAB::GetClosestGraveYard(Player* player) { - uint8 teamIndex = GetTeamIndexByTeamId(team); + uint8 teamIndex = GetTeamIndexByTeamId(player->GetTeam()); // Is there any occupied node for this team? std::vector nodes; @@ -617,13 +618,16 @@ WorldSafeLocsEntry const* BattleGroundAB::GetClosestGraveYard(float x, float y, // If so, select the closest node to place ghost on if( !nodes.empty() ) { + float plr_x = player->GetPositionX(); + float plr_y = player->GetPositionY(); + float mindist = 999999.0f; for (uint8 i = 0; i < nodes.size(); ++i) { WorldSafeLocsEntry const*entry = sWorldSafeLocsStore.LookupEntry( BG_AB_GraveyardIds[nodes[i]] ); if( !entry ) continue; - float dist = (entry->x - x)*(entry->x - x)+(entry->y - y)*(entry->y - y); + float dist = (entry->x - plr_x)*(entry->x - plr_x)+(entry->y - plr_y)*(entry->y - plr_y); if( mindist > dist ) { mindist = dist; diff --git a/src/game/BattleGroundAB.h b/src/game/BattleGroundAB.h index 0ea386bcd..3b38b3a96 100644 --- a/src/game/BattleGroundAB.h +++ b/src/game/BattleGroundAB.h @@ -242,7 +242,7 @@ class BattleGroundAB : public BattleGround void HandleAreaTrigger(Player *Source, uint32 Trigger); virtual bool SetupBattleGround(); virtual void Reset(); - virtual WorldSafeLocsEntry const* GetClosestGraveYard(float x, float y, float z, uint32 team); + virtual WorldSafeLocsEntry const* GetClosestGraveYard(Player* player); /* Scorekeeping */ virtual void UpdatePlayerScore(Player *Source, uint32 type, uint32 value); diff --git a/src/game/BattleGroundEY.cpp b/src/game/BattleGroundEY.cpp index 8b826f0cd..acdd4ca51 100644 --- a/src/game/BattleGroundEY.cpp +++ b/src/game/BattleGroundEY.cpp @@ -885,16 +885,16 @@ void BattleGroundEY::FillInitialWorldStates(WorldPacket& data) data << uint32(0xc0d) << uint32(0x17b); } -WorldSafeLocsEntry const *BattleGroundEY::GetClosestGraveYard(float x, float y, float z, uint32 team) +WorldSafeLocsEntry const *BattleGroundEY::GetClosestGraveYard(Player* player) { uint32 g_id = 0; - if(team == ALLIANCE) - g_id = EY_GRAVEYARD_MAIN_ALLIANCE; - else if(team == HORDE) - g_id = EY_GRAVEYARD_MAIN_HORDE; - else - return NULL; + switch(player->GetTeam()) + { + case ALLIANCE: g_id = EY_GRAVEYARD_MAIN_ALLIANCE; break; + case HORDE: g_id = EY_GRAVEYARD_MAIN_HORDE; break; + default: return NULL; + } float distance, nearestDistance; @@ -909,19 +909,24 @@ WorldSafeLocsEntry const *BattleGroundEY::GetClosestGraveYard(float x, float y, return NULL; } - distance = (entry->x - x)*(entry->x - x) + (entry->y - y)*(entry->y - y) + (entry->z - z)*(entry->z - z); + float plr_x = player->GetPositionX(); + float plr_y = player->GetPositionY(); + float plr_z = player->GetPositionZ(); + + + distance = (entry->x - plr_x)*(entry->x - plr_x) + (entry->y - plr_y)*(entry->y - plr_y) + (entry->z - plr_z)*(entry->z - plr_z); nearestDistance = distance; for(uint8 i = 0; i < EY_POINTS_MAX; ++i) { - if(m_PointOwnedByTeam[i]==team && m_PointState[i]==EY_POINT_UNDER_CONTROL) + if(m_PointOwnedByTeam[i]==player->GetTeam() && m_PointState[i]==EY_POINT_UNDER_CONTROL) { entry = sWorldSafeLocsStore.LookupEntry(m_CapturingPointTypes[i].GraveYardId); if(!entry) sLog.outError("BattleGroundEY: Not found graveyard: %u",m_CapturingPointTypes[i].GraveYardId); else { - distance = (entry->x - x)*(entry->x - x) + (entry->y - y)*(entry->y - y) + (entry->z - z)*(entry->z - z); + distance = (entry->x - plr_x)*(entry->x - plr_x) + (entry->y - plr_y)*(entry->y - plr_y) + (entry->z - plr_z)*(entry->z - plr_z); if(distance < nearestDistance) { nearestDistance = distance; diff --git a/src/game/BattleGroundEY.h b/src/game/BattleGroundEY.h index 5db6a796b..0da776457 100644 --- a/src/game/BattleGroundEY.h +++ b/src/game/BattleGroundEY.h @@ -24,8 +24,8 @@ class BattleGround; #define EY_MAX_TEAM_SCORE 2000 -#define BG_EY_FLAG_RESPAWN_TIME 10000 //10 seconds -#define BG_EY_FPOINTS_TICK_TIME 2000 //2 seconds +#define BG_EY_FLAG_RESPAWN_TIME (10*IN_MILISECONDS) //10 seconds +#define BG_EY_FPOINTS_TICK_TIME (2*IN_MILISECONDS) //2 seconds enum BG_EY_WorldStates { @@ -315,7 +315,7 @@ class BattleGroundEY : public BattleGround void HandleBuffUse(uint64 const& buff_guid); void HandleAreaTrigger(Player *Source, uint32 Trigger); void HandleKillPlayer(Player *player, Player *killer); - virtual WorldSafeLocsEntry const* GetClosestGraveYard(float x, float y, float z, uint32 team); + virtual WorldSafeLocsEntry const* GetClosestGraveYard(Player* player); virtual bool SetupBattleGround(); virtual void Reset(); void UpdateTeamScore(uint32 Team); diff --git a/src/game/BattleGroundHandler.cpp b/src/game/BattleGroundHandler.cpp index 0098490e1..8cb821cca 100644 --- a/src/game/BattleGroundHandler.cpp +++ b/src/game/BattleGroundHandler.cpp @@ -370,7 +370,7 @@ void WorldSession::HandleBattleGroundPlayerPortOpcode( WorldPacket &recv_data ) BattleGroundQueueTypeId bgQueueTypeId = BATTLEGROUND_QUEUE_NONE; // get the bg what we were invited to - bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(bgTypeId,type); + bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(bgTypeId, type); BattleGroundQueue::QueuedPlayersMap& qpMap = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].m_QueuedPlayers; BattleGroundQueue::QueuedPlayersMap::iterator itrPlayerStatus = qpMap.find(_player->GetGUID()); if(itrPlayerStatus == qpMap.end()) diff --git a/src/game/BattleGroundMgr.cpp b/src/game/BattleGroundMgr.cpp index 457b36c70..7e7291181 100644 --- a/src/game/BattleGroundMgr.cpp +++ b/src/game/BattleGroundMgr.cpp @@ -113,17 +113,18 @@ bool BattleGroundQueue::SelectionPool::KickGroup(uint32 size) // add group to selection pool // used when building selection pools -// returns true if we can invite more players, otherwise return false - (selection pool is set that time) +// returns true if we can invite more players +// returns false when selection pool is set bool BattleGroundQueue::SelectionPool::AddGroup(GroupQueueInfo *ginfo, uint32 desiredCount) { //if group is larger than desired count - don't allow to add it to pool - if (!ginfo->IsInvitedToBGInstanceGUID && desiredCount >= PlayerCount + ginfo->Players.size()) + if( !ginfo->IsInvitedToBGInstanceGUID && desiredCount >= PlayerCount + ginfo->Players.size() ) { SelectedGroups.push_back(ginfo); // increase selected players count PlayerCount += ginfo->Players.size(); } - if (PlayerCount == desiredCount) + if( PlayerCount < desiredCount ) return true; return false; } @@ -553,7 +554,8 @@ bool BattleGroundQueue::CheckPremadeMatch(BGQueueIdBasedOnLevel queue_id, uint32 { for(itr = m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE + i].begin(); itr != m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE + i].end(); ++itr) { - if( !(*itr)->IsInvitedToBGInstanceGUID && m_SelectionPools[i].AddGroup((*itr), maxPlayers) ) + //if itr can join BG and player count is less that maxPlayers, then add group to selectionpool + if( !(*itr)->IsInvitedToBGInstanceGUID && !m_SelectionPools[i].AddGroup((*itr), maxPlayers) ) break; } } @@ -575,7 +577,7 @@ bool BattleGroundQueue::CheckPremadeMatch(BGQueueIdBasedOnLevel queue_id, uint32 { //we must insert group to normal queue and erase pointer from premade queue m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE + i].push_front((*itr)); - m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_ALLIANCE + i].erase(itr); //pop_front(); + m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_ALLIANCE + i].erase(itr); } } } @@ -583,7 +585,7 @@ bool BattleGroundQueue::CheckPremadeMatch(BGQueueIdBasedOnLevel queue_id, uint32 return false; } -//this function tries to create battleground or arena with MinPlayersPerTeam against MinPlayersPerTeam +// this method tries to create battleground or arena with MinPlayersPerTeam against MinPlayersPerTeam bool BattleGroundQueue::CheckNormalMatch(BattleGround* bg_template, BGQueueIdBasedOnLevel queue_id, uint32 minPlayers, uint32 maxPlayers) { GroupsQueueType::const_iterator itr_team[BG_TEAMS_COUNT]; @@ -612,7 +614,7 @@ bool BattleGroundQueue::CheckNormalMatch(BattleGround* bg_template, BGQueueIdBas for(; itr_team[j] != m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE + j].end(); ++(itr_team[j])) { if( !(*(itr_team[j]))->IsInvitedToBGInstanceGUID ) - if( m_SelectionPools[j].AddGroup(*(itr_team[j]), m_SelectionPools[(j + 1) % BG_TEAMS_COUNT].GetPlayerCount()) ) + if( !m_SelectionPools[j].AddGroup(*(itr_team[j]), m_SelectionPools[(j + 1) % BG_TEAMS_COUNT].GetPlayerCount()) ) break; } // do not allow to start bg with more than 2 players more on 1 faction @@ -626,6 +628,65 @@ bool BattleGroundQueue::CheckNormalMatch(BattleGround* bg_template, BGQueueIdBas return m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() >= minPlayers && m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() >= minPlayers; } +// this method will check if we can invite players to same faction skirmish match +bool BattleGroundQueue::CheckSkirmishForSameFaction(BGQueueIdBasedOnLevel queue_id, uint32 minPlayersPerTeam) +{ + if( m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() < minPlayersPerTeam && m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() < minPlayersPerTeam ) + return false; + uint32 teamIndex = BG_TEAM_ALLIANCE; + uint32 otherTeam = BG_TEAM_HORDE; + uint32 otherTeamId = HORDE; + if ( m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() == minPlayersPerTeam ) + { + teamIndex = BG_TEAM_HORDE; + otherTeam = BG_TEAM_ALLIANCE; + otherTeamId = ALLIANCE; + } + //clear other team's selection + m_SelectionPools[otherTeam].Init(); + //store last ginfo pointer + GroupQueueInfo* ginfo = m_SelectionPools[teamIndex].SelectedGroups.back(); + //set itr_team to group that was added to selection pool latest + GroupsQueueType::iterator itr_team = m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE + teamIndex].begin(); + for(; itr_team != m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE + teamIndex].end(); ++itr_team) + if( ginfo == *itr_team ) + break; + if( itr_team == m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE + teamIndex].end() ) + return false; + GroupsQueueType::iterator itr_team2 = itr_team; + ++itr_team2; + //invite players to other selection pool + for(; itr_team2 != m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE + teamIndex].end(); ++itr_team2) + { + //if selection pool is full then break; + if( !(*itr_team2)->IsInvitedToBGInstanceGUID && !m_SelectionPools[otherTeam].AddGroup(*itr_team2, minPlayersPerTeam) ) + break; + } + if( m_SelectionPools[otherTeam].GetPlayerCount() != minPlayersPerTeam ) + return false; + + //here we have correct 2 selections and we need to change one teams team and move selection pool teams to other team's queue + for(GroupsQueueType::iterator itr = m_SelectionPools[otherTeam].SelectedGroups.begin(); itr != m_SelectionPools[otherTeam].SelectedGroups.end(); ++itr) + { + //set correct team + (*itr)->Team = otherTeamId; + //add team to other queue + m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE + otherTeam].push_front(*itr); + //remove team from old queue + GroupsQueueType::iterator itr2 = itr_team; + ++itr2; + for(; itr2 != m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE + teamIndex].end(); ++itr2) + { + if( *itr2 == *itr ) + { + m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE + teamIndex].erase(itr2); + break; + } + } + } + return true; +} + /* this method is called when group is inserted, or player / group is removed from BG Queue - there is only one player's status changed, so we don't use while(true) cycles to invite whole queue it must be called after fully adding the members of a group to ensure group joining @@ -644,9 +705,8 @@ void BattleGroundQueue::Update(BattleGroundTypeId bgTypeId, BGQueueIdBasedOnLeve //battleground with free slot for player should be always in the beggining of the queue // maybe it would be better to create bgfreeslotqueue for each queue_id_based_on_level - bool continueAdding = true; BGFreeSlotQueueType::iterator itr, next; - for (itr = sBattleGroundMgr.BGFreeSlotQueue[bgTypeId].begin(); continueAdding && itr != sBattleGroundMgr.BGFreeSlotQueue[bgTypeId].end(); itr = next) + for (itr = sBattleGroundMgr.BGFreeSlotQueue[bgTypeId].begin(); itr != sBattleGroundMgr.BGFreeSlotQueue[bgTypeId].end(); itr = next) { next = itr; ++next; @@ -672,8 +732,6 @@ void BattleGroundQueue::Update(BattleGroundTypeId bgTypeId, BGQueueIdBasedOnLeve if( !bg->HasFreeSlots() ) { - if( next == sBattleGroundMgr.BGFreeSlotQueue[bgTypeId].end() ) - continueAdding = false; // remove BG from BGFreeSlotQueue bg->RemoveFromBGFreeSlotQueue(); } @@ -754,7 +812,8 @@ void BattleGroundQueue::Update(BattleGroundTypeId bgTypeId, BGQueueIdBasedOnLeve if( !isRated ) { // if there are enough players in pools, start new battleground or non rated arena - if( CheckNormalMatch(bg_template, queue_id, MinPlayersPerTeam, MaxPlayersPerTeam) ) + if( CheckNormalMatch(bg_template, queue_id, MinPlayersPerTeam, MaxPlayersPerTeam) + || (bg_template->isArena() && CheckSkirmishForSameFaction(queue_id, MinPlayersPerTeam)) ) { // we successfully created a pool BattleGround * bg2 = NULL; @@ -1473,11 +1532,6 @@ BattleGround * BattleGroundMgr::CreateNewBattleGround(BattleGroundTypeId bgTypeI // reset the new bg (set status to status_wait_queue from status_none) bg->Reset(); - if( bg->isBattleGround() && sWorld.getConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_ENABLE) ) - { - sWorld.SendWorldText(LANG_BG_STARTED_ANNOUNCE_WORLD, bg->GetName(), bg->GetMinLevel(), bg->GetMaxLevel()); - } - // start the joining of the bg bg->SetStatus(STATUS_WAIT_JOIN); bg->SetQueueId(queue_id); diff --git a/src/game/BattleGroundMgr.h b/src/game/BattleGroundMgr.h index cf500629d..dc2979267 100644 --- a/src/game/BattleGroundMgr.h +++ b/src/game/BattleGroundMgr.h @@ -24,7 +24,8 @@ typedef std::map BattleGroundSet; -typedef std::deque BGFreeSlotQueueType; +//this container can't be deque, because deque doesn't like removing the last element - if you remove it, it invalidates next iterator and crash appears +typedef std::list BGFreeSlotQueueType; typedef UNORDERED_MAP BattleMastersMap; @@ -74,6 +75,7 @@ class BattleGroundQueue void FillPlayersToBG(BattleGround* bg, BGQueueIdBasedOnLevel queue_id); bool CheckPremadeMatch(BGQueueIdBasedOnLevel queue_id, uint32 MinPlayersPerTeam, uint32 MaxPlayersPerTeam); bool CheckNormalMatch(BattleGround* bg_template, BGQueueIdBasedOnLevel queue_id, uint32 minPlayers, uint32 maxPlayers); + bool CheckSkirmishForSameFaction(BGQueueIdBasedOnLevel queue_id, uint32 minPlayersPerTeam); GroupQueueInfo * AddGroup(Player * leader, BattleGroundTypeId bgTypeId, uint8 ArenaType, bool isRated, bool isPremade, uint32 ArenaRating, uint32 ArenaTeamId = 0); void AddPlayer(Player *plr, GroupQueueInfo *ginfo); void RemovePlayer(const uint64& guid, bool decreaseInvitedCount); diff --git a/src/game/BattleGroundWS.h b/src/game/BattleGroundWS.h index 281d182d5..f36834f65 100644 --- a/src/game/BattleGroundWS.h +++ b/src/game/BattleGroundWS.h @@ -22,8 +22,8 @@ #include "BattleGround.h" #define BG_WS_MAX_TEAM_SCORE 3 -#define BG_WS_FLAG_RESPAWN_TIME 23000 -#define BG_WS_FLAG_DROP_TIME 10000 +#define BG_WS_FLAG_RESPAWN_TIME (23*IN_MILISECONDS) +#define BG_WS_FLAG_DROP_TIME (10*IN_MILISECONDS) enum BG_WS_Sound { diff --git a/src/game/CharacterHandler.cpp b/src/game/CharacterHandler.cpp index 8c312e7f8..ae51ae60d 100644 --- a/src/game/CharacterHandler.cpp +++ b/src/game/CharacterHandler.cpp @@ -1191,7 +1191,8 @@ void WorldSession::HandleAlterAppearance( WorldPacket & recv_data ) SendPacket(&data); } - _player->SetMoney(_player->GetMoney() - Cost); // it isn't free + _player->ModifyMoney(-int32(Cost)); // it isn't free + _player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_AT_BARBER, Cost); _player->SetByteValue(PLAYER_BYTES, 2, uint8(bs_hair->hair_id)); _player->SetByteValue(PLAYER_BYTES, 3, uint8(Color)); diff --git a/src/game/Creature.cpp b/src/game/Creature.cpp index 32ab79c6a..bfdd2d58a 100644 --- a/src/game/Creature.cpp +++ b/src/game/Creature.cpp @@ -1450,7 +1450,7 @@ void Creature::setDeathState(DeathState s) { if((s == JUST_DIED && !m_isDeadByDefault)||(s == JUST_ALIVED && m_isDeadByDefault)) { - m_deathTimer = m_corpseDelay*1000; + m_deathTimer = m_corpseDelay*IN_MILISECONDS; // always save boss respawn time at death to prevent crash cheating if(sWorld.getConfig(CONFIG_SAVE_RESPAWN_TIME_IMMEDIATLY) || isWorldBoss()) @@ -1756,7 +1756,7 @@ void Creature::SaveRespawnTime() if(m_respawnTime > time(NULL)) // dead (no corpse) objmgr.SaveCreatureRespawnTime(m_DBTableGuid,GetInstanceId(),m_respawnTime); else if(m_deathTimer > 0) // dead (corpse) - objmgr.SaveCreatureRespawnTime(m_DBTableGuid,GetInstanceId(),time(NULL)+m_respawnDelay+m_deathTimer/1000); + objmgr.SaveCreatureRespawnTime(m_DBTableGuid,GetInstanceId(),time(NULL)+m_respawnDelay+m_deathTimer/IN_MILISECONDS); } bool Creature::IsOutOfThreatArea(Unit* pVictim) const @@ -1877,7 +1877,7 @@ void Creature::AddCreatureSpellCooldown(uint32 spellid) uint32 cooldown = GetSpellRecoveryTime(spellInfo); if(cooldown) - _AddCreatureSpellCooldown(spellid, time(NULL) + cooldown/1000); + _AddCreatureSpellCooldown(spellid, time(NULL) + cooldown/IN_MILISECONDS); if(spellInfo->Category) _AddCreatureCategoryCooldown(spellInfo->Category, time(NULL)); @@ -1896,7 +1896,7 @@ bool Creature::HasCategoryCooldown(uint32 spell_id) const return true; CreatureSpellCooldowns::const_iterator itr = m_CreatureCategoryCooldowns.find(spellInfo->Category); - return(itr != m_CreatureCategoryCooldowns.end() && time_t(itr->second + (spellInfo->CategoryRecoveryTime / 1000)) > time(NULL)); + return(itr != m_CreatureCategoryCooldowns.end() && time_t(itr->second + (spellInfo->CategoryRecoveryTime / IN_MILISECONDS)) > time(NULL)); } bool Creature::HasSpellCooldown(uint32 spell_id) const @@ -1925,7 +1925,7 @@ time_t Creature::GetRespawnTimeEx() const if(m_respawnTime > now) // dead (no corpse) return m_respawnTime; else if(m_deathTimer > 0) // dead (corpse) - return now+m_respawnDelay+m_deathTimer/1000; + return now+m_respawnDelay+m_deathTimer/IN_MILISECONDS; else return now; } @@ -1967,7 +1967,7 @@ void Creature::AllLootRemovedFromCorpse() // corpse was not skinnable -> apply corpse looted timer if (!cinfo || !cinfo->SkinLootId) - nDeathTimer = (uint32)((m_corpseDelay * 1000) * sWorld.getRate(RATE_CORPSE_DECAY_LOOTED)); + nDeathTimer = (uint32)((m_corpseDelay * IN_MILISECONDS) * sWorld.getRate(RATE_CORPSE_DECAY_LOOTED)); // corpse skinnable, but without skinning flag, and then skinned, corpse will despawn next update else nDeathTimer = 0; diff --git a/src/game/Creature.h b/src/game/Creature.h index 59c655477..90a83df10 100644 --- a/src/game/Creature.h +++ b/src/game/Creature.h @@ -195,6 +195,8 @@ struct CreatureInfo char const* AIName; uint32 MovementType; uint32 InhabitType; + float unk16; + float unk17; bool RacialLeader; bool RegenHealth; uint32 equipmentId; diff --git a/src/game/GameEvent.cpp b/src/game/GameEvent.cpp index 464083499..2504f454f 100644 --- a/src/game/GameEvent.cpp +++ b/src/game/GameEvent.cpp @@ -447,7 +447,7 @@ uint32 GameEvent::Update() // return the next e nextEventDelay = calcDelay; } sLog.outBasic("Next game event check in %u seconds.", nextEventDelay + 1); - return (nextEventDelay + 1) * 1000; // Add 1 second to be sure event has started/stopped at next call + return (nextEventDelay + 1) * IN_MILISECONDS; // Add 1 second to be sure event has started/stopped at next call } void GameEvent::UnApplyEvent(uint16 event_id) diff --git a/src/game/GridDefines.h b/src/game/GridDefines.h index ee05d069a..e1fdbc6b1 100644 --- a/src/game/GridDefines.h +++ b/src/game/GridDefines.h @@ -38,7 +38,7 @@ class Player; #define CENTER_GRID_OFFSET (SIZE_OF_GRIDS/2) -#define MIN_GRID_DELAY MINUTE*1000 +#define MIN_GRID_DELAY (MINUTE*IN_MILISECONDS) #define MIN_MAP_UPDATE_DELAY 50 #define MAX_NUMBER_OF_CELLS 8 diff --git a/src/game/Level0.cpp b/src/game/Level0.cpp index b7338a07f..15e0747e7 100644 --- a/src/game/Level0.cpp +++ b/src/game/Level0.cpp @@ -145,7 +145,7 @@ bool ChatHandler::HandleSaveCommand(const char* /*args*/) // save or plan save after 20 sec (logout delay) if current next save time more this value and _not_ output any messages to prevent cheat planning uint32 save_interval = sWorld.getConfig(CONFIG_INTERVAL_SAVE); - if(save_interval==0 || save_interval > 20*1000 && player->GetSaveTimer() <= save_interval - 20*1000) + if(save_interval==0 || save_interval > 20*IN_MILISECONDS && player->GetSaveTimer() <= save_interval - 20*IN_MILISECONDS) player->SaveToDB(); return true; diff --git a/src/game/LootHandler.cpp b/src/game/LootHandler.cpp index 57d1b6910..5c5d02699 100644 --- a/src/game/LootHandler.cpp +++ b/src/game/LootHandler.cpp @@ -222,6 +222,7 @@ void WorldSession::HandleLootMoneyOpcode( WorldPacket & /*recv_data*/ ) for (std::vector::iterator i = playersNear.begin(); i != playersNear.end(); ++i) { (*i)->ModifyMoney( money_per_player ); + (*i)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY, money_per_player); //Offset surely incorrect, but works WorldPacket data( SMSG_LOOT_MONEY_NOTIFY, 4 ); data << uint32(money_per_player); @@ -229,7 +230,10 @@ void WorldSession::HandleLootMoneyOpcode( WorldPacket & /*recv_data*/ ) } } else + { player->ModifyMoney( pLoot->gold ); + player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY, pLoot->gold); + } pLoot->gold = 0; pLoot->NotifyMoneyRemoved(); } diff --git a/src/game/LootMgr.cpp b/src/game/LootMgr.cpp index 591a123f8..3097cff41 100644 --- a/src/game/LootMgr.cpp +++ b/src/game/LootMgr.cpp @@ -275,6 +275,13 @@ bool LootStoreItem::IsValid(LootStore const& store, uint32 entry) const store.GetName(), entry, itemid, chance); return false; } + + if( maxcount < mincountOrRef) // wrong max count + { + sLog.outErrorDb("Table '%s' entry %d item %d: max count (%u) less that min count (%i) - skipped", store.GetName(), entry, itemid, uint32(maxcount), mincountOrRef); + return false; + } + } else // mincountOrRef < 0 { diff --git a/src/game/Mail.cpp b/src/game/Mail.cpp index c59849415..912ead72c 100644 --- a/src/game/Mail.cpp +++ b/src/game/Mail.cpp @@ -122,9 +122,9 @@ void WorldSession::HandleSendMail(WorldPacket & recv_data ) return; } - uint32 reqmoney = money + 30; - if (items_count) - reqmoney = money + (30 * items_count); + uint32 cost = items_count ? 30 * items_count : 30; // price hardcoded in client + + uint32 reqmoney = cost + money; if (pl->GetMoney() < reqmoney) { @@ -207,6 +207,7 @@ void WorldSession::HandleSendMail(WorldPacket & recv_data ) } pl->ModifyMoney( -int32(reqmoney) ); + pl->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL, cost); bool needItemDelay = false; diff --git a/src/game/MiscHandler.cpp b/src/game/MiscHandler.cpp index 956185971..d069929f6 100644 --- a/src/game/MiscHandler.cpp +++ b/src/game/MiscHandler.cpp @@ -1130,7 +1130,7 @@ void WorldSession::HandleMoveTeleportAck(WorldPacket&/* recv_data*/) recv_data >> guid; recv_data >> flags >> time; DEBUG_LOG("Guid " I64FMTD,guid); - DEBUG_LOG("Flags %u, time %u",flags, time/1000); + DEBUG_LOG("Flags %u, time %u",flags, time/IN_MILISECONDS); */ } diff --git a/src/game/MovementHandler.cpp b/src/game/MovementHandler.cpp index 84a28e463..ef7fee4df 100644 --- a/src/game/MovementHandler.cpp +++ b/src/game/MovementHandler.cpp @@ -231,52 +231,7 @@ void WorldSession::HandleMovementOpcodes( WorldPacket & recv_data ) // fall damage generation (ignore in flight case that can be triggered also at lags in moment teleportation to another map). if (opcode == MSG_MOVE_FALL_LAND && !GetPlayer()->isInFlight()) - { - // calculate total z distance of the fall - float z_diff = GetPlayer()->m_lastFallZ - movementInfo.z; - sLog.outDebug("zDiff = %f", z_diff); - Player *target = GetPlayer(); - - //Players with low fall distance, Feather Fall or physical immunity (charges used) are ignored - // 14.57 can be calculated by resolving damageperc formular below to 0 - if (z_diff >= 14.57f && !target->isDead() && !target->isGameMaster() && - !target->HasAuraType(SPELL_AURA_HOVER) && !target->HasAuraType(SPELL_AURA_FEATHER_FALL) && - !target->HasAuraType(SPELL_AURA_FLY) && !target->IsImmunedToDamage(SPELL_SCHOOL_MASK_NORMAL) ) - { - //Safe fall, fall height reduction - int32 safe_fall = target->GetTotalAuraModifier(SPELL_AURA_SAFE_FALL); - - float damageperc = 0.018f*(z_diff-safe_fall)-0.2426f; - - if(damageperc >0 ) - { - uint32 damage = (uint32)(damageperc * target->GetMaxHealth()*sWorld.getRate(RATE_DAMAGE_FALL)); - - float height = movementInfo.z; - target->UpdateGroundPositionZ(movementInfo.x,movementInfo.y,height); - - if (damage > 0) - { - //Prevent fall damage from being more than the player maximum health - if (damage > target->GetMaxHealth()) - damage = target->GetMaxHealth(); - - // Gust of Wind - if (target->GetDummyAura(43621)) - damage = target->GetMaxHealth()/2; - - target->EnvironmentalDamage(target->GetGUID(), DAMAGE_FALL, damage); - - // recheck alive, might have died of EnvironmentalDamage - if (target->isAlive()) - target->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING, uint32(z_diff*100)); - } - - //Z given by moveinfo, LastZ, FallTime, WaterZ, MapZ, Damage, Safefall reduction - DEBUG_LOG("FALLDAMAGE z=%f sz=%f pZ=%f FallTime=%d mZ=%f damage=%d SF=%d" , movementInfo.z, height, target->GetPositionZ(), movementInfo.fallTime, height, damage, safe_fall); - } - } - } + GetPlayer()->HandleFall(movementInfo); if(((movementInfo.flags & MOVEMENTFLAG_SWIMMING) != 0) != GetPlayer()->IsInWater()) { diff --git a/src/game/ObjectAccessor.cpp b/src/game/ObjectAccessor.cpp index b18a2d816..8359bd2b9 100644 --- a/src/game/ObjectAccessor.cpp +++ b/src/game/ObjectAccessor.cpp @@ -84,7 +84,7 @@ ObjectAccessor::GetNPCIfCanInteractWith(Player const &player, uint64 guid, uint3 if(factionTemplate) { FactionEntry const* faction = sFactionStore.LookupEntry(factionTemplate->faction); - if( faction->reputationListID >= 0 && player.GetReputationRank(faction) <= REP_UNFRIENDLY) + if( faction && faction->reputationListID >= 0 && player.GetReputationRank(faction) <= REP_UNFRIENDLY) return NULL; } diff --git a/src/game/ObjectMgr.cpp b/src/game/ObjectMgr.cpp index 8837b5c50..f8abddebf 100644 --- a/src/game/ObjectMgr.cpp +++ b/src/game/ObjectMgr.cpp @@ -2732,25 +2732,25 @@ void ObjectMgr::LoadQuests() "Title, Details, Objectives, OfferRewardText, RequestItemsText, EndText, ObjectiveText1, ObjectiveText2, ObjectiveText3, ObjectiveText4," // 39 40 41 42 43 44 45 46 "ReqItemId1, ReqItemId2, ReqItemId3, ReqItemId4, ReqItemCount1, ReqItemCount2, ReqItemCount3, ReqItemCount4," - // 47 48 49 50 51 52 53 54 55 56 57 58 - "ReqSourceId1, ReqSourceId2, ReqSourceId3, ReqSourceId4, ReqSourceCount1, ReqSourceCount2, ReqSourceCount3, ReqSourceCount4, ReqSourceRef1, ReqSourceRef2, ReqSourceRef3, ReqSourceRef4," - // 59 60 61 62 63 64 65 66 + // 47 48 49 50 51 52 53 54 + "ReqSourceId1, ReqSourceId2, ReqSourceId3, ReqSourceId4, ReqSourceCount1, ReqSourceCount2, ReqSourceCount3, ReqSourceCount4," + // 55 56 57 58 59 60 61 62 "ReqCreatureOrGOId1, ReqCreatureOrGOId2, ReqCreatureOrGOId3, ReqCreatureOrGOId4, ReqCreatureOrGOCount1, ReqCreatureOrGOCount2, ReqCreatureOrGOCount3, ReqCreatureOrGOCount4," - // 67 68 69 70 + // 63 64 65 66 "ReqSpellCast1, ReqSpellCast2, ReqSpellCast3, ReqSpellCast4," - // 71 72 73 74 75 76 + // 67 68 69 70 71 72 "RewChoiceItemId1, RewChoiceItemId2, RewChoiceItemId3, RewChoiceItemId4, RewChoiceItemId5, RewChoiceItemId6," - // 77 78 79 80 81 82 + // 73 74 75 76 77 78 "RewChoiceItemCount1, RewChoiceItemCount2, RewChoiceItemCount3, RewChoiceItemCount4, RewChoiceItemCount5, RewChoiceItemCount6," - // 83 84 85 86 87 88 89 90 + // 79 80 81 82 83 84 85 86 "RewItemId1, RewItemId2, RewItemId3, RewItemId4, RewItemCount1, RewItemCount2, RewItemCount3, RewItemCount4," - // 91 92 93 94 95 96 97 98 99 100 + // 87 88 89 90 91 92 93 94 95 96 "RewRepFaction1, RewRepFaction2, RewRepFaction3, RewRepFaction4, RewRepFaction5, RewRepValue1, RewRepValue2, RewRepValue3, RewRepValue4, RewRepValue5," - // 101 102 103 104 105 106 107 108 109 110 111 + // 97 98 99 100 101 102 103 104 105 106 107 "RewHonorableKills, RewOrReqMoney, RewMoneyMaxLevel, RewSpell, RewSpellCast, RewMailTemplateId, RewMailDelaySecs, PointMapId, PointX, PointY, PointOpt," - // 112 113 114 115 116 117 118 119 120 121 - "DetailsEmote1, DetailsEmote2, DetailsEmote3, DetailsEmote4,IncompleteEmote, CompleteEmote, OfferRewardEmote1, OfferRewardEmote2, OfferRewardEmote3, OfferRewardEmote4," - // 122 123 + // 108 109 110 111 112 113 114 115 116 117 + "DetailsEmote1, DetailsEmote2, DetailsEmote3, DetailsEmote4, IncompleteEmote, CompleteEmote, OfferRewardEmote1, OfferRewardEmote2, OfferRewardEmote3, OfferRewardEmote4," + // 118 119 "StartScript, CompleteScript" " FROM quest_template"); if(result == NULL) @@ -3044,20 +3044,6 @@ void ObjectMgr::LoadQuests() qinfo->GetQuestId(),j+1,id,id); // no changes, quest can't be done for this requirement } - - if(!qinfo->ReqSourceCount[j]) - { - sLog.outErrorDb("Quest %u has `ReqSourceId%d` = %u but `ReqSourceCount%d` = 0, quest can't be done.", - qinfo->GetQuestId(),j+1,id,j+1); - qinfo->ReqSourceId[j] = 0; // prevent incorrect work of quest - } - - if(!qinfo->ReqSourceRef[j]) - { - sLog.outErrorDb("Quest %u has `ReqSourceId%d` = %u but `ReqSourceRef%d` = 0, quest can't be done.", - qinfo->GetQuestId(),j+1,id,j+1); - qinfo->ReqSourceId[j] = 0; // prevent incorrect work of quest - } } else { @@ -3067,41 +3053,6 @@ void ObjectMgr::LoadQuests() qinfo->GetQuestId(),j+1,j+1,qinfo->ReqSourceCount[j]); // no changes, quest ignore this data } - - if(qinfo->ReqSourceRef[j]>0) - { - sLog.outErrorDb("Quest %u has `ReqSourceId%d` = 0 but `ReqSourceRef%d` = %u.", - qinfo->GetQuestId(),j+1,j+1,qinfo->ReqSourceRef[j]); - // no changes, quest ignore this data - } - } - } - - for(int j = 0; j < QUEST_SOURCE_ITEM_IDS_COUNT; ++j ) - { - uint32 ref = qinfo->ReqSourceRef[j]; - if(ref) - { - if(ref > QUEST_OBJECTIVES_COUNT) - { - sLog.outErrorDb("Quest %u has `ReqSourceRef%d` = %u but max value in `ReqSourceRef%d` is %u, quest can't be done.", - qinfo->GetQuestId(),j+1,ref,j+1,QUEST_OBJECTIVES_COUNT); - // no changes, quest can't be done for this requirement - } - else - if(!qinfo->ReqItemId[ref-1] && !qinfo->ReqSpell[ref-1]) - { - sLog.outErrorDb("Quest %u has `ReqSourceRef%d` = %u but `ReqItemId%u` = 0 and `ReqSpellCast%u` = 0, quest can't be done.", - qinfo->GetQuestId(),j+1,ref,ref,ref); - // no changes, quest can't be done for this requirement - } - else if(qinfo->ReqItemId[ref-1] && qinfo->ReqSpell[ref-1]) - { - sLog.outErrorDb("Quest %u has `ReqItemId%u` = %u and `ReqSpellCast%u` = %u, quest can't have both fields <> 0, then can't be done.", - qinfo->GetQuestId(),ref,qinfo->ReqItemId[ref-1],ref,qinfo->ReqSpell[ref-1]); - // no changes, quest can't be done for this requirement - qinfo->ReqSourceId[j] = 0; // prevent incorrect work of quest - } } } diff --git a/src/game/Pet.cpp b/src/game/Pet.cpp index a66f1ccf4..7cf205f67 100644 --- a/src/game/Pet.cpp +++ b/src/game/Pet.cpp @@ -1023,7 +1023,7 @@ void Pet::_LoadSpellCooldowns() continue; data << uint32(spell_id); - data << uint32(uint32(db_time-curTime)*1000); // in m.secs + data << uint32(uint32(db_time-curTime)*IN_MILISECONDS); _AddCreatureSpellCooldown(spell_id,db_time); diff --git a/src/game/PetAI.cpp b/src/game/PetAI.cpp index ffc843235..424af420e 100644 --- a/src/game/PetAI.cpp +++ b/src/game/PetAI.cpp @@ -298,7 +298,7 @@ void PetAI::UpdateAllies() Unit* owner = i_pet.GetCharmerOrOwner(); Group *pGroup = NULL; - m_updateAlliesTimer = 10000; //update friendly targets every 10 seconds, lesser checks increase performance + m_updateAlliesTimer = 10*IN_MILISECONDS; //update friendly targets every 10 seconds, lesser checks increase performance if(!owner) return; diff --git a/src/game/Player.cpp b/src/game/Player.cpp index 73f45687c..dffbf5825 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -62,7 +62,7 @@ #include -#define ZONE_UPDATE_INTERVAL 1000 +#define ZONE_UPDATE_INTERVAL (1*IN_MILISECONDS) #define PLAYER_SKILL_INDEX(x) (PLAYER_SKILL_INFO_1_1 + ((x)*3)) #define PLAYER_SKILL_VALUE_INDEX(x) (PLAYER_SKILL_INDEX(x)+1) @@ -359,7 +359,7 @@ Player::Player (WorldSession *session): Unit(), m_achievementMgr(this) m_swingErrorMsg = 0; - m_DetectInvTimer = 1000; + m_DetectInvTimer = 1*IN_MILISECONDS; m_bgBattleGroundID = 0; m_bgTypeID = BATTLEGROUND_TYPE_NONE; @@ -416,6 +416,8 @@ Player::Player (WorldSession *session): Unit(), m_achievementMgr(this) m_InstanceValid = true; m_dungeonDifficulty = DIFFICULTY_NORMAL; + m_lastPotionId = 0; + for (int i = 0; i < BASEMOD_END; i++) { m_auraBaseMod[i][FLAT_MOD] = 0.0f; @@ -809,7 +811,7 @@ void Player::StartMirrorTimer(MirrorTimerType Type, uint32 MaxValue) void Player::ModifyMirrorTimer(MirrorTimerType Type, uint32 MaxValue, uint32 CurrentValue, uint32 Regen) { if(Type==BREATH_TIMER) - m_breathTimer = ((MaxValue + 1000) - CurrentValue) / Regen; + m_breathTimer = ((MaxValue + 1*IN_MILISECONDS) - CurrentValue) / Regen; WorldPacket data(SMSG_START_MIRROR_TIMER, (21)); data << (uint32)Type; @@ -833,6 +835,9 @@ void Player::StopMirrorTimer(MirrorTimerType Type) void Player::EnvironmentalDamage(uint64 guid, EnviromentalDamage type, uint32 damage) { + if(!isAlive() || isGameMaster()) + return; + WorldPacket data(SMSG_ENVIRONMENTALDAMAGELOG, (21)); data << (uint64)guid; data << (uint8)(type!=DAMAGE_FALL_TO_VOID ? type : DAMAGE_FALL); @@ -843,13 +848,18 @@ void Player::EnvironmentalDamage(uint64 guid, EnviromentalDamage type, uint32 da DealDamage(this, damage, NULL, SELF_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); - if(type==DAMAGE_FALL && !isAlive()) // DealDamage not apply item durability loss at self damage + if(!isAlive()) { - DEBUG_LOG("We are fall to death, loosing 10 percents durability"); - DurabilityLossAll(0.10f,false); - // durability lost message - WorldPacket data(SMSG_DURABILITY_DAMAGE_DEATH, 0); - GetSession()->SendPacket(&data); + if(type==DAMAGE_FALL) // DealDamage not apply item durability loss at self damage + { + DEBUG_LOG("We are fall to death, loosing 10 percents durability"); + DurabilityLossAll(0.10f,false); + // durability lost message + WorldPacket data(SMSG_DURABILITY_DAMAGE_DEATH, 0); + GetSession()->SendPacket(&data); + } + + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM, 1, type); } } @@ -867,7 +877,7 @@ void Player::HandleDrowning() return; } - uint32 UnderWaterTime = 3*MINUTE*1000; // default duration + uint32 UnderWaterTime = 3*MINUTE*IN_MILISECONDS; // default duration AuraList const& mModWaterBreathing = GetAurasByType(SPELL_AURA_MOD_WATER_BREATHING); for(AuraList::const_iterator i = mModWaterBreathing.begin(); i != mModWaterBreathing.end(); ++i) @@ -879,7 +889,7 @@ void Player::HandleDrowning() if (!(m_isunderwater & UNDERWATER_WATER_TRIGGER)) { m_isunderwater|= UNDERWATER_WATER_TRIGGER; - m_breathTimer = UnderWaterTime + 1000; + m_breathTimer = UnderWaterTime + 1*IN_MILISECONDS; } //single trigger "show Breathbar" if ( m_breathTimer <= UnderWaterTime && !(m_isunderwater & UNDERWATER_WATER_BREATHB)) @@ -924,7 +934,7 @@ void Player::HandleLava() if (!(m_isunderwater & UNDERWATER_INLAVA)) { m_isunderwater|= UNDERWATER_WATER_BREATHB; - m_breathTimer = 1000; + m_breathTimer = 1*IN_MILISECONDS; } */ // Reset BreathTimer and still in the lava @@ -933,11 +943,9 @@ void Player::HandleLava() uint64 guid = GetGUID(); uint32 damage = urand(600, 700); // TODO: Get more detailed information about lava damage - // if not gamemaster then deal damage - if ( !isGameMaster() ) - EnvironmentalDamage(guid, DAMAGE_LAVA, damage); + EnvironmentalDamage(guid, DAMAGE_LAVA, damage); - m_breathTimer = 1000; + m_breathTimer = 1*IN_MILISECONDS; } } else if (!isAlive()) // Disable breath timer and reset underwater flags @@ -1257,7 +1265,7 @@ void Player::Update( uint32 p_time ) { m_drunkTimer += p_time; - if (m_drunkTimer > 10000) + if (m_drunkTimer > 10*IN_MILISECONDS) HandleSobering(); } @@ -1320,6 +1328,8 @@ void Player::setDeathState(DeathState s) if(!ressSpellId) ressSpellId = GetResurrectionSpellId(); GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP, 1); + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DEATH, 1); + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON, 1); } Unit::setDeathState(s); @@ -1561,6 +1571,7 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati else // this will be used instead of the current location in SaveToDB m_teleport_dest = WorldLocation(mapid, x, y, z, orientation); + SetFallInformation(0, z); //BuildHeartBeatMsg(&data); @@ -2398,6 +2409,9 @@ void Player::InitStatsForLevel(bool reapplyMods) void Player::SendInitialSpells() { + time_t curTime = time(NULL); + time_t infTime = curTime + MONTH/2; + uint16 spellCount = 0; WorldPacket data(SMSG_INITIAL_SPELLS, (1+2+4*m_spells.size()+2+m_spellCooldowns.size()*(2+2+2+4+4))); @@ -2430,12 +2444,15 @@ void Player::SendInitialSpells() if(!sEntry) continue; + // not send infinity cooldown + if(itr->second.end > infTime) + continue; + data << uint32(itr->first); time_t cooldown = 0; - time_t curTime = time(NULL); if(itr->second.end > curTime) - cooldown = (itr->second.end-curTime)*1000; + cooldown = (itr->second.end-curTime)*IN_MILISECONDS; data << uint16(itr->second.itemid); // cast item id data << uint16(sEntry->Category); // spell category @@ -3122,8 +3139,8 @@ void Player::RemoveArenaSpellCooldowns() SpellEntry const * entry = sSpellStore.LookupEntry(itr->first); // check if spellentry is present and if the cooldown is less than 15 mins if( entry && - entry->RecoveryTime <= 15 * MINUTE * 1000 && - entry->CategoryRecoveryTime <= 15 * MINUTE * 1000 ) + entry->RecoveryTime <= 15 * MINUTE * IN_MILISECONDS && + entry->CategoryRecoveryTime <= 15 * MINUTE * IN_MILISECONDS ) { // notify player WorldPacket data(SMSG_CLEAR_COOLDOWN, (4+8)); @@ -3153,7 +3170,7 @@ void Player::RemoveAllSpellCooldown() void Player::_LoadSpellCooldowns(QueryResult *result) { - m_spellCooldowns.clear(); + // some cooldowns can be already set at aura loading... //QueryResult *result = CharacterDatabase.PQuery("SELECT spell,item,time FROM character_spell_cooldown WHERE guid = '%u'",GetGUIDLow()); @@ -3194,17 +3211,20 @@ void Player::_SaveSpellCooldowns() CharacterDatabase.PExecute("DELETE FROM character_spell_cooldown WHERE guid = '%u'", GetGUIDLow()); time_t curTime = time(NULL); + time_t infTime = curTime + MONTH/2; // remove outdated and save active for(SpellCooldowns::iterator itr = m_spellCooldowns.begin();itr != m_spellCooldowns.end();) { if(itr->second.end <= curTime) m_spellCooldowns.erase(itr++); - else + else if(itr->second.end <= infTime) // not save locked cooldowns, it will be reset or set at reload { CharacterDatabase.PExecute("INSERT INTO character_spell_cooldown (guid,spell,item,time) VALUES ('%u', '%u', '%u', '" I64FMTD "')", GetGUIDLow(), itr->first, itr->second.itemid, uint64(itr->second.end)); ++itr; } + else + ++itr; } } @@ -3319,6 +3339,7 @@ bool Player::resetTalents(bool no_cost) if(!no_cost) { ModifyMoney(-(int32)cost); + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TALENTS, cost); m_resetTalentsCost = cost; m_resetTalentsTime = time(NULL); @@ -3327,6 +3348,13 @@ bool Player::resetTalents(bool no_cost) //FIXME: remove pet before or after unlearn spells? for now after unlearn to allow removing of talent related, pet affecting auras RemovePet(NULL,PET_SAVE_NOT_IN_SLOT, true); + if(m_canTitanGrip) + { + m_canTitanGrip = false; + if(sWorld.getConfig(CONFIG_OFFHAND_CHECK_AT_TALENTS_RESET)) + AutoUnequipOffhandIfNeed(); + } + return true; } @@ -3883,7 +3911,7 @@ void Player::ResurrectPlayer(float restore_percent, bool applySickness) { if(Aura* Aur = GetAura(SPELL_ID_PASSIVE_RESURRECTION_SICKNESS,i)) { - Aur->SetAuraDuration(delta*1000); + Aur->SetAuraDuration(delta*IN_MILISECONDS); Aur->SendAuraUpdate(false); } } @@ -3904,7 +3932,7 @@ void Player::KillPlayer() ApplyModFlag(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTE_RELEASE_TIMER, !sMapStore.LookupEntry(GetMapId())->Instanceable()); // 6 minutes until repop at graveyard - m_deathTimer = 6*MINUTE*1000; + m_deathTimer = 6*MINUTE*IN_MILISECONDS; UpdateCorpseReclaimDelay(); // dependent at use SetDeathPvP() call before kill @@ -4228,10 +4256,8 @@ void Player::RepopAtGraveyard() WorldSafeLocsEntry const *ClosestGrave = NULL; // Special handle for battleground maps - BattleGround *bg = sBattleGroundMgr.GetBattleGround(GetBattleGroundId(), m_bgTypeID); - - if(bg && (bg->GetTypeID() == BATTLEGROUND_AB || bg->GetTypeID() == BATTLEGROUND_EY)) - ClosestGrave = bg->GetClosestGraveYard(GetPositionX(), GetPositionY(), GetPositionZ(), GetTeam()); + if( BattleGround *bg = GetBattleGround() ) + ClosestGrave = bg->GetClosestGraveYard(this); else ClosestGrave = objmgr.GetClosestGraveYard( GetPositionX(), GetPositionY(), GetPositionZ(), GetMapId(), GetTeam() ); @@ -5621,7 +5647,8 @@ void Player::SetFactionVisibleForFactionTemplateId(uint32 FactionTemplateId) if(!factionTemplateEntry) return; - SetFactionVisibleForFactionId(factionTemplateEntry->faction); + if(factionTemplateEntry->faction) + SetFactionVisibleForFactionId(factionTemplateEntry->faction); } void Player::SetFactionVisibleForFactionId(uint32 FactionId) @@ -6389,6 +6416,9 @@ void Player::UpdateZone(uint32 newZone) if(isAlive()) DestroyZoneLimitedItem( true, newZone ); + // check some item equip limitations (in result lost CanTitanGrip at talent reset, for example) + AutoUnequipOffhandIfNeed(); + // recent client version not send leave/join channel packets for built-in local channels UpdateLocalChannels( newZone ); @@ -9811,6 +9841,12 @@ uint8 Player::CanStoreItems( Item **pItems,int count) const pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, t ); if( pBag ) { + pBagProto = pBag->GetProto(); + + // special bag already checked + if( pBagProto && (pBagProto->Class != ITEM_CLASS_CONTAINER || pBagProto->SubClass != ITEM_SUBCLASS_CONTAINER)) + continue; + for(uint32 j = 0; j < pBag->GetBagSize(); j++) { if( inv_bags[t-INVENTORY_SLOT_BAG_START][j] == 0 ) @@ -12600,10 +12636,10 @@ void Player::AddQuest( Quest const *pQuest, Object *questGiver ) // shared timed quest if(questGiver && questGiver->GetTypeId()==TYPEID_PLAYER) - limittime = ((Player*)questGiver)->getQuestStatusMap()[quest_id].m_timer / 1000; + limittime = ((Player*)questGiver)->getQuestStatusMap()[quest_id].m_timer / IN_MILISECONDS; AddTimedQuest( quest_id ); - questStatusData.m_timer = limittime * 1000; + questStatusData.m_timer = limittime * IN_MILISECONDS; qtime = static_cast(time(NULL)) + limittime; } else @@ -12728,10 +12764,18 @@ void Player::RewardQuest( Quest const *pQuest, uint32 reward, Object* questGiver if ( getLevel() < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL) ) GiveXP( XP , NULL ); else - ModifyMoney( int32(pQuest->GetRewMoneyMaxLevel() * sWorld.getRate(RATE_DROP_MONEY)) ); + { + int32 money = int32(pQuest->GetRewMoneyMaxLevel() * sWorld.getRate(RATE_DROP_MONEY)); + ModifyMoney( money ); + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD, money); + } // Give player extra money if GetRewOrReqMoney > 0 and get ReqMoney if negative - ModifyMoney( pQuest->GetRewOrReqMoney() ); + if(pQuest->GetRewOrReqMoney()) + { + ModifyMoney( pQuest->GetRewOrReqMoney() ); + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD, pQuest->GetRewOrReqMoney()); + } // honor reward if(pQuest->GetRewHonorableKills()) @@ -13762,28 +13806,21 @@ bool Player::HasQuestForItem( uint32 itemid ) const for (int j = 0; j < QUEST_SOURCE_ITEM_IDS_COUNT; j++) { // examined item is a source item - if (qinfo->ReqSourceId[j] == itemid && qinfo->ReqSourceRef[j] > 0 && qinfo->ReqSourceRef[j] <= QUEST_OBJECTIVES_COUNT) + if (qinfo->ReqSourceId[j] == itemid) { - uint32 idx = qinfo->ReqSourceRef[j]-1; + ItemPrototype const *pProto = objmgr.GetItemPrototype(itemid); - // total count of created ReqItems and SourceItems is less than ReqItemCount - if(qinfo->ReqItemId[idx] != 0 && - q_status.m_itemcount[idx] * qinfo->ReqSourceCount[j] + GetItemCount(itemid,true) < qinfo->ReqItemCount[idx] * qinfo->ReqSourceCount[j]) + // 'unique' item + if (pProto->MaxCount && GetItemCount(itemid,true) < pProto->MaxCount) return true; - // total count of casted ReqCreatureOrGOs and SourceItems is less than ReqCreatureOrGOCount - if (qinfo->ReqCreatureOrGOId[idx] != 0) + // allows custom amount drop when not 0 + if (qinfo->ReqSourceCount[j]) { - if(q_status.m_creatureOrGOcount[idx] * qinfo->ReqSourceCount[j] + GetItemCount(itemid,true) < qinfo->ReqCreatureOrGOCount[idx] * qinfo->ReqSourceCount[j]) + if (GetItemCount(itemid,true) < qinfo->ReqSourceCount[j]) return true; - } - // spell with SPELL_EFFECT_QUEST_COMPLETE or SPELL_EFFECT_SEND_EVENT (with script) case - else if(qinfo->ReqSpell[idx] != 0) - { - // not casted and need more reagents/item for use. - if(!q_status.m_explored && GetItemCount(itemid,true) < qinfo->ReqSourceCount[j]) - return true; - } + } else if (GetItemCount(itemid,true) < pProto->Stackable) + return true; } } } @@ -14323,6 +14360,14 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder ) { if( (*iter)->GetGUIDLow() == transGUID) { + MapEntry const* transMapEntry = sMapStore.LookupEntry((*iter)->GetMapId()); + // client without expansion support + if(GetSession()->Expansion() < transMapEntry->Expansion()) + { + sLog.outDebug("Player %s using client without required expansion tried login at transport at non accessible map %u", GetName(), (*iter)->GetMapId()); + break; + } + m_transport = *iter; m_transport->AddPassenger(this); SetMapId(m_transport->GetMapId()); @@ -14332,7 +14377,7 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder ) if(!m_transport) { - sLog.outError("ERROR: Player (guidlow %d) have invalid transport guid (%u). Teleport to default race/class locations.", + sLog.outError("ERROR: Player (guidlow %d) have problems with transport guid (%u). Teleport to default race/class locations.", guid,transGUID); RelocateToHomebind(); @@ -14345,6 +14390,16 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder ) transGUID = 0; } } + else // not transport case + { + MapEntry const* mapEntry = sMapStore.LookupEntry(GetMapId()); + // client without expansion support + if(GetSession()->Expansion() < mapEntry->Expansion()) + { + sLog.outDebug("Player %s using client without required expansion tried login at non accessible map %u", GetName(), GetMapId()); + RelocateToHomebind(); + } + } // NOW player must have valid map // load the player's map here if it's not already loaded @@ -15131,7 +15186,7 @@ void Player::_LoadQuestStatus(QueryResult *result) if (quest_time <= sWorld.GetGameTime()) questStatusData.m_timer = 1; else - questStatusData.m_timer = (quest_time - sWorld.GetGameTime()) * 1000; + questStatusData.m_timer = (quest_time - sWorld.GetGameTime()) * IN_MILISECONDS; } else quest_time = 0; @@ -15557,9 +15612,11 @@ bool Player::_LoadHomeBind(QueryResult *result) m_homebindZ = fields[4].GetFloat(); delete result; - // accept saved data only for valid position (and non instanceable) + MapEntry const* bindMapEntry = sMapStore.LookupEntry(m_homebindMapId); + + // accept saved data only for valid position (and non instanceable), and accessable if( MapManager::IsValidMapCoord(m_homebindMapId,m_homebindX,m_homebindY,m_homebindZ) && - !sMapStore.LookupEntry(m_homebindMapId)->Instanceable() ) + !bindMapEntry->Instanceable() && GetSession()->Expansion() >= bindMapEntry->Expansion()) { ok = true; } @@ -15818,6 +15875,7 @@ void Player::_SaveAuras() AuraMap::const_iterator itr2 = itr; // save previous spellEffectPair to db itr2--; + SpellEntry const *spellInfo = itr2->second->GetSpellProto(); //skip all auras from spells that are passive or need a shapeshift @@ -15992,11 +16050,11 @@ void Player::_SaveQuestStatus() case QUEST_NEW : CharacterDatabase.PExecute("INSERT INTO character_queststatus (guid,quest,status,rewarded,explored,timer,mobcount1,mobcount2,mobcount3,mobcount4,itemcount1,itemcount2,itemcount3,itemcount4) " "VALUES ('%u', '%u', '%u', '%u', '%u', '" I64FMTD "', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u')", - GetGUIDLow(), i->first, i->second.m_status, i->second.m_rewarded, i->second.m_explored, uint64(i->second.m_timer / 1000 + sWorld.GetGameTime()), i->second.m_creatureOrGOcount[0], i->second.m_creatureOrGOcount[1], i->second.m_creatureOrGOcount[2], i->second.m_creatureOrGOcount[3], i->second.m_itemcount[0], i->second.m_itemcount[1], i->second.m_itemcount[2], i->second.m_itemcount[3]); + GetGUIDLow(), i->first, i->second.m_status, i->second.m_rewarded, i->second.m_explored, uint64(i->second.m_timer / IN_MILISECONDS+ sWorld.GetGameTime()), i->second.m_creatureOrGOcount[0], i->second.m_creatureOrGOcount[1], i->second.m_creatureOrGOcount[2], i->second.m_creatureOrGOcount[3], i->second.m_itemcount[0], i->second.m_itemcount[1], i->second.m_itemcount[2], i->second.m_itemcount[3]); break; case QUEST_CHANGED : CharacterDatabase.PExecute("UPDATE character_queststatus SET status = '%u',rewarded = '%u',explored = '%u',timer = '" I64FMTD "',mobcount1 = '%u',mobcount2 = '%u',mobcount3 = '%u',mobcount4 = '%u',itemcount1 = '%u',itemcount2 = '%u',itemcount3 = '%u',itemcount4 = '%u' WHERE guid = '%u' AND quest = '%u' ", - i->second.m_status, i->second.m_rewarded, i->second.m_explored, uint64(i->second.m_timer / 1000 + sWorld.GetGameTime()), i->second.m_creatureOrGOcount[0], i->second.m_creatureOrGOcount[1], i->second.m_creatureOrGOcount[2], i->second.m_creatureOrGOcount[3], i->second.m_itemcount[0], i->second.m_itemcount[1], i->second.m_itemcount[2], i->second.m_itemcount[3], GetGUIDLow(), i->first ); + i->second.m_status, i->second.m_rewarded, i->second.m_explored, uint64(i->second.m_timer / IN_MILISECONDS + sWorld.GetGameTime()), i->second.m_creatureOrGOcount[0], i->second.m_creatureOrGOcount[1], i->second.m_creatureOrGOcount[2], i->second.m_creatureOrGOcount[3], i->second.m_itemcount[0], i->second.m_itemcount[1], i->second.m_itemcount[2], i->second.m_itemcount[3], GetGUIDLow(), i->first ); break; case QUEST_UNCHANGED: break; @@ -16679,7 +16737,7 @@ void Player::PetSpellInitialize() time_t cooldown = 0; if(itr->second > curTime) - cooldown = (itr->second - curTime) * 1000; + cooldown = (itr->second - curTime) * IN_MILISECONDS; data << uint32(itr->first); // spellid data << uint16(0); // spell category? @@ -16692,7 +16750,7 @@ void Player::PetSpellInitialize() time_t cooldown = 0; if(itr->second > curTime) - cooldown = (itr->second - curTime) * 1000; + cooldown = (itr->second - curTime) * IN_MILISECONDS; data << uint32(itr->first); // spellid data << uint16(0); // spell category? @@ -17167,6 +17225,7 @@ bool Player::ActivateTaxiPathTo(std::vector const& nodes, uint32 mount_i //Checks and preparations done, DO FLIGHT ModifyMoney(-(int32)totalcost); + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TRAVELLING, totalcost); // prevent stealth flight RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); @@ -17209,7 +17268,7 @@ void Player::ProhibitSpellScholl(SpellSchoolMask idSchoolMask, uint32 unTimeMs ) { data << unSpellId; data << unTimeMs; // in m.secs - AddSpellCooldown(unSpellId, 0, curTime + unTimeMs/1000); + AddSpellCooldown(unSpellId, 0, curTime + unTimeMs/IN_MILISECONDS); } } GetSession()->SendPacket(&data); @@ -17576,6 +17635,102 @@ void Player::UpdatePvP(bool state, bool ovrride) } } +void Player::AddSpellAndCategoryCooldowns(SpellEntry const* spellInfo, uint32 itemId, Spell* spell, bool infinityCooldown) +{ + // init cooldown values + uint32 cat = 0; + int32 rec = -1; + int32 catrec = -1; + + // some special item spells without correct cooldown in SpellInfo + // cooldown information stored in item prototype + // This used in same way in WorldSession::HandleItemQuerySingleOpcode data sending to client. + + if(itemId) + { + if(ItemPrototype const* proto = ObjectMgr::GetItemPrototype(itemId)) + { + for(int idx = 0; idx < 5; ++idx) + { + if(proto->Spells[idx].SpellId == spellInfo->Id) + { + cat = proto->Spells[idx].SpellCategory; + rec = proto->Spells[idx].SpellCooldown; + catrec = proto->Spells[idx].SpellCategoryCooldown; + break; + } + } + } + } + + // if no cooldown found above then base at DBC data + if(rec < 0 && catrec < 0) + { + cat = spellInfo->Category; + rec = spellInfo->RecoveryTime; + catrec = spellInfo->CategoryRecoveryTime; + } + + time_t curTime = time(NULL); + + time_t catrecTime; + time_t recTime; + + // overwrite time for selected category + if(infinityCooldown) + { + // use +MONTH as infinity mark for spell cooldown (will checked as MONTH/2 at save ans skipped) + // but not allow ignore until reset or re-login + catrecTime = catrec > 0 ? curTime+MONTH : 0; + recTime = rec > 0 ? curTime+MONTH : catrecTime; + } + else + { + // shoot spells used equipped item cooldown values already assigned in GetAttackTime(RANGED_ATTACK) + // prevent 0 cooldowns set by another way + if (rec <= 0 && catrec <= 0 && (cat == 76 || IsAutoRepeatRangedSpell(spellInfo) && spellInfo->Id != SPELL_ID_AUTOSHOT)) + rec = GetAttackTime(RANGED_ATTACK); + + // Now we have cooldown data (if found any), time to apply mods + if(rec > 0) + ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, rec, spell); + + if(catrec > 0) + ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, catrec, spell); + + // replace negative cooldowns by 0 + if (rec < 0) rec = 0; + if (catrec < 0) catrec = 0; + + // no cooldown after applying spell mods + if( rec == 0 && catrec == 0) + return; + + catrecTime = catrec ? curTime+catrec/IN_MILISECONDS : 0; + recTime = rec ? curTime+rec/IN_MILISECONDS : catrecTime; + } + + // self spell cooldown + if(recTime > 0) + AddSpellCooldown(spellInfo->Id, itemId, recTime); + + // category spells + if (cat && catrec > 0) + { + SpellCategoryStore::const_iterator i_scstore = sSpellCategoryStore.find(cat); + if(i_scstore != sSpellCategoryStore.end()) + { + for(SpellCategorySet::const_iterator i_scset = i_scstore->second.begin(); i_scset != i_scstore->second.end(); ++i_scset) + { + if(*i_scset == spellInfo->Id) // skip main spell, already handled above + continue; + + AddSpellCooldown(*i_scset, itemId, catrecTime); + } + } + } +} + void Player::AddSpellCooldown(uint32 spellid, uint32 itemid, time_t end_time) { SpellCooldown sc; @@ -17584,25 +17739,41 @@ void Player::AddSpellCooldown(uint32 spellid, uint32 itemid, time_t end_time) m_spellCooldowns[spellid] = sc; } -void Player::SendCooldownEvent(SpellEntry const *spellInfo) +void Player::SendCooldownEvent(SpellEntry const *spellInfo, uint32 itemId, Spell* spell) { - if ( !(spellInfo->Attributes & SPELL_ATTR_DISABLED_WHILE_ACTIVE) ) - return; + // start cooldowns at server side, if any + AddSpellAndCategoryCooldowns(spellInfo,itemId,spell); - // Get spell cooldown - int32 cooldown = GetSpellRecoveryTime(spellInfo); - // Apply spellmods - ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, cooldown); - if (cooldown < 0) - cooldown = 0; - // Add cooldown - AddSpellCooldown(spellInfo->Id, 0, time(NULL) + cooldown / 1000); - // Send activate + // Send activate cooldown timer (possible 0) at client side WorldPacket data(SMSG_COOLDOWN_EVENT, (4+8)); data << spellInfo->Id; data << GetGUID(); SendDirectMessage(&data); } + +void Player::UpdatePotionCooldown(Spell* spell) +{ + // no potion used i combat or still in combat + if(!m_lastPotionId || isInCombat()) + return; + + // Call not from spell cast, send cooldown event for item spells if no in combat + if(!spell) + { + // spell/item pair let set proper cooldown (except not existed charged spell cooldown spellmods for potions) + if(ItemPrototype const* proto = ObjectMgr::GetItemPrototype(m_lastPotionId)) + for(int idx = 0; idx < 5; ++idx) + if(proto->Spells[idx].SpellId && proto->Spells[idx].SpellTrigger == ITEM_SPELLTRIGGER_ON_USE) + if(SpellEntry const* spellInfo = sSpellStore.LookupEntry(proto->Spells[idx].SpellId)) + SendCooldownEvent(spellInfo,m_lastPotionId); + } + // from spell cases (m_lastPotionId set in Spell::SendSpellCooldown) + else + SendCooldownEvent(spell->m_spellInfo,m_lastPotionId,spell); + + m_lastPotionId = 0; +} + //slot to be excluded while counting bool Player::EnchantmentFitsRequirements(uint32 enchantmentcondition, int8 slot) { @@ -18479,7 +18650,7 @@ BGQueueIdBasedOnLevel Player::GetBattleGroundQueueIdFromLevel(BattleGroundTypeId float Player::GetReputationPriceDiscount( Creature const* pCreature ) const { FactionTemplateEntry const* vendor_faction = pCreature->getFactionTemplateEntry(); - if(!vendor_faction) + if(!vendor_faction || !vendor_faction->faction) return 1.0f; ReputationRank rank = GetReputationRank(vendor_faction->faction); @@ -18626,8 +18797,8 @@ void Player::AutoUnequipOffhandIfNeed() if(!offItem) return; - // need unequip for 2h-weapon without TitanGrip - if (!IsTwoHandUsed()) + // need unequip offhand for 2h-weapon without TitanGrip (in any from hands) + if (CanTitanGrip() || (offItem->GetProto()->InventoryType != INVTYPE_2HWEAPON && !IsTwoHandUsed())) return; ItemPosCountVec off_dest; @@ -18639,7 +18810,16 @@ void Player::AutoUnequipOffhandIfNeed() } else { - sLog.outError("Player::EquipItem: Can's store offhand item at 2hand item equip for player (GUID: %u).",GetGUIDLow()); + MailItemsInfo mi; + mi.AddItem(offItem->GetGUIDLow(), offItem->GetEntry(), offItem); + MoveItemFromInventory(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND, true); + CharacterDatabase.BeginTransaction(); + offItem->DeleteFromInventoryDB(); // deletes item from character's inventory + offItem->SaveToDB(); // recursive and not have transaction guard into self, item not in inventory and can be save standalone + CharacterDatabase.CommitTransaction(); + + std::string subject = GetSession()->GetMangosString(LANG_NOT_EQUIPPED_ITEM); + WorldSession::SendMailTo(this, MAIL_NORMAL, MAIL_STATIONERY_GM, GetGUIDLow(), GetGUIDLow(), subject, 0, &mi, 0, 0, MAIL_CHECK_MASK_NONE); } } @@ -19070,7 +19250,7 @@ void Player::SendCorpseReclaimDelay(bool load) //! corpse reclaim delay 30 * 1000ms or longer at often deaths WorldPacket data(SMSG_CORPSE_RECLAIM_DELAY, 4); - data << uint32(delay*1000); + data << uint32(delay*IN_MILISECONDS); GetSession()->SendPacket( &data ); } diff --git a/src/game/Player.h b/src/game/Player.h index bdf48b708..d3ea09598 100644 --- a/src/game/Player.h +++ b/src/game/Player.h @@ -1509,14 +1509,17 @@ class MANGOS_DLL_SPEC Player : public Unit time_t t = time(NULL); return itr != m_spellCooldowns.end() && itr->second.end > t ? itr->second.end - t : 0; } + void AddSpellAndCategoryCooldowns(SpellEntry const* spellInfo, uint32 itemId, Spell* spell = NULL, bool infinityCooldown = false ); void AddSpellCooldown(uint32 spell_id, uint32 itemid, time_t end_time); - void SendCooldownEvent(SpellEntry const *spellInfo); + void SendCooldownEvent(SpellEntry const *spellInfo, uint32 itemId = 0, Spell* spell = NULL); void ProhibitSpellScholl(SpellSchoolMask idSchoolMask, uint32 unTimeMs ); void RemoveSpellCooldown(uint32 spell_id) { m_spellCooldowns.erase(spell_id); } void RemoveArenaSpellCooldowns(); void RemoveAllSpellCooldown(); void _LoadSpellCooldowns(QueryResult *result); void _SaveSpellCooldowns(); + void SetLastPotionId(uint32 item_id) { m_lastPotionId = item_id; } + void UpdatePotionCooldown(Spell* spell = NULL); void setResurrectRequestData(uint64 guid, uint32 mapId, float X, float Y, float Z, uint32 health, uint32 mana) { @@ -1998,7 +2001,7 @@ class MANGOS_DLL_SPEC Player : public Unit /*** REST SYSTEM ***/ /*********************************************************/ - bool isRested() const { return GetRestTime() >= 10000; } + bool isRested() const { return GetRestTime() >= 10*IN_MILISECONDS; } uint32 GetXPRestBonus(uint32 xp); uint32 GetRestTime() const { return m_restTime;}; void SetRestTime(uint32 v) { m_restTime = v;}; @@ -2029,6 +2032,8 @@ class MANGOS_DLL_SPEC Player : public Unit m_lastFallTime = time; m_lastFallZ = z; } + void HandleFall(MovementInfo const& movementInfo); + bool isMoving() const { return HasUnitMovementFlag(movementFlagsMask); } bool isMovingOrTurning() const { return HasUnitMovementFlag(movementOrTurningFlagsMask); } @@ -2297,6 +2302,7 @@ class MANGOS_DLL_SPEC Player : public Unit PlayerMails m_mail; PlayerSpellMap m_spells; SpellCooldowns m_spellCooldowns; + uint32 m_lastPotionId; // last used health/mana potion in combat, that block next potion use ActionButtonList m_actionButtons; @@ -2445,7 +2451,7 @@ template T Player::ApplySpellMod(uint32 spellId, SpellModOp op, T &bas continue; // special case (skip >10sec spell casts for instant cast setting) - if( mod->op==SPELLMOD_CASTING_TIME && basevalue >= T(10000) && mod->value <= -100) + if( mod->op==SPELLMOD_CASTING_TIME && basevalue >= T(10*IN_MILISECONDS) && mod->value <= -100) continue; totalpct += mod->value; diff --git a/src/game/QueryHandler.cpp b/src/game/QueryHandler.cpp index 9902ef34c..63376a88e 100644 --- a/src/game/QueryHandler.cpp +++ b/src/game/QueryHandler.cpp @@ -192,8 +192,8 @@ void WorldSession::HandleCreatureQueryOpcode( WorldPacket & recv_data ) data << uint32(ci->DisplayID_A2); // modelid_male2 ? data << uint32(ci->DisplayID_H2); // modelid_femmale2 ? data << uint32(0); // new in 3.1 - data << float(1.0f); // unk - data << float(1.0f); // unk + data << float(ci->unk16); // unk + data << float(ci->unk17); // unk data << uint8(ci->RacialLeader); for(uint32 i = 0; i < 4; ++i) data << uint32(0); // added in 3.1 diff --git a/src/game/QuestDef.cpp b/src/game/QuestDef.cpp index 5d3578200..35ffefef4 100644 --- a/src/game/QuestDef.cpp +++ b/src/game/QuestDef.cpp @@ -73,59 +73,56 @@ Quest::Quest(Field * questRecord) for (int i = 0; i < QUEST_SOURCE_ITEM_IDS_COUNT; ++i) ReqSourceCount[i] = questRecord[51+i].GetUInt32(); - for (int i = 0; i < QUEST_SOURCE_ITEM_IDS_COUNT; ++i) - ReqSourceRef[i] = questRecord[55+i].GetUInt32(); + for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i) + ReqCreatureOrGOId[i] = questRecord[55+i].GetInt32(); for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i) - ReqCreatureOrGOId[i] = questRecord[59+i].GetInt32(); + ReqCreatureOrGOCount[i] = questRecord[59+i].GetUInt32(); for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i) - ReqCreatureOrGOCount[i] = questRecord[63+i].GetUInt32(); - - for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i) - ReqSpell[i] = questRecord[67+i].GetUInt32(); + ReqSpell[i] = questRecord[63+i].GetUInt32(); for (int i = 0; i < QUEST_REWARD_CHOICES_COUNT; ++i) - RewChoiceItemId[i] = questRecord[71+i].GetUInt32(); + RewChoiceItemId[i] = questRecord[67+i].GetUInt32(); for (int i = 0; i < QUEST_REWARD_CHOICES_COUNT; ++i) - RewChoiceItemCount[i] = questRecord[77+i].GetUInt32(); + RewChoiceItemCount[i] = questRecord[73+i].GetUInt32(); for (int i = 0; i < QUEST_REWARDS_COUNT; ++i) - RewItemId[i] = questRecord[83+i].GetUInt32(); + RewItemId[i] = questRecord[79+i].GetUInt32(); for (int i = 0; i < QUEST_REWARDS_COUNT; ++i) - RewItemCount[i] = questRecord[87+i].GetUInt32(); + RewItemCount[i] = questRecord[83+i].GetUInt32(); for (int i = 0; i < QUEST_REPUTATIONS_COUNT; ++i) - RewRepFaction[i] = questRecord[91+i].GetUInt32(); + RewRepFaction[i] = questRecord[87+i].GetUInt32(); for (int i = 0; i < QUEST_REPUTATIONS_COUNT; ++i) - RewRepValue[i] = questRecord[96+i].GetInt32(); + RewRepValue[i] = questRecord[92+i].GetInt32(); - RewHonorableKills = questRecord[101].GetUInt32(); - RewOrReqMoney = questRecord[102].GetInt32(); - RewMoneyMaxLevel = questRecord[103].GetUInt32(); - RewSpell = questRecord[104].GetUInt32(); - RewSpellCast = questRecord[105].GetUInt32(); - RewMailTemplateId = questRecord[106].GetUInt32(); - RewMailDelaySecs = questRecord[107].GetUInt32(); - PointMapId = questRecord[108].GetUInt32(); - PointX = questRecord[109].GetFloat(); - PointY = questRecord[110].GetFloat(); - PointOpt = questRecord[111].GetUInt32(); + RewHonorableKills = questRecord[97].GetUInt32(); + 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[112+i].GetUInt32(); + DetailsEmote[i] = questRecord[108+i].GetUInt32(); - IncompleteEmote = questRecord[116].GetUInt32(); - CompleteEmote = questRecord[117].GetUInt32(); + IncompleteEmote = questRecord[112].GetUInt32(); + CompleteEmote = questRecord[113].GetUInt32(); for (int i = 0; i < QUEST_EMOTE_COUNT; ++i) - OfferRewardEmote[i] = questRecord[118+i].GetInt32(); + OfferRewardEmote[i] = questRecord[114+i].GetInt32(); - QuestStartScript = questRecord[122].GetUInt32(); - QuestCompleteScript = questRecord[123].GetUInt32(); + QuestStartScript = questRecord[118].GetUInt32(); + QuestCompleteScript = questRecord[119].GetUInt32(); QuestFlags |= SpecialFlags << 16; diff --git a/src/game/QuestDef.h b/src/game/QuestDef.h index f29168a49..166096715 100644 --- a/src/game/QuestDef.h +++ b/src/game/QuestDef.h @@ -229,7 +229,6 @@ class Quest 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]; diff --git a/src/game/Spell.cpp b/src/game/Spell.cpp index 7ab289e45..d341c80d8 100644 --- a/src/game/Spell.cpp +++ b/src/game/Spell.cpp @@ -46,7 +46,7 @@ #include "BattleGround.h" #include "Util.h" -#define SPELL_CHANNEL_UPDATE_INTERVAL 1000 +#define SPELL_CHANNEL_UPDATE_INTERVAL (1*IN_MILISECONDS) extern pEffect SpellEffects[TOTAL_SPELL_EFFECTS]; @@ -2520,93 +2520,21 @@ void Spell::SendSpellCooldown() return; Player* _player = (Player*)m_caster; - // Add cooldown for max (disable spell) - // Cooldown started on SendCooldownEvent call - if (m_spellInfo->Attributes & SPELL_ATTR_DISABLED_WHILE_ACTIVE) + + // mana/health potions, disabled by client + if (m_spellInfo->Category==SPELLCATEGORY_HEALTH_MANA_POTIONS) { - _player->AddSpellCooldown(m_spellInfo->Id, 0, time(NULL) - 1); + // need in some way provided data for Spell::finish SendCooldownEvent + if(m_CastItem) + _player->SetLastPotionId(m_CastItem->GetEntry()); return; } - // init cooldown values - uint32 cat = 0; - int32 rec = -1; - int32 catrec = -1; - - // some special item spells without correct cooldown in SpellInfo - // cooldown information stored in item prototype - // This used in same way in WorldSession::HandleItemQuerySingleOpcode data sending to client. - - if(m_CastItem) - { - ItemPrototype const* proto = m_CastItem->GetProto(); - if(proto) - { - for(int idx = 0; idx < MAX_ITEM_PROTO_SPELLS; ++idx) - { - if(proto->Spells[idx].SpellId == m_spellInfo->Id) - { - cat = proto->Spells[idx].SpellCategory; - rec = proto->Spells[idx].SpellCooldown; - catrec = proto->Spells[idx].SpellCategoryCooldown; - break; - } - } - } - } - - // if no cooldown found above then base at DBC data - if(rec < 0 && catrec < 0) - { - cat = m_spellInfo->Category; - rec = m_spellInfo->RecoveryTime; - catrec = m_spellInfo->CategoryRecoveryTime; - } - - // shoot spells used equipped item cooldown values already assigned in GetAttackTime(RANGED_ATTACK) - // prevent 0 cooldowns set by another way - if (rec <= 0 && catrec <= 0 && (cat == 76 || IsAutoRepeatRangedSpell(m_spellInfo) && m_spellInfo->Id != SPELL_ID_AUTOSHOT)) - rec = _player->GetAttackTime(RANGED_ATTACK); - - // Now we have cooldown data (if found any), time to apply mods - if(rec > 0) - _player->ApplySpellMod(m_spellInfo->Id, SPELLMOD_COOLDOWN, rec, this); - - if(catrec > 0) - _player->ApplySpellMod(m_spellInfo->Id, SPELLMOD_COOLDOWN, catrec, this); - - // replace negative cooldowns by 0 - if (rec < 0) rec = 0; - if (catrec < 0) catrec = 0; - - // no cooldown after applying spell mods - if( rec == 0 && catrec == 0) + // have infinity cooldown but set at aura apply + if(m_spellInfo->Attributes & SPELL_ATTR_DISABLED_WHILE_ACTIVE) return; - time_t curTime = time(NULL); - - time_t catrecTime = catrec ? curTime+catrec/1000 : 0; // in secs - time_t recTime = rec ? curTime+rec/1000 : catrecTime;// in secs - - // self spell cooldown - if(recTime > 0) - _player->AddSpellCooldown(m_spellInfo->Id, m_CastItem ? m_CastItem->GetEntry() : 0, recTime); - - // category spells - if (catrec > 0) - { - SpellCategoryStore::const_iterator i_scstore = sSpellCategoryStore.find(cat); - if(i_scstore != sSpellCategoryStore.end()) - { - for(SpellCategorySet::const_iterator i_scset = i_scstore->second.begin(); i_scset != i_scstore->second.end(); ++i_scset) - { - if(*i_scset == m_spellInfo->Id) // skip main spell, already handled above - continue; - - _player->AddSpellCooldown(*i_scset, m_CastItem ? m_CastItem->GetEntry() : 0, catrecTime); - } - } - } + _player->AddSpellAndCategoryCooldowns(m_spellInfo,m_CastItem ? m_CastItem->GetEntry() : 0, this); } void Spell::update(uint32 difftime) @@ -2797,6 +2725,10 @@ void Spell::finish(bool ok) ((Player*)m_caster)->ClearComboPoints(); } + // mana/health potions, disabled by client, send event "not in combat" + if (m_caster->GetTypeId() == TYPEID_PLAYER && m_spellInfo->Category == SPELLCATEGORY_HEALTH_MANA_POTIONS) + ((Player*)m_caster)->UpdatePotionCooldown(this); + // call triggered spell only at successful cast (after clear combo points -> for add some if need) if(!m_TriggerSpells.empty()) TriggerSpell(); @@ -3818,7 +3750,7 @@ uint8 Spell::CanCast(bool strict) // - with greater than 15 min CD without SPELL_ATTR_EX4_USABLE_IN_ARENA flag // - with SPELL_ATTR_EX4_NOT_USABLE_IN_ARENA flag if( (m_spellInfo->AttributesEx4 & SPELL_ATTR_EX4_NOT_USABLE_IN_ARENA) || - GetSpellRecoveryTime(m_spellInfo) > 15 * MINUTE * 1000 && !(m_spellInfo->AttributesEx4 & SPELL_ATTR_EX4_USABLE_IN_ARENA) ) + GetSpellRecoveryTime(m_spellInfo) > 15 * MINUTE * IN_MILISECONDS && !(m_spellInfo->AttributesEx4 & SPELL_ATTR_EX4_USABLE_IN_ARENA) ) if(MapEntry const* mapEntry = sMapStore.LookupEntry(m_caster->GetMapId())) if(mapEntry->IsBattleArena()) return SPELL_FAILED_NOT_IN_ARENA; diff --git a/src/game/Spell.h b/src/game/Spell.h index 620d2c6a4..2b70422ac 100644 --- a/src/game/Spell.h +++ b/src/game/Spell.h @@ -204,7 +204,7 @@ enum SpellState SPELL_STATE_DELAYED = 5 }; -#define SPELL_SPELL_CHANNEL_UPDATE_INTERVAL 1000 +#define SPELL_SPELL_CHANNEL_UPDATE_INTERVAL (1*IN_MILISECONDS) typedef std::multimap SpellTargetTimeMap; diff --git a/src/game/SpellAuras.cpp b/src/game/SpellAuras.cpp index fcd96ac5c..f776a53de 100644 --- a/src/game/SpellAuras.cpp +++ b/src/game/SpellAuras.cpp @@ -581,7 +581,7 @@ void Aura::Update(uint32 diff) { Powers powertype = Powers(m_spellProto->powerType); int32 manaPerSecond = m_spellProto->manaPerSecond + m_spellProto->manaPerSecondPerLevel * caster->getLevel(); - m_timeCla = 1000; + m_timeCla = 1*IN_MILISECONDS; if (manaPerSecond) { if(powertype==POWER_HEALTH) @@ -940,6 +940,16 @@ void Aura::_AddAura() Unit* caster = GetCaster(); + // set infinity cooldown state for spells + if(caster && caster->GetTypeId() == TYPEID_PLAYER) + { + if (m_spellProto->Attributes & SPELL_ATTR_DISABLED_WHILE_ACTIVE) + { + Item* castItem = m_castItemGuid ? ((Player*)caster)->GetItemByGuid(m_castItemGuid) : NULL; + ((Player*)caster)->AddSpellAndCategoryCooldowns(m_spellProto,castItem ? castItem->GetEntry() : 0, NULL,true); + } + } + // passive auras (except totem auras) do not get placed in the slots // area auras with SPELL_AURA_NONE are not shown on target if((!m_isPassive || (caster && caster->GetTypeId() == TYPEID_UNIT && ((Creature*)caster)->isTotem())) && @@ -1122,6 +1132,7 @@ void Aura::_RemoveAura() if(caster && caster->GetTypeId() == TYPEID_PLAYER) { if ( GetSpellProto()->Attributes & SPELL_ATTR_DISABLED_WHILE_ACTIVE ) + // note: item based cooldowns and cooldown spell mods with charges ignored (unknown existed cases) ((Player*)caster)->SendCooldownEvent(GetSpellProto()); } } @@ -2067,22 +2078,6 @@ void Aura::HandleAuraDummy(bool apply, bool Real) m_modifier.m_amount = caster->SpellHealingBonus(m_target, GetSpellProto(), m_modifier.m_amount, SPELL_DIRECT_DAMAGE); return; } - - // some auras applied at aura apply - if(GetEffIndex()==0 && m_target->GetTypeId()==TYPEID_PLAYER) - { - SpellAreaForAreaMapBounds saBounds = spellmgr.GetSpellAreaForAuraMapBounds(GetId()); - if(saBounds.first != saBounds.second) - { - uint32 zone = m_target->GetZoneId(); - uint32 area = m_target->GetAreaId(); - - for(SpellAreaForAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr) - if(itr->second->autocast && itr->second->IsFitToRequirements((Player*)m_target,zone,area)) - if( !m_target->HasAura(itr->second->spellId,0) ) - m_target->CastSpell(m_target,itr->second->spellId,true); - } - } } // AT REMOVE else @@ -2128,30 +2123,29 @@ void Aura::HandleAuraDummy(bool apply, bool Real) return; } - // Waiting to Resurrect - if(GetId()==2584) + switch(GetId()) { - // Waiting to resurrect spell cancel, we must remove player from resurrect queue - if(m_target->GetTypeId() == TYPEID_PLAYER) - if(BattleGround *bg = ((Player*)m_target)->GetBattleGround()) - bg->RemovePlayerFromResurrectQueue(m_target->GetGUID()); - return; - } - - // Dark Fiend - if(GetId()==45934) - { - // Kill target if dispelled - if (m_removeMode==AURA_REMOVE_BY_DISPEL) - m_target->DealDamage(m_target, m_target->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); - return; - } - - // Burning Winds - if(GetId()==46308) // casted only at creatures at spawn - { - m_target->CastSpell(m_target,47287,true,NULL,this); - return; + case 2584: // Waiting to Resurrect + { + // Waiting to resurrect spell cancel, we must remove player from resurrect queue + if(m_target->GetTypeId() == TYPEID_PLAYER) + if(BattleGround *bg = ((Player*)m_target)->GetBattleGround()) + bg->RemovePlayerFromResurrectQueue(m_target->GetGUID()); + return; + } + case 45934: // Dark Fiend + { + // Kill target if dispelled + if (m_removeMode==AURA_REMOVE_BY_DISPEL) + m_target->DealDamage(m_target, m_target->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + return; + } + case 46308: // Burning Winds + { + // casted only at creatures at spawn + m_target->CastSpell(m_target,47287,true,NULL,this); + return; + } } if (caster && m_removeMode == AURA_REMOVE_BY_DEATH) @@ -2172,21 +2166,6 @@ void Aura::HandleAuraDummy(bool apply, bool Real) } } - - // some auras remove at aura remove - if(GetEffIndex()==0 && m_target->GetTypeId()==TYPEID_PLAYER) - { - SpellAreaForAreaMapBounds saBounds = spellmgr.GetSpellAreaForAuraMapBounds(GetId()); - if(saBounds.first != saBounds.second) - { - uint32 zone = m_target->GetZoneId(); - uint32 area = m_target->GetAreaId(); - - for(SpellAreaForAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr) - if(!itr->second->IsFitToRequirements((Player*)m_target,zone,area)) - m_target->RemoveAurasDueToSpell(itr->second->spellId); - } - } } // AT APPLY & REMOVE @@ -2411,6 +2390,29 @@ void Aura::HandleAuraDummy(bool apply, bool Real) m_target->RemovePetAura(petSpell); return; } + + if(GetEffIndex()==0 && m_target->GetTypeId()==TYPEID_PLAYER) + { + SpellAreaForAreaMapBounds saBounds = spellmgr.GetSpellAreaForAuraMapBounds(GetId()); + if(saBounds.first != saBounds.second) + { + uint32 zone = m_target->GetZoneId(); + uint32 area = m_target->GetAreaId(); + + for(SpellAreaForAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr) + { + // some auras remove at aura remove + if(!itr->second->IsFitToRequirements((Player*)m_target,zone,area)) + m_target->RemoveAurasDueToSpell(itr->second->spellId); + // some auras applied at aura apply + else if(itr->second->autocast) + { + if( !m_target->HasAura(itr->second->spellId,0) ) + m_target->CastSpell(m_target,itr->second->spellId,true); + } + } + } + } } void Aura::HandleAuraMounted(bool apply, bool Real) @@ -2833,7 +2835,7 @@ void Aura::HandleAuraTransform(bool apply, bool Real) // for players, start regeneration after 1s (in polymorph fast regeneration case) // only if caster is Player (after patch 2.4.2) if(IS_PLAYER_GUID(GetCasterGUID()) ) - ((Player*)m_target)->setRegenTimer(1000); + ((Player*)m_target)->setRegenTimer(1*IN_MILISECONDS); //dismount polymorphed target (after patch 2.4.2) if (m_target->IsMounted()) @@ -6683,6 +6685,29 @@ void Aura::HandlePhase(bool apply, bool Real) m_target->SetPhaseMask(apply ? GetMiscValue() : PHASEMASK_NORMAL,false); ((Player*)m_target)->GetSession()->SendSetPhaseShift(apply ? GetMiscValue() : PHASEMASK_NORMAL); + + if(GetEffIndex()==0) + { + SpellAreaForAreaMapBounds saBounds = spellmgr.GetSpellAreaForAuraMapBounds(GetId()); + if(saBounds.first != saBounds.second) + { + uint32 zone = m_target->GetZoneId(); + uint32 area = m_target->GetAreaId(); + + for(SpellAreaForAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr) + { + // some auras remove at aura remove + if(!itr->second->IsFitToRequirements((Player*)m_target,zone,area)) + m_target->RemoveAurasDueToSpell(itr->second->spellId); + // some auras applied at aura apply + else if(itr->second->autocast) + { + if( !m_target->HasAura(itr->second->spellId,0) ) + m_target->CastSpell(m_target,itr->second->spellId,true); + } + } + } + } } else m_target->SetPhaseMask(apply ? GetMiscValue() : PHASEMASK_NORMAL,false); diff --git a/src/game/SpellEffects.cpp b/src/game/SpellEffects.cpp index 31eedaac3..23ddedc2a 100644 --- a/src/game/SpellEffects.cpp +++ b/src/game/SpellEffects.cpp @@ -593,7 +593,7 @@ void Spell::EffectSchoolDMG(uint32 effect_idx) // Add main hand dps * effect[2] amount float averange = (m_caster->GetFloatValue(UNIT_FIELD_MINDAMAGE) + m_caster->GetFloatValue(UNIT_FIELD_MAXDAMAGE)) / 2; int32 count = m_caster->CalculateSpellDamage(m_spellInfo, 2, m_spellInfo->EffectBasePoints[2], unitTarget); - damage += count * int32(averange * 1000) / m_caster->GetAttackTime(BASE_ATTACK); + damage += count * int32(averange * IN_MILISECONDS) / m_caster->GetAttackTime(BASE_ATTACK); } break; } @@ -1109,7 +1109,23 @@ void Spell::EffectDummy(uint32 i) { m_caster->CastSpell(m_caster,54586,true); return; - } + } + case 58418: // Portal to Orgrimmar + { + if(!unitTarget) + return; + + unitTarget->CastSpell(unitTarget, 58419, true); + break; + } + case 58420: // Portal to Stormwind + { + if(!unitTarget) + return; + + unitTarget->CastSpell(unitTarget, 58421, true); + break; + } } //All IconID Check in there @@ -3416,7 +3432,7 @@ void Spell::EffectDispel(uint32 i) // On succes dispel // Devour Magic - if (m_spellInfo->SpellFamilyName == SPELLFAMILY_WARLOCK && m_spellInfo->Category == 12) + if (m_spellInfo->SpellFamilyName == SPELLFAMILY_WARLOCK && m_spellInfo->Category == SPELLCATEGORY_DEVOUR_MAGIC) { uint32 heal_spell = 0; switch (m_spellInfo->Id) @@ -3487,7 +3503,7 @@ void Spell::EffectDistract(uint32 /*i*/) // Set creature Distracted, Stop it, And turn it unitTarget->SetOrientation(angle); unitTarget->StopMoving(); - unitTarget->GetMotionMaster()->MoveDistract(damage*1000); + unitTarget->GetMotionMaster()->MoveDistract(damage*IN_MILISECONDS); } } @@ -4578,7 +4594,7 @@ void Spell::EffectSummonObjectWild(uint32 i) } int32 duration = GetSpellDuration(m_spellInfo); - pGameObj->SetRespawnTime(duration > 0 ? duration/1000 : 0); + pGameObj->SetRespawnTime(duration > 0 ? duration/IN_MILISECONDS : 0); pGameObj->SetSpellId(m_spellInfo->Id); if(pGameObj->GetGoType() != GAMEOBJECT_TYPE_FLAGDROP) // make dropped flag clickable for other players (not set owner guid (created by) for this)... @@ -4621,7 +4637,7 @@ void Spell::EffectSummonObjectWild(uint32 i) if(linkedGO->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), linkedEntry, map, m_caster->GetPhaseMask(), x, y, z, target->GetOrientation(), 0, 0, 0, 0, 100, 1)) { - linkedGO->SetRespawnTime(duration > 0 ? duration/1000 : 0); + linkedGO->SetRespawnTime(duration > 0 ? duration/IN_MILISECONDS : 0); linkedGO->SetSpellId(m_spellInfo->Id); m_caster->AddGameObject(linkedGO); @@ -5246,7 +5262,7 @@ void Spell::EffectDuel(uint32 i) pGameObj->SetUInt32Value(GAMEOBJECT_FACTION, m_caster->getFaction() ); pGameObj->SetUInt32Value(GAMEOBJECT_LEVEL, m_caster->getLevel()+1 ); int32 duration = GetSpellDuration(m_spellInfo); - pGameObj->SetRespawnTime(duration > 0 ? duration/1000 : 0); + pGameObj->SetRespawnTime(duration > 0 ? duration/IN_MILISECONDS : 0); pGameObj->SetSpellId(m_spellInfo->Id); m_caster->AddGameObject(pGameObj); @@ -5323,7 +5339,7 @@ void Spell::EffectSummonPlayer(uint32 /*i*/) WorldPacket data(SMSG_SUMMON_REQUEST, 8+4+4); data << uint64(m_caster->GetGUID()); // summoner guid data << uint32(m_caster->GetZoneId()); // summoner zone - data << uint32(MAX_PLAYER_SUMMON_DELAY*1000); // auto decline after msecs + data << uint32(MAX_PLAYER_SUMMON_DELAY*IN_MILISECONDS); // auto decline after msecs ((Player*)unitTarget)->GetSession()->SendPacket(&data); } @@ -5507,7 +5523,7 @@ void Spell::EffectEnchantHeldItem(uint32 i) return; // Apply the temporary enchantment - item->SetEnchantment(slot, enchant_id, duration*1000, 0); + item->SetEnchantment(slot, enchant_id, duration*IN_MILISECONDS, 0); item_owner->ApplyEnchantment(item,slot,true); } } @@ -5638,7 +5654,7 @@ void Spell::EffectSummonObject(uint32 i) pGameObj->SetUInt32Value(GAMEOBJECT_LEVEL,m_caster->getLevel()); int32 duration = GetSpellDuration(m_spellInfo); - pGameObj->SetRespawnTime(duration > 0 ? duration/1000 : 0); + pGameObj->SetRespawnTime(duration > 0 ? duration/IN_MILISECONDS : 0); pGameObj->SetSpellId(m_spellInfo->Id); m_caster->AddGameObject(pGameObj); @@ -6254,7 +6270,7 @@ void Spell::EffectTransmitted(uint32 effIndex) case 3: lastSec = 17; break; } - duration = duration - lastSec*1000 + FISHING_BOBBER_READY_TIME*1000; + duration = duration - lastSec*IN_MILISECONDS + FISHING_BOBBER_READY_TIME*IN_MILISECONDS; break; } case GAMEOBJECT_TYPE_SUMMONING_RITUAL: @@ -6274,7 +6290,7 @@ void Spell::EffectTransmitted(uint32 effIndex) } } - pGameObj->SetRespawnTime(duration > 0 ? duration/1000 : 0); + pGameObj->SetRespawnTime(duration > 0 ? duration/IN_MILISECONDS : 0); pGameObj->SetOwnerGUID(m_caster->GetGUID() ); @@ -6297,7 +6313,7 @@ void Spell::EffectTransmitted(uint32 effIndex) if(linkedGO->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), linkedEntry, cMap, m_caster->GetPhaseMask(), fx, fy, fz, m_caster->GetOrientation(), 0, 0, 0, 0, 100, 1)) { - linkedGO->SetRespawnTime(duration > 0 ? duration/1000 : 0); + linkedGO->SetRespawnTime(duration > 0 ? duration/IN_MILISECONDS : 0); linkedGO->SetUInt32Value(GAMEOBJECT_LEVEL, m_caster->getLevel() ); linkedGO->SetSpellId(m_spellInfo->Id); linkedGO->SetOwnerGUID(m_caster->GetGUID() ); diff --git a/src/game/SpellMgr.cpp b/src/game/SpellMgr.cpp index 89e88600e..f333db815 100644 --- a/src/game/SpellMgr.cpp +++ b/src/game/SpellMgr.cpp @@ -2387,7 +2387,7 @@ void SpellMgr::LoadSpellAreas() spellArea.questStart = fields[2].GetUInt32(); spellArea.questStartCanActive = fields[3].GetBool(); spellArea.questEnd = fields[4].GetUInt32(); - spellArea.auraSpell = fields[5].GetUInt32(); + spellArea.auraSpell = fields[5].GetInt32(); spellArea.raceMask = fields[6].GetUInt32(); spellArea.gender = Gender(fields[7].GetUInt8()); spellArea.autocast = fields[8].GetBool(); @@ -2398,10 +2398,35 @@ void SpellMgr::LoadSpellAreas() continue; } - if(mSpellAreaForAuraMap.find(spellArea.spellId)!=mSpellAreaForAuraMap.end()) { - sLog.outErrorDb("Spell %u listed in `spell_area` have aura spell (%u) requirement that already listed in table itself", spell,spellArea.auraSpell); - continue; + bool ok = true; + SpellAreaMapBounds sa_bounds = GetSpellAreaMapBounds(spellArea.spellId); + for(SpellAreaMap::const_iterator itr = sa_bounds.first; itr != sa_bounds.second; ++itr) + { + if(spellArea.spellId && itr->second.spellId && spellArea.spellId != itr->second.spellId) + continue; + if(spellArea.areaId && itr->second.areaId && spellArea.areaId!= itr->second.areaId) + continue; + if(spellArea.questStart && itr->second.questStart && spellArea.questStart!= itr->second.questStart) + continue; + if(spellArea.auraSpell && itr->second.auraSpell && spellArea.auraSpell!= itr->second.auraSpell) + continue; + if(spellArea.raceMask && itr->second.raceMask && (spellArea.raceMask & itr->second.raceMask)==0) + continue; + if(spellArea.gender != GENDER_NONE && itr->second.gender != GENDER_NONE && spellArea.gender!= itr->second.gender) + continue; + + // duplicate by requirements + ok =false; + break; + } + + if(!ok) + { + sLog.outErrorDb("Spell %u listed in `spell_area` already listed with similar requirements.", spell); + continue; + } + } if(spellArea.areaId && !GetAreaEntryByAreaID(spellArea.areaId)) @@ -2433,33 +2458,33 @@ void SpellMgr::LoadSpellAreas() if(spellArea.auraSpell) { - SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellArea.auraSpell); + SpellEntry const* spellInfo = sSpellStore.LookupEntry(abs(spellArea.auraSpell)); if(!spellInfo) { - sLog.outErrorDb("Spell %u listed in `spell_area` have wrong aura spell (%u) requirement", spell,spellArea.auraSpell); + sLog.outErrorDb("Spell %u listed in `spell_area` have wrong aura spell (%u) requirement", spell,abs(spellArea.auraSpell)); continue; } - if(spellInfo->EffectApplyAuraName[0]!=SPELL_AURA_DUMMY) + if(spellInfo->EffectApplyAuraName[0]!=SPELL_AURA_DUMMY && spellInfo->EffectApplyAuraName[0]!=SPELL_AURA_PHASE) { - sLog.outErrorDb("Spell %u listed in `spell_area` have aura spell requirement (%u) without dummy aura in effect 0", spell,spellArea.auraSpell); + sLog.outErrorDb("Spell %u listed in `spell_area` have aura spell requirement (%u) without dummy/phase aura in effect 0", spell,abs(spellArea.auraSpell)); continue; } - if(spellArea.auraSpell==spellArea.spellId) + if(abs(spellArea.auraSpell)==spellArea.spellId) { - sLog.outErrorDb("Spell %u listed in `spell_area` have aura spell (%u) requirement for itself", spell,spellArea.auraSpell); + sLog.outErrorDb("Spell %u listed in `spell_area` have aura spell (%u) requirement for itself", spell,abs(spellArea.auraSpell)); continue; } - // not allow autocast chains by auraSpell field - if(spellArea.autocast) + // not allow autocast chains by auraSpell field (but allow use as alternative if not present) + if(spellArea.autocast && spellArea.auraSpell > 0) { bool chain = false; SpellAreaForAuraMapBounds saBound = GetSpellAreaForAuraMapBounds(spellArea.spellId); for(SpellAreaForAuraMap::const_iterator itr = saBound.first; itr != saBound.second; ++itr) { - if(itr->second->autocast) + if(itr->second->autocast && itr->second->auraSpell > 0) { chain = true; break; @@ -2475,7 +2500,7 @@ void SpellMgr::LoadSpellAreas() SpellAreaMapBounds saBound2 = GetSpellAreaMapBounds(spellArea.auraSpell); for(SpellAreaMap::const_iterator itr2 = saBound2.first; itr2 != saBound2.second; ++itr2) { - if(itr2->second.autocast && itr2->second.auraSpell) + if(itr2->second.autocast && itr2->second.auraSpell > 0) { chain = true; break; @@ -2523,7 +2548,7 @@ void SpellMgr::LoadSpellAreas() // for search at aura apply if(spellArea.auraSpell) - mSpellAreaForAuraMap.insert(SpellAreaForAuraMap::value_type(spellArea.auraSpell,sa)); + mSpellAreaForAuraMap.insert(SpellAreaForAuraMap::value_type(abs(spellArea.auraSpell),sa)); ++count; } while( result->NextRow() ); @@ -2810,8 +2835,14 @@ bool SpellArea::IsFitToRequirements(Player const* player, uint32 newZone, uint32 if(auraSpell) { // not have expected aura - if(!player || !player->HasAura(auraSpell,0)) + if(!player) return false; + if(auraSpell > 0) + // have expected aura + return player->HasAura(auraSpell,0); + else + // not have expected aura + return !player->HasAura(-auraSpell,0); } return true; diff --git a/src/game/SpellMgr.h b/src/game/SpellMgr.h index 157981d5c..952b42c19 100644 --- a/src/game/SpellMgr.h +++ b/src/game/SpellMgr.h @@ -223,6 +223,13 @@ enum SpellFailedReason SPELL_FAILED_UNKNOWN = 181 }; +// only used in code +enum SpellCategories +{ + SPELLCATEGORY_HEALTH_MANA_POTIONS = 4, + SPELLCATEGORY_DEVOUR_MAGIC = 12 +}; + enum SpellFamilyNames { SPELLFAMILY_GENERIC = 0, @@ -683,7 +690,7 @@ struct SpellArea uint32 areaId; // zone/subzone/or 0 is not limited to zone uint32 questStart; // quest start (quest must be active or rewarded for spell apply) uint32 questEnd; // quest end (quest don't must be rewarded for spell apply) - uint32 auraSpell; // spell aura must be applied for spell apply + int32 auraSpell; // spell aura must be applied for spell apply )if possitive) and it don't must be applied in other case uint32 raceMask; // can be applied only to races Gender gender; // can be applied only to gender bool questStartCanActive; // if true then quest start can be active (not only rewarded) diff --git a/src/game/Unit.cpp b/src/game/Unit.cpp index de2e3335e..c17505c6c 100644 --- a/src/game/Unit.cpp +++ b/src/game/Unit.cpp @@ -516,6 +516,7 @@ uint32 Unit::DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDa if (pVictim->GetTypeId() == TYPEID_UNIT && !((Creature*)pVictim)->isPet() && !((Creature*)pVictim)->hasLootRecipient()) ((Creature*)pVictim)->SetLootRecipient(this); + if (health <= damage) { DEBUG_LOG("DealDamage: victim just died"); @@ -592,12 +593,12 @@ uint32 Unit::DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDa ((Creature*)this)->AI()->KilledUnit(pVictim); // achievement stuff - if ( pVictim->GetTypeId() == TYPEID_PLAYER) + if (pVictim->GetTypeId() == TYPEID_PLAYER) { - if(GetTypeId() == TYPEID_UNIT) + if (GetTypeId() == TYPEID_UNIT) ((Player*)pVictim)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE, GetEntry()); - else if(GetTypeId() == TYPEID_PLAYER) - ((Player*)pVictim)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER, 1); + else if(GetTypeId() == TYPEID_PLAYER && pVictim != this) + ((Player*)pVictim)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER, 1, ((Player*)this)->GetTeam()); } // 10% durability loss on death @@ -3679,7 +3680,7 @@ void Unit::RemoveAurasDueToSpellBySteal(uint32 spellId, uint64 casterGUID, Unit // set its duration and maximum duration // max duration 2 minutes (in msecs) int32 dur = aur->GetAuraDuration(); - const int32 max_dur = 2*MINUTE*1000; + const int32 max_dur = 2*MINUTE*IN_MILISECONDS; new_aur->SetAuraMaxDuration( max_dur > dur ? dur : max_dur ); new_aur->SetAuraDuration( max_dur > dur ? dur : max_dur ); @@ -3987,7 +3988,7 @@ void Unit::RemoveDynObject(uint32 spellid) return; for (DynObjectGUIDs::iterator i = m_dynObjGUIDs.begin(); i != m_dynObjGUIDs.end();) { - DynamicObject* dynObj = ObjectAccessor::GetDynamicObject(*this,*m_dynObjGUIDs.begin()); + DynamicObject* dynObj = ObjectAccessor::GetDynamicObject(*this,*i); if(!dynObj) { i = m_dynObjGUIDs.erase(i); @@ -4017,7 +4018,7 @@ DynamicObject * Unit::GetDynObject(uint32 spellId, uint32 effIndex) { for (DynObjectGUIDs::iterator i = m_dynObjGUIDs.begin(); i != m_dynObjGUIDs.end();) { - DynamicObject* dynObj = ObjectAccessor::GetDynamicObject(*this,*m_dynObjGUIDs.begin()); + DynamicObject* dynObj = ObjectAccessor::GetDynamicObject(*this,*i); if(!dynObj) { i = m_dynObjGUIDs.erase(i); @@ -4035,7 +4036,7 @@ DynamicObject * Unit::GetDynObject(uint32 spellId) { for (DynObjectGUIDs::iterator i = m_dynObjGUIDs.begin(); i != m_dynObjGUIDs.end();) { - DynamicObject* dynObj = ObjectAccessor::GetDynamicObject(*this,*m_dynObjGUIDs.begin()); + DynamicObject* dynObj = ObjectAccessor::GetDynamicObject(*this,*i); if(!dynObj) { i = m_dynObjGUIDs.erase(i); @@ -4054,6 +4055,15 @@ void Unit::AddGameObject(GameObject* gameObj) assert(gameObj && gameObj->GetOwnerGUID()==0); m_gameObj.push_back(gameObj); gameObj->SetOwnerGUID(GetGUID()); + + if ( GetTypeId()==TYPEID_PLAYER && gameObj->GetSpellId() ) + { + SpellEntry const* createBySpell = sSpellStore.LookupEntry(gameObj->GetSpellId()); + // Need disable spell use for owner + if (createBySpell && createBySpell->Attributes & SPELL_ATTR_DISABLED_WHILE_ACTIVE) + // note: item based cooldowns and cooldown spell mods with charges ignored (unknown existed cases) + ((Player*)this)->AddSpellAndCategoryCooldowns(createBySpell,0,NULL,true); + } } void Unit::RemoveGameObject(GameObject* gameObj, bool del) @@ -4066,6 +4076,7 @@ void Unit::RemoveGameObject(GameObject* gameObj, bool del) SpellEntry const* createBySpell = sSpellStore.LookupEntry(gameObj->GetSpellId()); // Need activate spell use for owner if (createBySpell && createBySpell->Attributes & SPELL_ATTR_DISABLED_WHILE_ACTIVE) + // note: item based cooldowns and cooldown spell mods with charges ignored (unknown existed cases) ((Player*)this)->SendCooldownEvent(createBySpell); } gameObj->SetOwnerGUID(0); @@ -6905,28 +6916,34 @@ bool Unit::IsHostileTo(Unit const* unit) const if(tester->GetTypeId()==TYPEID_PLAYER) { // forced reaction - ForcedReactions::const_iterator forceItr = ((Player*)tester)->m_forcedReactions.find(target_faction->faction); - if(forceItr!=((Player*)tester)->m_forcedReactions.end()) - return forceItr->second <= REP_HOSTILE; + if(target_faction->faction) + { + ForcedReactions::const_iterator forceItr = ((Player*)tester)->m_forcedReactions.find(target_faction->faction); + if(forceItr!=((Player*)tester)->m_forcedReactions.end()) + return forceItr->second <= REP_HOSTILE; - // if faction have reputation then hostile state for tester at 100% dependent from at_war state - if(FactionEntry const* raw_target_faction = sFactionStore.LookupEntry(target_faction->faction)) - if(raw_target_faction->reputationListID >=0) - if(FactionState const* factionState = ((Player*)tester)->GetFactionState(raw_target_faction)) - return (factionState->Flags & FACTION_FLAG_AT_WAR); + // if faction have reputation then hostile state for tester at 100% dependent from at_war state + if(FactionEntry const* raw_target_faction = sFactionStore.LookupEntry(target_faction->faction)) + if(raw_target_faction->reputationListID >=0) + if(FactionState const* factionState = ((Player*)tester)->GetFactionState(raw_target_faction)) + return (factionState->Flags & FACTION_FLAG_AT_WAR); + } } // CvP forced reaction and reputation case else if(target->GetTypeId()==TYPEID_PLAYER) { // forced reaction - ForcedReactions::const_iterator forceItr = ((Player const*)target)->m_forcedReactions.find(tester_faction->faction); - if(forceItr!=((Player const*)target)->m_forcedReactions.end()) - return forceItr->second <= REP_HOSTILE; + if(tester_faction->faction) + { + ForcedReactions::const_iterator forceItr = ((Player const*)target)->m_forcedReactions.find(tester_faction->faction); + if(forceItr!=((Player const*)target)->m_forcedReactions.end()) + return forceItr->second <= REP_HOSTILE; - // apply reputation state - FactionEntry const* raw_tester_faction = sFactionStore.LookupEntry(tester_faction->faction); - if(raw_tester_faction && raw_tester_faction->reputationListID >=0 ) - return ((Player const*)target)->GetReputationRank(raw_tester_faction) <= REP_HOSTILE; + // apply reputation state + FactionEntry const* raw_tester_faction = sFactionStore.LookupEntry(tester_faction->faction); + if(raw_tester_faction && raw_tester_faction->reputationListID >=0 ) + return ((Player const*)target)->GetReputationRank(raw_tester_faction) <= REP_HOSTILE; + } } // common faction based case (CvC,PvC,CvP) @@ -7014,28 +7031,34 @@ bool Unit::IsFriendlyTo(Unit const* unit) const if(tester->GetTypeId()==TYPEID_PLAYER) { // forced reaction - ForcedReactions::const_iterator forceItr = ((Player const*)tester)->m_forcedReactions.find(target_faction->faction); - if(forceItr!=((Player const*)tester)->m_forcedReactions.end()) - return forceItr->second >= REP_FRIENDLY; + if(target_faction->faction) + { + ForcedReactions::const_iterator forceItr = ((Player const*)tester)->m_forcedReactions.find(target_faction->faction); + if(forceItr!=((Player const*)tester)->m_forcedReactions.end()) + return forceItr->second >= REP_FRIENDLY; - // if faction have reputation then friendly state for tester at 100% dependent from at_war state - if(FactionEntry const* raw_target_faction = sFactionStore.LookupEntry(target_faction->faction)) - if(raw_target_faction->reputationListID >=0) - if(FactionState const* FactionState = ((Player*)tester)->GetFactionState(raw_target_faction)) - return !(FactionState->Flags & FACTION_FLAG_AT_WAR); + // if faction have reputation then friendly state for tester at 100% dependent from at_war state + if(FactionEntry const* raw_target_faction = sFactionStore.LookupEntry(target_faction->faction)) + if(raw_target_faction->reputationListID >=0) + if(FactionState const* FactionState = ((Player*)tester)->GetFactionState(raw_target_faction)) + return !(FactionState->Flags & FACTION_FLAG_AT_WAR); + } } // CvP forced reaction and reputation case else if(target->GetTypeId()==TYPEID_PLAYER) { // forced reaction - ForcedReactions::const_iterator forceItr = ((Player const*)target)->m_forcedReactions.find(tester_faction->faction); - if(forceItr!=((Player const*)target)->m_forcedReactions.end()) - return forceItr->second >= REP_FRIENDLY; + if(tester_faction->faction) + { + ForcedReactions::const_iterator forceItr = ((Player const*)target)->m_forcedReactions.find(tester_faction->faction); + if(forceItr!=((Player const*)target)->m_forcedReactions.end()) + return forceItr->second >= REP_FRIENDLY; - // apply reputation state - if(FactionEntry const* raw_tester_faction = sFactionStore.LookupEntry(tester_faction->faction)) - if(raw_tester_faction->reputationListID >=0 ) - return ((Player const*)target)->GetReputationRank(raw_tester_faction) >= REP_FRIENDLY; + // apply reputation state + if(FactionEntry const* raw_tester_faction = sFactionStore.LookupEntry(tester_faction->faction)) + if(raw_tester_faction->reputationListID >=0 ) + return ((Player const*)target)->GetReputationRank(raw_tester_faction) >= REP_FRIENDLY; + } } // common faction based case (CvC,PvC,CvP) @@ -7045,7 +7068,7 @@ bool Unit::IsFriendlyTo(Unit const* unit) const bool Unit::IsHostileToPlayers() const { FactionTemplateEntry const* my_faction = getFactionTemplateEntry(); - if(!my_faction) + if(!my_faction || !my_faction->faction) return false; FactionEntry const* raw_faction = sFactionStore.LookupEntry(my_faction->faction); @@ -7058,7 +7081,7 @@ bool Unit::IsHostileToPlayers() const bool Unit::IsNeutralToAll() const { FactionTemplateEntry const* my_faction = getFactionTemplateEntry(); - if(!my_faction) + if(!my_faction || !my_faction->faction) return true; FactionEntry const* raw_faction = sFactionStore.LookupEntry(my_faction->faction); @@ -8575,6 +8598,8 @@ void Unit::ClearInCombat() // Player's state will be cleared in Player::UpdateContestedPvP if(GetTypeId()!=TYPEID_PLAYER) clearUnitState(UNIT_STAT_ATTACK_PLAYER); + else + ((Player*)this)->UpdatePotionCooldown(); } bool Unit::isTargetableForAttack() const @@ -11331,4 +11356,4 @@ void Unit::SetPhaseMask(uint32 newPhaseMask, bool update) if(IsInWorld()) if(Pet* pet = GetPet()) pet->SetPhaseMask(newPhaseMask,true); -} \ No newline at end of file +} diff --git a/src/game/Weather.cpp b/src/game/Weather.cpp index aea7c19b9..be1aec79c 100644 --- a/src/game/Weather.cpp +++ b/src/game/Weather.cpp @@ -36,7 +36,7 @@ Weather::Weather(uint32 zone, WeatherZoneChances const* weatherChances) : m_zone m_type = WEATHER_TYPE_FINE; m_grade = 0; - sLog.outDetail("WORLD: Starting weather system for zone %u (change every %u minutes).", m_zone, (uint32)(m_timer.GetInterval() / (1000*MINUTE)) ); + sLog.outDetail("WORLD: Starting weather system for zone %u (change every %u minutes).", m_zone, (uint32)(m_timer.GetInterval() / (MINUTE*IN_MILISECONDS)) ); } /// Launch a weather update diff --git a/src/game/World.cpp b/src/game/World.cpp index 8b04917a7..0f3f1086f 100644 --- a/src/game/World.cpp +++ b/src/game/World.cpp @@ -552,9 +552,9 @@ void World::LoadConfigSettings(bool reload) } m_configs[CONFIG_ADDON_CHANNEL] = sConfig.GetBoolDefault("AddonChannel", true); m_configs[CONFIG_GRID_UNLOAD] = sConfig.GetBoolDefault("GridUnload", true); - m_configs[CONFIG_INTERVAL_SAVE] = sConfig.GetIntDefault("PlayerSaveInterval", 900000); + m_configs[CONFIG_INTERVAL_SAVE] = sConfig.GetIntDefault("PlayerSaveInterval", 15 * MINUTE * IN_MILISECONDS); - m_configs[CONFIG_INTERVAL_GRIDCLEAN] = sConfig.GetIntDefault("GridCleanUpDelay", 300000); + m_configs[CONFIG_INTERVAL_GRIDCLEAN] = sConfig.GetIntDefault("GridCleanUpDelay", 5 * MINUTE * IN_MILISECONDS); if(m_configs[CONFIG_INTERVAL_GRIDCLEAN] < MIN_GRID_DELAY) { sLog.outError("GridCleanUpDelay (%i) must be greater %u. Use this minimal value.",m_configs[CONFIG_INTERVAL_GRIDCLEAN],MIN_GRID_DELAY); @@ -572,7 +572,7 @@ void World::LoadConfigSettings(bool reload) if(reload) MapManager::Instance().SetMapUpdateInterval(m_configs[CONFIG_INTERVAL_MAPUPDATE]); - m_configs[CONFIG_INTERVAL_CHANGEWEATHER] = sConfig.GetIntDefault("ChangeWeatherInterval", 600000); + m_configs[CONFIG_INTERVAL_CHANGEWEATHER] = sConfig.GetIntDefault("ChangeWeatherInterval", 10 * MINUTE * IN_MILISECONDS); if(reload) { @@ -764,7 +764,7 @@ void World::LoadConfigSettings(bool reload) m_configs[CONFIG_CAST_UNSTUCK] = sConfig.GetBoolDefault("CastUnstuck", true); m_configs[CONFIG_INSTANCE_RESET_TIME_HOUR] = sConfig.GetIntDefault("Instance.ResetTimeHour", 4); - m_configs[CONFIG_INSTANCE_UNLOAD_DELAY] = sConfig.GetIntDefault("Instance.UnloadDelay", 1800000); + m_configs[CONFIG_INSTANCE_UNLOAD_DELAY] = sConfig.GetIntDefault("Instance.UnloadDelay", 30 * MINUTE * IN_MILISECONDS); m_configs[CONFIG_MAX_PRIMARY_TRADE_SKILL] = sConfig.GetIntDefault("MaxPrimaryTradeSkill", 2); m_configs[CONFIG_MIN_PETITION_SIGNS] = sConfig.GetIntDefault("MinPetitionSigns", 9); @@ -811,7 +811,7 @@ void World::LoadConfigSettings(bool reload) } if(reload) { - m_timers[WUPDATE_UPTIME].SetInterval(m_configs[CONFIG_UPTIME_UPDATE]*MINUTE*1000); + m_timers[WUPDATE_UPTIME].SetInterval(m_configs[CONFIG_UPTIME_UPDATE]*MINUTE*IN_MILISECONDS); m_timers[WUPDATE_UPTIME].Reset(); } @@ -932,7 +932,7 @@ void World::LoadConfigSettings(bool reload) m_configs[CONFIG_BATTLEGROUND_INVITATION_TYPE] = sConfig.GetIntDefault ("Battleground.InvitationType", 0); m_configs[CONFIG_BATTLEGROUND_PREMATURE_FINISH_TIMER] = sConfig.GetIntDefault ("BattleGround.PrematureFinishTimer", 5 * MINUTE * IN_MILISECONDS); m_configs[CONFIG_BATTLEGROUND_PREMADE_GROUP_WAIT_FOR_MATCH] = sConfig.GetIntDefault ("BattleGround.PremadeGroupWaitForMatch", 30 * MINUTE * IN_MILISECONDS); - m_configs[CONFIG_ARENA_MAX_RATING_DIFFERENCE] = sConfig.GetIntDefault ("Arena.MaxRatingDifference", 100); + m_configs[CONFIG_ARENA_MAX_RATING_DIFFERENCE] = sConfig.GetIntDefault ("Arena.MaxRatingDifference", 150); m_configs[CONFIG_ARENA_RATING_DISCARD_TIMER] = sConfig.GetIntDefault ("Arena.RatingDiscardTimer", 10 * MINUTE * IN_MILISECONDS); m_configs[CONFIG_ARENA_AUTO_DISTRIBUTE_POINTS] = sConfig.GetBoolDefault("Arena.AutoDistributePoints", false); m_configs[CONFIG_ARENA_AUTO_DISTRIBUTE_INTERVAL_DAYS] = sConfig.GetIntDefault ("Arena.AutoDistributeInterval", 7); @@ -940,6 +940,8 @@ void World::LoadConfigSettings(bool reload) m_configs[CONFIG_ARENA_SEASON_ID] = sConfig.GetIntDefault ("Arena.ArenaSeason.ID", 1); m_configs[CONFIG_ARENA_SEASON_IN_PROGRESS] = sConfig.GetBoolDefault("Arena.ArenaSeason.InProgress", true); + m_configs[CONFIG_OFFHAND_CHECK_AT_TALENTS_RESET] = sConfig.GetBoolDefault("OffhandCheckAtTalentsReset", false); + m_configs[CONFIG_INSTANT_LOGOUT] = sConfig.GetIntDefault("InstantLogout", SEC_MODERATOR); m_VisibleUnitGreyDistance = sConfig.GetFloatDefault("Visibility.Distance.Grey.Unit", 1); @@ -1365,18 +1367,19 @@ void World::SetInitialWorldSettings() m_timers[WUPDATE_OBJECTS].SetInterval(0); m_timers[WUPDATE_SESSIONS].SetInterval(0); - m_timers[WUPDATE_WEATHERS].SetInterval(1000); - m_timers[WUPDATE_AUCTIONS].SetInterval(MINUTE*1000); //set auction update interval to 1 minute - m_timers[WUPDATE_UPTIME].SetInterval(m_configs[CONFIG_UPTIME_UPDATE]*MINUTE*1000); + m_timers[WUPDATE_WEATHERS].SetInterval(1*IN_MILISECONDS); + m_timers[WUPDATE_AUCTIONS].SetInterval(MINUTE*IN_MILISECONDS); + m_timers[WUPDATE_UPTIME].SetInterval(m_configs[CONFIG_UPTIME_UPDATE]*MINUTE*IN_MILISECONDS); //Update "uptime" table based on configuration entry in minutes. - m_timers[WUPDATE_CORPSES].SetInterval(20*MINUTE*1000); //erase corpses every 20 minutes + m_timers[WUPDATE_CORPSES].SetInterval(20*MINUTE*IN_MILISECONDS); + //erase corpses every 20 minutes //to set mailtimer to return mails every day between 4 and 5 am //mailtimer is increased when updating auctions //one second is 1000 -(tested on win system) - mail_timer = ((((localtime( &m_gameTime )->tm_hour + 20) % 24)* HOUR * 1000) / m_timers[WUPDATE_AUCTIONS].GetInterval() ); + mail_timer = ((((localtime( &m_gameTime )->tm_hour + 20) % 24)* HOUR * IN_MILISECONDS) / m_timers[WUPDATE_AUCTIONS].GetInterval() ); //1440 - mail_timer_expires = ( (DAY * 1000) / (m_timers[WUPDATE_AUCTIONS].GetInterval())); + mail_timer_expires = ( (DAY * IN_MILISECONDS) / (m_timers[WUPDATE_AUCTIONS].GetInterval())); sLog.outDebug("Mail timer set to: %u, mail return is called every %u minutes", mail_timer, mail_timer_expires); ///- Initilize static helper structures diff --git a/src/game/World.h b/src/game/World.h index 9c908c127..55c0649ae 100644 --- a/src/game/World.h +++ b/src/game/World.h @@ -195,6 +195,7 @@ enum WorldConfigs CONFIG_ARENA_QUEUE_ANNOUNCER_ENABLE, CONFIG_ARENA_SEASON_ID, CONFIG_ARENA_SEASON_IN_PROGRESS, + CONFIG_OFFHAND_CHECK_AT_TALENTS_RESET, CONFIG_VALUE_COUNT }; diff --git a/src/mangosd/mangosd.conf.dist.in b/src/mangosd/mangosd.conf.dist.in index 8a333823f..a6a7f29bd 100644 --- a/src/mangosd/mangosd.conf.dist.in +++ b/src/mangosd/mangosd.conf.dist.in @@ -94,7 +94,7 @@ BindIP = "0.0.0.0" # # SocketSelectTime # Socket select time (in milliseconds) -# Default: 10000 +# Default: 10000 (10 secs) # # GridCleanUpDelay # Grid clean up delay (in milliseconds) @@ -115,8 +115,8 @@ BindIP = "0.0.0.0" # vmap.enableLOS # vmap.enableHeight # Enable/Disable VMmap support for line of sight and height calculation -# Default: 1 (true) -# 0 (false) +# Default: 0 (disable) +# 1 (enable) # # vmap.ignoreMapIds # Map id that will be ignored by VMaps @@ -504,7 +504,7 @@ LogColors = "" # # Instance.UnloadDelay # Unload the instance map from memory after some time if no players are inside. -# Default: 1800000 (miliseconds, i.e 30 minutes) +# Default: 1800000 (miliseconds 30 minutes) # 0 (instance maps are kept in memory until they are reset) # # Quests.LowLevelHideDiff @@ -546,6 +546,11 @@ LogColors = "" # Default: 0 - no skilups # 1 - skilups possible # +# OffhandCheckAtTalentsReset +# Talent reset can change offhand weapon restrictions for equip slots. +# Default: 0 - recheck offhand slot weapon only at zone update +# 1 - recheck offhand slot weapon at talent reset also +# # Event.Announce # Default: 0 (false) # 1 (true) @@ -600,6 +605,7 @@ MaxGroupXPDistance = 74 MailDeliveryDelay = 3600 SkillChance.Prospecting = 0 SkillChance.Milling = 0 +OffhandCheckAtTalentsReset = 0 Event.Announce = 0 BeepAtStart = 1 Motd = "Welcome to the Massive Network Game Object Server." @@ -835,7 +841,7 @@ Channel.SilentlyGMJoin = 0 # 1 (enable) # # GM.InGMList -# Is GM showed in GM list (if visible) in non-GM state (.gmoff) +# Is GM showed in GM list (if visible) in non-GM state (.gm off) # Default: 0 (false) # 1 (true) # @@ -860,8 +866,8 @@ Channel.SilentlyGMJoin = 0 # # GM.AllowAchievementGain # If enabled it allows gaining achievements for GM characters -# Default: 0 (disable) -# 1 (enable) (default) +# Default: 1 (enable) +# 0 (disable) # ################################################################################################################### @@ -1046,7 +1052,7 @@ Visibility.Distance.Grey.Object = 10 # # Death.Bones.World # Death.Bones.BattlegroundOrArena -# Enabled/disabled creating bones instead corpse at resurrection (in normal zones/instacnes, or battleground/arenas) +# Enable/disable creating bones instead corpse at resurrection (in normal zones/instacnes, or battleground/arenas) # Default: 1 (enabled) # 0 (disabled) # @@ -1110,19 +1116,19 @@ Death.Bones.BattlegroundOrArena = 1 # BATTLEGROUND CONFIG # # Battleground.CastDeserter -# Cast or not Deserter spell at player who leave battleground in progress -# Default: 1 (true) -# 0 (false) +# Cast Deserter spell at player who leave battleground in progress +# Default: 1 (enable) +# 0 (disable) # # Battleground.QueueAnnouncer.Enable # Enable queue announcer posting to chat -# Default: 0 (false) -# 1 (true) +# Default: 0 (disable) +# 1 (enable) # # Battleground.QueueAnnouncer.PlayerOnly # Enable queue announcer posting to chat -# Default: 0 (false) -# 1 (true) +# Default: 0 (disable) +# 1 (enable) # # Battleground.InvitationType # Set Battleground invitation type @@ -1154,7 +1160,7 @@ BattleGround.PremadeGroupWaitForMatch = 1800000 # # Arena.MaxRatingDifference # The maximum rating difference between two groups in rated matches -# Default: 100 (enable, recommended) +# Default: 150 (enable, recommended) # 0 (disable, rating difference is discarded) # # Arena.RatingDiscardTimer @@ -1162,7 +1168,7 @@ BattleGround.PremadeGroupWaitForMatch = 1800000 # rating information will be discarded when selecting teams for matches # also initiates an update by this timer # Default: 600000 (10 minutes, recommended) -# 0 (disable, DO NOT USE - it will cause lags) +# 0 (disable) # # Arena.AutoDistributePoints # Set if arena points should be distributed automatically, or by GM command @@ -1176,8 +1182,8 @@ BattleGround.PremadeGroupWaitForMatch = 1800000 # # Arena.QueueAnnouncer.Enable # Enable bg queue announcer posting to chat -# Default: 0 (false) -# 1 (true) +# Default: 0 (disable) +# 1 (enable) # # Arena.ArenaSeason.ID # Current area season id show in client @@ -1190,7 +1196,7 @@ BattleGround.PremadeGroupWaitForMatch = 1800000 # ################################################################################################################### -Arena.MaxRatingDifference = 100 +Arena.MaxRatingDifference = 150 Arena.RatingDiscardTimer = 600000 Arena.AutoDistributePoints = 0 Arena.AutoDistributeInterval = 7 diff --git a/src/shared/Common.h b/src/shared/Common.h index 2dce4ac37..c18f854ee 100644 --- a/src/shared/Common.h +++ b/src/shared/Common.h @@ -158,6 +158,7 @@ enum TimeConstants HOUR = MINUTE*60, DAY = HOUR*24, MONTH = DAY*30, + YEAR = MONTH*12, IN_MILISECONDS = 1000 }; diff --git a/src/shared/Database/DBCStructure.h b/src/shared/Database/DBCStructure.h index d0dbf3260..daf4849cc 100644 --- a/src/shared/Database/DBCStructure.h +++ b/src/shared/Database/DBCStructure.h @@ -72,7 +72,7 @@ struct AchievementCriteriaEntry uint32 requiredType; // 2 union { - // ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE = 0 + // ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE = 0 // TODO: also used for player deaths.. struct { @@ -80,7 +80,7 @@ struct AchievementCriteriaEntry uint32 creatureCount; // 4 } kill_creature; - // ACHIEVEMENT_CRITERIA_TYPE_WIN_BG = 1 + // ACHIEVEMENT_CRITERIA_TYPE_WIN_BG = 1 // TODO: there are further criterias instead just winning struct { @@ -88,27 +88,27 @@ struct AchievementCriteriaEntry uint32 winCount; // 4 } win_bg; - // ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL = 5 + // ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL = 5 struct { uint32 unused; // 3 uint32 level; // 4 } reach_level; - // ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL = 7 + // ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL = 7 struct { uint32 skillID; // 3 uint32 skillLevel; // 4 } reach_skill_level; - // ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT = 8 + // ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT = 8 struct { uint32 linkedAchievement; // 3 } complete_achievement; - // ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT = 9 + // ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT = 9 struct { uint32 unused; // 3 @@ -129,67 +129,73 @@ struct AchievementCriteriaEntry uint32 questCount; // 4 } complete_quests_in_zone; - // ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST = 14 + // ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST = 14 struct { uint32 unused; // 3 uint32 questCount; // 4 } complete_daily_quest; - // ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND= 15 + // ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND = 15 struct { uint32 mapID; // 3 } complete_battleground; - // ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP= 16 + // ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP = 16 struct { uint32 mapID; // 3 } death_at_map; - // ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_RAID = 19 + // ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON = 18 + struct + { + uint32 manLimit; // 3 + } death_in_dungeon; + + // ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_RAID = 19 struct { uint32 groupSize; // 3 can be 5, 10 or 25 } complete_raid; - // ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE = 20 + // ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE = 20 struct { uint32 creatureEntry; // 3 } killed_by_creature; - // ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING = 24 + // ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING = 24 struct { uint32 unused; // 3 uint32 fallHeight; // 4 } fall_without_dying; - // ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM = 26 + // ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM = 26 struct { - uint32 type; // 0 - fatigue, 1 - drowning, 2 - falling, 3 - ??, 5 - fire and lava - } deaths; + uint32 type; // 3, see enum EnviromentalDamage + } death_from; - // ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST = 27 + // ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST = 27 struct { uint32 questID; // 3 uint32 questCount; // 4 } complete_quest; - // ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET = 28 - // ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2= 69 + // ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET = 28 + // ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2 = 69 struct { uint32 spellID; // 3 uint32 spellCount; // 4 } be_spell_target; - // ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL= 29 - // ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2 = 110 + // ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL = 29 + // ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2 = 110 struct { uint32 spellID; // 3 @@ -203,32 +209,32 @@ struct AchievementCriteriaEntry uint32 killCount; // 4 } honorable_kill_at_area; - // ACHIEVEMENT_CRITERIA_TYPE_WIN_ARENA = 32 + // ACHIEVEMENT_CRITERIA_TYPE_WIN_ARENA = 32 struct { uint32 mapID; // 3 Reference to Map.dbc } win_arena; - // ACHIEVEMENT_CRITERIA_TYPE_PLAY_ARENA = 33 + // ACHIEVEMENT_CRITERIA_TYPE_PLAY_ARENA = 33 struct { uint32 mapID; // 3 Reference to Map.dbc } play_arena; - // ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL = 34 + // ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL = 34 struct { uint32 spellID; // 3 Reference to Map.dbc } learn_spell; - // ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM = 36 + // ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM = 36 struct { uint32 itemID; // 3 uint32 itemCount; // 4 } own_item; - // ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA = 37 + // ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA = 37 struct { uint32 unused; // 3 @@ -236,62 +242,62 @@ struct AchievementCriteriaEntry uint32 flag; // 5 4=in a row } win_rated_arena; - // ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_TEAM_RATING = 38 + // ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_TEAM_RATING = 38 struct { uint32 teamtype; // 3 {2,3,5} } highest_team_rating; - // ACHIEVEMENT_CRITERIA_TYPE_REACH_TEAM_RATING = 39 + // ACHIEVEMENT_CRITERIA_TYPE_REACH_TEAM_RATING = 39 struct { uint32 teamtype; // 3 {2,3,5} uint32 teamrating; // 4 } reach_team_rating; - // ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL = 40 + // ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL = 40 struct { uint32 skillID; // 3 uint32 skillLevel; // 4 apprentice=1, journeyman=2, expert=3, artisan=4, master=5, grand master=6 } learn_skill_level; - // ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM = 41 + // ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM = 41 struct { uint32 itemID; // 3 uint32 itemCount; // 4 } use_item; - // ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM = 42 + // ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM = 42 struct { uint32 itemID; // 3 uint32 itemCount; // 4 } loot_item; - // ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA = 43 + // ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA = 43 struct { // TODO: This rank is _NOT_ the index from AreaTable.dbc uint32 areaReference; // 3 } explore_area; - // ACHIEVEMENT_CRITERIA_TYPE_OWN_RANK= 44 + // ACHIEVEMENT_CRITERIA_TYPE_OWN_RANK = 44 struct { // TODO: This rank is _NOT_ the index from CharTitles.dbc uint32 rank; // 3 } own_rank; - // ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT= 45 + // ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT = 45 struct { uint32 unused; // 3 uint32 numberOfSlots; // 4 } buy_bank_slot; - // ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION= 46 + // ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION = 46 struct { uint32 factionID; // 3 @@ -305,56 +311,56 @@ struct AchievementCriteriaEntry uint32 numberOfExaltedFactions; // 4 } gain_exalted_reputation; - // ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP = 48 + // ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP = 48 struct { uint32 unused; // 3 uint32 numberOfVisits; // 4 } visit_barber; - // ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM = 49 + // ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM = 49 // TODO: where is the required itemlevel stored? struct { uint32 itemSlot; // 3 } equip_epic_item; - // ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT= 50 + // ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT = 50 struct { uint32 rollValue; // 3 uint32 count; // 4 } roll_need_on_loot; - // ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT= 51 + // ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT = 51 struct { uint32 rollValue; // 3 uint32 count; // 4 } roll_greed_on_loot; - // ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS = 52 + // ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS = 52 struct { uint32 classID; // 3 uint32 count; // 4 } hk_class; - // ACHIEVEMENT_CRITERIA_TYPE_HK_RACE = 53 + // ACHIEVEMENT_CRITERIA_TYPE_HK_RACE = 53 struct { uint32 raceID; // 3 uint32 count; // 4 } hk_race; - // ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE = 54 + // ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE = 54 // TODO: where is the information about the target stored? struct { uint32 emoteID; // 3 } do_emote; - // ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE = 13 - // ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE = 55 - // ACHIEVEMENT_CRITERIA_TYPE_GET_KILLING_BLOWS = 56 + // ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE = 13 + // ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE = 55 + // ACHIEVEMENT_CRITERIA_TYPE_GET_KILLING_BLOWS = 56 struct { uint32 unused; // 3 @@ -363,28 +369,35 @@ struct AchievementCriteriaEntry uint32 mapid; // 6 } healing_done; - // ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM = 57 + // ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM = 57 struct { uint32 itemID; // 3 } equip_item; + // ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD= 62 + struct + { + uint32 unused; // 3 + uint32 goldInCopper; // 4 + } quest_reward_money; - // ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY = 67 + + // ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY = 67 struct { uint32 unused; // 3 uint32 goldInCopper; // 4 } loot_money; - // ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT = 68 + // ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT = 68 struct { uint32 goEntry; // 3 uint32 useCount; // 4 } use_gameobject; - // ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL= 70 + // ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL = 70 // TODO: are those special criteria stored in the dbc or do we have to add another sql table? struct { @@ -392,66 +405,66 @@ struct AchievementCriteriaEntry uint32 killCount; // 4 } special_pvp_kill; - // ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT = 72 + // ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT = 72 struct { uint32 goEntry; // 3 uint32 lootCount; // 4 } fish_in_gameobject; - // ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS= 75 + // ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS = 75 struct { uint32 skillLine; // 3 uint32 spellCount; // 4 } learn_skilline_spell; - // ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL = 76 + // ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL = 76 struct { uint32 unused; // 3 uint32 duelCount; // 4 } win_duel; - // ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_POWER = 96 + // ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_POWER = 96 struct { uint32 powerType; // 3 mana=0, 1=rage, 3=energy, 6=runic power } highest_power; - // ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_STAT = 97 + // ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_STAT = 97 struct { uint32 statType; // 3 4=spirit, 3=int, 2=stamina, 1=agi, 0=strength } highest_stat; - // ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_SPELLPOWER = 98 + // ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_SPELLPOWER = 98 struct { uint32 spellSchool; // 3 } highest_spellpower; - // ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_RATING = 100 + // ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_RATING = 100 struct { uint32 ratingType; // 3 } highest_rating; - // ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE = 109 + // ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE = 109 struct { uint32 lootType; // 3 3=fishing, 2=pickpocket, 4=disentchant uint32 lootTypeCount; // 4 } loot_type; - // ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE= 112 + // ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE = 112 struct { uint32 skillLine; // 3 uint32 spellCount; // 4 } learn_skill_line; - // ACHIEVEMENT_CRITERIA_TYPE_EARN_HONORABLE_KILL = 113 + // ACHIEVEMENT_CRITERIA_TYPE_EARN_HONORABLE_KILL = 113 struct { uint32 unused; // 3 @@ -731,22 +744,38 @@ struct FactionTemplateEntry // helpers bool IsFriendlyTo(FactionTemplateEntry const& entry) const { - if(enemyFaction[0] == entry.faction || enemyFaction[1] == entry.faction || enemyFaction[2] == entry.faction || enemyFaction[3] == entry.faction ) - return false; - if(friendFaction[0] == entry.faction || friendFaction[1] == entry.faction || friendFaction[2] == entry.faction || friendFaction[3] == entry.faction ) - return true; + if(entry.faction) + { + for(int i = 0; i < 4; ++i) + if (enemyFaction[i] == entry.faction) + return false; + for(int i = 0; i < 4; ++i) + if (friendFaction[i] == entry.faction) + return true; + } return (friendlyMask & entry.ourMask) || (ourMask & entry.friendlyMask); } bool IsHostileTo(FactionTemplateEntry const& entry) const { - if(enemyFaction[0] == entry.faction || enemyFaction[1] == entry.faction || enemyFaction[2] == entry.faction || enemyFaction[3] == entry.faction ) - return true; - if(friendFaction[0] == entry.faction || friendFaction[1] == entry.faction || friendFaction[2] == entry.faction || friendFaction[3] == entry.faction ) - return false; + if(entry.faction) + { + for(int i = 0; i < 4; ++i) + if (enemyFaction[i] == entry.faction) + return true; + for(int i = 0; i < 4; ++i) + if (friendFaction[i] == entry.faction) + return false; + } return (hostileMask & entry.ourMask) != 0; } bool IsHostileToPlayers() const { return (hostileMask & FACTION_MASK_PLAYER) !=0; } - bool IsNeutralToAll() const { return hostileMask == 0 && friendlyMask == 0 && enemyFaction[0]==0 && enemyFaction[1]==0 && enemyFaction[2]==0 && enemyFaction[3]==0; } + bool IsNeutralToAll() const + { + for(int i = 0; i < 4; ++i) + if (enemyFaction[i] != 0) + return false; + return hostileMask == 0 && friendlyMask == 0; + } bool IsContestedGuardFaction() const { return (factionFlags & FACTION_TEMPLATE_FLAG_CONTESTED_GUARD)!=0; } }; diff --git a/src/shared/Database/SQLStorage.cpp b/src/shared/Database/SQLStorage.cpp index 875f56765..144b9dce1 100644 --- a/src/shared/Database/SQLStorage.cpp +++ b/src/shared/Database/SQLStorage.cpp @@ -25,8 +25,8 @@ extern DatabasePostgre WorldDatabase; extern DatabaseMysql WorldDatabase; #endif -const char CreatureInfosrcfmt[]="iiiiiisssiiiiiiiiiiffiffiiiiiiiiiiiffiiiiiiiiiiiiiiiiiiisiilliiis"; -const char CreatureInfodstfmt[]="iiiiiisssiiiiiiiiiiffiffiiiiiiiiiiiffiiiiiiiiiiiiiiiiiiisiilliiii"; +const char CreatureInfosrcfmt[]="iiiiiisssiiiiiiiiiiffiffiiiiiiiiiiiffiiiiiiiiiiiiiiiiiiisiifflliiis"; +const char CreatureInfodstfmt[]="iiiiiisssiiiiiiiiiiffiffiiiiiiiiiiiffiiiiiiiiiiiiiiiiiiisiifflliiii"; const char CreatureDataAddonInfofmt[]="iiiiiiis"; const char CreatureModelfmt[]="iffbi"; const char CreatureInfoAddonInfofmt[]="iiiiiiis"; diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index 6ce673beb..160925670 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "7361" + #define REVISION_NR "7387" #endif // __REVISION_NR_H__