mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-11 21:01:29 +00:00
Merge from master
This commit is contained in:
commit
feae79b417
@ -1,5 +1,15 @@
|
||||
EQEMu Changelog (Started on Sept 24, 2003 15:50)
|
||||
-------------------------------------------------------
|
||||
== 05/05/2013 ==
|
||||
Tabasco: Added EVENT_ITEM_TICK for interactive item quest scripts. (See https://github.com/josheb/quests_dc/blob/master/items/12204.pl)
|
||||
Added simple account flags. Would this work better as an additional qglobal category?
|
||||
Added SendMessage to EQW, useful for using CURL to broadcast to the server.
|
||||
Added WorldShutDown to EQW for timed world shutdowns, also updated console worldshutdown command to optionally use timed behavior.
|
||||
Added numerous modding hooks and mod_functions.cpp for easy custom formulas or future lua integration.
|
||||
Added various rules for monk AC bonus weight, archery bonuses, kick/bash stuns and mob stun levels.
|
||||
required SQL: 2013_05_05_Account_Flags.sql
|
||||
required SQL: 2013_05_05_Item_Tick.sql
|
||||
|
||||
== 04/28/2013 ==
|
||||
PiB: Implement CRC16 using CRC32.
|
||||
|
||||
|
||||
@ -428,7 +428,7 @@ bool Database::ReserveName(uint32 account_id, char* name)
|
||||
char errbuf[MYSQL_ERRMSG_SIZE];
|
||||
char *query = 0;
|
||||
|
||||
if (!RunQuery(query, MakeAnyLenString(&query, "INSERT into character_ SET account_id=%i, name='%s', profile=nullptr", account_id, name), errbuf)) {
|
||||
if (!RunQuery(query, MakeAnyLenString(&query, "INSERT into character_ SET account_id=%i, name='%s', profile=NULL", account_id, name), errbuf)) {
|
||||
cerr << "Error in ReserveName query '" << query << "' " << errbuf << endl;
|
||||
safe_delete_array(query);
|
||||
return false;
|
||||
@ -1196,9 +1196,9 @@ bool Database::GetSafePoints(const char* short_name, uint32 version, float* safe
|
||||
{
|
||||
cerr << "Error in GetSafePoint query '" << query << "' " << errbuf << endl;
|
||||
cerr << "If it errors, run the following querys:\n";
|
||||
cerr << "ALTER TABLE `zone` CHANGE `minium_level` `min_level` TINYINT(3) UNSIGNED DEFAULT \"0\" NOT nullptr;\n";
|
||||
cerr << "ALTER TABLE `zone` CHANGE `minium_status` `min_status` TINYINT(3) UNSIGNED DEFAULT \"0\" NOT nullptr;\n";
|
||||
cerr << "ALTER TABLE `zone` ADD flag_needed VARCHAR(128) NOT nullptr DEFAULT '';\n";
|
||||
cerr << "ALTER TABLE `zone` CHANGE `minium_level` `min_level` TINYINT(3) UNSIGNED DEFAULT \"0\" NOT NULL;\n";
|
||||
cerr << "ALTER TABLE `zone` CHANGE `minium_status` `min_status` TINYINT(3) UNSIGNED DEFAULT \"0\" NOT NULL;\n";
|
||||
cerr << "ALTER TABLE `zone` ADD flag_needed VARCHAR(128) NOT NULL DEFAULT '';\n";
|
||||
|
||||
safe_delete_array(query);
|
||||
}
|
||||
|
||||
@ -95,7 +95,7 @@ RULE_BOOL ( Character, CheckCursorEmptyWhenLooting, true ) // If true, a player
|
||||
RULE_BOOL ( Character, MaintainIntoxicationAcrossZones, true ) // If true, alcohol effects are maintained across zoning and logging out/in.
|
||||
RULE_BOOL ( Character, EnableDiscoveredItems, true ) // If enabled, it enables EVENT_DISCOVER_ITEM and also saves character names and timestamps for the first time an item is discovered.
|
||||
RULE_BOOL ( Character, EnableXTargetting, true) // Enable Extended Targetting Window, for users with UF and later clients.
|
||||
|
||||
RULE_BOOL ( Character, KeepLevelOverMax, false) // Don't delevel a character that has somehow gone over the level cap
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY( Mercs )
|
||||
@ -292,6 +292,7 @@ RULE_INT ( Spells, RootBreakFromSpells, 20) //Chance for root to break when cast
|
||||
RULE_INT ( Spells, DeathSaveCharismaMod, 3) //Determines how much charisma effects chance of death save firing.
|
||||
RULE_INT ( Spells, DivineInterventionHeal, 8000) //Divine intervention heal amount.
|
||||
RULE_BOOL ( Spells, AdditiveBonusValues, false) //Allow certain bonuses to be calculated by adding together the value from each item, instead of taking the highest value. (ie Add together all Cleave Effects)
|
||||
RULE_BOOL ( Spells, UseCHAScribeHack, false) //ScribeSpells and TrainDiscs quest functions will ignore entries where field 12 is CHA. What's the best way to do this?
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY( Combat )
|
||||
@ -361,6 +362,11 @@ RULE_INT ( Combat, NPCFlurryChance, 20) // Chance for NPC to flurry.
|
||||
RULE_BOOL (Combat,TauntOverLevel, 1) //Allows you to taunt NPC's over warriors level.
|
||||
RULE_REAL (Combat,TauntSkillFalloff, 0.33)//For every taunt skill point that's not maxed you lose this % chance to taunt.
|
||||
RULE_BOOL (Combat,EXPFromDmgShield, false) //Determine if damage from a damage shield counts for EXP gain.
|
||||
RULE_INT ( Combat, MonkACBonusWeight, 15)
|
||||
RULE_INT ( Combat, ClientStunLevel, 55) //This is the level where client kicks and bashes can stun the target
|
||||
RULE_INT ( Combat, QuiverWRHasteDiv, 3) //Weight Reduction is divided by this to get haste contribution for quivers
|
||||
RULE_BOOL ( Combat, UseArcheryBonusRoll, false) //Make the 51+ archery bonus require an actual roll
|
||||
RULE_INT ( Combat, ArcheryBonusChance, 50)
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY( NPC )
|
||||
|
||||
@ -1102,3 +1102,7 @@ bool IsShortDurationBuff(uint16 spell_id)
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* GetSpellName(int16 spell_id)
|
||||
{
|
||||
return( spells[spell_id].name );
|
||||
}
|
||||
|
||||
@ -806,5 +806,6 @@ bool DetrimentalSpellAllowsRest(uint16 spell_id);
|
||||
uint32 GetNimbusEffect(uint16 spell_id);
|
||||
int32 GetFuriousBash(uint16 spell_id);
|
||||
bool IsShortDurationBuff(uint16 spell_id);
|
||||
const char *GetSpellName(int16 spell_id);
|
||||
|
||||
#endif
|
||||
|
||||
11
utils/sql/git/required/2013_05_05_Account_Flags.sql
Normal file
11
utils/sql/git/required/2013_05_05_Account_Flags.sql
Normal file
@ -0,0 +1,11 @@
|
||||
--
|
||||
-- Table structure for table `account_flags`
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `account_flags` (
|
||||
`p_accid` int(10) unsigned NOT NULL,
|
||||
`p_flag` varchar(50) NOT NULL,
|
||||
`p_value` varchar(80) NOT NULL,
|
||||
PRIMARY KEY (`p_accid`,`p_flag`),
|
||||
KEY `p_accid` (`p_accid`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
|
||||
13
utils/sql/git/required/2013_05_05_Item_Tick.sql
Normal file
13
utils/sql/git/required/2013_05_05_Item_Tick.sql
Normal file
@ -0,0 +1,13 @@
|
||||
--
|
||||
-- Table structure for table `item_tick`
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `item_tick` (
|
||||
`it_itemid` int(11) NOT NULL,
|
||||
`it_chance` int(11) NOT NULL,
|
||||
`it_level` int(11) NOT NULL,
|
||||
`it_id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`it_qglobal` varchar(50) NOT NULL,
|
||||
`it_bagslot` tinyint(4) NOT NULL,
|
||||
PRIMARY KEY (`it_id`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
|
||||
@ -459,6 +459,13 @@ void EQW::ResolveBug(const char *id) {
|
||||
safe_delete_array(query);
|
||||
}
|
||||
|
||||
void EQW::SendMessage(uint32 type, const char *msg) {
|
||||
zoneserver_list.SendEmoteMessage(0, 0, 0, type, msg);
|
||||
}
|
||||
|
||||
void EQW::WorldShutDown(uint32 time, uint32 interval) {
|
||||
zoneserver_list.WorldShutDown(time, interval);
|
||||
}
|
||||
|
||||
#endif //EMBPERL
|
||||
|
||||
|
||||
@ -78,6 +78,8 @@ public:
|
||||
map<string,string> GetBugDetails(const char *id);
|
||||
void ResolveBug(const char *id);
|
||||
|
||||
void SendMessage(uint32 type, const char *msg);
|
||||
void WorldShutDown(uint32 time, uint32 interval);
|
||||
//END PERL EXPORT
|
||||
|
||||
protected:
|
||||
|
||||
@ -722,11 +722,24 @@ void Console::ProcessCommand(const char* command) {
|
||||
}
|
||||
}
|
||||
else if (strcasecmp(sep.arg[0], "worldshutdown") == 0 && admin >= consoleWorldStatus) {
|
||||
ServerPacket* pack = new ServerPacket(ServerOP_ShutdownAll);
|
||||
zoneserver_list.SendPacket(pack);
|
||||
delete pack;
|
||||
SendMessage(1, "Sending shutdown packet... goodbye.");
|
||||
CatchSignal(0);
|
||||
int32 time, interval;
|
||||
if(sep.IsNumber(1) && sep.IsNumber(2) && ((time=atoi(sep.arg[1]))>0) && ((interval=atoi(sep.arg[2]))>0)) {
|
||||
zoneserver_list.WorldShutDown(time, interval);
|
||||
}
|
||||
else if(strcasecmp(sep.arg[1], "now") == 0) {
|
||||
zoneserver_list.WorldShutDown(0, 0);
|
||||
}
|
||||
else if(strcasecmp(sep.arg[1], "disable") == 0) {
|
||||
SendEmoteMessage(0,0,0,15,"<SYSTEMWIDE MESSAGE>:SYSTEM MSG:World shutdown aborted.");
|
||||
zoneserver_list.SendEmoteMessage(0,0,0,15,"<SYSTEMWIDE MESSAGE>:SYSTEM MSG:World shutdown aborted.");
|
||||
zoneserver_list.shutdowntimer->Disable();
|
||||
zoneserver_list.reminder->Disable();
|
||||
}
|
||||
else {
|
||||
SendMessage(1, "Usage: worldshutdown [now] [disable] ([time] [interval])");
|
||||
//Go ahead and shut down since that's what this used to do when invoked this way.
|
||||
zoneserver_list.WorldShutDown(0, 0);
|
||||
}
|
||||
}
|
||||
else if (strcasecmp(sep.arg[0], "lock") == 0 && admin >= consoleLockStatus) {
|
||||
WorldConfig::LockWorld();
|
||||
|
||||
@ -969,6 +969,57 @@ XS(XS_EQW_ResolveBug)
|
||||
XSRETURN_EMPTY;
|
||||
}
|
||||
|
||||
XS(XS_EQW_SendMessage); /* prototype to pass -Wmissing-prototypes */
|
||||
XS(XS_EQW_SendMessage)
|
||||
{
|
||||
dXSARGS;
|
||||
if (items != 3)
|
||||
Perl_croak(aTHX_ "Usage: EQW::SendMessage(THIS, type, message)");
|
||||
{
|
||||
EQW * THIS;
|
||||
dXSTARG;
|
||||
uint32 msgtype = (uint32)SvUV(ST(1));
|
||||
char* msg = (char *)SvPV_nolen(ST(2));
|
||||
|
||||
if (sv_derived_from(ST(0), "EQW")) {
|
||||
IV tmp = SvIV((SV*)SvRV(ST(0)));
|
||||
THIS = INT2PTR(EQW *,tmp);
|
||||
}
|
||||
else
|
||||
Perl_croak(aTHX_ "THIS is not of type EQW");
|
||||
if(THIS == NULL)
|
||||
Perl_croak(aTHX_ "THIS is NULL, avoiding crash.");
|
||||
|
||||
THIS->SendMessage(msgtype, msg);
|
||||
}
|
||||
XSRETURN_EMPTY;
|
||||
}
|
||||
|
||||
XS(XS_EQW_WorldShutDown); /* prototype to pass -Wmissing-prototypes */
|
||||
XS(XS_EQW_WorldShutDown)
|
||||
{
|
||||
dXSARGS;
|
||||
if (items != 3)
|
||||
Perl_croak(aTHX_ "Usage: EQW::WorldShutDown(THIS, time, interval)");
|
||||
{
|
||||
EQW * THIS;
|
||||
dXSTARG;
|
||||
uint32 time = (uint32)SvUV(ST(1));
|
||||
uint32 interval = (uint32)SvUV(ST(2));
|
||||
|
||||
if (sv_derived_from(ST(0), "EQW")) {
|
||||
IV tmp = SvIV((SV*)SvRV(ST(0)));
|
||||
THIS = INT2PTR(EQW *,tmp);
|
||||
}
|
||||
else
|
||||
Perl_croak(aTHX_ "THIS is not of type EQW");
|
||||
if(THIS == NULL)
|
||||
Perl_croak(aTHX_ "THIS is NULL, avoiding crash.");
|
||||
|
||||
THIS->WorldShutDown(time, interval);
|
||||
}
|
||||
XSRETURN_EMPTY;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
@ -1020,6 +1071,8 @@ XS(boot_EQW)
|
||||
newXSproto(strcpy(buf, "ListBugs"), XS_EQW_ListBugs, file, "$$");
|
||||
newXSproto(strcpy(buf, "GetBugDetails"), XS_EQW_GetBugDetails, file, "$$");
|
||||
newXSproto(strcpy(buf, "ResolveBug"), XS_EQW_ResolveBug, file, "$$");
|
||||
newXSproto(strcpy(buf, "SendMessage"), XS_EQW_SendMessage, file, "$$$");
|
||||
newXSproto(strcpy(buf, "WorldShutDown"), XS_EQW_WorldShutDown, file, "$$$");
|
||||
XSRETURN_YES;
|
||||
}
|
||||
|
||||
|
||||
@ -716,14 +716,29 @@ void ZSList::GetZoneIDList(vector<uint32> &zones) {
|
||||
|
||||
}
|
||||
|
||||
void ZSList::WorldShutDown(uint32 time, uint32 interval)
|
||||
{
|
||||
if( time > 0 ) {
|
||||
SendEmoteMessage(0,0,0,15,"<SYSTEMWIDE MESSAGE>:SYSTEM MSG:World coming down in %i seconds, everyone log out before this time.",time);
|
||||
|
||||
time *= 1000;
|
||||
interval *= 1000;
|
||||
if(interval < 5000) { interval = 5000; }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
shutdowntimer->SetTimer(time);
|
||||
reminder->SetTimer(interval-1000);
|
||||
reminder->SetAtTrigger(interval);
|
||||
shutdowntimer->Start();
|
||||
reminder->Start();
|
||||
}
|
||||
else {
|
||||
SendEmoteMessage(0,0,0,15,"<SYSTEMWIDE MESSAGE>:SYSTEM MSG:World coming down, everyone log out now.");
|
||||
ServerPacket* pack = new ServerPacket;
|
||||
pack->opcode = ServerOP_ShutdownAll;
|
||||
pack->size=0;
|
||||
SendPacket(pack);
|
||||
safe_delete(pack);
|
||||
Process();
|
||||
CatchSignal(2);
|
||||
}
|
||||
}
|
||||
|
||||
@ -56,6 +56,7 @@ public:
|
||||
|
||||
int GetZoneCount();
|
||||
void GetZoneIDList(std::vector<uint32> &zones);
|
||||
void WorldShutDown(uint32 time, uint32 interval);
|
||||
|
||||
protected:
|
||||
uint32 NextID;
|
||||
|
||||
@ -85,6 +85,7 @@ SET(zone_sources
|
||||
zonedb.cpp
|
||||
zonedbasync.cpp
|
||||
zoning.cpp
|
||||
mod_functions.cpp
|
||||
)
|
||||
|
||||
SET(zone_headers
|
||||
|
||||
@ -318,6 +318,8 @@ bool Mob::CheckHitChance(Mob* other, SkillType skillinuse, int Hand, int16 chanc
|
||||
if(skillinuse == ARCHERY)
|
||||
chancetohit -= (chancetohit * RuleR(Combat, ArcheryHitPenalty)) / 100.0f;
|
||||
|
||||
chancetohit = mod_hit_chance(chancetohit, skillinuse, attacker);
|
||||
|
||||
// Chance to hit; Max 95%, Min 30%
|
||||
if(chancetohit > 1000) {
|
||||
//if chance to hit is crazy high, that means a discipline is in use, and let it stay there
|
||||
@ -396,6 +398,7 @@ bool Mob::AvoidDamage(Mob* other, int32 &damage, bool CanRiposte)
|
||||
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
|
||||
}
|
||||
}
|
||||
@ -434,6 +437,7 @@ bool Mob::AvoidDamage(Mob* other, int32 &damage, bool CanRiposte)
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -485,6 +489,7 @@ bool Mob::AvoidDamage(Mob* other, int32 &damage, bool CanRiposte)
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -507,6 +512,8 @@ bool Mob::AvoidDamage(Mob* other, int32 &damage, bool CanRiposte)
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -555,6 +562,10 @@ void Mob::MeleeMitigation(Mob *attacker, int32 &damage, int32 minhit)
|
||||
int shield_ac = 0;
|
||||
int armor = 0;
|
||||
float weight = 0.0;
|
||||
|
||||
float monkweight = RuleI(Combat, MonkACBonusWeight);
|
||||
monkweight = mod_monk_weight(monkweight, attacker);
|
||||
|
||||
if(IsClient())
|
||||
{
|
||||
armor = CastToClient()->GetRawACNoShield(shield_ac);
|
||||
@ -576,7 +587,7 @@ void Mob::MeleeMitigation(Mob *attacker, int32 &damage, int32 minhit)
|
||||
{
|
||||
softcap = RuleI(Combat, ClothACSoftcap);
|
||||
}
|
||||
else if(GetClass() == MONK && weight <= 15.0)
|
||||
else if(GetClass() == MONK && weight <= monkweight)
|
||||
{
|
||||
softcap = RuleI(Combat, MonkACSoftcap);
|
||||
}
|
||||
@ -603,7 +614,7 @@ void Mob::MeleeMitigation(Mob *attacker, int32 &damage, int32 minhit)
|
||||
{
|
||||
softcap_armor = softcap_armor * RuleR(Combat, WarriorACSoftcapReturn);
|
||||
}
|
||||
else if(GetClass() == SHADOWKNIGHT || GetClass() == PALADIN || (GetClass() == MONK && weight <= 15.0))
|
||||
else if(GetClass() == SHADOWKNIGHT || GetClass() == PALADIN || (GetClass() == MONK && weight <= monkweight))
|
||||
{
|
||||
softcap_armor = softcap_armor * RuleR(Combat, KnightACSoftcapReturn);
|
||||
}
|
||||
@ -637,6 +648,8 @@ void Mob::MeleeMitigation(Mob *attacker, int32 &damage, int32 minhit)
|
||||
}
|
||||
mitigation_rating *= 0.847;
|
||||
|
||||
mitigation_rating = mod_mitigation_rating(mitigation_rating, attacker);
|
||||
|
||||
if(attacker->IsClient())
|
||||
{
|
||||
attack_rating = (attacker->CastToClient()->CalcATK() + ((attacker->GetSTR()-66) * 0.9) + (attacker->GetSkill(OFFENSE)*1.345));
|
||||
@ -646,6 +659,8 @@ void Mob::MeleeMitigation(Mob *attacker, int32 &damage, int32 minhit)
|
||||
attack_rating = (attacker->GetATK() + (attacker->GetSkill(OFFENSE)*1.345) + ((attacker->GetSTR()-66) * 0.9));
|
||||
}
|
||||
|
||||
attack_rating = attacker->mod_attack_rating(attack_rating, this);
|
||||
|
||||
float d = 10.0;
|
||||
float mit_roll = MakeRandomFloat(0, mitigation_rating);
|
||||
float atk_roll = MakeRandomFloat(0, attack_rating);
|
||||
@ -669,6 +684,9 @@ void Mob::MeleeMitigation(Mob *attacker, int32 &damage, int32 minhit)
|
||||
if(thac20 > thac20cap)
|
||||
{
|
||||
thac20 = thac20cap;
|
||||
|
||||
|
||||
|
||||
}
|
||||
d += 10 * (m_diff / thac20);
|
||||
}
|
||||
@ -1221,6 +1239,8 @@ bool Client::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, b
|
||||
else
|
||||
damage = MakeRandomInt(min_hit, max_hit);
|
||||
|
||||
damage = mod_client_damage(damage, skillinuse, Hand, weapon, other);
|
||||
|
||||
mlog(COMBAT__DAMAGE, "Damage calculated to %d (min %d, max %d, str %d, skill %d, DMG %d, lv %d)",
|
||||
damage, min_hit, max_hit, GetSTR(), GetSkill(skillinuse), weapon_damage, mylevel);
|
||||
|
||||
@ -1458,6 +1478,9 @@ void Client::Death(Mob* killerMob, int32 damage, uint16 spell, SkillType attack_
|
||||
{
|
||||
if (killerMob->IsNPC()) {
|
||||
parse->EventNPC(EVENT_SLAY, killerMob->CastToNPC(), this, "", 0);
|
||||
|
||||
mod_client_death_npc(killerMob);
|
||||
|
||||
uint16 emoteid = killerMob->GetEmoteID();
|
||||
if(emoteid != 0)
|
||||
killerMob->CastToNPC()->DoNPCEmote(KILLEDPC,emoteid);
|
||||
@ -1473,6 +1496,9 @@ void Client::Death(Mob* killerMob, int32 damage, uint16 spell, SkillType attack_
|
||||
killerMob->CastToClient()->SetDueling(false);
|
||||
killerMob->CastToClient()->SetDuelTarget(0);
|
||||
entity_list.DuelMessage(killerMob,this,false);
|
||||
|
||||
mod_client_death_duel(killerMob);
|
||||
|
||||
} else {
|
||||
//otherwise, we just died, end the duel.
|
||||
Mob* who = entity_list.GetMob(GetDuelTarget());
|
||||
@ -1845,6 +1871,8 @@ bool NPC::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool
|
||||
damage = (max_dmg+eleBane);
|
||||
}
|
||||
|
||||
damage = mod_npc_damage(damage, skillinuse, Hand, &weapon_inst, other);
|
||||
|
||||
int32 hate = damage;
|
||||
if(IsPet())
|
||||
{
|
||||
@ -2043,7 +2071,7 @@ void NPC::Death(Mob* killerMob, int32 damage, uint16 spell, SkillType attack_ski
|
||||
entity_list.QueueClients(killerMob, app, false);
|
||||
|
||||
if(respawn2) {
|
||||
respawn2->DeathReset();
|
||||
respawn2->DeathReset(1);
|
||||
}
|
||||
|
||||
if (killerMob) {
|
||||
@ -2098,6 +2126,9 @@ void NPC::Death(Mob* killerMob, int32 damage, uint16 spell, SkillType attack_ski
|
||||
for (int i = 0; i < MAX_RAID_MEMBERS; i++) {
|
||||
if (kr->members[i].member != nullptr) { // If Group Member is Client
|
||||
parse->EventNPC(EVENT_KILLED_MERIT, this, kr->members[i].member, "killed", 0);
|
||||
|
||||
mod_npc_killed_merit(kr->members[i].member);
|
||||
|
||||
if(RuleB(TaskSystem, EnableTaskSystem))
|
||||
kr->members[i].member->UpdateTasksOnKill(GetNPCTypeID());
|
||||
PlayerCount++;
|
||||
@ -2138,6 +2169,9 @@ void NPC::Death(Mob* killerMob, int32 damage, uint16 spell, SkillType attack_ski
|
||||
if (kg->members[i] != nullptr && kg->members[i]->IsClient()) { // If Group Member is Client
|
||||
Client *c = kg->members[i]->CastToClient();
|
||||
parse->EventNPC(EVENT_KILLED_MERIT, this, c, "killed", 0);
|
||||
|
||||
mod_npc_killed_merit(c);
|
||||
|
||||
if(RuleB(TaskSystem, EnableTaskSystem))
|
||||
c->UpdateTasksOnKill(GetNPCTypeID());
|
||||
|
||||
@ -2182,6 +2216,9 @@ void NPC::Death(Mob* killerMob, int32 damage, uint16 spell, SkillType attack_ski
|
||||
}
|
||||
/* Send the EVENT_KILLED_MERIT event */
|
||||
parse->EventNPC(EVENT_KILLED_MERIT, this, give_exp_client, "killed", 0);
|
||||
|
||||
mod_npc_killed_merit(give_exp_client);
|
||||
|
||||
if(RuleB(TaskSystem, EnableTaskSystem))
|
||||
give_exp_client->UpdateTasksOnKill(GetNPCTypeID());
|
||||
|
||||
@ -2312,6 +2349,9 @@ void NPC::Death(Mob* killerMob, int32 damage, uint16 spell, SkillType attack_ski
|
||||
if(killerMob) {
|
||||
Mob *oos = killerMob->GetOwnerOrSelf();
|
||||
parse->EventNPC(EVENT_DEATH, this, oos, "", 0);
|
||||
|
||||
mod_npc_killed(oos);
|
||||
|
||||
uint16 emoteid = this->GetEmoteID();
|
||||
if(emoteid != 0)
|
||||
this->DoNPCEmote(ONDEATH,emoteid);
|
||||
@ -3404,7 +3444,7 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons
|
||||
{
|
||||
// NPCs can stun with their bash/kick as soon as they recieve it.
|
||||
// Clients can stun mobs under level 56 with their bash/kick when they get level 55 or greater.
|
||||
if((attacker->IsNPC()) || (attacker->IsClient() && attacker->GetLevel() >= 55 && GetLevel() < 56))
|
||||
if( attacker->IsNPC() || (attacker->IsClient() && attacker->GetLevel() >= RuleI(Combat, ClientStunLevel) && GetLevel() < RuleI(Spells, BaseImmunityLevel)) )
|
||||
{
|
||||
if (MakeRandomInt(0,99) < (RuleI(Character, NPCBashKickStunChance)) || attacker->IsClient())
|
||||
{
|
||||
|
||||
124
zone/client.cpp
124
zone/client.cpp
@ -166,7 +166,8 @@ Client::Client(EQStreamInterface* ieqs)
|
||||
qglobal_purge_timer(30000),
|
||||
TrackingTimer(2000),
|
||||
RespawnFromHoverTimer(0),
|
||||
merc_timer(RuleI(Mercs, UpkeepIntervalMS))
|
||||
merc_timer(RuleI(Mercs, UpkeepIntervalMS)),
|
||||
ItemTickTimer(10000)
|
||||
{
|
||||
for(int cf=0; cf < _FilterCount; cf++)
|
||||
ClientFilters[cf] = FilterShow;
|
||||
@ -325,6 +326,7 @@ Client::Client(EQStreamInterface* ieqs)
|
||||
}
|
||||
MaxXTargets = 5;
|
||||
XTargetAutoAddHaters = true;
|
||||
LoadAccountFlags();
|
||||
}
|
||||
|
||||
Client::~Client() {
|
||||
@ -842,6 +844,9 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s
|
||||
safe_delete(pack);
|
||||
}
|
||||
|
||||
//Return true to proceed, false to return
|
||||
if(!mod_client_message(message, chan_num)) { return; }
|
||||
|
||||
// Garble the message based on drunkness
|
||||
if (m_pp.intoxication > 0) {
|
||||
GarbleMessage(message, (int)(m_pp.intoxication / 3));
|
||||
@ -2337,7 +2342,8 @@ bool Client::CheckIncreaseSkill(SkillType skillid, Mob *against_who, int chancem
|
||||
if( against_who->SpecAttacks[IMMUNE_AGGRO] || against_who->IsClient() ||
|
||||
GetLevelCon(against_who->GetLevel()) == CON_GREEN )
|
||||
{
|
||||
return false;
|
||||
//false by default
|
||||
return mod_can_increase_skill(skillid, against_who);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2349,6 +2355,9 @@ bool Client::CheckIncreaseSkill(SkillType skillid, Mob *against_who, int chancem
|
||||
if (Chance < 1)
|
||||
Chance = 1; // Make it always possible
|
||||
Chance = (Chance * RuleI(Character, SkillUpModifier) / 100);
|
||||
|
||||
Chance = mod_increase_skill_chance(Chance, against_who);
|
||||
|
||||
if(MakeRandomFloat(0, 99) < Chance)
|
||||
{
|
||||
SetSkill(skillid, GetRawSkill(skillid) + 1);
|
||||
@ -2717,6 +2726,8 @@ bool Client::BindWound(Mob* bindmob, bool start, bool fail){
|
||||
max_percent = 70 + 10 * maxHPBonus;
|
||||
}
|
||||
|
||||
max_percent = mod_bindwound_percent(max_percent, bindmob);
|
||||
|
||||
int max_hp = bindmob->GetMaxHP()*max_percent/100;
|
||||
|
||||
// send bindmob new hp's
|
||||
@ -2736,6 +2747,8 @@ bool Client::BindWound(Mob* bindmob, bool start, bool fail){
|
||||
|
||||
bindhps += bindhps*bindBonus / 100;
|
||||
|
||||
bindhps = mod_bindwound_hp(bindhps, bindmob);
|
||||
|
||||
//if the bind takes them above the max bindable
|
||||
//cap it at that value. Dont know if live does it this way
|
||||
//but it makes sense to me.
|
||||
@ -7622,3 +7635,110 @@ some day.
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Client::LoadAccountFlags()
|
||||
{
|
||||
char errbuf[MYSQL_ERRMSG_SIZE];
|
||||
char *query = 0;
|
||||
MYSQL_RES *result;
|
||||
MYSQL_ROW row;
|
||||
|
||||
accountflags.clear();
|
||||
MakeAnyLenString(&query, "SELECT p_flag, p_value FROM account_flags WHERE p_accid = '%d'", account_id);
|
||||
if(database.RunQuery(query, strlen(query), errbuf, &result))
|
||||
{
|
||||
while(row = mysql_fetch_row(result))
|
||||
{
|
||||
std::string fname(row[0]);
|
||||
std::string fval(row[1]);
|
||||
accountflags[fname] = fval;
|
||||
}
|
||||
mysql_free_result(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "Error in LoadAccountFlags query '" << query << "' " << errbuf << std::endl;
|
||||
}
|
||||
safe_delete_array(query);
|
||||
}
|
||||
|
||||
void Client::SetAccountFlag(std::string flag, std::string val)
|
||||
{
|
||||
char errbuf[MYSQL_ERRMSG_SIZE];
|
||||
char *query = 0;
|
||||
|
||||
MakeAnyLenString(&query, "REPLACE INTO account_flags (p_accid, p_flag, p_value) VALUES( '%d', '%s', '%s')", account_id, flag.c_str(), val.c_str());
|
||||
if(!database.RunQuery(query, strlen(query), errbuf))
|
||||
{
|
||||
std::cerr << "Error in SetAccountFlags query '" << query << "' " << errbuf << std::endl;
|
||||
}
|
||||
safe_delete_array(query);
|
||||
|
||||
accountflags[flag] = val;
|
||||
}
|
||||
|
||||
std::string Client::GetAccountFlag(std::string flag)
|
||||
{
|
||||
return(accountflags[flag]);
|
||||
}
|
||||
|
||||
void Client::TickItemCheck()
|
||||
{
|
||||
int i;
|
||||
|
||||
if(zone->tick_items.empty()) { return; }
|
||||
|
||||
//Scan equip slots for items
|
||||
for(i = 0; i <= 21; i++)
|
||||
{
|
||||
TryItemTick(i);
|
||||
}
|
||||
//Scan main inventory + cursor
|
||||
for(i = 22; i < 31; i++)
|
||||
{
|
||||
TryItemTick(i);
|
||||
}
|
||||
//Scan bags
|
||||
for(i = 251; i < 340; i++)
|
||||
{
|
||||
TryItemTick(i);
|
||||
}
|
||||
}
|
||||
|
||||
void Client::TryItemTick(int slot)
|
||||
{
|
||||
int iid = 0;
|
||||
const ItemInst* inst = m_inv[slot];
|
||||
if(inst == 0) { return; }
|
||||
|
||||
iid = inst->GetID();
|
||||
|
||||
if(zone->tick_items.count(iid) > 0)
|
||||
{
|
||||
if( GetLevel() >= zone->tick_items[iid].level && MakeRandomInt(0, 100) >= (100 - zone->tick_items[iid].chance) && (zone->tick_items[iid].bagslot || slot < 22) )
|
||||
{
|
||||
ItemInst* e_inst = (ItemInst*)inst;
|
||||
parse->EventItem(EVENT_ITEM_TICK, this, e_inst, e_inst->GetID(), slot);
|
||||
}
|
||||
}
|
||||
|
||||
//Only look at augs in main inventory
|
||||
if(slot > 21) { return; }
|
||||
|
||||
for(int x = 0; x < MAX_AUGMENT_SLOTS; ++x)
|
||||
{
|
||||
ItemInst * a_inst = inst->GetAugment(x);
|
||||
if(!a_inst) { continue; }
|
||||
|
||||
iid = a_inst->GetID();
|
||||
|
||||
if(zone->tick_items.count(iid) > 0)
|
||||
{
|
||||
if( GetLevel() >= zone->tick_items[iid].level && MakeRandomInt(0, 100) >= (100 - zone->tick_items[iid].chance) )
|
||||
{
|
||||
ItemInst* e_inst = (ItemInst*)a_inst;
|
||||
parse->EventItem(EVENT_ITEM_TICK, this, e_inst, e_inst->GetID(), slot);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1134,6 +1134,35 @@ public:
|
||||
void DuplicateLoreMessage(uint32 ItemID);
|
||||
void GarbleMessage(char *, uint8);
|
||||
|
||||
void TickItemCheck();
|
||||
void TryItemTick(int slot);
|
||||
int16 GetActSTR() { return( min(GetMaxSTR(), GetSTR()) ); }
|
||||
int16 GetActSTA() { return( min(GetMaxSTA(), GetSTA()) ); }
|
||||
int16 GetActDEX() { return( min(GetMaxDEX(), GetDEX()) ); }
|
||||
int16 GetActAGI() { return( min(GetMaxAGI(), GetAGI()) ); }
|
||||
int16 GetActINT() { return( min(GetMaxINT(), GetINT()) ); }
|
||||
int16 GetActWIS() { return( min(GetMaxWIS(), GetWIS()) ); }
|
||||
int16 GetActCHA() { return( min(GetMaxCHA(), GetCHA()) ); }
|
||||
void LoadAccountFlags();
|
||||
void SetAccountFlag(std::string flag, std::string val);
|
||||
std::string GetAccountFlag(std::string flag); float GetDamageMultiplier(SkillType);
|
||||
int mod_client_damage(int damage, SkillType skillinuse, int hand, ItemInst* weapon, Mob* other);
|
||||
bool mod_client_message(char* message, uint8 chan_num);
|
||||
bool mod_can_increase_skill(SkillType skillid, Mob* against_who);
|
||||
int16 mod_increase_skill_chance(int16 chance, Mob* against_who);
|
||||
int mod_bindwound_percent(int max_percent, Mob* bindmob);
|
||||
int mod_bindwound_hp(int bindhps, Mob* bindmob);
|
||||
int mod_client_haste(int h);
|
||||
void mod_consider(Mob* tmob, Consider_Struct* con);
|
||||
bool mod_saylink(const std::string&, bool silentsaylink);
|
||||
int16 mod_pet_power(int16 act_power, uint16 spell_id);
|
||||
float mod_tradeskill_chance(float chance, DBTradeskillRecipe_Struct *spec);
|
||||
float mod_tradeskill_skillup(float chance_stage2);
|
||||
int32 mod_tribute_item_value(int32 pts);
|
||||
void mod_client_death_npc(Mob* killerMob);
|
||||
void mod_client_death_duel(Mob* killerMob);
|
||||
void mod_client_death_env();
|
||||
|
||||
protected:
|
||||
friend class Mob;
|
||||
void CalcItemBonuses(StatBonuses* newbon);
|
||||
@ -1437,6 +1466,8 @@ private:
|
||||
|
||||
struct XTarget_Struct XTargets[XTARGET_HARDCAP];
|
||||
|
||||
Timer ItemTickTimer;
|
||||
std::map<std::string,std::string> accountflags;
|
||||
};
|
||||
|
||||
#include "parser.h"
|
||||
|
||||
@ -1415,6 +1415,8 @@ int Client::CalcHaste() {
|
||||
h += spellbonuses.hastetype3;
|
||||
h += ExtraHaste; //GM granted haste.
|
||||
|
||||
h = mod_client_haste(h);
|
||||
|
||||
if (spellbonuses.inhibitmelee){
|
||||
if (h >= 0)
|
||||
h -= spellbonuses.inhibitmelee;
|
||||
|
||||
@ -2076,6 +2076,29 @@ void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app)
|
||||
}
|
||||
}
|
||||
|
||||
int r;
|
||||
bool tryaug = false;
|
||||
ItemInst* clickaug = 0;
|
||||
Item_Struct* augitem = 0;
|
||||
|
||||
for(r = 0; r < MAX_AUGMENT_SLOTS; r++) {
|
||||
const ItemInst* aug_i = inst->GetAugment(r);
|
||||
if(!aug_i)
|
||||
continue;
|
||||
const Item_Struct* aug = aug_i->GetItem();
|
||||
if(!aug)
|
||||
continue;
|
||||
|
||||
if ( (aug->Click.Type == ET_ClickEffect) || (aug->Click.Type == ET_Expendable) || (aug->Click.Type == ET_EquipClick) || (aug->Click.Type == ET_ClickEffect2) )
|
||||
{
|
||||
tryaug = true;
|
||||
clickaug = (ItemInst*)aug_i;
|
||||
augitem = (Item_Struct*)aug;
|
||||
spell_id = aug->Click.Effect;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if((spell_id <= 0) && (item->ItemType != ItemTypeFood && item->ItemType != ItemTypeDrink && item->ItemType != ItemTypeAlcohol && item->ItemType != ItemTypeSpell))
|
||||
{
|
||||
LogFile->write(EQEMuLog::Debug, "Item with no effect right clicked by %s",GetName());
|
||||
@ -2123,6 +2146,39 @@ void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app)
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (tryaug)
|
||||
{
|
||||
if (clickaug->GetCharges() == 0)
|
||||
{
|
||||
//Message(0, "This item is out of charges.");
|
||||
Message_StringID(13, ITEM_OUT_OF_CHARGES);
|
||||
return;
|
||||
}
|
||||
if(GetLevel() >= augitem->Click.Level2)
|
||||
{
|
||||
if(parse->ItemHasQuestSub(clickaug, "EVENT_ITEM_CLICK_CAST"))
|
||||
{
|
||||
//TODO: need to enforce and set recast timers here because the spell may not be cast.
|
||||
parse->EventItem(EVENT_ITEM_CLICK_CAST, this, clickaug, clickaug->GetID(), slot_id);
|
||||
inst = m_inv[slot_id];
|
||||
if (!inst)
|
||||
{
|
||||
// Item was deleted by the perl event
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//We assume augs aren't consumable
|
||||
CastSpell(augitem->Click.Effect, target_id, 10, augitem->CastTime, 0, 0, slot_id);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Message_StringID(13, ITEMS_INSUFFICIENT_LEVEL);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(GetClientVersion() >= EQClientSoD && !inst->IsEquipable(GetBaseRace(),GetClass()))
|
||||
@ -2605,6 +2661,8 @@ void Client::Handle_OP_Consider(const EQApplicationPacket *app)
|
||||
con->faction = FACTION_DUBIOUS;
|
||||
}
|
||||
|
||||
mod_consider(tmob, con);
|
||||
|
||||
QueuePacket(outapp);
|
||||
safe_delete(outapp);
|
||||
return;
|
||||
@ -3200,6 +3258,8 @@ void Client::Handle_OP_ItemLinkClick(const EQApplicationPacket *app)
|
||||
|
||||
if((response).size() > 0)
|
||||
{
|
||||
if( !mod_saylink(response, silentsaylink) ) { return; }
|
||||
|
||||
if(this->GetTarget() && this->GetTarget()->IsNPC())
|
||||
{
|
||||
if(silentsaylink)
|
||||
@ -6069,7 +6129,7 @@ void Client::Handle_OP_RecipesFavorite(const EQApplicationPacket *app)
|
||||
" LEFT JOIN tradeskill_recipe_entries AS tre ON tr.id=tre.recipe_id "
|
||||
" LEFT JOIN (SELECT recipe_id, madecount FROM char_recipe_list WHERE char_id = %u) AS crl ON tr.id=crl.recipe_id "
|
||||
" WHERE tr.id IN (%s) "
|
||||
" AND tr.must_learn & 0x20 <> 0x20 AND ((tr.must_learn & 0x3 <> 0 AND crl.madecount IS NOT nullptr) OR (tr.must_learn & 0x3 = 0)) "
|
||||
" AND tr.must_learn & 0x20 <> 0x20 AND ((tr.must_learn & 0x3 <> 0 AND crl.madecount IS NOT NULL) OR (tr.must_learn & 0x3 = 0)) "
|
||||
" GROUP BY tr.id "
|
||||
" HAVING sum(if(tre.item_id %s AND tre.iscontainer > 0,1,0)) > 0 "
|
||||
" LIMIT 100 ", CharacterID(), buf, containers);
|
||||
@ -6124,7 +6184,7 @@ void Client::Handle_OP_RecipesSearch(const EQApplicationPacket *app)
|
||||
" LEFT JOIN tradeskill_recipe_entries AS tre ON tr.id=tre.recipe_id "
|
||||
" LEFT JOIN (SELECT recipe_id, madecount FROM char_recipe_list WHERE char_id = %u) AS crl ON tr.id=crl.recipe_id "
|
||||
" WHERE %s tr.trivial >= %u AND tr.trivial <= %u "
|
||||
" AND tr.must_learn & 0x20 <> 0x20 AND((tr.must_learn & 0x3 <> 0 AND crl.madecount IS NOT nullptr) OR (tr.must_learn & 0x3 = 0)) "
|
||||
" AND tr.must_learn & 0x20 <> 0x20 AND((tr.must_learn & 0x3 <> 0 AND crl.madecount IS NOT NULL) OR (tr.must_learn & 0x3 = 0)) "
|
||||
" GROUP BY tr.id "
|
||||
" HAVING sum(if(tre.item_id %s AND tre.iscontainer > 0,1,0)) > 0 "
|
||||
" LIMIT 200 "
|
||||
@ -7714,7 +7774,11 @@ void Client::Handle_OP_EnvDamage(const EQApplicationPacket *app)
|
||||
SetHP(GetHP() - damage);
|
||||
|
||||
if(GetHP() <= 0)
|
||||
{
|
||||
mod_client_death_env();
|
||||
|
||||
Death(0, 32000, SPELL_UNKNOWN, HAND_TO_HAND);
|
||||
}
|
||||
SendHPUpdate();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -645,6 +645,11 @@ bool Client::Process() {
|
||||
--m_pp.intoxication;
|
||||
CalcBonuses();
|
||||
}
|
||||
|
||||
if(ItemTickTimer.Check())
|
||||
{
|
||||
TickItemCheck();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -88,7 +88,10 @@ const char *QuestEventSubroutines[_LargestEventID] = {
|
||||
"EVENT_CLICK_OBJECT",
|
||||
"EVENT_DISCOVER_ITEM",
|
||||
"EVENT_DISCONNECT",
|
||||
"EVENT_CONNECT"
|
||||
"EVENT_CONNECT",
|
||||
"EVENT_ITEM_TICK",
|
||||
"EVENT_DUEL_WIN",
|
||||
"EVENT_DUEL_LOSE"
|
||||
};
|
||||
|
||||
PerlembParser::PerlembParser() : perl(nullptr) {
|
||||
|
||||
@ -1888,6 +1888,12 @@ void EntityList::QueueClientsStatus(Mob* sender, const EQApplicationPacket* app,
|
||||
void EntityList::DuelMessage(Mob* winner, Mob* loser, bool flee) {
|
||||
LinkedListIterator<Client*> iterator(client_list);
|
||||
|
||||
if(winner->GetLevelCon(winner->GetLevel(), loser->GetLevel()) > 2)
|
||||
{
|
||||
parse->EventPlayer(EVENT_DUEL_WIN, winner->CastToClient(), loser->GetName(), loser->CastToClient()->CharacterID());
|
||||
parse->EventPlayer(EVENT_DUEL_LOSE, loser->CastToClient(), winner->GetName(), winner->CastToClient()->CharacterID());
|
||||
}
|
||||
|
||||
iterator.Reset();
|
||||
while(iterator.MoreElements()) {
|
||||
Client *cur = iterator.GetData();
|
||||
|
||||
@ -58,6 +58,9 @@ typedef enum {
|
||||
EVENT_DISCOVER_ITEM,
|
||||
EVENT_DISCONNECT,
|
||||
EVENT_CONNECT,
|
||||
EVENT_ITEM_TICK,
|
||||
EVENT_DUEL_WIN,
|
||||
EVENT_DUEL_LOSE,
|
||||
|
||||
_LargestEventID
|
||||
} QuestEventID;
|
||||
|
||||
@ -289,8 +289,16 @@ void Client::SetEXP(uint32 set_exp, uint32 set_aaxp, bool isrezzexp) {
|
||||
|
||||
if(check_level > maxlevel) {
|
||||
check_level = maxlevel;
|
||||
|
||||
if(RuleB(Character, KeepLevelOverMax))
|
||||
{
|
||||
set_exp = GetEXPForLevel(GetLevel()+1);
|
||||
}
|
||||
else
|
||||
{
|
||||
set_exp = GetEXPForLevel(maxlevel);
|
||||
}
|
||||
}
|
||||
|
||||
if(RuleB(Character, PerCharacterQglobalMaxLevel)){
|
||||
uint32 MaxLevel = GetCharMaxLevelFromQGlobal();
|
||||
|
||||
@ -376,3 +376,8 @@ void NPC::AddLootTable() {
|
||||
}
|
||||
}
|
||||
|
||||
void NPC::AddLootTable(uint32 ldid) {
|
||||
if (npctype_id != 0) { // check if it's a GM spawn
|
||||
database.AddLootTableToNPC(this,ldid, &itemlist, &copper, &silver, &gold, &platinum);
|
||||
}
|
||||
}
|
||||
|
||||
23
zone/mob.cpp
23
zone/mob.cpp
@ -1975,7 +1975,7 @@ void Mob::SetAttackTimer() {
|
||||
continue;
|
||||
if(pi->IsType(ItemClassContainer) && pi->GetItem()->BagType == bagTypeQuiver)
|
||||
{
|
||||
float temp_wr = (pi->GetItem()->BagWR / 3);
|
||||
float temp_wr = ( pi->GetItem()->BagWR / RuleI(Combat, QuiverWRHasteDiv) );
|
||||
if(temp_wr > max_quiver)
|
||||
{
|
||||
max_quiver = temp_wr;
|
||||
@ -3784,12 +3784,12 @@ void Mob::InsertQuestGlobal(int charid, int npcid, int zoneid, const char *varna
|
||||
char *query = 0;
|
||||
char errbuf[MYSQL_ERRMSG_SIZE];
|
||||
|
||||
// Make duration string either "unix_timestamp(now()) + xxx" or "nullptr"
|
||||
// Make duration string either "unix_timestamp(now()) + xxx" or "NULL"
|
||||
stringstream duration_ss;
|
||||
|
||||
if (duration == INT_MAX)
|
||||
{
|
||||
duration_ss << "nullptr";
|
||||
duration_ss << "NULL";
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -4675,3 +4675,20 @@ FACTION_VALUE Mob::GetSpecialFactionCon(Mob* iOther) {
|
||||
}
|
||||
}
|
||||
|
||||
bool Mob::HasSpellEffect(int effectid)
|
||||
{
|
||||
int i;
|
||||
|
||||
uint32 buff_count = GetMaxTotalSlots();
|
||||
for(i = 0; i < buff_count; i++)
|
||||
{
|
||||
if(buffs[i].spellid == SPELL_UNKNOWN) { continue; }
|
||||
|
||||
if(IsEffectInSpell(buffs[i].spellid, effectid))
|
||||
{
|
||||
return(1);
|
||||
}
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
24
zone/mob.h
24
zone/mob.h
@ -789,6 +789,30 @@ public:
|
||||
inline void SetEmoteID(uint16 emote) { emoteid = emote; }
|
||||
inline uint16 GetEmoteID() { return emoteid; }
|
||||
|
||||
bool HasSpellEffect(int effectid);
|
||||
int mod_effect_value(int effect_value, uint16 spell_id, int effect_type, Mob* caster);
|
||||
float mod_hit_chance(float chancetohit, SkillType skillinuse, Mob* attacker);
|
||||
float mod_riposte_chance(float ripostchance, Mob* attacker);
|
||||
float mod_block_chance(float blockchance, Mob* attacker);
|
||||
float mod_parry_chance(float parrychance, Mob* attacker);
|
||||
float mod_dodge_chance(float dodgechance, Mob* attacker);
|
||||
float mod_monk_weight(float monkweight, Mob* attacker);
|
||||
float mod_mitigation_rating(float mitigation_rating, Mob* attacker);
|
||||
float mod_attack_rating(float attack_rating, Mob* defender);
|
||||
int32 mod_kick_damage(int32 dmg);
|
||||
int32 mod_bash_damage(int32 dmg);
|
||||
int32 mod_frenzy_damage(int32 dmg);
|
||||
int32 mod_monk_special_damage(int32 ndamage, SkillType skill_type);
|
||||
int32 mod_backstab_damage(int32 ndamage);
|
||||
int mod_archery_bonus_chance(int bonuschance, const ItemInst* RangeWeapon);
|
||||
uint32 mod_archery_bonus_damage(uint32 MaxDmg);
|
||||
int32 mod_archery_damage(int32 TotalDmg, bool hasbonus);
|
||||
uint16 mod_throwing_damage(uint16 MaxDmg);
|
||||
int32 mod_cast_time(int32 cast_time);
|
||||
int mod_buff_duration(int res, Mob* caster, Mob* target, uint16 spell_id);
|
||||
int mod_spell_stack(uint16 spellid1, int caster_level1, Mob* caster1, uint16 spellid2, int caster_level2, Mob* caster2);
|
||||
int mod_spell_resist(int resist_chance, int level_mod, int resist_modifier, int target_resist, uint8 resist_type, uint16 spell_id, Mob* caster);
|
||||
|
||||
protected:
|
||||
void CommonDamage(Mob* other, int32 &damage, const uint16 spell_id, const SkillType attack_skill, bool &avoidable, const int8 buffslot, const bool iBuffTic);
|
||||
static uint16 GetProcID(uint16 spell_id, uint8 effect_index);
|
||||
|
||||
72
zone/mod_functions.cpp
Normal file
72
zone/mod_functions.cpp
Normal file
@ -0,0 +1,72 @@
|
||||
#include "../common/debug.h"
|
||||
#include "../common/timer.h"
|
||||
#include <cmath>
|
||||
#include <stdlib.h>
|
||||
#include "spawn2.h"
|
||||
#include "entity.h"
|
||||
#include "masterentity.h"
|
||||
#include "zone.h"
|
||||
#include "spawngroup.h"
|
||||
#include "zonedb.h"
|
||||
#include "npc.h"
|
||||
#include "mob.h"
|
||||
#include "client.h"
|
||||
#include "worldserver.h"
|
||||
#include "QuestParserCollection.h"
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
extern EntityList entity_list;
|
||||
extern Zone* zone;
|
||||
|
||||
extern WorldServer worldserver;
|
||||
|
||||
using namespace std;
|
||||
|
||||
void Zone::mod_init() { return; }
|
||||
void Zone::mod_repop() { return; }
|
||||
|
||||
void NPC::mod_prespawn(Spawn2 *sp) { return; }
|
||||
int NPC::mod_npc_damage(int damage, SkillType skillinuse, int hand, ItemInst* weapon, Mob* other) { return(damage); }
|
||||
void NPC::mod_npc_killed_merit(Mob* c) { return; }
|
||||
void NPC::mod_npc_killed(Mob* oos) { return; }
|
||||
|
||||
int Client::mod_client_damage(int damage, SkillType skillinuse, int hand, ItemInst* weapon, Mob* other) { return(damage); }
|
||||
bool Client::mod_client_message(char* message, uint8 chan_num) { return(true); } //Potentially dangerous string handling here
|
||||
bool Client::mod_can_increase_skill(SkillType skillid, Mob* against_who) { return(false); }
|
||||
int16 Client::mod_increase_skill_chance(int16 chance, Mob* against_who) { return(chance); }
|
||||
int Client::mod_bindwound_percent(int max_percent, Mob* bindmob) { return(max_percent); }
|
||||
int Client::mod_bindwound_hp(int bindhps, Mob* bindmob) { return(bindhps); }
|
||||
int Client::mod_client_haste(int h) { return(h); }
|
||||
void Client::mod_consider(Mob* tmob, Consider_Struct* con) { return; }
|
||||
bool Client::mod_saylink(const std::string&, bool silentsaylink) { return(true); }
|
||||
int16 Client::mod_pet_power(int16 act_power, uint16 spell_id) { return(act_power); }
|
||||
float Client::mod_tradeskill_chance(float chance, DBTradeskillRecipe_Struct *spec) { return(chance); }
|
||||
float Client::mod_tradeskill_skillup(float chance_stage2) { return(chance_stage2); }
|
||||
int32 Client::mod_tribute_item_value(int32 pts) { return(pts); }
|
||||
void Client::mod_client_death_npc(Mob* killerMob) { return; }
|
||||
void Client::mod_client_death_duel(Mob* killerMob) { return; }
|
||||
void Client::mod_client_death_env() { return; }
|
||||
|
||||
int Mob::mod_effect_value(int effect_value, uint16 spell_id, int effect_type, Mob* caster) { return(effect_value); }
|
||||
float Mob::mod_hit_chance(float chancetohit, SkillType skillinuse, Mob* attacker) { return(chancetohit); }
|
||||
float Mob::mod_riposte_chance(float ripostechance, Mob* attacker) { return(ripostechance); }
|
||||
float Mob::mod_block_chance(float blockchance, Mob* attacker) { return(blockchance); }
|
||||
float Mob::mod_parry_chance(float parrychance, Mob* attacker) { return(parrychance); }
|
||||
float Mob::mod_dodge_chance(float dodgechance, Mob* attacker) { return(dodgechance); }
|
||||
float Mob::mod_monk_weight(float monkweight, Mob* attacker) { return(monkweight); }
|
||||
float Mob::mod_mitigation_rating(float mitigation_rating, Mob* attacker) { return(mitigation_rating); }
|
||||
float Mob::mod_attack_rating(float attack_rating, Mob* defender) { return(attack_rating); }
|
||||
int32 Mob::mod_kick_damage(int32 dmg) { return(dmg); }
|
||||
int32 Mob::mod_bash_damage(int32 dmg) { return(dmg); }
|
||||
int32 Mob::mod_frenzy_damage(int32 dmg) { return(dmg); }
|
||||
int32 Mob::mod_monk_special_damage(int32 ndamage, SkillType skill_type) { return(ndamage); }
|
||||
int32 Mob::mod_backstab_damage(int32 ndamage) { return(ndamage); }
|
||||
int Mob::mod_archery_bonus_chance(int bonuschance, const ItemInst* RangeWeapon) { return(bonuschance); }
|
||||
uint32 Mob::mod_archery_bonus_damage(uint32 MaxDmg) { return(MaxDmg); }
|
||||
int32 Mob::mod_archery_damage(int32 TotalDmg, bool hasbonus) { return(TotalDmg); }
|
||||
uint16 Mob::mod_throwing_damage(uint16 MaxDmg) { return(MaxDmg); }
|
||||
int32 Mob::mod_cast_time(int32 cast_time) { return(cast_time); }
|
||||
int Mob::mod_buff_duration(int res, Mob* caster, Mob* target, uint16 spell_id) { return(res); }
|
||||
int Mob::mod_spell_stack(uint16 spellid1, int caster_level1, Mob* caster1, uint16 spellid2, int caster_level2, Mob* caster2) { return(2); }
|
||||
int Mob::mod_spell_resist(int resist_chance, int level_mod, int resist_modifier, int target_resist, uint8 resist_type, uint16 spell_id, Mob* caster) { return(resist_chance + level_mod + resist_modifier + target_resist); }
|
||||
72
zone/mod_functions_base.cpp
Normal file
72
zone/mod_functions_base.cpp
Normal file
@ -0,0 +1,72 @@
|
||||
#include "../common/debug.h"
|
||||
#include "../common/timer.h"
|
||||
#include <cmath>
|
||||
#include <stdlib.h>
|
||||
#include "spawn2.h"
|
||||
#include "entity.h"
|
||||
#include "masterentity.h"
|
||||
#include "zone.h"
|
||||
#include "spawngroup.h"
|
||||
#include "zonedb.h"
|
||||
#include "npc.h"
|
||||
#include "mob.h"
|
||||
#include "client.h"
|
||||
#include "worldserver.h"
|
||||
#include "QuestParserCollection.h"
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
extern EntityList entity_list;
|
||||
extern Zone* zone;
|
||||
|
||||
extern WorldServer worldserver;
|
||||
|
||||
using namespace std;
|
||||
|
||||
void Zone::mod_init() { return; }
|
||||
void Zone::mod_repop() { return; }
|
||||
|
||||
void NPC::mod_prespawn(Spawn2 *sp) { return; }
|
||||
int NPC::mod_npc_damage(int damage, SkillType skillinuse, int hand, ItemInst* weapon, Mob* other) { return(damage); }
|
||||
void NPC::mod_npc_killed_merit(Mob* c) { return; }
|
||||
void NPC::mod_npc_killed(Mob* oos) { return; }
|
||||
|
||||
int Client::mod_client_damage(int damage, SkillType skillinuse, int hand, ItemInst* weapon, Mob* other) { return(damage); }
|
||||
bool Client::mod_client_message(char* message, uint8 chan_num) { return(true); } //Potentially dangerous string handling here
|
||||
bool Client::mod_can_increase_skill(SkillType skillid, Mob* against_who) { return(false); }
|
||||
int16 Client::mod_increase_skill_chance(int16 chance, Mob* against_who) { return(chance); }
|
||||
int Client::mod_bindwound_percent(int max_percent, Mob* bindmob) { return(max_percent); }
|
||||
int Client::mod_bindwound_hp(int bindhps, Mob* bindmob) { return(bindhps); }
|
||||
int Client::mod_client_haste(int h) { return(h); }
|
||||
void Client::mod_consider(Mob* tmob, Consider_Struct* con) { return; }
|
||||
bool Client::mod_saylink(const std::string&, bool silentsaylink) { return(true); }
|
||||
int16 Client::mod_pet_power(int16 act_power, uint16 spell_id) { return(act_power); }
|
||||
float Client::mod_tradeskill_chance(float chance, DBTradeskillRecipe_Struct *spec) { return(chance); }
|
||||
float Client::mod_tradeskill_skillup(float chance_stage2) { return(chance_stage2); }
|
||||
int32 Client::mod_tribute_item_value(int32 pts) { return(pts); }
|
||||
void Client::mod_client_death_npc(Mob* killerMob) { return; }
|
||||
void Client::mod_client_death_duel(Mob* killerMob) { return; }
|
||||
void Client::mod_client_death_env() { return; }
|
||||
|
||||
int Mob::mod_effect_value(int effect_value, uint16 spell_id, int effect_type, Mob* caster) { return(effect_value); }
|
||||
float Mob::mod_hit_chance(float chancetohit, SkillType skillinuse, Mob* attacker) { return(chancetohit); }
|
||||
float Mob::mod_riposte_chance(float ripostechance, Mob* attacker) { return(ripostechance); }
|
||||
float Mob::mod_block_chance(float blockchance, Mob* attacker) { return(blockchance); }
|
||||
float Mob::mod_parry_chance(float parrychance, Mob* attacker) { return(parrychance); }
|
||||
float Mob::mod_dodge_chance(float dodgechance, Mob* attacker) { return(dodgechance); }
|
||||
float Mob::mod_monk_weight(float monkweight, Mob* attacker) { return(monkweight); }
|
||||
float Mob::mod_mitigation_rating(float mitigation_rating, Mob* attacker) { return(mitigation_rating); }
|
||||
float Mob::mod_attack_rating(float attack_rating, Mob* defender) { return(attack_rating); }
|
||||
int32 Mob::mod_kick_damage(int32 dmg) { return(dmg); }
|
||||
int32 Mob::mod_bash_damage(int32 dmg) { return(dmg); }
|
||||
int32 Mob::mod_frenzy_damage(int32 dmg) { return(dmg); }
|
||||
int32 Mob::mod_monk_special_damage(int32 ndamage, SkillType skill_type) { return(ndamage); }
|
||||
int32 Mob::mod_backstab_damage(int32 ndamage) { return(ndamage); }
|
||||
int Mob::mod_archery_bonus_chance(int bonuschance, const ItemInst* RangeWeapon) { return(bonuschance); }
|
||||
uint32 Mob::mod_archery_bonus_damage(uint32 MaxDmg) { return(MaxDmg); }
|
||||
int32 Mob::mod_archery_damage(int32 TotalDmg, bool hasbonus) { return(TotalDmg); }
|
||||
uint16 Mob::mod_throwing_damage(uint16 MaxDmg) { return(MaxDmg); }
|
||||
int32 Mob::mod_cast_time(int32 cast_time) { return(cast_time); }
|
||||
int Mob::mod_buff_duration(int res, Mob* caster, Mob* target, uint16 spell_id) { return(res); }
|
||||
int Mob::mod_spell_stack(uint16 spellid1, int caster_level1, Mob* caster1, uint16 spellid2, int caster_level2, Mob* caster2) { return(2); }
|
||||
int Mob::mod_spell_resist(int resist_chance, int level_mod, int resist_modifier, int target_resist, uint8 resist_type, uint16 spell_id, Mob* caster) { return(resist_chance + level_mod + resist_modifier + target_resist); }
|
||||
75
zone/npc.cpp
75
zone/npc.cpp
@ -1895,7 +1895,8 @@ void NPC::ModifyNPCStat(const char *identifier, const char *newValue)
|
||||
|
||||
if(id == "special_attacks")
|
||||
{
|
||||
NPCSpecialAttacks(val.c_str(), 0);
|
||||
//Added reset flag.
|
||||
NPCSpecialAttacks(val.c_str(), 0, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -2414,3 +2415,75 @@ FACTION_VALUE NPC::CheckNPCFactionAlly(int32 other_faction) {
|
||||
bool NPC::IsFactionListAlly(uint32 other_faction) {
|
||||
return(CheckNPCFactionAlly(other_faction) == FACTION_ALLY);
|
||||
}
|
||||
|
||||
int NPC::GetScore()
|
||||
{
|
||||
int lv = min(70, (int)GetLevel());
|
||||
int basedmg = (lv*2)*(1+(lv / 100)) - (lv / 2);
|
||||
int minx = 0;
|
||||
int basehp = 0;
|
||||
int hpcontrib = 0;
|
||||
int dmgcontrib = 0;
|
||||
int spccontrib = 0;
|
||||
int hp = GetMaxHP();
|
||||
int mindmg = min_dmg;
|
||||
int maxdmg = max_dmg;
|
||||
int final;
|
||||
|
||||
if(lv < 46)
|
||||
{
|
||||
minx = static_cast<int> (ceil( ((lv - (lv / 10.0)) - 1.0) ));
|
||||
basehp = (lv * 10) + (lv * lv);
|
||||
}
|
||||
else
|
||||
{
|
||||
minx = static_cast<int> (ceil( ((lv - (lv / 10.0)) - 1.0) - (( lv - 45.0 ) / 2.0) ));
|
||||
basehp = (lv * 10) + ((lv * lv) * 4);
|
||||
}
|
||||
|
||||
if(hp > basehp)
|
||||
{
|
||||
hpcontrib = static_cast<int> (((hp / static_cast<float> (basehp)) * 1.5));
|
||||
if(hpcontrib > 5) { hpcontrib = 5; }
|
||||
|
||||
if(maxdmg > basedmg)
|
||||
{
|
||||
dmgcontrib = static_cast<int> (ceil( ((maxdmg / basedmg) * 1.5) ));
|
||||
}
|
||||
|
||||
if(HasNPCSpecialAtk("E")) { spccontrib++; } //Enrage
|
||||
if(HasNPCSpecialAtk("F")) { spccontrib++; } //Flurry
|
||||
if(HasNPCSpecialAtk("R")) { spccontrib++; } //Rampage
|
||||
if(HasNPCSpecialAtk("r")) { spccontrib++; } //Area Rampage
|
||||
if(HasNPCSpecialAtk("S")) { spccontrib++; } //Summon
|
||||
if(HasNPCSpecialAtk("T")) { spccontrib += 2; } //Triple
|
||||
if(HasNPCSpecialAtk("Q")) { spccontrib += 3; } //Quad
|
||||
if(HasNPCSpecialAtk("U")) { spccontrib += 5; } //Unslowable
|
||||
if(HasNPCSpecialAtk("L")) { spccontrib++; } //Innate Dual Wield
|
||||
}
|
||||
|
||||
if(npc_spells_id > 12)
|
||||
{
|
||||
if(lv < 16)
|
||||
spccontrib++;
|
||||
else
|
||||
spccontrib += static_cast<int> (floor(lv/15.0));
|
||||
}
|
||||
|
||||
final = minx + hpcontrib + dmgcontrib + spccontrib;
|
||||
final = max(1, final);
|
||||
final = min(100, final);
|
||||
return(final);
|
||||
}
|
||||
|
||||
uint32 NPC::GetSpawnKillCount()
|
||||
{
|
||||
uint32 sid = GetSpawnPointID();
|
||||
|
||||
if(sid > 0)
|
||||
{
|
||||
return(zone->GetSpawnKillCount(sid));
|
||||
}
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
@ -154,7 +154,7 @@ public:
|
||||
void AddItem(const Item_Struct* item, uint16 charges, bool equipitem = true);
|
||||
void AddItem(uint32 itemid, uint16 charges, bool equipitem = true);
|
||||
void AddLootTable();
|
||||
|
||||
void AddLootTable(uint32 ldid);
|
||||
void DescribeAggro(Client *towho, Mob *mob, bool verbose);
|
||||
void RemoveItem(uint32 item_id, uint16 quantity = 0, uint16 slot = 0);
|
||||
void CheckMinMaxLevel(Mob *them);
|
||||
@ -454,6 +454,13 @@ public:
|
||||
|
||||
void PrintOutQuestItems(Client* c);
|
||||
|
||||
uint32 GetSpawnKillCount();
|
||||
int GetScore();
|
||||
void mod_prespawn(Spawn2 *sp);
|
||||
int mod_npc_damage(int damage, SkillType skillinuse, int hand, ItemInst* weapon, Mob* other);
|
||||
void mod_npc_killed_merit(Mob* c);
|
||||
void mod_npc_killed(Mob* oos);
|
||||
|
||||
protected:
|
||||
|
||||
const NPCType* NPCTypedata;
|
||||
|
||||
@ -5651,6 +5651,91 @@ XS(XS_Client_GetInstanceID)
|
||||
XSRETURN(1);
|
||||
}
|
||||
|
||||
XS(XS_Client_HasSpellScribed); /* prototype to pass -Wmissing-prototypes */
|
||||
XS(XS_Client_HasSpellScribed)
|
||||
{
|
||||
dXSARGS;
|
||||
if (items != 2)
|
||||
Perl_croak(aTHX_ "Usage: Client::HasSpellScribed(THIS, spell_id)");
|
||||
{
|
||||
Client * THIS;
|
||||
bool RETVAL;
|
||||
int spell_id = (int)SvUV(ST(1));
|
||||
|
||||
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 == NULL)
|
||||
Perl_croak(aTHX_ "THIS is NULL, avoiding crash.");
|
||||
|
||||
RETVAL = THIS->HasSpellScribed(spell_id);
|
||||
ST(0) = boolSV(RETVAL);
|
||||
sv_2mortal(ST(0));
|
||||
}
|
||||
XSRETURN(1);
|
||||
}
|
||||
|
||||
XS(XS_Client_SetAccountFlag); /* prototype to pass -Wmissing-prototypes */
|
||||
XS(XS_Client_SetAccountFlag)
|
||||
{
|
||||
dXSARGS;
|
||||
if (items != 3)
|
||||
Perl_croak(aTHX_ "Usage: Client::SetAccountFlag(THIS, flag, value)");
|
||||
{
|
||||
Client * THIS;
|
||||
//char* flag = (char *)SvPV_nolen(ST(1));
|
||||
//char* value = (char *)SvTRUE(ST(2));
|
||||
|
||||
std::string flag( (char *)SvPV_nolen(ST(1)) );
|
||||
std::string value( (char *)SvTRUE(ST(2)) );
|
||||
|
||||
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 == NULL)
|
||||
Perl_croak(aTHX_ "THIS is NULL, avoiding crash.");
|
||||
|
||||
THIS->SetAccountFlag(flag, value);
|
||||
}
|
||||
XSRETURN_EMPTY;
|
||||
}
|
||||
|
||||
XS(XS_Client_GetAccountFlag); /* prototype to pass -Wmissing-prototypes */
|
||||
XS(XS_Client_GetAccountFlag)
|
||||
{
|
||||
dXSARGS;
|
||||
if (items != 2)
|
||||
Perl_croak(aTHX_ "Usage: Client::GetAccountFlag(THIS, flag)");
|
||||
{
|
||||
Client * THIS;
|
||||
//char* flag = (char *)SvPV_nolen(ST(1));
|
||||
//char* value = (char *)SvTRUE(ST(2));
|
||||
|
||||
std::string flag( (char *)SvPV_nolen(ST(1)) );
|
||||
dXSTARG;
|
||||
|
||||
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 == NULL)
|
||||
Perl_croak(aTHX_ "THIS is NULL, avoiding crash.");
|
||||
|
||||
std::string value = THIS->GetAccountFlag(flag);
|
||||
|
||||
sv_setpv(TARG, value.c_str()); XSprePUSH; PUSHTARG;
|
||||
}
|
||||
XSRETURN(1);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
#endif
|
||||
@ -5877,6 +5962,9 @@ XS(boot_Client)
|
||||
newXSproto(strcpy(buf, "AddAlternateCurrencyValue"), XS_Client_AddAlternateCurrencyValue, file, "$$$");
|
||||
newXSproto(strcpy(buf, "SendWebLink"), XS_Client_SendWebLink, file, "$:$");
|
||||
newXSproto(strcpy(buf, "GetInstanceID"), XS_Client_GetInstanceID, file, "$$");
|
||||
newXSproto(strcpy(buf, "HasSpellScribed"), XS_Client_HasSkill, file, "$$");
|
||||
newXSproto(strcpy(buf, "SetAccountFlag"), XS_Client_SetAccountFlag, file, "$$");
|
||||
newXSproto(strcpy(buf, "GetAccountFlag"), XS_Client_GetAccountFlag, file, "$$");
|
||||
XSRETURN_YES;
|
||||
}
|
||||
|
||||
|
||||
@ -3909,8 +3909,8 @@ XS(XS_Mob_CastSpell); /* prototype to pass -Wmissing-prototypes */
|
||||
XS(XS_Mob_CastSpell)
|
||||
{
|
||||
dXSARGS;
|
||||
if (items < 3 || items > 6)
|
||||
Perl_croak(aTHX_ "Usage: Mob::CastSpell(THIS, spell_id, target_id, slot= 10, casttime= -1, mana_cost= -1)");
|
||||
if (items < 3 || items > 7)
|
||||
Perl_croak(aTHX_ "Usage: Mob::CastSpell(THIS, spell_id, target_id, slot= 10, casttime= -1, mana_cost= -1, resist_adjust = 0)");
|
||||
{
|
||||
Mob * THIS;
|
||||
uint16 spell_id = (uint16)SvUV(ST(1));
|
||||
@ -3918,6 +3918,7 @@ XS(XS_Mob_CastSpell)
|
||||
uint16 slot;
|
||||
int32 casttime;
|
||||
int32 mana_cost;
|
||||
int16 resist_adjust;
|
||||
|
||||
if (sv_derived_from(ST(0), "Mob")) {
|
||||
IV tmp = SvIV((SV*)SvRV(ST(0)));
|
||||
@ -3946,7 +3947,16 @@ XS(XS_Mob_CastSpell)
|
||||
mana_cost = (int32)SvIV(ST(5));
|
||||
}
|
||||
|
||||
THIS->CastSpell(spell_id, target_id, slot, casttime, mana_cost);
|
||||
if(items < 7)
|
||||
{
|
||||
resist_adjust = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
resist_adjust = (int16)SvIV(ST(6));
|
||||
}
|
||||
|
||||
THIS->CastSpell(spell_id, target_id, slot, casttime, mana_cost, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0, 0, &resist_adjust);
|
||||
}
|
||||
XSRETURN_EMPTY;
|
||||
}
|
||||
@ -3955,13 +3965,14 @@ XS(XS_Mob_SpellFinished); /* prototype to pass -Wmissing-prototypes */
|
||||
XS(XS_Mob_SpellFinished)
|
||||
{
|
||||
dXSARGS;
|
||||
if (items < 2 || items > 4)
|
||||
Perl_croak(aTHX_ "Usage: Mob::SpellFinished(spell_id, spell_target = this, mana_cost = 0)");
|
||||
if (items < 2 || items > 5)
|
||||
Perl_croak(aTHX_ "Usage: Mob::SpellFinished(spell_id, spell_target = this, mana_cost = 0, resist_diff = 0)");
|
||||
{
|
||||
Mob * THIS;
|
||||
uint16 spell_id = (uint16)SvUV(ST(1));
|
||||
Mob * spell_target;
|
||||
uint16 mana_cost = 0;
|
||||
int16 resist_diff;
|
||||
|
||||
if (sv_derived_from(ST(0), "Mob")) {
|
||||
IV tmp = SvIV((SV*)SvRV(ST(0)));
|
||||
@ -3990,7 +4001,16 @@ XS(XS_Mob_SpellFinished)
|
||||
if (items > 3)
|
||||
mana_cost = (uint16)SvUV(ST(3));
|
||||
|
||||
THIS->SpellFinished(spell_id, spell_target, 10, mana_cost, -1, spells[spell_id].ResistDiff);
|
||||
if (items > 4)
|
||||
{
|
||||
resist_diff = (int16)SvUV(ST(4));
|
||||
}
|
||||
else
|
||||
{
|
||||
resist_diff = spells[spell_id].ResistDiff;
|
||||
}
|
||||
|
||||
THIS->SpellFinished(spell_id, spell_target, 10, mana_cost, -1, resist_diff);
|
||||
}
|
||||
XSRETURN_EMPTY;
|
||||
}
|
||||
|
||||
@ -143,8 +143,18 @@ XS(XS_NPC_AddLootTable)
|
||||
if(THIS == nullptr)
|
||||
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
|
||||
|
||||
uint32 loottable_id = 0;
|
||||
|
||||
if(items > 1)
|
||||
{
|
||||
loottable_id = (uint32)SvUV(ST(1));
|
||||
THIS->AddLootTable(loottable_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
THIS->AddLootTable();
|
||||
}
|
||||
}
|
||||
XSRETURN_EMPTY;
|
||||
}
|
||||
|
||||
@ -2075,6 +2085,57 @@ XS(XS_NPC_GetAccuracyRating)
|
||||
XSRETURN(1);
|
||||
}
|
||||
|
||||
XS(XS_NPC_GetSpawnKillCount); /* prototype to pass -Wmissing-prototypes */
|
||||
XS(XS_NPC_GetSpawnKillCount)
|
||||
{
|
||||
dXSARGS;
|
||||
if (items != 1)
|
||||
Perl_croak(aTHX_ "Usage: NPC::GetSpawnKillCount(THIS)");
|
||||
{
|
||||
NPC * THIS;
|
||||
uint32 RETVAL;
|
||||
dXSTARG;
|
||||
|
||||
if (sv_derived_from(ST(0), "NPC")) {
|
||||
IV tmp = SvIV((SV*)SvRV(ST(0)));
|
||||
THIS = INT2PTR(NPC *,tmp);
|
||||
}
|
||||
else
|
||||
Perl_croak(aTHX_ "THIS is not of type NPC");
|
||||
if(THIS == NULL)
|
||||
Perl_croak(aTHX_ "THIS is NULL, avoiding crash.");
|
||||
|
||||
RETVAL = THIS->GetSpawnKillCount();
|
||||
XSprePUSH; PUSHu((UV)RETVAL);
|
||||
}
|
||||
XSRETURN(1);
|
||||
}
|
||||
|
||||
XS(XS_NPC_GetScore); /* prototype to pass -Wmissing-prototypes */
|
||||
XS(XS_NPC_GetScore)
|
||||
{
|
||||
dXSARGS;
|
||||
if (items != 1)
|
||||
Perl_croak(aTHX_ "Usage: NPC::GetScore(THIS)");
|
||||
{
|
||||
NPC * THIS;
|
||||
int RETVAL;
|
||||
dXSTARG;
|
||||
|
||||
if (sv_derived_from(ST(0), "NPC")) {
|
||||
IV tmp = SvIV((SV*)SvRV(ST(0)));
|
||||
THIS = INT2PTR(NPC *,tmp);
|
||||
}
|
||||
else
|
||||
Perl_croak(aTHX_ "THIS is not of type NPC");
|
||||
if(THIS == NULL)
|
||||
Perl_croak(aTHX_ "THIS is NULL, avoiding crash.");
|
||||
|
||||
RETVAL = THIS->GetScore();
|
||||
XSprePUSH; PUSHi((UV)RETVAL);
|
||||
}
|
||||
XSRETURN(1);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
@ -2174,6 +2235,8 @@ XS(boot_NPC)
|
||||
newXSproto(strcpy(buf, "GetSlowMitigation"), XS_NPC_GetAttackSpeed, file, "$");
|
||||
newXSproto(strcpy(buf, "GetAttackSpeed"), XS_NPC_GetSlowMitigation, file, "$");
|
||||
newXSproto(strcpy(buf, "GetAccuracyRating"), XS_NPC_GetAccuracyRating, file, "$");
|
||||
newXSproto(strcpy(buf, "GetSpawnKillCount"), XS_NPC_GetSpawnKillCount, file, "$");
|
||||
newXSproto(strcpy(buf, "GetScore"), XS_NPC_GetScore, file, "$");
|
||||
XSRETURN_YES;
|
||||
}
|
||||
|
||||
|
||||
@ -232,8 +232,10 @@ void Mob::MakePoweredPet(uint16 spell_id, const char* pettype, int16 petpower, c
|
||||
|
||||
int16 act_power = 0; // The actual pet power we'll use.
|
||||
if (petpower == -1) {
|
||||
if (this->IsClient())
|
||||
if (this->IsClient()) {
|
||||
act_power = CastToClient()->GetFocusEffect(focusPetPower, spell_id);
|
||||
act_power = CastToClient()->mod_pet_power(act_power, spell_id);
|
||||
}
|
||||
}
|
||||
else if (petpower > 0)
|
||||
act_power = petpower;
|
||||
|
||||
@ -833,7 +833,8 @@ uint16 QuestManager::scribespells(uint8 max_level, uint8 min_level) {
|
||||
spells[curspell].classes[WARRIOR] != 0 && //check if spell exists
|
||||
spells[curspell].classes[initiator->GetPP().class_-1] <= max_level && //maximum level
|
||||
spells[curspell].classes[initiator->GetPP().class_-1] >= min_level && //minimum level
|
||||
spells[curspell].skill != 52
|
||||
spells[curspell].skill != 52 &&
|
||||
( RuleB(Spells, UseCHAScribeHack) && spells[curspell].effectid[EFFECT_COUNT - 1] != 10 )
|
||||
)
|
||||
{
|
||||
if (book_slot == -1) //no more book slots
|
||||
@ -872,7 +873,8 @@ uint16 QuestManager::traindiscs(uint8 max_level, uint8 min_level) {
|
||||
spells[curspell].classes[WARRIOR] != 0 && //check if spell exists
|
||||
spells[curspell].classes[initiator->GetPP().class_-1] <= max_level && //maximum level
|
||||
spells[curspell].classes[initiator->GetPP().class_-1] >= min_level && //minimum level
|
||||
spells[curspell].skill != 52
|
||||
spells[curspell].skill != 52 &&
|
||||
( RuleB(Spells, UseCHAScribeHack) && spells[curspell].effectid[EFFECT_COUNT - 1] != 10 )
|
||||
)
|
||||
{
|
||||
if(IsDiscipline(curspell)){
|
||||
@ -1239,11 +1241,11 @@ int QuestManager::InsertQuestGlobal(
|
||||
char *query = 0;
|
||||
char errbuf[MYSQL_ERRMSG_SIZE];
|
||||
|
||||
// Make duration string either "unix_timestamp(now()) + xxx" or "nullptr"
|
||||
// Make duration string either "unix_timestamp(now()) + xxx" or "NULL"
|
||||
stringstream duration_ss;
|
||||
if (duration == INT_MAX)
|
||||
{
|
||||
duration_ss << "nullptr";
|
||||
duration_ss << "NULL";
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@ -253,6 +253,8 @@ public:
|
||||
bool createBot(const char *name, const char *lastname, uint8 level, uint16 race, uint8 botclass, uint8 gender);
|
||||
#endif
|
||||
|
||||
inline uint16 GetMana(uint32 spell_id) { return( spells[spell_id].mana); }
|
||||
|
||||
protected:
|
||||
Mob *owner; //NPC is never nullptr when functions are called.
|
||||
Client *initiator; //this can be null.
|
||||
|
||||
@ -68,7 +68,7 @@ Spawn2::Spawn2(uint32 in_spawn2_id, uint32 spawngroup_id,
|
||||
float in_x, float in_y, float in_z, float in_heading,
|
||||
uint32 respawn, uint32 variance, uint32 timeleft, uint32 grid,
|
||||
uint16 in_cond_id, int16 in_min_value, bool in_enabled, EmuAppearance anim)
|
||||
: timer(100000)
|
||||
: timer(100000), killcount(0)
|
||||
{
|
||||
spawn2_id = in_spawn2_id;
|
||||
spawngroup_id_ = spawngroup_id;
|
||||
@ -219,6 +219,9 @@ bool Spawn2::Process() {
|
||||
|
||||
currentnpcid = npcid;
|
||||
NPC* npc = new NPC(tmp, this, x, y, z, heading, FlyMode3);
|
||||
|
||||
npc->mod_prespawn(this);
|
||||
|
||||
npcthis = npc;
|
||||
npc->AddLootTable();
|
||||
npc->SetSp2(spawngroup_id_);
|
||||
@ -328,7 +331,7 @@ void Spawn2::ForceDespawn()
|
||||
}
|
||||
|
||||
//resets our spawn as if we just died
|
||||
void Spawn2::DeathReset()
|
||||
void Spawn2::DeathReset(bool realdeath)
|
||||
{
|
||||
//get our reset based on variance etc and store it locally
|
||||
uint32 cur = resetTimer();
|
||||
@ -338,6 +341,8 @@ void Spawn2::DeathReset()
|
||||
//zero out our NPC since he is now gone
|
||||
npcthis = nullptr;
|
||||
|
||||
if(realdeath) { killcount++; }
|
||||
|
||||
//if we have a valid spawn id
|
||||
if(spawn2_id)
|
||||
{
|
||||
|
||||
@ -49,7 +49,7 @@ public:
|
||||
void Repop(uint32 delay = 0);
|
||||
void ForceDespawn();
|
||||
|
||||
void DeathReset(); //resets the spawn in the case the npc dies, also updates db if needed
|
||||
void DeathReset(bool realdeath = 0); //resets the spawn in the case the npc dies, also updates db if needed
|
||||
|
||||
void SpawnConditionChanged(const SpawnCondition &c, int16 old_value);
|
||||
uint32 GetID() { return spawn2_id; }
|
||||
@ -71,6 +71,7 @@ public:
|
||||
bool NPCPointerValid() { return (npcthis!=nullptr); }
|
||||
void SetNPCPointer(NPC* n) { npcthis = n; }
|
||||
void SetTimer(uint32 duration) { timer.Start(duration); }
|
||||
uint32 GetKillCount() { return killcount; }
|
||||
protected:
|
||||
friend class Zone;
|
||||
Timer timer;
|
||||
@ -92,6 +93,7 @@ private:
|
||||
bool enabled;
|
||||
EmuAppearance anim;
|
||||
bool IsDespawned;
|
||||
uint32 killcount;
|
||||
};
|
||||
|
||||
class SpawnCondition {
|
||||
|
||||
@ -48,6 +48,8 @@ int Mob::GetKickDamage() {
|
||||
int32 mindmg = 1;
|
||||
ApplySpecialAttackMod(KICK, dmg,mindmg);
|
||||
|
||||
dmg = mod_kick_damage(dmg);
|
||||
|
||||
return(dmg);
|
||||
}
|
||||
|
||||
@ -67,6 +69,8 @@ int Mob::GetBashDamage() {
|
||||
int32 mindmg = 1;
|
||||
ApplySpecialAttackMod(BASH, dmg, mindmg);
|
||||
|
||||
dmg = mod_bash_damage(dmg);
|
||||
|
||||
return(dmg);
|
||||
}
|
||||
|
||||
@ -281,6 +285,8 @@ void Client::OPCombatAbility(const EQApplicationPacket *app) {
|
||||
int32 min_dmg = 0;
|
||||
DoAnim(anim2HSlashing);
|
||||
|
||||
max_dmg = mod_frenzy_damage(max_dmg);
|
||||
|
||||
if (GetLevel() < 51)
|
||||
min_dmg = 1;
|
||||
else
|
||||
@ -491,6 +497,9 @@ int Mob::MonkSpecialAttack(Mob* other, uint8 unchecked_type)
|
||||
}
|
||||
}
|
||||
|
||||
//This can potentially stack with changes to kick damage
|
||||
ht = ndamage = mod_monk_special_damage(ndamage, skill_type);
|
||||
|
||||
DoSpecialAttackDamage(other, skill_type, ndamage, min_dmg, ht, reuse);
|
||||
|
||||
return(reuse);
|
||||
@ -663,6 +672,8 @@ void Mob::RogueBackstab(Mob* other, bool min_damage, int ReuseTime)
|
||||
ndamage = -5;
|
||||
}
|
||||
|
||||
ndamage = mod_backstab_damage(ndamage);
|
||||
|
||||
DoSpecialAttackDamage(other, BACKSTAB, ndamage, min_hit, hate, ReuseTime);
|
||||
DoAnim(animPiercing);
|
||||
}
|
||||
@ -879,22 +890,34 @@ void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Item
|
||||
|
||||
mlog(COMBAT__RANGED, "Bow DMG %d, Arrow DMG %d, Max Damage %d.", WDmg, ADmg, MaxDmg);
|
||||
|
||||
bool dobonus = false;
|
||||
if(GetClass() == RANGER && GetLevel() > 50)
|
||||
{
|
||||
int bonuschance = RuleI(Combat, ArcheryBonusChance);
|
||||
|
||||
bonuschance = mod_archery_bonus_chance(bonuschance, RangeWeapon);
|
||||
|
||||
if( !RuleB(Combat, UseArcheryBonusRoll) || (MakeRandomInt(1, 100) < bonuschance) )
|
||||
{
|
||||
if(RuleB(Combat, ArcheryBonusRequiresStationary))
|
||||
{
|
||||
if(other->IsNPC() && !other->IsMoving() && !other->IsRooted())
|
||||
{
|
||||
MaxDmg *= (float)2;
|
||||
hate *= (float)2;
|
||||
mlog(COMBAT__RANGED, "Ranger. Double damage success roll, doubling damage to %d", MaxDmg);
|
||||
Message_StringID(MT_CritMelee, BOW_DOUBLE_DAMAGE);
|
||||
dobonus = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dobonus = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(dobonus)
|
||||
{
|
||||
MaxDmg *= (float)2;
|
||||
hate *= (float)2;
|
||||
MaxDmg = mod_archery_bonus_damage(MaxDmg);
|
||||
|
||||
mlog(COMBAT__RANGED, "Ranger. Double damage success roll, doubling damage to %d", MaxDmg);
|
||||
Message_StringID(MT_CritMelee, BOW_DOUBLE_DAMAGE);
|
||||
}
|
||||
@ -925,6 +948,9 @@ void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Item
|
||||
ApplyMeleeDamageBonus(ARCHERY, TotalDmg);
|
||||
TotalDmg += other->GetAdditionalDamage(this, 0, true, ARCHERY);
|
||||
TotalDmg += (itembonuses.HeroicDEX / 10) + (TotalDmg * other->GetSkillDmgTaken(ARCHERY) / 100) + GetSkillDmgAmt(ARCHERY);
|
||||
|
||||
TotalDmg = mod_archery_damage(TotalDmg, dobonus);
|
||||
|
||||
TryCriticalHit(other, ARCHERY, TotalDmg);
|
||||
other->AddToHateList(this, hate, 0, false);
|
||||
}
|
||||
@ -941,6 +967,12 @@ void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Item
|
||||
{
|
||||
TryWeaponProc(RangeWeapon, other, 11);
|
||||
}
|
||||
|
||||
//Arrow procs because why not?
|
||||
if((Ammo != NULL) && GetTarget() && other && (other->GetHP() > -10))
|
||||
{
|
||||
TryWeaponProc(Ammo, other, 11);
|
||||
}
|
||||
}
|
||||
|
||||
void NPC::RangedAttack(Mob* other)
|
||||
@ -1088,6 +1120,8 @@ uint16 Mob::GetThrownDamage(int16 wDmg, int32& TotalDmg, int& minDmg)
|
||||
if(MaxDmg < minDmg)
|
||||
MaxDmg = minDmg;
|
||||
|
||||
MaxDmg = mod_throwing_damage(MaxDmg);
|
||||
|
||||
return MaxDmg;
|
||||
}
|
||||
|
||||
|
||||
@ -627,7 +627,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
|
||||
#endif
|
||||
// EverHood
|
||||
if(caster && GetPrimaryFaction()>0) {
|
||||
caster->AddFactionBonus(GetPrimaryFaction(),spell.base[0]);
|
||||
caster->AddFactionBonus(GetPrimaryFaction(),effect_value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -2809,6 +2809,8 @@ int Mob::CalcSpellEffectValue(uint16 spell_id, int effect_id, int caster_level,
|
||||
mlog(SPELLS__BARDS, "Effect value %d altered with bard modifier of %d to yeild %d", oval, mod, effect_value);
|
||||
}
|
||||
|
||||
effect_value = mod_effect_value(effect_value, spell_id, spells[spell_id].effectid[effect_id], caster);
|
||||
|
||||
return(effect_value);
|
||||
}
|
||||
|
||||
@ -2816,6 +2818,7 @@ int Mob::CalcSpellEffectValue(uint16 spell_id, int effect_id, int caster_level,
|
||||
int Mob::CalcSpellEffectValue_formula(int formula, int base, int max, int caster_level, uint16 spell_id, int ticsremaining)
|
||||
{
|
||||
/*
|
||||
neotokyo: i need those formulas checked!!!!
|
||||
|
||||
0 = base
|
||||
1 - 99 = base + level * formulaID
|
||||
@ -2844,9 +2847,6 @@ int Mob::CalcSpellEffectValue_formula(int formula, int base, int max, int caster
|
||||
int result = 0, updownsign = 1, ubase = base;
|
||||
if(ubase < 0)
|
||||
ubase = 0 - ubase;
|
||||
int level_diff = caster_level - GetMinLevel(spell_id);
|
||||
if (level_diff < 0)
|
||||
level_diff = 0;
|
||||
|
||||
// this updown thing might look messed up but if you look at the
|
||||
// spells it actually looks like some have a positive base and max where
|
||||
@ -2903,23 +2903,23 @@ snare has both of them negative, yet their range should work the same:
|
||||
result = ubase + (caster_level / 5); break;
|
||||
|
||||
case 111:
|
||||
result = updownsign * (ubase + 6 * level_diff); break;
|
||||
result = updownsign * (ubase + 6 * (caster_level - GetMinLevel(spell_id))); break;
|
||||
case 112:
|
||||
result = updownsign * (ubase + 8 * level_diff); break;
|
||||
result = updownsign * (ubase + 8 * (caster_level - GetMinLevel(spell_id))); break;
|
||||
case 113:
|
||||
result = updownsign * (ubase + 10 * level_diff); break;
|
||||
result = updownsign * (ubase + 10 * (caster_level - GetMinLevel(spell_id))); break;
|
||||
case 114:
|
||||
result = updownsign * (ubase + 15 * level_diff); break;
|
||||
result = updownsign * (ubase + 15 * (caster_level - GetMinLevel(spell_id))); break;
|
||||
|
||||
//these formula were updated according to lucy 10/16/04
|
||||
case 115: // this is only in symbol of transal
|
||||
result = ubase + 6 * level_diff; break;
|
||||
result = ubase + 6 * (caster_level - GetMinLevel(spell_id)); break;
|
||||
case 116: // this is only in symbol of ryltan
|
||||
result = ubase + 8 * level_diff; break;
|
||||
result = ubase + 8 * (caster_level - GetMinLevel(spell_id)); break;
|
||||
case 117: // this is only in symbol of pinzarn
|
||||
result = ubase + 12 * level_diff; break;
|
||||
result = ubase + 12 * (caster_level - GetMinLevel(spell_id)); break;
|
||||
case 118: // used in naltron and a few others
|
||||
result = ubase + 20 * level_diff; break;
|
||||
result = ubase + 20 * (caster_level - GetMinLevel(spell_id)); break;
|
||||
|
||||
case 119: // confirmed 2/6/04
|
||||
result = ubase + (caster_level / 8); break;
|
||||
|
||||
@ -440,6 +440,8 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot,
|
||||
return(true);
|
||||
}
|
||||
|
||||
cast_time = mod_cast_time(cast_time);
|
||||
|
||||
// ok we know it has a cast time so we can start the timer now
|
||||
spellend_timer.Start(cast_time);
|
||||
|
||||
@ -1099,11 +1101,53 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, uint16 slot,
|
||||
if(IsClient() && ((slot == USE_ITEM_SPELL_SLOT) || (slot == POTION_BELT_SPELL_SLOT))
|
||||
&& inventory_slot != 0xFFFFFFFF) // 10 is an item
|
||||
{
|
||||
bool fromaug = false;
|
||||
const ItemInst* inst = CastToClient()->GetInv()[inventory_slot];
|
||||
if (inst && inst->IsType(ItemClassCommon) && (inst->GetItem()->Click.Effect == spell_id) && inst->GetCharges())
|
||||
Item_Struct* augitem = 0;
|
||||
uint32 recastdelay = 0;
|
||||
uint32 recasttype = 0;
|
||||
|
||||
for(int r = 0; r < MAX_AUGMENT_SLOTS; r++) {
|
||||
const ItemInst* aug_i = inst->GetAugment(r);
|
||||
|
||||
if(!aug_i)
|
||||
continue;
|
||||
const Item_Struct* aug = aug_i->GetItem();
|
||||
if(!aug)
|
||||
continue;
|
||||
|
||||
if ( aug->Click.Effect == spell_id )
|
||||
{
|
||||
recastdelay = aug_i->GetItem()->RecastDelay;
|
||||
recasttype = aug_i->GetItem()->RecastType;
|
||||
fromaug = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//Test the aug recast delay
|
||||
if(IsClient() && fromaug && recastdelay > 0)
|
||||
{
|
||||
if(!CastToClient()->GetPTimers().Expired(&database, (pTimerItemStart + recasttype), false)) {
|
||||
Message_StringID(13, SPELL_RECAST);
|
||||
mlog(SPELLS__CASTING_ERR, "Casting of %d canceled: item spell reuse timer not expired", spell_id);
|
||||
InterruptSpell();
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Can we start the timer here? I don't see why not.
|
||||
CastToClient()->GetPTimers().Start((pTimerItemStart + recasttype), recastdelay);
|
||||
}
|
||||
}
|
||||
|
||||
if (inst && inst->IsType(ItemClassCommon) && (inst->GetItem()->Click.Effect == spell_id) && inst->GetCharges() || fromaug)
|
||||
{
|
||||
//const Item_Struct* item = inst->GetItem();
|
||||
int16 charges = inst->GetItem()->MaxCharges;
|
||||
|
||||
if(fromaug) { charges = -1; } //Don't destroy the parent item
|
||||
|
||||
if(charges > -1) { // charged item, expend a charge
|
||||
mlog(SPELLS__CASTING, "Spell %d: Consuming a charge from item %s (%d) which had %d/%d charges.", spell_id, inst->GetItem()->Name, inst->GetItem()->ID, inst->GetCharges(), inst->GetItem()->MaxCharges);
|
||||
DeleteChargeFromSlot = inventory_slot;
|
||||
@ -2328,6 +2372,8 @@ int Mob::CalcBuffDuration(Mob *caster, Mob *target, uint16 spell_id, int32 caste
|
||||
IsBlindSpell(spell_id))
|
||||
res += 1;
|
||||
|
||||
res = mod_buff_duration(res, caster, target, spell_id);
|
||||
|
||||
mlog(SPELLS__CASTING, "Spell %d: Casting level %d, formula %d, base_duration %d: result %d",
|
||||
spell_id, castlevel, formula, duration, res);
|
||||
|
||||
@ -2431,6 +2477,9 @@ int Mob::CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2,
|
||||
return -1;
|
||||
}
|
||||
|
||||
int modval = mod_spell_stack(spellid1, caster_level1, caster1, spellid2, caster_level2, caster2);
|
||||
if(modval < 2) { return(modval); }
|
||||
|
||||
//resurrection effects wont count for overwrite/block stacking
|
||||
switch(spellid1)
|
||||
{
|
||||
@ -4055,19 +4104,6 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use
|
||||
level_mod = 1000;
|
||||
}
|
||||
|
||||
//Add our level, resist and -spell resist modifier to our roll chance
|
||||
resist_chance += level_mod;
|
||||
resist_chance += resist_modifier;
|
||||
resist_chance += target_resist;
|
||||
|
||||
if (CharismaCheck)
|
||||
{
|
||||
//For charm chance to break checks, Default 10 CHA = -1 resist mod.
|
||||
int16 cha_resist_modifier = 0;
|
||||
cha_resist_modifier = caster->GetCHA()/RuleI(Spells, CharismaEffectiveness);
|
||||
resist_chance -= cha_resist_modifier;
|
||||
}
|
||||
|
||||
//Even more level stuff this time dealing with damage spells
|
||||
if(IsNPC() && IsDamageSpell(spell_id) && GetLevel() >= 17)
|
||||
{
|
||||
@ -4084,9 +4120,24 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use
|
||||
{
|
||||
level_diff = GetLevel() - caster->GetLevel();
|
||||
}
|
||||
resist_chance += (2 * level_diff);
|
||||
level_mod += (2 * level_diff);
|
||||
}
|
||||
|
||||
if (CharismaCheck)
|
||||
{
|
||||
//For charm chance to break checks, Default 10 CHA = -1 resist mod.
|
||||
int16 cha_resist_modifier = 0;
|
||||
cha_resist_modifier = caster->GetCHA()/RuleI(Spells, CharismaEffectiveness);
|
||||
resist_modifier -= cha_resist_modifier;
|
||||
}
|
||||
|
||||
//Add our level, resist and -spell resist modifier to our roll chance
|
||||
resist_chance += level_mod;
|
||||
resist_chance += resist_modifier;
|
||||
resist_chance += target_resist;
|
||||
|
||||
resist_chance = mod_spell_resist(resist_chance, level_mod, resist_modifier, target_resist, resist_type, spell_id, caster);
|
||||
|
||||
//Do our min and max resist checks.
|
||||
if(resist_chance > spells[spell_id].MaxResist && spells[spell_id].MaxResist != 0)
|
||||
{
|
||||
|
||||
@ -1158,7 +1158,7 @@ void TaskManager::SendTaskSelector(Client *c, Mob *mob, int TaskCount, int *Task
|
||||
if(Tasks[TaskList[i]] != nullptr) break;
|
||||
}
|
||||
|
||||
// FIXME: The 10 and 5 values in this calculation are to account for the string "nullptr" we are putting in 3 times.
|
||||
// FIXME: The 10 and 5 values in this calculation are to account for the string "ABCD" we are putting in 3 times.
|
||||
//
|
||||
// Calculate how big the packet needs to be pased on the number of tasks and the
|
||||
// size of the variable length strings.
|
||||
@ -1236,9 +1236,9 @@ void TaskManager::SendTaskSelector(Client *c, Mob *mob, int TaskCount, int *Task
|
||||
// FIXME: In live packets, these two strings appear to be the same as the Text1 and Text2
|
||||
// strings from the first activity in the task, however the task chooser/selector
|
||||
// does not appear to make use of them.
|
||||
sprintf(Ptr, "nullptr");
|
||||
sprintf(Ptr, "ABCD");
|
||||
Ptr = Ptr + strlen(Ptr) + 1;
|
||||
sprintf(Ptr, "nullptr");
|
||||
sprintf(Ptr, "ABCD");
|
||||
Ptr = Ptr + strlen(Ptr) + 1;
|
||||
|
||||
AvailableTaskTrailer = (AvailableTaskTrailer_Struct*)Ptr;
|
||||
@ -1253,7 +1253,7 @@ void TaskManager::SendTaskSelector(Client *c, Mob *mob, int TaskCount, int *Task
|
||||
|
||||
// In some packets, this next string looks like a short task summary, however it doesn't
|
||||
// appear anywhere in the client window.
|
||||
sprintf(Ptr, "nullptr");
|
||||
sprintf(Ptr, "ABCD");
|
||||
Ptr = Ptr + strlen(Ptr) + 1;
|
||||
}
|
||||
|
||||
|
||||
@ -1016,6 +1016,8 @@ bool Client::TradeskillExecute(DBTradeskillRecipe_Struct *spec) {
|
||||
}
|
||||
}
|
||||
|
||||
chance = mod_tradeskill_chance(chance, spec);
|
||||
|
||||
if (((spec->tradeskill==75) || GetGM() || (chance > res)) || MakeRandomInt(0, 99) < AAChance){
|
||||
success_modifier = 1;
|
||||
|
||||
@ -1108,6 +1110,8 @@ void Client::CheckIncreaseTradeskill(int16 bonusstat, int16 stat_modifier, float
|
||||
}
|
||||
}
|
||||
|
||||
chance_stage2 = mod_tradeskill_skillup(chance_stage2);
|
||||
|
||||
if (chance_stage2 > MakeRandomFloat(0, 99)) {
|
||||
//Only if stage1 and stage2 succeeded you get a skillup.
|
||||
SetSkill(tradeskill, current_raw_skill + 1);
|
||||
|
||||
@ -251,6 +251,9 @@ int32 Client::TributeItem(uint32 slot, uint32 quantity) {
|
||||
|
||||
//figure out what its worth
|
||||
int32 pts = inst->GetItem()->Favor;
|
||||
|
||||
pts = mod_tribute_item_value(pts);
|
||||
|
||||
if(pts < 1) {
|
||||
Message(13, "This item is worthless for favor.");
|
||||
return(0);
|
||||
|
||||
@ -917,7 +917,8 @@ Zone::Zone(uint32 in_zoneid, uint32 in_instanceid, const char* in_short_name)
|
||||
autoshutdown_timer((RuleI(Zone, AutoShutdownDelay))),
|
||||
clientauth_timer(AUTHENTICATION_TIMEOUT * 1000),
|
||||
spawn2_timer(1000),
|
||||
qglobal_purge_timer(30000)
|
||||
qglobal_purge_timer(30000),
|
||||
hotzone_timer(120000)
|
||||
{
|
||||
zoneid = in_zoneid;
|
||||
instanceid = in_instanceid;
|
||||
@ -1164,6 +1165,12 @@ bool Zone::Init(bool iStaticZone) {
|
||||
zone->zone_time.setEQTimeZone(database.GetZoneTZ(zoneid, GetInstanceVersion()));
|
||||
|
||||
LogFile->write(EQEMuLog::Status, "Init Finished: ZoneID = %d, Time Offset = %d", zoneid, zone->zone_time.getEQTimeZone());
|
||||
|
||||
LoadTickItems();
|
||||
|
||||
//MODDING HOOK FOR ZONE INIT
|
||||
mod_init();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1483,6 +1490,8 @@ bool Zone::Process() {
|
||||
}
|
||||
}
|
||||
|
||||
if(hotzone_timer.Check()) { UpdateHotzone(); }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1531,6 +1540,9 @@ void Zone::Repop(uint32 delay) {
|
||||
MZoneLock.unlock();
|
||||
|
||||
initgrids_timer.Start();
|
||||
|
||||
//MODDING HOOK FOR REPOP
|
||||
mod_repop();
|
||||
}
|
||||
|
||||
void Zone::GetTimeSync()
|
||||
@ -2657,3 +2669,87 @@ void Zone::ReloadWorld(uint32 Option){
|
||||
parse->ReloadQuests();
|
||||
}
|
||||
}
|
||||
|
||||
void Zone::LoadTickItems()
|
||||
{
|
||||
char errbuf[MYSQL_ERRMSG_SIZE];
|
||||
char* query = 0;
|
||||
MYSQL_RES *result;
|
||||
MYSQL_ROW row;
|
||||
#if _MSC_VER==1600
|
||||
item_tick_struct ti_tmp;
|
||||
#endif
|
||||
tick_items.clear();
|
||||
//tick_globals.clear();
|
||||
|
||||
if(database.RunQuery(query, MakeAnyLenString(&query, "SELECT it_itemid, it_chance, it_level, it_qglobal, it_bagslot FROM item_tick"), errbuf, &result))
|
||||
{
|
||||
while((row = mysql_fetch_row(result)))
|
||||
{
|
||||
if(atoi(row[0]) < 1)
|
||||
{
|
||||
//tick_globals[std::string(row[0])] = { 0, atoi(row[1]), atoi(row[2]), (int16)atoi(row[4]), std::string(row[3]) };
|
||||
}
|
||||
else
|
||||
#if _MSC_VER==1600
|
||||
{
|
||||
ti_tmp.itemid = atoi(row[0]);
|
||||
ti_tmp.chance = atoi(row[1]);
|
||||
ti_tmp.level = atoi(row[2]);
|
||||
ti_tmp.bagslot = (int16)atoi(row[4]);
|
||||
ti_tmp.qglobal = std::string(row[3]);
|
||||
tick_items[atoi(row[0])] = ti_tmp;
|
||||
}
|
||||
#else
|
||||
{
|
||||
tick_items[atoi(row[0])] = { atoi(row[0]), atoi(row[1]), atoi(row[2]), (int16)atoi(row[4]), std::string(row[3]) };
|
||||
}
|
||||
#endif
|
||||
}
|
||||
mysql_free_result(result);
|
||||
safe_delete_array(query);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogFile->write(EQEMuLog::Error, "Error in Zone::LoadTickItems: %s (%s)", query, errbuf);
|
||||
safe_delete_array(query);
|
||||
}
|
||||
}
|
||||
|
||||
uint32 Zone::GetSpawnKillCount(uint32 in_spawnid) {
|
||||
LinkedListIterator<Spawn2*> iterator(spawn2_list);
|
||||
|
||||
iterator.Reset();
|
||||
while(iterator.MoreElements())
|
||||
{
|
||||
if(iterator.GetData()->GetID() == in_spawnid)
|
||||
{
|
||||
return(iterator.GetData()->killcount);
|
||||
}
|
||||
iterator.Advance();
|
||||
}
|
||||
}
|
||||
|
||||
void Zone::UpdateHotzone()
|
||||
{
|
||||
char errbuf[MYSQL_ERRMSG_SIZE];
|
||||
char* query = 0;
|
||||
MYSQL_RES *result;
|
||||
MYSQL_ROW row;
|
||||
bool updh;
|
||||
|
||||
if(database.RunQuery(query, MakeAnyLenString(&query,"SELECT hotzone FROM zone WHERE short_name = '%s'", GetShortName()), errbuf, &result) )
|
||||
{
|
||||
if( (row = mysql_fetch_row(result)) )
|
||||
{
|
||||
updh = atoi(row[0]) == 0 ? false:true;
|
||||
//Hotzone status has changed
|
||||
if(is_hotzone != updh)
|
||||
{
|
||||
is_hotzone = updh;
|
||||
}
|
||||
}
|
||||
mysql_free_result(result);
|
||||
}
|
||||
safe_delete_array(query);
|
||||
}
|
||||
|
||||
19
zone/zone.h
19
zone/zone.h
@ -33,6 +33,7 @@
|
||||
#include "tasks.h"
|
||||
#include "pathing.h"
|
||||
#include "QGlobals.h"
|
||||
#include <unordered_map>
|
||||
|
||||
class Map;
|
||||
class WaterMap;
|
||||
@ -69,6 +70,14 @@ struct ZoneEXPModInfo {
|
||||
float AAExpMod;
|
||||
};
|
||||
|
||||
struct item_tick_struct {
|
||||
uint32 itemid;
|
||||
uint32 chance;
|
||||
uint32 level;
|
||||
int16 bagslot;
|
||||
std::string qglobal;
|
||||
};
|
||||
|
||||
extern EntityList entity_list;
|
||||
class database;
|
||||
class PathManager;
|
||||
@ -253,6 +262,15 @@ public:
|
||||
|
||||
LinkedList<NPC_Emote_Struct*> NPCEmoteList;
|
||||
|
||||
void LoadTickItems();
|
||||
uint32 GetSpawnKillCount(uint32 in_spawnid);
|
||||
void UpdateHotzone();
|
||||
unordered_map<int, item_tick_struct> tick_items;
|
||||
|
||||
//MODDING HOOKS
|
||||
void mod_init();
|
||||
void mod_repop();
|
||||
|
||||
private:
|
||||
uint32 zoneid;
|
||||
uint32 instanceid;
|
||||
@ -305,6 +323,7 @@ private:
|
||||
LinkedList<ZoneClientAuth_Struct*> client_auth_list;
|
||||
QGlobalCache *qGlobals;
|
||||
|
||||
Timer hotzone_timer;
|
||||
Mutex MZoneLock;
|
||||
};
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user