Dungeon Crawl custom code merge

Added numerous modding hooks.
Added rules:
	Character:KeepLevelOverMax - Don't delevel a character if they are found to be over max level rule.
	Spells:UseCHAScribeHack - Optionally omit spells with CHA in effect12 when using scribespells and traindiscs
	Combat:MonkACBonusWeight - Adjust the weight threshold for monk AC bonus
	Combat:ClientStunLevel - Adjust the level clients kicks and bashes start to roll for stuns
	Combat;QuiverWRHasteDiv - Adjust the divisor applied to weight reduction for haste calcs
	Combat:UseArcheryBonusRoll - Make archery stationary bonus a roll
	Combat:ArcheryBonusChance - Archery stationary bonus chance

Added account flags and associated perl wrappers
Added EVENT_ITEM_TICK for interactive items
Added EVENT_DUEL_WIN and EVENT_DUEL_LOSE, which exports $enemyname and $enemyid
Added timer and interval to console worldshutdown command
Added EQW interface for worldshutdown and server-wide messages
This commit is contained in:
Tabasco 2013-04-24 15:58:51 -05:00
parent b15cb08f54
commit 56490400ca
45 changed files with 1321 additions and 156 deletions

View File

@ -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 )
@ -291,7 +291,8 @@ RULE_INT ( Spells, MaxCastTimeReduction, 50) //Max percent your spell cast time
RULE_INT ( Spells, RootBreakFromSpells, 20) //Chance for root to break when cast on.
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, 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 )

View File

@ -1102,3 +1102,8 @@ bool IsShortDurationBuff(uint16 spell_id)
return false;
}
//DCBOOKMARK
const char* GetSpellName(int16 spell_id)
{
return( spells[spell_id].name );
}

View File

@ -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

View 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;

View 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;

View File

@ -459,6 +459,14 @@ void EQW::ResolveBug(const char *id) {
safe_delete_array(query);
}
//DCBOOKMARK
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

View File

@ -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:

View File

@ -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();

View File

@ -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;
}

View File

@ -716,14 +716,30 @@ void ZSList::GetZoneIDList(vector<uint32> &zones) {
}
//DCBOOKMARK
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);
}
}

View File

@ -56,7 +56,8 @@ public:
int GetZoneCount();
void GetZoneIDList(std::vector<uint32> &zones);
void WorldShutDown(uint32 time, uint32 interval);
protected:
uint32 NextID;
LinkedList<ZoneServer*> list;

View File

@ -84,6 +84,7 @@ SET(zone_sources
zonedb.cpp
zonedbasync.cpp
zoning.cpp
mod_functions.cpp
)
SET(zone_headers

View File

@ -318,6 +318,9 @@ bool Mob::CheckHitChance(Mob* other, SkillType skillinuse, int Hand, int16 chanc
if(skillinuse == ARCHERY)
chancetohit -= (chancetohit * RuleR(Combat, ArcheryHitPenalty)) / 100.0f;
//DCBOOKMARK
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 +399,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 + (GetDEX()/200);
bonus *= riposte_chance;
//DCBOOKMARK
bonus = mod_riposte_chance(bonus, attacker);
RollTable[0] = bonus + (itembonuses.HeroicDEX / 25); // 25 heroic = 1%, applies to ripo, parry, block
}
}
@ -434,6 +439,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/35.0 + (GetDEX()/200);
//DCBOOKMARK
bonus = mod_block_chance(bonus, attacker);
RollTable[1] = RollTable[0] + (bonus * block_chance);
}
}
@ -485,6 +492,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 + (GetDEX()/200);
bonus *= parry_chance;
//DCBOOKMARK
bonus = mod_parry_chance(bonus, attacker);
RollTable[2] = RollTable[1] + bonus;
}
}
@ -507,6 +516,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 +566,11 @@ void Mob::MeleeMitigation(Mob *attacker, int32 &damage, int32 minhit)
int shield_ac = 0;
int armor = 0;
float weight = 0.0;
//DCBOOKMARK
float monkweight = RuleI(Combat, MonkACBonusWeight);
monkweight = mod_monk_weight(monkweight, attacker);
if(IsClient())
{
armor = CastToClient()->GetRawACNoShield(shield_ac);
@ -576,7 +592,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 +619,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 +653,9 @@ void Mob::MeleeMitigation(Mob *attacker, int32 &damage, int32 minhit)
}
mitigation_rating *= 0.847;
//DCBOOKMARK
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 +665,9 @@ void Mob::MeleeMitigation(Mob *attacker, int32 &damage, int32 minhit)
attack_rating = (attacker->GetATK() + (attacker->GetSkill(OFFENSE)*1.345) + ((attacker->GetSTR()-66) * 0.9));
}
//DCBOOKMARK
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 +691,9 @@ void Mob::MeleeMitigation(Mob *attacker, int32 &damage, int32 minhit)
if(thac20 > thac20cap)
{
thac20 = thac20cap;
}
d += 10 * (m_diff / thac20);
}
@ -1221,6 +1246,9 @@ bool Client::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, b
else
damage = MakeRandomInt(min_hit, max_hit);
//DCBOOKMARK
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 +1486,10 @@ void Client::Death(Mob* killerMob, int32 damage, uint16 spell, SkillType attack_
{
if (killerMob->IsNPC()) {
parse->EventNPC(EVENT_SLAY, killerMob->CastToNPC(), this, "", 0);
//DCBOOKMARK
mod_client_death_npc(killerMob);
uint16 emoteid = killerMob->GetEmoteID();
if(emoteid != 0)
killerMob->CastToNPC()->DoNPCEmote(KILLEDPC,emoteid);
@ -1473,6 +1505,10 @@ 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);
//DCBOOKMARK
mod_client_death_duel(killerMob);
} else {
//otherwise, we just died, end the duel.
Mob* who = entity_list.GetMob(GetDuelTarget());
@ -1844,6 +1880,9 @@ bool NPC::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool
damage = (max_dmg+eleBane);
}
//DCBOOKMARK
damage = mod_npc_damage(damage, skillinuse, Hand, &weapon_inst, other);
int32 hate = damage;
if(IsPet())
{
@ -2042,7 +2081,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) {
@ -2097,6 +2136,10 @@ 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 != NULL) { // If Group Member is Client
parse->EventNPC(EVENT_KILLED_MERIT, this, kr->members[i].member, "killed", 0);
//DCBOOKMARK
mod_npc_killed_merit(kr->members[i].member);
if(RuleB(TaskSystem, EnableTaskSystem))
kr->members[i].member->UpdateTasksOnKill(GetNPCTypeID());
PlayerCount++;
@ -2137,6 +2180,10 @@ void NPC::Death(Mob* killerMob, int32 damage, uint16 spell, SkillType attack_ski
if (kg->members[i] != NULL && kg->members[i]->IsClient()) { // If Group Member is Client
Client *c = kg->members[i]->CastToClient();
parse->EventNPC(EVENT_KILLED_MERIT, this, c, "killed", 0);
//DCBOOKMARK
mod_npc_killed_merit(c);
if(RuleB(TaskSystem, EnableTaskSystem))
c->UpdateTasksOnKill(GetNPCTypeID());
@ -2181,6 +2228,10 @@ 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);
//DCBOOKMARK
mod_npc_killed_merit(give_exp_client);
if(RuleB(TaskSystem, EnableTaskSystem))
give_exp_client->UpdateTasksOnKill(GetNPCTypeID());
@ -2311,6 +2362,10 @@ 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);
//DCBOOKMARK
mod_npc_killed(oos);
uint16 emoteid = this->GetEmoteID();
if(emoteid != 0)
this->DoNPCEmote(ONDEATH,emoteid);
@ -3402,8 +3457,8 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons
if (damage > 0 && ((skill_used == BASH || skill_used == KICK) && attacker))
{
// 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))
// Clients can stun mobs under level 56 with their bash/kick when they get level 55 or greater. -- DCBOOKMARK for rules
if( attacker->IsNPC() || (attacker->IsClient() && attacker->GetLevel() >= RuleI(Combat, ClientStunLevel) && GetLevel() < RuleI(Spells, BaseImmunityLevel)) )
{
if (MakeRandomInt(0,99) < (RuleI(Character, NPCBashKickStunChance)) || attacker->IsClient())
{

View File

@ -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,8 @@ Client::Client(EQStreamInterface* ieqs)
}
MaxXTargets = 5;
XTargetAutoAddHaters = true;
//DCBOOKMARK
LoadAccountFlags();
}
Client::~Client() {
@ -842,6 +845,9 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s
safe_delete(pack);
}
//DCBOOKMARK - 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));
@ -2334,10 +2340,11 @@ bool Client::CheckIncreaseSkill(SkillType skillid, Mob *against_who, int chancem
if(against_who)
{
if(against_who->SpecAttacks[IMMUNE_AGGRO] || against_who->IsClient() ||
GetLevelCon(against_who->GetLevel()) == CON_GREEN)
if( against_who->SpecAttacks[IMMUNE_AGGRO] || against_who->IsClient() ||
GetLevelCon(against_who->GetLevel()) == CON_GREEN )
{
return false;
//DCBOOKMARK - false by default
return mod_can_increase_skill(skillid, against_who);
}
}
@ -2349,6 +2356,10 @@ 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);
//DCBOOKMARK
Chance = mod_increase_skill_chance(Chance, against_who);
if(MakeRandomFloat(0, 99) < Chance)
{
SetSkill(skillid, GetRawSkill(skillid) + 1);
@ -2717,6 +2728,9 @@ bool Client::BindWound(Mob* bindmob, bool start, bool fail){
max_percent = 70 + 10 * maxHPBonus;
}
//DCBOOKMARK
max_percent = mod_bindwound_percent(max_percent, bindmob);
int max_hp = bindmob->GetMaxHP()*max_percent/100;
// send bindmob new hp's
@ -2735,7 +2749,10 @@ bool Client::BindWound(Mob* bindmob, bool start, bool fail){
int bindBonus = spellbonuses.BindWound + itembonuses.BindWound + aabonuses.BindWound;
bindhps += bindhps*bindBonus / 100;
//DCBOOKMARK
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.
@ -7606,3 +7623,109 @@ some day.
}
return 0;
}
//DCBOOKMARK
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;
//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);
}
}
}
}

View File

@ -1133,6 +1133,36 @@ public:
void DuplicateLoreMessage(uint32 ItemID);
void GarbleMessage(char *, uint8);
//DCBOOKMARK
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);
@ -1434,8 +1464,11 @@ private:
uint8 MaxXTargets;
bool XTargetAutoAddHaters;
struct XTarget_Struct XTargets[XTARGET_HARDCAP];
struct XTarget_Struct XTargets[XTARGET_HARDCAP];
//DCBOOKMARK
Timer ItemTickTimer;
std::map<std::string,std::string> accountflags;
};
#include "parser.h"

View File

@ -1415,6 +1415,9 @@ int Client::CalcHaste() {
h += spellbonuses.hastetype3;
h += ExtraHaste; //GM granted haste.
//DCBOOKMARK
h = mod_client_haste(h);
if (spellbonuses.inhibitmelee){
if (h >= 0)
h -= spellbonuses.inhibitmelee;

View File

@ -2076,6 +2076,30 @@ void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app)
}
}
//DCBOOKMARK
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 +2147,39 @@ void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app)
return;
}
}
else if (tryaug) //DCBOOKMARK
{
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 +2662,9 @@ void Client::Handle_OP_Consider(const EQApplicationPacket *app)
con->faction = FACTION_DUBIOUS;
}
//DCBOOKMARK
mod_consider(tmob, con);
QueuePacket(outapp);
safe_delete(outapp);
return;
@ -3200,6 +3260,9 @@ void Client::Handle_OP_ItemLinkClick(const EQApplicationPacket *app)
if((response).size() > 0)
{
//DCBOOKMARK
if( !mod_saylink(response, silentsaylink) ) { return; }
if(this->GetTarget() && this->GetTarget()->IsNPC())
{
if(silentsaylink)
@ -7714,7 +7777,12 @@ void Client::Handle_OP_EnvDamage(const EQApplicationPacket *app)
SetHP(GetHP() - damage);
if(GetHP() <= 0)
{
//DCBOOKMARK
mod_client_death_env();
Death(0, 32000, SPELL_UNKNOWN, HAND_TO_HAND);
}
SendHPUpdate();
return;
}

View File

@ -645,6 +645,12 @@ bool Client::Process() {
--m_pp.intoxication;
CalcBonuses();
}
//DCBOOKMARK
if(ItemTickTimer.Check())
{
TickItemCheck();
}
}
}

View File

@ -93,7 +93,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"
};
extern Zone* zone;
@ -739,6 +742,13 @@ void PerlembParser::EventCommon(QuestEventID event, uint32 objid, const char * d
ExportVar(packagename.c_str(), "itemname", iteminst->GetItem()->Name);
break;
}
case EVENT_ITEM_TICK:
{
ExportVar(packagename.c_str(), "itemid", objid);
ExportVar(packagename.c_str(), "itemname", iteminst->GetItem()->Name);
ExportVar(packagename.c_str(), "invslot", extradata);
break;
}
case EVENT_ITEM_CLICK_CAST:
case EVENT_ITEM_CLICK: {
ExportVar(packagename.c_str(), "itemid", objid);
@ -796,6 +806,14 @@ void PerlembParser::EventCommon(QuestEventID event, uint32 objid, const char * d
break;
}
case EVENT_DUEL_WIN:
case EVENT_DUEL_LOSE:
{
ExportVar(packagename.c_str(), "enemyname", data);
ExportVar(packagename.c_str(), "enemyid", extradata);
break;
}
//nothing special about these events
case EVENT_DEATH:
case EVENT_SPAWN:

View File

@ -1888,6 +1888,13 @@ void EntityList::QueueClientsStatus(Mob* sender, const EQApplicationPacket* app,
void EntityList::DuelMessage(Mob* winner, Mob* loser, bool flee) {
LinkedListIterator<Client*> iterator(client_list);
//DCBOOKMARK
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();

View File

@ -58,7 +58,10 @@ typedef enum {
EVENT_DISCOVER_ITEM,
EVENT_DISCONNECT,
EVENT_CONNECT,
EVENT_ITEM_TICK,
EVENT_DUEL_WIN,
EVENT_DUEL_LOSE,
_LargestEventID
} QuestEventID;

View File

@ -289,7 +289,16 @@ void Client::SetEXP(uint32 set_exp, uint32 set_aaxp, bool isrezzexp) {
if(check_level > maxlevel) {
check_level = maxlevel;
set_exp = GetEXPForLevel(maxlevel);
//DCBOOKMARK
if(RuleB(Character, KeepLevelOverMax))
{
set_exp = GetEXPForLevel(GetLevel()+1);
}
else
{
set_exp = GetEXPForLevel(maxlevel);
}
}
if(RuleB(Character, PerCharacterQglobalMaxLevel)){
@ -665,4 +674,4 @@ uint32 Client::GetCharMaxLevelFromQGlobal() {
}
return false; // Default is false
}
}

View File

@ -376,3 +376,9 @@ void NPC::AddLootTable() {
}
}
//DCBOOKMARK
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);
}
}

View File

@ -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;
@ -4675,3 +4675,21 @@ FACTION_VALUE Mob::GetSpecialFactionCon(Mob* iOther) {
}
}
//DCBOOKMARK
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);
}

View File

@ -789,6 +789,31 @@ public:
inline void SetEmoteID(uint16 emote) { emoteid = emote; }
inline uint16 GetEmoteID() { return emoteid; }
//DCBOOKMARK
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
View 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); }

View 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); }

View File

@ -1895,7 +1895,8 @@ void NPC::ModifyNPCStat(const char *identifier, const char *newValue)
if(id == "special_attacks")
{
NPCSpecialAttacks(val.c_str(), 0);
//DCBOOKMARK - Added reset flag.
NPCSpecialAttacks(val.c_str(), 0, 1);
return;
}
@ -2414,3 +2415,74 @@ FACTION_VALUE NPC::CheckNPCFactionAlly(int32 other_faction) {
bool NPC::IsFactionListAlly(uint32 other_faction) {
return(CheckNPCFactionAlly(other_faction) == FACTION_ALLY);
}
//DCBOOKMARK
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 = ceil( ((lv - (lv / 10)) - 1) );
basehp = (lv * 10) + (lv * lv);
}
else
{
minx = ceil( ((lv - (lv / 10)) - 1) - (( abs(45 - lv) ) / 2) );
basehp = (lv * 10) + ((lv * lv) * 4);
}
if(hp > basehp)
{
hpcontrib = (int)( (float)((float)hp / (float)basehp) * 1.5);
if(hpcontrib > 5) { hpcontrib = 5; }
if(maxdmg > basedmg)
{
dmgcontrib = 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 += (int)floor(lv/15); }
}
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);
}

View File

@ -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,14 @@ public:
void PrintOutQuestItems(Client* c);
//DCBOOKMARK
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;

View File

@ -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;
}

View File

@ -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;
}
@ -8352,4 +8372,4 @@ XS(boot_Mob)
XSRETURN_YES;
}
#endif //EMBPERL_XS_CLASSES
#endif //EMBPERL_XS_CLASSES

View File

@ -143,7 +143,17 @@ XS(XS_NPC_AddLootTable)
if(THIS == NULL)
Perl_croak(aTHX_ "THIS is NULL, avoiding crash.");
THIS->AddLootTable();
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_GetSpawnKillCount, file, "$");
XSRETURN_YES;
}

View File

@ -3500,6 +3500,38 @@ XS(XS__crosszonemessageplayerbyname)
XSRETURN_EMPTY;
}
XS(XS__GetSpellName);
XS(XS__GetSpellName)
{
dXSARGS;
if (items != 1)
Perl_croak(aTHX_ "Usage: GetSpellName(spell_id)");
int32 spell_id = (int32)SvUV(ST(0));
dXSTARG;
Const_char * RETVAL = GetSpellName(spell_id);
sv_setpv(TARG, RETVAL); XSprePUSH; PUSHTARG;
XSRETURN(1);
}
XS(XS__GetMana);
XS(XS__GetMana)
{
dXSARGS;
if (items != 1)
Perl_croak(aTHX_ "Usage: GetMana(spell_id)");
uint32 spell_id = (uint32)SvIV(ST(0));
uint16 manacost = 0;
dXSTARG;
manacost = quest_manager.GetMana(spell_id);
XSRETURN_UV(manacost);
}
/*
This is the callback perl will look for to setup the
quest package's XSUBs
@ -3722,6 +3754,8 @@ EXTERN_C XS(boot_quest)
newXS(strcpy(buf, "crosszonesignalclientbycharid"), XS__crosszonesignalclientbycharid, file);
newXS(strcpy(buf, "crosszonesignalclientbyname"), XS__crosszonesignalclientbyname, file);
newXS(strcpy(buf, "crosszonemessageplayerbyname"), XS__crosszonemessageplayerbyname, file);
newXS(strcpy(buf, "GetSpellName"), XS__GetSpellName, file);
newXS(strcpy(buf, "GetMana"), XS__GetMana, file);
XSRETURN_YES;
}

View File

@ -232,8 +232,11 @@ 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);
//DCBOOKMARK
act_power = CastToClient()->mod_pet_power(act_power, spell_id);
}
}
else if (petpower > 0)
act_power = petpower;

View File

@ -830,10 +830,11 @@ uint16 QuestManager::scribespells(uint8 max_level, uint8 min_level) {
{
if
(
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].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 &&
( RuleB(Spells, UseCHAScribeHack) && spells[curspell].effectid[EFFECT_COUNT - 1] != 10 ) //DCBOOKMARK
)
{
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 ) //DCBOOKMARK
)
{
if(IsDiscipline(curspell)){

View File

@ -257,6 +257,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 NULL when functions are called.
Client *initiator; //this can be null.

View File

@ -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,10 @@ bool Spawn2::Process() {
currentnpcid = npcid;
NPC* npc = new NPC(tmp, this, x, y, z, heading, FlyMode3);
//DCBOOKMARK
npc->mod_prespawn(this);
npcthis = npc;
npc->AddLootTable();
npc->SetSp2(spawngroup_id_);
@ -328,7 +332,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 +342,9 @@ void Spawn2::DeathReset()
//zero out our NPC since he is now gone
npcthis = NULL;
//DCBOOKMARK
if(realdeath) { killcount++; }
//if we have a valid spawn id
if(spawn2_id)
{

View File

@ -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,8 @@ public:
bool NPCPointerValid() { return (npcthis!=NULL); }
void SetNPCPointer(NPC* n) { npcthis = n; }
void SetTimer(uint32 duration) { timer.Start(duration); }
//DCBOOKMARK
uint32 GetKillCount() { return killcount; }
protected:
friend class Zone;
Timer timer;
@ -92,6 +94,8 @@ private:
bool enabled;
EmuAppearance anim;
bool IsDespawned;
//DCBOOKMARK
uint32 killcount;
};
class SpawnCondition {

View File

@ -48,6 +48,9 @@ int Mob::GetKickDamage() {
int32 mindmg = 1;
ApplySpecialAttackMod(KICK, dmg,mindmg);
//DCBOOKMARK
dmg = mod_kick_damage(dmg);
return(dmg);
}
@ -67,6 +70,9 @@ int Mob::GetBashDamage() {
int32 mindmg = 1;
ApplySpecialAttackMod(BASH, dmg, mindmg);
//DCBOOKMARK
dmg = mod_bash_damage(dmg);
return(dmg);
}
@ -281,6 +287,9 @@ void Client::OPCombatAbility(const EQApplicationPacket *app) {
int32 min_dmg = 0;
DoAnim(anim2HSlashing);
//DCBOOKMARK
max_dmg = mod_frenzy_damage(max_dmg);
if (GetLevel() < 51)
min_dmg = 1;
else
@ -490,7 +499,10 @@ int Mob::MonkSpecialAttack(Mob* other, uint8 unchecked_type)
ht = max_dmg;
}
}
//DCBOOKMARK -- 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 +675,9 @@ void Mob::RogueBackstab(Mob* other, bool min_damage, int ReuseTime)
ndamage = -5;
}
//DCBOOKMARK
ndamage = mod_backstab_damage(ndamage);
DoSpecialAttackDamage(other, BACKSTAB, ndamage, min_hit, hate, ReuseTime);
DoAnim(animPiercing);
}
@ -879,22 +894,35 @@ 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)
{
if(RuleB(Combat, ArcheryBonusRequiresStationary))
//DCBOOKMARK
int bonuschance = RuleI(Combat, ArcheryBonusChance);
bonuschance = mod_archery_bonus_chance(bonuschance, RangeWeapon);
if( !RuleB(Combat, UseArcheryBonusRoll) || (MakeRandomInt(1, 100) < bonuschance) )
{
if(other->IsNPC() && !other->IsMoving() && !other->IsRooted())
if(RuleB(Combat, ArcheryBonusRequiresStationary))
{
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);
if(other->IsNPC() && !other->IsMoving() && !other->IsRooted())
{
dobonus = true;
}
}
else
{
dobonus = true;
}
}
else
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 +953,10 @@ 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);
//DCBOOKMARK
TotalDmg = mod_archery_damage(TotalDmg, dobonus);
TryCriticalHit(other, ARCHERY, TotalDmg);
other->AddToHateList(this, hate, 0, false);
}
@ -941,6 +973,12 @@ void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Item
{
TryWeaponProc(RangeWeapon, other, 11);
}
//DCBOOKMARK - Arrow procs because why not?
if((Ammo != NULL) && GetTarget() && other && (other->GetHP() > -10))
{
TryWeaponProc(Ammo, other, 11);
}
}
void NPC::RangedAttack(Mob* other)
@ -1088,6 +1126,9 @@ uint16 Mob::GetThrownDamage(int16 wDmg, int32& TotalDmg, int& minDmg)
if(MaxDmg < minDmg)
MaxDmg = minDmg;
//DCBOOKMARK
MaxDmg = mod_throwing_damage(MaxDmg);
return MaxDmg;
}

View File

@ -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;
}
@ -1178,7 +1178,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
{
Gate();
}
// shadow step is handled by client already, nothing required
// solar: shadow step is handled by client already, nothing required
break;
}
@ -1189,7 +1189,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
#endif
if (spells[spell_id].base[i] == 1)
BuffFadeByEffect(SE_Blind);
// handled by client
// solar: handled by client
// TODO: blind flag?
break;
}
@ -1369,7 +1369,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
#ifdef SPELL_EFFECT_SPAM
snprintf(effect_desc, _EDLEN, "Spin: %d", effect_value);
#endif
// the spinning is handled by the client
// solar: the spinning is handled by the client
int max_level = spells[spell_id].max[i];
if(max_level == 0)
max_level = RuleI(Spells, BaseImmunityLevel); // Default max is 55 level limit
@ -1383,7 +1383,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
}
else
{
// the spinning is handled by the client
// solar: the spinning is handled by the client
// Stun duration is based on the effect_value, not the buff duration(alot don't have buffs)
Stun(effect_value);
if(!IsClient()) {
@ -2004,7 +2004,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
#ifdef SPELL_EFFECT_SPAM
snprintf(effect_desc, _EDLEN, "Call Pet");
#endif
// this is cast on self, not on the pet
// solar: this is cast on self, not on the pet
if(GetPet() && GetPet()->IsNPC())
{
GetPet()->CastToNPC()->GMMove(GetX(), GetY(), GetZ(), GetHeading());
@ -2015,7 +2015,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
case SE_StackingCommand_Block:
case SE_StackingCommand_Overwrite:
{
// these are special effects used by the buff stuff
// solar: these are special effects used by the buff stuff
break;
}
@ -2100,7 +2100,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
#ifdef SPELL_EFFECT_SPAM
snprintf(effect_desc, _EDLEN, "Skill Attack");
#endif
/*
/*Kayen:
Weapon Damage = spells[spell_id].base[i]
Chance to Hit Bonus = spells[spell_id].base2[i]
???? = spells[spell_id].max[i] - MOST of the effects have this value.
@ -2809,13 +2809,17 @@ 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);
}
//DCBOOKMARK
effect_value = mod_effect_value(effect_value, spell_id, spells[spell_id].effectid[effect_id], caster);
return(effect_value);
}
// generic formula calculations
// solar: generic formula calculations
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,11 +2848,8 @@ 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
// solar: 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
// the max is actually less than the base, hence they grow downward
/*
@ -2877,17 +2878,17 @@ snare has both of them negative, yet their range should work the same:
case 70:
result = ubase/100; break;
case 0:
case 100: // confirmed 2/6/04
case 100: // solar: confirmed 2/6/04
result = ubase; break;
case 101: // confirmed 2/6/04
case 101: // solar: confirmed 2/6/04
result = updownsign * (ubase + (caster_level / 2)); break;
case 102: // confirmed 2/6/04
case 102: // solar: confirmed 2/6/04
result = updownsign * (ubase + caster_level); break;
case 103: // confirmed 2/6/04
case 103: // solar: confirmed 2/6/04
result = updownsign * (ubase + (caster_level * 2)); break;
case 104: // confirmed 2/6/04
case 104: // solar: confirmed 2/6/04
result = updownsign * (ubase + (caster_level * 3)); break;
case 105: // confirmed 2/6/04
case 105: // solar: confirmed 2/6/04
result = updownsign * (ubase + (caster_level * 4)); break;
case 107:
@ -2895,35 +2896,35 @@ snare has both of them negative, yet their range should work the same:
result = updownsign * (ubase + (caster_level / 2)); break;
case 108:
result = updownsign * (ubase + (caster_level / 3)); break;
case 109: // confirmed 2/6/04
case 109: // solar: confirmed 2/6/04
result = updownsign * (ubase + (caster_level / 4)); break;
case 110: // confirmed 2/6/04
case 110: // solar: confirmed 2/6/04
//is there a reason we dont use updownsign here???
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;
case 116: // this is only in symbol of ryltan
result = ubase + 8 * level_diff; break;
case 117: // this is only in symbol of pinzarn
result = ubase + 12 * level_diff; break;
case 118: // used in naltron and a few others
result = ubase + 20 * level_diff; break;
case 115: // solar: this is only in symbol of transal
result = ubase + 6 * (caster_level - GetMinLevel(spell_id)); break;
case 116: // solar: this is only in symbol of ryltan
result = ubase + 8 * (caster_level - GetMinLevel(spell_id)); break;
case 117: // solar: this is only in symbol of pinzarn
result = ubase + 12 * (caster_level - GetMinLevel(spell_id)); break;
case 118: // solar: used in naltron and a few others
result = ubase + 20 * (caster_level - GetMinLevel(spell_id)); break;
case 119: // confirmed 2/6/04
case 119: // solar: confirmed 2/6/04
result = ubase + (caster_level / 8); break;
case 121: // corrected 2/6/04
case 121: // solar: corrected 2/6/04
result = ubase + (caster_level / 3); break;
case 122:
{
@ -2935,7 +2936,7 @@ snare has both of them negative, yet their range should work the same:
result = updownsign * (ubase - (12 * ticdif));
break;
}
case 123: // added 2/6/04
case 123: // solar: added 2/6/04
result = MakeRandomInt(ubase, abs(max));
break;
@ -3306,7 +3307,7 @@ void Mob::DoBuffTic(uint16 spell_id, uint32 ticsremaining, uint8 caster_level, M
}
}
// removes the buff in the buff slot 'slot'
// solar: removes the buff in the buff slot 'slot'
void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses)
{
if(slot < 0 || slot > GetMaxTotalSlots())
@ -5319,7 +5320,7 @@ int32 Mob::GetAdditionalDamage(Mob *caster, uint32 spell_id, bool use_skill, uin
int32 Mob::ApplySpellEffectiveness(Mob* caster, int16 spell_id, int32 value, bool IsBard) {
//9-17-12: This is likely causing crashes, disabled till can resolve.
//Kayen 9-17-12: This is likely causing crashes, disabled till can resolve.
if (IsBard)
return value;

View File

@ -440,6 +440,9 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot,
return(true);
}
//DCBOOKMARK
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 +1102,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
{
const ItemInst* inst = CastToClient()->GetInv()[inventory_slot];
if (inst && inst->IsType(ItemClassCommon) && (inst->GetItem()->Click.Effect == spell_id) && inst->GetCharges())
bool fromaug = false;
const ItemInst* inst = CastToClient()->GetInv()[inventory_slot];
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 +2373,9 @@ int Mob::CalcBuffDuration(Mob *caster, Mob *target, uint16 spell_id, int32 caste
IsBlindSpell(spell_id))
res += 1;
//DCBOOKMARK
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 +2479,10 @@ int Mob::CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2,
return -1;
}
//DCBOOKMARK
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)
{
@ -4019,40 +4071,67 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use
resist_chance = 0;
}
//Adjust our resist chance based on level modifiers
int temp_level_diff = GetLevel() - caster->GetLevel();
if(IsNPC() && GetLevel() >= RuleI(Casting, ResistFalloff))
{
int a = (RuleI(Casting, ResistFalloff) - 1) - caster->GetLevel();
if(a > 0)
{
temp_level_diff = a;
}
else
{
temp_level_diff = 0;
}
}
//Adjust our resist chance based on level modifiers
int temp_level_diff = GetLevel() - caster->GetLevel();
if(IsNPC() && GetLevel() >= RuleI(Casting,ResistFalloff))
{
int a = (RuleI(Casting,ResistFalloff)-1) - caster->GetLevel();
if(a > 0)
{
temp_level_diff = a;
}
else
{
temp_level_diff = 0;
}
}
if(IsClient() && GetLevel() >= 21 && temp_level_diff > 15)
{
temp_level_diff = 15;
}
if(IsClient() && GetLevel() >= 21 && temp_level_diff > 15)
{
temp_level_diff = 15;
}
if(IsNPC() && temp_level_diff < -9)
{
temp_level_diff = -9;
}
if(IsNPC() && temp_level_diff < -9)
{
temp_level_diff = -9;
}
int level_mod = temp_level_diff * temp_level_diff / 2;
if(temp_level_diff < 0)
{
level_mod = -level_mod;
}
int level_mod = temp_level_diff * temp_level_diff / 2;
if(temp_level_diff < 0)
{
level_mod = -level_mod;
}
if(IsNPC() && (caster->GetLevel() - GetLevel()) < -20)
if(IsNPC() && (caster->GetLevel() - GetLevel()) < -20)
{
level_mod = 1000;
}
//Even more level stuff this time dealing with damage spells
if(IsNPC() && IsDamageSpell(spell_id) && GetLevel() >= 17)
{
int level_diff;
if(GetLevel() >= RuleI(Casting,ResistFalloff))
{
level_diff = (RuleI(Casting,ResistFalloff)-1) - caster->GetLevel();
if(level_diff < 0)
{
level_diff = 0;
}
}
else
{
level_diff = GetLevel() - caster->GetLevel();
}
level_mod += (2 * level_diff);
}
if (CharismaCheck)
{
level_mod = 1000;
//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
@ -4060,32 +4139,8 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use
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)
{
int level_diff;
if(GetLevel() >= RuleI(Casting, ResistFalloff))
{
level_diff = (RuleI(Casting, ResistFalloff) - 1) - caster->GetLevel();
if(level_diff < 0)
{
level_diff = 0;
}
}
else
{
level_diff = GetLevel() - caster->GetLevel();
}
resist_chance += (2 * level_diff);
}
//DCBOOKMARK
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)

View File

@ -1016,6 +1016,9 @@ bool Client::TradeskillExecute(DBTradeskillRecipe_Struct *spec) {
}
}
//DCBOOKMARK
chance = mod_tradeskill_chance(chance, spec);
if (((spec->tradeskill==75) || GetGM() || (chance > res)) || MakeRandomInt(0, 99) < AAChance){
success_modifier = 1;
@ -1108,6 +1111,9 @@ void Client::CheckIncreaseTradeskill(int16 bonusstat, int16 stat_modifier, float
}
}
//DCBOOKMARK
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);

View File

@ -251,6 +251,10 @@ int32 Client::TributeItem(uint32 slot, uint32 quantity) {
//figure out what its worth
int32 pts = inst->GetItem()->Favor;
//DCBOOKMARK
pts = mod_tribute_item_value(pts);
if(pts < 1) {
Message(13, "This item is worthless for favor.");
return(0);

View File

@ -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,13 @@ 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());
//DCBOOKMARK
LoadTickItems();
//MODDING HOOK FOR ZONE INIT
mod_init();
return true;
}
@ -1483,6 +1491,9 @@ bool Zone::Process() {
}
}
//DCBOOKMARK
if(hotzone_timer.Check()) { UpdateHotzone(); }
return true;
}
@ -1531,6 +1542,9 @@ void Zone::Repop(uint32 delay) {
MZoneLock.unlock();
initgrids_timer.Start();
//MODDING HOOK FOR REPOP
mod_repop();
}
void Zone::GetTimeSync()
@ -2657,3 +2671,75 @@ void Zone::ReloadWorld(uint32 Option){
parse->ReloadQuests();
}
}
//DCBOOKMARK
void Zone::LoadTickItems()
{
char errbuf[MYSQL_ERRMSG_SIZE];
char* query = 0;
MYSQL_RES *result;
MYSQL_ROW row;
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
{
tick_items[atoi(row[0])] = { atoi(row[0]), atoi(row[1]), atoi(row[2]), (int16)atoi(row[4]), std::string(row[3]) };
}
}
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);
}

View File

@ -33,6 +33,7 @@
#include "tasks.h"
#include "pathing.h"
#include "QGlobals.h"
#include <unordered_map>
class Map;
class WaterMap;
@ -69,6 +70,15 @@ struct ZoneEXPModInfo {
float AAExpMod;
};
//DCBOOKMARK
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 +263,16 @@ public:
LinkedList<NPC_Emote_Struct*> NPCEmoteList;
//DCBOOKMARK
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 +325,7 @@ private:
LinkedList<ZoneClientAuth_Struct*> client_auth_list;
QGlobalCache *qGlobals;
Timer hotzone_timer;
Mutex MZoneLock;
};