Merge from master

This commit is contained in:
KimLS 2015-07-15 23:15:25 -07:00
commit 9f1f36cca6
95 changed files with 25778 additions and 6435 deletions

View File

@ -1,5 +1,46 @@
EQEMu Changelog (Started on Sept 24, 2003 15:50)
-------------------------------------------------------
== 07/06/2015 ==
mackal: Implement Triple Attack Skill
Parses showed about rand(1000) for the chance, may need more investigating
Corrected Double Attack chances as well
Running optional 2015_07_06_TripleAttack.sql will set current toons to their max skill
This is optional because the admins might want to go a different route.
== 07/05/2015 ==
mackal: Rewrite NPC combat attack round logic
An NPC "quading" is really just an NPC with innate dual wield that doubles on both hands
The old rules allowed NPCs to hit 6 times in one round
NPCs also seem to have their own skill progression for DW/DA
See: http://www.eqemulator.org/forums/showthread.php?t=38708
You can set Combat:UseLiveCombatRounds to false to use the old rules
PC Double Attack rates kind of follow the same thing but still needs to be implemented
Kinglykrab: WARNING: summonburriedplayercorpse is now summonburiedplayercorpse, getplayerburriedcorpsecount is now getplayerburiedcorpsecount, summon_burried_player_corpse is now summon_buried_player_corpse, and get_player_burried_corpse_count is now get_player_buried_corpse_count FIX THESE IN YOUR SCRIPTS OR THEY WILL NOT WORK!!!!
Added bot saylinks (thanks Uleat!)
Command aliases for #augmentitem (#aug), #findnpctype (#fn), #findspell (#fs)
Bot command changes: #bot sow -> #bot speed, #bot magepet -> #bot setpet, #bot resurrectme -> #bot resurrect
Changed all occurrences of burried to buried in the code.
client_packet.cpp was referencing old columns in character_corpses, not sure how we didn't already see this before.
== 7/4/2015 ==
mackal: Reworked the activated avoidace skills (riposte, dodge, etc) based on dev quotes
This also fixes the order things are checked (avoidance skills, THEN hit/miss)
Also riposte immunity from the increase riposte chance spell effect.
== 7/2/2015 ==
KLS/Demonstar55: AA system has been rewritten fixing a ton of bugs and extending functionality while making it easier to create new AA points.
KLS/Demonstar55: New tables are needed and so older data will need to be migrated to the new system.
KLS/Demonstar55: The SQL saves the old aa points spent/avail/character_alt_abilities data if any server ops want to do something different than PEQ did with it.
KLS/Demonstar55: Will try to get a wiki entry written up next week some time explaining the system but it's really not hard to follow the db tables in the meantime.
== 7/1/2015 ==
Akkadius: Fix an issue where emote messages would overflow the buffer of 256 by increasing the size and changing some of the initialization
Akkadius: Added a custom Health Update message that will display in the middle of the players screen, to enable this server wide you must enable rule 'Character:MarqueeHPUpdates'
Example: https://www.youtube.com/watch?v=KUVdbPxLIn0
Akkadius: (Haynar) Fixed some runspeed issues with Perl and LUA scripts
Akkadius: (Haynar) Updated #showstats and #npcstats for new speed calcs to display speeds again in familiar float format.
Akkadius: (Haynar) Improved client movement while AI Controlled, such as feared and charmed. Movement will be much smoother from clients perspective.
== 06/12/2015 ==
Uleat: Adjusted SessionStats to better reflect a sister implementation

View File

@ -150,4 +150,26 @@ static ClientVersion ClientVersionFromBit(uint32 clientVersionBit)
}
}
static uint32 ExpansionFromClientVersion(ClientVersion clientVersion)
{
switch(clientVersion)
{
case ClientVersion::Unknown:
case ClientVersion::Client62:
case ClientVersion::Titanium:
return 0x000007FFU;
case ClientVersion::SoF:
return 0x00007FFFU;
case ClientVersion::SoD:
return 0x0000FFFFU;
case ClientVersion::UF:
return 0x0001FFFFU;
case ClientVersion::RoF:
case ClientVersion::RoF2:
return 0x000FFFFFU;
default:
return 0;
}
}
#endif /* CLIENTVERSIONS_H */

View File

@ -1384,7 +1384,7 @@ bool Database::MoveCharacterToZone(const char* charname, const char* zonename) {
}
bool Database::MoveCharacterToZone(uint32 iCharID, const char* iZonename) {
std::string query = StringFormat("UPDATE `character_data` SET `zone_id` = %i, `x` = -1, `y` = -1, `z` = -1 WHERE `id` = %i", iZonename, GetZoneID(iZonename), iCharID);
std::string query = StringFormat("UPDATE `character_data` SET `zone_id` = %i, `x` = -1, `y` = -1, `z` = -1 WHERE `id` = %i", GetZoneID(iZonename), iCharID);
auto results = QueryDatabase(query);
if (!results.Success()) {

View File

@ -548,4 +548,5 @@ N(OP_ZoneServerInfo),
N(OP_ZoneServerReady),
N(OP_ZoneSpawns),
N(OP_ZoneUnavail),
N(OP_ResetAA),
// mail and chat opcodes located in ../mail_oplist.h

View File

@ -4219,6 +4219,52 @@ struct UseAA_Struct {
uint32 end;
};
//new AA stuff
//reference only
struct AARankInfo_Struct
{
uint32 id;
int32 upper_hotkey_sid;
int32 lower_hotkey_sid;
int32 title_sid;
int32 desc_sid;
int32 level_req;
int32 cost;
uint32 seq;
uint32 current_level;
uint32 type;
int32 spell;
int32 spell_type;
int32 spell_refresh;
int32 classes;
int32 max_level;
int32 prev_id;
int32 next_id;
int32 total_cost;
int32 expansion;
int32 category;
uint32 charges;
uint8 grant_only;
uint32 total_effects;
uint32 total_prereqs;
};
struct AARankPrereq_Struct
{
int32 aa_id;
int32 points;
};
struct AARankEffect_Struct
{
int32 effect_id;
int32 base1;
int32 base2;
int32 slot;
};
//old AA stuff
struct AA_Ability {
/*00*/ uint32 skill_id;
/*04*/ uint32 base1;
@ -4273,7 +4319,7 @@ struct SendAA_Struct {
struct AA_Action {
/*00*/ uint32 action;
/*04*/ uint32 ability;
/*08*/ uint32 unknown08;
/*08*/ uint32 target_id;
/*12*/ uint32 exp_value;
};

View File

@ -867,7 +867,7 @@ bool BaseGuildManager::QueryWithLogging(std::string query, const char *errmsg) {
//factored out so I dont have to copy this crap.
#ifdef BOTS
#define GuildMemberBaseQuery \
"SELECT c.id,c.name,c.class,c.level,c.timelaston,c.zoneid," \
"SELECT c.id,c.name,c.class,c.level,c.last_login,c.zone_id," \
" g.guild_id,g.rank,g.tribute_enable,g.total_tribute,g.last_tribute," \
" g.banker,g.public_note,g.alt" \
" FROM vwBotCharacterMobs AS c LEFT JOIN vwGuildMembers AS g ON c.id=g.char_id AND c.mobtype = g.mobtype "

View File

@ -146,6 +146,7 @@ INr(OP_GuildDelete); //?
IN(OP_GuildPublicNote, GuildUpdate_PublicNote);
INz(OP_GetGuildsList); //?
IN(OP_SetGuildMOTD, GuildMOTD_Struct);
IN(OP_SetRunMode, SetRunMode_Struct);
INz(OP_GuildPeace); //?
INz(OP_GuildWar); //?
IN(OP_GuildLeader, GuildMakeLeader);

View File

@ -2810,7 +2810,7 @@ namespace RoF
eq->aa_spent = emu->aa_spent;
// These fields may need to be correctly populated at some point
eq->aapoints_assigned = emu->aa_spent + 1;
eq->aapoints_assigned = emu->aa_spent;
eq->aa_spent_general = 0;
eq->aa_spent_archetype = 0;
eq->aa_spent_class = 0;
@ -2846,58 +2846,81 @@ namespace RoF
ENCODE(OP_SendAATable)
{
ENCODE_LENGTH_ATLEAST(SendAA_Struct);
SETUP_VAR_ENCODE(SendAA_Struct);
ALLOC_VAR_ENCODE(structs::SendAA_Struct, sizeof(structs::SendAA_Struct) + emu->total_abilities*sizeof(structs::AA_Ability));
EQApplicationPacket *inapp = *p;
*p = nullptr;
AARankInfo_Struct *emu = (AARankInfo_Struct*)inapp->pBuffer;
// Check clientver field to verify this AA should be sent for SoF
// clientver 1 is for all clients and 5 is for Live
if (emu->clientver <= 7)
{
OUT(id);
eq->unknown004 = 1;
//eq->hotkey_sid = (emu->hotkey_sid==4294967295UL)?0:(emu->id - emu->current_level + 1);
//eq->hotkey_sid2 = (emu->hotkey_sid2==4294967295UL)?0:(emu->id - emu->current_level + 1);
//eq->title_sid = emu->id - emu->current_level + 1;
//eq->desc_sid = emu->id - emu->current_level + 1;
eq->hotkey_sid = (emu->hotkey_sid == 4294967295UL) ? -1 : (emu->sof_next_skill);
eq->hotkey_sid2 = (emu->hotkey_sid2 == 4294967295UL) ? -1 : (emu->sof_next_skill);
eq->title_sid = emu->sof_next_skill;
eq->desc_sid = emu->sof_next_skill;
OUT(class_type);
OUT(cost);
OUT(seq);
OUT(current_level);
eq->prereq_skill_count = 1; // min 1
OUT(prereq_skill);
eq->prereq_minpoints_count = 1; // min 1
OUT(prereq_minpoints);
eq->type = emu->sof_type;
OUT(spellid);
eq->unknown057 = 1; // Introduced during HoT
OUT(spell_type);
OUT(spell_refresh);
OUT(classes);
OUT(berserker);
//eq->max_level = emu->sof_max_level;
OUT(max_level);
OUT(last_id);
OUT(next_id);
OUT(cost2);
eq->aa_expansion = emu->aa_expansion;
eq->special_category = emu->special_category;
eq->expendable_charges = emu->special_category == 7 ? 1 : 0; // temp hack, this can actually be any number
OUT(total_abilities);
unsigned int r;
for (r = 0; r < emu->total_abilities; r++) {
OUT(abilities[r].skill_id);
OUT(abilities[r].base1);
OUT(abilities[r].base2);
OUT(abilities[r].slot);
}
// the structs::SendAA_Struct includes enough space for 1 prereq which is the min even if it has no prereqs
auto prereq_size = emu->total_prereqs > 1 ? (emu->total_prereqs - 1) * 8 : 0;
auto outapp = new EQApplicationPacket(OP_SendAATable, sizeof(structs::SendAA_Struct) + emu->total_effects * sizeof(structs::AA_Ability) + prereq_size);
inapp->SetReadPosition(sizeof(AARankInfo_Struct)+emu->total_effects * sizeof(AARankEffect_Struct));
std::vector<int32> skill;
std::vector<int32> points;
for(auto i = 0; i < emu->total_prereqs; ++i) {
skill.push_back(inapp->ReadUInt32());
points.push_back(inapp->ReadUInt32());
}
FINISH_ENCODE();
outapp->WriteUInt32(emu->id);
outapp->WriteUInt8(1);
outapp->WriteSInt32(emu->upper_hotkey_sid);
outapp->WriteSInt32(emu->lower_hotkey_sid);
outapp->WriteSInt32(emu->title_sid);
outapp->WriteSInt32(emu->desc_sid);
outapp->WriteSInt32(emu->level_req);
outapp->WriteSInt32(emu->cost);
outapp->WriteUInt32(emu->seq);
outapp->WriteUInt32(emu->current_level);
if(emu->total_prereqs) {
outapp->WriteUInt32(emu->total_prereqs);
for(auto &e : skill)
outapp->WriteSInt32(e);
outapp->WriteUInt32(emu->total_prereqs);
for(auto &e : points)
outapp->WriteSInt32(e);
}
else {
outapp->WriteUInt32(1);
outapp->WriteUInt32(0);
outapp->WriteUInt32(1);
outapp->WriteUInt32(0);
}
outapp->WriteSInt32(emu->type);
outapp->WriteSInt32(emu->spell);
outapp->WriteSInt32(1);
outapp->WriteSInt32(emu->spell_type);
outapp->WriteSInt32(emu->spell_refresh);
outapp->WriteSInt32(emu->classes);
outapp->WriteSInt32(emu->max_level);
outapp->WriteSInt32(emu->prev_id);
outapp->WriteSInt32(emu->next_id);
outapp->WriteSInt32(emu->total_cost);
outapp->WriteUInt8(0);
outapp->WriteUInt8(emu->grant_only);
outapp->WriteUInt8(0);
outapp->WriteUInt32(emu->charges);
outapp->WriteSInt32(emu->expansion);
outapp->WriteSInt32(emu->category);
outapp->WriteUInt8(0); // shroud
outapp->WriteUInt8(0); // unknown109
outapp->WriteUInt8(0); // loh
outapp->WriteUInt8(0); // unknown111
outapp->WriteUInt32(emu->total_effects);
inapp->SetReadPosition(sizeof(AARankInfo_Struct));
for(auto i = 0; i < emu->total_effects; ++i) {
outapp->WriteUInt32(inapp->ReadUInt32()); // skill_id
outapp->WriteUInt32(inapp->ReadUInt32()); // base1
outapp->WriteUInt32(inapp->ReadUInt32()); // base2
outapp->WriteUInt32(inapp->ReadUInt32()); // slot
}
dest->FastQueuePacket(&outapp);
delete inapp;
}
ENCODE(OP_SendCharInfo)

View File

@ -2466,7 +2466,8 @@ namespace RoF2
outapp->WriteUInt32(emu->lastlogin);
outapp->WriteUInt32(emu->timePlayedMin);
outapp->WriteUInt32(emu->timeentitledonaccount);
outapp->WriteUInt32(0x0007ffff); // Expansion bitmask
outapp->WriteUInt32(emu->expansions);
//outapp->WriteUInt32(0x0007ffff); // Expansion bitmask
outapp->WriteUInt32(structs::MAX_PP_LANGUAGE);
@ -2899,7 +2900,7 @@ namespace RoF2
eq->aa_spent = emu->aa_spent;
// These fields may need to be correctly populated at some point
eq->aapoints_assigned = emu->aa_spent + 1;
eq->aapoints_assigned = emu->aa_spent;
eq->aa_spent_general = 0;
eq->aa_spent_archetype = 0;
eq->aa_spent_class = 0;
@ -2935,58 +2936,80 @@ namespace RoF2
ENCODE(OP_SendAATable)
{
ENCODE_LENGTH_ATLEAST(SendAA_Struct);
SETUP_VAR_ENCODE(SendAA_Struct);
ALLOC_VAR_ENCODE(structs::SendAA_Struct, sizeof(structs::SendAA_Struct) + emu->total_abilities*sizeof(structs::AA_Ability));
EQApplicationPacket *inapp = *p;
*p = nullptr;
AARankInfo_Struct *emu = (AARankInfo_Struct*)inapp->pBuffer;
// the structs::SendAA_Struct includes enough space for 1 prereq which is the min even if it has no prereqs
auto prereq_size = emu->total_prereqs > 1 ? (emu->total_prereqs - 1) * 8 : 0;
auto outapp = new EQApplicationPacket(OP_SendAATable, sizeof(structs::SendAA_Struct) + emu->total_effects * sizeof(structs::AA_Ability) + prereq_size);
inapp->SetReadPosition(sizeof(AARankInfo_Struct)+emu->total_effects * sizeof(AARankEffect_Struct));
// Check clientver field to verify this AA should be sent for SoF
// clientver 1 is for all clients and 5 is for Live
if (emu->clientver <= 8)
{
OUT(id);
eq->unknown004 = 1;
//eq->hotkey_sid = (emu->hotkey_sid==4294967295UL)?0:(emu->id - emu->current_level + 1);
//eq->hotkey_sid2 = (emu->hotkey_sid2==4294967295UL)?0:(emu->id - emu->current_level + 1);
//eq->title_sid = emu->id - emu->current_level + 1;
//eq->desc_sid = emu->id - emu->current_level + 1;
eq->hotkey_sid = (emu->hotkey_sid == 4294967295UL) ? -1 : (emu->sof_next_skill);
eq->hotkey_sid2 = (emu->hotkey_sid2 == 4294967295UL) ? -1 : (emu->sof_next_skill);
eq->title_sid = emu->sof_next_skill;
eq->desc_sid = emu->sof_next_skill;
OUT(class_type);
OUT(cost);
OUT(seq);
OUT(current_level);
eq->prereq_skill_count = 1; // min 1
OUT(prereq_skill);
eq->prereq_minpoints_count = 1; // min 1
OUT(prereq_minpoints);
eq->type = emu->sof_type;
OUT(spellid);
eq->unknown057 = 1; // Introduced during HoT
OUT(spell_type);
OUT(spell_refresh);
OUT(classes);
OUT(berserker);
//eq->max_level = emu->sof_max_level;
OUT(max_level);
OUT(last_id);
OUT(next_id);
OUT(cost2);
eq->aa_expansion = emu->aa_expansion;
eq->special_category = emu->special_category;
OUT(total_abilities);
eq->expendable_charges = emu->special_category == 7 ? 1 : 0; // temp hack, this can actually be any number
unsigned int r;
for (r = 0; r < emu->total_abilities; r++) {
OUT(abilities[r].skill_id);
OUT(abilities[r].base1);
OUT(abilities[r].base2);
OUT(abilities[r].slot);
}
std::vector<int32> skill;
std::vector<int32> points;
for(auto i = 0; i < emu->total_prereqs; ++i) {
skill.push_back(inapp->ReadUInt32());
points.push_back(inapp->ReadUInt32());
}
FINISH_ENCODE();
outapp->WriteUInt32(emu->id);
outapp->WriteUInt8(1);
outapp->WriteSInt32(emu->upper_hotkey_sid);
outapp->WriteSInt32(emu->lower_hotkey_sid);
outapp->WriteSInt32(emu->title_sid);
outapp->WriteSInt32(emu->desc_sid);
outapp->WriteSInt32(emu->level_req);
outapp->WriteSInt32(emu->cost);
outapp->WriteUInt32(emu->seq);
outapp->WriteUInt32(emu->current_level);
if (emu->total_prereqs) {
outapp->WriteUInt32(emu->total_prereqs);
for (auto &e : skill)
outapp->WriteSInt32(e);
outapp->WriteUInt32(emu->total_prereqs);
for (auto &e : points)
outapp->WriteSInt32(e);
} else {
outapp->WriteUInt32(1);
outapp->WriteUInt32(0);
outapp->WriteUInt32(1);
outapp->WriteUInt32(0);
}
outapp->WriteSInt32(emu->type);
outapp->WriteSInt32(emu->spell);
outapp->WriteSInt32(1);
outapp->WriteSInt32(emu->spell_type);
outapp->WriteSInt32(emu->spell_refresh);
outapp->WriteSInt32(emu->classes);
outapp->WriteSInt32(emu->max_level);
outapp->WriteSInt32(emu->prev_id);
outapp->WriteSInt32(emu->next_id);
outapp->WriteSInt32(emu->total_cost);
outapp->WriteUInt8(0);
outapp->WriteUInt8(emu->grant_only);
outapp->WriteUInt8(0);
outapp->WriteUInt32(emu->charges);
outapp->WriteSInt32(emu->expansion);
outapp->WriteSInt32(emu->category);
outapp->WriteUInt8(0); // shroud
outapp->WriteUInt8(0); // unknown109
outapp->WriteUInt8(0); // loh
outapp->WriteUInt8(0); // unknown111
outapp->WriteUInt32(emu->total_effects);
inapp->SetReadPosition(sizeof(AARankInfo_Struct));
for(auto i = 0; i < emu->total_effects; ++i) {
outapp->WriteUInt32(inapp->ReadUInt32()); // skill_id
outapp->WriteUInt32(inapp->ReadUInt32()); // base1
outapp->WriteUInt32(inapp->ReadUInt32()); // base2
outapp->WriteUInt32(inapp->ReadUInt32()); // slot
}
dest->FastQueuePacket(&outapp);
delete inapp;
}
ENCODE(OP_SendCharInfo)

View File

@ -4289,7 +4289,7 @@ struct AA_List {
struct AA_Action {
/*00*/ uint32 action;
/*04*/ uint32 ability;
/*08*/ uint32 unknown08;
/*08*/ uint32 target_id;
/*12*/ uint32 exp_value;
/*16*/
};

View File

@ -4288,7 +4288,7 @@ struct AA_List {
struct AA_Action {
/*00*/ uint32 action;
/*04*/ uint32 ability;
/*08*/ uint32 unknown08;
/*08*/ uint32 target_id;
/*12*/ uint32 exp_value;
/*16*/
};

View File

@ -1677,8 +1677,8 @@ namespace SoD
OUT(copper_bank);
OUT(platinum_shared);
// OUT(unknown13156[84]);
//OUT(expansions);
eq->expansions = 16383;
OUT(expansions);
//eq->expansions = 16383;
// OUT(unknown13244[12]);
OUT(autosplit);
// OUT(unknown13260[16]);
@ -1862,55 +1862,56 @@ namespace SoD
ENCODE(OP_SendAATable)
{
ENCODE_LENGTH_ATLEAST(SendAA_Struct);
SETUP_VAR_ENCODE(SendAA_Struct);
ALLOC_VAR_ENCODE(structs::SendAA_Struct, sizeof(structs::SendAA_Struct) + emu->total_abilities*sizeof(structs::AA_Ability));
EQApplicationPacket *inapp = *p;
*p = nullptr;
AARankInfo_Struct *emu = (AARankInfo_Struct*)inapp->pBuffer;
// Check clientver field to verify this AA should be sent for SoF
// clientver 1 is for all clients and 5 is for SoD
if (emu->clientver <= 5)
{
OUT(id);
eq->unknown004 = 1;
//eq->hotkey_sid = (emu->hotkey_sid==4294967295UL)?0:(emu->id - emu->current_level + 1);
//eq->hotkey_sid2 = (emu->hotkey_sid2==4294967295UL)?0:(emu->id - emu->current_level + 1);
//eq->title_sid = emu->id - emu->current_level + 1;
//eq->desc_sid = emu->id - emu->current_level + 1;
eq->hotkey_sid = (emu->hotkey_sid == 4294967295UL) ? 0 : (emu->sof_next_skill);
eq->hotkey_sid2 = (emu->hotkey_sid2 == 4294967295UL) ? 0 : (emu->sof_next_skill);
eq->title_sid = emu->sof_next_skill;
eq->desc_sid = emu->sof_next_skill;
OUT(class_type);
OUT(cost);
OUT(seq);
OUT(current_level);
OUT(prereq_skill);
OUT(prereq_minpoints);
eq->type = emu->sof_type;
OUT(spellid);
OUT(spell_type);
OUT(spell_refresh);
OUT(classes);
OUT(berserker);
//eq->max_level = emu->sof_max_level;
OUT(max_level);
OUT(last_id);
OUT(next_id);
OUT(cost2);
eq->aa_expansion = emu->aa_expansion;
eq->special_category = emu->special_category;
eq->expendable_charges = emu->special_category == 7 ? 1 : 0; // temp hack, this can actually be any number
OUT(total_abilities);
unsigned int r;
for (r = 0; r < emu->total_abilities; r++) {
OUT(abilities[r].skill_id);
OUT(abilities[r].base1);
OUT(abilities[r].base2);
OUT(abilities[r].slot);
}
EQApplicationPacket *outapp = new EQApplicationPacket(OP_SendAATable, sizeof(structs::SendAA_Struct) + emu->total_effects * sizeof(structs::AA_Ability));
structs::SendAA_Struct *eq = (structs::SendAA_Struct*)outapp->pBuffer;
inapp->SetReadPosition(sizeof(AARankInfo_Struct));
outapp->SetWritePosition(sizeof(structs::SendAA_Struct));
eq->id = emu->id;
eq->unknown004 = 1;
eq->id = emu->id;
eq->hotkey_sid = emu->upper_hotkey_sid;
eq->hotkey_sid2 = emu->lower_hotkey_sid;
eq->desc_sid = emu->desc_sid;
eq->title_sid = emu->title_sid;
eq->class_type = emu->level_req;
eq->cost = emu->cost;
eq->seq = emu->seq;
eq->current_level = emu->current_level;
eq->type = emu->type;
eq->spellid = emu->spell;
eq->spell_type = emu->spell_type;
eq->spell_refresh = emu->spell_refresh;
eq->classes = emu->classes;
eq->max_level = emu->max_level;
eq->last_id = emu->prev_id;
eq->next_id = emu->next_id;
eq->cost2 = emu->total_cost;
eq->grant_only = emu->grant_only;
eq->expendable_charges = emu->charges;
eq->aa_expansion = emu->expansion;
eq->special_category = emu->category;
eq->total_abilities = emu->total_effects;
for(auto i = 0; i < eq->total_abilities; ++i) {
eq->abilities[i].skill_id = inapp->ReadUInt32();
eq->abilities[i].base1 = inapp->ReadUInt32();
eq->abilities[i].base2 = inapp->ReadUInt32();
eq->abilities[i].slot = inapp->ReadUInt32();
}
FINISH_ENCODE();
if(emu->total_prereqs > 0) {
eq->prereq_skill = inapp->ReadUInt32();
eq->prereq_minpoints = inapp->ReadUInt32();
}
dest->FastQueuePacket(&outapp);
delete inapp;
}
ENCODE(OP_SendCharInfo)

View File

@ -3813,8 +3813,7 @@ struct SendAA_Struct {
/*0049*/ uint32 spellid;
/*0053*/ uint32 spell_type;
/*0057*/ uint32 spell_refresh;
/*0061*/ uint16 classes;
/*0063*/ uint16 berserker; //seems to be 1 if its a berserker ability
/*0061*/ uint32 classes;
/*0065*/ uint32 max_level;
/*0069*/ uint32 last_id;
/*0073*/ uint32 next_id;
@ -3840,7 +3839,7 @@ struct AA_List {
struct AA_Action {
/*00*/ uint32 action;
/*04*/ uint32 ability;
/*08*/ uint32 unknown08;
/*08*/ uint32 target_id;
/*12*/ uint32 exp_value;
};

View File

@ -1335,8 +1335,8 @@ namespace SoF
OUT(copper_bank);
OUT(platinum_shared);
// OUT(unknown13156[84]);
//OUT(expansions);
eq->expansions = 16383;
OUT(expansions);
//eq->expansions = 16383;
// OUT(unknown13244[12]);
OUT(autosplit);
// OUT(unknown13260[16]);
@ -1520,56 +1520,56 @@ namespace SoF
ENCODE(OP_SendAATable)
{
ENCODE_LENGTH_ATLEAST(SendAA_Struct);
EQApplicationPacket *inapp = *p;
*p = nullptr;
AARankInfo_Struct *emu = (AARankInfo_Struct*)inapp->pBuffer;
SETUP_VAR_ENCODE(SendAA_Struct);
ALLOC_VAR_ENCODE(structs::SendAA_Struct, sizeof(structs::SendAA_Struct) + emu->total_abilities*sizeof(structs::AA_Ability));
EQApplicationPacket *outapp = new EQApplicationPacket(OP_SendAATable, sizeof(structs::SendAA_Struct) + emu->total_effects * sizeof(structs::AA_Ability));
structs::SendAA_Struct *eq = (structs::SendAA_Struct*)outapp->pBuffer;
// Check clientver field to verify this AA should be sent for SoF
// clientver 1 is for all clients and 4 is for SoF
if (emu->clientver <= 4)
{
OUT(id);
eq->unknown004 = 1;
//eq->hotkey_sid = (emu->hotkey_sid==4294967295UL)?0:(emu->id - emu->current_level + 1);
//eq->hotkey_sid2 = (emu->hotkey_sid2==4294967295UL)?0:(emu->id - emu->current_level + 1);
//eq->title_sid = emu->id - emu->current_level + 1;
//eq->desc_sid = emu->id - emu->current_level + 1;
eq->hotkey_sid = (emu->hotkey_sid == 4294967295UL) ? 0 : (emu->sof_next_skill);
eq->hotkey_sid2 = (emu->hotkey_sid2 == 4294967295UL) ? 0 : (emu->sof_next_skill);
eq->title_sid = emu->sof_next_skill;
eq->desc_sid = emu->sof_next_skill;
OUT(class_type);
OUT(cost);
OUT(seq);
OUT(current_level);
OUT(prereq_skill);
OUT(prereq_minpoints);
eq->type = emu->sof_type;
OUT(spellid);
OUT(spell_type);
OUT(spell_refresh);
OUT(classes);
OUT(berserker);
//eq->max_level = emu->sof_max_level;
OUT(max_level);
OUT(last_id);
OUT(next_id);
OUT(cost2);
eq->aa_expansion = emu->aa_expansion;
eq->special_category = emu->special_category;
eq->expendable_charges = emu->special_category == 7 ? 1 : 0; // temp hack, this can actually be any number
OUT(total_abilities);
unsigned int r;
for (r = 0; r < emu->total_abilities; r++) {
OUT(abilities[r].skill_id);
OUT(abilities[r].base1);
OUT(abilities[r].base2);
OUT(abilities[r].slot);
}
inapp->SetReadPosition(sizeof(AARankInfo_Struct));
outapp->SetWritePosition(sizeof(structs::SendAA_Struct));
eq->id = emu->id;
eq->unknown004 = 1;
eq->id = emu->id;
eq->hotkey_sid = emu->upper_hotkey_sid;
eq->hotkey_sid2 = emu->lower_hotkey_sid;
eq->desc_sid = emu->desc_sid;
eq->title_sid = emu->title_sid;
eq->class_type = emu->level_req;
eq->cost = emu->cost;
eq->seq = emu->seq;
eq->current_level = emu->current_level;
eq->type = emu->type;
eq->spellid = emu->spell;
eq->spell_type = emu->spell_type;
eq->spell_refresh = emu->spell_refresh;
eq->classes = emu->classes;
eq->max_level = emu->max_level;
eq->last_id = emu->prev_id;
eq->next_id = emu->next_id;
eq->cost2 = emu->total_cost;
eq->grant_only = emu->grant_only;
eq->expendable_charges = emu->charges;
eq->aa_expansion = emu->expansion;
eq->special_category = emu->category;
eq->total_abilities = emu->total_effects;
for(auto i = 0; i < eq->total_abilities; ++i) {
eq->abilities[i].skill_id = inapp->ReadUInt32();
eq->abilities[i].base1 = inapp->ReadUInt32();
eq->abilities[i].base2 = inapp->ReadUInt32();
eq->abilities[i].slot = inapp->ReadUInt32();
}
FINISH_ENCODE();
if(emu->total_prereqs > 0) {
eq->prereq_skill = inapp->ReadUInt32();
eq->prereq_minpoints = inapp->ReadUInt32();
}
dest->FastQueuePacket(&outapp);
delete inapp;
}
ENCODE(OP_SendCharInfo)
@ -1665,8 +1665,6 @@ namespace SoF
emu_ptr += sizeof(CharacterSelectEntry_Struct);
eq_ptr += sizeof(structs::CharacterSelectEntry_Struct);
}
FINISH_ENCODE();
}
//hack hack hack

View File

@ -3677,8 +3677,7 @@ struct SendAA_Struct {
/*0049*/ uint32 spellid;
/*0053*/ uint32 spell_type;
/*0057*/ uint32 spell_refresh;
/*0061*/ uint16 classes;
/*0063*/ uint16 berserker; //seems to be 1 if its a berserker ability
/*0061*/ uint32 classes;
/*0065*/ uint32 max_level;
/*0069*/ uint32 last_id;
/*0073*/ uint32 next_id;
@ -3702,7 +3701,7 @@ struct AA_List {
struct AA_Action {
/*00*/ uint32 action;
/*04*/ uint32 ability;
/*08*/ uint32 unknown08;
/*08*/ uint32 target_id;
/*12*/ uint32 exp_value;
};

View File

@ -1107,50 +1107,52 @@ namespace Titanium
ENCODE(OP_SendAATable)
{
ENCODE_LENGTH_ATLEAST(SendAA_Struct);
EQApplicationPacket *inapp = *p;
*p = nullptr;
AARankInfo_Struct *emu = (AARankInfo_Struct*)inapp->pBuffer;
SETUP_VAR_ENCODE(SendAA_Struct);
ALLOC_VAR_ENCODE(structs::SendAA_Struct, sizeof(structs::SendAA_Struct) + emu->total_abilities*sizeof(structs::AA_Ability));
EQApplicationPacket *outapp = new EQApplicationPacket(OP_SendAATable, sizeof(structs::SendAA_Struct) + emu->total_effects * sizeof(structs::AA_Ability));
structs::SendAA_Struct *eq = (structs::SendAA_Struct*)outapp->pBuffer;
// Check clientver field to verify this AA should be sent for Titanium
// clientver 1 is for all clients and 3 is for Titanium
if (emu->clientver <= 3)
{
OUT(id);
eq->unknown004 = 1;
eq->hotkey_sid = (emu->hotkey_sid == 4294967295UL) ? 0 : (emu->id - emu->current_level + 1);
eq->hotkey_sid2 = (emu->hotkey_sid2 == 4294967295UL) ? 0 : (emu->id - emu->current_level + 1);
eq->title_sid = emu->id - emu->current_level + 1;
eq->desc_sid = emu->id - emu->current_level + 1;
OUT(class_type);
OUT(cost);
OUT(seq);
OUT(current_level);
OUT(prereq_skill);
OUT(prereq_minpoints);
OUT(type);
OUT(spellid);
OUT(spell_type);
OUT(spell_refresh);
OUT(classes);
OUT(berserker);
OUT(max_level);
OUT(last_id);
OUT(next_id);
OUT(cost2);
OUT(unknown80[0]);
OUT(unknown80[1]);
OUT(total_abilities);
unsigned int r;
for (r = 0; r < emu->total_abilities; r++) {
OUT(abilities[r].skill_id);
OUT(abilities[r].base1);
OUT(abilities[r].base2);
OUT(abilities[r].slot);
}
inapp->SetReadPosition(sizeof(AARankInfo_Struct));
outapp->SetWritePosition(sizeof(structs::SendAA_Struct));
eq->id = emu->id;
eq->unknown004 = 1;
eq->id = emu->id;
eq->hotkey_sid = emu->upper_hotkey_sid;
eq->hotkey_sid2 = emu->lower_hotkey_sid;
eq->desc_sid = emu->desc_sid;
eq->title_sid = emu->title_sid;
eq->class_type = emu->level_req;
eq->cost = emu->cost;
eq->seq = emu->seq;
eq->current_level = emu->current_level;
eq->type = emu->type;
eq->spellid = emu->spell;
eq->spell_type = emu->spell_type;
eq->spell_refresh = emu->spell_refresh;
eq->classes = emu->classes;
eq->max_level = emu->max_level;
eq->last_id = emu->prev_id;
eq->next_id = emu->next_id;
eq->cost2 = emu->total_cost;
eq->total_abilities = emu->total_effects;
for(auto i = 0; i < eq->total_abilities; ++i) {
eq->abilities[i].skill_id = inapp->ReadUInt32();
eq->abilities[i].base1 = inapp->ReadUInt32();
eq->abilities[i].base2 = inapp->ReadUInt32();
eq->abilities[i].slot = inapp->ReadUInt32();
}
FINISH_ENCODE();
if(emu->total_prereqs > 0) {
eq->prereq_skill = inapp->ReadUInt32();
eq->prereq_minpoints = inapp->ReadUInt32();
}
dest->FastQueuePacket(&outapp);
delete inapp;
}
ENCODE(OP_SendCharInfo)

View File

@ -3157,8 +3157,7 @@ struct SendAA_Struct {
/*0052*/ uint32 spellid;
/*0056*/ uint32 spell_type;
/*0060*/ uint32 spell_refresh;
/*0064*/ uint16 classes;
/*0066*/ uint16 berserker; //seems to be 1 if its a berserker ability
/*0064*/ uint32 classes;
/*0068*/ uint32 max_level;
/*0072*/ uint32 last_id;
/*0076*/ uint32 next_id;
@ -3175,7 +3174,7 @@ struct AA_List {
struct AA_Action {
/*00*/ uint32 action;
/*04*/ uint32 ability;
/*08*/ uint32 unknown08;
/*08*/ uint32 target_id;
/*12*/ uint32 exp_value;
};

View File

@ -1805,11 +1805,13 @@ namespace UF
//NOTE: new client supports 300 AAs, our internal rep/PP
//only supports 240..
for (r = 0; r < MAX_PP_AA_ARRAY; r++) {
OUT(aa_array[r].AA);
OUT(aa_array[r].value);
OUT(aa_array[r].charges);
eq->aa_array[r].AA = emu->aa_array[r].AA;
eq->aa_array[r].value = emu->aa_array[r].value;
eq->aa_array[r].charges = emu->aa_array[r].charges;
}
// OUT(unknown02220[4]);
OUT(mana);
OUT(cur_hp);
OUT(STR);
@ -1939,8 +1941,8 @@ namespace UF
OUT(copper_bank);
OUT(platinum_shared);
// OUT(unknown13156[84]);
//OUT(expansions);
eq->expansions = 0xffff;
OUT(expansions);
//eq->expansions = 0x1ffff;
// OUT(unknown13244[12]);
OUT(autosplit);
// OUT(unknown13260[16]);
@ -2128,7 +2130,7 @@ namespace UF
eq->aa_spent = emu->aa_spent;
eq->aa_assigned = emu->aa_spent;
eq->aa_spent3 = emu->aa_spent;
eq->aa_spent3 = 0;
eq->unknown012 = 0;
eq->unknown016 = 0;
eq->unknown020 = 0;
@ -2145,55 +2147,56 @@ namespace UF
ENCODE(OP_SendAATable)
{
ENCODE_LENGTH_ATLEAST(SendAA_Struct);
SETUP_VAR_ENCODE(SendAA_Struct);
ALLOC_VAR_ENCODE(structs::SendAA_Struct, sizeof(structs::SendAA_Struct) + emu->total_abilities*sizeof(structs::AA_Ability));
EQApplicationPacket *inapp = *p;
*p = nullptr;
AARankInfo_Struct *emu = (AARankInfo_Struct*)inapp->pBuffer;
// Check clientver field to verify this AA should be sent for SoF
// clientver 1 is for all clients and 6 is for Underfoot
if (emu->clientver <= 6)
{
OUT(id);
eq->unknown004 = 1;
//eq->hotkey_sid = (emu->hotkey_sid==4294967295UL)?0:(emu->id - emu->current_level + 1);
//eq->hotkey_sid2 = (emu->hotkey_sid2==4294967295UL)?0:(emu->id - emu->current_level + 1);
//eq->title_sid = emu->id - emu->current_level + 1;
//eq->desc_sid = emu->id - emu->current_level + 1;
eq->hotkey_sid = (emu->hotkey_sid == 4294967295UL) ? 0 : (emu->sof_next_skill);
eq->hotkey_sid2 = (emu->hotkey_sid2 == 4294967295UL) ? 0 : (emu->sof_next_skill);
eq->title_sid = emu->sof_next_skill;
eq->desc_sid = emu->sof_next_skill;
OUT(class_type);
OUT(cost);
OUT(seq);
OUT(current_level);
OUT(prereq_skill);
OUT(prereq_minpoints);
eq->type = emu->sof_type;
OUT(spellid);
OUT(spell_type);
OUT(spell_refresh);
OUT(classes);
OUT(berserker);
//eq->max_level = emu->sof_max_level;
OUT(max_level);
OUT(last_id);
OUT(next_id);
OUT(cost2);
eq->aa_expansion = emu->aa_expansion;
eq->special_category = emu->special_category;
eq->expendable_charges = emu->special_category == 7 ? 1 : 0; // temp hack, this can actually be any number
OUT(total_abilities);
unsigned int r;
for (r = 0; r < emu->total_abilities; r++) {
OUT(abilities[r].skill_id);
OUT(abilities[r].base1);
OUT(abilities[r].base2);
OUT(abilities[r].slot);
}
EQApplicationPacket *outapp = new EQApplicationPacket(OP_SendAATable, sizeof(structs::SendAA_Struct) + emu->total_effects * sizeof(structs::AA_Ability));
structs::SendAA_Struct *eq = (structs::SendAA_Struct*)outapp->pBuffer;
inapp->SetReadPosition(sizeof(AARankInfo_Struct));
outapp->SetWritePosition(sizeof(structs::SendAA_Struct));
eq->id = emu->id;
eq->unknown004 = 1;
eq->id = emu->id;
eq->hotkey_sid = emu->upper_hotkey_sid;
eq->hotkey_sid2 = emu->lower_hotkey_sid;
eq->desc_sid = emu->desc_sid;
eq->title_sid = emu->title_sid;
eq->class_type = emu->level_req;
eq->cost = emu->cost;
eq->seq = emu->seq;
eq->current_level = emu->current_level;
eq->type = emu->type;
eq->spellid = emu->spell;
eq->spell_type = emu->spell_type;
eq->spell_refresh = emu->spell_refresh;
eq->classes = emu->classes;
eq->max_level = emu->max_level;
eq->last_id = emu->prev_id;
eq->next_id = emu->next_id;
eq->cost2 = emu->total_cost;
eq->grant_only = emu->grant_only;
eq->expendable_charges = emu->charges;
eq->aa_expansion = emu->expansion;
eq->special_category = emu->category;
eq->total_abilities = emu->total_effects;
for(auto i = 0; i < eq->total_abilities; ++i) {
eq->abilities[i].skill_id = inapp->ReadUInt32();
eq->abilities[i].base1 = inapp->ReadUInt32();
eq->abilities[i].base2 = inapp->ReadUInt32();
eq->abilities[i].slot = inapp->ReadUInt32();
}
FINISH_ENCODE();
if(emu->total_prereqs > 0) {
eq->prereq_skill = inapp->ReadUInt32();
eq->prereq_minpoints = inapp->ReadUInt32();
}
dest->FastQueuePacket(&outapp);
delete inapp;
}
ENCODE(OP_SendCharInfo)

View File

@ -3886,8 +3886,7 @@ struct SendAA_Struct {
/*0049*/ uint32 spellid;
/*0053*/ uint32 spell_type;
/*0057*/ uint32 spell_refresh;
/*0061*/ uint16 classes;
/*0063*/ uint16 berserker; //seems to be 1 if its a berserker ability
/*0061*/ uint32 classes;
/*0065*/ uint32 max_level;
/*0069*/ uint32 last_id;
/*0073*/ uint32 next_id;
@ -3913,7 +3912,7 @@ struct AA_List {
struct AA_Action {
/*00*/ uint32 action;
/*04*/ uint32 ability;
/*08*/ uint32 unknown08;
/*08*/ uint32 target_id;
/*12*/ uint32 exp_value;
};

View File

@ -139,8 +139,4 @@ protected:
std::map<pTimerType, PersistentTimer *> _list;
};
//code prettying macros
#define AA_Choose3(val, v1, v2, v3) (val==1?v1:(val==2?v2:v3))
#define AA_Choose5(val, v1, v2, v3, v4, v5) (val==1?v1:(val==2?v2:(val==3?v3:(val==4?v4:v5))))
#endif

View File

@ -99,6 +99,7 @@ RULE_BOOL(Character, EnableXTargetting, true) // Enable Extended Targetting Wind
RULE_BOOL(Character, KeepLevelOverMax, false) // Don't delevel a character that has somehow gone over the level cap
RULE_INT(Character, FoodLossPerUpdate, 35) // How much food/water you lose per stamina update
RULE_INT(Character, BaseInstrumentSoftCap, 36) // Softcap for instrument mods, 36 commonly referred to as "3.6" as well.
RULE_BOOL(Character, UseSpellFileSongCap, true) // When they removed the AA that increased the cap they removed the above and just use the spell field
RULE_INT(Character, BaseRunSpeedCap, 158) // Base Run Speed Cap, on live it's 158% which will give you a runspeed of 1.580 hard capped to 225.
RULE_INT(Character, OrnamentationAugmentType, 20) //Ornamentation Augment Type
RULE_REAL(Character, EnvironmentDamageMulipliter, 1)
@ -113,6 +114,8 @@ RULE_INT(Character, TradeskillUpMakePoison, 2) // Make Poison skillup rate adjus
RULE_INT(Character, TradeskillUpPottery, 4) // Pottery skillup rate adjust. Lower is faster.
RULE_INT(Character, TradeskillUpResearch, 1) // Research skillup rate adjust. Lower is faster.
RULE_INT(Character, TradeskillUpTinkering, 2) // Tinkering skillup rate adjust. Lower is faster.
RULE_BOOL(Character, SpamHPUpdates, false) // if your server has stupid amounts of HP that causes client display issues, turn this on!
RULE_BOOL(Character, MarqueeHPUpdates, false) // Will show Health % in center of screen < 100%
RULE_CATEGORY_END()
RULE_CATEGORY(Mercs)
@ -183,6 +186,7 @@ RULE_INT(World, MinGMAntiHackStatus, 1) //Minimum GM status to check against Ant
RULE_INT(World, SoFStartZoneID, -1) //Sets the Starting Zone for SoF Clients separate from Titanium Clients (-1 is disabled)
RULE_INT(World, TitaniumStartZoneID, -1) //Sets the Starting Zone for Titanium Clients (-1 is disabled). Replaces the old method.
RULE_INT(World, ExpansionSettings, 16383) // Sets the expansion settings for the server, This is sent on login to world and affects client expansion settings. Defaults to all expansions enabled up to TSS.
RULE_BOOL(World, UseClientBasedExpansionSettings, true) // if true it will overrule World, ExpansionSettings and set someone's expansion based on the client they're using
RULE_INT(World, PVPSettings, 0) // Sets the PVP settings for the server, 1 = Rallos Zek RuleSet, 2 = Tallon/Vallon Zek Ruleset, 4 = Sullon Zek Ruleset, 6 = Discord Ruleset, anything above 6 is the Discord Ruleset without the no-drop restrictions removed. TODO: Edit IsAttackAllowed in Zone to accomodate for these rules.
RULE_BOOL (World, IsGMPetitionWindowEnabled, false)
RULE_INT (World, FVNoDropFlag, 0) // Sets the Firiona Vie settings on the client. If set to 2, the flag will be set for GMs only, allowing trading of no-drop items.
@ -444,6 +448,7 @@ RULE_BOOL(Combat, OneProcPerWeapon, true) //If enabled, One proc per weapon per
RULE_BOOL(Combat, ProjectileDmgOnImpact, true) //If enabled, projectiles (ie arrows) will hit on impact, instead of instantly.
RULE_BOOL(Combat, MeleePush, true) // enable melee push
RULE_INT(Combat, MeleePushChance, 50) // (NPCs) chance the target will be pushed. Made up, 100 actually isn't that bad
RULE_BOOL(Combat, UseLiveCombatRounds, true) // turn this false if you don't want to worry about fixing up combat rounds for NPCs
RULE_CATEGORY_END()
RULE_CATEGORY(NPC)
@ -501,6 +506,8 @@ RULE_INT(Bots, BotAAExpansion, 8) // Bots get AAs through this expansion
RULE_BOOL(Bots, BotGroupXP, false) // Determines whether client gets xp for bots outside their group.
RULE_BOOL(Bots, BotBardUseOutOfCombatSongs, true) // Determines whether bard bots use additional out of combat songs.
RULE_BOOL(Bots, BotLevelsWithOwner, false) // Auto-updates spawned bots as owner levels/de-levels (false is original behavior)
RULE_BOOL(Bots, BotCharacterLevelEnabled, false) // Enables required level to spawn bots
RULE_INT(Bots, BotCharacterLevel, 0) // 0 as default (if level > this value you can spawn bots if BotCharacterLevelEnabled is true)
RULE_CATEGORY_END()
#endif

View File

@ -761,6 +761,7 @@ typedef enum {
struct LauncherZoneRequest {
uint8 command;
char short_name[33];
uint16 port;
};
struct LauncherZoneStatus {

View File

@ -1662,6 +1662,7 @@ void SharedDatabase::LoadSpells(void *data, int max_spells) {
sp[tempid].not_extendable = atoi(row[197]) != 0;
sp[tempid].suspendable = atoi(row[200]) != 0;
sp[tempid].viral_range = atoi(row[201]);
sp[tempid].songcap = atoi(row[202]);
sp[tempid].no_block = atoi(row[205]);
sp[tempid].spellgroup=atoi(row[207]);
sp[tempid].rank = atoi(row[208]);
@ -1992,7 +1993,7 @@ const LootDrop_Struct* SharedDatabase::GetLootDrop(uint32 lootdrop_id) {
void SharedDatabase::LoadCharacterInspectMessage(uint32 character_id, InspectMessage_Struct* message) {
std::string query = StringFormat("SELECT `inspect_message` FROM `character_inspect_messages` WHERE `id` = %u LIMIT 1", character_id);
auto results = QueryDatabase(query);
auto results = QueryDatabase(query);
auto row = results.begin();
memset(message, '\0', sizeof(InspectMessage_Struct));
for (auto row = results.begin(); row != results.end(); ++row) {
@ -2002,7 +2003,7 @@ void SharedDatabase::LoadCharacterInspectMessage(uint32 character_id, InspectMes
void SharedDatabase::SaveCharacterInspectMessage(uint32 character_id, const InspectMessage_Struct* message) {
std::string query = StringFormat("REPLACE INTO `character_inspect_messages` (id, inspect_message) VALUES (%u, '%s')", character_id, EscapeString(message->text).c_str());
auto results = QueryDatabase(query);
auto results = QueryDatabase(query);
}
void SharedDatabase::GetBotInspectMessage(uint32 botid, InspectMessage_Struct* message) {

View File

@ -108,16 +108,17 @@ enum SkillUseTypes
/*13869*/ SkillBerserking,
/*13902*/ SkillTaunt,
/*05837*/ SkillFrenzy, // This appears to be the only listed one not grouped with the others
/*00000*/ _EmuSkillCount // move to last position of active enumeration labels
// SoF+ specific skills
// /*03670*/ SkillRemoveTraps,
// /*13049*/ SkillTripleAttack,
/*03670*/ SkillRemoveTraps,
/*13049*/ SkillTripleAttack,
// RoF2+ specific skills
// /*00789*/ Skill2HPiercing,
// /*01216*/ SkillNone, // This needs to move down as new skills are added
/*00000*/ _EmuSkillCount // move to last position of active enumeration labels
// Skill Counts
// /*-----*/ _SkillCount_62 = 75, // use for Ti and earlier max skill checks
// /*-----*/ _SkillCount_SoF = 77, // use for SoF thru RoF1 max skill checks
@ -170,7 +171,7 @@ enum SkillUseTypes
};
// temporary until it can be sorted out...
#define HIGHEST_SKILL SkillFrenzy
#define HIGHEST_SKILL SkillTripleAttack
// Spell Effects use this value to determine if an effect applies to all skills.
#define ALL_SKILLS -1

View File

@ -447,7 +447,7 @@ bool IsTGBCompatibleSpell(uint16 spell_id)
bool IsBardSong(uint16 spell_id)
{
if (IsValidSpell(spell_id) && spells[spell_id].classes[BARD - 1] < 127 && !spells[spell_id].IsDisciplineBuff)
if (IsValidSpell(spell_id) && spells[spell_id].classes[BARD - 1] < 255 && !spells[spell_id].IsDisciplineBuff)
return true;
return false;

View File

@ -381,47 +381,47 @@ typedef enum {
#define SE_GiveDoubleAttack 225 // implemented[AA] - Allow any class to double attack with set chance.
#define SE_TwoHandBash 226 // *not implemented as bonus
#define SE_ReduceSkillTimer 227 // implemented
//#define SE_ReduceFallDamage 228 // not implented as bonus - reduce the damage that you take from falling
#define SE_ReduceFallDamage 228 // not implented as bonus - reduce the damage that you take from falling
#define SE_PersistantCasting 229 // implemented
//#define SE_ExtendedShielding 230 // not used as bonus - increase range of /shield ability
#define SE_ExtendedShielding 230 // not used as bonus - increase range of /shield ability
#define SE_StunBashChance 231 // implemented - increase chance to stun from bash.
#define SE_DivineSave 232 // implemented (base1 == % chance on death to insta-res) (base2 == spell cast on save)
#define SE_Metabolism 233 // implemented - Modifies food/drink consumption rates.
//#define SE_ReduceApplyPoisonTime 234 // not implemented as bonus - reduces the time to apply poison
#define SE_ReduceApplyPoisonTime 234 // not implemented as bonus - reduces the time to apply poison
#define SE_ChannelChanceSpells 235 // implemented[AA] - chance to channel from SPELLS *No longer used on live.
//#define SE_FreePet 236 // not used
#define SE_GivePetGroupTarget 237 // implemented[AA] - (Pet Affinity)
#define SE_IllusionPersistence 238 // implemented - lends persistence to your illusionary disguises, causing them to last until you die or the illusion is forcibly removed.
//#define SE_FeignedCastOnChance 239 // *not implemented as bonus - ability gives you an increasing chance for your feigned deaths to not be revealed by spells cast upon you.
#define SE_FeignedCastOnChance 239 // *not implemented as bonus - ability gives you an increasing chance for your feigned deaths to not be revealed by spells cast upon you.
//#define SE_StringUnbreakable 240 // not used [Likely related to above - you become immune to feign breaking on a resisted spell and have a good chance of feigning through a spell that successfully lands upon you.]
#define SE_ImprovedReclaimEnergy 241 // implemented - increase the amount of mana returned to you when reclaiming your pet.
#define SE_IncreaseChanceMemwipe 242 // implemented - increases the chance to wipe hate with memory blurr
#define SE_CharmBreakChance 243 // implemented - Total Domination
#define SE_RootBreakChance 244 // implemented[AA] reduce the chance that your root will break.
//#define SE_TrapCircumvention 245 // *not implemented[AA] - decreases the chance that you will set off a trap when opening a chest
#define SE_TrapCircumvention 245 // *not implemented[AA] - decreases the chance that you will set off a trap when opening a chest
#define SE_SetBreathLevel 246 // *not implemented as bonus
#define SE_RaiseSkillCap 247 // *not implemented[AA] - adds skill over the skill cap.
//#define SE_SecondaryForte 248 // not implemented as bonus(gives you a 2nd specialize skill that can go past 50 to 100)
#define SE_SecondaryForte 248 // not implemented as bonus(gives you a 2nd specialize skill that can go past 50 to 100)
#define SE_SecondaryDmgInc 249 // implemented[AA] Allows off hand weapon to recieve a damage bonus (Sinister Strikes)
#define SE_SpellProcChance 250 // implemented - Increase chance to proc from melee proc spells (ie Spirit of Panther)
#define SE_ConsumeProjectile 251 // implemented[AA] - chance to not consume an arrow (ConsumeProjectile = 100)
#define SE_FrontalBackstabChance 252 // implemented[AA] - chance to perform a full damage backstab from front.
#define SE_FrontalBackstabMinDmg 253 // implemented[AA] - allow a frontal backstab for mininum damage.
#define SE_Blank 254 // implemented
//#define SE_ShieldDuration 255 // not implemented as bonus - increases duration of /shield
//#define SE_ShroudofStealth 256 // not implemented as bonus - rogue improved invs
//#define SE_PetDiscipline 257 // not implemented as bonus - /pet hold
#define SE_ShieldDuration 255 // not implemented as bonus - increases duration of /shield
#define SE_ShroudofStealth 256 // not implemented as bonus - rogue improved invs
#define SE_PetDiscipline 257 // not implemented as bonus - /pet hold
#define SE_TripleBackstab 258 // implemented[AA] - chance to perform a triple backstab
#define SE_CombatStability 259 // implemented[AA] - damage mitigation
#define SE_AddSingingMod 260 // implemented[AA] - Instrument/Singing Mastery, base1 is the mod, base2 is the ItemType
#define SE_SongModCap 261 // implemented[AA] - Song Mod cap increase (no longer used on live)
#define SE_RaiseStatCap 262 // implemented
//#define SE_TradeSkillMastery 263 // not implemented - lets you raise more than one tradeskill above master.
//#define SE_HastenedAASkill 264 // not implemented as bonus - Use redux field in aa_actions table for this effect
#define SE_TradeSkillMastery 263 // not implemented - lets you raise more than one tradeskill above master.
#define SE_HastenedAASkill 264 // implemented
#define SE_MasteryofPast 265 // implemented[AA] - Spells less than effect values level can not be fizzled
#define SE_ExtraAttackChance 266 // implemented - increase chance to score an extra attack with a 2-Handed Weapon.
#define SE_PetDiscipline2 267 // *not implemented - /pet focus, /pet no cast
//#define SE_ReduceTradeskillFail 268 // *not implemented? - reduces chance to fail with given tradeskill by a percent chance
#define SE_ReduceTradeskillFail 268 // *not implemented? - reduces chance to fail with given tradeskill by a percent chance
#define SE_MaxBindWound 269 // implemented[AA] - Increase max HP you can bind wound.
#define SE_BardSongRange 270 // implemented[AA] - increase range of beneficial bard songs (Sionachie's Crescendo)
#define SE_BaseMovementSpeed 271 // implemented[AA] - mods basemove speed, doesn't stack with other move mods
@ -434,14 +434,14 @@ typedef enum {
#define SE_FinishingBlow 278 // implemented[AA] - chance to do massive damage under 10% HP (base1 = chance, base2 = damage)
#define SE_Flurry 279 // implemented
#define SE_PetFlurry 280 // implemented[AA]
//#define SE_FeignedMinion 281 // *not implemented[AA] ability allows you to instruct your pet to feign death via the '/pet feign' command. value = succeed chance
#define SE_FeignedMinion 281 // *not implemented[AA] ability allows you to instruct your pet to feign death via the '/pet feign' command. value = succeed chance
#define SE_ImprovedBindWound 282 // implemented[AA] - increase bind wound amount by percent.
#define SE_DoubleSpecialAttack 283 // implemented[AA] - Chance to perform second special attack as monk
//#define SE_LoHSetHeal 284 // not used
//#define SE_NimbleEvasion 285 // *not implemented - base1 = 100 for max
#define SE_NimbleEvasion 285 // *not implemented - base1 = 100 for max
#define SE_FcDamageAmt 286 // implemented - adds direct spell damage
#define SE_SpellDurationIncByTic 287 // implemented
#define SE_SpecialAttackKBProc 288 // implemented[AA] - Chance to to do a knockback from special attacks [AA Dragon Punch].
#define SE_SkillAttackProc 288 // implemented[AA] - Chance to proc spell on skill attack usage (ex. Dragon Punch)
#define SE_CastOnFadeEffect 289 // implemented - Triggers only if fades after natural duration.
#define SE_IncreaseRunSpeedCap 290 // implemented[AA] - increases run speed over the hard cap
#define SE_Purify 291 // implemented - Removes determental effects
@ -515,8 +515,8 @@ typedef enum {
//#define SE_PassiveSenseTrap 359 // *not implemented - Invulnerability (Brell's Blessing)
#define SE_ProcOnKillShot 360 // implemented - a buff that has a base1 % to cast spell base2 when you kill a "challenging foe" base3 min level
#define SE_SpellOnDeath 361 // implemented - casts spell on death of buffed
//#define SE_PotionBeltSlots 362 // *not implemented[AA] 'Quick Draw' expands the potion belt by one additional available item slot per rank.
//#define SE_BandolierSlots 363 // *not implemented[AA] 'Battle Ready' expands the bandolier by one additional save slot per rank.
#define SE_PotionBeltSlots 362 // *not implemented[AA] 'Quick Draw' expands the potion belt by one additional available item slot per rank.
#define SE_BandolierSlots 363 // *not implemented[AA] 'Battle Ready' expands the bandolier by one additional save slot per rank.
#define SE_TripleAttackChance 364 // implemented
#define SE_ProcOnSpellKillShot 365 // implemented - chance to trigger a spell on kill when the kill is caused by a specific spell with this effect in it (10470 Venin)
#define SE_ShieldEquipDmgMod 366 // implemented[AA] Damage modifier to melee if shield equiped. (base1 = dmg mod , base2 = ?) ie Shield Specialist AA
@ -525,7 +525,7 @@ typedef enum {
#define SE_CorruptionCounter 369 // implemented
#define SE_ResistCorruption 370 // implemented
#define SE_AttackSpeed4 371 // implemented - stackable slow effect 'Inhibit Melee'
//#define SE_ForageSkill 372 // *not implemented[AA] Will increase the skill cap for those that have the Forage skill and grant the skill and raise the cap to those that do not.
#define SE_ForageSkill 372 // *not implemented[AA] Will increase the skill cap for those that have the Forage skill and grant the skill and raise the cap to those that do not.
#define SE_CastOnFadeEffectAlways 373 // implemented - Triggers if fades after natural duration OR from rune/numhits fades.
#define SE_ApplyEffect 374 // implemented
#define SE_DotCritDmgIncrease 375 // implemented - Increase damage of DoT critical amount
@ -544,7 +544,7 @@ typedef enum {
//#define SE_SummonCorpseZone 388 // *not implemented - summons a corpse from any zone(nec AA)
#define SE_FcTimerRefresh 389 // implemented - Refresh spell icons
//#define SE_FcTimerLockout 390 // *not implemented - Sets recast timers to specific value, focus limited.
#define SE_MeleeVulnerability 391 // implemented [Live SPA has this as LimitManaMax however that is clearly not the effect used]
#define SE_LimitManaMax 391 // implemented
#define SE_FcHealAmt 392 // implemented - Adds or removes healing from spells
#define SE_FcHealPctIncoming 393 // implemented - HealRate with focus restrictions.
#define SE_FcHealAmtIncoming 394 // implemented - Adds/Removes amount of healing on target by X value with foucs restrictions.
@ -579,7 +579,7 @@ typedef enum {
#define SE_LimitUseType 423 // implemented - limit a focus to require a certain numhits type
#define SE_GravityEffect 424 // implemented - Pulls/pushes you toward/away the mob at a set pace
//#define SE_Display 425 // *not implemented - Illusion: Flying Dragon(21626)
//#define SE_IncreaseExtTargetWindow 426 // *not implmented[AA] - increases the capacity of your extended target window
#define SE_IncreaseExtTargetWindow 426 // *not implmented[AA] - increases the capacity of your extended target window
#define SE_SkillProc 427 // implemented - chance to proc when using a skill(ie taunt)
#define SE_LimitToSkill 428 // implemented - limits what skills will effect a skill proc
#define SE_SkillProcSuccess 429 // implemented - chance to proc when tje skill in use successfully fires.
@ -735,8 +735,8 @@ struct SPDat_Spell_Struct
/* 198- 199 */
/* 200 */ bool suspendable; // buff is suspended in suspended buff zones
/* 201 */ int viral_range;
/* 202 */
/* 203 */ //int songcap; // individual song cap (how live currently does it, not implemented)
/* 202 */ int songcap; // individual song cap
/* 203 */
/* 204 */
/* 205 */ bool no_block;
/* 206 */

View File

@ -30,7 +30,7 @@
Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
*/
#define CURRENT_BINARY_DATABASE_VERSION 9083
#define CURRENT_BINARY_DATABASE_VERSION 9086
#define COMPILE_DATE __DATE__
#define COMPILE_TIME __TIME__
#ifndef WIN32

View File

@ -102,8 +102,6 @@ int main(int argc, char *argv[]) {
Log.Out(Logs::Detail, Logs::Launcher, "Starting main loop...");
// zones["test"] = new ZoneLaunch(&world, "./zone", "dynamic_1");
ProcLauncher *launch = ProcLauncher::get();
RunLoops = true;
while(RunLoops) {

View File

@ -86,14 +86,13 @@ void WorldServer::Process() {
}
const LauncherZoneRequest *lzr = (const LauncherZoneRequest *) pack->pBuffer;
switch(ZoneRequestCommands(lzr->command)) {
case ZR_Start: {
if(m_zones.find(lzr->short_name) != m_zones.end()) {
Log.Out(Logs::Detail, Logs::Launcher, "World told us to start zone %s, but it is already running.", lzr->short_name);
} else {
Log.Out(Logs::Detail, Logs::Launcher, "World told us to start zone %s.", lzr->short_name);
ZoneLaunch *l = new ZoneLaunch(this, m_name, lzr->short_name, m_config);
ZoneLaunch *l = new ZoneLaunch(this, m_name, lzr->short_name, lzr->port, m_config);
m_zones[lzr->short_name] = l;
}
break;

View File

@ -34,10 +34,11 @@ void ZoneLaunch::InitStartTimer() {
}
ZoneLaunch::ZoneLaunch(WorldServer *world, const char *launcher_name,
const char *zone_name, const EQEmuConfig *config)
const char *zone_name, uint16 port, const EQEmuConfig *config)
: m_state(StateStartPending),
m_world(world),
m_zone(zone_name),
m_port(port),
m_launcherName(launcher_name),
m_config(config),
m_timer(config->RestartWait),
@ -61,10 +62,14 @@ void ZoneLaunch::SendStatus() const {
void ZoneLaunch::Start() {
ProcLauncher::Spec *spec = new ProcLauncher::Spec();
spec->program = m_config->ZoneExe;
// if(m_zone.substr(0,7) == "dynamic")
// spec->args.push_back(".");
// else
spec->args.push_back(m_zone);
if(m_port) {
std::string arg = m_zone + std::string(":") + std::to_string(m_port);
spec->args.push_back(arg);
} else {
spec->args.push_back(m_zone);
}
spec->args.push_back(m_launcherName);
spec->handler = this;
spec->logFile = m_config->LogPrefix + m_zone + m_config->LogSuffix;

View File

@ -28,7 +28,7 @@ class EQEmuConfig;
class ZoneLaunch : protected ProcLauncher::EventHandler {
public:
ZoneLaunch(WorldServer *world, const char *launcher_name,
const char *zone_name, const EQEmuConfig *config);
const char *zone_name, uint16 port, const EQEmuConfig *config);
virtual ~ZoneLaunch();
void Stop(bool graceful = true);
@ -63,6 +63,7 @@ protected:
const std::string m_zone;
const char *const m_launcherName;
const EQEmuConfig *const m_config;
const uint16 m_port;
Timer m_timer;
ProcLauncher::ProcRef m_ref;

View File

@ -353,6 +353,7 @@ OP_OpenContainer=0x0000
OP_Marquee=0x502e
OP_ItemRecastDelay=0x15a9
#OP_OpenInventory=0x0000 # Likely does not exist in RoF -U
OP_ResetAA=0x1669
# Expeditions
OP_DzAddPlayer=0x4701

View File

@ -337,6 +337,9 @@
9081|2015_05_23_dbstr_us.sql|SHOW TABLES LIKE 'db_str'|empty|
9082|2015_05_25_npc_types_texture_fields.sql|SHOW COLUMNS FROM `npc_types` LIKE 'armtexture'|empty|
9083|2015_06_07_aa_update.sql|SHOW COLUMNS FROM `character_alternate_abilities` LIKE 'charges'|empty|
9084|2015_06_30_runspeed_adjustments.sql|SELECT `runspeed` FROM `npc_types` WHERE `runspeed` > 3|not_empty|
9085|2015_07_01_Marquee_Rule.sql|SELECT * FROM `rule_values` WHERE `rule_name` LIKE '%Character:MarqueeHPUpdates%'|empty|
9086|2015_07_02_aa_rework.sql|SHOW TABLES LIKE 'aa_ranks'|empty|
# Upgrade conditions:
# This won't be needed after this system is implemented, but it is used database that are not

View File

@ -0,0 +1,7 @@
/* This rescales the old peq runspeeds which were about 80 percent too high to new values */
/* This section should only ever be run once */
UPDATE npc_types SET npc_types.runspeed = 1.050 WHERE (npc_types.runspeed > 0 and npc_types.runspeed < 1.2);
UPDATE npc_types SET npc_types.runspeed = 1.325 WHERE (npc_types.runspeed > 1.19 and npc_types.runspeed < 1.75 and race != 73 and race != 72);
UPDATE npc_types SET npc_types.runspeed = 1.575 WHERE (npc_types.runspeed > 1.69 and npc_types.runspeed < 2.2);
UPDATE npc_types SET npc_types.runspeed = 1.850 WHERE (npc_types.runspeed > 2.19 and npc_types.runspeed < 3);
UPDATE npc_types SET npc_types.runspeed = (npc_types.runspeed * 0.8) WHERE (npc_types.runspeed > 2.99 and npc_types.runspeed < 20);

View File

@ -0,0 +1,61 @@
/* some specific by name */
UPDATE npc_types SET npc_types.runspeed = 3.175 WHERE npc_types.name = 'a_shadowed_man';
UPDATE npc_types SET npc_types.runspeed = 1.850 WHERE npc_types.name = 'aviak_egret';
UPDATE npc_types SET npc_types.runspeed = 1.575 WHERE npc_types.name = 'froglok_hunter';
UPDATE npc_types SET npc_types.runspeed = 1.575 WHERE npc_types.name = 'froglok_forager';
/* rhinos */
UPDATE npc_types SET npc_types.runspeed = 1.850 WHERE npc_types.race = 135;
/* centaurs */
UPDATE npc_types SET npc_types.runspeed = 1.850 WHERE npc_types.race = 16;
/* griffins */
UPDATE npc_types SET npc_types.runspeed = 1.850 WHERE npc_types.race = 47;
/* wolves - use size, to not change cubs*/
UPDATE npc_types SET npc_types.runspeed = 1.575 WHERE (npc_types.race = 42 and npc_types.size > 5);
/* sarnaks */
UPDATE npc_types SET npc_types.runspeed = 1.325 WHERE npc_types.race = 131;
/* sabertooth tigers - use size, to not change cubs*/
UPDATE npc_types SET npc_types.runspeed = 1.575 WHERE (npc_types.race = 119 and npc_types.size > 6);
/* lions */
UPDATE npc_types SET npc_types.runspeed = 1.575 WHERE (npc_types.race = 50 and npc_types.size > 7);
/* panthers/pumas */
UPDATE npc_types SET npc_types.runspeed = 1.575 WHERE npc_types.race = 76;
/* beetles */
UPDATE npc_types SET npc_types.runspeed = 1.05 WHERE npc_types.race = 22;
/*leeches*/
UPDATE npc_types SET npc_types.runspeed = 1.05 WHERE npc_types.race = 104;
/*a_brontotherium*/
UPDATE npc_types SET npc_types.runspeed = 1.575 WHERE npc_types.race = 169;
/* raptors */
UPDATE npc_types SET npc_types.runspeed = 1.850 WHERE npc_types.race = 163;
/* vicious plants */
UPDATE npc_types SET npc_types.runspeed = 1.575 WHERE npc_types.race = 162;
/* western wastes, drakes, cragwyrms and wyvern */
UPDATE npc_types
JOIN spawnentry ON npc_types.id = spawnentry.npcID
JOIN spawn2 ON spawn2.spawngroupID = spawnentry.spawngroupID
SET npc_types.runspeed = 1.575
WHERE ((npc_types.race = 89 OR npc_types.race = 157 OR npc_types.race = 158) AND spawn2.zone = 'westwastes');
/* velium hounds/wolves */
UPDATE npc_types
JOIN spawnentry ON npc_types.id = spawnentry.npcID
JOIN spawn2 ON spawn2.spawngroupID = spawnentry.spawngroupID
SET npc_types.runspeed = 1.850
WHERE (npc_types.race = 42 AND spawn2.zone = 'westwastes');
/* Overthere Specials, goons, etc. */
UPDATE npc_types
JOIN spawnentry ON npc_types.id = spawnentry.npcID
JOIN spawn2 ON spawn2.spawngroupID = spawnentry.spawngroupID
SET npc_types.runspeed = 1.850
WHERE ((npc_types.race = 77 or npc_types.race = 147) AND spawn2.zone = 'overthere');
UPDATE npc_types SET npc_types.runspeed = 1.850 WHERE npc_types.name = 'Captain_Rottgrime';
UPDATE npc_types SET npc_types.runspeed = 1.850 WHERE npc_types.name = 'an_undead_marine';
/* Pet Speeds. */
UPDATE npc_types
JOIN pets ON npc_types.id = pets.npcID
SET npc_types.runspeed = 1.575;
/* raptors in tim are slower than other raptors in kunark */
UPDATE npc_types
JOIN spawnentry ON npc_types.id = spawnentry.npcID
JOIN spawn2 ON spawn2.spawngroupID = spawnentry.spawngroupID
SET npc_types.runspeed = 1.325
WHERE ( npc_types.race = 163 AND spawn2.zone = 'timorous' );

View File

@ -0,0 +1 @@
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Combat:UseLiveCombatRounds', 'true', 'If true use live NPC combat rules, false will use old rules.');

View File

@ -0,0 +1,27 @@
DELIMITER $$
DROP PROCEDURE IF EXISTS GrantTripleAttack$$
CREATE PROCEDURE GrantTripleAttack()
BEGIN
DECLARE finished INT;
DECLARE char_id INT;
DECLARE skill_max INT;
DECLARE cur CURSOR FOR SELECT character_data.id, skill_caps.cap FROM `character_data` LEFT JOIN `skill_caps` ON character_data.`level` = skill_caps.`level` AND character_data.class = skill_caps.class AND skill_caps.skillID = 76;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET finished = 1;
OPEN cur;
SET finished = 0;
REPEAT
FETCH cur INTO char_id, skill_max;
IF skill_max IS NOT NULL AND skill_max > 0 THEN
REPLACE INTO `character_skills` (`id`, `skill_id`, `value`) VALUES(char_id, 76, skill_max);
END IF;
UNTIL finished END REPEAT;
CLOSE cur;
END$$
DELIMITER ;
CALL GrantTripleAttack();
DROP PROCEDURE GrantTripleAttack;

View File

@ -0,0 +1,7 @@
/* This rescales the old peq runspeeds which were about 80 percent too high to new values */
/* This section should only ever be run once */
UPDATE npc_types SET npc_types.runspeed = 1.050 WHERE (npc_types.runspeed > 0 and npc_types.runspeed < 1.2);
UPDATE npc_types SET npc_types.runspeed = 1.325 WHERE (npc_types.runspeed > 1.19 and npc_types.runspeed < 1.75 and race != 73 and race != 72);
UPDATE npc_types SET npc_types.runspeed = 1.575 WHERE (npc_types.runspeed > 1.69 and npc_types.runspeed < 2.2);
UPDATE npc_types SET npc_types.runspeed = 1.850 WHERE (npc_types.runspeed > 2.19 and npc_types.runspeed < 3);
UPDATE npc_types SET npc_types.runspeed = 3 WHERE npc_types.runspeed > 3;

View File

@ -0,0 +1 @@
INSERT INTO `rule_values` (`rule_name`, `rule_value`, `notes`) VALUES ('Character:MarqueeHPUpdates', 'false', 'Will show Health % in center of screen < 100%');

File diff suppressed because it is too large Load Diff

View File

@ -152,7 +152,13 @@ void Client::SendEnterWorld(std::string name)
void Client::SendExpansionInfo() {
auto outapp = new EQApplicationPacket(OP_ExpansionInfo, sizeof(ExpansionInfo_Struct));
ExpansionInfo_Struct *eis = (ExpansionInfo_Struct*)outapp->pBuffer;
eis->Expansions = (RuleI(World, ExpansionSettings));
if(RuleB(World, UseClientBasedExpansionSettings)) {
eis->Expansions = ExpansionFromClientVersion(eqs->GetClientVersion());
//eis->Expansions = ExpansionFromClientVersion(this->GetCLE.
} else {
eis->Expansions = (RuleI(World, ExpansionSettings));
}
QueuePacket(outapp);
safe_delete(outapp);
}

View File

@ -60,7 +60,7 @@ bool LauncherLink::Process() {
end = m_states.end();
for(; cur != end; ++cur) {
if(!cur->second.up) {
StartZone(cur->first.c_str());
StartZone(cur->first.c_str(), cur->second.port);
}
}
m_bootTimer.Disable();
@ -184,14 +184,6 @@ bool LauncherLink::Process() {
bool LauncherLink::ContainsZone(const char *short_name) const {
return(m_states.find(short_name) != m_states.end());
/*
* std::map<std::string, bool>::const_iterator cur, end;
cur = m_states.begin();
end = m_states.end();
for(; cur != end; cur++) {
if(
}*/
}
void LauncherLink::BootZone(const char *short_name, uint16 port) {
@ -202,15 +194,20 @@ void LauncherLink::BootZone(const char *short_name, uint16 port) {
Log.Out(Logs::Detail, Logs::World_Server, "%s: Loaded zone '%s' on port %d", m_name.c_str(), short_name, zs.port);
m_states[short_name] = zs;
StartZone(short_name);
StartZone(short_name, port);
}
void LauncherLink::StartZone(const char *short_name) {
StartZone(short_name, 0);
}
void LauncherLink::StartZone(const char *short_name, uint16 port) {
auto pack = new ServerPacket(ServerOP_LauncherZoneRequest, sizeof(LauncherZoneRequest));
LauncherZoneRequest* s = (LauncherZoneRequest *) pack->pBuffer;
strn0cpy(s->short_name, short_name, 32);
s->command = ZR_Start;
s->port = port;
SendPacket(pack);
delete pack;
@ -222,6 +219,7 @@ void LauncherLink::RestartZone(const char *short_name) {
strn0cpy(s->short_name, short_name, 32);
s->command = ZR_Restart;
s->port = 0;
SendPacket(pack);
delete pack;
@ -233,6 +231,7 @@ void LauncherLink::StopZone(const char *short_name) {
strn0cpy(s->short_name, short_name, 32);
s->command = ZR_Stop;
s->port = 0;
SendPacket(pack);
delete pack;
@ -332,35 +331,3 @@ void LauncherLink::Shutdown() {
SendPacket(pack);
delete pack;
}

View File

@ -50,6 +50,7 @@ public:
void Shutdown();
void BootZone(const char *short_name, uint16 port);
void StartZone(const char *short_name);
void StartZone(const char *short_name, uint16 port);
void RestartZone(const char *short_name);
void StopZone(const char *short_name);
void BootDynamics(uint8 new_total);

View File

@ -2,6 +2,7 @@ CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
SET(zone_sources
aa.cpp
aa_ability.cpp
aggro.cpp
attack.cpp
beacon.cpp
@ -126,6 +127,7 @@ SET(zone_sources
SET(zone_headers
aa.h
aa_ability.h
basic_functions.h
beacon.h
bot.h

File diff suppressed because it is too large Load Diff

638
zone/aa.h
View File

@ -1,27 +1,8 @@
#ifndef AA_H
#define AA_H
struct AA_Ability;
struct SendAA_Struct;
#define MANA_BURN 664
#include <map>
#define MAX_SWARM_PETS 12 //this can change as long as you make more coords (swarm_pet_x/swarm_pet_y)
//this might be missing some, and some might not be used...
typedef enum { //AA Targeting Constants
aaTargetUser = 1,
aaTargetCurrent = 2, //use current target
aaTargetGroup = 3, //target group of user
aaTargetCurrentGroup = 4, //target group of current target
aaTargetPet = 5 //target the user's pet
} aaTargetType;
typedef enum {
aaActionNone = 0,
aaActionAETaunt = 1,
@ -42,21 +23,6 @@ typedef enum {
aaActionFadingMemories = 16
} aaNonspellAction;
//use these for AAs which dont cast spells, yet need effects
//if this list grows beyond 32, more work is needed in *AAEffect
typedef enum { //AA Effect IDs
aaEffectMassGroupBuff = 1, //unused - Handled via spell effect.
aaEffectRampage,
aaEffectSharedHealth,
aaEffectFlamingArrows,
aaEffectFrostArrows,
aaEffectWarcry,
aaEffectLeechTouch,
aaEffectProjectIllusion, // unused - Handled via spell effect
_maxaaEffectType = 32
} aaEffectType;
enum { //leadership AA indexes
groupAAMarkNPC = 0,
groupAANPCHealth,
@ -133,571 +99,6 @@ static const uint8 LeadershipAACosts[_maxLeaderAA][MAX_LEADERSHIP_TIERS] = {
{ 0, 0, 0, 0, 0, 0 }, //raidAA15
};
/*
typedef enum { //AA IDs
aaNone = 0,
aaInnateStrength = 2, //works
aaInnateStamina = 7, //works
aaInnateAgility = 12, //works
//aaCompleteHeal = 13,/ //not implemented, but is in dbstr_us.txt
aaInnateDexterity = 17, //works
aaInnateIntelligence = 22, //works
aaInnateWisdom = 27, //works
aaInnateCharisma = 32, //works
aaInnateFireProtection = 37, //works
aaInnateColdProtection = 42, //works
aaInnateMagicProtection = 47, //works
aaInnatePoisonProtection = 52, //works
aaInnateDiseaseProtection = 57, //works
aaInnateRunSpeed = 62, //works
aaInnateRegeneration = 65, //works
aaInnateMetabolism = 68,
aaInnateLungCapacity = 71, //handled by client
aaFirstAid = 74, //untested
aaHealingAdept = 77, //untested
aaHealingGift = 80, //untested
aaSpellCastingMastery = 83, //untested
aaSpellCastingReinforcement = 86, //untested
aaMentalClarity = 89,
aaSpellCastingFury = 92, //untested
aaChanellingFocus = 95,
aaSpellCastingSubtlety = 98, //untested
aaSpellCastingExpertise = 101, //untested
aaSpellCastingDeftness = 104, //untested
aaNaturalDurability = 107, //works
aaNaturalHealing = 110, //untested
aaCombatFury = 113, //untested
aaFearResistance = 116, //untested
aaFinishingBlow = 119, //untested
aaCombatStability = 122,
aaCombatAgility = 125,
aaMassGroupBuff = 128, //untested
aaDivineResurrection = 129, //DB
aaInnateInvisToUndead = 130, //DB
aaCelestialRegeneration = 131, //untested
aaBestowDivineAura = 132, //DB
aaTurnUndead = 133, //DB
aaPurifySoul = 136, //DB
aaQuickEvacuation = 137, //untested
aaExodus = 140, //untested
aaQuickDamage = 141, //untested
aaEnhancedRoot = 144,
aaDireCharm = 145, //untested
aaCannibalization = 146, //DB
aaQuickBuff = 147, //untested
aaAlchemyMastery = 150,
aaRabidBear = 153, //DB
aaManaBurn = 154, //DB
aaImprovedFamiliar = 155, //untested, implemented?
aaNexusGate = 156, //DB
aaUnknown54 = 157,
aaPermanentIllusion = 158,
aaJewelCraftMastery = 159,
aaGatherMana = 162, //DB
aaMendCompanion = 163, //DB
aaQuickSummoning = 164, //untested
aaFrenziedBurnout = 167, //DB
aaElementalFormFire = 168, //DB
aaElementalFormWater = 171, //DB
aaElementalFormEarth = 174, //DB
aaElementalFormAir = 177, //DB
aaImprovedReclaimEnergy = 180, //untested
aaTurnSummoned = 181, //DB
aaElementalPact = 182, //DB
aaLifeBurn = 183, //DB
aaDeadMesmerization = 184, //DB
aaFearstorm = 185, //DB
aaFleshToBone = 186, //DB
aaCallToCorpse = 187, //DB
aaDivineStun = 188, //DB
aaImprovedLayOnHands = 189,
aaSlayUndead = 190,
aaActOfValor = 193, //DB
aaHolySteed = 194, //DB
aaFearless = 195,
aa2HandBash = 196, //works. handled by client?
aaInnateCamouflage = 197, //DB
aaAmbidexterity = 198, //untested
aaArcheryMastery = 199, //untested
aaFletchingMastery = 202, //removed from db?
aaEndlessQuiver = 205, //untested
aaUnholySteed = 206, //DB
aaImprovedHarmTouch = 207, //untested
aaLeechTouch = 208, //DB
aaDeathPeace = 209,
aaSoulAbrasion = 210, //untested
aaInstrumentMastery = 213, //untested
aaUnknown91 = 216, //not used
aaUnknown92 = 219, //not used
aaUnknown93 = 222, //not used
aaJamFest = 225,
aaUnknown95 = 228,
aaSonicCall = 229,
aaCriticalMend = 230, //untested
aaPurifyBody = 233, //DB
aaChainCombo = 234,
aaRapidFeign = 237, //works
aaReturnKick = 240,
aaEscape = 243, //DB
aaPoisonMastery = 244,
aaDoubleRiposte = 247, //untested
aaQuickHide = 250,
aaQuickThrow = 253, //corrected from dbstr_us.txt
aaPurgePoison = 254, //DB
aaFlurry = 255, //untested
aaRampage = 258, //untested
aaAreaTaunt = 259, //untested
aaWarcry = 260, //DB
aaBandageWound = 263, //untested
aaSpellCastingReinforcementMastery = 266, //untested
aaSpellCastingFuryMastery = 267, //untested
aaExtendedNotes = 270, //untested
aaDragonPunch = 273,
aaStrongRoot = 274, //DB
aaSingingMastery = 275, //untested
aaBodyAndMindRejuvenation = 278, //added
aaPhysicalEnhancement = 279, //untested
aaAdvTrapNegotiation = 280, //untested
aaAcrobatics = 283, //untested
aaScribbleNotes = 286,
aaChaoticStab = 287, //untested
aaPetDiscipline = 288, //added
aaHobbleofSpirits = 289, //DB
aaFrenzyofSpirit = 290, //DB
aaParagonofSpirit = 291, //DB
aaAdvancedInnateStrength = 292, //works
aaAdvancedInnateStamina = 302, //works
aaAdvancedInnateAgility = 312, //works
aaAdvancedInnateDexterity = 322, //works
aaAdvancedInnateIntelligence = 332, //works
aaAdvancedInnateWisdom = 342, //works
aaAdvancedInnateCharisma = 352, //works
aaWardingofSolusek = 362, //works
aaBlessingofEci = 372, //works
aaMarrsProtection = 382, //works
aaShroudofTheFaceless = 392, //works
aaBertoxxulousGift = 402, //works
aaNewTanaanCraftingMastery = 412,
aaPlanarPower = 418, //untested
aaPlanarDurability = 423, //added
aaInnateEnlightenment = 426, //added
aaAdvancedSpellCastingMastery = 431,//untested
aaAdvancedHealingAdept = 434, //untested
aaAdvancedHealingGift = 437, //untested
aaCoupdeGrace = 440, //added
aaFuryoftheAges = 443, //added
aaMasteryofthePast = 446, //untested
aaLightningReflexes = 449, //added
aaInnateDefense = 454, //added
aaRadiantCure = 459, //DB
aaHastenedDivinity = 462, //DB
aaHastenedTurning = 465, //DB
aaHastenedPurificationofSoul = 468, //DB
aaHastenedGathering = 471, //DB
aaHastenedRabidity = 474, //DB
aaHastenedExodus = 477, //DB
aaHastenedRoot = 480, //DB
aaHastenedMending = 483, //DB
aaHastenedBanishment = 486, //DB
aaHastenedInstigation = 489, //DB, maybe
aaFuriousRampage = 492, //DB
aaHastenedPurificationoftheBody = 495,//DB
aaHastyExit = 498, //DB
aaHastenedPurification = 501, //DB
aaFlashofSteel = 504,
aaDivineArbitration = 507, //DB
aaWrathoftheWild = 510, //DB
aaVirulentParalysis = 513, //DB
aaHarvestofDruzzil = 516, //DB
aaEldritchRune = 517, //DB
aaServantofRo = 520, //DB
aaWaketheDead = 523, //DB
aaSuspendedMinion = 526, //untested
aaSpiritCall = 528, //DB
aaCelestialRenewal = 531, //DB
aaAllegiantFamiliar = 533,
aaHandofPiety = 534, //DB
aaMithanielsBinding = 537, //untested
aaMendingoftheTranquil = 539,
aaRagingFlurry = 542,
aaGuardianoftheForest = 545, //DB
aaSpiritoftheWood = 548, //DB
aaBestialFrenzy = 551, //untested
aaHarmoniousAttack = 556, //untested
aaKnightsAdvantage = 561,
aaFerocity = 564,
aaViscidRoots = 567,
aaSionachiesCrescendo = 568, //untested
aaAyonaesTutelage = 571,
aaFeignedMinion = 574,
aaUnfailingDivinity = 577,
aaAnimationEmpathy = 580, // Implemented
aaRushtoJudgement = 583,
aaLivingShield = 586,
aaConsumptionoftheSoul = 589, //untested
aaBoastfulBellow = 592, //DB
aaFervrentBlessing = 593, //untested
aaTouchoftheWicked = 596, //untested
aaPunishingBlade = 599,
aaSpeedoftheKnight = 602,
aaShroudofStealth = 605,
aaNimbleEvasion = 606,
aaTechniqueofMasterWu = 611,
aaHostoftheElements = 616, //DB
aaCallofXuzl = 619, //DB
aaHastenedStealth = 622,
aaIngenuity = 625,
aaFleetofFoot = 628,
aaFadingMemories = 630,
aaTacticalMastery = 631,
aaTheftofLife = 634,
aaFuryofMagic = 637,
aaFuryofMagicMastery2 = 640, //whats the difference?
aaProjectIllusion = 643,
aaHeadshot = 644, //added
aaEntrap = 645, //DB
aaUnholyTouch = 646, //untested
aaTotalDomination = 649, // Implemented
aaStalwartEndurance = 652, //implemented as bonus
aaQuickSummoning2 = 655, //whats the difference?
aaMentalClarity2 = 658, //whats the difference?
aaInnateRegeneration2 = 661, //whats the difference?
aaManaBurn2 = 664, //whats the difference?
aaExtendedNotes2 = 665, //not implemented - later expansions replaced Extended Notes with this.
aaSionachiesCrescendo2 = 668, //not implemented - later expansions replaced Sionachies Crescendo with this.
aaImprovedReclaimEnergy2 = 671, //whats the difference? untetsed
aaSwiftJourney = 672, //implemented as bonus
aaConvalescence = 674, //added 9/26/08
aaLastingBreath = 676, //handled by client
aaPackrat = 678, //added 9/29/08
aaHeightenedEndurance = 683,
aaWeaponAffinity = 686, //implemented
aaSecondaryForte = 691,
aaPersistantCasting = 692,
aaTuneofPursuance = 695,
aaImprovedInstrumentMastery = 700,
aaImprovedSingingMastery =701,
aaExultantBellowing = 702,
aaEchoofTaelosia = 707,
aaInternalMetronome = 710, //In 2006 this AA was removed.
aaPiousSupplication = 715,
aaBeastialAlignment = 718, //untested
aaWrathofXuzl = 721,
aaFeralSwipe = 723, //DB?
aaWardersFury = 724,
aaWardersAlacrity = 729,
aaPetAffinity = 734, // Implemented
aaMasteryofthePast2 = 735, //whats the difference?
aaSpellCastingSubtlety2 = 738, //whats the difference?
aaTouchoftheDivine = 741,
aaDivineAvatar = 746, //DB
aaExquisiteBenediction = 749, //DB
aaQuickenedCuring = 754,
aaNaturesBoon = 757, //DB
aaAdvancedTracking = 762,
aaCriticalAffliction = 767,
aaFuryofMagicMastery = 770, //whats the difference?
aaDoppelganger = 773,
aaEnchancedForgetfulness = 776,
aaMesmerizationMastery = 781,
aaQuickMassGroupBuff = 782,
aaSharedHealth = 785,
aaElementalFury = 790,
aaElementalAlacrity = 795,
aaElementalAgility = 800,
aaElementalDurability = 803,
aaSinisterStrikes = 806,
aaStrikethrough = 807,
aaStonewall = 810,
aaRapidStrikes = 815,
aaKickMastery = 820,
aaHightenedAwareness = 823,
aaDestructiveForce = 828, //DB
aaSwarmofDecay = 831, //DB
aaDeathsFury = 834,
aaQuickeningofDeath = 839,
aaAdvancedTheftofLife = 844,
aaTripleBackstab = 846,
aaHastenedPiety = 849,
aaImmobilizingBash = 852,
aaViciousSmash = 855,
aaRadiantCure2 = 860, //whats the difference?
aaPurification = 863,
aaPrecisionofthePathfinder = 864,
aaCoatofThistles = 867,
aaFlamingArrows = 872, //untested
aaFrostArrows = 875, //untested
aaSeizedOpportunity = 878,
aaTrapCircumvention = 881,
aaImprovedHastyExit = 886,
aaVirulentVenom = 888,
aaImprovedConsumptionofSoul = 893,
aaIntenseHatred = 895,
aaAdvancedSpiritCall = 900,
aaCalloftheAncients = 902, //DB
aaSturdiness = 907,
aaWarlordsTenacity = 912, //DB
aaStrengthenedStrike = 915,
aaExtendedShielding = 918,
aaRosFlamingFamiliar = 921, //DB
aaEcisIcyFamiliar = 922, //DB
aaDruzzilsMysticalFamiliar = 923, //DB
aaAdvancedFuryofMagicMastery = 924, //added 9/29/08
aaWardofDestruction = 926, //DB
aaFrenziedDevastation = 931, //DB
aaCombatFury2 = 934, //whats the difference?
aaCombatFury3 = 937, //whats the difference?
aaCombatFury4 = 940, //whats the difference?
aaFuryoftheAges2 = 943, //whats the difference?
aaFuryoftheAges3 = 946, //whats the difference?
aaFuryoftheAges4 = 949, //whats the difference?
aaPlanarDurability2 = 952, //whats the difference?
aaInnateEnlightenment2 = 955, //whats the difference?
aaDireCharm2 = 960, //whats the difference?
aaDireCharm3 = 961, //whats the difference?
aaTouchoftheDivine2 = 962, //whats the difference?
aaTouchofDecay = 967,
aaCalloftheAncients2 = 970, //whats the difference?
aaImprovedVision = 975,
aaEternalBreath = 978, //handled by client
aaBlacksmithingMastery = 979, //added 9/29/08
aaBakingMastery = 982, //added 9/29/08
aaBrewingMastery = 985, //added 9/29/08
aaFletchingMastery2 = 988, //added 9/29/08
aaPotteryMastery = 991, //added 9/29/08
aaTailoringMastery = 994, //added 9/29/08
aaSalvage = 997,
aaOrigin = 1000, //spell
aaChaoticPotential = 1001, //added
aaDiscordantDefiance = 1006, //added 9/29/08
aaTrialsofMataMuram = 1011,
aaMysticalAttuning = 1021,
aaDelayDeath = 1026,
aaHealthyAura = 1031,
aaFitness = 1036,
aaVeteransWrath = 1041, //added 9/29/08
aaVeteransWrath2 = 1044, //whats the difference?
aaVeteransWrath3 = 1047, //whats the difference?
aaVeteransWrath4 = 1050, //whats the difference?
aaDeathblow = 1053,
aaReflexiveMastery = 1061,
aaDefensiveInstincts = 1066,
aaMnemonicRetention = 1071, //Implemented
aaExpansiveMind = 1072, //added 9/29/08
aaSleightofHand = 1077,
aaSleightofHand2 = 1080, //whats the difference?
aaHealingAdeptMastery = 1083,
aaHealingGiftMastery = 1086,
aaArcaneTongues = 1089,
aaMasterofDisguise = 1092,
aaSlipperyAttacks = 1093,
aaImprovedCriticalAffliction = 1099,
aaFortifiedBellowing = 1102,
aaFuryofMagic2 = 1107, //whats the difference?
aaDanceofBlades = 1110,
aaShieldofNotes = 1116,
aaRoarofThunder = 1119,
aaPersistentMinion = 1122,
aaPerfectionofSpirit = 1123,
aaReplentishCompanion = 1126,
aaAdvancedPetDiscipline = 1129,
aaThrowingMastery = 1131,
aaBlurofAxes = 1134,
aaHastenedWarCry = 1137,
aaDeadAim = 1140,
aaFrenziedDefense = 1143,
aaTirelessSprint = 1146,
aaDesperation = 1149,
aaUntamedRage = 1150,
aaEchoingCries = 1155,
aaViciousFrenzy = 1158,
aaCrazedOnslaught = 1163,
aaOverwhelmingAttack = 1172,
aaFuriousRage = 1175,
aaBloodPact = 1178,
aaShieldingResistance = 1181,
aaHealingBoon = 1186,
aaResplendentCure = 1189,
aaCelestialHammer = 1192,
aaDivineRetribution = 1195,
aaCelestialRejuvination = 1203,
aaFerventBenediction = 1206,
aaSanctuary = 1209,
aaDestructiveFury = 1210, //added 9/29/08
aaDestructiveFury2 = 1213, //whats the difference?
aaBoonoftheForest = 1222,
aaSpiritoftheGrove = 1225,
aaCalloftheWild = 1228,
aaSecondaryRecall = 1229,
aaNaturesBounty = 1230,
aaStasis = 1233,
aaColorShock = 1239,
aaMindOverMatter = 1242,
aaSoothingWords = 1245,
aaElementalSwarm = 1248,
aaHeartofFlames = 1251,
aaHeartofVapor = 1252,
aaHeartofIce = 1253,
aaHeartofStone = 1254,
aaImitateDeath = 1255,
aaCripplingStrike = 1256,
aaStunningKick = 1259,
aaEyeGouge = 1262,
aaIronKicks = 1265,
aaStyleoftheMimic = 1268,
aaDeathPeace2 = 1272, //whats the difference?
aaArmyoftheDead = 1274,
aaCelestialStun = 1277,
aaHandofDevotion = 1278,
aaSteadfastWill = 1284,
aaShieldBlock = 1287,
aaScoutsEfficiency = 1290,
aaGuardianoftheGlade = 1293,
aaTrackingMastery = 1296,
aaFlurryofKnives = 1301,
aaPrecision = 1304,
aaNervesofSteel = 1307,
aaTouchoftheCursed = 1313,
aaSpiritualCorrosion = 1316,
aaSoulThief = 1319,
aaSpiritualChanneling = 1323,
aaBoonoftheAncients = 1324,
aaAncestralAid = 1327,
aaResoluteDefiance = 1330,
aaPresstheAttack = 1333,
aaMindCrash = 1334,
aaProlongedDestruction = 1337,
aaRosGreaterFamiliar = 1340,
aaEcisGreaterFamiliar = 1341,
aaDruzzilsGreaterFamiliar = 1342,
aaTeleportBind = 1343,
aaDevotedFamiliar = 1344,
aaAuspiceoftheHunter = 1345,
aaSavageSpirit = 1348,
aaPresstheAttack2 = 1351, //whats the difference?
aaCripplingStrike2 = 1352, //whats the difference?
aaStunningKick2 = 1353, //whats the difference?
aaEyeGouge2 = 1358, //whats the difference?
//Dragons of Norrath
//good info here: http://www.eqthieves.com/exp-don-progression.htm and here: http://everquest.allakhazam.com/db/guides.html?guide=811
aaGiftoftheDarkReign = 1361, //from dbstr_us.txt
aaTenacityoftheDarkReign = 1362, //from dbstr_us.txt
aaEmbraceoftheDarkReign = 1363, //from dbstr_us.txt
aaPoweroftheDarkReign = 1364, //from dbstr_us.txt
aaFervoroftheDarkReign = 1365, //from dbstr_us.txt
aaGiftoftheKeepers = 1366, //from dbstr_us.txt
aaValoroftheKeepers = 1367, //from dbstr_us.txt
aaEmbraceoftheKeepers = 1368, //from dbstr_us.txt
aaPoweroftheKeepers = 1369, //from dbstr_us.txt
aaSanctityoftheKeepers = 1370, //from dbstr_us.txt
//Veteran AAs
aaLessonoftheDevoted = 1371, //from dbstr_us.txt
aaInfusionoftheFaithful = 1372, //from dbstr_us.txt
aaChaoticJester = 1373, //from dbstr_us.txt
aaExpedientRecovery = 1374, //from dbstr_us.txt
aaSteadfastServant = 1375, //from dbstr_us.txt
aaStaunchRecovery = 1376, //from dbstr_us.txt
aaIntensityoftheResolute = 1377, //from dbstr_us.txt
//Depths of Darkhollow
//the following 5 look to be used as flags for completion of the Blood Raids for access to the Demiplane of Blood
//quest info here: http://everquest.allakhazam.com/db/quest.html?quest=3582
//"You must also complete the five Blood Raids in any order: The Council of Nine, Emperor Draygun, Bloodeye, Matriarch Shyra, Sendaii, the Hive Queen"
//"The AA's you receive are: Curse of Blood (1/5), Affliction of Blood (2/5), Torment of Blood (3/5), Temptation of Blood (4/5), Invitation of Blood (5/5)."
aaCurseofBlood = 1378, //from dbstr_us.txt
aaAfflictionofBlood = 1379, //from dbstr_us.txt
aaTormentofBlood = 1380, //from dbstr_us.txt
aaTemptationofBlood = 1381, //from dbstr_us.txt
aaInvitationofBlood = 1382, //from dbstr_us.txt
aaTurnUndead2 = 1383, //from dbstr_us.txt, Class AA changed in DoD
aaWrackUndead = 1386, //from dbstr_us.txt, PoP Class AA changed in DoD
aaEradicateUndead = 1387, //from dbstr_us.txt
aaInnateSeeInvis = 1388, //from dbstr_us.txt
aaProlongedMortality = 1389, //from dbstr_us.txt
aaPrecognition = 1394, //from dbstr_us.txt
aaThickSkin = 1399, //from dbstr_us.txt
aaSilentCasting = 1404, //from dbstr_us.txt
aaSilentCasting2 = 1409, //from dbstr_us.txt
aaHastenedMindCrash = 1414, //from dbstr_us.txt
aaFieldDressing = 1417, //from dbstr_us.txt
aaBandageWounds = 1420, //from dbstr_us.txt
aaCascadingRage = 1425, //from dbstr_us.txt
aaElementalFerocity = 1430, //from dbstr_us.txt
aaGiftofMana = 1435, //from dbstr_us.txt
aaRuneofShadows = 1440, //from dbstr_us.txt
aaChannelingMastery = 1445, //from dbstr_us.txt
aaConservation = 1453, //from dbstr_us.txt
aaCryofBattle = 1458, //from dbstr_us.txt
aaWardofPurity = 1459, //from dbstr_us.txt
aaTurnSummoned2 = 1462, //from dbstr_us.txt
aaWrackSummoned = 1465, //from dbstr_us.txt
aaEradicateSummoned = 1466, //from dbstr_us.txt
aaWardersSavagery = 1467, //from dbstr_us.txt
aaShackleofSpirits = 1470, //from dbstr_us.txt
aaHastenedThunder = 1471, //from dbstr_us.txt
aaTranslocationalAnchor = 1474, //from dbstr_us.txt
aaStealthyGetaway = 1477, //from dbstr_us.txt
aaPyromancy = 1478, //from dbstr_us.txt
aaMasteryofFury = 1483, //from dbstr_us.txt
aaAbundantHealing = 1486, //from dbstr_us.txt
aaGreaterAvatar = 1491, //from dbstr_us.txt
aaSharedCamouflage = 1494, //from dbstr_us.txt
aaConvergenceofSpirits = 1495, //from dbstr_us.txt
aaNaturesGuardian = 1498, //from dbstr_us.txt
aaEdictofCommand = 1501, //from dbstr_us.txt
aaExtendedBurnout = 1504, //from dbstr_us.txt
aaGuardianofRo = 1507, //from dbstr_us.txt
aaBloodMagic = 1510, //from dbstr_us.txt
aaGraverobbing = 1511, //from dbstr_us.txt
aaAfflictionMastery = 1514, //from dbstr_us.txt
aaGreaterRabidBear = 1517, //from dbstr_us.txt
aaAncestralGuard = 1520, //from dbstr_us.txt
aaCloakofLight = 1523, //from dbstr_us.txt
aaVanquishUndead = 1524, //from dbstr_us.txt
aaCloakofShadows = 1527, //from dbstr_us.txt
aaWillfulDeath = 1528, //from dbstr_us.txt
aaSwiftBlade = 1533, //from dbstr_us.txt
aaWickedBlade = 1536, //from dbstr_us.txt
aaForcedOpening = 1539, //from dbstr_us.txt
aaAppraisal = 1542, //from dbstr_us.txt
aaPreciseStrikes = 1543, //from dbstr_us.txt
aaHastenedDeath = 1546, //from dbstr_us.txt
aaUnflinchingResolve = 1549, //from dbstr_us.txt
aaWeightlessSteps = 1552, //from dbstr_us.txt
aaHastenedBlades = 1555, //from dbstr_us.txt
aaImprovedHarmoniousAttack = 1563, //from dbstr_us.txt
aaImprovedBestialFrenzy = 1566, //from dbstr_us.txt
aaSongofStone = 1569, //from dbstr_us.txt
aaDeepSleep = 1572, //from dbstr_us.txt
aaCompanionsGift = 1577, //from dbstr_us.txt
aaHastenedDefiance = 1583, //from dbstr_us.txt
aaDauntlessPerseverance = 1586, //from dbstr_us.txt
aaConcentration = 1587, //from dbstr_us.txt
aaEnhancedAggression = 1592, //from dbstr_us.txt
aaCallofChallenge = 1597, //from dbstr_us.txt
aaCacophony = 1598, //from dbstr_us.txt
aaImprovedHeadshot = 1601, //from dbstr_us.txt
aaAnatomy = 1604, //from dbstr_us.txt
aaFetterofSpirits = 1607, //from dbstr_us.txt
aaTrickShot = 1608, //from dbstr_us.txt
aaLightningStrikes = 1616, //from dbstr_us.txt
aaRelentlessAssault = 1621, //from dbstr_us.txt
aaKnightsExpertise = 1624, //from dbstr_us.txt
aaSelosEnduringCadence = 1627, //from dbstr_us.txt
aaHarmTouch = 7800, //from dbstr_us.txt
aaLayonHands = 7850, //from dbstr_us.txt
aaLayonHandsRank16 = 7866,
aaHighestID //this should always be last, and should always
//follow the highest AA ID
} aaID;
*/
typedef enum { //AA IDs
aaNone =0,
aaInnateStrength =2,//implemented as bonus
@ -2109,21 +1510,6 @@ typedef enum { //AA IDs
//follow the highest AA ID
} aaID;
//Structure representing the database's AA actions
struct AA_DBAction {
uint32 reuse_time; //in seconds
uint16 spell_id; //spell to cast, SPELL_UNKNOWN=no spell
aaTargetType target; //from aaTargetType
aaNonspellAction action; //non-spell action to take
uint16 mana_cost; //mana the NON-SPELL action costs
uint16 duration; //duration of NON-SPELL effect, 0=N/A
aaID redux_aa; //AA which reduces reuse time
int32 redux_rate; //%/point in redux_aa reduction in reuse time
aaID redux_aa2; //AA which reduces reuse time
int32 redux_rate2; //%/point in redux_aa reduction in reuse time
};
//Structure representing the database's swarm pet configs
struct AA_SwarmPet {
uint8 count; //number to summon
@ -2131,23 +1517,6 @@ struct AA_SwarmPet {
uint16 duration; //how long they last, in seconds
};
struct AALevelCost_Struct
{
uint32 Level;
uint32 Cost;
};
//assumes that no activatable aa.has more than 5 ranks
#define MAX_AA_ACTION_RANKS 20
extern AA_DBAction AA_Actions[aaHighestID][MAX_AA_ACTION_RANKS]; //[aaid][rank]
extern std::map<uint16, AA_SwarmPet> AA_SwarmPets; //key=spell_id
#define AA_Choose3(val, v1, v2, v3) (val==1?v1:(val==2?v2:v3))
extern std::map<uint32,SendAA_Struct*>aas_send;
extern std::map<uint32, std::map<uint32, AA_Ability> > aa_effects;
extern std::map<uint32, AALevelCost_Struct> AARequiredLevelAndCost;
enum { //values of AA_Action.action
aaActionActivate = 0,
aaActionSetEXP = 1,
@ -2167,4 +1536,11 @@ public:
uint32 owner_id;
};
enum AATimers
{
aaTimerRampage,
aaTimerWarcry,
aaTimerMax
};
#endif

70
zone/aa_ability.cpp Normal file
View File

@ -0,0 +1,70 @@
/* EQEMu: Everquest Server Emulator
Copyright (C) 2001-2015 EQEMu Development Team (http://eqemulator.net)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY except by those people which sell it, which
are required to give you total support for your newly bought product;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "../common/global_define.h"
#include "../common/types.h"
#include "masterentity.h"
#include "aa_ability.h"
AA::Rank *AA::Ability::GetMaxRank() {
if(!first)
return nullptr;
Rank *current = first;
while(current->next) {
current = current->next;
}
return current;
}
AA::Rank *AA::Ability::GetRankByPointsSpent(int current_level) {
if(current_level == 0)
return nullptr;
if(!first)
return nullptr;
int i = 1;
Rank *current = first;
while(current->next) {
if(i == current_level) {
break;
}
i++;
current = current->next;
}
return current;
}
int AA::Ability::GetMaxLevel(Mob *who) {
int max_level = 0;
Rank *current = first;
while(current) {
if(!who->CanUseAlternateAdvancementRank(current)) {
return max_level;
}
max_level++;
current = current->next;
}
return max_level;
}

61
zone/aa_ability.h Normal file
View File

@ -0,0 +1,61 @@
/* EQEMu: Everquest Server Emulator
Copyright (C) 2001-2015 EQEMu Development Team (http://eqemulator.net)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY except by those people which sell it, which
are required to give you total support for your newly bought product;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef EQEMU_ZONE_AA_ABILITY_H
#define EQEMU_ZONE_AA_ABILITY_H
#include "../common/global_define.h"
#include <unordered_map>
#include <vector>
#include <memory>
#include "aa_rank_effects.h"
#include "aa_rank.h"
class Mob;
namespace AA
{
class Ability
{
public:
Ability() { }
~Ability() { }
Rank *GetMaxRank();
Rank *GetRankByPointsSpent(int current_level);
int GetMaxLevel(Mob *who);
int id;
std::string name;
int category;
int classes;
int races;
int deities;
int drakkin_heritage;
int status;
bool grant_only;
int type;
int charges;
int first_rank_id;
Rank *first;
};
}
#endif

56
zone/aa_rank.h Normal file
View File

@ -0,0 +1,56 @@
/* EQEMu: Everquest Server Emulator
Copyright (C) 2001-2015 EQEMu Development Team (http://eqemulator.net)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY except by those people which sell it, which
are required to give you total support for your newly bought product;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef EQEMU_ZONE_AA_RANK_H
#define EQEMU_ZONE_AA_RANK_H
namespace AA
{
class Ability;
class Rank
{
public:
Rank() { }
~Rank() { }
int id;
int upper_hotkey_sid;
int lower_hotkey_sid;
int title_sid;
int desc_sid;
int cost;
int level_req;
int spell;
int spell_type;
int recast_time;
int prev_id;
Rank *prev;
int next_id;
Rank *next;
int current_value;
int expansion;
int total_cost;
Ability *base_ability;
std::vector<RankEffect> effects;
std::map<int, int> prereqs;
};
}
#endif

38
zone/aa_rank_effects.h Normal file
View File

@ -0,0 +1,38 @@
/* EQEMu: Everquest Server Emulator
Copyright (C) 2001-2015 EQEMu Development Team (http://eqemulator.net)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY except by those people which sell it, which
are required to give you total support for your newly bought product;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef EQEMU_ZONE_AA_RANK_EFFECTS_H
#define EQEMU_ZONE_AA_RANK_EFFECTS_H
#include "../common/global_define.h"
#include <string>
namespace AA
{
struct RankEffect
{
int slot;
int effect_id;
int base1;
int base2;
};
}
#endif

View File

@ -341,7 +341,7 @@ bool Mob::CheckHitChance(Mob* other, SkillUseTypes skillinuse, int Hand, int16 c
return(tohit_roll <= chancetohit);
}
bool Mob::AvoidDamage(Mob* other, int32 &damage, bool CanRiposte)
bool Mob::AvoidDamage(Mob *other, int32 &damage, int hand)
{
/* called when a mob is attacked, does the checks to see if it's a hit
* and does other mitigation checks. 'this' is the mob being attacked.
@ -353,22 +353,32 @@ bool Mob::AvoidDamage(Mob* other, int32 &damage, bool CanRiposte)
* -4 - dodge
*
*/
float skill;
float bonus;
float RollTable[4] = {0,0,0,0};
float roll;
Mob *attacker=other;
Mob *defender=this;
//garunteed hit
bool ghit = false;
if((attacker->aabonuses.MeleeSkillCheck + attacker->spellbonuses.MeleeSkillCheck + attacker->itembonuses.MeleeSkillCheck) > 500)
ghit = true;
/* Order according to current (SoF+?) dev quotes:
* https://forums.daybreakgames.com/eq/index.php?threads/test-update-06-10-15.223510/page-2#post-3261772
* https://forums.daybreakgames.com/eq/index.php?threads/test-update-06-10-15.223510/page-2#post-3268227
* Riposte 50, hDEX, must have weapon/fists, doesn't work on archery/throwing
* Block 25, hDEX, works on archery/throwing, behind block done here if back to attacker base1 is chance
* Parry 45, hDEX, doesn't work on throwing/archery, must be facing target
* Dodge 45, hAGI, works on archery/throwing, monks can dodge attacks from behind
* Shield Block, rand base1
* Staff Block, rand base1
* regular strike through
* avoiding the attack (CheckHitChance)
* As soon as one succeeds, none of the rest are checked
*
* Formula (all int math)
* (posted for parry, assume rest at the same)
* Chance = (((SKILL + 100) + [((SKILL+100) * SPA(175).Base1) / 100]) / 45) + [(hDex / 25) - min([hDex / 25], hStrikethrough)].
* hStrikethrough is a mob stat that was added to counter the bonuses of heroic stats
* Number rolled against 100, if the chance is greater than 100 it happens 100% of time
*
* Things with 10k accuracy mods can be avoided with these skills qq
*/
Mob *attacker = other;
Mob *defender = this;
bool InFront = false;
if (attacker->InFrontMob(this, attacker->GetX(), attacker->GetY()))
InFront = true;
bool InFront = attacker->InFrontMob(this, attacker->GetX(), attacker->GetY());
/*
This special ability adds a negative modifer to the defenders riposte/block/parry/chance
@ -383,10 +393,9 @@ bool Mob::AvoidDamage(Mob* other, int32 &damage, bool CanRiposte)
int counter_parry = 0;
int counter_dodge = 0;
if (attacker->GetSpecialAbility(COUNTER_AVOID_DAMAGE)){
if (attacker->GetSpecialAbility(COUNTER_AVOID_DAMAGE)) {
counter_all = attacker->GetSpecialAbilityParam(COUNTER_AVOID_DAMAGE, 0);
counter_riposte = attacker->GetSpecialAbilityParam(COUNTER_AVOID_DAMAGE,1);
counter_riposte = attacker->GetSpecialAbilityParam(COUNTER_AVOID_DAMAGE, 1);
counter_block = attacker->GetSpecialAbilityParam(COUNTER_AVOID_DAMAGE, 2);
counter_parry = attacker->GetSpecialAbilityParam(COUNTER_AVOID_DAMAGE, 3);
counter_dodge = attacker->GetSpecialAbilityParam(COUNTER_AVOID_DAMAGE, 4);
@ -400,31 +409,37 @@ bool Mob::AvoidDamage(Mob* other, int32 &damage, bool CanRiposte)
Log.Out(Logs::Detail, Logs::Combat, "I am enraged, riposting frontal attack.");
}
/////////////////////////////////////////////////////////
// riposte
/////////////////////////////////////////////////////////
float riposte_chance = 0.0f;
if (CanRiposte && damage > 0 && CanThisClassRiposte() && InFront)
{
riposte_chance = (100.0f + static_cast<float>(aabonuses.RiposteChance + spellbonuses.RiposteChance +
itembonuses.RiposteChance - counter_riposte - counter_all)) / 100.0f;
skill = GetSkill(SkillRiposte);
if (IsClient()) {
// riposte -- it may seem crazy, but if the attacker has SPA 173 on them, they are immune to Ripo
bool ImmuneRipo = attacker->aabonuses.RiposteChance || attacker->spellbonuses.RiposteChance || attacker->itembonuses.RiposteChance;
// Need to check if we have something in MainHand to actually attack with (or fists)
if (hand != MainRange && CanThisClassRiposte() && InFront && !ImmuneRipo) {
if (IsClient())
CastToClient()->CheckIncreaseSkill(SkillRiposte, other, -10);
// check auto discs ... I guess aa/items too :P
if (spellbonuses.RiposteChance == 10000 || aabonuses.RiposteChance == 10000 || itembonuses.RiposteChance == 10000) {
damage = -3;
return true;
}
if (!ghit) { //if they are not using a garunteed hit discipline
bonus = 2.0 + skill/60.0 + (GetDEX()/200);
bonus *= riposte_chance;
bonus = mod_riposte_chance(bonus, attacker);
RollTable[0] = bonus + (itembonuses.HeroicDEX / 25); // 25 heroic = 1%, applies to ripo, parry, block
int chance = GetSkill(SkillRiposte) + 100;
chance += (chance * (aabonuses.RiposteChance + spellbonuses.RiposteChance + itembonuses.RiposteChance)) / 100;
chance /= 50;
chance += itembonuses.HeroicDEX / 25; // live has "heroic strickthrough" here to counter
if (counter_riposte || counter_all) {
float counter = (counter_riposte + counter_all) / 100.0f;
chance -= chance * counter;
}
// AA Slippery Attacks
if (hand == MainSecondary) {
int slip = aabonuses.OffhandRiposteFail + itembonuses.OffhandRiposteFail + spellbonuses.OffhandRiposteFail;
chance += chance * slip / 100;
}
if (chance > 0 && zone->random.Roll(chance)) { // could be <0 from offhand stuff
damage = -3;
return true;
}
}
///////////////////////////////////////////////////////
// block
///////////////////////////////////////////////////////
bool bBlockFromRear = false;
// a successful roll on this does not mean a successful block is forthcoming. only that a chance to block
@ -435,101 +450,100 @@ bool Mob::AvoidDamage(Mob* other, int32 &damage, bool CanRiposte)
if (BlockBehindChance && zone->random.Roll(BlockBehindChance))
bBlockFromRear = true;
float block_chance = 0.0f;
if (damage > 0 && CanThisClassBlock() && (InFront || bBlockFromRear)) {
block_chance = (100.0f + static_cast<float>(aabonuses.IncreaseBlockChance + spellbonuses.IncreaseBlockChance +
itembonuses.IncreaseBlockChance - counter_block - counter_all)) / 100.0f;
skill = CastToClient()->GetSkill(SkillBlock);
if (IsClient()) {
if (CanThisClassBlock() && (InFront || bBlockFromRear)) {
if (IsClient())
CastToClient()->CheckIncreaseSkill(SkillBlock, other, -10);
}
if (!ghit) { //if they are not using a garunteed hit discipline
bonus = 2.0 + skill/35.0 + (GetDEX()/200);
bonus = mod_block_chance(bonus, attacker);
RollTable[1] = RollTable[0] + (bonus * block_chance);
}
}
else{
RollTable[1] = RollTable[0];
}
//Try Shield Block OR TwoHandBluntBlockCheck
if(damage > 0 && HasShieldEquiped() && (aabonuses.ShieldBlock || spellbonuses.ShieldBlock || itembonuses.ShieldBlock) && (InFront || bBlockFromRear))
RollTable[1] += static_cast<float>(aabonuses.ShieldBlock + spellbonuses.ShieldBlock + itembonuses.ShieldBlock - counter_block - counter_all);
else if(damage > 0 && HasTwoHandBluntEquiped() && (aabonuses.TwoHandBluntBlock || spellbonuses.TwoHandBluntBlock || itembonuses.TwoHandBluntBlock) && (InFront || bBlockFromRear))
RollTable[1] += static_cast<float>(aabonuses.TwoHandBluntBlock + spellbonuses.TwoHandBluntBlock + itembonuses.TwoHandBluntBlock - counter_block - counter_all);
//////////////////////////////////////////////////////
// parry
//////////////////////////////////////////////////////
float parry_chance = 0.0f;
if (damage > 0 && CanThisClassParry() && InFront){
parry_chance = (100.0f + static_cast<float>(aabonuses.ParryChance + itembonuses.ParryChance +
itembonuses.ParryChance - counter_parry - counter_all)) / 100.0f;
skill = CastToClient()->GetSkill(SkillParry);
if (IsClient()) {
CastToClient()->CheckIncreaseSkill(SkillParry, other, -10);
}
if (!ghit) { //if they are not using a garunteed hit discipline
bonus = 2.0 + skill/60.0 + (GetDEX()/200);
bonus *= parry_chance;
bonus = mod_parry_chance(bonus, attacker);
RollTable[2] = RollTable[1] + bonus;
}
}
else{
RollTable[2] = RollTable[1];
}
////////////////////////////////////////////////////////
// dodge
////////////////////////////////////////////////////////
float dodge_chance = 0.0f;
if (damage > 0 && CanThisClassDodge() && InFront){
dodge_chance = (100.0f + static_cast<float>(aabonuses.DodgeChance + spellbonuses.DodgeChance +
itembonuses.DodgeChance - counter_dodge - counter_all)) / 100.0f;
skill = CastToClient()->GetSkill(SkillDodge);
if (IsClient()) {
CastToClient()->CheckIncreaseSkill(SkillDodge, other, -10);
}
if (!ghit) { //if they are not using a garunteed hit discipline
bonus = 2.0 + skill/60.0 + (GetAGI()/200);
bonus *= dodge_chance;
//DCBOOMKAR
bonus = mod_dodge_chance(bonus, attacker);
RollTable[3] = RollTable[2] + bonus - (itembonuses.HeroicDEX / 25) + (itembonuses.HeroicAGI / 25);
}
}
else{
RollTable[3] = RollTable[2];
}
if(damage > 0){
roll = zone->random.Real(0,100);
if(roll <= RollTable[0]){
damage = -3;
}
else if(roll <= RollTable[1]){
// check auto discs ... I guess aa/items too :P
if (spellbonuses.IncreaseBlockChance == 10000 || aabonuses.IncreaseBlockChance == 10000 ||
itembonuses.IncreaseBlockChance == 10000) {
damage = -1;
return true;
}
else if(roll <= RollTable[2]){
damage = -2;
int chance = GetSkill(SkillBlock) + 100;
chance += (chance * (aabonuses.IncreaseBlockChance + spellbonuses.IncreaseBlockChance + itembonuses.IncreaseBlockChance)) / 100;
chance /= 25;
chance += itembonuses.HeroicDEX / 25; // live has "heroic strickthrough" here to counter
if (counter_block || counter_all) {
float counter = (counter_block + counter_all) / 100.0f;
chance -= chance * counter;
}
else if(roll <= RollTable[3]){
damage = -4;
if (zone->random.Roll(chance)) {
damage = -1;
return true;
}
}
Log.Out(Logs::Detail, Logs::Combat, "Final damage after all avoidances: %d", damage);
// parry
if (CanThisClassParry() && InFront && hand != MainRange) {
if (IsClient())
CastToClient()->CheckIncreaseSkill(SkillParry, other, -10);
// check auto discs ... I guess aa/items too :P
if (spellbonuses.ParryChance == 10000 || aabonuses.ParryChance == 10000 || itembonuses.ParryChance == 10000) {
damage = -2;
return true;
}
int chance = GetSkill(SkillParry) + 100;
chance += (chance * (aabonuses.ParryChance + spellbonuses.ParryChance + itembonuses.ParryChance)) / 100;
chance /= 45;
chance += itembonuses.HeroicDEX / 25; // live has "heroic strickthrough" here to counter
if (counter_parry || counter_all) {
float counter = (counter_parry + counter_all) / 100.0f;
chance -= chance * counter;
}
if (zone->random.Roll(chance)) {
damage = -2;
return true;
}
}
// dodge
if (CanThisClassDodge() && (InFront || GetClass() == MONK) ) {
if (IsClient())
CastToClient()->CheckIncreaseSkill(SkillDodge, other, -10);
// check auto discs ... I guess aa/items too :P
if (spellbonuses.DodgeChance == 10000 || aabonuses.DodgeChance == 10000 || itembonuses.DodgeChance == 10000) {
damage = -4;
return true;
}
int chance = GetSkill(SkillDodge) + 100;
chance += (chance * (aabonuses.DodgeChance + spellbonuses.DodgeChance + itembonuses.DodgeChance)) / 100;
chance /= 45;
chance += itembonuses.HeroicAGI / 25; // live has "heroic strickthrough" here to counter
if (counter_dodge || counter_all) {
float counter = (counter_dodge + counter_all) / 100.0f;
chance -= chance * counter;
}
if (zone->random.Roll(chance)) {
damage = -4;
return true;
}
}
// Try Shield Block OR TwoHandBluntBlockCheck
if (HasShieldEquiped() && (aabonuses.ShieldBlock || spellbonuses.ShieldBlock || itembonuses.ShieldBlock) && (InFront || bBlockFromRear)) {
int chance = aabonuses.ShieldBlock + spellbonuses.ShieldBlock + itembonuses.ShieldBlock;
if (counter_block || counter_all) {
float counter = (counter_block + counter_all) / 100.0f;
chance -= chance * counter;
}
if (zone->random.Roll(chance)) {
damage = -1;
return true;
}
}
if (HasTwoHandBluntEquiped() && (aabonuses.TwoHandBluntBlock || spellbonuses.TwoHandBluntBlock || itembonuses.TwoHandBluntBlock) && (InFront || bBlockFromRear)) {
int chance = aabonuses.TwoHandBluntBlock + itembonuses.TwoHandBluntBlock + spellbonuses.TwoHandBluntBlock;
if (counter_block || counter_all) {
float counter = (counter_block + counter_all) / 100.0f;
chance -= chance * counter;
}
if (zone->random.Roll(chance)) {
damage = -1;
return true;
}
}
if (damage < 0)
return true;
return false;
}
@ -1286,6 +1300,7 @@ bool Client::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, b
}
}
// this effect is actually a min cap that happens after the final damage is calculated
min_hit += min_hit * GetMeleeMinDamageMod_SE(skillinuse) / 100;
if(max_hit < min_hit)
@ -1312,66 +1327,52 @@ bool Client::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, b
}
//check to see if we hit..
if(!other->CheckHitChance(this, skillinuse, Hand, hit_chance_bonus)) {
Log.Out(Logs::Detail, Logs::Combat, "Attack missed. Damage set to 0.");
damage = 0;
} else { //we hit, try to avoid it
other->AvoidDamage(this, damage);
other->MeleeMitigation(this, damage, min_hit, opts);
if(damage > 0)
CommonOutgoingHitSuccess(other, damage, skillinuse);
Log.Out(Logs::Detail, Logs::Combat, "Final damage after all reductions: %d", damage);
}
//riposte
bool slippery_attack = false; // Part of hack to allow riposte to become a miss, but still allow a Strikethrough chance (like on Live)
if (damage == -3) {
if (bRiposte) return false;
else {
if (Hand == MainSecondary) {// Do we even have it & was attack with mainhand? If not, don't bother with other calculations
//Live AA - SlipperyAttacks
//This spell effect most likely directly modifies the actual riposte chance when using offhand attack.
int32 OffhandRiposteFail = aabonuses.OffhandRiposteFail + itembonuses.OffhandRiposteFail + spellbonuses.OffhandRiposteFail;
OffhandRiposteFail *= -1; //Live uses a negative value for this.
if (OffhandRiposteFail &&
(OffhandRiposteFail > 99 || zone->random.Roll(OffhandRiposteFail))) {
damage = 0; // Counts as a miss
slippery_attack = true;
} else
DoRiposte(other);
if (IsDead()) return false;
if (other->AvoidDamage(this, damage, Hand)) {
if (!bRiposte && !IsStrikethrough) {
int strike_through = itembonuses.StrikeThrough + spellbonuses.StrikeThrough + aabonuses.StrikeThrough;
if(strike_through && zone->random.Roll(strike_through)) {
Message_StringID(MT_StrikeThrough, STRIKETHROUGH_STRING); // You strike through your opponents defenses!
Attack(other, Hand, false, true); // Strikethrough only gives another attempted hit
return false;
}
else
// I'm pretty sure you can riposte a riposte
if (damage == -3 && !bRiposte) {
DoRiposte(other);
if (IsDead()) return false;
if (IsDead())
return false;
}
}
Log.Out(Logs::Detail, Logs::Combat, "Avoided damage with code %d", damage);
} else {
if (other->CheckHitChance(this, skillinuse, Hand, hit_chance_bonus)) {
other->MeleeMitigation(this, damage, min_hit, opts);
if (damage > 0)
CommonOutgoingHitSuccess(other, damage, skillinuse);
Log.Out(Logs::Detail, Logs::Combat, "Final damage after all reductions: %d", damage);
} else {
Log.Out(Logs::Detail, Logs::Combat, "Attack missed. Damage set to 0.");
damage = 0;
}
}
if (((damage < 0) || slippery_attack) && !bRiposte && !IsStrikethrough) { // Hack to still allow Strikethrough chance w/ Slippery Attacks AA
int32 bonusStrikeThrough = itembonuses.StrikeThrough + spellbonuses.StrikeThrough + aabonuses.StrikeThrough;
if(bonusStrikeThrough && zone->random.Roll(bonusStrikeThrough)) {
Message_StringID(MT_StrikeThrough, STRIKETHROUGH_STRING); // You strike through your opponents defenses!
Attack(other, Hand, false, true); // Strikethrough only gives another attempted hit
return false;
}
}
}
else{
} else {
damage = -5;
}
// Hate Generation is on a per swing basis, regardless of a hit, miss, or block, its always the same.
// If we are this far, this means we are atleast making a swing.
if (!bRiposte) // Ripostes never generate any aggro.
other->AddToHateList(this, hate);
other->AddToHateList(this, hate);
///////////////////////////////////////////////////////////
////// Send Attack Damage
///////////////////////////////////////////////////////////
if (damage > 0 && aabonuses.SkillAttackProc[0] && aabonuses.SkillAttackProc[1] == skillinuse &&
IsValidSpell(aabonuses.SkillAttackProc[2])) {
float chance = aabonuses.SkillAttackProc[0] / 1000.0f;
if (zone->random.Roll(chance))
SpellFinished(aabonuses.SkillAttackProc[2], other, 10, 0, -1,
spells[aabonuses.SkillAttackProc[2]].ResistDiff);
}
other->Damage(this, damage, SPELL_UNKNOWN, skillinuse);
if (IsDead()) return false;
@ -1905,21 +1906,18 @@ bool NPC::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool
hit_chance_bonus += opts->hit_chance;
}
if(!other->CheckHitChance(this, skillinuse, Hand, hit_chance_bonus)) {
damage = 0; //miss
} else { //hit, check for damage avoidance
other->AvoidDamage(this, damage);
other->MeleeMitigation(this, damage, min_dmg+eleBane, opts);
if(damage > 0) {
if (other->AvoidDamage(this, damage, Hand)) {
if (!bRiposte && damage == -3)
DoRiposte(other);
} else {
if (other->CheckHitChance(this, skillinuse, Hand, hit_chance_bonus)) {
other->MeleeMitigation(this, damage, min_dmg+eleBane, opts);
CommonOutgoingHitSuccess(other, damage, skillinuse);
} else {
damage = 0;
}
Log.Out(Logs::Detail, Logs::Combat, "Generating hate %d towards %s", hate, GetName());
// now add done damage to the hate list
if(damage > 0)
other->AddToHateList(this, hate);
else
other->AddToHateList(this, 0);
}
other->AddToHateList(this, hate);
}
Log.Out(Logs::Detail, Logs::Combat, "Final damage against %s: %d", other->GetName(), damage);
@ -1932,12 +1930,6 @@ bool NPC::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool
else
damage = -5;
//cant riposte a riposte
if (bRiposte && damage == -3) {
Log.Out(Logs::Detail, Logs::Combat, "Riposte of riposte canceled.");
return false;
}
if(GetHP() > 0 && !other->HasDied()) {
other->Damage(this, damage, SPELL_UNKNOWN, skillinuse, false); // Not avoidable client already had thier chance to Avoid
} else
@ -1967,11 +1959,6 @@ bool NPC::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool
if(GetHP() > 0 && !other->HasDied())
TriggerDefensiveProcs(nullptr, other, Hand, damage);
// now check ripostes
if (damage == -3) { // riposting
DoRiposte(other);
}
if (damage > 0)
return true;
@ -3419,41 +3406,39 @@ bool Mob::HasRangedProcs() const
return false;
}
bool Client::CheckDoubleAttack(bool tripleAttack) {
bool Client::CheckDoubleAttack()
{
int chance = 0;
int skill = GetSkill(SkillDoubleAttack);
//Check for bonuses that give you a double attack chance regardless of skill (ie Bestial Frenzy/Harmonious Attack AA)
uint32 bonusGiveDA = aabonuses.GiveDoubleAttack + spellbonuses.GiveDoubleAttack + itembonuses.GiveDoubleAttack;
if(!HasSkill(SkillDoubleAttack) && !bonusGiveDA)
int bonusGiveDA = aabonuses.GiveDoubleAttack + spellbonuses.GiveDoubleAttack + itembonuses.GiveDoubleAttack;
if (skill > 0)
chance = skill + GetLevel();
else if (!bonusGiveDA)
return false;
float chance = 0.0f;
if (bonusGiveDA)
chance += bonusGiveDA / 100.0f * 500; // convert to skill value
int per_inc = aabonuses.DoubleAttackChance + spellbonuses.DoubleAttackChance + itembonuses.DoubleAttackChance;
if (per_inc)
chance += chance * per_inc / 100;
uint16 skill = GetSkill(SkillDoubleAttack);
return zone->random.Int(1, 500) <= chance;
}
int32 bonusDA = aabonuses.DoubleAttackChance + spellbonuses.DoubleAttackChance + itembonuses.DoubleAttackChance;
// Admittedly these parses were short, but this check worked for 3 toons across multiple levels
// with varying triple attack skill (1-3% error at least)
bool Client::CheckTripleAttack()
{
int chance = GetSkill(SkillTripleAttack);
if (chance < 1)
return false;
//Use skill calculations otherwise, if you only have AA applied GiveDoubleAttack chance then use that value as the base.
if (skill)
chance = (float(skill+GetLevel()) * (float(100.0f+bonusDA+bonusGiveDA) /100.0f)) /500.0f;
else
chance = (float(bonusGiveDA) * (float(100.0f+bonusDA)/100.0f) ) /100.0f;
int per_inc = aabonuses.TripleAttackChance + spellbonuses.TripleAttackChance + itembonuses.TripleAttackChance;
if (per_inc)
chance += chance * per_inc / 100;
//Live now uses a static Triple Attack skill (lv 46 = 2% lv 60 = 20%) - We do not have this skill on EMU ATM.
//A reasonable forumla would then be TA = 20% * chance
//AA's can also give triple attack skill over cap. (ie Burst of Power) NOTE: Skill ID in spell data is 76 (Triple Attack)
//Kayen: Need to decide if we can implement triple attack skill before working in over the cap effect.
if(tripleAttack) {
// Only some Double Attack classes get Triple Attack [This is already checked in client_processes.cpp]
int32 triple_bonus = spellbonuses.TripleAttackChance + itembonuses.TripleAttackChance;
chance *= 0.2f; //Baseline chance is 20% of your double attack chance.
chance *= float(100.0f+triple_bonus)/100.0f; //Apply modifiers.
}
if(zone->random.Roll(chance))
return true;
return false;
return zone->random.Int(1, 1000) <= chance;
}
bool Client::CheckDoubleRangedAttack() {
@ -3465,6 +3450,21 @@ bool Client::CheckDoubleRangedAttack() {
return false;
}
bool Mob::CheckDoubleAttack()
{
// Not 100% certain pets follow this or if it's just from pets not always
// having the same skills as most mobs
int chance = GetSkill(SkillDoubleAttack);
if (GetLevel() > 35)
chance += GetLevel();
int per_inc = aabonuses.DoubleAttackChance + spellbonuses.DoubleAttackChance + itembonuses.DoubleAttackChance;
if (per_inc)
chance += chance * per_inc / 100;
return zone->random.Int(1, 500) <= chance;
}
void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, const SkillUseTypes skill_used, bool &avoidable, const int8 buffslot, const bool iBuffTic) {
// This method is called with skill_used=ABJURE for Damage Shield damage.
bool FromDamageShield = (skill_used == SkillAbjuration);
@ -3512,14 +3512,6 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons
if(damage > 0) {
//if there is some damage being done and theres an attacker involved
if(attacker) {
if(spell_id == SPELL_HARM_TOUCH2 && attacker->IsClient() && attacker->CastToClient()->CheckAAEffect(aaEffectLeechTouch)){
int healed = damage;
healed = attacker->GetActSpellHealing(spell_id, healed);
attacker->HealDamage(healed);
entity_list.MessageClose(this, true, 300, MT_Emote, "%s beams a smile at %s", attacker->GetCleanName(), this->GetCleanName() );
attacker->CastToClient()->DisableAAEffect(aaEffectLeechTouch);
}
// if spell is lifetap add hp to the caster
if (spell_id != SPELL_UNKNOWN && IsLifetapSpell( spell_id )) {
int healed = damage;
@ -3583,6 +3575,9 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons
SetHP(GetHP() - damage);
if (IsClient())
this->CastToClient()->SendHPUpdateMarquee();
if(HasDied()) {
bool IsSaved = false;
@ -3693,11 +3688,11 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons
//send an HP update if we are hurt
if(GetHP() < GetMaxHP())
SendHPUpdate(!iBuffTic); // the OP_Damage actually updates the client in these cases, so we skill them
SendHPUpdate(!iBuffTic); // the OP_Damage actually updates the client in these cases, so we skip the HP update for them
} //end `if damage was done`
//send damage packet...
if(!iBuffTic) { //buff ticks do not send damage, instead they just call SendHPUpdate(), which is done below
if(!iBuffTic) { //buff ticks do not send damage, instead they just call SendHPUpdate(), which is done above
EQApplicationPacket* outapp = new EQApplicationPacket(OP_Damage, sizeof(CombatDamage_Struct));
CombatDamage_Struct* a = (CombatDamage_Struct*)outapp->pBuffer;
a->target = GetID();
@ -4413,42 +4408,55 @@ bool Mob::TryFinishingBlow(Mob *defender, SkillUseTypes skillinuse)
return false;
}
void Mob::DoRiposte(Mob* defender) {
void Mob::DoRiposte(Mob *defender)
{
Log.Out(Logs::Detail, Logs::Combat, "Preforming a riposte");
if (!defender)
return;
defender->Attack(this, MainPrimary, true);
if (HasDied()) return;
if (HasDied())
return;
int32 DoubleRipChance = defender->aabonuses.GiveDoubleRiposte[0] +
defender->spellbonuses.GiveDoubleRiposte[0] +
defender->itembonuses.GiveDoubleRiposte[0];
// this effect isn't used on live? See no AAs or spells
int32 DoubleRipChance = defender->aabonuses.DoubleRiposte + defender->spellbonuses.DoubleRiposte +
defender->itembonuses.DoubleRiposte;
DoubleRipChance = defender->aabonuses.DoubleRiposte +
defender->spellbonuses.DoubleRiposte +
defender->itembonuses.DoubleRiposte;
//Live AA - Double Riposte
if(DoubleRipChance && zone->random.Roll(DoubleRipChance)) {
Log.Out(Logs::Detail, Logs::Combat, "Preforming a double riposed (%d percent chance)", DoubleRipChance);
if (DoubleRipChance && zone->random.Roll(DoubleRipChance)) {
Log.Out(Logs::Detail, Logs::Combat,
"Preforming a double riposted from SE_DoubleRiposte (%d percent chance)", DoubleRipChance);
defender->Attack(this, MainPrimary, true);
if (HasDied()) return;
if (HasDied())
return;
}
//Double Riposte effect, allows for a chance to do RIPOSTE with a skill specfic special attack (ie Return Kick).
//Coded narrowly: Limit to one per client. Limit AA only. [1 = Skill Attack Chance, 2 = Skill]
DoubleRipChance = defender->aabonuses.GiveDoubleRiposte[0] + defender->spellbonuses.GiveDoubleRiposte[0] +
defender->itembonuses.GiveDoubleRiposte[0];
// Live AA - Double Riposte
if (DoubleRipChance && zone->random.Roll(DoubleRipChance)) {
Log.Out(Logs::Detail, Logs::Combat,
"Preforming a double riposted from SE_GiveDoubleRiposte base1 == 0 (%d percent chance)",
DoubleRipChance);
defender->Attack(this, MainPrimary, true);
if (HasDied())
return;
}
// Double Riposte effect, allows for a chance to do RIPOSTE with a skill specific special attack (ie Return Kick).
// Coded narrowly: Limit to one per client. Limit AA only. [1 = Skill Attack Chance, 2 = Skill]
DoubleRipChance = defender->aabonuses.GiveDoubleRiposte[1];
if(DoubleRipChance && zone->random.Roll(DoubleRipChance)) {
Log.Out(Logs::Detail, Logs::Combat, "Preforming a return SPECIAL ATTACK (%d percent chance)", DoubleRipChance);
if (DoubleRipChance && zone->random.Roll(DoubleRipChance)) {
Log.Out(Logs::Detail, Logs::Combat, "Preforming a return SPECIAL ATTACK (%d percent chance)",
DoubleRipChance);
if (defender->GetClass() == MONK)
defender->MonkSpecialAttack(this, defender->aabonuses.GiveDoubleRiposte[2]);
else if (defender->IsClient())
defender->CastToClient()->DoClassAttacks(this,defender->aabonuses.GiveDoubleRiposte[2], true);
else if (defender->IsClient() && defender->CastToClient()->HasSkill((SkillUseTypes)defender->aabonuses.GiveDoubleRiposte[2]))
defender->CastToClient()->DoClassAttacks(this, defender->aabonuses.GiveDoubleRiposte[2], true);
}
}
@ -4640,7 +4648,7 @@ void Mob::TrySkillProc(Mob *on, uint16 skill, uint16 ReuseTime, bool Success, ui
if (IsClient() && aabonuses.LimitToSkill[skill]){
CanProc = true;
uint32 effect = 0;
uint32 effect_id = 0;
int32 base1 = 0;
int32 base2 = 0;
uint32 slot = 0;
@ -4659,36 +4667,41 @@ void Mob::TrySkillProc(Mob *on, uint16 skill, uint16 ReuseTime, bool Success, ui
proc_spell_id = 0;
ProcMod = 0;
std::map<uint32, std::map<uint32, AA_Ability> >::const_iterator find_iter = aa_effects.find(aaid);
if(find_iter == aa_effects.end())
break;
for(auto &rank_info : aa_ranks) {
auto ability_rank = zone->GetAlternateAdvancementAbilityAndRank(rank_info.first, rank_info.second.first);
auto ability = ability_rank.first;
auto rank = ability_rank.second;
for (std::map<uint32, AA_Ability>::const_iterator iter = aa_effects[aaid].begin(); iter != aa_effects[aaid].end(); ++iter) {
effect = iter->second.skill_id;
base1 = iter->second.base1;
base2 = iter->second.base2;
slot = iter->second.slot;
if (effect == SE_SkillProc || effect == SE_SkillProcSuccess) {
proc_spell_id = base1;
ProcMod = static_cast<float>(base2);
if(!ability) {
continue;
}
else if (effect == SE_LimitToSkill && base1 <= HIGHEST_SKILL) {
for(auto &effect : rank->effects) {
effect_id = effect.effect_id;
base1 = effect.base1;
base2 = effect.base2;
slot = effect.slot;
if (CanProc && base1 == skill && IsValidSpell(proc_spell_id)) {
float final_chance = chance * (ProcMod / 100.0f);
if(effect_id == SE_SkillProc || effect_id == SE_SkillProcSuccess) {
proc_spell_id = base1;
ProcMod = static_cast<float>(base2);
}
else if(effect_id == SE_LimitToSkill && base1 <= HIGHEST_SKILL) {
if (zone->random.Roll(final_chance)) {
ExecWeaponProc(nullptr, proc_spell_id, on);
CanProc = false;
break;
if (CanProc && base1 == skill && IsValidSpell(proc_spell_id)) {
float final_chance = chance * (ProcMod / 100.0f);
if (zone->random.Roll(final_chance)) {
ExecWeaponProc(nullptr, proc_spell_id, on);
CanProc = false;
break;
}
}
}
}
else {
proc_spell_id = 0;
ProcMod = 0;
else {
proc_spell_id = 0;
ProcMod = 0;
}
}
}
}
@ -4906,7 +4919,6 @@ void Client::SetAttackTimer()
attack_timer.SetAtTrigger(4000, true);
Timer *TimerToUse = nullptr;
const Item_Struct *PrimaryWeapon = nullptr;
for (int i = MainRange; i <= MainSecondary; i++) {
//pick a timer
@ -4928,19 +4940,8 @@ void Client::SetAttackTimer()
//special offhand stuff
if (i == MainSecondary) {
//if we have a 2H weapon in our main hand, no dual
if (PrimaryWeapon != nullptr) {
if (PrimaryWeapon->ItemClass == ItemClassCommon
&& (PrimaryWeapon->ItemType == ItemType2HSlash
|| PrimaryWeapon->ItemType == ItemType2HBlunt
|| PrimaryWeapon->ItemType == ItemType2HPiercing)) {
attack_dw_timer.Disable();
continue;
}
}
//if we cant dual wield, skip it
if (!CanThisClassDualWield()) {
if (!CanThisClassDualWield() || HasTwoHanderEquipped()) {
attack_dw_timer.Disable();
continue;
}
@ -4989,9 +4990,6 @@ void Client::SetAttackTimer()
if (quiver_haste > 0)
speed *= quiver_haste;
TimerToUse->SetAtTrigger(std::max(RuleI(Combat, MinHastedDelay), speed), true, true);
if (i == MainPrimary)
PrimaryWeapon = ItemToUse;
}
}
@ -5029,8 +5027,8 @@ void NPC::SetAttackTimer()
//special offhand stuff
if (i == MainSecondary) {
//NPCs get it for free at 13
if(GetLevel() < 13) {
// SPECATK_QUAD is uncheesable
if(!CanThisClassDualWield() || (HasTwoHanderEquipped() && !GetSpecialAbility(SPECATK_QUAD))) {
attack_dw_timer.Disable();
continue;
}
@ -5039,3 +5037,140 @@ void NPC::SetAttackTimer()
TimerToUse->SetAtTrigger(std::max(RuleI(Combat, MinHastedDelay), speed), true, true);
}
}
void Client::DoAttackRounds(Mob *target, int hand, bool IsFromSpell)
{
if (!target)
return;
Attack(target, hand, false, false, IsFromSpell);
bool candouble = CanThisClassDoubleAttack();
// extra off hand non-sense, can only double with skill of 150 or above
// or you have any amount of GiveDoubleAttack
if (candouble && hand == MainSecondary)
candouble = GetSkill(SkillDoubleAttack) > 149 || (aabonuses.GiveDoubleAttack + spellbonuses.GiveDoubleAttack + itembonuses.GiveDoubleAttack) > 0;
if (candouble) {
CheckIncreaseSkill(SkillDoubleAttack, target, -10);
if (CheckDoubleAttack()) {
Attack(target, hand, false, false, IsFromSpell);
// you can only triple from the main hand
if (hand == MainPrimary && CanThisClassTripleAttack()) {
CheckIncreaseSkill(SkillTripleAttack, target, -10);
if (CheckTripleAttack())
Attack(target, hand, false, false, IsFromSpell);
}
}
}
if (hand == MainPrimary) {
// According to http://www.monkly-business.net/forums/showpost.php?p=312095&postcount=168 a dev told them flurry isn't dependant on triple attack
// the parses kind of back that up and all of my parses seemed to be 4 or 5 attacks in the round which would work out to be
// doubles or triples with 2 from flurries or triple with 1 or 2 flurries ... Going with the "dev quote" I guess like we've always had it
auto flurrychance = aabonuses.FlurryChance + spellbonuses.FlurryChance + itembonuses.FlurryChance;
if (flurrychance && zone->random.Roll(flurrychance)) {
Attack(target, hand, false, false, IsFromSpell);
Attack(target, hand, false, false, IsFromSpell);
Message_StringID(MT_NPCFlurry, YOU_FLURRY);
}
// I haven't parsed where this guy happens, but it's not part of the normal chain above so this is fine
auto extraattackchance = aabonuses.ExtraAttackChance + spellbonuses.ExtraAttackChance + itembonuses.ExtraAttackChance;
if (extraattackchance && HasTwoHanderEquipped() && zone->random.Roll(extraattackchance))
Attack(target, hand, false, false, IsFromSpell);
}
}
bool Mob::CheckDualWield()
{
// Pets /might/ follow a slightly different progression
// although it could all be from pets having different skills than most mobs
int chance = GetSkill(SkillDualWield);
if (GetLevel() > 35)
chance += GetLevel();
chance += aabonuses.Ambidexterity + spellbonuses.Ambidexterity + itembonuses.Ambidexterity;
int per_inc = spellbonuses.DualWieldChance + aabonuses.DualWieldChance + itembonuses.DualWieldChance;
if (per_inc)
chance += chance * per_inc / 100;
return zone->random.Int(1, 375) <= chance;
}
bool Client::CheckDualWield()
{
int chance = GetSkill(SkillDualWield) + GetLevel();
chance += aabonuses.Ambidexterity + spellbonuses.Ambidexterity + itembonuses.Ambidexterity;
int per_inc = spellbonuses.DualWieldChance + aabonuses.DualWieldChance + itembonuses.DualWieldChance;
if (per_inc)
chance += chance * per_inc / 100;
return zone->random.Int(1, 375) <= chance;
}
void Mob::DoMainHandAttackRounds(Mob *target, ExtraAttackOptions *opts)
{
if (!target)
return;
if (RuleB(Combat, UseLiveCombatRounds)) {
// A "quad" on live really is just a successful dual wield where both double attack
// The mobs that could triple lost the ability to when the triple attack skill was added in
Attack(target, MainPrimary, false, false, false, opts);
if (CanThisClassDoubleAttack() && CheckDoubleAttack())
Attack(target, MainPrimary, false, false, false, opts);
return;
}
if (IsNPC()) {
int16 n_atk = CastToNPC()->GetNumberOfAttacks();
if (n_atk <= 1) {
Attack(target, MainPrimary, false, false, false, opts);
} else {
for (int i = 0; i < n_atk; ++i) {
Attack(target, MainPrimary, false, false, false, opts);
}
}
} else {
Attack(target, MainPrimary, false, false, false, opts);
}
// we use this random value in three comparisons with different
// thresholds, and if its truely random, then this should work
// out reasonably and will save us compute resources.
int32 RandRoll = zone->random.Int(0, 99);
if ((CanThisClassDoubleAttack() || GetSpecialAbility(SPECATK_TRIPLE) || GetSpecialAbility(SPECATK_QUAD))
// check double attack, this is NOT the same rules that clients use...
&&
RandRoll < (GetLevel() + NPCDualAttackModifier)) {
Attack(target, MainPrimary, false, false, false, opts);
// lets see if we can do a triple attack with the main hand
// pets are excluded from triple and quads...
if ((GetSpecialAbility(SPECATK_TRIPLE) || GetSpecialAbility(SPECATK_QUAD)) && !IsPet() &&
RandRoll < (GetLevel() + NPCTripleAttackModifier)) {
Attack(target, MainPrimary, false, false, false, opts);
// now lets check the quad attack
if (GetSpecialAbility(SPECATK_QUAD) && RandRoll < (GetLevel() + NPCQuadAttackModifier)) {
Attack(target, MainPrimary, false, false, false, opts);
}
}
}
}
void Mob::DoOffHandAttackRounds(Mob *target, ExtraAttackOptions *opts)
{
if (!target)
return;
// Mobs will only dual wield w/ the flag or have a secondary weapon
// For now, SPECATK_QUAD means innate DW when Combat:UseLiveCombatRounds is true
if ((GetSpecialAbility(SPECATK_INNATE_DW) ||
(RuleB(Combat, UseLiveCombatRounds) && GetSpecialAbility(SPECATK_QUAD))) ||
GetEquipment(MaterialSecondary) != 0) {
if (CheckDualWield()) {
Attack(target, MainSecondary, false, false, false, opts);
if (CanThisClassDoubleAttack() && GetLevel() > 35 && CheckDoubleAttack())
Attack(target, MainSecondary, false, false, false, opts);
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -169,7 +169,6 @@ public:
uint16 BotGetSpellType(int spellslot) { return AIspells[spellslot].type; }
uint16 BotGetSpellPriority(int spellslot) { return AIspells[spellslot].priority; }
virtual float GetProcChances(float ProcBonus, uint16 hand);
virtual bool AvoidDamage(Mob* other, int32 &damage, bool CanRiposte);
virtual int GetMonkHandToHandDamage(void);
virtual bool TryFinishingBlow(Mob *defender, SkillUseTypes skillinuse);
virtual void DoRiposte(Mob* defender);
@ -314,11 +313,12 @@ public:
virtual float GetAOERange(uint16 spell_id);
virtual bool SpellEffect(Mob* caster, uint16 spell_id, float partial = 100);
virtual void DoBuffTic(const Buffs_Struct &buff, int slot, Mob* caster = nullptr);
virtual bool CastSpell(uint16 spell_id, uint16 target_id, uint16 slot = USE_ITEM_SPELL_SLOT, int32 casttime = -1, int32 mana_cost = -1, uint32* oSpellWillFinish = 0, uint32 item_slot = 0xFFFFFFFF, int16 *resist_adjust = nullptr);
virtual bool CastSpell(uint16 spell_id, uint16 target_id, uint16 slot = USE_ITEM_SPELL_SLOT, int32 casttime = -1, int32 mana_cost = -1, uint32* oSpellWillFinish = 0,
uint32 item_slot = 0xFFFFFFFF, int16 *resist_adjust = nullptr, uint32 aa_id = 0);
virtual bool SpellOnTarget(uint16 spell_id, Mob* spelltar);
virtual bool IsImmuneToSpell(uint16 spell_id, Mob *caster);
virtual bool DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_center, CastAction_type &CastAction);
virtual bool DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot = USE_ITEM_SPELL_SLOT, int32 casttime = -1, int32 mana_cost = -1, uint32* oSpellWillFinish = 0, uint32 item_slot = 0xFFFFFFFF);
virtual bool DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot = USE_ITEM_SPELL_SLOT, int32 casttime = -1, int32 mana_cost = -1, uint32* oSpellWillFinish = 0, uint32 item_slot = 0xFFFFFFFF, uint32 aa_id = 0);
// Bot Action Command Methods
bool MesmerizeTarget(Mob* target);
@ -448,12 +448,8 @@ public:
bool IsBotWISCaster() { return (GetClass() == CLERIC || GetClass() == DRUID || GetClass() == SHAMAN); }
bool CanHeal();
int GetRawACNoShield(int &shield_ac);
void LoadAAs();
uint32 GetAA(uint32 aa_id);
void ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon);
bool GetHasBeenSummoned() { return _hasBeenSummoned; }
const glm::vec3 GetPreSummonLocation() const { return m_PreSummonLocation; }
bool GetGroupMessagesOn() { return _groupMessagesOn; }
bool GetInHealRotation() { return _isInHealRotation; }
bool GetHealRotationActive() { return (GetInHealRotation() && _isHealRotationActive); }
bool GetHealRotationUseFastHeals() { return _healRotationUseFastHeals; }
@ -539,7 +535,6 @@ public:
void SetDisciplineRecastTimer(int timer_index, int32 recast_delay);
void SetHasBeenSummoned(bool s);
void SetPreSummonLocation(const glm::vec3& location) { m_PreSummonLocation = location; }
void SetGroupMessagesOn(bool groupMessagesOn) { _groupMessagesOn = groupMessagesOn; }
void SetInHealRotation( bool inRotation ) { _isInHealRotation = inRotation; }
void SetHealRotationActive( bool isActive ) { _isHealRotationActive = isActive; }
void SetHealRotationUseFastHeals( bool useFastHeals ) { _healRotationUseFastHeals = useFastHeals; }
@ -552,6 +547,8 @@ public:
void SetNumHealRotationMembers( uint8 numMembers ) { _numHealRotationMembers = numMembers; }
void SetBardUseOutOfCombatSongs(bool useOutOfCombatSongs) { _bardUseOutOfCombatSongs = useOutOfCombatSongs;}
void SetShowHelm(bool showhelm) { _showhelm = showhelm; }
std::string CreateSayLink(Client* botOwner, const char* message, const char* name);
// Class Destructors
virtual ~Bot();
@ -564,7 +561,7 @@ protected:
virtual bool CheckBotDoubleAttack(bool Triple = false);
virtual int32 GetBotFocusEffect(BotfocusType bottype, uint16 spell_id);
virtual int32 CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spell_id, bool best_focus=false);
virtual int32 CalcBotAAFocus(BotfocusType type, uint32 aa_ID, uint16 spell_id);
virtual int32 CalcBotAAFocus(BotfocusType type, uint32 aa_ID, uint32 points, uint16 spell_id);
virtual void PerformTradeWithClient(int16 beginSlotID, int16 endSlotID, Client* client);
virtual bool AIDoSpellCast(uint8 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgainBefore = 0);
virtual float GetMaxMeleeRangeToTarget(Mob* target);
@ -608,7 +605,6 @@ private:
bool _hasBeenSummoned;
glm::vec3 m_PreSummonLocation;
uint8 _spellCastingChances[MaxStances][MaxSpellTypes];
bool _groupMessagesOn;
bool _isInHealRotation;
bool _isHealRotationActive;
bool _healRotationUseFastHeals;
@ -646,12 +642,12 @@ private:
uint8 _baseGender; // Bots gender. Necessary to preserve the original value otherwise it can be changed by illusions.
// Class Methods
void LoadAAs();
int32 acmod();
void GenerateBaseStats();
void GenerateAppearance();
void GenerateArmorClass();
int32 GenerateBaseHitPoints();
void GenerateAABonuses(StatBonuses* newbon);
int32 GenerateBaseManaPoints();
void GenerateSpecialAttacks();
void SetBotID(uint32 botID);

View File

@ -75,7 +75,7 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 iSpellTypes) {
MakeAnyLenString(&gmsg, "Attempting to mez %s.", addMob->GetCleanName());
if(gmsg && GetGroupMessagesOn())
if(gmsg)
BotGroupSay(this, gmsg);
}
}
@ -262,7 +262,7 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 iSpellTypes) {
}
}
if(gmsg && GetGroupMessagesOn())
if(gmsg)
BotGroupSay(this, gmsg);
}
}
@ -793,7 +793,7 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 iSpellTypes) {
MakeAnyLenString(&gmsg, "Attempting to slow %s.", tar->GetCleanName());
if(gmsg && GetGroupMessagesOn())
if(gmsg)
BotGroupSay(this, gmsg);
}
}

View File

@ -176,7 +176,6 @@ Client::Client(EQStreamInterface* ieqs)
admin = 0;
lsaccountid = 0;
shield_target = nullptr;
SQL_log = nullptr;
guild_id = GUILD_NONE;
guildrank = 0;
GuildBanker = false;
@ -198,6 +197,7 @@ Client::Client(EQStreamInterface* ieqs)
SetTarget(0);
auto_attack = false;
auto_fire = false;
runmode = false;
linkdead_timer.Disable();
zonesummon_id = 0;
zonesummon_ignorerestrictions = 0;
@ -440,7 +440,7 @@ void Client::SendZoneInPackets()
//Send AA Exp packet:
if (GetLevel() >= 51)
SendAAStats();
SendAlternateAdvancementStats();
// Send exp packets
outapp = new EQApplicationPacket(OP_ExpUpdate, sizeof(ExpUpdate_Struct));
@ -457,7 +457,7 @@ void Client::SendZoneInPackets()
}
safe_delete(outapp);
SendAATimers();
SendAlternateAdvancementTimers();
outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(ZoneInSendName_Struct));
ZoneInSendName_Struct* zonesendname = (ZoneInSendName_Struct*)outapp->pBuffer;
@ -524,48 +524,39 @@ void Client::ReportConnectingState() {
};
}
bool Client::SaveAA(){
int first_entry = 0;
std::string rquery;
/* Save Player AA */
bool Client::SaveAA() {
std::string iquery;
int spentpoints = 0;
for (int a = 0; a < MAX_PP_AA_ARRAY; a++) {
uint32 points = aa[a]->value;
if (points > HIGHEST_AA_VALUE) {
aa[a]->value = HIGHEST_AA_VALUE;
points = HIGHEST_AA_VALUE;
}
if (points > 0) {
SendAA_Struct* curAA = zone->FindAA(aa[a]->AA - aa[a]->value + 1);
if (curAA) {
for (int rank = 0; rank<points; rank++) {
std::map<uint32, AALevelCost_Struct>::iterator RequiredLevel = AARequiredLevelAndCost.find(aa[a]->AA - aa[a]->value + 1 + rank);
if (RequiredLevel != AARequiredLevelAndCost.end()) {
spentpoints += RequiredLevel->second.Cost;
}
else
spentpoints += (curAA->cost + (curAA->cost_inc * rank));
}
}
}
}
m_pp.aapoints_spent = spentpoints + m_epp.expended_aa;
int highest = 0;
for (int a = 0; a < MAX_PP_AA_ARRAY; a++) {
if (aa[a]->AA > 0) { // those with value 0 will be cleaned up on next load
if (first_entry != 1){
rquery = StringFormat("REPLACE INTO `character_alternate_abilities` (id, slot, aa_id, aa_value, charges)"
" VALUES (%u, %u, %u, %u, %u)", character_id, a, aa[a]->AA, aa[a]->value, aa[a]->charges);
first_entry = 1;
int i = 0;
for(auto &rank : aa_ranks) {
AA::Ability *ability = zone->GetAlternateAdvancementAbility(rank.first);
if(!ability)
continue;
if(rank.second.first > 0) {
AA::Rank *r = ability->GetRankByPointsSpent(rank.second.first);
if(!r)
continue;
spentpoints += r->total_cost;
if(i == 0) {
iquery = StringFormat("REPLACE INTO `character_alternate_abilities` (id, aa_id, aa_value, charges)"
" VALUES (%u, %u, %u, %u)", character_id, ability->first_rank_id, rank.second.first, rank.second.second);
} else {
rquery = rquery + StringFormat(", (%u, %u, %u, %u, %u)", character_id, a, aa[a]->AA, aa[a]->value, aa[a]->charges);
iquery += StringFormat(", (%u, %u, %u, %u)", character_id, ability->first_rank_id, rank.second.first, rank.second.second);
}
highest = a;
i++;
}
}
auto results = database.QueryDatabase(rquery);
/* This is another part of the hack to clean up holes left by expendable AAs */
rquery = StringFormat("DELETE FROM `character_alternate_abilities` WHERE `id` = %u AND `slot` > %d", character_id, highest);
m_pp.aapoints_spent = spentpoints + m_epp.expended_aa;
if(iquery.length() > 0) {
database.QueryDatabase(iquery);
}
return true;
}
@ -8040,56 +8031,6 @@ void Client::TryItemTimer(int slot)
}
}
void Client::RefundAA() {
int cur = 0;
bool refunded = false;
for(int x = 0; x < aaHighestID; x++) {
cur = GetAA(x);
if(cur > 0){
SendAA_Struct* curaa = zone->FindAA(x);
if(cur){
SetAA(x, 0);
for(int j = 0; j < cur; j++) {
m_pp.aapoints += curaa->cost + (curaa->cost_inc * j);
refunded = true;
}
}
else
{
m_pp.aapoints += cur;
SetAA(x, 0);
refunded = true;
}
}
}
if(refunded) {
SaveAA();
Save();
// Kick();
}
}
void Client::IncrementAA(int aa_id) {
SendAA_Struct* aa2 = zone->FindAA(aa_id);
if(aa2 == nullptr)
return;
if(GetAA(aa_id) == aa2->max_level)
return;
SetAA(aa_id, GetAA(aa_id) + 1);
SaveAA();
SendAA(aa_id);
SendAATable();
SendAAStats();
CalcBonuses();
}
void Client::SendItemScale(ItemInst *inst) {
int slot = m_inv.GetSlotByItemInst(inst);
if(slot != -1) {
@ -8638,3 +8579,16 @@ void Client::QuestReward(Mob* target, uint32 copper, uint32 silver, uint32 gold,
QueuePacket(outapp, false, Client::CLIENT_CONNECTED);
safe_delete(outapp);
}
void Client::SendHPUpdateMarquee(){
if (!RuleB(Character, MarqueeHPUpdates))
return;
/* Health Update Marquee Display: Custom*/
uint32 health_percentage = (uint32)(this->cur_hp * 100 / this->max_hp);
if (health_percentage == 100)
return;
std::string health_update_notification = StringFormat("Health: %u%%", health_percentage);
this->SendMarqueeMessage(15, 510, 0, 3000, 3000, health_update_notification);
}

View File

@ -45,7 +45,6 @@ struct Item_Struct;
#include "../common/item_struct.h"
#include "../common/clientversions.h"
#include "aa.h"
#include "common.h"
#include "merc.h"
#include "mob.h"
@ -227,6 +226,7 @@ public:
virtual int32 GetMeleeMitDmg(Mob *attacker, int32 damage, int32 minhit, float mit_rating, float atk_rating);
virtual void SetAttackTimer();
float GetQuiverHaste();
void DoAttackRounds(Mob *target, int hand, bool IsFromSpell = false);
void AI_Init();
void AI_Start(uint32 iMoveDelay = 0);
@ -541,7 +541,6 @@ public:
bool Flurry();
bool Rampage();
void DurationRampage(uint32 duration);
inline uint32 GetEXP() const { return m_pp.exp; }
@ -623,6 +622,7 @@ public:
inline uint32 AccountID() const { return account_id; }
inline const char* AccountName()const { return account_name; }
inline int GetAccountCreation() const { return account_creation; }
inline int16 Admin() const { return admin; }
inline uint32 CharacterID() const { return character_id; }
void UpdateAdmin(bool iFromDB = true);
@ -759,45 +759,35 @@ public:
inline PTimerList &GetPTimers() { return(p_timers); }
//AA Methods
void SendAAList();
void ResetAA();
void SendClearAA();
void SendAA(uint32 id, int seq=1);
void SendPreviousAA(uint32 id, int seq=1);
void BuyAA(AA_Action* action);
//this function is used by some AA stuff
void MemorizeSpell(uint32 slot,uint32 spellid,uint32 scribing);
void SetAATitle(const char *Title);
void SetTitleSuffix(const char *txt);
inline uint32 GetMaxAAXP(void) const { return max_AAXP; }
inline uint32 GetAAXP() const { return m_pp.expAA; }
void SendAAStats();
void SendAATable();
void SendAATimers();
int GetAATimerID(aaID activate);
int CalcAAReuseTimer(const AA_DBAction *caa);
void ActivateAA(aaID activate);
void SendAATimer(uint32 ability, uint32 begin, uint32 end);
void EnableAAEffect(aaEffectType type, uint32 duration = 0);
void DisableAAEffect(aaEffectType type);
bool CheckAAEffect(aaEffectType type);
void HandleAAAction(aaID activate);
uint32 GetAA(uint32 aa_id) const;
bool SetAA(uint32 aa_id, uint32 new_value);
inline uint32 GetAAPointsSpent() { return m_pp.aapoints_spent; }
int16 CalcAAFocusEffect(focusType type, uint16 focus_spell, uint16 spell_id);
int16 CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id);
void SetAAPoints(uint32 points) { m_pp.aapoints = points; SendAAStats(); }
void AddAAPoints(uint32 points) { m_pp.aapoints += points; SendAAStats(); }
//New AA Methods
void SendAlternateAdvancementRank(int aa_id, int level);
void SendAlternateAdvancementTable();
void SendAlternateAdvancementStats();
void PurchaseAlternateAdvancementRank(int rank_id);
bool GrantAlternateAdvancementAbility(int aa_id, int points, bool ignore_cost = false);
void IncrementAlternateAdvancementRank(int rank_id);
void ActivateAlternateAdvancementAbility(int rank_id, int target_id);
void SendAlternateAdvancementPoints();
void SendAlternateAdvancementTimer(int ability, int begin, int end);
void SendAlternateAdvancementTimers();
void ResetAlternateAdvancementTimer(int ability);
void ResetAlternateAdvancementTimers();
void SetAAPoints(uint32 points) { m_pp.aapoints = points; SendAlternateAdvancementStats(); }
void AddAAPoints(uint32 points) { m_pp.aapoints += points; SendAlternateAdvancementStats(); }
int GetAAPoints() { return m_pp.aapoints; }
int GetSpentAA() { return m_pp.aapoints_spent; }
//old AA methods that we still use
void ResetAA();
void RefundAA();
void IncrementAA(int aa_id);
int32 GetAAEffectDataBySlot(uint32 aa_ID, uint32 slot_id, bool GetEffect, bool GetBase1, bool GetBase2);
int32 GetAAEffectid(uint32 aa_ID, uint32 slot_id) { return GetAAEffectDataBySlot(aa_ID, slot_id, true, false,false); }
int32 GetAABase1(uint32 aa_ID, uint32 slot_id) { return GetAAEffectDataBySlot(aa_ID, slot_id, false, true,false); }
int32 GetAABase2(uint32 aa_ID, uint32 slot_id) { return GetAAEffectDataBySlot(aa_ID, slot_id, false, false,true); }
void SendClearAA();
inline uint32 GetMaxAAXP(void) const { return max_AAXP; }
inline uint32 GetAAXP() const { return m_pp.expAA; }
int16 CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id);
void SetAATitle(const char *Title);
void SetTitleSuffix(const char *txt);
void MemorizeSpell(uint32 slot, uint32 spellid, uint32 scribing);
int32 acmod();
// Item methods
@ -898,8 +888,10 @@ public:
bool CheckTradeLoreConflict(Client* other);
void LinkDead();
void Insight(uint32 t_id);
bool CheckDoubleAttack(bool tripleAttack = false);
bool CheckDoubleAttack();
bool CheckTripleAttack();
bool CheckDoubleRangedAttack();
bool CheckDualWield();
//remove charges/multiple objects from inventory:
//bool DecreaseByType(uint32 type, uint8 amt);
@ -1157,6 +1149,7 @@ public:
void RemoveAutoXTargets();
void ShowXTargets(Client *c);
bool GroupFollow(Client* inviter);
inline bool GetRunMode() const { return runmode; }
void InitializeMercInfo();
bool CheckCanSpawnMerc(uint32 template_id);
@ -1260,6 +1253,9 @@ public:
void QuestReward(Mob* target, uint32 copper = 0, uint32 silver = 0, uint32 gold = 0, uint32 platinum = 0, uint32 itemid = 0, uint32 exp = 0, bool faction = false);
void ResetHPUpdateTimer() { hpupdate_timer.Start(); }
void SendHPUpdateMarquee();
protected:
friend class Mob;
void CalcItemBonuses(StatBonuses* newbon);
@ -1267,8 +1263,6 @@ protected:
void AdditiveWornBonuses(const ItemInst *inst, StatBonuses* newbon, bool isAug = false);
int CalcRecommendedLevelBonus(uint8 level, uint8 reclevel, int basestat);
void CalcEdibleBonuses(StatBonuses* newbon);
void CalcAABonuses(StatBonuses* newbon);
void ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon);
void ProcessItemCaps();
void MakeBuffFadePacket(uint16 spell_id, int slot_id, bool send_message = true);
bool client_data_loaded;
@ -1276,6 +1270,8 @@ protected:
int16 GetFocusEffect(focusType type, uint16 spell_id);
uint16 GetSympatheticFocusEffect(focusType type, uint16 spell_id);
void FinishAlternateAdvancementPurchase(AA::Rank *rank, bool ignore_cost);
Mob* bind_sight_target;
glm::vec4 m_AutoAttackPosition;
@ -1385,6 +1381,7 @@ private:
bool AFK;
bool auto_attack;
bool auto_fire;
bool runmode;
uint8 gmspeed;
bool medding;
uint16 horseId;
@ -1492,11 +1489,7 @@ private:
uint32 tribute_master_id;
FILE *SQL_log;
uint32 max_AAXP;
uint32 staminacount;
AA_Array* aa[MAX_PP_AA_ARRAY]; //this list contains pointers into our player profile
std::map<uint32,uint8> aa_points;
bool npcflag;
uint8 npclevel;
bool feigned;

View File

@ -1121,7 +1121,7 @@ int32 Client::CalcMaxMana()
switch (GetCasterClass()) {
case 'I':
case 'W': {
max_mana = (CalcBaseMana() + itembonuses.Mana + spellbonuses.Mana + GroupLeadershipAAManaEnhancement());
max_mana = (CalcBaseMana() + itembonuses.Mana + spellbonuses.Mana + aabonuses.Mana + GroupLeadershipAAManaEnhancement());
break;
}
case 'N': {
@ -1978,7 +1978,18 @@ uint32 Mob::GetInstrumentMod(uint16 spell_id) const
return 10;
uint32 effectmod = 10;
int effectmodcap = RuleI(Character, BaseInstrumentSoftCap);
int effectmodcap = 0;
bool nocap = false;
if (RuleB(Character, UseSpellFileSongCap)) {
effectmodcap = spells[spell_id].songcap / 10;
// this looks a bit weird, but easiest way I could think to keep both systems working
if (effectmodcap == 0)
nocap = true;
else
effectmodcap += 10;
} else {
effectmodcap = RuleI(Character, BaseInstrumentSoftCap);
}
// this should never use spell modifiers...
// if a spell grants better modifers, they are copied into the item mods
// because the spells are supposed to act just like having the intrument.
@ -2048,10 +2059,11 @@ uint32 Mob::GetInstrumentMod(uint16 spell_id) const
effectmod = 10;
return effectmod;
}
effectmodcap += aabonuses.songModCap + spellbonuses.songModCap + itembonuses.songModCap;
if (!RuleB(Character, UseSpellFileSongCap))
effectmodcap += aabonuses.songModCap + spellbonuses.songModCap + itembonuses.songModCap;
if (effectmod < 10)
effectmod = 10;
if (effectmod > effectmodcap)
if (!nocap && effectmod > effectmodcap) // if the cap is calculated to be 0 using new rules, no cap.
effectmod = effectmodcap;
Log.Out(Logs::Detail, Logs::Spells, "%s::GetInstrumentMod() spell=%d mod=%d modcap=%d\n", GetName(), spell_id,
effectmod, effectmodcap);

View File

@ -389,6 +389,7 @@ void MapOpcodes()
ConnectedOpcodes[OP_XTargetRequest] = &Client::Handle_OP_XTargetRequest;
ConnectedOpcodes[OP_YellForHelp] = &Client::Handle_OP_YellForHelp;
ConnectedOpcodes[OP_ZoneChange] = &Client::Handle_OP_ZoneChange;
ConnectedOpcodes[OP_ResetAA] = &Client::Handle_OP_ResetAA;
}
void ClearMappedOpcode(EmuOpcode op)
@ -1082,7 +1083,7 @@ void Client::Handle_Connect_OP_ReqNewZone(const EQApplicationPacket *app)
void Client::Handle_Connect_OP_SendAAStats(const EQApplicationPacket *app)
{
SendAATimers();
SendAlternateAdvancementTimers();
EQApplicationPacket* outapp = new EQApplicationPacket(OP_SendAAStats, 0);
QueuePacket(outapp);
safe_delete(outapp);
@ -1091,7 +1092,7 @@ void Client::Handle_Connect_OP_SendAAStats(const EQApplicationPacket *app)
void Client::Handle_Connect_OP_SendAATable(const EQApplicationPacket *app)
{
SendAAList();
SendAlternateAdvancementTable();
return;
}
@ -1152,7 +1153,7 @@ void Client::Handle_Connect_OP_TGB(const EQApplicationPacket *app)
void Client::Handle_Connect_OP_UpdateAA(const EQApplicationPacket *app)
{
SendAATable();
SendAlternateAdvancementPoints();
}
void Client::Handle_Connect_OP_WearChange(const EQApplicationPacket *app)
@ -1439,58 +1440,15 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
if (m_pp.ldon_points_tak < 0 || m_pp.ldon_points_tak > 2000000000){ m_pp.ldon_points_tak = 0; }
if (m_pp.ldon_points_available < 0 || m_pp.ldon_points_available > 2000000000){ m_pp.ldon_points_available = 0; }
/* Initialize AA's : Move to function eventually */
for (uint32 a = 0; a < MAX_PP_AA_ARRAY; a++)
aa[a] = &m_pp.aa_array[a];
query = StringFormat(
"SELECT "
"slot, "
"aa_id, "
"aa_value, "
"charges "
"FROM "
"`character_alternate_abilities` "
"WHERE `id` = %u ORDER BY `slot`", this->CharacterID());
results = database.QueryDatabase(query); i = 0;
int offset = 0; // offset to fix the hole from expendables
for (auto row = results.begin(); row != results.end(); ++row) {
i = atoi(row[0]) - offset;
m_pp.aa_array[i].AA = atoi(row[1]);
m_pp.aa_array[i].value = atoi(row[2]);
m_pp.aa_array[i].charges = atoi(row[3]);
/* A used expendable could cause there to be a "hole" in the array, this is very bad. Bad things like keeping your expendable after use.
We could do a few things, one of them being reshuffling when the hole is created or defer the fixing until a later point, like during load!
Or just never making a hole in the array and just have hacks every where. Fixing the hole at load really just keeps 1 hack in Client::SendAATable
and keeping this offset that will cause the next AA to be pushed back over the hole. We also need to clean up on save so we don't have multiple
entries for a single AA.
*/
if (m_pp.aa_array[i].value == 0)
offset++;
if(RuleB(World, UseClientBasedExpansionSettings)) {
m_pp.expansions = ExpansionFromClientVersion(GetClientVersion());
}
else {
m_pp.expansions = RuleI(World, ExpansionSettings);
}
for (uint32 a = 0; a < MAX_PP_AA_ARRAY; a++){
uint32 id = aa[a]->AA;
//watch for invalid AA IDs
if (id == aaNone)
continue;
if (id >= aaHighestID) {
aa[a]->AA = aaNone;
aa[a]->value = 0;
continue;
}
if (aa[a]->value == 0) {
aa[a]->AA = aaNone;
continue;
}
if (aa[a]->value > HIGHEST_AA_VALUE) {
aa[a]->AA = aaNone;
aa[a]->value = 0;
continue;
}
if (aa[a]->value > 1) /* hack in some stuff for sony's new AA method (where each level of each aa.has a seperate ID) */
aa_points[(id - aa[a]->value + 1)] = aa[a]->value;
else
aa_points[id] = aa[a]->value;
if(!database.LoadAlternateAdvancement(this)) {
Log.Out(Logs::General, Logs::Error, "Error loading AA points for %s", GetName());
}
if (SPDAT_RECORDS > 0) {
@ -1615,11 +1573,6 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
/* Update LFP in case any (or all) of our group disbanded while we were zoning. */
if (IsLFP()) { UpdateLFP(); }
/* Get Expansions from variables table and ship via PP */
char val[20] = { 0 };
if (database.GetVariable("Expansions", val, 20)){ m_pp.expansions = atoi(val); }
else{ m_pp.expansions = 0x3FF; }
p_timers.SetCharID(CharacterID());
if (!p_timers.Load(&database)) {
Log.Out(Logs::General, Logs::Error, "Unable to load ability timers from the database for %s (%i)!", GetCleanName(), CharacterID());
@ -1794,38 +1747,39 @@ void Client::Handle_OP_AAAction(const EQApplicationPacket *app)
Log.Out(Logs::Detail, Logs::AA, "Received OP_AAAction");
if (app->size != sizeof(AA_Action)){
printf("Error! OP_AAAction size didnt match!\n");
Log.Out(Logs::General, Logs::AA, "Error! OP_AAAction size didnt match!");
return;
}
AA_Action* action = (AA_Action*)app->pBuffer;
if (action->action == aaActionActivate) {//AA Hotkey
Log.Out(Logs::Detail, Logs::AA, "Activating AA %d", action->ability);
ActivateAA((aaID)action->ability);
ActivateAlternateAdvancementAbility(action->ability, action->target_id);
}
else if (action->action == aaActionBuy) {
BuyAA(action);
PurchaseAlternateAdvancementRank(action->ability);
}
else if (action->action == aaActionDisableEXP){ //Turn Off AA Exp
if (m_epp.perAA > 0)
Message_StringID(0, AA_OFF);
m_epp.perAA = 0;
SendAAStats();
SendAlternateAdvancementStats();
}
else if (action->action == aaActionSetEXP) {
if (m_epp.perAA == 0)
Message_StringID(0, AA_ON);
m_epp.perAA = action->exp_value;
if (m_epp.perAA<0 || m_epp.perAA>100) m_epp.perAA = 0; // stop exploit with sanity check
if (m_epp.perAA < 0 || m_epp.perAA > 100)
m_epp.perAA = 0; // stop exploit with sanity check
// send an update
SendAAStats();
SendAATable();
SendAlternateAdvancementStats();
SendAlternateAdvancementTable();
}
else {
printf("Unknown AA action: %u %u 0x%x %d\n", action->action, action->ability, action->unknown08, action->exp_value);
Log.Out(Logs::General, Logs::AA, "Unknown AA action : %u %u %u %d", action->action, action->ability, action->target_id, action->exp_value);
}
return;
}
void Client::Handle_OP_AcceptNewTask(const EQApplicationPacket *app)
@ -3898,8 +3852,6 @@ void Client::Handle_OP_CastSpell(const EQApplicationPacket *app)
return;
}
m_TargetRing = glm::vec3(castspell->x_pos, castspell->y_pos, castspell->z_pos);
CastSpell(spell_to_cast, castspell->target_id, castspell->slot);
}
/* Spell Slot or Potion Belt Slot */
@ -6056,7 +6008,7 @@ void Client::Handle_OP_GMSearchCorpse(const EQApplicationPacket *app)
char *escSearchString = new char[129];
database.DoEscapeString(escSearchString, gmscs->Name, strlen(gmscs->Name));
std::string query = StringFormat("SELECT charname, zoneid, x, y, z, time_of_death, rezzed, IsBurried "
std::string query = StringFormat("SELECT charname, zoneid, x, y, z, time_of_death, is_rezzed, is_buried "
"FROM character_corpses WheRE charname LIKE '%%%s%%' ORDER BY charname LIMIT %i",
escSearchString, maxResults);
safe_delete_array(escSearchString);
@ -8852,7 +8804,7 @@ void Client::Handle_OP_LFGuild(const EQApplicationPacket *app)
pack->WriteUInt32(QSG_LFGuild_UpdatePlayerInfo);
pack->WriteUInt32(GetBaseClass());
pack->WriteUInt32(GetLevel());
pack->WriteUInt32(GetAAPointsSpent());
pack->WriteUInt32(GetSpentAA());
pack->WriteString(pts->Comment);
pack->WriteUInt32(pts->Toggle);
pack->WriteUInt32(pts->TimeZone);
@ -11836,6 +11788,18 @@ void Client::Handle_OP_SetGuildMOTD(const EQApplicationPacket *app)
void Client::Handle_OP_SetRunMode(const EQApplicationPacket *app)
{
if (app->size < sizeof(SetRunMode_Struct)) {
Log.Out(Logs::General, Logs::Error, "Received invalid sized "
"OP_SetRunMode: got %d, expected %d", app->size,
sizeof(SetRunMode_Struct));
DumpPacket(app);
return;
}
SetRunMode_Struct* rms = (SetRunMode_Struct*)app->pBuffer;
if (rms->mode)
runmode = true;
else
runmode = false;
return;
}
@ -14216,9 +14180,12 @@ void Client::Handle_OP_YellForHelp(const EQApplicationPacket *app)
return;
}
/*
void Client::Handle_OP_ZoneChange(const EQApplicationPacket *app)
void Client::Handle_OP_ResetAA(const EQApplicationPacket *app)
{
if(Admin() >= 50) {
Message(0, "Resetting AA points.");
ResetAA();
}
return;
}
*/

View File

@ -295,3 +295,4 @@
void Handle_OP_XTargetRequest(const EQApplicationPacket *app);
void Handle_OP_YellForHelp(const EQApplicationPacket *app);
void Handle_OP_ZoneChange(const EQApplicationPacket *app);
void Handle_OP_ResetAA(const EQApplicationPacket *app);

View File

@ -391,73 +391,12 @@ bool Client::Process() {
}
else if (auto_attack_target->GetHP() > -10) // -10 so we can watch people bleed in PvP
{
if(CheckAAEffect(aaEffectRampage))
{
entity_list.AEAttack(this, 30);
} else {
Attack(auto_attack_target, MainPrimary); // Kaiyodo - added attacking hand to arguments
}
ItemInst *wpn = GetInv().GetItem(MainPrimary);
TryWeaponProc(wpn, auto_attack_target, MainPrimary);
bool tripleAttackSuccess = false;
if( auto_attack_target && CanThisClassDoubleAttack() ) {
CheckIncreaseSkill(SkillDoubleAttack, auto_attack_target, -10);
if(CheckDoubleAttack()) {
//should we allow rampage on double attack?
if(CheckAAEffect(aaEffectRampage)) {
entity_list.AEAttack(this, 30);
} else {
Attack(auto_attack_target, MainPrimary, false);
}
}
//triple attack: rangers, monks, warriors, berserkers over level 60
if((((GetClass() == MONK || GetClass() == WARRIOR || GetClass() == RANGER || GetClass() == BERSERKER)
&& GetLevel() >= 60) || GetSpecialAbility(SPECATK_TRIPLE))
&& CheckDoubleAttack(true))
{
tripleAttackSuccess = true;
Attack(auto_attack_target, MainPrimary, false);
}
//quad attack, does this belong here??
if(GetSpecialAbility(SPECATK_QUAD) && CheckDoubleAttack(true))
{
Attack(auto_attack_target, MainPrimary, false);
}
}
//Live AA - Flurry, Rapid Strikes ect (Flurry does not require Triple Attack).
int16 flurrychance = aabonuses.FlurryChance + spellbonuses.FlurryChance + itembonuses.FlurryChance;
if (auto_attack_target && flurrychance)
{
if(zone->random.Int(0, 99) < flurrychance)
{
Message_StringID(MT_NPCFlurry, YOU_FLURRY);
Attack(auto_attack_target, MainPrimary, false);
Attack(auto_attack_target, MainPrimary, false);
}
}
int16 ExtraAttackChanceBonus = spellbonuses.ExtraAttackChance + itembonuses.ExtraAttackChance + aabonuses.ExtraAttackChance;
if (auto_attack_target && ExtraAttackChanceBonus) {
ItemInst *wpn = GetInv().GetItem(MainPrimary);
if(wpn){
if(wpn->GetItem()->ItemType == ItemType2HSlash ||
wpn->GetItem()->ItemType == ItemType2HBlunt ||
wpn->GetItem()->ItemType == ItemType2HPiercing )
{
if(zone->random.Int(0, 99) < ExtraAttackChanceBonus)
{
Attack(auto_attack_target, MainPrimary, false);
}
}
}
}
DoAttackRounds(auto_attack_target, MainPrimary);
if (CheckAATimer(aaTimerRampage))
entity_list.AEAttack(this, 30);
}
}
@ -489,32 +428,12 @@ bool Client::Process() {
//you can't see your target
}
else if(auto_attack_target->GetHP() > -10) {
float DualWieldProbability = 0.0f;
int16 Ambidexterity = aabonuses.Ambidexterity + spellbonuses.Ambidexterity + itembonuses.Ambidexterity;
DualWieldProbability = (GetSkill(SkillDualWield) + GetLevel() + Ambidexterity) / 400.0f; // 78.0 max
int16 DWBonus = spellbonuses.DualWieldChance + itembonuses.DualWieldChance;
DualWieldProbability += DualWieldProbability*float(DWBonus)/ 100.0f;
float random = zone->random.Real(0, 1);
CheckIncreaseSkill(SkillDualWield, auto_attack_target, -10);
if (random < DualWieldProbability){ // Max 78% of DW
if(CheckAAEffect(aaEffectRampage)) {
entity_list.AEAttack(this, 30, MainSecondary);
} else {
Attack(auto_attack_target, MainSecondary); // Single attack with offhand
}
if (CheckDualWield()) {
ItemInst *wpn = GetInv().GetItem(MainSecondary);
TryWeaponProc(wpn, auto_attack_target, MainSecondary);
if( CanThisClassDoubleAttack() && CheckDoubleAttack()) {
if(CheckAAEffect(aaEffectRampage)) {
entity_list.AEAttack(this, 30, MainSecondary);
} else {
if(auto_attack_target && auto_attack_target->GetHP() > -10)
Attack(auto_attack_target, MainSecondary); // Single attack with offhand
}
}
DoAttackRounds(auto_attack_target, MainSecondary);
}
}
}
@ -522,9 +441,7 @@ bool Client::Process() {
if (position_timer.Check()) {
if (IsAIControlled())
{
if(IsMoving())
SendPosUpdate(2);
else
if(!IsMoving())
{
animation = 0;
m_Delta = glm::vec4(0.0f, 0.0f, 0.0f, m_Delta.w);

View File

@ -147,7 +147,7 @@ Access Levels:
*/
int command_init(void) {
if
(
@ -168,10 +168,10 @@ int command_init(void) {
command_add("aggro", "(range) [-v] - Display aggro information for all mobs 'range' distance from your target. -v is verbose faction info.", 80, command_aggro) ||
command_add("aggrozone", "[aggro] - Aggro every mob in the zone with X aggro. Default is 0. Not recommend if you're not invulnerable.", 100, command_aggrozone) ||
command_add("ai", "[factionid/spellslist/con/guard/roambox/stop/start] - Modify AI on NPC target", 100, command_ai) ||
command_add("altactivate", "[argument] - activates alternate advancement abilities, use altactivate help for more information", 0, command_altactivate) ||
command_add("appearance", "[type] [value] - Send an appearance packet for you or your target", 150, command_appearance) ||
command_add("attack", "[targetname] - Make your NPC target attack targetname", 150, command_attack) ||
command_add("augmentitem", "Force augments an item. Must have the augment item window open.", 250, command_augmentitem) ||
command_add("aug", nullptr, 250, command_augmentitem) ||
command_add("ban", "[name] [reason]- Ban by character name", 150, command_ban) ||
command_add("beard", "- Change the beard of your target", 80, command_beard) ||
command_add("beardcolor", "- Change the beard color of your target", 80, command_beardcolor) ||
@ -218,11 +218,14 @@ int command_init(void) {
command_add("flagedit", "- Edit zone flags on your target", 100, command_flagedit) ||
command_add("flags", "- displays the flags of you or your target", 0, command_flags) ||
command_add("flymode", "[0/1/2] - Set your or your player target's flymode to off/on/levitate", 50, command_flymode) ||
command_add("fn", nullptr, 100, command_findnpctype) ||
command_add("fov", "- Check wether you're behind or in your target's field of view", 80, command_fov) ||
command_add("freeze", "- Freeze your target", 80, command_freeze) ||
command_add("fs", nullptr, 50, command_findspell) ||
command_add("fz", nullptr, 100, command_findzone) ||
command_add("gassign", "[id] - Assign targetted NPC to predefined wandering grid id", 100, command_gassign) ||
command_add("gender", "[0/1/2] - Change your or your target's gender to male/female/neuter", 50, command_gender) ||
command_add("getplayerburriedcorpsecount", "- Get the target's total number of burried player corpses.", 100, command_getplayerburriedcorpsecount) ||
command_add("getplayerburiedcorpsecount", "- Get the target's total number of buried player corpses.", 100, command_getplayerburiedcorpsecount) ||
command_add("getvariable", "[varname] - Get the value of a variable from the database", 200, command_getvariable) ||
command_add("gi", nullptr,200, command_giveitem) ||
command_add("ginfo", "- get group info on target.", 20, command_ginfo) ||
@ -275,7 +278,6 @@ int command_init(void) {
command_add("los", nullptr,0, command_checklos) ||
command_add("makepet", "[level] [class] [race] [texture] - Make a pet", 50, command_makepet) ||
command_add("mana", "- Fill your or your target's mana", 50, command_mana) ||
command_add("manaburn", "- Use AA Wizard class skill manaburn on target", 10, command_manaburn) ||
command_add("maxskills", "Maxes skills for you.", 200, command_max_all_skills) ||
command_add("memspell", "[slotid] [spellid] - Memorize spellid in the specified slot", 50, command_memspell) ||
command_add("merchant_close_shop", "Closes a merchant shop", 100, command_merchantcloseshop) ||
@ -324,6 +326,7 @@ int command_init(void) {
command_add("raidloot", "LEADER|GROUPLEADER|SELECTED|ALL - Sets your raid loot settings if you have permission to do so.", 0, command_raidloot) ||
command_add("randomfeatures", "- Temporarily randomizes the Facial Features of your target", 80, command_randomfeatures) ||
command_add("refreshgroup", "- Refreshes Group.", 0, command_refreshgroup) ||
command_add("reloadaa", "Reloads AA data", 200, command_reloadaa) ||
command_add("reloadallrules", "Executes a reload of all rules.", 80, command_reloadallrules) ||
command_add("reloademote", "Reloads NPC Emotes", 80, command_reloademote) ||
command_add("reloadlevelmods", nullptr,255, command_reloadlevelmods) ||
@ -336,8 +339,10 @@ int command_init(void) {
command_add("reloadzonepoints", "- Reload zone points from database", 150, command_reloadzps) ||
command_add("reloadzps", nullptr,0, command_reloadzps) ||
command_add("repop", "[delay] - Repop the zone with optional delay", 100, command_repop) ||
command_add("resetaa", "- Resets a Player's AA in their profile and refunds spent AA's to unspent, disconnects player.", 200, command_resetaa) ||
command_add("resetaa", "- Resets a Player's AA in their profile and refunds spent AA's to unspent, may disconnect player.", 200, command_resetaa) ||
command_add("resetaa_timer", "Command to reset AA cooldown timers.", 200, command_resetaa_timer) ||
command_add("revoke", "[charname] [1/0] - Makes charname unable to talk on OOC", 200, command_revoke) ||
command_add("rq", nullptr, 150, command_reloadqst) ||
command_add("rules", "(subcommand) - Manage server rules", 250, command_rules) ||
command_add("save", "- Force your player or player corpse target to be saved to the database", 50, command_save) ||
command_add("scribespell", "[spellid] - Scribe specified spell in your target's spell book.", 180, command_scribespell) ||
@ -386,7 +391,7 @@ int command_init(void) {
command_add("spon", "- Sends OP_MemorizeSpell", 80, command_spon) ||
command_add("stun", "[duration] - Stuns you or your target for duration", 100, command_stun) ||
command_add("summon", "[charname] - Summons your player/npc/corpse target, or charname if specified", 80, command_summon) ||
command_add("summonburriedplayercorpse", "- Summons the target's oldest burried corpse, if any exist.", 100, command_summonburriedplayercorpse) ||
command_add("summonburiedplayercorpse", "- Summons the target's oldest buried corpse, if any exist.", 100, command_summonburiedplayercorpse) ||
command_add("summonitem", "[itemid] [charges] - Summon an item onto your cursor. Charges are optional.", 200, command_summonitem) ||
command_add("suspend", "[name] [days] [reason] - Suspend by character name and for specificed number of days", 150, command_suspend) ||
command_add("task", "(subcommand) - Task system commands", 150, command_task) ||
@ -431,7 +436,7 @@ int command_init(void) {
command_add("zsave", " - Saves zheader to the database", 80, command_zsave) ||
command_add("zsky", "[skytype] - Change zone sky type", 80, command_zsky) ||
command_add("zstats", "- Show info about zone header", 80, command_zstats) ||
command_add("zunderworld", "[zcoord] - Sets the underworld using zcoord", 80, command_zunderworld) ||
command_add("zunderworld", "[zcoord] - Sets the underworld using zcoord", 80, command_zunderworld) ||
command_add("zuwcoords", "[z coord] - Set underworld coord", 80, command_zuwcoords)
)
{
@ -641,8 +646,8 @@ void command_logcommand(Client *c, const char *message)
c->AccountName(),
c->AccountID(),
admin,c->GetName(),
c->GetTarget()?c->GetTarget()->GetName():"None",
"Command",
c->GetTarget()?c->GetTarget()->GetName():"None",
"Command",
message,
1
);
@ -675,8 +680,8 @@ void command_incstat(Client* c, const Seperator* sep){
}
}
void command_resetaa(Client* c,const Seperator *sep){
if(c->GetTarget()!=0 && c->GetTarget()->IsClient()){
void command_resetaa(Client* c,const Seperator *sep) {
if(c->GetTarget() && c->GetTarget()->IsClient()){
c->GetTarget()->CastToClient()->ResetAA();
c->Message(13,"Successfully reset %s's AAs", c->GetTarget()->GetName());
}
@ -728,7 +733,7 @@ void command_setfaction(Client *c, const Seperator *sep)
auto npcTypeID = c->GetTarget()->CastToNPC()->GetNPCTypeID();
c->Message(15,"Setting NPC %u to faction %i", npcTypeID, atoi(sep->argplus[1]));
std::string query = StringFormat("UPDATE npc_types SET npc_faction_id = %i WHERE id = %i",
std::string query = StringFormat("UPDATE npc_types SET npc_faction_id = %i WHERE id = %i",
atoi(sep->argplus[1]), npcTypeID);
database.QueryDatabase(query);
}
@ -1486,7 +1491,7 @@ void command_npcstats(Client *c, const Seperator *sep)
c->Message(0, "Current HP: %i Max HP: %i", c->GetTarget()->GetHP(), c->GetTarget()->GetMaxHP());
//c->Message(0, "Weapon Item Number: %s", c->GetTarget()->GetWeapNo());
c->Message(0, "Gender: %i Size: %f Bodytype: %d", c->GetTarget()->GetGender(), c->GetTarget()->GetSize(), c->GetTarget()->GetBodyType());
c->Message(0, "Runspeed: %i Walkspeed: %i", c->GetTarget()->GetRunspeed(), c->GetTarget()->GetWalkspeed());
c->Message(0, "Runspeed: %.3f Walkspeed: %.3f", static_cast<float>(0.025f * c->GetTarget()->GetRunspeed()), static_cast<float>(0.025f * c->GetTarget()->GetWalkspeed()));
c->Message(0, "Spawn Group: %i Grid: %i", c->GetTarget()->CastToNPC()->GetSp2(), c->GetTarget()->CastToNPC()->GetGrid());
c->Message(0, "EmoteID: %i", c->GetTarget()->CastToNPC()->GetEmoteID());
c->GetTarget()->CastToNPC()->QueryLoot(c);
@ -2299,7 +2304,7 @@ void command_setskill(Client *c, const Seperator *sep)
Log.Out(Logs::General, Logs::Normal, "Set skill request from %s, target:%s skill_id:%i value:%i", c->GetName(), c->GetTarget()->GetName(), atoi(sep->arg[1]), atoi(sep->arg[2]) );
int skill_num = atoi(sep->arg[1]);
uint16 skill_value = atoi(sep->arg[2]);
if(skill_num < HIGHEST_SKILL)
if(skill_num <= HIGHEST_SKILL)
c->GetTarget()->CastToClient()->SetSkill((SkillUseTypes)skill_num, skill_value);
}
}
@ -2570,7 +2575,7 @@ void command_peekinv(Client *c, const Seperator *sep)
item_link = linker.GenerateLink();
c->Message((item_data == nullptr), "WornSlot: %i, Item: %i (%s), Charges: %i",
c->Message((item_data == nullptr), "WornSlot: %i, Item: %i (%s), Charges: %i",
indexMain, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges()));
}
@ -2581,7 +2586,7 @@ void command_peekinv(Client *c, const Seperator *sep)
item_link = linker.GenerateLink();
c->Message((item_data == nullptr), "WornSlot: %i, Item: %i (%s), Charges: %i",
c->Message((item_data == nullptr), "WornSlot: %i, Item: %i (%s), Charges: %i",
MainPowerSource, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges()));
}
@ -2593,7 +2598,7 @@ void command_peekinv(Client *c, const Seperator *sep)
item_link = linker.GenerateLink();
c->Message((item_data == nullptr), "InvSlot: %i, Item: %i (%s), Charges: %i",
c->Message((item_data == nullptr), "InvSlot: %i, Item: %i (%s), Charges: %i",
indexMain, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges()));
for (uint8 indexSub = SUB_BEGIN; inst_main && inst_main->IsType(ItemClassContainer) && (indexSub < EmuConstants::ITEM_CONTAINER_SIZE); ++indexSub) {
@ -2603,7 +2608,7 @@ void command_peekinv(Client *c, const Seperator *sep)
item_link = linker.GenerateLink();
c->Message((item_data == nullptr), " InvBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i",
c->Message((item_data == nullptr), " InvBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i",
Inventory::CalcSlotId(indexMain, indexSub), indexMain, indexSub, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges()));
}
}
@ -2615,7 +2620,7 @@ void command_peekinv(Client *c, const Seperator *sep)
item_link = linker.GenerateLink();
c->Message(1, "CursorSlot: %i, Item: %i (%s), Charges: %i",
c->Message(1, "CursorSlot: %i, Item: %i (%s), Charges: %i",
MainCursor, 0, item_link.c_str(), 0);
}
else {
@ -2627,7 +2632,7 @@ void command_peekinv(Client *c, const Seperator *sep)
item_link = linker.GenerateLink();
c->Message((item_data == nullptr), "CursorSlot: %i, Depth: %i, Item: %i (%s), Charges: %i",
c->Message((item_data == nullptr), "CursorSlot: %i, Depth: %i, Item: %i (%s), Charges: %i",
MainCursor, cursorDepth, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges()));
for (uint8 indexSub = SUB_BEGIN; (cursorDepth == 0) && inst_main && inst_main->IsType(ItemClassContainer) && (indexSub < EmuConstants::ITEM_CONTAINER_SIZE); ++indexSub) {
@ -2637,7 +2642,7 @@ void command_peekinv(Client *c, const Seperator *sep)
item_link = linker.GenerateLink();
c->Message((item_data == nullptr), " CursorBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i",
c->Message((item_data == nullptr), " CursorBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i",
Inventory::CalcSlotId(MainCursor, indexSub), MainCursor, indexSub, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges()));
}
}
@ -2652,7 +2657,7 @@ void command_peekinv(Client *c, const Seperator *sep)
item_link = linker.GenerateLink();
c->Message((item_data == nullptr), "TributeSlot: %i, Item: %i (%s), Charges: %i",
c->Message((item_data == nullptr), "TributeSlot: %i, Item: %i (%s), Charges: %i",
indexMain, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges()));
}
@ -2664,7 +2669,7 @@ void command_peekinv(Client *c, const Seperator *sep)
item_link = linker.GenerateLink();
c->Message((item_data == nullptr), "BankSlot: %i, Item: %i (%s), Charges: %i",
c->Message((item_data == nullptr), "BankSlot: %i, Item: %i (%s), Charges: %i",
indexMain, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges()));
for (uint8 indexSub = SUB_BEGIN; inst_main && inst_main->IsType(ItemClassContainer) && (indexSub < EmuConstants::ITEM_CONTAINER_SIZE); ++indexSub) {
@ -2674,7 +2679,7 @@ void command_peekinv(Client *c, const Seperator *sep)
item_link = linker.GenerateLink();
c->Message((item_data == nullptr), " BankBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i",
c->Message((item_data == nullptr), " BankBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i",
Inventory::CalcSlotId(indexMain, indexSub), indexMain, indexSub, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges()));
}
}
@ -2685,8 +2690,8 @@ void command_peekinv(Client *c, const Seperator *sep)
linker.SetItemInst(inst_main);
item_link = linker.GenerateLink();
c->Message((item_data == nullptr), "SharedBankSlot: %i, Item: %i (%s), Charges: %i",
c->Message((item_data == nullptr), "SharedBankSlot: %i, Item: %i (%s), Charges: %i",
indexMain, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges()));
for (uint8 indexSub = SUB_BEGIN; inst_main && inst_main->IsType(ItemClassContainer) && (indexSub < EmuConstants::ITEM_CONTAINER_SIZE); ++indexSub) {
@ -2696,7 +2701,7 @@ void command_peekinv(Client *c, const Seperator *sep)
item_link = linker.GenerateLink();
c->Message((item_data == nullptr), " SharedBankBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i",
c->Message((item_data == nullptr), " SharedBankBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i",
Inventory::CalcSlotId(indexMain, indexSub), indexMain, indexSub, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges()));
}
}
@ -2709,7 +2714,7 @@ void command_peekinv(Client *c, const Seperator *sep)
item_link = linker.GenerateLink();
c->Message((item_data == nullptr), "TradeSlot: %i, Item: %i (%s), Charges: %i",
c->Message((item_data == nullptr), "TradeSlot: %i, Item: %i (%s), Charges: %i",
indexMain, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges()));
for (uint8 indexSub = SUB_BEGIN; inst_main && inst_main->IsType(ItemClassContainer) && (indexSub < EmuConstants::ITEM_CONTAINER_SIZE); ++indexSub) {
@ -2719,7 +2724,7 @@ void command_peekinv(Client *c, const Seperator *sep)
item_link = linker.GenerateLink();
c->Message((item_data == nullptr), " TradeBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i",
c->Message((item_data == nullptr), " TradeBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i",
Inventory::CalcSlotId(indexMain, indexSub), indexMain, indexSub, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges()));
}
}
@ -2741,7 +2746,7 @@ void command_peekinv(Client *c, const Seperator *sep)
item_link = linker.GenerateLink();
c->Message((item_data == nullptr), "WorldSlot: %i, Item: %i (%s), Charges: %i",
c->Message((item_data == nullptr), "WorldSlot: %i, Item: %i (%s), Charges: %i",
(EmuConstants::WORLD_BEGIN + indexMain), ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges()));
for (uint8 indexSub = SUB_BEGIN; inst_main && inst_main->IsType(ItemClassContainer) && (indexSub < EmuConstants::ITEM_CONTAINER_SIZE); ++indexSub) {
@ -2751,7 +2756,7 @@ void command_peekinv(Client *c, const Seperator *sep)
item_link = linker.GenerateLink();
c->Message((item_data == nullptr), " WorldBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i",
c->Message((item_data == nullptr), " WorldBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i",
INVALID_INDEX, indexMain, indexSub, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges()));
}
}
@ -3749,7 +3754,7 @@ void command_spellinfo(Client *c, const Seperator *sep)
c->Message(0, " zonetype: %d", s->zonetype);
c->Message(0, " EnvironmentType: %d", s->EnvironmentType);
c->Message(0, " TimeOfDay: %d", s->TimeOfDay);
c->Message(0, " classes[15]: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d",
c->Message(0, " classes[15]: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d",
s->classes[0], s->classes[1], s->classes[2], s->classes[3], s->classes[4],
s->classes[5], s->classes[6], s->classes[7], s->classes[8], s->classes[9],
s->classes[10], s->classes[11], s->classes[12], s->classes[13], s->classes[14]);
@ -3879,7 +3884,7 @@ void command_repop(Client *c, const Seperator *sep)
LinkedListIterator<Spawn2*> iterator(zone->spawn2_list);
iterator.Reset();
while (iterator.MoreElements()) {
std::string query = StringFormat("DELETE FROM respawn_times WHERE id = %lu AND instance_id = %lu",
std::string query = StringFormat("DELETE FROM respawn_times WHERE id = %lu AND instance_id = %lu",
(unsigned long)iterator.GetData()->GetID(),
(unsigned long)zone->GetInstanceID());
auto results = database.QueryDatabase(query);
@ -4253,7 +4258,7 @@ void command_spawnfix(Client *c, const Seperator *sep) {
return;
}
std::string query = StringFormat("UPDATE spawn2 SET x = '%f', y = '%f', z = '%f', heading = '%f' WHERE id = '%i'",
std::string query = StringFormat("UPDATE spawn2 SET x = '%f', y = '%f', z = '%f', heading = '%f' WHERE id = '%i'",
c->GetX(), c->GetY(), c->GetZ(), c->GetHeading(),s2->GetID());
auto results = database.QueryDatabase(query);
if (!results.Success()) {
@ -4396,11 +4401,11 @@ void command_time(Client *c, const Seperator *sep)
c->Message(13, "To set the Time: #time HH [MM]");
TimeOfDay_Struct eqTime;
zone->zone_time.GetCurrentEQTimeOfDay( time(0), &eqTime);
sprintf(timeMessage,"%02d:%s%d %s (Timezone: %ih %im)",
sprintf(timeMessage,"%02d:%s%d %s (Timezone: %ih %im)",
((eqTime.hour - 1) % 12) == 0 ? 12 : ((eqTime.hour - 1) % 12),
(eqTime.minute < 10) ? "0" : "",
(eqTime.minute < 10) ? "0" : "",
eqTime.minute,
(eqTime.hour >= 13) ? "pm" : "am",
(eqTime.hour >= 13) ? "pm" : "am",
zone->zone_time.getEQTimeZoneHr(),
zone->zone_time.getEQTimeZoneMin()
);
@ -4842,36 +4847,6 @@ void command_zonestatus(Client *c, const Seperator *sep)
}
}
void command_manaburn(Client *c, const Seperator *sep)
{
Mob* target=c->GetTarget();
if (c->GetTarget() == 0)
c->Message(0, "#Manaburn needs a target.");
else {
int cur_level=c->GetAA(MANA_BURN);//ManaBurn ID
if (DistanceSquared(c->GetPosition(), target->GetPosition()) > 200)
c->Message(0,"You are too far away from your target.");
else {
if(cur_level == 1) {
if(c->IsAttackAllowed(target))
{
c->SetMana(0);
int nukedmg=(c->GetMana())*2;
if (nukedmg>0)
{
target->Damage(c, nukedmg, 2751, SkillAbjuration/*hackish*/);
c->Message(4,"You unleash an enormous blast of magical energies.");
}
Log.Out(Logs::General, Logs::Normal, "Manaburn request from %s, damage: %d", c->GetName(), nukedmg);
}
}
else
c->Message(0, "You have not learned this skill.");
}
}
}
void command_doanim(Client *c, const Seperator *sep)
{
if (!sep->IsNumber(1))
@ -5586,16 +5561,23 @@ void command_setaapts(Client *c, const Seperator *sep)
if(sep->arg[1][0] == '\0' || sep->arg[2][0] == '\0')
c->Message(0, "Usage: #setaapts <AA|group|raid> <new AA points value>");
else if(atoi(sep->arg[2]) <= 0 || atoi(sep->arg[2]) > 200)
c->Message(0, "You must have a number greater than 0 for points and no more than 200.");
else if(atoi(sep->arg[2]) <= 0 || atoi(sep->arg[2]) > 5000)
c->Message(0, "You must have a number greater than 0 for points and no more than 5000.");
else if(!strcasecmp(sep->arg[1], "group")) {
t->SetLeadershipEXP(atoi(sep->arg[2])*GROUP_EXP_PER_POINT, t->GetRaidEXP());
t->GetPP().group_leadership_points = atoi(sep->arg[2]);
t->GetPP().group_leadership_exp = 0;
t->Message(MT_Experience, "Setting Group AA points to %u", t->GetPP().group_leadership_points);
t->SendLeadershipEXPUpdate();
} else if(!strcasecmp(sep->arg[1], "raid")) {
t->SetLeadershipEXP(t->GetGroupEXP(), atoi(sep->arg[2])*RAID_EXP_PER_POINT);
t->GetPP().raid_leadership_points = atoi(sep->arg[2]);
t->GetPP().raid_leadership_exp = 0;
t->Message(MT_Experience, "Setting Raid AA points to %u", t->GetPP().raid_leadership_points);
t->SendLeadershipEXPUpdate();
} else {
t->SetEXP(t->GetEXP(),t->GetMaxAAXP()*atoi(sep->arg[2]),false);
t->SendAAStats();
t->SendAATable();
t->GetPP().aapoints = atoi(sep->arg[2]);
t->GetPP().expAA = 0;
t->Message(MT_Experience, "Setting personal AA points to %u", t->GetPP().aapoints);
t->SendAlternateAdvancementStats();
}
}
@ -5754,7 +5736,7 @@ void command_suspend(Client *c, const Seperator *sep)
}
std::string query = StringFormat("UPDATE `account` SET `suspendeduntil` = DATE_ADD(NOW(), INTERVAL %i DAY), "
"suspend_reason = '%s' WHERE `id` = %i",
"suspend_reason = '%s' WHERE `id` = %i",
duration, EscapeString(message).c_str(), accountID);
auto results = database.QueryDatabase(query);
@ -6226,21 +6208,21 @@ void command_npcedit(Client *c, const Seperator *sep)
database.QueryDatabase(query);
return;
}
if (strcasecmp(sep->arg[1], "meleetype") == 0) {
c->Message(15,"NPCID %u now has a primary melee type of %i and a secondary melee type of %i.", npcTypeID, atoi(sep->arg[2]), atoi(sep->arg[3]));
std::string query = StringFormat("UPDATE npc_types SET prim_melee_type = %i, sec_melee_type = %i WHERE id = %i", atoi(sep->arg[2]), atoi(sep->arg[3]), npcTypeID);
database.QueryDatabase(query);
return;
}
if (strcasecmp(sep->arg[1], "rangedtype") == 0) {
c->Message(15,"NPCID %u now has a ranged type of %i.", npcTypeID, atoi(sep->argplus[2]));
std::string query = StringFormat("UPDATE npc_types SET rangedtype = %i WHERE id = %i", atoi(sep->argplus[2]), npcTypeID);
database.QueryDatabase(query);
return;
}
if (strcasecmp(sep->arg[1], "ammoidfile") == 0) {
c->Message(15,"NPCID %u's ammo id file is now %i", npcTypeID, atoi(sep->argplus[2]));
std::string query = StringFormat("UPDATE npc_types SET ammoidfile = %i WHERE id = %i", atoi(sep->argplus[2]), npcTypeID);
@ -6275,7 +6257,7 @@ void command_npcedit(Client *c, const Seperator *sep)
database.QueryDatabase(query);
return;
}
if (strcasecmp(sep->arg[1], "walkspeed") == 0) {
c->Message(15,"NPCID %u now walks at %f", npcTypeID, atof(sep->argplus[2]));
std::string query = StringFormat("UPDATE npc_types SET walkspeed = %f WHERE id = %i", atof(sep->argplus[2]), npcTypeID);
@ -6429,7 +6411,7 @@ void command_npcedit(Client *c, const Seperator *sep)
database.QueryDatabase(query);
return;
}
if (strcasecmp(sep->arg[1], "Avoidance") == 0) {
c->Message(15,"NPCID %u now has %i Avoidance.", npcTypeID, atoi(sep->argplus[2]));
std::string query = StringFormat("UPDATE npc_types SET avoidance = %i WHERE id = %i", atoi(sep->argplus[2]), npcTypeID);
@ -6485,7 +6467,7 @@ void command_npcedit(Client *c, const Seperator *sep)
database.QueryDatabase(query);
return;
}
if (strcasecmp(sep->arg[1], "Attackcount") == 0) {
c->Message(15,"NPCID %u now has attack_count set to %i", npcTypeID,atoi(sep->arg[2]));
std::string query = StringFormat("UPDATE npc_types SET attack_count = %i WHERE id = %i", atoi(sep->argplus[2]),npcTypeID);
@ -6522,7 +6504,7 @@ void command_npcedit(Client *c, const Seperator *sep)
"luclin_hairstyle = %i, luclin_beard = %i, "
"face = %i, drakkin_heritage = %i, "
"drakkin_tattoo = %i, drakkin_details = %i "
"WHERE id = %i",
"WHERE id = %i",
target->GetHairColor(), target->GetBeardColor(),
target->GetHairStyle(), target->GetBeard(),
target->GetLuclinFace(), target->GetDrakkinHeritage(),
@ -6608,7 +6590,7 @@ void command_npcedit(Client *c, const Seperator *sep)
database.QueryDatabase(query);
return;
}
if (strcasecmp(sep->arg[1], "slow_mitigation") == 0) {
c->Message(15, "NPCID %u's slow mitigation limit is now %i.", npcTypeID, atoi(sep->arg[2]));
std::string query = StringFormat("UPDATE npc_types SET slow_mitigation = %i WHERE id = %i", atoi(sep->argplus[2]), npcTypeID);
@ -6663,7 +6645,7 @@ void command_qglobal(Client *c, const Seperator *sep) {
}
if(!strcasecmp(sep->arg[1], "on")) {
std::string query = StringFormat("UPDATE npc_types SET qglobal = 1 WHERE id = '%i'",
std::string query = StringFormat("UPDATE npc_types SET qglobal = 1 WHERE id = '%i'",
target->GetNPCTypeID());
auto results = database.QueryDatabase(query);
if(!results.Success()) {
@ -6676,7 +6658,7 @@ void command_qglobal(Client *c, const Seperator *sep) {
}
if(!strcasecmp(sep->arg[1], "off")) {
std::string query = StringFormat("UPDATE npc_types SET qglobal = 0 WHERE id = '%i'",
std::string query = StringFormat("UPDATE npc_types SET qglobal = 0 WHERE id = '%i'",
target->GetNPCTypeID());
auto results = database.QueryDatabase(query);
if(!results.Success()) {
@ -7072,13 +7054,13 @@ void command_ginfo(Client *c, const Seperator *sep)
if(g->membername[r][0] == '\0')
continue;
c->Message(0, "...Zoned Member: %s, Roles: %s %s %s", g->membername[r],
(g->MemberRoles[r] & RoleAssist) ? "Assist" : "",
(g->MemberRoles[r] & RoleTank) ? "Tank" : "",
(g->MemberRoles[r] & RoleAssist) ? "Assist" : "",
(g->MemberRoles[r] & RoleTank) ? "Tank" : "",
(g->MemberRoles[r] & RolePuller) ? "Puller" : "");
} else {
c->Message(0, "...In-Zone Member: %s (0x%x) Roles: %s %s %s", g->membername[r], g->members[r],
(g->MemberRoles[r] & RoleAssist) ? "Assist" : "",
(g->MemberRoles[r] & RoleTank) ? "Tank" : "",
(g->MemberRoles[r] & RoleAssist) ? "Assist" : "",
(g->MemberRoles[r] & RoleTank) ? "Tank" : "",
(g->MemberRoles[r] & RolePuller) ? "Puller" : "");
}
@ -7241,7 +7223,7 @@ void command_flagedit(Client *c, const Seperator *sep) {
flag_name[127] = '\0';
std::string query = StringFormat("UPDATE zone SET flag_needed = '%s' "
"WHERE zoneidnumber = %d AND version = %d",
"WHERE zoneidnumber = %d AND version = %d",
flag_name, zoneid, zone->GetInstanceVersion());
auto results = database.QueryDatabase(query);
if(!results.Success()) {
@ -7268,7 +7250,7 @@ void command_flagedit(Client *c, const Seperator *sep) {
}
std::string query = StringFormat("UPDATE zone SET flag_needed = '' "
"WHERE zoneidnumber = %d AND version = %d",
"WHERE zoneidnumber = %d AND version = %d",
zoneid, zone->GetInstanceVersion());
auto results = database.QueryDatabase(query);
if(!results.Success()) {
@ -7678,56 +7660,6 @@ void command_reloadtitles(Client *c, const Seperator *sep)
}
void command_altactivate(Client *c, const Seperator *sep){
if(sep->arg[1][0] == '\0'){
c->Message(10, "Invalid argument, usage:");
c->Message(10, "#altactivate list - lists the AA ID numbers that are available to you");
c->Message(10, "#altactivate time [argument] - returns the time left until you can use the AA with the ID that matches the argument.");
c->Message(10, "#altactivate [argument] - activates the AA with the ID that matches the argument.");
return;
}
if(!strcasecmp(sep->arg[1], "help")){
c->Message(10, "Usage:");
c->Message(10, "#altactivate list - lists the AA ID numbers that are available to you");
c->Message(10, "#altactivate time [argument] - returns the time left until you can use the AA with the ID that matches the argument.");
c->Message(10, "#altactivate [argument] - activates the AA with the ID that matches the argument.");
return;
}
if(!strcasecmp(sep->arg[1], "list")){
c->Message(10, "You have access to the following AA Abilities:");
int x, val;
SendAA_Struct* saa = nullptr;
for(x = 0; x < aaHighestID; x++){
if(AA_Actions[x][0].spell_id || AA_Actions[x][0].action){ //if there's an action or spell associated we assume it's a valid
val = 0; //and assume if they don't have a value for the first rank then it isn't valid for any rank
saa = nullptr;
val = c->GetAA(x);
if(val){
saa = zone->FindAA(x);
c->Message(10, "%d: %s %d", x, saa->name, val);
}
}
}
}
else if(!strcasecmp(sep->arg[1], "time")){
int ability = atoi(sep->arg[2]);
if(c->GetAA(ability)){
int remain = c->GetPTimers().GetRemainingTime(pTimerAAStart + ability);
if(remain)
c->Message(10, "You may use that ability in %d minutes and %d seconds.", (remain/60), (remain%60));
else
c->Message(10, "You may use that ability now.");
}
else{
c->Message(10, "You do not have access to that ability.");
}
}
else
{
c->ActivateAA((aaID) atoi(sep->arg[1]));
}
}
void command_traindisc(Client *c, const Seperator *sep)
{
uint8 max_level, min_level;
@ -7874,7 +7806,7 @@ void command_deletegraveyard(Client *c, const Seperator *sep)
return;
}
void command_summonburriedplayercorpse(Client *c, const Seperator *sep)
void command_summonburiedplayercorpse(Client *c, const Seperator *sep)
{
Client *t=c;
@ -7888,12 +7820,12 @@ void command_summonburriedplayercorpse(Client *c, const Seperator *sep)
Corpse* PlayerCorpse = database.SummonBuriedCharacterCorpses(t->CharacterID(), t->GetZoneID(), zone->GetInstanceID(), t->GetPosition());
if(!PlayerCorpse)
c->Message(0, "Your target doesn't have any burried corpses.");
c->Message(0, "Your target doesn't have any buried corpses.");
return;
}
void command_getplayerburriedcorpsecount(Client *c, const Seperator *sep)
void command_getplayerburiedcorpsecount(Client *c, const Seperator *sep)
{
Client *t=c;
@ -7907,9 +7839,9 @@ void command_getplayerburriedcorpsecount(Client *c, const Seperator *sep)
uint32 CorpseCount = database.GetCharacterBuriedCorpseCount(t->CharacterID());
if(CorpseCount > 0)
c->Message(0, "Your target has a total of %u burried corpses.", CorpseCount);
c->Message(0, "Your target has a total of %u buried corpses.", CorpseCount);
else
c->Message(0, "Your target doesn't have any burried corpses.");
c->Message(0, "Your target doesn't have any buried corpses.");
return;
}
@ -7950,7 +7882,7 @@ void command_advnpcspawn(Client *c, const Seperator *sep)
std::string query = StringFormat("INSERT INTO spawngroup "
"(name, spawn_limit, dist, max_x, min_x, max_y, min_y, delay) "
"VALUES (\"%s\", %i, %f, %f, %f, %f, %f, %i)",
"VALUES (\"%s\", %i, %f, %f, %f, %f, %f, %i)",
sep->arg[2],
(sep->arg[3]? atoi(sep->arg[3]): 0),
(sep->arg[4]? atof(sep->arg[4]): 0),
@ -7977,7 +7909,7 @@ void command_advnpcspawn(Client *c, const Seperator *sep)
}
std::string query = StringFormat("INSERT INTO spawnentry (spawngroupID, npcID, chance) "
"VALUES (%i, %i, %i)",
"VALUES (%i, %i, %i)",
atoi(sep->arg[2]), atoi(sep->arg[3]), atoi(sep->arg[4]));
auto results = database.QueryDatabase(query);
if (!results.Success()) {
@ -7998,7 +7930,7 @@ void command_advnpcspawn(Client *c, const Seperator *sep)
}
std::string query = StringFormat("UPDATE spawngroup SET dist = '%f', max_x = '%f', min_x = '%f', "
"max_y = '%f', min_y = '%f', delay = '%i' WHERE id = '%i'",
"max_y = '%f', min_y = '%f', delay = '%i' WHERE id = '%i'",
atof(sep->arg[3]), atof(sep->arg[4]), atof(sep->arg[5]),
atof(sep->arg[6]), atof(sep->arg[7]), atoi(sep->arg[8]),
atoi(sep->arg[2]));
@ -8083,7 +8015,7 @@ void command_advnpcspawn(Client *c, const Seperator *sep)
}
std::string query = StringFormat("UPDATE spawn2 SET x = '%f', y = '%f', z = '%f', heading = '%f' "
"WHERE id = '%i'",
"WHERE id = '%i'",
c->GetX(), c->GetY(), c->GetZ(), c->GetHeading(),s2->GetID());
auto results = database.QueryDatabase(query);
if (!results.Success()) {
@ -8154,7 +8086,7 @@ void command_advnpcspawn(Client *c, const Seperator *sep)
int16 version = atoi(sep->arg[2]);
std::string query = StringFormat("UPDATE spawn2 SET version = %i "
"WHERE spawngroupID = '%i'",
"WHERE spawngroupID = '%i'",
version, c->GetTarget()->CastToNPC()->GetSp2());
auto results = database.QueryDatabase(query);
if (!results.Success()) {
@ -10205,7 +10137,7 @@ void command_zopp(Client *c, const Seperator *sep)
ItemInst* FakeItemInst = database.CreateItem(FakeItem, charges);
c->SendItemPacket(slotid, FakeItemInst, packettype);
c->Message(0, "Sending zephyr op packet to client - [%s] %s (%u) with %i %s to slot %i.",
c->Message(0, "Sending zephyr op packet to client - [%s] %s (%u) with %i %s to slot %i.",
packettype == ItemPacketTrade ? "Trade" : "Summon", FakeItem->Name, itemid, charges,
std::abs(charges == 1) ? "charge" : "charges", slotid);
safe_delete(FakeItemInst);
@ -10424,7 +10356,7 @@ void command_tune(Client *c, const Seperator *sep)
ac_override = 0;
if (!info_level)
info_level = 1;
if(!strcasecmp(sep->arg[2], "A"))
c->Tune_FindATKByPctMitigation(defender, attacker, pct_mitigation, interval, max_loop,ac_override,info_level);
else if(!strcasecmp(sep->arg[2], "B"))
@ -10459,7 +10391,7 @@ void command_tune(Client *c, const Seperator *sep)
atk_override = 0;
if (!info_level)
info_level = 1;
if(!strcasecmp(sep->arg[2], "A"))
c->Tune_FindACByPctMitigation(defender, attacker, pct_mitigation, interval, max_loop,atk_override,info_level);
else if(!strcasecmp(sep->arg[2], "B"))
@ -10501,7 +10433,7 @@ void command_tune(Client *c, const Seperator *sep)
c->Message(10, "#Tune - Error hit chance out of bounds. [Max %.2f Min .2f]", RuleR(Combat,MaxChancetoHit),RuleR(Combat,MinChancetoHit));
return;
}
if(!strcasecmp(sep->arg[2], "A"))
c->Tune_FindAccuaryByHitChance(defender, attacker, hit_chance, interval, max_loop,avoid_override,info_level);
else if(!strcasecmp(sep->arg[2], "B"))
@ -10543,7 +10475,7 @@ void command_tune(Client *c, const Seperator *sep)
c->Message(10, "#Tune - Error hit chance out of bounds. [Max %.2f Min .2f]", RuleR(Combat,MaxChancetoHit),RuleR(Combat,MinChancetoHit));
return;
}
if(!strcasecmp(sep->arg[2], "A"))
c->Tune_FindAvoidanceByHitChance(defender, attacker, hit_chance, interval, max_loop,acc_override, info_level);
else if(!strcasecmp(sep->arg[2], "B"))
@ -10569,7 +10501,7 @@ void command_logtest(Client *c, const Seperator *sep){
for (i = 0; i < atoi(sep->arg[1]); i++){
Log.Out(Logs::General, Logs::Debug, "[%u] Test #2... Took %f seconds", i, ((float)(std::clock() - t)) / CLOCKS_PER_SEC);
}
}
}
@ -10625,7 +10557,7 @@ void command_logs(Client *c, const Seperator *sep){
c->Message(15, "Your Log Settings have been applied");
c->Message(15, "Output Method: %s :: Debug Level: %i - Category: %s", sep->arg[2], atoi(sep->arg[4]), Logs::LogCategoryName[atoi(sep->arg[3])]);
}
/* We use a general 'is_category_enabled' now, let's update when we update any output settings
/* We use a general 'is_category_enabled' now, let's update when we update any output settings
This is used in hot places of code to check if its enabled in any way before triggering logs
*/
if (sep->arg[4] > 0){
@ -10653,9 +10585,41 @@ void command_mysqltest(Client *c, const Seperator *sep)
for (i = 0; i < atoi(sep->arg[1]); i++){
std::string query = "SELECT * FROM `zone`";
auto results = database.QueryDatabase(query);
}
}
}
Log.Out(Logs::General, Logs::Debug, "MySQL Test... Took %f seconds", ((float)(std::clock() - t)) / CLOCKS_PER_SEC);
Log.Out(Logs::General, Logs::Debug, "MySQL Test... Took %f seconds", ((float)(std::clock() - t)) / CLOCKS_PER_SEC);
}
void command_resetaa_timer(Client *c, const Seperator *sep) {
Client *target = nullptr;
if(!c->GetTarget() || !c->GetTarget()->IsClient()) {
target = c;
} else {
target = c->GetTarget()->CastToClient();
}
if(sep->IsNumber(1))
{
int timer_id = atoi(sep->arg[1]);
c->Message(0, "Reset of timer %i for %s", timer_id, c->GetName());
c->ResetAlternateAdvancementTimer(timer_id);
}
else if(!strcasecmp(sep->arg[1], "all"))
{
c->Message(0, "Reset all timers for %s", c->GetName());
c->ResetAlternateAdvancementTimers();
}
else
{
c->Message(0, "usage: #resetaa_timer [all | timer_id]");
}
}
void command_reloadaa(Client *c, const Seperator *sep) {
c->Message(0, "Reloading Alternate Advancement Data...");
zone->LoadAlternateAdvancement();
c->Message(0, "Alternate Advancement Data Reloaded");
entity_list.SendAlternateAdvancementStats();
}
void command_hotfix(Client *c, const Seperator *sep) {

View File

@ -216,7 +216,6 @@ void command_time(Client *c, const Seperator *sep);
void command_guild(Client *c, const Seperator *sep);
bool helper_guild_edit(Client *c, uint32 dbid, uint32 eqid, uint8 rank, const char* what, const char* value);
void command_zonestatus(Client *c, const Seperator *sep);
void command_manaburn(Client *c, const Seperator *sep);
void command_doanim(Client *c, const Seperator *sep);
void command_randomfeatures(Client *c, const Seperator *sep);
void command_face(Client *c, const Seperator *sep);
@ -276,13 +275,12 @@ void command_guildlist(Client *c, const Seperator *sep);
void command_rules(Client *c, const Seperator *sep);
void command_task(Client *c, const Seperator *sep);
void command_reloadtitles(Client *c, const Seperator *sep);
void command_altactivate(Client *c, const Seperator *sep);
void command_refundaa(Client *c, const Seperator *sep);
void command_traindisc(Client *c, const Seperator *sep);
void command_deletegraveyard(Client *c, const Seperator *sep);
void command_setgraveyard(Client *c, const Seperator *sep);
void command_getplayerburriedcorpsecount(Client *c, const Seperator *sep);
void command_summonburriedplayercorpse(Client *c, const Seperator *sep);
void command_getplayerburiedcorpsecount(Client *c, const Seperator *sep);
void command_summonburiedplayercorpse(Client *c, const Seperator *sep);
void command_unscribespell(Client *c, const Seperator *sep);
void command_scribespell(Client *c, const Seperator *sep);
void command_refreshgroup(Client *c, const Seperator *sep);
@ -325,6 +323,8 @@ void command_tune(Client *c, const Seperator *sep);
void command_logtest(Client *c, const Seperator *sep);
void command_mysqltest(Client *c, const Seperator *sep);
void command_logs(Client *c, const Seperator *sep);
void command_resetaa_timer(Client *c, const Seperator *sep);
void command_reloadaa(Client *c, const Seperator *sep);
void command_hotfix(Client *c, const Seperator *sep);
#ifdef EQPROFILE

View File

@ -22,6 +22,7 @@
#define TARGET_RING_SPELL_SLOT 12
#define DISCIPLINE_SPELL_SLOT 10
#define ABILITY_SPELL_SLOT 9
#define ALTERNATE_ABILITY_SPELL_SLOT 0xFF
//LOS Parameters:
#define HEAD_POSITION 0.9f //ratio of GetSize() where NPCs see from
@ -400,7 +401,6 @@ struct StatBonuses {
int32 Metabolism; // Food/drink consumption rates.
bool Sanctuary; // Sanctuary effect, lowers place on hate list until cast on others.
int32 FactionModPct; // Modifies amount of faction gained.
int32 MeleeVulnerability; // Weakness/mitigation to melee damage
bool LimitToSkill[HIGHEST_SKILL+2]; // Determines if we need to search for a skill proc.
uint32 SkillProc[MAX_SKILL_PROCS]; // Max number of spells containing skill_procs.
uint32 SkillProcSuccess[MAX_SKILL_PROCS]; // Max number of spells containing skill_procs_success.
@ -412,7 +412,7 @@ struct StatBonuses {
int8 BaseMovementSpeed; // Adjust base run speed, does not stack with other movement bonuses.
uint8 IncreaseRunSpeedCap; // Increase max run speed above cap.
int32 DoubleSpecialAttack; // Chance to to perform a double special attack (ie flying kick 2x)
int32 SpecialAttackKBProc[2]; // Chance to to do a knockback from special attacks. (0 = chance 1 = Skill)
int32 SkillAttackProc[3]; // [0] chance to proc [2] spell on [1] skill usage
uint8 FrontalStunResist; // Chance to resist a frontal stun
int32 BindWound; // Increase amount of HP by percent.
int32 MaxBindWound; // Increase max amount of HP you can bind wound.
@ -461,6 +461,7 @@ struct StatBonuses {
uint8 AssassinateLevel; // Max Level Assassinate will be effective at.
int32 PetMeleeMitigation; // Add AC to owner's pet.
bool IllusionPersistence; // Causes illusions not to fade.
uint16 extra_xtargets; // extra xtarget entries
};
typedef struct

View File

@ -835,7 +835,7 @@ bool Corpse::Process() {
Save();
player_corpse_depop = true;
corpse_db_id = 0;
Log.Out(Logs::General, Logs::None, "Tagged %s player corpse has burried.", this->GetName());
Log.Out(Logs::General, Logs::None, "Tagged %s player corpse has buried.", this->GetName());
}
else {
Log.Out(Logs::General, Logs::Error, "Unable to bury %s player corpse.", this->GetName());

View File

@ -421,12 +421,6 @@ int32 Mob::GetActSpellDuration(uint16 spell_id, int32 duration)
int tic_inc = 0;
tic_inc = GetFocusEffect(focusSpellDurByTic, spell_id);
// unsure on the exact details, but bard songs that don't cost mana at some point get an extra tick, 60 for now
// a level 53 bard reported getting 2 tics
// bard DOTs do get this extra tick, but beneficial long bard songs don't? (invul, crescendo)
if ((IsShortDurationBuff(spell_id) || IsDetrimentalSpell(spell_id)) && IsBardSong(spell_id) &&
spells[spell_id].mana == 0 && GetClass() == BARD && GetLevel() > 60)
tic_inc++;
float focused = ((duration * increase) / 100.0f) + tic_inc;
int ifocused = static_cast<int>(focused);
@ -878,7 +872,7 @@ void EntityList::AEBardPulse(Mob *caster, Mob *center, uint16 spell_id, bool aff
caster->CastToClient()->CheckSongSkillIncrease(spell_id);
}
//Dook- Rampage and stuff for clients.
// Rampage and stuff for clients. Normal and Duration rampages
//NPCs handle it differently in Mob::Rampage
void EntityList::AEAttack(Mob *attacker, float dist, int Hand, int count, bool IsFromSpell) {
//Dook- Will need tweaking, currently no pets or players or horses
@ -896,7 +890,10 @@ void EntityList::AEAttack(Mob *attacker, float dist, int Hand, int count, bool I
&& curmob->GetRace() != 216 && curmob->GetRace() != 472 /* dont attack horses */
&& (DistanceSquared(curmob->GetPosition(), attacker->GetPosition()) <= dist2)
) {
attacker->Attack(curmob, Hand, false, false, IsFromSpell);
if (!attacker->IsClient() || attacker->GetClass() == MONK || attacker->GetClass() == RANGER)
attacker->Attack(curmob, Hand, false, false, IsFromSpell);
else
attacker->CastToClient()->DoAttackRounds(curmob, Hand, IsFromSpell);
hit++;
if (count != 0 && hit >= count)
return;

View File

@ -1748,18 +1748,18 @@ XS(XS__clear_zone_flag)
XSRETURN_EMPTY;
}
XS(XS__summonburriedplayercorpse);
XS(XS__summonburriedplayercorpse)
XS(XS__summonburiedplayercorpse);
XS(XS__summonburiedplayercorpse)
{
dXSARGS;
if (items != 5)
Perl_croak(aTHX_ "Usage: summonburriedplayercorpse(char_id,dest_x,dest_y,dest_z,dest_heading)");
Perl_croak(aTHX_ "Usage: summonburiedplayercorpse(char_id,dest_x,dest_y,dest_z,dest_heading)");
bool RETVAL;
uint32 char_id = (int)SvIV(ST(0));
auto position = glm::vec4((float)SvIV(ST(1)), (float)SvIV(ST(2)), (float)SvIV(ST(3)),(float)SvIV(ST(4)));
RETVAL = quest_manager.summonburriedplayercorpse(char_id, position);
RETVAL = quest_manager.summonburiedplayercorpse(char_id, position);
ST(0) = boolSV(RETVAL);
sv_2mortal(ST(0));
@ -1784,19 +1784,19 @@ XS(XS__summonallplayercorpses)
XSRETURN(1);
}
XS(XS__getplayerburriedcorpsecount);
XS(XS__getplayerburriedcorpsecount)
XS(XS__getplayerburiedcorpsecount);
XS(XS__getplayerburiedcorpsecount)
{
dXSARGS;
if (items != 1)
Perl_croak(aTHX_ "Usage: getplayerburriedcorpsecount(char_id)");
Perl_croak(aTHX_ "Usage: getplayerburiedcorpsecount(char_id)");
uint32 RETVAL;
dXSTARG;
uint32 char_id = (int)SvIV(ST(0));
RETVAL = quest_manager.getplayerburriedcorpsecount(char_id);
RETVAL = quest_manager.getplayerburiedcorpsecount(char_id);
XSprePUSH; PUSHu((IV)RETVAL);
XSRETURN(1);
@ -3712,7 +3712,7 @@ EXTERN_C XS(boot_quest)
newXS(strcpy(buf, "get_spawn_condition"), XS__get_spawn_condition, file);
newXS(strcpy(buf, "getguildnamebyid"), XS__getguildnamebyid, file);
newXS(strcpy(buf, "getlevel"), XS__getlevel, file);
newXS(strcpy(buf, "getplayerburriedcorpsecount"), XS__getplayerburriedcorpsecount, file);
newXS(strcpy(buf, "getplayerburiedcorpsecount"), XS__getplayerburiedcorpsecount, file);
newXS(strcpy(buf, "gettaskactivitydonecount"), XS__gettaskactivitydonecount, file);
newXS(strcpy(buf, "givecash"), XS__givecash, file);
newXS(strcpy(buf, "gmmove"), XS__gmmove, file);
@ -3802,7 +3802,7 @@ EXTERN_C XS(boot_quest)
newXS(strcpy(buf, "stopalltimers"), XS__stopalltimers, file);
newXS(strcpy(buf, "stoptimer"), XS__stoptimer, file);
newXS(strcpy(buf, "summonallplayercorpses"), XS__summonallplayercorpses, file);
newXS(strcpy(buf, "summonburriedplayercorpse"), XS__summonburriedplayercorpse, file);
newXS(strcpy(buf, "summonburiedplayercorpse"), XS__summonburiedplayercorpse, file);
newXS(strcpy(buf, "summonitem"), XS__summonitem, file);
newXS(strcpy(buf, "surname"), XS__surname, file);
newXS(strcpy(buf, "targlobal"), XS__targlobal, file);

View File

@ -58,7 +58,6 @@ extern uint32 numclients;
extern PetitionList petition_list;
extern char errorname[32];
extern uint16 adverrornum;
Entity::Entity()
{
@ -4705,3 +4704,11 @@ void EntityList::StopMobAI()
mob.second->AI_ShutDown();
}
}
void EntityList::SendAlternateAdvancementStats() {
for(auto &c : client_list) {
c.second->SendAlternateAdvancementTable();
c.second->SendAlternateAdvancementStats();
c.second->SendAlternateAdvancementPoints();
}
}

View File

@ -398,7 +398,6 @@ public:
void SaveAllClientsTaskState();
void ReloadAllClientsTaskState(int TaskID=0);
uint16 CreateGroundObject(uint32 itemid, const glm::vec4& position, uint32 decay_time = 300000);
uint16 CreateGroundObjectFromModel(const char *model, const glm::vec4& position, uint8 type = 0x00, uint32 decay_time = 0);
uint16 CreateDoor(const char *model, const glm::vec4& position, uint8 type = 0, uint16 size = 100);
@ -429,6 +428,7 @@ public:
uint16 GetFreeID();
void RefreshAutoXTargets(Client *c);
void RefreshClientXTargets(Client *c);
void SendAlternateAdvancementStats();
protected:
friend class Zone;

View File

@ -515,7 +515,7 @@ void Client::SetEXP(uint32 set_exp, uint32 set_aaxp, bool isrezzexp) {
if (GetLevel() < 51) {
m_epp.perAA = 0; // turn off aa exp if they drop below 51
} else
SendAAStats(); //otherwise, send them an AA update
SendAlternateAdvancementStats(); //otherwise, send them an AA update
//send the expdata in any case so the xp bar isnt stuck after leveling
uint32 tmpxp1 = GetEXPForLevel(GetLevel()+1);

View File

@ -535,37 +535,20 @@ int HateList::AreaRampage(Mob *caster, Mob *target, int count, ExtraAttackOption
if (!target || !caster)
return 0;
int ret = 0;
std::list<uint32> id_list;
auto iterator = list.begin();
while (iterator != list.end())
{
struct_HateList *h = (*iterator);
++iterator;
if (h && h->entity_on_hatelist && h->entity_on_hatelist != caster)
{
if (caster->CombatRange(h->entity_on_hatelist))
{
id_list.push_back(h->entity_on_hatelist->GetID());
++ret;
int hit_count = 0;
auto it = list.begin();
while (it != list.end() && hit_count < count) {
struct_HateList *h = (*it);
if (h && h->entity_on_hatelist && h->entity_on_hatelist != caster) {
if (caster->CombatRange(h->entity_on_hatelist)) {
++hit_count;
caster->ProcessAttackRounds(h->entity_on_hatelist, opts);
}
}
++it;
}
std::list<uint32>::iterator iter = id_list.begin();
while (iter != id_list.end())
{
Mob *cur = entity_list.GetMobID((*iter));
if (cur)
{
for (int i = 0; i < count; ++i) {
caster->Attack(cur, MainPrimary, false, false, false, opts);
}
}
iter++;
}
return ret;
return hit_count;
}
void HateList::SpellCast(Mob *caster, uint32 spell_id, float range, Mob* ae_center)

View File

@ -334,9 +334,11 @@ void NPC::AddLootDrop(const Item_Struct *item2, ItemList* itemlist, int16 charge
eslot = MaterialPrimary;
if (item2->Damage > 0)
SendAddPlayerState(PlayerState::PrimaryWeaponEquipped);
if (item2->ItemType == ItemType2HBlunt || item2->ItemType == ItemType2HSlash || item2->ItemType == ItemType2HPiercing)
SetTwoHanderEquipped(true);
}
else if (foundslot == MainSecondary
&& (GetOwner() != nullptr || (GetLevel() >= 13 && zone->random.Roll(NPC_DW_CHANCE)) || (item2->Damage==0)) &&
&& (GetOwner() != nullptr || (CanThisClassDualWield() && zone->random.Roll(NPC_DW_CHANCE)) || (item2->Damage==0)) &&
(item2->ItemType == ItemType1HSlash || item2->ItemType == ItemType1HBlunt || item2->ItemType == ItemTypeShield ||
item2->ItemType == ItemType1HPiercing))
{

View File

@ -350,6 +350,11 @@ const char *Lua_Client::AccountName() {
return self->AccountName();
}
int Lua_Client::GetAccountAge() {
Lua_Safe_Call_Int();
return time(nullptr) - self->GetAccountCreation();
}
int Lua_Client::Admin() {
Lua_Safe_Call_Bool();
return self->Admin();
@ -1024,7 +1029,17 @@ void Lua_Client::AddLevelBasedExp(int exp_pct, int max_level) {
void Lua_Client::IncrementAA(int aa) {
Lua_Safe_Call_Void();
self->IncrementAA(aa);
self->IncrementAlternateAdvancementRank(aa);
}
bool Lua_Client::GrantAlternateAdvancementAbility(int aa_id, int points) {
Lua_Safe_Call_Bool();
self->GrantAlternateAdvancementAbility(aa_id, points);
}
bool Lua_Client::GrantAlternateAdvancementAbility(int aa_id, int points, bool ignore_cost) {
Lua_Safe_Call_Bool();
self->GrantAlternateAdvancementAbility(aa_id, points, ignore_cost);
}
void Lua_Client::MarkSingleCompassLoc(float in_x, float in_y, float in_z) {
@ -1365,6 +1380,7 @@ luabind::scope lua_register_client() {
.def("GetRawItemAC", (int(Lua_Client::*)(void))&Lua_Client::GetRawItemAC)
.def("AccountID", (uint32(Lua_Client::*)(void))&Lua_Client::AccountID)
.def("AccountName", (const char *(Lua_Client::*)(void))&Lua_Client::AccountName)
.def("GetAccountAge", (int(Lua_Client::*)(void))&Lua_Client::GetAccountAge)
.def("Admin", (int(Lua_Client::*)(void))&Lua_Client::Admin)
.def("CharacterID", (uint32(Lua_Client::*)(void))&Lua_Client::CharacterID)
.def("GuildRank", (int(Lua_Client::*)(void))&Lua_Client::GuildRank)
@ -1500,6 +1516,8 @@ luabind::scope lua_register_client() {
.def("AddLevelBasedExp", (void(Lua_Client::*)(int))&Lua_Client::AddLevelBasedExp)
.def("AddLevelBasedExp", (void(Lua_Client::*)(int,int))&Lua_Client::AddLevelBasedExp)
.def("IncrementAA", (void(Lua_Client::*)(int))&Lua_Client::IncrementAA)
.def("GrantAlternateAdvancementAbility", (bool(Lua_Client::*)(int, int))&Lua_Client::GrantAlternateAdvancementAbility)
.def("GrantAlternateAdvancementAbility", (bool(Lua_Client::*)(int, int, bool))&Lua_Client::GrantAlternateAdvancementAbility)
.def("MarkSingleCompassLoc", (void(Lua_Client::*)(float,float,float))&Lua_Client::MarkSingleCompassLoc)
.def("MarkSingleCompassLoc", (void(Lua_Client::*)(float,float,float,int))&Lua_Client::MarkSingleCompassLoc)
.def("GetNextAvailableSpellBookSlot", (int(Lua_Client::*)(void))&Lua_Client::GetNextAvailableSpellBookSlot)

View File

@ -166,9 +166,9 @@ public:
void SummonItem(uint32 item_id, int charges, uint32 aug1, uint32 aug2, uint32 aug3);
void SummonItem(uint32 item_id, int charges, uint32 aug1, uint32 aug2, uint32 aug3, uint32 aug4);
void SummonItem(uint32 item_id, int charges, uint32 aug1, uint32 aug2, uint32 aug3, uint32 aug4, uint32 aug5);
void SummonItem(uint32 item_id, int charges, uint32 aug1, uint32 aug2, uint32 aug3, uint32 aug4, uint32 aug5,
void SummonItem(uint32 item_id, int charges, uint32 aug1, uint32 aug2, uint32 aug3, uint32 aug4, uint32 aug5,
bool attuned);
void SummonItem(uint32 item_id, int charges, uint32 aug1, uint32 aug2, uint32 aug3, uint32 aug4, uint32 aug5,
void SummonItem(uint32 item_id, int charges, uint32 aug1, uint32 aug2, uint32 aug3, uint32 aug4, uint32 aug5,
bool attuned, int to_slot);
void SetStats(int type, int value);
void IncStats(int type, int value);
@ -233,6 +233,8 @@ public:
void AddLevelBasedExp(int exp_pct);
void AddLevelBasedExp(int exp_pct, int max_level);
void IncrementAA(int aa);
bool GrantAlternateAdvancementAbility(int aa_id, int points);
bool GrantAlternateAdvancementAbility(int aa_id, int points, bool ignore_cost);
void MarkSingleCompassLoc(float in_x, float in_y, float in_z);
void MarkSingleCompassLoc(float in_x, float in_y, float in_z, int count);
int GetNextAvailableSpellBookSlot();
@ -260,6 +262,7 @@ public:
bool HasSpellScribed(int spell_id);
void SetAccountFlag(std::string flag, std::string val);
std::string GetAccountFlag(std::string flag);
int GetAccountAge();
Lua_Group GetGroup();
Lua_Raid GetRaid();
bool PutItemInInventory(int slot_id, Lua_ItemInst inst);

View File

@ -513,16 +513,16 @@ void lua_toggle_spawn_event(int event_id, bool enable, bool strict, bool reset)
quest_manager.toggle_spawn_event(event_id, enable, strict, reset);
}
void lua_summon_burried_player_corpse(uint32 char_id, float x, float y, float z, float h) {
quest_manager.summonburriedplayercorpse(char_id, glm::vec4(x, y, z, h));
void lua_summon_buried_player_corpse(uint32 char_id, float x, float y, float z, float h) {
quest_manager.summonburiedplayercorpse(char_id, glm::vec4(x, y, z, h));
}
void lua_summon_all_player_corpses(uint32 char_id, float x, float y, float z, float h) {
quest_manager.summonallplayercorpses(char_id, glm::vec4(x, y, z, h));
}
int lua_get_player_burried_corpse_count(uint32 char_id) {
return quest_manager.getplayerburriedcorpsecount(char_id);
int lua_get_player_buried_corpse_count(uint32 char_id) {
return quest_manager.getplayerburiedcorpsecount(char_id);
}
bool lua_bury_player_corpse(uint32 char_id) {
@ -1524,9 +1524,9 @@ luabind::scope lua_register_general() {
luabind::def("spawn_condition", &lua_spawn_condition),
luabind::def("get_spawn_condition", &lua_get_spawn_condition),
luabind::def("toggle_spawn_event", &lua_toggle_spawn_event),
luabind::def("summon_burried_player_corpse", &lua_summon_burried_player_corpse),
luabind::def("summon_buried_player_corpse", &lua_summon_buried_player_corpse),
luabind::def("summon_all_player_corpses", &lua_summon_all_player_corpses),
luabind::def("get_player_burried_corpse_count", &lua_get_player_burried_corpse_count),
luabind::def("get_player_buried_corpse_count", &lua_get_player_buried_corpse_count),
luabind::def("bury_player_corpse", &lua_bury_player_corpse),
luabind::def("task_selector", &lua_task_selector),
luabind::def("task_set_selector", &lua_task_set_selector),

View File

@ -790,7 +790,7 @@ bool Lua_Mob::CastSpell(int spell_id, int target_id, int slot, int cast_time, in
int16 res = resist_adjust;
return self->CastSpell(spell_id, target_id, slot, cast_time, mana_cost, nullptr, static_cast<uint32>(item_slot),
static_cast<uint32>(timer), static_cast<uint32>(timer_duration), 0, &res);
static_cast<uint32>(timer), static_cast<uint32>(timer_duration), &res);
}
bool Lua_Mob::SpellFinished(int spell_id, Lua_Mob target) {
@ -1195,6 +1195,21 @@ int Lua_Mob::GetAA(int id) {
return self->GetAA(id);
}
int Lua_Mob::GetAAByAAID(int id) {
Lua_Safe_Call_Int();
return self->GetAAByAAID(id);
}
bool Lua_Mob::SetAA(int rank_id, int new_value) {
Lua_Safe_Call_Bool();
return self->SetAA(rank_id, new_value);
}
bool Lua_Mob::SetAA(int rank_id, int new_value, int charges) {
Lua_Safe_Call_Bool();
return self->SetAA(rank_id, new_value, charges);
}
bool Lua_Mob::DivineAura() {
Lua_Safe_Call_Bool();
return self->DivineAura();
@ -2074,6 +2089,9 @@ luabind::scope lua_register_mob() {
.def("CheckHealAggroAmount", (int(Lua_Mob::*)(int))&Lua_Mob::CheckHealAggroAmount)
.def("CheckHealAggroAmount", (int(Lua_Mob::*)(int,uint32))&Lua_Mob::CheckHealAggroAmount)
.def("GetAA", (int(Lua_Mob::*)(int))&Lua_Mob::GetAA)
.def("GetAAByAAID", (int(Lua_Mob::*)(int))&Lua_Mob::GetAAByAAID)
.def("SetAA", (bool(Lua_Mob::*)(int,int))&Lua_Mob::SetAA)
.def("SetAA", (bool(Lua_Mob::*)(int,int,int))&Lua_Mob::SetAA)
.def("DivineAura", (bool(Lua_Mob::*)(void))&Lua_Mob::DivineAura)
.def("SetOOCRegen", (void(Lua_Mob::*)(int))&Lua_Mob::SetOOCRegen)
.def("GetEntityVariable", (const char*(Lua_Mob::*)(const char*))&Lua_Mob::GetEntityVariable)
@ -2216,7 +2234,14 @@ luabind::scope lua_register_special_abilities() {
luabind::value("destructible_object", static_cast<int>(DESTRUCTIBLE_OBJECT)),
luabind::value("no_harm_from_client", static_cast<int>(NO_HARM_FROM_CLIENT)),
luabind::value("always_flee", static_cast<int>(ALWAYS_FLEE)),
luabind::value("flee_percent", static_cast<int>(FLEE_PERCENT))
luabind::value("flee_percent", static_cast<int>(FLEE_PERCENT)),
luabind::value("allow_beneficial", static_cast<int>(ALLOW_BENEFICIAL)),
luabind::value("disable_melee", static_cast<int>(DISABLE_MELEE)),
luabind::value("npc_chase_distance", static_cast<int>(NPC_CHASE_DISTANCE)),
luabind::value("allow_to_tank", static_cast<int>(ALLOW_TO_TANK)),
luabind::value("ignore_root_aggro_rules", static_cast<int>(IGNORE_ROOT_AGGRO_RULES)),
luabind::value("casting_resist_diff", static_cast<int>(CASTING_RESIST_DIFF)),
luabind::value("counter_avoid_damage", static_cast<int>(COUNTER_AVOID_DAMAGE))
];
}

View File

@ -249,6 +249,9 @@ public:
int CheckHealAggroAmount(int spell_id);
int CheckHealAggroAmount(int spell_id, uint32 heal_possible);
int GetAA(int id);
int GetAAByAAID(int id);
bool SetAA(int rank_id, int new_value);
bool SetAA(int rank_id, int new_value, int charges);
bool DivineAura();
void SetOOCRegen(int regen);
const char* GetEntityVariable(const char *name);

View File

@ -211,6 +211,7 @@ Mob::Mob(const char* in_name,
trackable = true;
has_shieldequiped = false;
has_twohandbluntequiped = false;
has_twohanderequipped = false;
has_numhits = false;
has_MGB = false;
has_ProjectIllusion = false;
@ -307,8 +308,8 @@ Mob::Mob(const char* in_name,
casting_spell_id = 0;
casting_spell_timer = 0;
casting_spell_timer_duration = 0;
casting_spell_type = 0;
casting_spell_inventory_slot = 0;
casting_spell_aa_id = 0;
target = 0;
ActiveProjectileATK = false;
@ -725,7 +726,7 @@ int Mob::_GetFearSpeed() const {
movemod = -85;
if (IsClient()) {
if (CastToClient()->IsRunning())
if (CastToClient()->GetRunMode())
speed_mod = GetBaseRunspeed();
else
speed_mod = GetBaseWalkspeed();
@ -1085,7 +1086,7 @@ void Mob::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho)
ns->spawn.max_hp = 100; //this field needs a better name
ns->spawn.race = race;
ns->spawn.runspeed = runspeed;
ns->spawn.walkspeed = runspeed * 0.5f;
ns->spawn.walkspeed = walkspeed;
ns->spawn.class_ = class_;
ns->spawn.gender = gender;
ns->spawn.level = level;
@ -1142,6 +1143,10 @@ void Mob::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho)
else
ns->spawn.flymode = flymode;
if(IsBoat()) {
ns->spawn.flymode = 1;
}
ns->spawn.lastName[0] = '\0';
strn0cpy(ns->spawn.lastName, lastname, sizeof(ns->spawn.lastName));
@ -1354,8 +1359,12 @@ void Mob::SendHPUpdate(bool skip_self)
}
}
bool dospam = RuleB(Character, SpamHPUpdates);
// send to self - we need the actual hps here
if(IsClient() && !skip_self) {
if(IsClient() && (!skip_self || dospam)) {
this->CastToClient()->SendHPUpdateMarquee();
EQApplicationPacket* hp_app2 = new EQApplicationPacket(OP_HPUpdate,sizeof(SpawnHPUpdate_Struct));
SpawnHPUpdate_Struct* ds = (SpawnHPUpdate_Struct*)hp_app2->pBuffer;
ds->cur_hp = CastToClient()->GetHP() - itembonuses.HP;
@ -1364,7 +1373,8 @@ void Mob::SendHPUpdate(bool skip_self)
CastToClient()->QueuePacket(hp_app2);
safe_delete(hp_app2);
}
ResetHPUpdateTimer(); // delay the timer
if (!dospam)
ResetHPUpdateTimer(); // delay the timer
}
// this one just warps the mob to the current location
@ -1385,19 +1395,23 @@ void Mob::SendPosUpdate(uint8 iSendToSelf) {
MakeSpawnUpdate(spu);
if (iSendToSelf == 2) {
if (this->IsClient())
this->CastToClient()->FastQueuePacket(&app,false);
if (IsClient()) {
CastToClient()->FastQueuePacket(&app,false);
}
}
else
{
if(move_tic_count == RuleI(Zone, NPCPositonUpdateTicCount))
{
entity_list.QueueClients(this, app, (iSendToSelf==0), false);
entity_list.QueueClients(this, app, (iSendToSelf == 0), false);
move_tic_count = 0;
}
else
else if(move_tic_count % 2 == 0)
{
entity_list.QueueCloseClients(this, app, (iSendToSelf==0), 800, nullptr, false);
entity_list.QueueCloseClients(this, app, (iSendToSelf == 0), 700, nullptr, false);
move_tic_count++;
}
else {
move_tic_count++;
}
}
@ -1475,7 +1489,7 @@ void Mob::ShowStats(Client* client)
if(n->respawn2 != 0)
spawngroupid = n->respawn2->SpawnGroupID();
client->Message(0, " NPCID: %u SpawnGroupID: %u Grid: %i LootTable: %u FactionID: %i SpellsID: %u ", GetNPCTypeID(),spawngroupid, n->GetGrid(), n->GetLoottableID(), n->GetNPCFactionID(), n->GetNPCSpellsID());
client->Message(0, " Accuracy: %i MerchantID: %i EmoteID: %i Runspeed: %u Walkspeed: %u", n->GetAccuracyRating(), n->MerchantType, n->GetEmoteID(), n->GetRunspeed(), n->GetWalkspeed());
client->Message(0, " Accuracy: %i MerchantID: %i EmoteID: %i Runspeed: %.3f Walkspeed: %.3f", n->GetAccuracyRating(), n->MerchantType, n->GetEmoteID(), static_cast<float>(0.025f * n->GetRunspeed()), static_cast<float>(0.025f * n->GetWalkspeed()));
n->QueryLoot(client);
}
if (IsAIControlled()) {
@ -2399,6 +2413,14 @@ bool Mob::CanThisClassDoubleAttack(void) const
}
}
bool Mob::CanThisClassTripleAttack() const
{
if (!IsClient())
return false; // When they added the real triple attack skill, mobs lost the ability to triple
else
return CastToClient()->HasSkill(SkillTripleAttack);
}
bool Mob::IsWarriorClass(void) const
{
switch(GetClass())
@ -3505,9 +3527,11 @@ void Mob::TriggerOnCast(uint32 focus_spell, uint32 spell_id, bool aa_trigger)
uint32 trigger_spell_id = 0;
if (aa_trigger && IsClient()){
//focus_spell = aaid
trigger_spell_id = CastToClient()->CalcAAFocus(focusTriggerOnCast, focus_spell, spell_id);
if (aa_trigger && IsClient()) {
// focus_spell = aaid
auto rank = zone->GetAlternateAdvancementRank(focus_spell);
if (rank)
trigger_spell_id = CastToClient()->CalcAAFocus(focusTriggerOnCast, *rank, spell_id);
if(IsValidSpell(trigger_spell_id) && GetTarget())
SpellFinished(trigger_spell_id, GetTarget(), 10, 0, -1, spells[trigger_spell_id].ResistDiff);
@ -3763,11 +3787,8 @@ int16 Mob::GetSkillDmgTaken(const SkillUseTypes skill_used)
skilldmg_mod += itembonuses.SkillDmgTaken[HIGHEST_SKILL+1] + spellbonuses.SkillDmgTaken[HIGHEST_SKILL+1] +
itembonuses.SkillDmgTaken[skill_used] + spellbonuses.SkillDmgTaken[skill_used];
skilldmg_mod += SkillDmgTaken_Mod[skill_used] + SkillDmgTaken_Mod[HIGHEST_SKILL+1];
skilldmg_mod += spellbonuses.MeleeVulnerability + itembonuses.MeleeVulnerability + aabonuses.MeleeVulnerability;
if(skilldmg_mod < -100)
skilldmg_mod = -100;

View File

@ -23,6 +23,8 @@
#include "hate_list.h"
#include "pathing.h"
#include "position.h"
#include "aa_ability.h"
#include "aa.h"
#include <set>
#include <vector>
#include <memory>
@ -147,7 +149,7 @@ public:
int MonkSpecialAttack(Mob* other, uint8 skill_used);
virtual void TryBackstab(Mob *other,int ReuseTime = 10);
void TriggerDefensiveProcs(const ItemInst* weapon, Mob *on, uint16 hand = MainPrimary, int damage = 0);
virtual bool AvoidDamage(Mob* attacker, int32 &damage, bool CanRiposte = true);
bool AvoidDamage(Mob* attacker, int32 &damage, int hand);
virtual bool CheckHitChance(Mob* attacker, SkillUseTypes skillinuse, int Hand, int16 chance_mod = 0);
virtual void TryCriticalHit(Mob *defender, uint16 skill, int32 &damage, ExtraAttackOptions *opts = nullptr);
void TryPetCriticalHit(Mob *defender, uint16 skill, int32 &damage);
@ -164,6 +166,20 @@ public:
void CommonOutgoingHitSuccess(Mob* defender, int32 &damage, SkillUseTypes skillInUse);
void CommonBreakInvisible();
bool HasDied();
virtual bool CheckDualWield();
void DoMainHandAttackRounds(Mob *target, ExtraAttackOptions *opts = nullptr);
void DoOffHandAttackRounds(Mob *target, ExtraAttackOptions *opts = nullptr);
virtual bool CheckDoubleAttack();
// inline process for places where we need to do them outside of the AI_Process
void ProcessAttackRounds(Mob *target, ExtraAttackOptions *opts = nullptr)
{
if (target) {
DoMainHandAttackRounds(target, opts);
if (CanThisClassDualWield())
DoOffHandAttackRounds(target, opts);
}
return;
}
//Appearance
void SendLevelAppearance();
@ -221,10 +237,12 @@ public:
virtual void SpellProcess();
virtual bool CastSpell(uint16 spell_id, uint16 target_id, uint16 slot = USE_ITEM_SPELL_SLOT, int32 casttime = -1,
int32 mana_cost = -1, uint32* oSpellWillFinish = 0, uint32 item_slot = 0xFFFFFFFF,
uint32 timer = 0xFFFFFFFF, uint32 timer_duration = 0, uint32 type = 0, int16 *resist_adjust = nullptr);
uint32 timer = 0xFFFFFFFF, uint32 timer_duration = 0, int16 *resist_adjust = nullptr,
uint32 aa_id = 0);
virtual bool DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot = 10, int32 casttime = -1,
int32 mana_cost = -1, uint32* oSpellWillFinish = 0, uint32 item_slot = 0xFFFFFFFF,
uint32 timer = 0xFFFFFFFF, uint32 timer_duration = 0, uint32 type = 0, int16 resist_adjust = 0);
uint32 timer = 0xFFFFFFFF, uint32 timer_duration = 0, int16 resist_adjust = 0,
uint32 aa_id = 0);
void CastedSpellFinished(uint16 spell_id, uint32 target_id, uint16 slot, uint16 mana_used,
uint32 inventory_slot = 0xFFFFFFFF, int16 resist_adjust = 0);
bool SpellFinished(uint16 spell_id, Mob *target, uint16 slot = 10, uint16 mana_used = 0,
@ -318,6 +336,8 @@ public:
inline void SetShieldEquiped(bool val) { has_shieldequiped = val; }
bool HasTwoHandBluntEquiped() const { return has_twohandbluntequiped; }
inline void SetTwoHandBluntEquiped(bool val) { has_twohandbluntequiped = val; }
bool HasTwoHanderEquipped() { return has_twohanderequipped; }
void SetTwoHanderEquipped(bool val) { has_twohanderequipped = val; }
virtual uint16 GetSkill(SkillUseTypes skill_num) const { return 0; }
virtual uint32 GetEquipment(uint8 material_slot) const { return(0); }
virtual int32 GetEquipmentMaterial(uint8 material_slot) const;
@ -365,7 +385,7 @@ public:
inline Mob* GetTarget() const { return target; }
virtual void SetTarget(Mob* mob);
virtual inline float GetHPRatio() const { return max_hp == 0 ? 0 : ((float)cur_hp/max_hp*100); }
virtual inline float GetIntHPRatio() const { return max_hp == 0 ? 0 : (cur_hp/max_hp*100); }
virtual inline int GetIntHPRatio() const { return max_hp == 0 ? 0 : static_cast<int>(cur_hp * 100 / max_hp); }
inline virtual int32 GetAC() const { return AC + itembonuses.AC + spellbonuses.AC; }
inline virtual int32 GetATK() const { return ATK + itembonuses.ATK + spellbonuses.ATK; }
inline virtual int32 GetATKBonus() const { return itembonuses.ATK + spellbonuses.ATK; }
@ -730,6 +750,7 @@ public:
virtual int GetMonkHandToHandDamage(void);
bool CanThisClassDoubleAttack(void) const;
bool CanThisClassTripleAttack() const;
bool CanThisClassDualWield(void) const;
bool CanThisClassRiposte(void) const;
bool CanThisClassDodge(void) const;
@ -860,7 +881,6 @@ public:
uint32 GetZoneID() const; //for perl
virtual int32 CheckAggroAmount(uint16 spell_id, bool isproc = false);
virtual int32 CheckHealAggroAmount(uint16 spell_id, uint32 heal_possible = 0);
virtual uint32 GetAA(uint32 aa_id) const { return(0); }
uint32 GetInstrumentMod(uint16 spell_id) const;
int CalcSpellEffectValue(uint16 spell_id, int effect_id, int caster_level = 1, uint32 instrument_mod = 10, Mob *caster = nullptr, int ticsremaining = 0);
@ -956,6 +976,19 @@ public:
void Tune_FindAccuaryByHitChance(Mob* defender, Mob *attacker, float hit_chance, int interval, int max_loop, int avoid_override, int Msg = 0);
void Tune_FindAvoidanceByHitChance(Mob* defender, Mob *attacker, float hit_chance, int interval, int max_loop, int acc_override, int Msg = 0);
//aa new
uint32 GetAA(uint32 rank_id, uint32 *charges = nullptr) const;
uint32 GetAAByAAID(uint32 aa_id, uint32 *charges = nullptr) const;
bool SetAA(uint32 rank_id, uint32 new_value, uint32 charges = 0);
void ClearAAs() { aa_ranks.clear(); }
bool CanUseAlternateAdvancementRank(AA::Rank *rank);
bool CanPurchaseAlternateAdvancementRank(AA::Rank *rank, bool check_price, bool check_grant);
int GetAlternateAdvancementCooldownReduction(AA::Rank *rank_in);
void ExpendAlternateAdvancementCharge(uint32 aa_id);
void CalcAABonuses(StatBonuses* newbon);
void ApplyAABonuses(const AA::Rank &rank, StatBonuses* newbon);
bool CheckAATimer(int timer);
protected:
void CommonDamage(Mob* other, int32 &damage, const uint16 spell_id, const SkillUseTypes attack_skill, bool &avoidable, const int8 buffslot, const bool iBuffTic);
static uint16 GetProcID(uint16 spell_id, uint8 effect_index);
@ -969,7 +1002,6 @@ protected:
virtual bool AI_PursueCastCheck() { return(false); }
virtual bool AI_IdleCastCheck() { return(false); }
bool IsFullHP;
bool moved;
@ -1145,6 +1177,7 @@ protected:
uint32 casting_spell_timer_duration;
uint32 casting_spell_type;
int16 casting_spell_resist_adjust;
uint32 casting_spell_aa_id;
bool casting_spell_checks;
uint16 bardsong;
uint8 bardsong_slot;
@ -1191,6 +1224,7 @@ protected:
bool offhand;
bool has_shieldequiped;
bool has_twohandbluntequiped;
bool has_twohanderequipped;
bool has_numhits;
bool has_MGB;
bool has_ProjectIllusion;
@ -1311,6 +1345,9 @@ protected:
bool bEnraged;
bool destructibleobject;
std::unordered_map<uint32, std::pair<uint32, uint32>> aa_ranks;
Timer aa_timers[aaTimerMax];
private:
void _StopSong(); //this is not what you think it is
Mob* target;

View File

@ -342,7 +342,7 @@ bool NPC::AIDoSpellCast(uint8 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgain
SetCurrentSpeed(0);
}
return CastSpell(AIspells[i].spellid, tar->GetID(), 1, AIspells[i].manacost == -2 ? 0 : -1, mana_cost, oDontDoAgainBefore, -1, -1, 0, 0, &(AIspells[i].resist_adjust));
return CastSpell(AIspells[i].spellid, tar->GetID(), 1, AIspells[i].manacost == -2 ? 0 : -1, mana_cost, oDontDoAgainBefore, -1, -1, 0, &(AIspells[i].resist_adjust));
}
bool EntityList::AICheckCloseBeneficialSpells(NPC* caster, uint8 iChance, float iRange, uint16 iSpellTypes) {
@ -772,7 +772,10 @@ void Client::AI_Process()
engaged = true;
} else {
if(AImovement_timer->Check()) {
//animation = GetFearSpeed() * 21;
int speed = GetFearSpeed();
animation = speed;
speed *= 2;
SetCurrentSpeed(speed);
// Check if we have reached the last fear point
if ((std::abs(GetX() - m_FearWalkTarget.x) < 0.1) &&
(std::abs(GetY() - m_FearWalkTarget.y) < 0.1)) {
@ -780,18 +783,18 @@ void Client::AI_Process()
CalculateNewFearpoint();
}
if(!RuleB(Pathing, Fear) || !zone->pathing)
CalculateNewPosition2(m_FearWalkTarget.x, m_FearWalkTarget.y, m_FearWalkTarget.z, GetFearSpeed(), true);
CalculateNewPosition2(m_FearWalkTarget.x, m_FearWalkTarget.y, m_FearWalkTarget.z, speed, true);
else
{
bool WaypointChanged, NodeReached;
glm::vec3 Goal = UpdatePath(m_FearWalkTarget.x, m_FearWalkTarget.y, m_FearWalkTarget.z,
GetFearSpeed(), WaypointChanged, NodeReached);
speed, WaypointChanged, NodeReached);
if(WaypointChanged)
tar_ndx = 20;
CalculateNewPosition2(Goal.x, Goal.y, Goal.z, GetFearSpeed());
CalculateNewPosition2(Goal.x, Goal.y, Goal.z, speed);
}
}
return;
@ -824,118 +827,45 @@ void Client::AI_Process()
bool is_combat_range = CombatRange(GetTarget());
if(is_combat_range) {
if(charm_class_attacks_timer.Check()) {
if (is_combat_range) {
if (charm_class_attacks_timer.Check()) {
DoClassAttacks(GetTarget());
}
if (AImovement_timer->Check()) {
if(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()) != m_Position.w)
{
if (CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()) !=
m_Position.w) {
SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()));
SendPosition();
}
SetCurrentSpeed(0);
}
if(GetTarget() && !IsStunned() && !IsMezzed() && !GetFeigned()) {
if(attack_timer.Check()) {
Attack(GetTarget(), MainPrimary);
if(GetTarget()) {
if(CheckDoubleAttack()) {
Attack(GetTarget(), MainPrimary);
if(GetTarget()) {
bool triple_attack_success = false;
if((((GetClass() == MONK || GetClass() == WARRIOR || GetClass() == RANGER || GetClass() == BERSERKER)
&& GetLevel() >= 60) || GetSpecialAbility(SPECATK_TRIPLE))
&& CheckDoubleAttack(true))
{
Attack(GetTarget(), MainPrimary, true);
triple_attack_success = true;
}
if(GetTarget())
{
//Live AA - Flurry, Rapid Strikes ect (Flurry does not require Triple Attack).
int16 flurrychance = aabonuses.FlurryChance + spellbonuses.FlurryChance + itembonuses.FlurryChance;
if (flurrychance)
{
if(zone->random.Roll(flurrychance))
{
Message_StringID(MT_NPCFlurry, YOU_FLURRY);
Attack(GetTarget(), MainPrimary, false);
Attack(GetTarget(), MainPrimary, false);
}
}
int16 ExtraAttackChanceBonus = spellbonuses.ExtraAttackChance + itembonuses.ExtraAttackChance + aabonuses.ExtraAttackChance;
if (ExtraAttackChanceBonus && GetTarget()) {
ItemInst *wpn = GetInv().GetItem(MainPrimary);
if(wpn){
if(wpn->GetItem()->ItemType == ItemType2HSlash ||
wpn->GetItem()->ItemType == ItemType2HBlunt ||
wpn->GetItem()->ItemType == ItemType2HPiercing )
{
if(zone->random.Roll(ExtraAttackChanceBonus))
{
Attack(GetTarget(), MainPrimary, false);
}
}
}
}
if (GetClass() == WARRIOR || GetClass() == BERSERKER)
{
if(!dead && !berserk && this->GetHPRatio() < 30)
{
entity_list.MessageClose_StringID(this, false, 200, 0, BERSERK_START, GetName());
berserk = true;
}
else if (berserk && this->GetHPRatio() > 30)
{
entity_list.MessageClose_StringID(this, false, 200, 0, BERSERK_END, GetName());
berserk = false;
}
}
}
}
}
}
if (GetTarget() && !IsStunned() && !IsMezzed() && !GetFeigned()) {
if (attack_timer.Check()) {
// Should charmed clients not be procing?
DoAttackRounds(GetTarget(), MainPrimary);
}
}
if(CanThisClassDualWield() && attack_dw_timer.Check())
{
if(GetTarget())
{
float DualWieldProbability = 0.0f;
int16 Ambidexterity = aabonuses.Ambidexterity + spellbonuses.Ambidexterity + itembonuses.Ambidexterity;
DualWieldProbability = (GetSkill(SkillDualWield) + GetLevel() + Ambidexterity) / 400.0f; // 78.0 max
int16 DWBonus = spellbonuses.DualWieldChance + itembonuses.DualWieldChance;
DualWieldProbability += DualWieldProbability*float(DWBonus)/ 100.0f;
if(zone->random.Roll(DualWieldProbability))
{
Attack(GetTarget(), MainSecondary);
if(CheckDoubleAttack())
{
Attack(GetTarget(), MainSecondary);
}
if (CanThisClassDualWield() && GetTarget() && !IsStunned() && !IsMezzed() && !GetFeigned()) {
if (attack_dw_timer.Check()) {
if (CheckDualWield()) {
// Should charmed clients not be procing?
DoAttackRounds(GetTarget(), MainSecondary);
}
}
}
}
else
{
} else {
if(!IsRooted())
{
if(AImovement_timer->Check())
{
int newspeed = GetRunspeed();
animation = newspeed;
newspeed *= 2;
SetCurrentSpeed(newspeed);
if(!RuleB(Pathing, Aggro) || !zone->pathing)
CalculateNewPosition2(GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ(), GetRunspeed());
CalculateNewPosition2(GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ(), newspeed);
else
{
bool WaypointChanged, NodeReached;
@ -945,7 +875,7 @@ void Client::AI_Process()
if(WaypointChanged)
tar_ndx = 20;
CalculateNewPosition2(Goal.x, Goal.y, Goal.z, GetRunspeed());
CalculateNewPosition2(Goal.x, Goal.y, Goal.z, newspeed);
}
}
}
@ -989,11 +919,12 @@ void Client::AI_Process()
{
if(AImovement_timer->Check())
{
int speed = GetWalkspeed();
if (dist >= 5625)
speed = GetRunspeed();
CalculateNewPosition2(owner->GetX(), owner->GetY(), owner->GetZ(), speed);
int nspeed = (dist >= 5625 ? GetRunspeed() : GetWalkspeed());
animation = nspeed;
nspeed *= 2;
SetCurrentSpeed(nspeed);
CalculateNewPosition2(owner->GetX(), owner->GetY(), owner->GetZ(), nspeed);
}
}
else
@ -1170,43 +1101,9 @@ void Mob::AI_Process() {
//try main hand first
if(attack_timer.Check()) {
if(IsNPC()) {
int16 n_atk = CastToNPC()->GetNumberOfAttacks();
if(n_atk <= 1) {
Attack(target, MainPrimary);
} else {
for(int i = 0; i < n_atk; ++i) {
Attack(target, MainPrimary);
}
}
} else {
Attack(target, MainPrimary);
}
if (target) {
//we use this random value in three comparisons with different
//thresholds, and if its truely random, then this should work
//out reasonably and will save us compute resources.
int32 RandRoll = zone->random.Int(0, 99);
if ((CanThisClassDoubleAttack() || GetSpecialAbility(SPECATK_TRIPLE)
|| GetSpecialAbility(SPECATK_QUAD))
//check double attack, this is NOT the same rules that clients use...
&& RandRoll < (GetLevel() + NPCDualAttackModifier)) {
Attack(target, MainPrimary);
// lets see if we can do a triple attack with the main hand
//pets are excluded from triple and quads...
if ((GetSpecialAbility(SPECATK_TRIPLE) || GetSpecialAbility(SPECATK_QUAD))
&& !IsPet() && RandRoll < (GetLevel() + NPCTripleAttackModifier)) {
Attack(target, MainPrimary);
// now lets check the quad attack
if (GetSpecialAbility(SPECATK_QUAD)
&& RandRoll < (GetLevel() + NPCQuadAttackModifier)) {
Attack(target, MainPrimary);
}
}
}
}
DoMainHandAttackRounds(target);
bool specialed = false; // NPCs can only do one of these a round
if (GetSpecialAbility(SPECATK_FLURRY)) {
int flurry_chance = GetSpecialAbilityParam(SPECATK_FLURRY, 0);
flurry_chance = flurry_chance > 0 ? flurry_chance : RuleI(Combat, NPCFlurryChance);
@ -1238,6 +1135,7 @@ void Mob::AI_Process() {
opts.crit_flat = cur;
Flurry(&opts);
specialed = true;
}
}
@ -1258,7 +1156,7 @@ void Mob::AI_Process() {
}
}
if (GetSpecialAbility(SPECATK_RAMPAGE))
if (GetSpecialAbility(SPECATK_RAMPAGE) && !specialed)
{
int rampage_chance = GetSpecialAbilityParam(SPECATK_RAMPAGE, 0);
rampage_chance = rampage_chance > 0 ? rampage_chance : 20;
@ -1294,10 +1192,11 @@ void Mob::AI_Process() {
opts.crit_flat = cur;
}
Rampage(&opts);
specialed = true;
}
}
if (GetSpecialAbility(SPECATK_AREA_RAMPAGE))
if (GetSpecialAbility(SPECATK_AREA_RAMPAGE) && !specialed)
{
int rampage_chance = GetSpecialAbilityParam(SPECATK_AREA_RAMPAGE, 0);
rampage_chance = rampage_chance > 0 ? rampage_chance : 20;
@ -1334,30 +1233,14 @@ void Mob::AI_Process() {
}
AreaRampage(&opts);
specialed = true;
}
}
}
//now off hand
if (attack_dw_timer.Check() && CanThisClassDualWield())
{
int myclass = GetClass();
//can only dual wield without a weapon if your a monk
if(GetSpecialAbility(SPECATK_INNATE_DW) || (GetEquipment(MaterialSecondary) != 0 && GetLevel() > 29) || myclass == MONK || myclass == MONKGM) {
float DualWieldProbability = (GetSkill(SkillDualWield) + GetLevel()) / 400.0f;
if(zone->random.Roll(DualWieldProbability))
{
Attack(target, MainSecondary);
if (CanThisClassDoubleAttack())
{
if (zone->random.Roll(GetLevel() + 20))
{
Attack(target, MainSecondary);
}
}
}
}
}
DoOffHandAttackRounds(target);
//now special attacks (kick, etc)
if(IsNPC())
@ -1494,7 +1377,7 @@ void Mob::AI_Process() {
int speed = GetWalkspeed();
if (dist >= 5625)
speed = GetRunspeed();
CalculateNewPosition2(owner->GetX(), owner->GetY(), owner->GetZ(), speed);
}
else
@ -2095,14 +1978,14 @@ bool Mob::Rampage(ExtraAttackOptions *opts)
if (m_target == GetTarget())
continue;
if (CombatRange(m_target)) {
Attack(m_target, MainPrimary, false, false, false, opts);
ProcessAttackRounds(m_target, opts);
index_hit++;
}
}
}
if (RuleB(Combat, RampageHitsTarget) && index_hit < rampage_targets)
Attack(GetTarget(), MainPrimary, false, false, false, opts);
ProcessAttackRounds(GetTarget(), opts);
return true;
}
@ -2120,9 +2003,8 @@ void Mob::AreaRampage(ExtraAttackOptions *opts)
rampage_targets = rampage_targets > 0 ? rampage_targets : 1;
index_hit = hate_list.AreaRampage(this, GetTarget(), rampage_targets, opts);
if(index_hit == 0) {
Attack(GetTarget(), MainPrimary, false, false, false, opts);
}
if(index_hit == 0)
ProcessAttackRounds(GetTarget(), opts);
}
uint32 Mob::GetLevelCon(uint8 mylevel, uint8 iOtherLevel) {

View File

@ -28,7 +28,6 @@
#include "../common/eq_packet_structs.h"
#include "../common/mutex.h"
#include "../common/version.h"
#include "../common/packet_dump_file.h"
#include "../common/opcodemgr.h"
#include "../common/guilds.h"
@ -57,17 +56,14 @@
#include "titles.h"
#include "guild_mgr.h"
#include "tasks.h"
#include "quest_parser_collection.h"
#include "embparser.h"
#include "lua_parser.h"
#include "questmgr.h"
#include <iostream>
#include <string>
#include <fstream>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
@ -96,7 +92,6 @@ EntityList entity_list;
WorldServer worldserver;
uint32 numclients = 0;
char errorname[32];
uint16 adverrornum = 0;
extern Zone* zone;
EQStreamFactory eqsf(ZoneStream);
npcDecayTimes_Struct npcCorpseDecayTimes[100];
@ -116,28 +111,77 @@ int main(int argc, char** argv) {
RegisterExecutablePlatform(ExePlatformZone);
Log.LoadLogSettingsDefaults();
set_exception_handler();
const char *zone_name;
QServ = new QueryServ;
if(argc == 3) {
Log.Out(Logs::General, Logs::Zone_Server, "Loading server configuration..");
if(!ZoneConfig::LoadConfig()) {
Log.Out(Logs::General, Logs::Error, "Loading server configuration failed.");
return 1;
}
const ZoneConfig *Config = ZoneConfig::get();
const char *zone_name;
uint32 instance_id = 0;
std::string z_name;
if(argc == 4) {
instance_id = atoi(argv[3]);
worldserver.SetLauncherName(argv[2]);
worldserver.SetLaunchedName(argv[1]);
if(strncmp(argv[1], "dynamic_", 8) == 0) {
//dynamic zone with a launcher name correlation
auto zone_port = SplitString(argv[1], ':');
if(zone_port.size() > 0) {
z_name = zone_port[0];
}
if(zone_port.size() > 1) {
std::string p_name = zone_port[1];
Config->SetZonePort(atoi(p_name.c_str()));
}
worldserver.SetLaunchedName(z_name.c_str());
if(strncmp(z_name.c_str(), "dynamic_", 8) == 0) {
zone_name = ".";
}
else {
zone_name = z_name.c_str();
}
} else if(argc == 3) {
worldserver.SetLauncherName(argv[2]);
auto zone_port = SplitString(argv[1], ':');
if(zone_port.size() > 0) {
z_name = zone_port[0];
}
if(zone_port.size() > 1) {
std::string p_name = zone_port[1];
Config->SetZonePort(atoi(p_name.c_str()));
}
worldserver.SetLaunchedName(z_name.c_str());
if(strncmp(z_name.c_str(), "dynamic_", 8) == 0) {
zone_name = ".";
} else {
zone_name = argv[1];
worldserver.SetLaunchedName(zone_name);
zone_name = z_name.c_str();
}
} else if (argc == 2) {
worldserver.SetLauncherName("NONE");
worldserver.SetLaunchedName(argv[1]);
if(strncmp(argv[1], "dynamic_", 8) == 0) {
//dynamic zone with a launcher name correlation
auto zone_port = SplitString(argv[1], ':');
if(zone_port.size() > 0) {
z_name = zone_port[0];
}
if(zone_port.size() > 1) {
std::string p_name = zone_port[1];
Config->SetZonePort(atoi(p_name.c_str()));
}
worldserver.SetLaunchedName(z_name.c_str());
if(strncmp(z_name.c_str(), "dynamic_", 8) == 0) {
zone_name = ".";
} else {
zone_name = argv[1];
worldserver.SetLaunchedName(zone_name);
}
else {
zone_name = z_name.c_str();
}
} else {
zone_name = ".";
@ -145,13 +189,6 @@ int main(int argc, char** argv) {
worldserver.SetLauncherName("NONE");
}
Log.Out(Logs::General, Logs::Zone_Server, "Loading server configuration..");
if (!ZoneConfig::LoadConfig()) {
Log.Out(Logs::General, Logs::Error, "Loading server configuration failed.");
return 1;
}
const ZoneConfig *Config = ZoneConfig::get();
worldserver.SetPassword(Config->SharedKey.c_str());
Log.Out(Logs::General, Logs::Zone_Server, "Connecting to MySQL...");
@ -257,9 +294,6 @@ int main(int argc, char** argv) {
Log.Out(Logs::General, Logs::Zone_Server, "Loading titles");
title_manager.LoadTitles();
Log.Out(Logs::General, Logs::Zone_Server, "Loading AA effects");
database.LoadAAEffects();
Log.Out(Logs::General, Logs::Zone_Server, "Loading tributes");
database.LoadTributes();
@ -324,7 +358,7 @@ int main(int argc, char** argv) {
#endif
if (!strlen(zone_name) || !strcmp(zone_name,".")) {
Log.Out(Logs::General, Logs::Zone_Server, "Entering sleep mode");
} else if (!Zone::Bootup(database.GetZoneID(zone_name), 0, true)) { //todo: go above and fix this to allow cmd line instance
} else if (!Zone::Bootup(database.GetZoneID(zone_name), instance_id, true)) {
Log.Out(Logs::General, Logs::Error, "Zone Bootup failed :: Zone::Bootup");
zone = 0;
}

View File

@ -31,7 +31,6 @@
#include "../common/linked_list.h"
#include "../common/servertalk.h"
#include "aa.h"
#include "client.h"
#include "entity.h"
#include "npc.h"
@ -279,6 +278,18 @@ NPC::NPC(const NPCType* d, Spawn2* in_respawn, const glm::vec4& position, int if
for(r = 0; r <= HIGHEST_SKILL; r++) {
skills[r] = database.GetSkillCap(GetClass(),(SkillUseTypes)r,moblevel);
}
// some overrides -- really we need to be able to set skills for mobs in the DB
// There are some known low level SHM/BST pets that do not follow this, which supports
// the theory of needing to be able to set skills for each mob separately
if (moblevel > 50) {
skills[SkillDoubleAttack] = 250;
skills[SkillDualWield] = 250;
} else if (moblevel > 3) {
skills[SkillDoubleAttack] = moblevel * 5;
skills[SkillDualWield] = skills[SkillDoubleAttack];
} else {
skills[SkillDoubleAttack] = moblevel * 5;
}
if(d->trap_template > 0)
{
@ -1929,7 +1940,15 @@ void NPC::ModifyNPCStat(const char *identifier, const char *newValue)
else if(id == "pr") { PR = atoi(val.c_str()); return; }
else if(id == "dr") { DR = atoi(val.c_str()); return; }
else if(id == "PhR") { PhR = atoi(val.c_str()); return; }
else if(id == "runspeed") { runspeed = (float)atof(val.c_str()); CalcBonuses(); return; }
else if(id == "runspeed") {
runspeed = (float)atof(val.c_str());
base_runspeed = (int)((float)runspeed * 40.0f);
base_walkspeed = base_runspeed * 100 / 265;
walkspeed = ((float)base_walkspeed) * 0.025f;
base_fearspeed = base_runspeed * 100 / 127;
fearspeed = ((float)base_fearspeed) * 0.025f;
CalcBonuses(); return;
}
else if(id == "special_attacks") { NPCSpecialAttacks(val.c_str(), 0, 1); return; }
else if(id == "special_abilities") { ProcessSpecialAbilities(val.c_str()); return; }
else if(id == "attack_speed") { attack_speed = (float)atof(val.c_str()); CalcBonuses(); return; }

View File

@ -4077,33 +4077,7 @@ XS(XS_Client_RefundAA) {
if(THIS == nullptr)
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
int curpt = 0;
bool refunded = false;
for(int x1=0;x1<aaHighestID;x1++){
curpt = THIS->GetAA(x1);
if(curpt > 0){
SendAA_Struct* curaa = zone->FindAA(x1);
if(curaa){
THIS->SetAA(x1, 0);
for(int x2=0;x2<curpt;x2++){ //add up all the AA points pt by pt to get the correct cost
THIS->GetPP().aapoints += curaa->cost + (curaa->cost_inc * x2);
refunded = true;
}
}
else //aa doesn't exist.. but if they bought it then it had at least a cost of 1 point each
{ //so give back what we can
THIS->GetPP().aapoints += curpt;
THIS->SetAA(x1, 0);
refunded = true;
}
}
}
if(refunded){
THIS->Save(); //save of course
THIS->Kick(); //client gets all buggy if we don't immediatly relog so just force it on them
}
THIS->RefundAA();
}
XSRETURN_EMPTY;
}
@ -4915,11 +4889,44 @@ XS(XS_Client_IncrementAA)
if(THIS == nullptr)
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
THIS->IncrementAA(aaskillid);
THIS->IncrementAlternateAdvancementRank(aaskillid);
}
XSRETURN_EMPTY;
}
XS(XS_Client_GrantAlternateAdvancementAbility); /* prototype to pass -Wmissing-prototypes */
XS(XS_Client_GrantAlternateAdvancementAbility)
{
dXSARGS;
if(items < 3 || items > 4)
Perl_croak(aTHX_ "Usage: Client::GrantAlternateAdvancementAbility(THIS, aa_id, points, [ignore_cost])");
{
Client * THIS;
bool RETVAL;
int aa_id = (int)SvIV(ST(1));
int points = (int)SvIV(ST(2));
bool ignore_cost = false;
if(sv_derived_from(ST(0), "Client")) {
IV tmp = SvIV((SV*)SvRV(ST(0)));
THIS = INT2PTR(Client *, tmp);
}
else
Perl_croak(aTHX_ "THIS is not of type Client");
if(THIS == nullptr)
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
if(items > 3) {
ignore_cost = (bool)SvTRUE(ST(3));
}
RETVAL = THIS->GrantAlternateAdvancementAbility(aa_id, points, ignore_cost);
ST(0) = boolSV(RETVAL);
sv_2mortal(ST(0));
}
XSRETURN(1);
}
XS(XS_Client_GetAALevel);
XS(XS_Client_GetAALevel)
{
@ -6463,6 +6470,7 @@ XS(boot_Client)
newXSproto(strcpy(buf, "GetIP"), XS_Client_GetIP, file, "$");
newXSproto(strcpy(buf, "AddLevelBasedExp"), XS_Client_AddLevelBasedExp, file, "$$;$");
newXSproto(strcpy(buf, "IncrementAA"), XS_Client_IncrementAA, file, "$$");
newXSproto(strcpy(buf, "GrantAlternateAdvancementAbility"), XS_Client_GrantAlternateAdvancementAbility, file, "$$$;$");
newXSproto(strcpy(buf, "GetAALevel"), XS_Client_GetAALevel, file, "$$");
newXSproto(strcpy(buf, "MarkCompassLoc"), XS_Client_MarkCompassLoc, file, "$$$$");
newXSproto(strcpy(buf, "ClearCompassMark"), XS_Client_ClearCompassMark, file, "$");

View File

@ -3998,9 +3998,9 @@ XS(XS_Mob_CastSpell)
}
if (resist_adjust == 0)//If you do not pass resist adjust as nullptr it will ignore the spells default resist adjust
THIS->CastSpell(spell_id, target_id, slot, casttime, mana_cost, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0, 0);
THIS->CastSpell(spell_id, target_id, slot, casttime, mana_cost, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0);
else
THIS->CastSpell(spell_id, target_id, slot, casttime, mana_cost, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0, 0, &resist_adjust);
THIS->CastSpell(spell_id, target_id, slot, casttime, mana_cost, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0, &resist_adjust);
}
XSRETURN_EMPTY;
}
@ -6360,12 +6360,12 @@ XS(XS_Mob_GetAA)
{
dXSARGS;
if (items != 2)
Perl_croak(aTHX_ "Usage: Mob::GetAA(THIS, aa_id)");
Perl_croak(aTHX_ "Usage: Mob::GetAA(THIS, rank_id)");
{
Mob * THIS;
uint32 RETVAL;
dXSTARG;
uint32 aa_id = (uint32)SvUV(ST(1));
uint32 rank_id = (uint32)SvUV(ST(1));
if (sv_derived_from(ST(0), "Mob")) {
IV tmp = SvIV((SV*)SvRV(ST(0)));
@ -6376,12 +6376,68 @@ XS(XS_Mob_GetAA)
if(THIS == nullptr)
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
RETVAL = THIS->GetAA(aa_id);
RETVAL = THIS->GetAA(rank_id);
XSprePUSH; PUSHu((UV)RETVAL);
}
XSRETURN(1);
}
XS(XS_Mob_GetAAByAAID); /* prototype to pass -Wmissing-prototypes */
XS(XS_Mob_GetAAByAAID)
{
dXSARGS;
if(items != 2)
Perl_croak(aTHX_ "Usage: Mob::GetAAByAAID(THIS, aa_id)");
{
Mob * THIS;
uint32 RETVAL;
dXSTARG;
uint32 aa_id = (uint32)SvUV(ST(1));
if(sv_derived_from(ST(0), "Mob")) {
IV tmp = SvIV((SV*)SvRV(ST(0)));
THIS = INT2PTR(Mob *, tmp);
}
else
Perl_croak(aTHX_ "THIS is not of type Mob");
if(THIS == nullptr)
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
RETVAL = THIS->GetAAByAAID(aa_id);
XSprePUSH; PUSHu((UV)RETVAL);
}
XSRETURN(1);
}
XS(XS_Mob_SetAA); /* prototype to pass -Wmissing-prototypes */
XS(XS_Mob_SetAA)
{
dXSARGS;
if(items < 3 || items > 4)
Perl_croak(aTHX_ "Usage: Mob::SetAA(THIS, aa_id, points, [charges])");
{
Mob * THIS;
bool RETVAL;
int aa_id = (int)SvIV(ST(1));
int points = (int)SvIV(ST(2));
int charges = (items == 4) ? (int)SvIV(ST(3)) : 0;
if(sv_derived_from(ST(0), "Mob")) {
IV tmp = SvIV((SV*)SvRV(ST(0)));
THIS = INT2PTR(Mob *, tmp);
}
else
Perl_croak(aTHX_ "THIS is not of type Mob");
if(THIS == nullptr)
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
RETVAL = THIS->SetAA(aa_id, points, charges);
ST(0) = boolSV(RETVAL);
sv_2mortal(ST(0));
}
XSRETURN(1);
}
XS(XS_Mob_DivineAura); /* prototype to pass -Wmissing-prototypes */
XS(XS_Mob_DivineAura)
{
@ -8625,6 +8681,8 @@ XS(boot_Mob)
newXSproto(strcpy(buf, "CheckAggroAmount"), XS_Mob_CheckAggroAmount, file, "$$");
newXSproto(strcpy(buf, "CheckHealAggroAmount"), XS_Mob_CheckHealAggroAmount, file, "$$");
newXSproto(strcpy(buf, "GetAA"), XS_Mob_GetAA, file, "$$");
newXSproto(strcpy(buf, "GetAAByAAID"), XS_Mob_GetAAByAAID, file, "$$");
newXSproto(strcpy(buf, "SetAA"), XS_Mob_SetAA, file, "$$$;$");
newXSproto(strcpy(buf, "DivineAura"), XS_Mob_DivineAura, file, "$");
newXSproto(strcpy(buf, "AddFeignMemory"), XS_Mob_AddFeignMemory, file, "$$");
newXSproto(strcpy(buf, "RemoveFromFeignMemory"), XS_Mob_RemoveFromFeignMemory, file, "$$");

View File

@ -874,7 +874,7 @@ bool QuestManager::isdisctome(int item_id) {
void QuestManager::safemove() {
QuestManagerCurrentQuestVars();
if (initiator && initiator->IsClient())
initiator->GoToSafeCoords(zone->GetZoneID(), 0);
initiator->GoToSafeCoords(zone->GetZoneID(), zone->GetInstanceID());
}
void QuestManager::rain(int weather) {
@ -1774,7 +1774,7 @@ void QuestManager::sethp(int hpperc) {
owner->Damage(owner, newhp, SPELL_UNKNOWN, SkillHandtoHand, false, 0, false);
}
bool QuestManager::summonburriedplayercorpse(uint32 char_id, const glm::vec4& position) {
bool QuestManager::summonburiedplayercorpse(uint32 char_id, const glm::vec4& position) {
bool Result = false;
if(char_id <= 0)
@ -1798,7 +1798,7 @@ bool QuestManager::summonallplayercorpses(uint32 char_id, const glm::vec4& posit
return true;
}
uint32 QuestManager::getplayerburriedcorpsecount(uint32 char_id) {
uint32 QuestManager::getplayerburiedcorpsecount(uint32 char_id) {
uint32 Result = 0;
if(char_id > 0) {

View File

@ -159,9 +159,9 @@ public:
void set_zone_flag(int zone_id);
void clear_zone_flag(int zone_id);
void sethp(int hpperc);
bool summonburriedplayercorpse(uint32 char_id, const glm::vec4& position);
bool summonburiedplayercorpse(uint32 char_id, const glm::vec4& position);
bool summonallplayercorpses(uint32 char_id, const glm::vec4& position);
uint32 getplayerburriedcorpsecount(uint32 char_id);
uint32 getplayerburiedcorpsecount(uint32 char_id);
bool buryplayercorpse(uint32 char_id);
void forcedooropen(uint32 doorid, bool altmode);
void forcedoorclose(uint32 doorid, bool altmode);

View File

@ -130,49 +130,41 @@ void Mob::DoSpecialAttackDamage(Mob *who, SkillUseTypes skill, int32 max_damage,
min_damage += min_damage * GetMeleeMinDamageMod_SE(skill) / 100;
if(HitChance && !who->CheckHitChance(this, skill, MainPrimary))
max_damage = 0;
else{
bool CanRiposte = true;
if(skill == SkillThrowing || skill == SkillArchery) // changed from '&&'
CanRiposte = false;
if (CanAvoid)
who->AvoidDamage(this, max_damage, CanRiposte);
who->MeleeMitigation(this, max_damage, min_damage);
if(max_damage > 0)
int hand = MainPrimary; // Avoid checks hand for throwing/archery exclusion, primary should work for most
if (skill == SkillThrowing || skill == SkillArchery)
hand = MainRange;
if (who->AvoidDamage(this, max_damage, hand)) {
if (max_damage == -3)
DoRiposte(who);
} else {
if (HitChance || who->CheckHitChance(this, skill, MainPrimary)) {
who->MeleeMitigation(this, max_damage, min_damage);
CommonOutgoingHitSuccess(who, max_damage, skill);
} else {
max_damage = 0;
}
}
who->AddToHateList(this, hate, 0, false);
if (max_damage > 0 && aabonuses.SkillAttackProc[0] && aabonuses.SkillAttackProc[1] == skill &&
IsValidSpell(aabonuses.SkillAttackProc[2])) {
float chance = aabonuses.SkillAttackProc[0] / 1000.0f;
if (zone->random.Roll(chance))
SpellFinished(aabonuses.SkillAttackProc[2], who, 10, 0, -1,
spells[aabonuses.SkillAttackProc[2]].ResistDiff);
}
who->Damage(this, max_damage, SPELL_UNKNOWN, skill, false);
//Make sure 'this' has not killed the target and 'this' is not dead (Damage shield ect).
if(!GetTarget())return;
if (HasDied()) return;
//[AA Dragon Punch] value[0] = 100 for 25%, chance value[1] = skill
if(aabonuses.SpecialAttackKBProc[0] && aabonuses.SpecialAttackKBProc[1] == skill){
int kb_chance = 25;
kb_chance += kb_chance*(100-aabonuses.SpecialAttackKBProc[0])/100;
if (zone->random.Roll(kb_chance))
SpellFinished(904, who, 10, 0, -1, spells[904].ResistDiff);
//who->Stun(100); Kayen: This effect does not stun on live, it only moves the NPC.
}
if (HasSkillProcs())
TrySkillProc(who, skill, ReuseTime*1000);
if (max_damage > 0 && HasSkillProcSuccess())
TrySkillProc(who, skill, ReuseTime*1000, true);
if(max_damage == -3 && !who->HasDied())
DoRiposte(who);
}
@ -544,7 +536,7 @@ void Mob::TryBackstab(Mob *other, int ReuseTime) {
RogueBackstab(other,false,ReuseTime);
if (level > 54) {
if(IsClient() && CastToClient()->CheckDoubleAttack(false))
if(IsClient() && CastToClient()->CheckDoubleAttack())
{
if(other->GetHP() > 0)
RogueBackstab(other,false,ReuseTime);
@ -566,7 +558,7 @@ void Mob::TryBackstab(Mob *other, int ReuseTime) {
if (level > 54) {
// Check for double attack with main hand assuming maxed DA Skill (MS)
if(IsClient() && CastToClient()->CheckDoubleAttack(false))
if(IsClient() && CastToClient()->CheckDoubleAttack())
if(other->GetHP() > 0)
RogueBackstab(other,true, ReuseTime);
@ -975,7 +967,7 @@ void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Ite
}
if (!HeadShot)
other->AvoidDamage(this, TotalDmg, false);
other->AvoidDamage(this, TotalDmg, MainRange);
other->MeleeMitigation(this, TotalDmg, minDmg);
if(TotalDmg > 0){
@ -1308,7 +1300,7 @@ void NPC::DoRangedAttackDmg(Mob* other, bool Launch, int16 damage_mod, int16 cha
TotalDmg += TotalDmg * damage_mod / 100;
other->AvoidDamage(this, TotalDmg, false);
other->AvoidDamage(this, TotalDmg, MainRange);
other->MeleeMitigation(this, TotalDmg, MinDmg);
if (TotalDmg > 0)
@ -1542,7 +1534,7 @@ void Mob::DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Ite
Log.Out(Logs::Detail, Logs::Combat, "Item DMG %d. Max Damage %d. Hit for damage %d", WDmg, MaxDmg, TotalDmg);
if (!Assassinate_Dmg)
other->AvoidDamage(this, TotalDmg, false); //CanRiposte=false - Can not riposte throw attacks.
other->AvoidDamage(this, TotalDmg, MainRange);
other->MeleeMitigation(this, TotalDmg, minDmg);
if(TotalDmg > 0)
@ -2414,27 +2406,26 @@ void Mob::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes
else
damage = zone->random.Int(min_hit, max_hit);
if(!other->CheckHitChance(this, skillinuse, Hand, chance_mod)) {
damage = 0;
if (other->AvoidDamage(this, damage, CanRiposte ? MainRange : MainPrimary)) { // MainRange excludes ripo, primary doesn't have any extra behavior
if (damage == -3) {
DoRiposte(other);
if (HasDied())
return;
}
} else {
other->AvoidDamage(this, damage, CanRiposte);
other->MeleeMitigation(this, damage, min_hit);
if(damage > 0)
if (other->CheckHitChance(this, skillinuse, Hand, chance_mod)) {
other->MeleeMitigation(this, damage, min_hit);
CommonOutgoingHitSuccess(other, damage, skillinuse);
} else {
damage = 0;
}
}
if (damage == -3) {
DoRiposte(other);
if (HasDied())
return;
}
}
else
damage = -5;
other->AddToHateList(this, hate);
bool CanSkillProc = true;
if (skillinuse == SkillOffense){ //Hack to allow damage to display.
skillinuse = SkillTigerClaw; //'strike' your opponent - Arbitrary choice for message.
@ -2442,19 +2433,18 @@ void Mob::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes
}
other->AddToHateList(this, hate, 0, false);
if (damage > 0 && aabonuses.SkillAttackProc[0] && aabonuses.SkillAttackProc[1] == skillinuse &&
IsValidSpell(aabonuses.SkillAttackProc[2])) {
float chance = aabonuses.SkillAttackProc[0] / 1000.0f;
if (zone->random.Roll(chance))
SpellFinished(aabonuses.SkillAttackProc[2], other, 10, 0, -1,
spells[aabonuses.SkillAttackProc[2]].ResistDiff);
}
other->Damage(this, damage, SPELL_UNKNOWN, skillinuse);
if (HasDied())
return;
if(aabonuses.SpecialAttackKBProc[0] && aabonuses.SpecialAttackKBProc[1] == skillinuse){
int kb_chance = 25;
kb_chance += kb_chance*(100-aabonuses.SpecialAttackKBProc[0])/100;
if (zone->random.Roll(kb_chance))
SpellFinished(904, other, 10, 0, -1, spells[904].ResistDiff);
}
if (CanSkillProc && HasSkillProcs())
TrySkillProc(other, skillinuse, ReuseTime);

File diff suppressed because it is too large Load Diff

View File

@ -146,7 +146,8 @@ void NPC::SpellProcess()
// to allow procs to work
bool Mob::CastSpell(uint16 spell_id, uint16 target_id, uint16 slot,
int32 cast_time, int32 mana_cost, uint32* oSpellWillFinish, uint32 item_slot,
uint32 timer, uint32 timer_duration, uint32 type, int16 *resist_adjust)
uint32 timer, uint32 timer_duration, int16 *resist_adjust,
uint32 aa_id)
{
Log.Out(Logs::Detail, Logs::Spells, "CastSpell called for spell %s (%d) on entity %d, slot %d, time %d, mana %d, from item slot %d",
(IsValidSpell(spell_id))?spells[spell_id].name:"UNKNOWN SPELL", spell_id, target_id, slot, cast_time, mana_cost, (item_slot==0xFFFFFFFF)?999:item_slot);
@ -318,11 +319,11 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, uint16 slot,
if(resist_adjust)
{
return(DoCastSpell(spell_id, target_id, slot, cast_time, mana_cost, oSpellWillFinish, item_slot, timer, timer_duration, type, *resist_adjust));
return(DoCastSpell(spell_id, target_id, slot, cast_time, mana_cost, oSpellWillFinish, item_slot, timer, timer_duration, *resist_adjust, aa_id));
}
else
{
return(DoCastSpell(spell_id, target_id, slot, cast_time, mana_cost, oSpellWillFinish, item_slot, timer, timer_duration, type, spells[spell_id].ResistDiff));
return(DoCastSpell(spell_id, target_id, slot, cast_time, mana_cost, oSpellWillFinish, item_slot, timer, timer_duration, spells[spell_id].ResistDiff, aa_id));
}
}
@ -336,8 +337,8 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, uint16 slot,
//
bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot,
int32 cast_time, int32 mana_cost, uint32* oSpellWillFinish,
uint32 item_slot, uint32 timer, uint32 timer_duration, uint32 type,
int16 resist_adjust)
uint32 item_slot, uint32 timer, uint32 timer_duration,
int16 resist_adjust, uint32 aa_id)
{
Mob* pMob = nullptr;
int32 orgcasttime;
@ -361,7 +362,7 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot,
casting_spell_timer = timer;
casting_spell_timer_duration = timer_duration;
}
casting_spell_type = type;
casting_spell_aa_id = aa_id;
SaveSpellLoc();
Log.Out(Logs::Detail, Logs::Spells, "Casting %d Started at (%.3f,%.3f,%.3f)", spell_id, m_SpellLocation.x, m_SpellLocation.y, m_SpellLocation.z);
@ -408,24 +409,19 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot,
// ok now we know the target
casting_spell_targetid = target_id;
if (mana_cost == -1) {
// We don't get actual mana cost here, that's done when we consume the mana
if (mana_cost == -1)
mana_cost = spell.mana;
mana_cost = GetActSpellCost(spell_id, mana_cost);
}
if(HasMGB() && spells[spell_id].can_mgb)
mana_cost *= 2;
// mana is checked for clients on the frontend. we need to recheck it for NPCs though
// fix: items dont need mana :-/
// If you're at full mana, let it cast even if you dont have enough mana
// we calculated this above, now enforce it
if(mana_cost > 0 && slot != 10)
if(mana_cost > 0 && slot != USE_ITEM_SPELL_SLOT)
{
int my_curmana = GetMana();
int my_maxmana = GetMaxMana();
if(my_curmana < spell.mana) // not enough mana
if(my_curmana < mana_cost) // not enough mana
{
//this is a special case for NPCs with no mana...
if(IsNPC() && my_curmana == my_maxmana)
@ -783,9 +779,9 @@ void Mob::ZeroCastingVars()
casting_spell_inventory_slot = 0;
casting_spell_timer = 0;
casting_spell_timer_duration = 0;
casting_spell_type = 0;
casting_spell_resist_adjust = 0;
casting_spell_checks = false;
casting_spell_aa_id = 0;
delaytimer = false;
}
@ -816,10 +812,9 @@ void Mob::InterruptSpell(uint16 message, uint16 color, uint16 spellid)
CastToNPC()->AI_Event_SpellCastFinished(false, casting_spell_slot);
}
if(casting_spell_type == 1 && IsClient()) { //Rest AA Timer on failed cast
CastToClient()->SendAATimer(casting_spell_timer - pTimerAAStart, 0, 0xFFFFFF);
CastToClient()->Message_StringID(15,ABILITY_FAILED);
CastToClient()->GetPTimers().Clear(&database, casting_spell_timer);
if(casting_spell_aa_id && IsClient()) { //Rest AA Timer on failed cast
CastToClient()->Message_StringID(MT_SpellFailure, ABILITY_FAILED);
CastToClient()->ResetAlternateAdvancementTimer(casting_spell_aa_id);
}
ZeroCastingVars(); // resets all the state keeping stuff
@ -2078,7 +2073,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16
else if(!SpellOnTarget(spell_id, spell_target, false, true, resist_adjust, false, level_override)) {
if(IsBuffSpell(spell_id) && IsBeneficialSpell(spell_id)) {
// Prevent mana usage/timers being set for beneficial buffs
if(casting_spell_type == 1)
if(casting_spell_aa_id)
InterruptSpell();
return false;
}
@ -2157,11 +2152,11 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16
}
#endif //BOTS
// We hold off turning MBG off so we can still use it to calc the mana cost
if(spells[spell_id].can_mgb && HasMGB())
{
SpellOnTarget(spell_id, this);
entity_list.MassGroupBuff(this, this, spell_id, true);
SetMGB(false);
}
else
{
@ -2262,20 +2257,36 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16
}
// if this was a spell slot or an ability use up the mana for it
// CastSpell already reduced the cost for it if we're a client with focus
if(slot != USE_ITEM_SPELL_SLOT && slot != POTION_BELT_SPELL_SLOT && slot != TARGET_RING_SPELL_SLOT && mana_used > 0)
{
mana_used = GetActSpellCost(spell_id, mana_used);
if (HasMGB() && spells[spell_id].can_mgb) {
mana_used *= 2;
SetMGB(false);
}
// clamp if we some how got focused above our current mana
if (GetMana() < mana_used)
mana_used = GetMana();
Log.Out(Logs::Detail, Logs::Spells, "Spell %d: consuming %d mana", spell_id, mana_used);
if (!DoHPToManaCovert(mana_used))
if (!DoHPToManaCovert(mana_used)) {
SetMana(GetMana() - mana_used);
TryTriggerOnValueAmount(false, true);
}
}
//set our reuse timer on long ass reuse_time spells...
if(IsClient() && !isproc)
{
if(spell_id == casting_spell_id && casting_spell_timer != 0xFFFFFFFF)
if(casting_spell_aa_id) {
AA::Rank *rank = zone->GetAlternateAdvancementRank(casting_spell_aa_id);
if(rank && rank->base_ability) {
ExpendAlternateAdvancementCharge(rank->base_ability->id);
}
}
else if(spell_id == casting_spell_id && casting_spell_timer != 0xFFFFFFFF)
{
//aa new todo: aa expendable charges here
CastToClient()->GetPTimers().Start(casting_spell_timer, casting_spell_timer_duration);
Log.Out(Logs::Detail, Logs::Spells, "Spell %d: Setting custom reuse timer %d to %d", spell_id, casting_spell_timer, casting_spell_timer_duration);
}
@ -3753,7 +3764,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r
// if SpellEffect returned false there's a problem applying the
// spell. It's most likely a buff that can't stack.
Log.Out(Logs::Detail, Logs::Spells, "Spell %d could not apply its effects %s -> %s\n", spell_id, GetName(), spelltar->GetName());
if(casting_spell_type != 1) // AA is handled differently
if(casting_spell_aa_id)
Message_StringID(MT_SpellFailure, SPELL_NO_HOLD);
safe_delete(action_packet);
return false;
@ -4116,8 +4127,7 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster)
}
return true;
}
else if (IsClient() && CastToClient()->CheckAAEffect(aaEffectWarcry))
else if (CheckAATimer(aaTimerWarcry))
{
Message(13, "Your are immune to fear.");
Log.Out(Logs::Detail, Logs::Spells, "Clients has WarCry effect, immune to fear!");

View File

@ -179,10 +179,10 @@ bool TitleManager::IsClientEligibleForTitle(Client *c, std::vector<TitleEntry>::
if((Title->Class >= 0) && (c->GetBaseClass() != Title->Class))
return false;
if((Title->MinAAPoints >= 0) && (c->GetAAPointsSpent() < static_cast<uint32>(Title->MinAAPoints)))
if((Title->MinAAPoints >= 0) && (c->GetSpentAA() < static_cast<uint32>(Title->MinAAPoints)))
return false;
if((Title->MaxAAPoints >= 0) && (c->GetAAPointsSpent() > static_cast<uint32>(Title->MaxAAPoints)))
if((Title->MaxAAPoints >= 0) && (c->GetSpentAA() > static_cast<uint32>(Title->MaxAAPoints)))
return false;
if(Title->SkillID >= 0)

View File

@ -212,7 +212,7 @@ void NPC::UpdateWaypoint(int wp_index)
Log.Out(Logs::Detail, Logs::AI, "Next waypoint %d: (%.3f, %.3f, %.3f, %.3f)", wp_index, m_CurrentWayPoint.x, m_CurrentWayPoint.y, m_CurrentWayPoint.z, m_CurrentWayPoint.w);
//fix up pathing Z
if(zone->HasMap() && RuleB(Map, FixPathingZAtWaypoints))
if(zone->HasMap() && RuleB(Map, FixPathingZAtWaypoints) && !IsBoat())
{
if(!RuleB(Watermap, CheckForWaterAtWaypoints) || !zone->HasWaterMap() ||
@ -443,31 +443,10 @@ void NPC::NextGuardPosition() {
}
}
/*
// we need this for charmed NPCs
void Mob::SaveSpawnSpot() {
spawn_x = x_pos;
spawn_y = y_pos;
spawn_z = z_pos;
spawn_heading = heading;
}*/
/*float Mob::CalculateDistanceToNextWaypoint() {
return CalculateDistance(cur_wp_x, cur_wp_y, cur_wp_z);
}*/
float Mob::CalculateDistance(float x, float y, float z) {
return (float)sqrtf( ((m_Position.x-x)*(m_Position.x-x)) + ((m_Position.y-y)*(m_Position.y-y)) + ((m_Position.z-z)*(m_Position.z-z)) );
}
/*
uint8 NPC::CalculateHeadingToNextWaypoint() {
return CalculateHeadingToTarget(cur_wp_x, cur_wp_y);
}
*/
float Mob::CalculateHeadingToTarget(float in_x, float in_y) {
float angle;
@ -521,7 +500,7 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, int speed, boo
}
bool send_update = false;
int compare_steps = IsBoat() ? 1 : 20;
int compare_steps = 20;
if(tar_ndx < compare_steps && m_TargetLocation.x==x && m_TargetLocation.y==y) {
float new_x = m_Position.x + m_TargetV.x*tar_vector;
@ -597,7 +576,7 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, int speed, boo
pRunAnimSpeed = speed;
if(IsClient())
{
animation = speed;
animation = speed / 2;
}
//pRunAnimSpeed = (int8)(speed*NPC_RUNANIM_RATIO);
//speed *= NPC_SPEED_MULTIPLIER;
@ -611,7 +590,7 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, int speed, boo
tar_vector = (float)speed / mag;
// mob move fix
int numsteps = (int) ( mag * 16.0f / (float)speed);
int numsteps = (int) ( mag * 16.0f / (float)speed + 0.5f);
// mob move fix
@ -621,9 +600,9 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, int speed, boo
if (numsteps>1)
{
tar_vector=1.0f ;
m_TargetV.x = 1.25f * m_TargetV.x/(float)numsteps;
m_TargetV.y = 1.25f * m_TargetV.y/(float)numsteps;
m_TargetV.z = 1.25f *m_TargetV.z/(float)numsteps;
m_TargetV.x = m_TargetV.x/(float)numsteps;
m_TargetV.y = m_TargetV.y/(float)numsteps;
m_TargetV.z = m_TargetV.z/(float)numsteps;
float new_x = m_Position.x + m_TargetV.x;
float new_y = m_Position.y + m_TargetV.y;
@ -636,7 +615,7 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, int speed, boo
m_Position.y = new_y;
m_Position.z = new_z;
m_Position.w = CalculateHeadingToTarget(x, y);
tar_ndx = 22 - numsteps;
tar_ndx = 20 - numsteps;
Log.Out(Logs::Detail, Logs::AI, "Next position2 (%.3f, %.3f, %.3f) (%d steps)", m_Position.x, m_Position.y, m_Position.z, numsteps);
}
else
@ -656,6 +635,11 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, int speed, boo
else {
tar_vector/=16.0f;
float dur = Timer::GetCurrentTime() - pLastChange;
if(dur < 1.0f) {
dur = 1.0f;
}
tar_vector = (tar_vector * AImovement_duration) / 100.0f;
float new_x = m_Position.x + m_TargetV.x*tar_vector;
float new_y = m_Position.y + m_TargetV.y*tar_vector;
@ -717,8 +701,6 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, int speed, boo
}
else
{
// force an update now
move_tic_count = RuleI(Zone, NPCPositonUpdateTicCount);
SendPosUpdate();
SetAppearance(eaStanding, false);
}

View File

@ -1931,10 +1931,10 @@ bool WorldServer::SendChannelMessage(Client* from, const char* to, uint8 chan_nu
bool WorldServer::SendEmoteMessage(const char* to, uint32 to_guilddbid, uint32 type, const char* message, ...) {
va_list argptr;
char buffer[256];
char buffer[4096] = { 0 };
va_start(argptr, message);
vsnprintf(buffer, 256, message, argptr);
vsnprintf(buffer, sizeof(buffer) - 1, message, argptr);
va_end(argptr);
return SendEmoteMessage(to, to_guilddbid, 0, type, buffer);
@ -1942,10 +1942,10 @@ bool WorldServer::SendEmoteMessage(const char* to, uint32 to_guilddbid, uint32 t
bool WorldServer::SendEmoteMessage(const char* to, uint32 to_guilddbid, int16 to_minstatus, uint32 type, const char* message, ...) {
va_list argptr;
char buffer[256];
char buffer[4096] = { 0 };
va_start(argptr, message);
vsnprintf(buffer, 256, message, argptr);
vsnprintf(buffer, sizeof(buffer) - 1, message, argptr);
va_end(argptr);
if (!Connected() && to == 0) {

View File

@ -68,7 +68,6 @@ extern bool staticzone;
extern NetConnection net;
extern PetitionList petition_list;
extern QuestParserCollection* parse;
extern uint16 adverrornum;
extern uint32 numclients;
extern WorldServer worldserver;
extern Zone* zone;
@ -805,8 +804,6 @@ Zone::Zone(uint32 in_zoneid, uint32 in_instanceid, const char* in_short_name)
weather_intensity = 0;
blocked_spells = nullptr;
totalBS = 0;
aas = nullptr;
totalAAs = 0;
zone_has_current_time = false;
Instance_Shutdown_Timer = nullptr;
@ -865,16 +862,6 @@ Zone::~Zone() {
safe_delete(qGlobals);
safe_delete_array(adv_data);
safe_delete_array(map_name);
if(aas != nullptr) {
int r;
for(r = 0; r < totalAAs; r++) {
uchar *data = (uchar *) aas[r];
safe_delete_array(data);
}
safe_delete_array(aas);
}
safe_delete(GuildBanks);
}
@ -953,16 +940,12 @@ bool Zone::Init(bool iStaticZone) {
zone->LoadAlternateCurrencies();
zone->LoadNPCEmotes(&NPCEmoteList);
//Load AA information
adverrornum = 500;
LoadAAs();
LoadAlternateAdvancement();
//Load merchant data
adverrornum = 501;
zone->GetMerchantDataForZoneLoad();
//Load temporary merchant data
adverrornum = 502;
zone->LoadTempMerchantData();
// Merc data
@ -974,7 +957,6 @@ bool Zone::Init(bool iStaticZone) {
if (RuleB(Zone, LevelBasedEXPMods))
zone->LoadLevelEXPMods();
adverrornum = 503;
petition_list.ClearPetitions();
petition_list.ReadDatabase();

View File

@ -27,6 +27,7 @@
#include "qglobals.h"
#include "spawn2.h"
#include "spawngroup.h"
#include "aa_ability.h"
struct ZonePoint
{
@ -113,11 +114,13 @@ public:
inline const uint32& GetMaxClients() { return pMaxClients; }
void LoadAAs();
int GetTotalAAs() { return totalAAs; }
SendAA_Struct* GetAABySequence(uint32 seq) { return aas[seq]; }
SendAA_Struct* FindAA(uint32 id);
uint8 GetTotalAALevels(uint32 skill_id);
//new AA
void LoadAlternateAdvancement();
AA::Ability *GetAlternateAdvancementAbility(int id);
AA::Ability *GetAlternateAdvancementAbilityByRank(int rank_id);
AA::Rank *GetAlternateAdvancementRank(int rank_id);
std::pair<AA::Ability*, AA::Rank*> GetAlternateAdvancementAbilityAndRank(int id, int points_spent);
void LoadZoneDoors(const char* zone, int16 version);
bool LoadZoneObjects();
bool LoadGroundSpawns();
@ -193,6 +196,10 @@ public:
char *adv_data;
bool did_adventure_actions;
//new AA
std::unordered_map<int, std::unique_ptr<AA::Ability>> aa_abilities;
std::unordered_map<int, std::unique_ptr<AA::Rank>> aa_ranks;
void DoAdventureCountIncrease();
void DoAdventureAssassinationCountIncrease();
void DoAdventureActions();
@ -313,9 +320,6 @@ private:
int totalBS;
ZoneSpellsBlocked *blocked_spells;
int totalAAs;
SendAA_Struct **aas; //array of AA structs
/*
Spawn related things
*/

View File

@ -6,6 +6,7 @@
#include "position.h"
#include "../common/faction.h"
#include "../common/eqemu_logsys.h"
#include "aa_ability.h"
class Client;
class Corpse;
@ -339,17 +340,10 @@ public:
bool SetCharacterFactionLevel(uint32 char_id, int32 faction_id, int32 value, uint8 temp, faction_map &val_list); // needed for factions Dec, 16 2001
bool LoadFactionData();
/* AAs */
bool LoadAAEffects();
bool LoadAAEffects2();
bool LoadSwarmSpells();
SendAA_Struct*GetAASkillVars(uint32 skill_id);
uint8 GetTotalAALevels(uint32 skill_id);
uint32 GetSizeAA();
uint32 CountAAs();
void LoadAAs(SendAA_Struct **load);
uint32 CountAAEffects();
void FillAAEffects(SendAA_Struct* aa_struct);
/* AAs New */
bool LoadAlternateAdvancementAbilities(std::unordered_map<int, std::unique_ptr<AA::Ability>> &abilities,
std::unordered_map<int, std::unique_ptr<AA::Rank>> &ranks);
bool LoadAlternateAdvancement(Client *c);
/* Zone related */
bool GetZoneCFG(uint32 zoneid, uint16 instance_id, NewZone_Struct *data, bool &can_bind, bool &can_combat, bool &can_levitate, bool &can_castoutdoor, bool &is_city, bool &is_hotzone, bool &allow_mercs, uint8 &zone_type, int &ruleset, char **map_filename);