From ee6072f232a54bfbda5c4cd4c9a92dbd5377d6eb Mon Sep 17 00:00:00 2001 From: VladimirMangos Date: Mon, 19 Jan 2009 23:51:07 +0300 Subject: [PATCH] [7117] Implement explicit recipe discovery abilities. * Implement SPELL_EFFECT_CREATE_ITEM_2 (157). This alos let work many item creating spells. * Add `skill_discovery_template`.`reqClass` for allow clas specific racipes storing in table. * Make primary key for `skill_discovery_template` pair (spellId,reqSpell) that allow have duplicate recipes for different reqSpells. * Implement SPELL_EFFECT_SCRIPT (77) cases for explicit recipe discovery spells with learn spell selected by `skill_discovery_template` data. Note: as expected explicit recipe discovery abilities always return some spell while exist any not learned yet for player class. --- sql/mangos.sql | 5 +- ...117_01_mangos_skill_discovery_template.sql | 6 ++ sql/updates/Makefile.am | 2 + src/game/SharedDefines.h | 2 +- src/game/SkillDiscovery.cpp | 81 ++++++++++++++++--- src/game/Spell.cpp | 2 +- src/game/Spell.h | 1 - src/game/SpellEffects.cpp | 38 +++++++-- src/game/SpellMgr.h | 5 ++ src/shared/revision_nr.h | 2 +- 10 files changed, 118 insertions(+), 26 deletions(-) create mode 100644 sql/updates/7117_01_mangos_skill_discovery_template.sql diff --git a/sql/mangos.sql b/sql/mangos.sql index 8d4088f95..b2e3c89e0 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_7107_01_mangos_string` bit(1) default NULL + `required_7117_01_mangos_skill_discovery_template` bit(1) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Used DB version notes'; -- @@ -12999,8 +12999,9 @@ DROP TABLE IF EXISTS `skill_discovery_template`; CREATE TABLE `skill_discovery_template` ( `spellId` mediumint(8) unsigned NOT NULL default '0' COMMENT 'SpellId of the discoverable spell', `reqSpell` mediumint(8) unsigned NOT NULL default '0' COMMENT 'spell requirement', + `reqClass` tinyint(2) unsigned NOT NULL default '0' COMMENT 'class requirement', `chance` float NOT NULL default '0' COMMENT 'chance to discover', - PRIMARY KEY (`spellId`) + PRIMARY KEY (`spellId`,`reqSpell`), ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Skill Discovery System'; -- diff --git a/sql/updates/7117_01_mangos_skill_discovery_template.sql b/sql/updates/7117_01_mangos_skill_discovery_template.sql new file mode 100644 index 000000000..c9cb8ad7c --- /dev/null +++ b/sql/updates/7117_01_mangos_skill_discovery_template.sql @@ -0,0 +1,6 @@ +ALTER TABLE db_version CHANGE COLUMN required_7107_01_mangos_string required_7117_01_mangos_skill_discovery_template bit; + +ALTER TABLE skill_discovery_template + DROP PRIMARY KEY, + ADD PRIMARY KEY (`spellId`,`reqSpell`), + ADD COLUMN reqClass tinyint(2) unsigned NOT NULL default '0' COMMENT 'class requirement' AFTER reqSpell; diff --git a/sql/updates/Makefile.am b/sql/updates/Makefile.am index 2cee52875..648949442 100644 --- a/sql/updates/Makefile.am +++ b/sql/updates/Makefile.am @@ -142,6 +142,7 @@ pkgdata_DATA = \ 7100_01_characters_character_spell.sql \ 7107_01_mangos_string.sql \ 7113_01_characters_character_achievement_progress.sql \ + 7117_01_mangos_skill_discovery_template.sql \ README ## Additional files to include when running 'make dist' @@ -264,4 +265,5 @@ EXTRA_DIST = \ 7100_01_characters_character_spell.sql \ 7107_01_mangos_string.sql \ 7113_01_characters_character_achievement_progress.sql \ + 7117_01_mangos_skill_discovery_template.sql \ README diff --git a/src/game/SharedDefines.h b/src/game/SharedDefines.h index cf4c90549..08c9f8be6 100644 --- a/src/game/SharedDefines.h +++ b/src/game/SharedDefines.h @@ -675,7 +675,7 @@ enum SpellEffects SPELL_EFFECT_154 = 154, SPELL_EFFECT_TITAN_GRIP = 155, SPELL_EFFECT_ADD_SOCKET = 156, - SPELL_EFFECT_157 = 157, + SPELL_EFFECT_CREATE_ITEM_2 = 157, SPELL_EFFECT_MILLING = 158, SPELL_EFFECT_ALLOW_RENAME_PET = 159, TOTAL_SPELL_EFFECTS = 160 diff --git a/src/game/SkillDiscovery.cpp b/src/game/SkillDiscovery.cpp index 7a2281428..eb7b687bf 100644 --- a/src/game/SkillDiscovery.cpp +++ b/src/game/SkillDiscovery.cpp @@ -29,14 +29,15 @@ struct SkillDiscoveryEntry { - uint32 spellId; - float chance; + uint32 spellId; // discavered spell + uint32 reqClass; // class limitation + float chance; // chance SkillDiscoveryEntry() - : spellId(0), chance(0) {} + : spellId(0), reqClass(0), chance(0) {} - SkillDiscoveryEntry(uint16 _spellId, float _chance) - : spellId(_spellId), chance(_chance) {} + SkillDiscoveryEntry(uint16 _spellId, uint32 req_class, float _chance) + : spellId(_spellId), reqClass(req_class), chance(_chance) {} }; typedef std::list SkillDiscoveryList; @@ -51,8 +52,8 @@ void LoadSkillDiscoveryTable() uint32 count = 0; - // 0 1 2 - QueryResult *result = WorldDatabase.Query("SELECT spellId, reqSpell, chance FROM skill_discovery_template"); + // 0 1 2 3 + QueryResult *result = WorldDatabase.Query("SELECT spellId, reqSpell, reqClass, chance FROM skill_discovery_template"); if (result) { @@ -67,11 +68,18 @@ void LoadSkillDiscoveryTable() uint32 spellId = fields[0].GetUInt32(); int32 reqSkillOrSpell = fields[1].GetInt32(); - float chance = fields[2].GetFloat(); + uint32 reqClass = fields[2].GetInt32(); + float chance = fields[3].GetFloat(); if( chance <= 0 ) // chance { - ssNonDiscoverableEntries << "spellId = " << spellId << " reqSkillOrSpell = " << reqSkillOrSpell << " chance = " << chance << "\n"; + ssNonDiscoverableEntries << "spellId = " << spellId << " reqSkillOrSpell = " << reqSkillOrSpell << " reqClass = " << reqClass << " chance = " << chance << "(chance problem)\n"; + continue; + } + + if(reqClass && (reqClass >= MAX_CLASSES || ((1 << (reqClass-1)) & CLASSMASK_ALL_PLAYABLE)==0)) + { + ssNonDiscoverableEntries << "spellId = " << spellId << " reqSkillOrSpell = " << reqSkillOrSpell << " reqClass = " << reqClass << " chance = " << chance << "(class problem)\n"; continue; } @@ -84,13 +92,16 @@ void LoadSkillDiscoveryTable() continue; } - if( spellEntry->Mechanic != MECHANIC_DISCOVERY ) + // mechanic discovery + if (spellEntry->Mechanic != MECHANIC_DISCOVERY && + // explicit discovery ability + !IsExplicitDiscoverySpell(spellEntry)) { - sLog.outErrorDb("Spell (ID: %u) not have have MECHANIC_DISCOVERY (28) value in Mechanic field in spell.dbc but listed in `skill_discovery_template` table",spellId); + sLog.outErrorDb("Spell (ID: %u) not have have MECHANIC_DISCOVERY (28) value in Mechanic field in spell.dbc and not 100% chance random discovery ability but listed in `skill_discovery_template` table",spellId); continue; } - SkillDiscoveryStore[reqSkillOrSpell].push_back( SkillDiscoveryEntry(spellId, chance) ); + SkillDiscoveryStore[reqSkillOrSpell].push_back( SkillDiscoveryEntry(spellId, reqClass, chance) ); } else if( reqSkillOrSpell == 0 ) // skill case { @@ -105,7 +116,7 @@ void LoadSkillDiscoveryTable() for(SkillLineAbilityMap::const_iterator _spell_idx = lower; _spell_idx != upper; ++_spell_idx) { - SkillDiscoveryStore[-int32(_spell_idx->second->skillId)].push_back( SkillDiscoveryEntry(spellId, chance) ); + SkillDiscoveryStore[-int32(_spell_idx->second->skillId)].push_back( SkillDiscoveryEntry(spellId, reqClass, chance) ); } } else @@ -113,6 +124,7 @@ void LoadSkillDiscoveryTable() sLog.outErrorDb("Spell (ID: %u) have negative value in `reqSpell` field in `skill_discovery_template` table",spellId); continue; } + ++count; } while (result->NextRow()); @@ -137,8 +149,45 @@ uint32 GetSkillDiscoverySpell(uint32 skillId, uint32 spellId, Player* player) if(tab != SkillDiscoveryStore.end()) { + SpellEntry const* spellInfo = sSpellStore.LookupEntry (spellId); + if(!spellInfo) + return 0; + + // explicit discovery spell chances (alwasy success if case exist) + if(IsExplicitDiscoverySpell(spellInfo)) + { + float full_chance = 0; + for(SkillDiscoveryList::iterator item_iter = tab->second.begin(); item_iter != tab->second.end(); ++item_iter) + if(!item_iter->reqClass || player->getClass ()==item_iter->reqClass) + if(!player->HasSpell(item_iter->spellId)) + full_chance += item_iter->chance; + + float rate = full_chance / 100.0f; + float roll = rand_chance() * rate; // roll now in range 0..full_chance + + for(SkillDiscoveryList::iterator item_iter = tab->second.begin(); item_iter != tab->second.end(); ++item_iter) + { + if(item_iter->reqClass && player->getClass ()!= item_iter->reqClass) + continue; + + if(player->HasSpell(item_iter->spellId)) + continue; + + if(item_iter->chance > roll) + return item_iter->spellId; + + roll -= item_iter->chance; + } + + return 0; + } + + for(SkillDiscoveryList::iterator item_iter = tab->second.begin(); item_iter != tab->second.end(); ++item_iter) { + if(item_iter->reqClass && player->getClass ()!= item_iter->reqClass) + continue; + if( roll_chance_f(item_iter->chance * sWorld.getRate(RATE_SKILL_DISCOVERY)) && !player->HasSpell(item_iter->spellId) ) return item_iter->spellId; @@ -147,12 +196,18 @@ uint32 GetSkillDiscoverySpell(uint32 skillId, uint32 spellId, Player* player) return 0; } + if(!skillId) + return 0; + // check skill line case tab = SkillDiscoveryStore.find(-(int32)skillId); if(tab != SkillDiscoveryStore.end()) { for(SkillDiscoveryList::iterator item_iter = tab->second.begin(); item_iter != tab->second.end(); ++item_iter) { + if(item_iter->reqClass && player->getClass ()!= item_iter->reqClass) + continue; + if( roll_chance_f(item_iter->chance * sWorld.getRate(RATE_SKILL_DISCOVERY)) && !player->HasSpell(item_iter->spellId) ) return item_iter->spellId; diff --git a/src/game/Spell.cpp b/src/game/Spell.cpp index 53a23275d..b59412bfa 100644 --- a/src/game/Spell.cpp +++ b/src/game/Spell.cpp @@ -3078,7 +3078,7 @@ void Spell::SendLogExecute() data << uint8(0); break; case SPELL_EFFECT_CREATE_ITEM: - case SPELL_EFFECT_157: + case SPELL_EFFECT_CREATE_ITEM_2: data << uint32(m_spellInfo->EffectItemType[0]); break; case SPELL_EFFECT_SUMMON: diff --git a/src/game/Spell.h b/src/game/Spell.h index 5befc6789..bbe50f5c0 100644 --- a/src/game/Spell.h +++ b/src/game/Spell.h @@ -358,7 +358,6 @@ class Spell void setState(uint32 state) { m_spellState = state; } void DoCreateItem(uint32 i, uint32 itemtype); - void WriteSpellGoTargets( WorldPacket * data ); void WriteAmmoToPacket( WorldPacket * data ); void FillTargetMap(); diff --git a/src/game/SpellEffects.cpp b/src/game/SpellEffects.cpp index 5e5412496..c95963817 100644 --- a/src/game/SpellEffects.cpp +++ b/src/game/SpellEffects.cpp @@ -54,6 +54,7 @@ #include "Util.h" #include "TemporarySummon.h" #include "ScriptCalls.h" +#include "SkillDiscovery.h" pEffect SpellEffects[TOTAL_SPELL_EFFECTS]= { @@ -214,7 +215,7 @@ pEffect SpellEffects[TOTAL_SPELL_EFFECTS]= &Spell::EffectNULL, //154 unused &Spell::EffectTitanGrip, //155 SPELL_EFFECT_TITAN_GRIP Allows you to equip two-handed axes, maces and swords in one hand, but you attack $49152s1% slower than normal. &Spell::EffectNULL, //156 Add Socket - &Spell::EffectNULL, //157 create/learn random item/spell for profession + &Spell::EffectCreateItem, //157 SPELL_EFFECT_CREATE_ITEM_2 create/learn item/spell for profession &Spell::EffectMilling, //158 milling &Spell::EffectNULL //159 allow rename pet once again }; @@ -4599,12 +4600,6 @@ void Spell::EffectScriptEffect(uint32 effIndex) { // TODO: we must implement hunter pet summon at login there (spell 6962) - // by spell id - switch(m_spellInfo->Id) - { - - } - switch(m_spellInfo->SpellFamilyName) { case SPELLFAMILY_GENERIC: @@ -4833,6 +4828,7 @@ void Spell::EffectScriptEffect(uint32 effIndex) break; } + // Emblazon Runeblade case 51770: { if(!unitTarget) @@ -4850,7 +4846,35 @@ void Spell::EffectScriptEffect(uint32 effIndex) unitTarget->CastSpell(unitTarget, damage, false); break; } + // random spell learn instead placeholder + case 60893: // Northrend Alchemy Research + case 61177: // Northrend Inscription Research + case 61288: // Minor Inscription Research + case 61756: // Northrend Inscription Research (FAST QA VERSION) + { + if(!IsExplicitDiscoverySpell(m_spellInfo)) + { + sLog.outError("Wrong explicit discowry spell %u structure, or outdated...",m_spellInfo->Id); + return; + } + if(m_caster->GetTypeId()!=TYPEID_PLAYER) + return; + Player* player = (Player*)m_caster; + + // need replace effect 0 item by loot + uint32 reagent_id = m_spellInfo->EffectItemType[0]; + if(!player->HasItemCount(reagent_id,1)) + return; + + // remove reagent + uint32 count = 1; + player->DestroyItemCount (reagent_id,count,true); + + if(uint32 discoveredSpell = GetSkillDiscoverySpell(0, m_spellInfo->Id, player)) + player->learnSpell(discoveredSpell); + return; + } } break; } diff --git a/src/game/SpellMgr.h b/src/game/SpellMgr.h index 1ae82e7c1..7f2b8803b 100644 --- a/src/game/SpellMgr.h +++ b/src/game/SpellMgr.h @@ -312,6 +312,11 @@ inline bool IsElementalShield(SpellEntry const *spellInfo) return (spellInfo->SpellFamilyFlags & 0x42000000400LL) || spellInfo->Id == 23552; } +inline bool IsExplicitDiscoverySpell(SpellEntry const *spellInfo) +{ + return spellInfo->Effect[0]==SPELL_EFFECT_CREATE_ITEM_2 && spellInfo->Effect[1]==SPELL_EFFECT_SCRIPT_EFFECT; +} + int32 CompareAuraRanks(uint32 spellId_1, uint32 effIndex_1, uint32 spellId_2, uint32 effIndex_2); bool IsSingleFromSpellSpecificPerCaster(uint32 spellSpec1,uint32 spellSpec2); bool IsPassiveSpell(uint32 spellId); diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index b6df063fc..baa9e020a 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 "7116" + #define REVISION_NR "7117" #endif // __REVISION_NR_H__