This commit is contained in:
SecretsOTheP 2013-11-18 23:56:11 -05:00
commit 8b7984cf7d
26 changed files with 939 additions and 817 deletions

View File

@ -1,5 +1,31 @@
EQEMu Changelog (Started on Sept 24, 2003 15:50)
-------------------------------------------------------
== 11/18/2013 ==
demonstar55: Added assistradius to npc_types, defaults to aggroradius if set to 0 (old behaviour)
== 11/17/2013 ==
Sorvani: fixed leash and tether special abilities to use the specified range correctly.
demonstar55: Rewrote the Mob::_GetMovementSpeed fix an issue that arose from the change on 11/11
- Added the rule Character:BaseRunSpeedCap (default 158) so people can customize what their runspeed cap is. Hardcapped to 225 so stuff doesn't get too crazy.
== 11/16/2013 ==
Leere: Fixed the drinking message for auto-consume, it will again correctly show up for forced consumption instead.
demonstar55: Added Mob::DoCastingChecks() which will check for various fail conditions while the casting bar is up. This is called after Mob::DoCastSpell() starts the casting and before it returns.
== 11/15/2013 ==
demonstar55: Fixed Mob::CalcFocusEffect()'s SE_LimitEffect
Leere: Fixed a stacking issue for SE_StackingCommand_Block
== 11/13/2013 ==
demonstar55: Implemented bard song effect cap. You can set the base cap with the rule Character:BaseInstrumentSoftCap, defaults to 36 or "3.6" as it is sometimes referred to.
demonstar55: Fix Echo of Taelosia and Ayonae's Tutelage to increase the mod cap instead of further improving the instrument mod
demonstar55: Implemented Singing/Instrument Mastery as an AA bonus.
== 11/11/2013 ==
demonstar55: Changed the way walk speed is calculated to allow mobs to have their walk speed equal a 100% movement reduction
== 11/09/2013 ==
Leere: Fixed Bard mana regen, they now only are affected by items and AA.
== 11/07/2013 ==
KLS: Added a system to use the BaseData system in the client.

View File

@ -74,11 +74,11 @@ To use ptimers, you need to create the table below in your DB:
Schema:
CREATE TABLE timers (
char_id INT(11) NOT nullptr,
type MEDIUMINT UNSIGNED NOT nullptr,
start INT UNSIGNED NOT nullptr,
duration INT UNSIGNED NOT nullptr,
enable TINYINT NOT nullptr,
char_id INT(11) NOT NULL,
type MEDIUMINT UNSIGNED NOT NULL,
start INT UNSIGNED NOT NULL,
duration INT UNSIGNED NOT NULL,
enable TINYINT NOT NULL,
PRIMARY KEY(char_id, type)
);

View File

@ -35,17 +35,17 @@ Requred SQL:
CREATE TABLE rule_sets (
ruleset_id TINYINT UNSIGNED NOT nullptr auto_increment,
name VARCHAR(255) NOT nullptr,
ruleset_id TINYINT UNSIGNED NOT NULL auto_increment,
name VARCHAR(255) NOT NULL,
PRIMARY KEY(ruleset_id)
);
INSERT INTO rule_sets VALUES(0, "default");
UPDATE rule_sets SET ruleset_id=0;
CREATE TABLE rule_values (
ruleset_id TINYINT UNSIGNED NOT nullptr,
rule_name VARCHAR(64) NOT nullptr,
rule_value VARCHAR(10) NOT nullptr,
ruleset_id TINYINT UNSIGNED NOT NULL,
rule_name VARCHAR(64) NOT NULL,
rule_value VARCHAR(10) NOT NULL,
INDEX(ruleset_id),
PRIMARY KEY(ruleset_id,rule_name)
);

View File

@ -97,6 +97,8 @@ RULE_BOOL ( Character, EnableDiscoveredItems, true ) // If enabled, it enables E
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_INT ( Character, FoodLossPerUpdate, 35) // How much food/water you lose per stamina update
RULE_INT ( Character, BaseInstrumentSoftCap, 36) // Softcap for instrument mods, 36 commonly referred to as "3.6" as well.
RULE_INT ( Character, BaseRunSpeedCap, 158) // Base Run Speed Cap, on live it's 158% which will give you a runspeed of 1.580 hard capped to 225.
RULE_CATEGORY_END()
RULE_CATEGORY( Mercs )

File diff suppressed because it is too large Load Diff

View File

@ -404,7 +404,7 @@ typedef enum {
#define SE_PetDiscipline 257 // not implemented as bonus - /pet hold
#define SE_TripleBackstab 258 // implemented[AA] - chance to perform a triple backstab
#define SE_CombatStability 259 // implemented[AA] - damage mitigation
#define SE_AddSingingMod 260 // *not implemented
#define SE_AddSingingMod 260 // implemented[AA] - Instrument/Singing Mastery, base1 is the mod, base2 is the ItemType
//#define SE_Unknown261 261 // not used
#define SE_RaiseStatCap 262 // implemented
#define SE_TradeSkillMastery 263 // implemented - lets you raise more than one tradeskill above master.

View File

@ -0,0 +1,30 @@
-- Instrument Mastery
REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (213, 1, 260, 2, 23);
REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (213, 2, 260, 2, 24);
REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (213, 3, 260, 2, 25);
REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (213, 4, 260, 2, 26);
REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (214, 1, 260, 4, 23);
REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (214, 2, 260, 4, 24);
REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (214, 3, 260, 4, 25);
REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (214, 4, 260, 4, 26);
REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (215, 1, 260, 6, 23);
REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (215, 2, 260, 6, 24);
REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (215, 3, 260, 6, 25);
REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (215, 4, 260, 6, 26);
-- Improved Instrument Mastery
REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (700, 1, 260, 2, 23);
REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (700, 2, 260, 2, 24);
REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (700, 3, 260, 2, 25);
REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (700, 4, 260, 2, 26);
-- Singing Mastery
REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (275, 1, 260, 2, 50);
REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (276, 1, 260, 4, 50);
REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (277, 1, 260, 6, 50);
-- Improved Singing Mastery
REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (701, 1, 260, 2, 50);

View File

@ -0,0 +1 @@
ALTER TABLE `npc_types` ADD `assistradius` INT( 10 ) UNSIGNED NOT NULL DEFAULT '0' AFTER `aggroradius`;

View File

@ -1089,17 +1089,17 @@ void Mob::AI_Process() {
return;
if(GetSpecialAbility(TETHER)) {
float aggro_range = static_cast<float>(GetSpecialAbilityParam(TETHER, 0));
aggro_range = aggro_range > 0.0f ? aggro_range : pAggroRange * pAggroRange;
float tether_range = static_cast<float>(GetSpecialAbilityParam(TETHER, 0));
tether_range = tether_range > 0.0f ? tether_range * tether_range : pAggroRange * pAggroRange;
if(DistNoRootNoZ(CastToNPC()->GetSpawnPointX(), CastToNPC()->GetSpawnPointY()) > aggro_range) {
if(DistNoRootNoZ(CastToNPC()->GetSpawnPointX(), CastToNPC()->GetSpawnPointY()) > tether_range) {
GMMove(CastToNPC()->GetSpawnPointX(), CastToNPC()->GetSpawnPointY(), CastToNPC()->GetSpawnPointZ(), CastToNPC()->GetSpawnPointH());
}
} else if(GetSpecialAbility(LEASH)) {
float aggro_range = static_cast<float>(GetSpecialAbilityParam(LEASH, 0));
aggro_range = aggro_range > 0.0f ? aggro_range : pAggroRange * pAggroRange;
float leash_range = static_cast<float>(GetSpecialAbilityParam(LEASH, 0));
leash_range = leash_range > 0.0f ? leash_range * leash_range : pAggroRange * pAggroRange;
if(DistNoRootNoZ(CastToNPC()->GetSpawnPointX(), CastToNPC()->GetSpawnPointY()) > aggro_range) {
if(DistNoRootNoZ(CastToNPC()->GetSpawnPointX(), CastToNPC()->GetSpawnPointY()) > leash_range) {
GMMove(CastToNPC()->GetSpawnPointX(), CastToNPC()->GetSpawnPointY(), CastToNPC()->GetSpawnPointZ(), CastToNPC()->GetSpawnPointH());
SetHP(GetMaxHP());
BuffFadeAll();

View File

@ -874,6 +874,26 @@ void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon)
case SE_CombatStability:
newbon->CombatStability += base1;
break;
case SE_AddSingingMod:
switch (base2)
{
case ItemTypeWindInstrument:
newbon->windMod += base1;
break;
case ItemTypeStringedInstrument:
newbon->stringedMod += base1;
break;
case ItemTypeBrassInstrument:
newbon->brassMod += base1;
break;
case ItemTypePercussionInstrument:
newbon->percussionMod += base1;
break;
case ItemTypeSinging:
newbon->singingMod += base1;
break;
}
break;
case SE_PetCriticalHit:
newbon->PetCriticalHit += base1;
break;
@ -1188,6 +1208,7 @@ void Mob::CalcSpellBonuses(StatBonuses* newbon)
}
//this prolly suffer from roundoff error slightly...
newbon->AC = newbon->AC * 10 / 34; //ratio determined impirically from client.
if (GetClass() == BARD) newbon->ManaRegen = 0; // Bards do not get mana regen from spells.
}
void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* newbon, uint16 casterId, bool item_bonus, uint32 ticsremaining, int buffslot)
@ -2230,6 +2251,27 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
newbon->CombatStability += effect_value;
break;
case SE_AddSingingMod:
switch (spells[spell_id].base2[i])
{
case ItemTypeWindInstrument:
newbon->windMod += effect_value;
break;
case ItemTypeStringedInstrument:
newbon->stringedMod += effect_value;
break;
case ItemTypeBrassInstrument:
newbon->brassMod += effect_value;
break;
case ItemTypePercussionInstrument:
newbon->percussionMod += effect_value;
break;
case ItemTypeSinging:
newbon->singingMod += effect_value;
break;
}
break;
case SE_PetAvoidance:
newbon->PetAvoidance += effect_value;
break;

View File

@ -8032,7 +8032,7 @@ void Client::Consume(const Item_Struct *item, uint8 type, int16 slot, bool auto_
m_pp.thirst_level += tchange;
DeleteItemInInventory(slot, 1, false);
if(auto_consume) //no message if the client consumed for us
if(!auto_consume) //no message if the client consumed for us
entity_list.MessageClose_StringID(this, true, 50, 0, DRINKING_MESSAGE, GetName(), item->Name);
#if EQDEBUG >= 1

View File

@ -1796,6 +1796,7 @@ uint16 Mob::GetInstrumentMod(uint16 spell_id) const {
return(10);
uint16 effectmod = 10;
int effectmodcap = RuleI(Character, BaseInstrumentSoftCap);
//this should never use spell modifiers...
//if a spell grants better modifers, they are copied into the item mods
@ -1812,6 +1813,7 @@ uint16 Mob::GetInstrumentMod(uint16 spell_id) const {
effectmod = itembonuses.percussionMod;
else
effectmod = spellbonuses.percussionMod;
effectmod += aabonuses.percussionMod;
break;
case SkillStringedInstruments:
if(itembonuses.stringedMod == 0 && spellbonuses.stringedMod == 0)
@ -1822,6 +1824,7 @@ uint16 Mob::GetInstrumentMod(uint16 spell_id) const {
effectmod = itembonuses.stringedMod;
else
effectmod = spellbonuses.stringedMod;
effectmod += aabonuses.stringedMod;
break;
case SkillWindInstruments:
if(itembonuses.windMod == 0 && spellbonuses.windMod == 0)
@ -1832,6 +1835,7 @@ uint16 Mob::GetInstrumentMod(uint16 spell_id) const {
effectmod = itembonuses.windMod;
else
effectmod = spellbonuses.windMod;
effectmod += aabonuses.windMod;
break;
case SkillBrassInstruments:
if(itembonuses.brassMod == 0 && spellbonuses.brassMod == 0)
@ -1842,6 +1846,7 @@ uint16 Mob::GetInstrumentMod(uint16 spell_id) const {
effectmod = itembonuses.brassMod;
else
effectmod = spellbonuses.brassMod;
effectmod += aabonuses.brassMod;
break;
case SkillSinging:
if(itembonuses.singingMod == 0 && spellbonuses.singingMod == 0)
@ -1850,30 +1855,26 @@ uint16 Mob::GetInstrumentMod(uint16 spell_id) const {
effectmod = itembonuses.singingMod;
else
effectmod = spellbonuses.singingMod;
effectmod += aabonuses.singingMod;
break;
default:
effectmod = 10;
break;
}
if(spells[spell_id].skill == SkillSinging)
{
effectmod += 2*GetAA(aaSingingMastery);
effectmod += 2*GetAA(aaImprovedSingingMastery);
}
else
{
effectmod += 2*GetAA(aaInstrumentMastery);
effectmod += 2*GetAA(aaImprovedInstrumentMastery);
}
effectmod += 2*GetAA(aaAyonaesTutelage); //singing & instruments
effectmod += 2*GetAA(aaEchoofTaelosia); //singing & instruments
// TODO: These shouldn't be hardcoded.
effectmodcap += GetAA(aaAyonaesTutelage);
effectmodcap += GetAA(aaEchoofTaelosia);
if(effectmod < 10)
effectmod = 10;
_log(SPELLS__BARDS, "%s::GetInstrumentMod() spell=%d mod=%d\n", GetName(), spell_id, effectmod);
if (effectmod > effectmodcap)
effectmod = effectmodcap;
_log(SPELLS__BARDS, "%s::GetInstrumentMod() spell=%d mod=%d modcap=%d\n",
GetName(), spell_id, effectmod, effectmodcap);
return(effectmod);
}

View File

@ -917,7 +917,7 @@ void Client::CheatDetected(CheatTypes CheatType, float x, float y, float z)
Message(13, "Large warp detected.");
char hString[250];
sprintf(hString, "/MQWarp with location %.2f, %.2f, %.2f", GetX(), GetY(), GetZ());
database.SetMQDetectionFlag(this->account_name,this->name, hString, zone->GetShortName());
database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName());
}
break;
case MQWarpShadowStep:
@ -927,7 +927,7 @@ void Client::CheatDetected(CheatTypes CheatType, float x, float y, float z)
{
char *hString = nullptr;
MakeAnyLenString(&hString, "/MQWarp(SS) with location %.2f, %.2f, %.2f, the target was shadow step exempt but we still found this suspicious.", GetX(), GetY(), GetZ());
database.SetMQDetectionFlag(this->account_name,this->name, hString, zone->GetShortName());
database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName());
safe_delete_array(hString);
}
break;
@ -938,7 +938,7 @@ void Client::CheatDetected(CheatTypes CheatType, float x, float y, float z)
{
char *hString = nullptr;
MakeAnyLenString(&hString, "/MQWarp(KB) with location %.2f, %.2f, %.2f, the target was Knock Back exempt but we still found this suspicious.", GetX(), GetY(), GetZ());
database.SetMQDetectionFlag(this->account_name,this->name, hString, zone->GetShortName());
database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName());
safe_delete_array(hString);
}
break;
@ -952,7 +952,7 @@ void Client::CheatDetected(CheatTypes CheatType, float x, float y, float z)
{
char *hString = nullptr;
MakeAnyLenString(&hString, "/MQWarp(LT) with location %.2f, %.2f, %.2f, running fast but not fast enough to get killed, possibly: small warp, speed hack, excessive lag, marked as suspicious.", GetX(), GetY(), GetZ());
database.SetMQDetectionFlag(this->account_name,this->name, hString, zone->GetShortName());
database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName());
safe_delete_array(hString);
}
}
@ -963,7 +963,7 @@ void Client::CheatDetected(CheatTypes CheatType, float x, float y, float z)
{
char hString[250];
sprintf(hString, "/MQZone used at %.2f, %.2f, %.2f to %.2f %.2f %.2f", GetX(), GetY(), GetZ(), x, y, z);
database.SetMQDetectionFlag(this->account_name,this->name, hString, zone->GetShortName());
database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName());
}
break;
case MQZoneUnknownDest:
@ -971,13 +971,15 @@ void Client::CheatDetected(CheatTypes CheatType, float x, float y, float z)
{
char hString[250];
sprintf(hString, "/MQZone used at %.2f, %.2f, %.2f", GetX(), GetY(), GetZ());
database.SetMQDetectionFlag(this->account_name,this->name, hString, zone->GetShortName());
database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName());
}
break;
case MQGate:
if (RuleB(Zone, EnableMQGateDetector)&& ((this->Admin() < RuleI(Zone, MQGateExemptStatus) || (RuleI(Zone, MQGateExemptStatus)) == -1))) {
Message(13, "Illegal gate request.");
database.SetMQDetectionFlag(this->account_name,this->name, "/MQGate", zone->GetShortName());
char hString[250];
sprintf(hString, "/MQGate used at %.2f, %.2f, %.2f", GetX(), GetY(), GetZ());
database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName());
if(zone)
{
this->SetZone(this->GetZoneID(), zone->GetInstanceID()); //Prevent the player from zoning, place him back in the zone where he tried to originally /gate.
@ -991,13 +993,13 @@ void Client::CheatDetected(CheatTypes CheatType, float x, float y, float z)
break;
case MQGhost: //Not currently implemented, but the framework is in place - just needs detection scenarios identified
if (RuleB(Zone, EnableMQGhostDetector) && ((this->Admin() < RuleI(Zone, MQGhostExemptStatus) || (RuleI(Zone, MQGhostExemptStatus)) == -1))) {
database.SetMQDetectionFlag(this->account_name,this->name, "/MQGhost", zone->GetShortName());
database.SetMQDetectionFlag(this->account_name, this->name, "/MQGhost", zone->GetShortName());
}
break;
default:
char *hString = nullptr;
MakeAnyLenString(&hString, "Unhandled HackerDetection flag with location %.2f, %.2f, %.2f.", GetX(), GetY(), GetZ());
database.SetMQDetectionFlag(this->account_name,this->name, hString, zone->GetShortName());
database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName());
safe_delete_array(hString);
break;
}

View File

@ -6647,6 +6647,7 @@ void command_npcedit(Client *c, const Seperator *sep)
c->Message(0, "#npcedit Mindmg - Sets an NPCs minimum damage");
c->Message(0, "#npcedit Maxdmg - Sets an NPCs maximum damage");
c->Message(0, "#npcedit Aggroradius - Sets an NPCs aggro radius");
c->Message(0, "#npcedit Assistradius - Sets an NPCs assist radius");
c->Message(0, "#npcedit Social - Set to 1 if an NPC should assist others on its faction");
c->Message(0, "#npcedit Runspeed - Sets an NPCs run speed");
c->Message(0, "#npcedit MR - Sets an NPCs magic resistance");
@ -6853,6 +6854,15 @@ void command_npcedit(Client *c, const Seperator *sep)
c->LogSQL(query);
safe_delete_array(query);
}
else if ( strcasecmp( sep->arg[1], "assistradius" ) == 0 )
{
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
c->Message(15,"NPCID %u now has an assist radius of %i",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2]));
database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set assistradius=%i where id=%i",atoi(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf);
c->LogSQL(query);
safe_delete_array(query);
}
else if ( strcasecmp( sep->arg[1], "social" ) == 0 )
{
char errbuf[MYSQL_ERRMSG_SIZE];

View File

@ -530,14 +530,8 @@ void Group::SplitExp(uint32 exp, Mob* other) {
}
float groupmod;
if (membercount == 2)
groupmod = 1.2;
else if (membercount == 3)
groupmod = 1.4;
else if (membercount == 4)
groupmod = 1.6;
else if (membercount == 5)
groupmod = 1.8;
if (membercount > 1 && membercount < 6)
groupmod = 1 + .2*(membercount - 1); //2members=1.2exp, 3=1.4, 4=1.6, 5=1.8
else if (membercount == 6)
groupmod = 2.16;
else

View File

@ -56,27 +56,27 @@ very low chance of dropping.
Schema:
CREATE TABLE forage (
id int(11) NOT nullptr auto_increment,
zoneid int(4) NOT nullptr default '0',
Itemid int(11) NOT nullptr default '0',
level smallint(6) NOT nullptr default '0',
chance smallint(6) NOT nullptr default '0',
id int(11) NOT NULL auto_increment,
zoneid int(4) NOT NULL default '0',
Itemid int(11) NOT NULL default '0',
level smallint(6) NOT NULL default '0',
chance smallint(6) NOT NULL default '0',
PRIMARY KEY (id)
) TYPE=MyISAM;
old table upgrade:
alter table forage add chance smallint(6) NOT nullptr default '0';
alter table forage add chance smallint(6) NOT NULL default '0';
update forage set chance=100;
CREATE TABLE fishing (
id int(11) NOT nullptr auto_increment,
zoneid int(4) NOT nullptr default '0',
Itemid int(11) NOT nullptr default '0',
skill_level smallint(6) NOT nullptr default '0',
chance smallint(6) NOT nullptr default '0',
npc_id int NOT nullptr default 0,
npc_chance int NOT nullptr default 0,
id int(11) NOT NULL auto_increment,
zoneid int(4) NOT NULL default '0',
Itemid int(11) NOT NULL default '0',
skill_level smallint(6) NOT NULL default '0',
chance smallint(6) NOT NULL default '0',
npc_id int NOT NULL default 0,
npc_chance int NOT NULL default 0,
PRIMARY KEY (id)
) TYPE=MyISAM;

View File

@ -27,49 +27,49 @@
/*
CREATE TABLE guilds (
id MEDIUMINT UNSIGNED NOT nullptr,
name VARCHAR(32) NOT nullptr,
leader int NOT nullptr,
minstatus SMALLINT NOT nullptr,
tribute INT UNSIGNED NOT nullptr,
motd TEXT NOT nullptr DEFAULT '',
id MEDIUMINT UNSIGNED NOT NULL,
name VARCHAR(32) NOT NULL,
leader int NOT NULL,
minstatus SMALLINT NOT NULL,
tribute INT UNSIGNED NOT NULL,
motd TEXT NOT NULL DEFAULT '',
PRIMARY KEY(id),
UNIQUE KEY(name),
UNIQUE KEY(leader)
);
CREATE TABLE guild_ranks (
guild_id MEDIUMINT UNSIGNED NOT nullptr,
rank TINYINT UNSIGNED NOT nullptr,
title VARCHAR(128) NOT nullptr,
can_hear TINYINT UNSIGNED NOT nullptr,
can_speak TINYINT UNSIGNED NOT nullptr,
can_invite TINYINT UNSIGNED NOT nullptr,
can_remove TINYINT UNSIGNED NOT nullptr,
can_promote TINYINT UNSIGNED NOT nullptr,
can_demote TINYINT UNSIGNED NOT nullptr,
can_motd TINYINT UNSIGNED NOT nullptr,
can_warpeace TINYINT UNSIGNED NOT nullptr,
guild_id MEDIUMINT UNSIGNED NOT NULL,
rank TINYINT UNSIGNED NOT NULL,
title VARCHAR(128) NOT NULL,
can_hear TINYINT UNSIGNED NOT NULL,
can_speak TINYINT UNSIGNED NOT NULL,
can_invite TINYINT UNSIGNED NOT NULL,
can_remove TINYINT UNSIGNED NOT NULL,
can_promote TINYINT UNSIGNED NOT NULL,
can_demote TINYINT UNSIGNED NOT NULL,
can_motd TINYINT UNSIGNED NOT NULL,
can_warpeace TINYINT UNSIGNED NOT NULL,
PRIMARY KEY(guild_id,rank)
);
# guild1 < guild2 by definition.
CREATE TABLE guild_relations (
guild1 MEDIUMINT UNSIGNED NOT nullptr,
guild2 MEDIUMINT UNSIGNED NOT nullptr,
relation TINYINT NOT nullptr,
guild1 MEDIUMINT UNSIGNED NOT NULL,
guild2 MEDIUMINT UNSIGNED NOT NULL,
relation TINYINT NOT NULL,
PRIMARY KEY(guild1, guild1)
);
CREATE TABLE guild_members (
char_id INT NOT nullptr,
guild_id MEDIUMINT UNSIGNED NOT nullptr,
rank TINYINT UNSIGNED NOT nullptr,
tribute_enable TINYINT UNSIGNED NOT nullptr DEFAULT 0,
total_tribute INT UNSIGNED NOT nullptr DEFAULT 0,
last_tribute INT UNSIGNED NOT nullptr DEFAULT 0,
banker TINYINT UNSIGNED NOT nullptr DEFAULT 0,
public_note TEXT NOT nullptr DEFAULT '',
char_id INT NOT NULL,
guild_id MEDIUMINT UNSIGNED NOT NULL,
rank TINYINT UNSIGNED NOT NULL,
tribute_enable TINYINT UNSIGNED NOT NULL DEFAULT 0,
total_tribute INT UNSIGNED NOT NULL DEFAULT 0,
last_tribute INT UNSIGNED NOT NULL DEFAULT 0,
banker TINYINT UNSIGNED NOT NULL DEFAULT 0,
public_note TEXT NOT NULL DEFAULT '',
PRIMARY KEY(char_id)
);

View File

@ -517,76 +517,75 @@ bool Mob::IsInvisible(Mob* other) const
return(false);
}
float Mob::_GetMovementSpeed(int mod) const {
float Mob::_GetMovementSpeed(int mod) const
{
// List of movement speed modifiers, including AAs & spells:
// http://everquest.allakhazam.com/db/item.html?item=1721;page=1;howmany=50#m10822246245352
if (IsRooted())
return 0.0f;
float aa_mod = 0.0f;
float speed_mod = runspeed;
bool has_horse = false;
if (IsClient())
{
if(CastToClient()->GetGMSpeed())
{
// These two cases ignore the cap, be wise in the DB for horses.
if (IsClient()) {
if (CastToClient()->GetGMSpeed()) {
speed_mod = 3.125f;
}
else
{
Mob* horse = entity_list.GetMob(CastToClient()->GetHorseId());
if(horse)
{
if (mod != 0)
speed_mod += speed_mod * static_cast<float>(mod) / 100.0f;
return speed_mod;
} else {
Mob *horse = entity_list.GetMob(CastToClient()->GetHorseId());
if (horse) {
speed_mod = horse->GetBaseRunspeed();
has_horse = true;
if (mod != 0)
speed_mod += speed_mod * static_cast<float>(mod) / 100.0f;
return speed_mod;
}
}
}
aa_mod += itembonuses.BaseMovementSpeed + spellbonuses.BaseMovementSpeed + aabonuses.BaseMovementSpeed;
int spell_mod = spellbonuses.movementspeed + itembonuses.movementspeed;
int aa_mod = 0;
int spell_mod = 0;
int runspeedcap = RuleI(Character,BaseRunSpeedCap);
int movemod = 0;
float frunspeedcap = 0.0f;
if(spell_mod < 0)
{
runspeedcap += itembonuses.IncreaseRunSpeedCap + spellbonuses.IncreaseRunSpeedCap + aabonuses.IncreaseRunSpeedCap;
aa_mod += itembonuses.BaseMovementSpeed + spellbonuses.BaseMovementSpeed + aabonuses.BaseMovementSpeed;
spell_mod += spellbonuses.movementspeed + itembonuses.movementspeed;
// hard cap
if (runspeedcap > 225)
runspeedcap = 225;
if (spell_mod < 0)
movemod += spell_mod;
}
else if(spell_mod > (aa_mod))
{
else if (spell_mod > aa_mod)
movemod = spell_mod;
}
else
{
movemod = static_cast<int>(aa_mod);
}
movemod = aa_mod;
if(movemod < -85) //cap it at moving very very slow
// cap negative movemods from snares mostly
if (movemod < -85)
movemod = -85;
if (!has_horse && movemod != 0)
speed_mod += (speed_mod * float(movemod) / 100.0f);
if (movemod != 0)
speed_mod += speed_mod * static_cast<float>(movemod) / 100.0f;
if(mod != 0)
speed_mod += (speed_mod * (float)mod / 100.0f);
// runspeed caps
frunspeedcap = static_cast<float>(runspeedcap) / 100.0f;
if (IsClient() && speed_mod > frunspeedcap)
speed_mod = frunspeedcap;
if(speed_mod <= 0.0f)
return(0.0001f);
// apply final mod such as the -47 for walking
// use runspeed since it should stack with snares
// and if we get here, we know runspeed was the initial
// value before we applied movemod.
if (mod != 0)
speed_mod += runspeed * static_cast<float>(mod) / 100.0f;
//runspeed cap.
if(IsClient())
{
if (speed_mod > 1.58){
uint8 bonus_IncreaseRunSpeedCap = itembonuses.IncreaseRunSpeedCap + spellbonuses.IncreaseRunSpeedCap + aabonuses.IncreaseRunSpeedCap;
if (bonus_IncreaseRunSpeedCap){
speed_mod += float(bonus_IncreaseRunSpeedCap)/100.0f;
if(speed_mod > 1.74)
speed_mod = 1.74;
}
else
speed_mod = 1.58;
}
}
if (speed_mod <= 0.0f)
speed_mod = IsClient() ? 0.0001f : 0.0f;
return speed_mod;
}

View File

@ -198,6 +198,7 @@ public:
void InterruptSpell(uint16, uint16, uint16 spellid = SPELL_UNKNOWN);
inline bool IsCasting() const { return((casting_spell_id != 0)); }
uint16 CastingSpellID() const { return casting_spell_id; }
bool DoCastingChecks();
//Buff
void BuffProcess();
@ -998,6 +999,7 @@ protected:
uint32 casting_spell_timer_duration;
uint32 casting_spell_type;
int16 casting_spell_resist_adjust;
bool casting_spell_checks;
uint16 bardsong;
uint8 bardsong_slot;
uint32 bardsong_target_id;

View File

@ -148,7 +148,7 @@ NPC::NPC(const NPCType* d, Spawn2* in_respawn, float x, float y, float z, float
logging_enabled = NPC_DEFAULT_LOGGING_ENABLED;
pAggroRange = d->aggroradius;
pAssistRange = GetAggroRange();
pAssistRange = d->assistradius;
findable = d->findable;
trackable = d->trackable;

View File

@ -34,33 +34,33 @@ extern WorldServer worldserver;
/*
CREATE TABLE spawn_conditions (
zone VARCHAR(16) NOT nullptr,
id MEDIUMINT UNSIGNED NOT nullptr DEFAULT '1',
value MEDIUMINT NOT nullptr DEFAULT '0',
onchange TINYINT UNSIGNED NOT nullptr DEFAULT '0',
name VARCHAR(255) NOT nullptr DEFAULT '',
zone VARCHAR(16) NOT NULL,
id MEDIUMINT UNSIGNED NOT NULL DEFAULT '1',
value MEDIUMINT NOT NULL DEFAULT '0',
onchange TINYINT UNSIGNED NOT NULL DEFAULT '0',
name VARCHAR(255) NOT NULL DEFAULT '',
PRIMARY KEY(zone,id)
);
CREATE TABLE spawn_events (
#identifiers
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
zone VARCHAR(16) NOT nullptr,
cond_id MEDIUMINT UNSIGNED NOT nullptr,
name VARCHAR(255) NOT nullptr DEFAULT '',
zone VARCHAR(16) NOT NULL,
cond_id MEDIUMINT UNSIGNED NOT NULL,
name VARCHAR(255) NOT NULL DEFAULT '',
#timing information
period INT UNSIGNED NOT nullptr,
next_minute TINYINT UNSIGNED NOT nullptr,
next_hour TINYINT UNSIGNED NOT nullptr,
next_day TINYINT UNSIGNED NOT nullptr,
next_month TINYINT UNSIGNED NOT nullptr,
next_year INT UNSIGNED NOT nullptr,
enabled TINYINT NOT nullptr DEFAULT '1',
period INT UNSIGNED NOT NULL,
next_minute TINYINT UNSIGNED NOT NULL,
next_hour TINYINT UNSIGNED NOT NULL,
next_day TINYINT UNSIGNED NOT NULL,
next_month TINYINT UNSIGNED NOT NULL,
next_year INT UNSIGNED NOT NULL,
enabled TINYINT NOT NULL DEFAULT '1',
#action:
action TINYINT UNSIGNED NOT nullptr DEFAULT '0',
argument MEDIUMINT NOT nullptr DEFAULT '0'
action TINYINT UNSIGNED NOT NULL DEFAULT '0',
argument MEDIUMINT NOT NULL DEFAULT '0'
);
*/

View File

@ -325,6 +325,9 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
case SE_CurrentMana:
{
// Bards don't get mana from effects, good or bad.
if(GetClass() == BARD)
break;
if(IsManaTapSpell(spell_id)) {
if(GetCasterClass() != 'N') {
#ifdef SPELL_EFFECT_SPAM
@ -352,6 +355,9 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
case SE_CurrentManaOnce:
{
// Bards don't get mana from effects, good or bad.
if(GetClass() == BARD)
break;
#ifdef SPELL_EFFECT_SPAM
snprintf(effect_desc, _EDLEN, "Current Mana Once: %+i", effect_value);
#endif
@ -2727,6 +2733,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
case SE_ImprovedBindWound:
case SE_MaxBindWound:
case SE_CombatStability:
case SE_AddSingingMod:
case SE_PetAvoidance:
case SE_GiveDoubleRiposte:
case SE_Ambidexterity:
@ -4272,7 +4279,7 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo
case SE_LimitEffect:
if(focus_spell.base[i] < 0){
if(IsEffectInSpell(spell_id,focus_spell.base[i])){ //we limit this effect, can't have
if(IsEffectInSpell(spell_id,(focus_spell.base[i] * -1))){ //we limit this effect, can't have
return 0;
}
}

View File

@ -16,9 +16,7 @@ Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org)
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
General outline of spell casting process
1.
@ -64,14 +62,10 @@ Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org)
If this was timed, CastedSpellFinished() will restore the client's
spell bar gems.
Most user code should call CastSpell(), with a 0 casting time if needed,
and not SpellFinished().
*/
#include "../common/debug.h"
#include "../common/spdat.h"
#include "masterentity.h"
@ -88,8 +82,8 @@ Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org)
#include <assert.h>
#ifndef WIN32
#include <stdlib.h>
#include "../common/unix.h"
#include <stdlib.h>
#include "../common/unix.h"
#endif
#ifdef _GOTFRAGS
@ -115,7 +109,7 @@ void Mob::SpellProcess()
}
// a timed spell is finished casting
if (casting_spell_id != 0 && spellend_timer.Check())
if (casting_spell_id != 0 && casting_spell_checks && spellend_timer.Check())
{
spellend_timer.Disable();
delaytimer = false;
@ -342,7 +336,6 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot,
mlog(SPELLS__CASTING, "DoCastSpell called for spell %s (%d) on entity %d, slot %d, time %d, mana %d, from item %d",
spell.name, spell_id, target_id, slot, cast_time, mana_cost, item_slot==0xFFFFFFFF?999:item_slot);
casting_spell_id = spell_id;
casting_spell_slot = slot;
casting_spell_inventory_slot = item_slot;
@ -474,9 +467,66 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot,
entity_list.QueueCloseClients(this, outapp, false, 200, 0, true); //IsClient() ? FILTER_PCSPELLS : FILTER_NPCSPELLS);
safe_delete(outapp);
outapp = nullptr;
if (!DoCastingChecks()) {
InterruptSpell();
return false;
}
return(true);
}
/*
* Some failures should be caught before the spell finishes casting
* This is especially helpful to clients when they cast really long things
* If this passes it sets casting_spell_checks to true which is checked in
* SpellProcess(), if a situation ever arises where a spell is delayed by these
* it's probably doing something wrong.
*/
bool Mob::DoCastingChecks()
{
if (!IsClient() || (IsClient() && CastToClient()->GetGM())) {
casting_spell_checks = true;
return true;
}
uint16 spell_id = casting_spell_id;
Mob *spell_target = entity_list.GetMob(casting_spell_targetid);
if (RuleB(Spells, BuffLevelRestrictions) &&
!spell_target->CheckSpellLevelRestriction(spell_id)) {
mlog(SPELLS__BUFFS, "Spell %d failed: recipient did not meet the level restrictions", spell_id);
if (!IsBardSong(spell_id))
Message_StringID(MT_SpellFailure, SPELL_TOO_POWERFUL);
return false;
}
if (spells[spell_id].zonetype == 1 && !zone->CanCastOutdoor()) {
Message_StringID(13, CAST_OUTDOORS);
return false;
}
if (IsEffectInSpell(spell_id, SE_Levitate) && !zone->CanLevitate()) {
Message(13, "You can't levitate in this zone.");
return false;
}
if (zone->IsSpellBlocked(spell_id, GetX(), GetY(), GetZ())) {
const char *msg = zone->GetSpellBlockedMessage(spell_id, GetX(), GetY(), GetZ());
if (msg) {
Message(13, msg);
return false;
} else {
Message(13, "You can't cast this spell here.");
return false;
}
}
casting_spell_checks = true;
return true;
}
uint16 Mob::GetSpecializeSkillValue(uint16 spell_id) const {
switch(spells[spell_id].skill) {
case SkillAbjuration:
@ -687,6 +737,7 @@ void Mob::ZeroCastingVars()
casting_spell_timer_duration = 0;
casting_spell_type = 0;
casting_spell_resist_adjust = 0;
casting_spell_checks = false;
delaytimer = false;
}
@ -1107,52 +1158,52 @@ 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];
Item_Struct* augitem = 0;
uint32 recastdelay = 0;
uint32 recasttype = 0;
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);
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_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;
}
}
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);
}
}
//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(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);
@ -1729,7 +1780,6 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16
}
}
//determine the type of spell target we have
CastAction_type CastAction;
if(!DetermineSpellTargets(spell_id, spell_target, ae_center, CastAction))
@ -1821,8 +1871,8 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16
if(!SpellOnTarget(spell_id, spell_target, false, true, resist_adjust, false)) {
if(IsBuffSpell(spell_id) && IsBeneficialSpell(spell_id)) {
// Prevent mana usage/timers being set for beneficial buffs
if(casting_spell_type == 1)
InterruptSpell();
if(casting_spell_type == 1)
InterruptSpell();
return false;
}
}
@ -2103,7 +2153,6 @@ bool Mob::ApplyNextBardPulse(uint16 spell_id, Mob *spell_target, uint16 slot) {
SetMana(GetMana() - mana_used);
}
// check line of sight to target if it's a detrimental spell
if(spell_target && IsDetrimentalSpell(spell_id) && !CheckLosFN(spell_target))
{
@ -2371,7 +2420,7 @@ int Mob::CalcBuffDuration(Mob *caster, Mob *target, uint16 spell_id, int32 caste
castlevel = caster_level_override;
int res = CalcBuffDuration_formula(castlevel, formula, duration);
res = mod_buff_duration(res, caster, target, spell_id);
mlog(SPELLS__CASTING, "Spell %d: Casting level %d, formula %d, base_duration %d: result %d",
@ -2509,43 +2558,6 @@ int Mob::CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2,
}
}
// check for special stacking block command in spell1 against spell2
for(i = 0; i < EFFECT_COUNT; i++)
{
effect1 = sp1.effectid[i];
if(effect1 == SE_StackingCommand_Block)
{
/*
The logic here is if you're comparing the same spells they can't block each other
from refreshing
*/
if(spellid1 == spellid2)
continue;
blocked_effect = sp1.base[i];
blocked_slot = sp1.formula[i] - 201; //they use base 1 for slots, we use base 0
blocked_below_value = sp1.max[i];
if(sp2.effectid[blocked_slot] == blocked_effect)
{
sp2_value = CalcSpellEffectValue(spellid2, blocked_slot, caster_level2);
mlog(SPELLS__STACKING, "%s (%d) blocks effect %d on slot %d below %d. New spell has value %d on that slot/effect. %s.",
sp1.name, spellid1, blocked_effect, blocked_slot, blocked_below_value, sp2_value, (sp2_value < blocked_below_value)?"Blocked":"Not blocked");
if(sp2_value < blocked_below_value)
{
mlog(SPELLS__STACKING, "Blocking spell because sp2_value < blocked_below_value");
return -1; // blocked
}
} else {
mlog(SPELLS__STACKING, "%s (%d) blocks effect %d on slot %d below %d, but we do not have that effect on that slot. Ignored.",
sp1.name, spellid1, blocked_effect, blocked_slot, blocked_below_value);
}
}
}
// check for special stacking overwrite in spell2 against effects in spell1
for(i = 0; i < EFFECT_COUNT; i++)
{
@ -2588,14 +2600,41 @@ int Mob::CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2,
// arbitration takes place if 2 spells have the same effect at the same
// effect slot, otherwise they're stackable, even if it's the same effect
bool will_overwrite = false;
bool effect_match = true; // Figure out if we're identical in effects on all slots.
for(i = 0; i < EFFECT_COUNT; i++)
{
if(IsBlankSpellEffect(spellid1, i) || IsBlankSpellEffect(spellid2, i))
continue;
effect1 = sp1.effectid[i];
effect2 = sp2.effectid[i];
/*
Quick check, are the effects the same, if so then
keep going else ignore it for stacking purposes.
*/
if(effect1 != effect2) {
effect_match = false;
continue;
}
// If both spells have SE_StackingCommand_Block in the slot then we check if
// it applies to the same slot and effect type.
// This is handled here because IsBlankSpellEffect() would block it otherwise,
// but for stacking we need to handle it.
if (effect1 == SE_StackingCommand_Block) {
if (sp1.formula[i] == sp2.formula[i] && sp1.base[i] == sp2.base[i]) {
if(sp1.max[i] > sp2.max[i]) {
return(-1);
} else {
continue;
}
} else {
effect_match = false;
continue;
}
}
if(IsBlankSpellEffect(spellid1, i) || IsBlankSpellEffect(spellid2, i))
continue;
//Effects which really aren't going to affect stacking.
if(effect1 == SE_CurrentHPOnce ||
effect1 == SE_CurseCounter ||
@ -2604,13 +2643,6 @@ int Mob::CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2,
continue;
}
/*
Quick check, are the effects the same, if so then
keep going else ignore it for stacking purposes.
*/
if(effect1 != effect2)
continue;
/*
Skip check if effect is SE_Limit*
skip checking effect2 since we know they are equal
@ -2698,9 +2730,50 @@ int Mob::CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2,
will_overwrite = true;
}
// check for special stacking block command in spell1 against spell2
// This has to happen last so that we don't mess ourselves up for effect identical
// spells. They should just overwrite each other without needing the stacking block
if (!effect_match)
{
for(i = 0; i < EFFECT_COUNT; i++)
{
effect1 = sp1.effectid[i];
if(effect1 == SE_StackingCommand_Block)
{
/*
The logic here is if you're comparing the same spells they can't block each other
from refreshing
*/
if(spellid1 == spellid2)
continue;
blocked_effect = sp1.base[i];
blocked_slot = sp1.formula[i] - 201; //they use base 1 for slots, we use base 0
blocked_below_value = sp1.max[i];
if(sp2.effectid[blocked_slot] == blocked_effect)
{
sp2_value = CalcSpellEffectValue(spellid2, blocked_slot, caster_level2);
mlog(SPELLS__STACKING, "%s (%d) blocks effect %d on slot %d below %d. New spell has value %d on that slot/effect. %s.",
sp1.name, spellid1, blocked_effect, blocked_slot, blocked_below_value, sp2_value, (sp2_value < blocked_below_value)?"Blocked":"Not blocked");
if(sp2_value < blocked_below_value)
{
mlog(SPELLS__STACKING, "Blocking spell because sp2_value < blocked_below_value");
return -1; // blocked
}
} else {
mlog(SPELLS__STACKING, "%s (%d) blocks effect %d on slot %d below %d, but we do not have that effect on that slot. Ignored.",
sp1.name, spellid1, blocked_effect, blocked_slot, blocked_below_value);
}
}
}
}
//if we get here, then none of the values on the new spell are "worse"
//so now we see if this new spell is any better, or if its not related at all
if(will_overwrite) {
if(will_overwrite || effect_match) {
mlog(SPELLS__STACKING, "Stacking code decided that %s should overwrite %s.", sp2.name, sp1.name);
return(1);
}
@ -2714,7 +2787,7 @@ int Mob::CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2,
// derived from http://samanna.net/eq.general/buffs.shtml
// spells 1-50: no restrictons
// 51-65: SpellLevel/2+15
// 66+L Group Spells 62, Single Target 61
// 66+ Group Spells 62, Single Target 61
bool Mob::CheckSpellLevelRestriction(uint16 spell_id)
{
return true;
@ -2724,20 +2797,15 @@ bool Client::CheckSpellLevelRestriction(uint16 spell_id)
{
int SpellLevel = GetMinLevel(spell_id);
// Only check for beneficial buffs, if it's a bard song, only if it's short duration
if(IsBuffSpell(spell_id) && IsBeneficialSpell(spell_id) &&
!(IsBardSong(spell_id) && !IsShortDurationBuff(spell_id)))
{
if(SpellLevel > 65)
{
if(IsGroupSpell(spell_id) && GetLevel() < 62)
// Only check for beneficial buffs
if (IsBuffSpell(spell_id) && IsBeneficialSpell(spell_id)) {
if (SpellLevel > 65) {
if (IsGroupSpell(spell_id) && GetLevel() < 62)
return false;
else if(GetLevel() < 61)
else if (GetLevel() < 61)
return false;
}
else if(SpellLevel > 50) // 51-65
{
if(GetLevel() < (SpellLevel/2+15))
} else if (SpellLevel > 50) { // 51-65
if (GetLevel() < (SpellLevel / 2 + 15))
return false;
}
}
@ -2911,7 +2979,6 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid
SendPetBuffsToClient();
}
if((IsClient() && !CastToClient()->GetPVP()) || (IsPet() && GetOwner() && GetOwner()->IsClient() && !GetOwner()->CastToClient()->GetPVP()) ||
(IsMerc() && GetOwner() && GetOwner()->IsClient() && !GetOwner()->CastToClient()->GetPVP()))
{
@ -3103,7 +3170,6 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect, bool use_r
// send to people in the area, ignoring caster and target
entity_list.QueueCloseClients(spelltar, action_packet, true, 200, this, true, spelltar->IsClient() ? FilterPCSpells : FilterNPCSpells);
/* Send the EVENT_CAST_ON event */
if(spelltar->IsNPC())
{
@ -3234,7 +3300,6 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect, bool use_r
}
if(!IsBeneficialAllowed(spelltar) ||
(IsGroupOnlySpell(spell_id) &&
!(
@ -3271,7 +3336,6 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect, bool use_r
// ok at this point the spell is permitted to affect the target,
// but we need to check special cases and resists
// check immunities
if(spelltar->IsImmuneToSpell(spell_id, this))
{
@ -4014,7 +4078,6 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use
}
}
if (!CharismaCheck){
//Check for Spell Effect specific resistance chances (ie AA Mental Fortitude)
@ -4110,60 +4173,60 @@ 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)
{
level_mod = 1000;
}
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);
}
//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)
{
@ -4723,7 +4786,6 @@ uint16 Mob::GetSpellIDFromSlot(uint8 slot)
return 0;
}
bool Mob::FindType(uint16 type, bool bOffensive, uint16 threshold) {
uint32 buff_count = GetMaxTotalSlots();
for (int i = 0; i < buff_count; i++) {
@ -4922,7 +4984,6 @@ int Mob::GetCasterLevel(uint16 spell_id) {
return(level);
}
//this method does NOT tell the client to stop singing the song.
//this is NOT the right way to stop a mob from singing, use InterruptSpell
//you should really know what your doing before you call this
@ -4965,7 +5026,6 @@ void Mob::SendPetBuffsToClient()
int PetBuffCount = 0;
EQApplicationPacket* outapp = new EQApplicationPacket(OP_PetBuffWindow,sizeof(PetBuff_Struct));
PetBuff_Struct* pbs=(PetBuff_Struct*)outapp->pBuffer;
memset(outapp->pBuffer,0,outapp->size);
@ -5044,8 +5104,6 @@ EQApplicationPacket *Mob::MakeBuffsPacket(bool for_target)
return outapp;
}
void Mob::BuffModifyDurationBySpellID(uint16 spell_id, int32 newDuration)
{
uint32 buff_count = GetMaxTotalSlots();

View File

@ -27,20 +27,20 @@
Schema:
CREATE TABLE traps (
id int(11) NOT nullptr auto_increment,
zone varchar(16) NOT nullptr default '',
x int(11) NOT nullptr default '0',
y int(11) NOT nullptr default '0',
z int(11) NOT nullptr default '0',
chance tinyint NOT nullptr default '0',
maxzdiff float NOT nullptr default '0',
radius float NOT nullptr default '0',
effect int(11) NOT nullptr default '0',
effectvalue int(11) NOT nullptr default '0',
effectvalue2 int(11) NOT nullptr default '0',
message varcahr(200) NOT nullptr;
skill int(11) NOT nullptr default '0',
spawnchance int(11) NOT nullptr default '0',
id int(11) NOT NULL auto_increment,
zone varchar(16) NOT NULL default '',
x int(11) NOT NULL default '0',
y int(11) NOT NULL default '0',
z int(11) NOT NULL default '0',
chance tinyint NOT NULL default '0',
maxzdiff float NOT NULL default '0',
radius float NOT NULL default '0',
effect int(11) NOT NULL default '0',
effectvalue int(11) NOT NULL default '0',
effectvalue2 int(11) NOT NULL default '0',
message varcahr(200) NOT NULL;
skill int(11) NOT NULL default '0',
spawnchance int(11) NOT NULL default '0',
PRIMARY KEY (id)
) TYPE=MyISAM;

View File

@ -1046,6 +1046,7 @@ const NPCType* ZoneDatabase::GetNPCType (uint32 id) {
"npc_types.hp_regen_rate,"
"npc_types.mana_regen_rate,"
"npc_types.aggroradius,"
"npc_types.assistradius,"
"npc_types.bodytype,"
"npc_types.npc_faction_id,"
"npc_types.face,"
@ -1145,6 +1146,9 @@ const NPCType* ZoneDatabase::GetNPCType (uint32 id) {
// set defaultvalue for aggroradius
if (tmpNPCType->aggroradius <= 0)
tmpNPCType->aggroradius = 70;
tmpNPCType->assistradius = (int32)atoi(row[r++]);
if (tmpNPCType->assistradius <= 0)
tmpNPCType->assistradius = tmpNPCType->aggroradius;
if (row[r] && strlen(row[r]))
tmpNPCType->bodytype = (uint8)atoi(row[r]);

View File

@ -97,6 +97,7 @@ struct NPCType
int32 hp_regen;
int32 mana_regen;
int32 aggroradius; // added for AI improvement - neotokyo
int32 assistradius; // assist radius, defaults to aggroradis if not set
uint8 see_invis; // See Invis flag added
bool see_invis_undead; // See Invis vs. Undead flag added
bool see_hide;