1
0

Added classic spell level support (aka mini ding spells) like warrior getting knee break at lvl 19.6

This commit is contained in:
Emagi 2025-01-13 09:06:11 -05:00
parent 9b60035656
commit 82a1885887
9 changed files with 56 additions and 26 deletions

View File

@ -154,9 +154,9 @@ void Bot::MessageGroup(string msg) {
void Bot::GetNewSpells() { void Bot::GetNewSpells() {
vector<Spell*> spells; vector<Spell*> spells;
vector<Spell*>* spells1 = master_spell_list.GetSpellListByAdventureClass(GetAdventureClass(), GetLevel(), 1); vector<Spell*>* spells1 = master_spell_list.GetSpellListByAdventureClass(GetAdventureClass(), (double)GetLevel(), 1);
vector<Spell*>* spells2 = master_spell_list.GetSpellListByAdventureClass(classes.GetBaseClass(GetAdventureClass()), GetLevel(), 1); vector<Spell*>* spells2 = master_spell_list.GetSpellListByAdventureClass(classes.GetBaseClass(GetAdventureClass()), (double)GetLevel(), 1);
vector<Spell*>* spells3 = master_spell_list.GetSpellListByAdventureClass(classes.GetSecondaryBaseClass(GetAdventureClass()), GetLevel(), 1); vector<Spell*>* spells3 = master_spell_list.GetSpellListByAdventureClass(classes.GetSecondaryBaseClass(GetAdventureClass()), (double)GetLevel(), 1);
spells.insert(spells.end(), spells1->begin(), spells1->end()); spells.insert(spells.end(), spells1->begin(), spells1->end());
spells.insert(spells.end(), spells2->begin(), spells2->end()); spells.insert(spells.end(), spells2->begin(), spells2->end());

View File

@ -2505,8 +2505,11 @@ void Entity::CureDetrimentByType(int8 cure_count, int8 det_type, string cure_nam
bool has_level_checks = false; bool has_level_checks = false;
for (int32 x = 0; x < levels->size(); x++){ for (int32 x = 0; x < levels->size(); x++){
has_level_checks = true; has_level_checks = true;
int8 use_classic_lvls = rule_manager.GetGlobalRule(R_Spells, UseClassicSpellLevel)->GetInt8();
if(levels->at(x)->classic_spell_level == 0.0f)
use_classic_lvls = 0;
// class checks are worthless we can't guarantee the caster is that class // class checks are worthless we can't guarantee the caster is that class
if (!cure_level || cure_level >= (levels->at(x)->spell_level / 10)){ if (!cure_level || (use_classic_lvls && cure_level >= std::floor(levels->at(x)->classic_spell_level)) || (!use_classic_lvls && cure_level >= (levels->at(x)->spell_level / 10))){
pass_level_check = true; pass_level_check = true;
break; break;
} }
@ -2555,7 +2558,10 @@ void Entity::CureDetrimentByControlEffect(int8 cure_count, int8 control_type, st
info_struct = det->caster->GetInfoStruct(); info_struct = det->caster->GetInfoStruct();
pass_level_check = false; pass_level_check = false;
for (int32 x = 0; x < levels->size(); x++){ for (int32 x = 0; x < levels->size(); x++){
if (!cure_level || cure_level >= (levels->at(x)->spell_level / 10)){ int8 use_classic_lvls = rule_manager.GetGlobalRule(R_Spells, UseClassicSpellLevel)->GetInt8();
if(levels->at(x)->classic_spell_level == 0.0f)
use_classic_lvls = 0;
if (!cure_level || (use_classic_lvls && cure_level >= std::floor(levels->at(x)->classic_spell_level)) || (!use_classic_lvls && cure_level >= (levels->at(x)->spell_level / 10))){
pass_level_check = true; pass_level_check = true;
break; break;
} }

View File

@ -4490,6 +4490,8 @@ bool Player::AddXP(int32 xp_amount){
GetPlayerInfo()->CalculateXPPercentages(); GetPlayerInfo()->CalculateXPPercentages();
current_xp_percent = ((float)GetXP()/(float)GetNeededXP())*100; current_xp_percent = ((float)GetXP()/(float)GetNeededXP())*100;
if(current_xp_percent >= miniding_min_percent){ if(current_xp_percent >= miniding_min_percent){
if(GetClient() && rule_manager.GetGlobalRule(R_Spells, UseClassicSpellLevel)->GetInt8())
GetClient()->SendNewAdventureSpells(); // mini ding involves checking spells again in classic level settings
SetHP(GetTotalHP()); SetHP(GetTotalHP());
SetPower(GetTotalPower()); SetPower(GetTotalPower());
GetZone()->SendCastSpellPacket(332, this, this); //send mini level up spell effect GetZone()->SendCastSpellPacket(332, this, this); //send mini level up spell effect

View File

@ -394,6 +394,7 @@ void RuleManager::Init()
RULE_INIT(R_Spells, MinistrationPowerReductionMax, "15.0"); // max percentage of power reduction for spells with ministration mastery skill (default is 15.0 for 15%) RULE_INIT(R_Spells, MinistrationPowerReductionMax, "15.0"); // max percentage of power reduction for spells with ministration mastery skill (default is 15.0 for 15%)
RULE_INIT(R_Spells, MinistrationPowerReductionSkill, "25"); // divides by integer value to establish how much skill req for higher power reduction RULE_INIT(R_Spells, MinistrationPowerReductionSkill, "25"); // divides by integer value to establish how much skill req for higher power reduction
RULE_INIT(R_Spells, MasterSkillReduceSpellResist, "25"); // divides by integer value to establish how much skill bonus for reducing spell resistance on target RULE_INIT(R_Spells, MasterSkillReduceSpellResist, "25"); // divides by integer value to establish how much skill bonus for reducing spell resistance on target
RULE_INIT(R_Spells, UseClassicSpellLevel, "0"); // Uses fractional spell levels (eg. you gain spells inbetween levels).
RULE_INIT(R_Expansion, GlobalExpansionFlag, "0"); RULE_INIT(R_Expansion, GlobalExpansionFlag, "0");
RULE_INIT(R_Expansion, GlobalHolidayFlag, "0"); RULE_INIT(R_Expansion, GlobalHolidayFlag, "0");

View File

@ -240,6 +240,7 @@ enum RuleType {
MinistrationPowerReductionMax, MinistrationPowerReductionMax,
MinistrationPowerReductionSkill, MinistrationPowerReductionSkill,
MasterSkillReduceSpellResist, MasterSkillReduceSpellResist,
UseClassicSpellLevel,
/* ZONE TIMERS */ /* ZONE TIMERS */
RegenTimer, RegenTimer,

View File

@ -167,7 +167,7 @@ Spell::Spell(Spell* host_spell, bool unique_spell)
for (itr = host_spell->levels.begin(); itr != host_spell->levels.end(); itr++) for (itr = host_spell->levels.begin(); itr != host_spell->levels.end(); itr++)
{ {
LevelArray* lvlArray = *itr; LevelArray* lvlArray = *itr;
AddSpellLevel(lvlArray->adventure_class, lvlArray->tradeskill_class, lvlArray->spell_level); AddSpellLevel(lvlArray->adventure_class, lvlArray->tradeskill_class, lvlArray->spell_level, lvlArray->classic_spell_level);
} }
std::vector<SpellDisplayEffect*>::iterator sdeitr; std::vector<SpellDisplayEffect*>::iterator sdeitr;
@ -295,6 +295,9 @@ int16 Spell::GetLevelRequired(Player* player){
for(itr = levels.begin(); itr != levels.end(); itr++){ for(itr = levels.begin(); itr != levels.end(); itr++){
level = *itr; level = *itr;
if(level && level->adventure_class == player->GetAdventureClass()){ if(level && level->adventure_class == player->GetAdventureClass()){
if(rule_manager.GetGlobalRule(R_Spells, UseClassicSpellLevel)->GetInt8() && level->classic_spell_level > 0.0f)
ret = std::floor(level->classic_spell_level);
else
ret = level->spell_level/10; ret = level->spell_level/10;
break; break;
} }
@ -722,9 +725,14 @@ void Spell::AppendLevelInformation(PacketStruct* packet) {
LevelArray* levelData = tmpArray->at(i); LevelArray* levelData = tmpArray->at(i);
packet->setArrayDataByName("adventure_class", levelData->adventure_class, i); packet->setArrayDataByName("adventure_class", levelData->adventure_class, i);
packet->setArrayDataByName("tradeskill_class", levelData->tradeskill_class, i); packet->setArrayDataByName("tradeskill_class", levelData->tradeskill_class, i);
if(rule_manager.GetGlobalRule(R_Spells, UseClassicSpellLevel)->GetInt8() && levelData->classic_spell_level) {
packet->setArrayDataByName("spell_level", (int16)(levelData->classic_spell_level * 10.0f), i);
}
else {
packet->setArrayDataByName("spell_level", levelData->spell_level, i); packet->setArrayDataByName("spell_level", levelData->spell_level, i);
} }
} }
}
sint16 Spell::TranslateClientSpellIcon(int16 version) { sint16 Spell::TranslateClientSpellIcon(int16 version) {
sint16 spell_icon = GetSpellIcon(); sint16 spell_icon = GetSpellIcon();
@ -1270,12 +1278,13 @@ const char* Spell::GetDescription(){
return spell->description.data.c_str(); return spell->description.data.c_str();
} }
void Spell::AddSpellLevel(int8 adventure_class, int8 tradeskill_class, int16 level){ void Spell::AddSpellLevel(int8 adventure_class, int8 tradeskill_class, int16 level, float classic_spell_level){
std::unique_lock lock(MSpellInfo); std::unique_lock lock(MSpellInfo);
LevelArray* lvl = new LevelArray; LevelArray* lvl = new LevelArray;
lvl->adventure_class = adventure_class; lvl->adventure_class = adventure_class;
lvl->tradeskill_class = tradeskill_class; lvl->tradeskill_class = tradeskill_class;
lvl->spell_level = level; lvl->spell_level = level;
lvl->classic_spell_level = classic_spell_level;
levels.push_back(lvl); levels.push_back(lvl);
} }
@ -2169,14 +2178,15 @@ vector <LevelArray*>* Spell::GetSpellLevels(){
bool Spell::ScribeAllowed(Player* player){ bool Spell::ScribeAllowed(Player* player){
std::shared_lock lock(MSpellInfo); std::shared_lock lock(MSpellInfo);
bool ret = false; bool ret = false;
double current_xp_percent = ((double)player->GetXP()/(double)player->GetNeededXP())*100;
if(player){ if(player){
for(int32 i=0;!ret && i<levels.size();i++){ for(int32 i=0;!ret && i<levels.size();i++){
int16 mylevel = player->GetLevel(); bool classiclevelmatch = ((double)player->GetLevel()+current_xp_percent) >= levels[i]->classic_spell_level;
int16 spelllevels = levels[i]->spell_level; bool classic_match_only = false;
bool advlev = player->GetAdventureClass() == levels[i]->adventure_class; if(levels[i]->classic_spell_level > 0.0f && rule_manager.GetGlobalRule(R_Spells, UseClassicSpellLevel)->GetInt8()) {
bool tslev = player->GetTradeskillClass() == levels[i]->tradeskill_class; classic_match_only = true;
bool levelmatch = player->GetLevel() >= levels[i]->spell_level; }
if((player->GetAdventureClass() == levels[i]->adventure_class || player->GetTradeskillClass() == levels[i]->tradeskill_class) && player->GetLevel() >= levels[i]->spell_level/10) if((player->GetAdventureClass() == levels[i]->adventure_class || player->GetTradeskillClass() == levels[i]->tradeskill_class) && ((!classic_match_only && player->GetLevel() >= levels[i]->spell_level/10) || (classic_match_only && classiclevelmatch)))
ret = true; ret = true;
} }
} }
@ -2332,12 +2342,13 @@ EQ2Packet* MasterSpellList::GetSpecialSpellPacket(int32 id, int8 tier, Client* c
return 0; return 0;
} }
vector<Spell*>* MasterSpellList::GetSpellListByAdventureClass(int8 class_id, int16 max_level, int8 max_tier){ vector<Spell*>* MasterSpellList::GetSpellListByAdventureClass(int8 class_id, double max_level_classic, int8 max_tier){
vector<Spell*>* ret = new vector<Spell*>; vector<Spell*>* ret = new vector<Spell*>;
if(class_id >= MAX_CLASSES) { if(class_id >= MAX_CLASSES) {
return ret; return ret;
} }
int8 use_classic_levels = rule_manager.GetGlobalRule(R_Spells, UseClassicSpellLevel)->GetInt8();
Spell* spell = 0; Spell* spell = 0;
vector<LevelArray*>* levels = 0; vector<LevelArray*>* levels = 0;
LevelArray* level = 0; LevelArray* level = 0;
@ -2345,7 +2356,7 @@ vector<Spell*>* MasterSpellList::GetSpellListByAdventureClass(int8 class_id, int
MMasterSpellList.lock(); MMasterSpellList.lock();
map<int32, map<int32, Spell*> >::iterator iter; map<int32, map<int32, Spell*> >::iterator iter;
map<int32, Spell*>::iterator iter2; map<int32, Spell*>::iterator iter2;
max_level *= 10; //convert to client level format, which is 10 times higher int16 max_level = std::floor(max_level_classic) * 10; //convert to client level format, which is 10 times higher
for(iter = class_spell_list[class_id].begin();iter != class_spell_list[class_id].end(); iter++){ for(iter = class_spell_list[class_id].begin();iter != class_spell_list[class_id].end(); iter++){
for(iter2 = iter->second.begin();iter2 != iter->second.end(); iter2++){ for(iter2 = iter->second.begin();iter2 != iter->second.end(); iter2++){
spell = iter2->second; spell = iter2->second;
@ -2354,10 +2365,16 @@ vector<Spell*>* MasterSpellList::GetSpellListByAdventureClass(int8 class_id, int
levels = spell->GetSpellLevels(); levels = spell->GetSpellLevels();
for(level_itr = levels->begin(); level_itr != levels->end(); level_itr++){ for(level_itr = levels->begin(); level_itr != levels->end(); level_itr++){
level = *level_itr; level = *level_itr;
if(level->spell_level <= max_level && level->adventure_class == class_id){ if(level->adventure_class == class_id){
if((!use_classic_levels || level->classic_spell_level == 0.0f) && level->spell_level <= max_level) {
ret->push_back(spell); ret->push_back(spell);
break; break;
} }
else if(use_classic_levels && level->classic_spell_level <= max_level_classic) {
ret->push_back(spell);
break;
}
}
} }
} }
} }

View File

@ -218,6 +218,7 @@ struct LevelArray{
int8 adventure_class; int8 adventure_class;
int8 tradeskill_class; int8 tradeskill_class;
int16 spell_level; int16 spell_level;
float classic_spell_level;
}; };
struct SpellDisplayEffect{ struct SpellDisplayEffect{
int8 percentage; int8 percentage;
@ -328,7 +329,7 @@ public:
EQ2Packet* SerializeSpell(Client* client, bool display, bool trait_display = false, int8 packet_type = 0, int8 sub_packet_type = 0, const char* struct_name = 0, bool send_partial_packet = false); EQ2Packet* SerializeSpell(Client* client, bool display, bool trait_display = false, int8 packet_type = 0, int8 sub_packet_type = 0, const char* struct_name = 0, bool send_partial_packet = false);
EQ2Packet* SerializeSpecialSpell(Client* client, bool display, int8 packet_type = 0, int8 sub_packet_type = 0); EQ2Packet* SerializeSpecialSpell(Client* client, bool display, int8 packet_type = 0, int8 sub_packet_type = 0);
EQ2Packet* SerializeAASpell(Client* client,int8 tier, AltAdvanceData* data, bool display, bool trait_display = false, int8 packet_type = 0, int8 sub_packet_type = 0, const char* struct_name = 0); EQ2Packet* SerializeAASpell(Client* client,int8 tier, AltAdvanceData* data, bool display, bool trait_display = false, int8 packet_type = 0, int8 sub_packet_type = 0, const char* struct_name = 0);
void AddSpellLevel(int8 adventure_class, int8 tradeskill_class, int16 level); void AddSpellLevel(int8 adventure_class, int8 tradeskill_class, int16 level, float classic_spell_level);
void AddSpellEffect(int8 percentage, int8 subbullet, string description); void AddSpellEffect(int8 percentage, int8 subbullet, string description);
void AddSpellLuaData(int8 type, int int_value, int int_value2, float float_value, float float_value2, bool bool_value, string string_value,string string_value2, string helper); void AddSpellLuaData(int8 type, int int_value, int int_value2, float float_value, float float_value2, bool bool_value, string string_value,string string_value2, string helper);
void AddSpellLuaDataInt(int value, int value2, string helper); void AddSpellLuaDataInt(int value, int value2, string helper);
@ -402,7 +403,7 @@ public:
map<int32, map<int32, Spell* > > class_spell_list[MAX_CLASSES]; map<int32, map<int32, Spell* > > class_spell_list[MAX_CLASSES];
map<int32, Spell*> spell_soecrc_map; map<int32, Spell*> spell_soecrc_map;
Spell* GetSpell(int32 id, int8 tier); Spell* GetSpell(int32 id, int8 tier);
vector<Spell*>* GetSpellListByAdventureClass(int8 class_id, int16 max_level, int8 max_tier); vector<Spell*>* GetSpellListByAdventureClass(int8 class_id, double max_level, int8 max_tier);
vector<Spell*>* GetSpellListByTradeskillClass(int8 class_id, int16 max_level, int8 max_tier); vector<Spell*>* GetSpellListByTradeskillClass(int8 class_id, int16 max_level, int8 max_tier);
Spell* GetSpellByName(const char* name); Spell* GetSpellByName(const char* name);
Spell* GetSpellByCRC(int32 spell_crc); Spell* GetSpellByCRC(int32 spell_crc);

View File

@ -4872,7 +4872,7 @@ void WorldDatabase::SaveQuickBar(int32 char_id, vector<QuickBarItem*>* quickbar_
map<int32, vector<LevelArray*> >* WorldDatabase::LoadSpellClasses(){ map<int32, vector<LevelArray*> >* WorldDatabase::LoadSpellClasses(){
map<int32, vector<LevelArray*> >* ret = new map<int32, vector<LevelArray*> >(); map<int32, vector<LevelArray*> >* ret = new map<int32, vector<LevelArray*> >();
Query query; Query query;
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT spell_id, adventure_class_id, tradeskill_class_id, level FROM spell_classes"); MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT spell_id, adventure_class_id, tradeskill_class_id, level, classic_level FROM spell_classes");
MYSQL_ROW row; MYSQL_ROW row;
LevelArray* level = 0; LevelArray* level = 0;
while(result && (row = mysql_fetch_row(result))){ while(result && (row = mysql_fetch_row(result))){
@ -4880,6 +4880,7 @@ map<int32, vector<LevelArray*> >* WorldDatabase::LoadSpellClasses(){
level->adventure_class = atoi(row[1]); level->adventure_class = atoi(row[1]);
level->tradeskill_class = atoi(row[2]); level->tradeskill_class = atoi(row[2]);
level->spell_level = atoi(row[3]); level->spell_level = atoi(row[3]);
level->classic_spell_level = atof(row[4]);
(*ret)[atoul(row[0])].push_back(level); (*ret)[atoul(row[0])].push_back(level);
} }
return ret; return ret;
@ -5101,7 +5102,7 @@ void WorldDatabase::LoadSpells()
for(int8 i=0; i<level_array->size(); i++) for(int8 i=0; i<level_array->size(); i++)
{ {
spell->AddSpellLevel(level_array->at(i)->adventure_class, level_array->at(i)->tradeskill_class, level_array->at(i)->spell_level*10); spell->AddSpellLevel(level_array->at(i)->adventure_class, level_array->at(i)->tradeskill_class, level_array->at(i)->spell_level*10, level_array->at(i)->classic_spell_level);
} }
} }

View File

@ -10167,8 +10167,9 @@ void Client::ProcessTeleportLocation(EQApplicationPacket* app) {
} }
void Client::SendNewSpells(int8 class_id) { void Client::SendNewSpells(int8 class_id) {
if (class_id > 0) { if (class_id > 0 && player) {
vector<Spell*>* spells = master_spell_list.GetSpellListByAdventureClass(class_id, player->GetLevel(), 1); double current_xp_percent = ((double)player->GetXP()/(double)player->GetNeededXP())*100;
vector<Spell*>* spells = master_spell_list.GetSpellListByAdventureClass(class_id, (double)player->GetLevel()+current_xp_percent, 1);
AddSendNewSpells(spells); AddSendNewSpells(spells);
safe_delete(spells); safe_delete(spells);
} }