mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-13 06:21:28 +00:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
4d70cb20e7
@ -1,5 +1,37 @@
|
|||||||
EQEMu Changelog (Started on Sept 24, 2003 15:50)
|
EQEMu Changelog (Started on Sept 24, 2003 15:50)
|
||||||
-------------------------------------------------------
|
-------------------------------------------------------
|
||||||
|
== 04/06/2014 ==
|
||||||
|
Uleat: Changed Mob::CanThisClassDualWield() behavior. This should let non-monk/beastlord dual-wielding classes attack with either fist as long as the other hand is occupied.
|
||||||
|
Notes:
|
||||||
|
See this thread for more information and to provide feedback: http://www.eqemulator.org/forums/showthread.php?p=229328#post229328
|
||||||
|
|
||||||
|
|
||||||
|
== 04/05/2014 ==
|
||||||
|
Akkadius: Fix for the Fix for the Fix: Rule Combat:OneProcPerWeapon was created so that you can revert to the original proc functionality
|
||||||
|
for custom servers that have balanced their content around having more than 1 aug proc on weapons. By having this rule set to 'false' you revert this functionality.
|
||||||
|
This rule is set to 'true' by default as the original functionality from Live was intended to be
|
||||||
|
Akkadius: (Performance Adjustment) Removed AsyncLoadVariables from InterserverTimer.Check() in both zone and world. By watching the MySQL general.log file on mass zone idle activity, you can
|
||||||
|
see that the query 'SELECT varname, value, unix_timestamp() FROM variables where unix_timestamp(ts) >= timestamp' is called every 10 seconds. This function is loading
|
||||||
|
variables that are initially loaded on World and Zone bootup. When running a large amount of zone servers, the amount of MySQL chatter that is produced is enormous and
|
||||||
|
unnecessary. For example, if I ran 400 zone servers, I would see 3,456,000 unnecessary queries from all idle or active zone processes in a 24 hour interval.
|
||||||
|
Secrets: Added a rule to enable multiple procs from the same weapon's other slots if a proc is deemed to trigger, Defaults to true.
|
||||||
|
If Combat:OneProcPerWeapon is not enabled, we reset the try for that weapon regardless of if we procced or not.
|
||||||
|
This is for some servers that may want to have as many procs triggering from weapons as possible in a single round.
|
||||||
|
|
||||||
|
Optional SQL: utils/sql/git/optional/2014_04_05_ProcRules.sql
|
||||||
|
|
||||||
|
== 04/04/2014 ==
|
||||||
|
Kayen: Implemented 'Physical Resists' (Resist Type 9) to be consistent with live based on extensive parsing.
|
||||||
|
SQL will add new field to npc_types 'PhR' and fill in database with values consistent with observations.
|
||||||
|
|
||||||
|
Required SQL: utils/sql/git/optional/2014_04_04_PhysicalResists.sql
|
||||||
|
|
||||||
|
== 04/03/2014 ==
|
||||||
|
Kayen: Implemented live like spell projectiles (ie. Mage Bolts).
|
||||||
|
|
||||||
|
Optional SQL: utils/sql/git/optional/2014_04_03_SpellProjectileRules.sql
|
||||||
|
Note: The rules in this SQL are for setting the item id for the graphic used by the projectile on different clients.
|
||||||
|
|
||||||
== 04/01/2014 ==
|
== 04/01/2014 ==
|
||||||
demonstar55: Implemented ability for a merchant to open and close shop.
|
demonstar55: Implemented ability for a merchant to open and close shop.
|
||||||
Lua quest functions: e.self:MerchantOpenShop() and e.self:MerchantCloseShop()
|
Lua quest functions: e.self:MerchantOpenShop() and e.self:MerchantCloseShop()
|
||||||
|
|||||||
@ -304,6 +304,9 @@ RULE_BOOL ( Spells, BuffLevelRestrictions, true) //Buffs will not land on low le
|
|||||||
RULE_INT ( Spells, RootBreakCheckChance, 70) //Determines chance for a root break check to occur each buff tick.
|
RULE_INT ( Spells, RootBreakCheckChance, 70) //Determines chance for a root break check to occur each buff tick.
|
||||||
RULE_INT ( Spells, FearBreakCheckChance, 70) //Determines chance for a fear break check to occur each buff tick.
|
RULE_INT ( Spells, FearBreakCheckChance, 70) //Determines chance for a fear break check to occur each buff tick.
|
||||||
RULE_INT ( Spells, SuccorFailChance, 2) //Determines chance for a succor spell not to teleport an invidual player
|
RULE_INT ( Spells, SuccorFailChance, 2) //Determines chance for a succor spell not to teleport an invidual player
|
||||||
|
RULE_INT ( Spells, FRProjectileItem_Titanium, 1113) // Item id for Titanium clients for Fire 'spell projectile'.
|
||||||
|
RULE_INT ( Spells, FRProjectileItem_SOF, 80684) // Item id for SOF clients for Fire 'spell projectile'.
|
||||||
|
RULE_INT ( Spells, FRProjectileItem_NPC, 80684) // Item id for NPC Fire 'spell projectile'.
|
||||||
RULE_CATEGORY_END()
|
RULE_CATEGORY_END()
|
||||||
|
|
||||||
RULE_CATEGORY( Combat )
|
RULE_CATEGORY( Combat )
|
||||||
@ -393,6 +396,7 @@ RULE_BOOL ( Combat, UseArcheryBonusRoll, false) //Make the 51+ archery bonus req
|
|||||||
RULE_INT ( Combat, ArcheryBonusChance, 50)
|
RULE_INT ( Combat, ArcheryBonusChance, 50)
|
||||||
RULE_INT ( Combat, BerserkerFrenzyStart, 35)
|
RULE_INT ( Combat, BerserkerFrenzyStart, 35)
|
||||||
RULE_INT ( Combat, BerserkerFrenzyEnd, 45)
|
RULE_INT ( Combat, BerserkerFrenzyEnd, 45)
|
||||||
|
RULE_BOOL ( Combat, OneProcPerWeapon, true) //If enabled, One proc per weapon per round
|
||||||
RULE_CATEGORY_END()
|
RULE_CATEGORY_END()
|
||||||
|
|
||||||
RULE_CATEGORY( NPC )
|
RULE_CATEGORY( NPC )
|
||||||
|
|||||||
@ -258,7 +258,7 @@ typedef enum {
|
|||||||
#define SE_Familiar 108 // implemented
|
#define SE_Familiar 108 // implemented
|
||||||
#define SE_SummonItemIntoBag 109 // implemented - summons stuff into container
|
#define SE_SummonItemIntoBag 109 // implemented - summons stuff into container
|
||||||
//#define SE_IncreaseArchery 110 // not used
|
//#define SE_IncreaseArchery 110 // not used
|
||||||
#define SE_ResistAll 111 // implemented
|
#define SE_ResistAll 111 // implemented - Note: Physical Resists are not modified by this effect.
|
||||||
#define SE_CastingLevel 112 // implemented
|
#define SE_CastingLevel 112 // implemented
|
||||||
#define SE_SummonHorse 113 // implemented
|
#define SE_SummonHorse 113 // implemented
|
||||||
#define SE_ChangeAggro 114 // implemented - Hate modifing buffs(ie horrifying visage)
|
#define SE_ChangeAggro 114 // implemented - Hate modifing buffs(ie horrifying visage)
|
||||||
@ -270,7 +270,7 @@ typedef enum {
|
|||||||
#define SE_HealRate 120 // implemented - reduces healing by a %
|
#define SE_HealRate 120 // implemented - reduces healing by a %
|
||||||
#define SE_ReverseDS 121 // implemented
|
#define SE_ReverseDS 121 // implemented
|
||||||
//#define SE_ReduceSkill 122 // not used
|
//#define SE_ReduceSkill 122 // not used
|
||||||
#define SE_Screech 123 // implemented? Spell Blocker(can only have one buff with this effect at one time)
|
#define SE_Screech 123 // implemented Spell Blocker(If have buff with value +1 will block any effect with -1)
|
||||||
#define SE_ImprovedDamage 124 // implemented
|
#define SE_ImprovedDamage 124 // implemented
|
||||||
#define SE_ImprovedHeal 125 // implemented
|
#define SE_ImprovedHeal 125 // implemented
|
||||||
#define SE_SpellResistReduction 126 // implemented
|
#define SE_SpellResistReduction 126 // implemented
|
||||||
|
|||||||
@ -0,0 +1,3 @@
|
|||||||
|
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:FRProjectileItem_Titanium', '1113', 'Item id for Titanium clients for Fire spell projectile.');
|
||||||
|
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:FRProjectileItem_SOF', '80684', 'Item id for Titanium clients for Fire spell projectile.');
|
||||||
|
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:FRProjectileItem_NPC', '80684', 'Item id for Titanium clients for Fire spell projectile.');
|
||||||
1
utils/sql/git/optional/2014_04_05_ProcRules.sql
Normal file
1
utils/sql/git/optional/2014_04_05_ProcRules.sql
Normal file
@ -0,0 +1 @@
|
|||||||
|
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Combat:OneProcPerWeapon', 'true', 'If OneProcPerWeapon is not enabled, we reset the proc try for that weapon regardless of if we procced or not.');
|
||||||
7
utils/sql/git/required/2014_04_04_PhysicalResist.sql
Normal file
7
utils/sql/git/required/2014_04_04_PhysicalResist.sql
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
ALTER TABLE `npc_types` ADD `PhR` smallint( 5 ) UNSIGNED NOT NULL DEFAULT '0' AFTER `Corrup`;
|
||||||
|
|
||||||
|
-- Approximate baseline live npc values based on extensive parsing.
|
||||||
|
UPDATE npc_types SET PhR = 10 WHERE PhR = 0 AND level <= 50;
|
||||||
|
UPDATE npc_types SET PhR = (10 + (level - 50)) WHERE PhR = 0 AND (level > 50 AND level <= 60);
|
||||||
|
UPDATE npc_types SET PhR = (20 + ((level - 60)*4)) WHERE PhR = 0 AND level > 60;
|
||||||
|
|
||||||
@ -461,7 +461,7 @@ int main(int argc, char** argv) {
|
|||||||
if (InterserverTimer.Check()) {
|
if (InterserverTimer.Check()) {
|
||||||
InterserverTimer.Start();
|
InterserverTimer.Start();
|
||||||
database.ping();
|
database.ping();
|
||||||
AsyncLoadVariables(dbasync, &database);
|
// AsyncLoadVariables(dbasync, &database);
|
||||||
ReconnectCounter++;
|
ReconnectCounter++;
|
||||||
if (ReconnectCounter >= 12) { // only create thread to reconnect every 10 minutes. previously we were creating a new thread every 10 seconds
|
if (ReconnectCounter >= 12) { // only create thread to reconnect every 10 minutes. previously we were creating a new thread every 10 seconds
|
||||||
ReconnectCounter = 0;
|
ReconnectCounter = 0;
|
||||||
|
|||||||
@ -262,6 +262,7 @@
|
|||||||
#define DISCIPLINE_REUSE_MSG 5807 //You can use the ability %1 again in %2 hour(s) %3 minute(s) %4 seconds.
|
#define DISCIPLINE_REUSE_MSG 5807 //You can use the ability %1 again in %2 hour(s) %3 minute(s) %4 seconds.
|
||||||
#define DISCIPLINE_REUSE_MSG2 5808 //You can use the ability %1 again in %2 minute(s) %3 seconds.
|
#define DISCIPLINE_REUSE_MSG2 5808 //You can use the ability %1 again in %2 minute(s) %3 seconds.
|
||||||
#define FAILED_TAUNT 5811 //You have failed to taunt your target.
|
#define FAILED_TAUNT 5811 //You have failed to taunt your target.
|
||||||
|
#define PHYSICAL_RESIST_FAIL 5817 //Your target avoided your %1 ability.
|
||||||
#define AA_NO_TARGET 5825 //You must first select a target for this ability!
|
#define AA_NO_TARGET 5825 //You must first select a target for this ability!
|
||||||
#define FORAGE_MASTERY 6012 //Your forage mastery has enabled you to find something else!
|
#define FORAGE_MASTERY 6012 //Your forage mastery has enabled you to find something else!
|
||||||
#define GUILD_BANK_CANNOT_DEPOSIT 6097 // Cannot deposit this item. Containers must be empty, and only one of each LORE and no NO TRADE or TEMPORARY items may be deposited.
|
#define GUILD_BANK_CANNOT_DEPOSIT 6097 // Cannot deposit this item. Containers must be empty, and only one of each LORE and no NO TRADE or TEMPORARY items may be deposited.
|
||||||
@ -333,6 +334,7 @@
|
|||||||
#define ALREADY_CASTING 12442 //You are already casting a spell!
|
#define ALREADY_CASTING 12442 //You are already casting a spell!
|
||||||
#define SENSE_CORPSE_NOT_NAME 12446 //You don't sense any corpses of that name.
|
#define SENSE_CORPSE_NOT_NAME 12446 //You don't sense any corpses of that name.
|
||||||
#define SENSE_CORPSE_NONE 12447 //You don't sense any corpses.
|
#define SENSE_CORPSE_NONE 12447 //You don't sense any corpses.
|
||||||
|
#define SCREECH_BUFF_BLOCK 12448 //Your immunity buff protected you from the spell %1!
|
||||||
#define NOT_HOLDING_ITEM 12452 //You are not holding an item!
|
#define NOT_HOLDING_ITEM 12452 //You are not holding an item!
|
||||||
#define SENSE_UNDEAD 12471 //You sense undead in this direction.
|
#define SENSE_UNDEAD 12471 //You sense undead in this direction.
|
||||||
#define SENSE_ANIMAL 12472 //You sense an animal in this direction.
|
#define SENSE_ANIMAL 12472 //You sense an animal in this direction.
|
||||||
|
|||||||
@ -4085,6 +4085,10 @@ void Mob::TryWeaponProc(const ItemInst *inst, const Item_Struct *weapon, Mob *on
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//If OneProcPerWeapon is not enabled, we reset the try for that weapon regardless of if we procced or not.
|
||||||
|
//This is for some servers that may want to have as many procs triggering from weapons as possible in a single round.
|
||||||
|
if(!RuleB(Combat, OneProcPerWeapon))
|
||||||
|
proced = false;
|
||||||
|
|
||||||
if (!proced && inst) {
|
if (!proced && inst) {
|
||||||
for (int r = 0; r < MAX_AUGMENT_SLOTS; r++) {
|
for (int r = 0; r < MAX_AUGMENT_SLOTS; r++) {
|
||||||
@ -4109,7 +4113,8 @@ void Mob::TryWeaponProc(const ItemInst *inst, const Item_Struct *weapon, Mob *on
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ExecWeaponProc(aug_i, aug->Proc.Effect, on);
|
ExecWeaponProc(aug_i, aug->Proc.Effect, on);
|
||||||
break;
|
if (RuleB(Combat, OneProcPerWeapon))
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2588,6 +2588,12 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
|
|||||||
|
|
||||||
case SE_NegateIfCombat:
|
case SE_NegateIfCombat:
|
||||||
newbon->NegateIfCombat = true;
|
newbon->NegateIfCombat = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SE_Screech:
|
||||||
|
newbon->Screech = effect_value;
|
||||||
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -570,6 +570,9 @@ bool Client::Process() {
|
|||||||
viral_timer_counter = 0;
|
viral_timer_counter = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(projectile_timer.Check())
|
||||||
|
SpellProjectileEffect();
|
||||||
|
|
||||||
if(spellbonuses.GravityEffect == 1) {
|
if(spellbonuses.GravityEffect == 1) {
|
||||||
if(gravity_timer.Check())
|
if(gravity_timer.Check())
|
||||||
DoGravityEffect();
|
DoGravityEffect();
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
#include "../common/spdat.h"
|
#include "../common/spdat.h"
|
||||||
|
|
||||||
#define HIGHEST_RESIST 9 //Max resist type value
|
#define HIGHEST_RESIST 9 //Max resist type value
|
||||||
|
#define MAX_SPELL_PROJECTILE 10 //Max amount of spell projectiles that can be active by a single mob.
|
||||||
|
|
||||||
/* solar: macros for IsAttackAllowed, IsBeneficialAllowed */
|
/* solar: macros for IsAttackAllowed, IsBeneficialAllowed */
|
||||||
#define _CLIENT(x) (x && x->IsClient() && !x->CastToClient()->IsBecomeNPC())
|
#define _CLIENT(x) (x && x->IsClient() && !x->CastToClient()->IsBecomeNPC())
|
||||||
@ -345,6 +346,7 @@ struct StatBonuses {
|
|||||||
uint16 AbsorbMagicAtt[2]; // 0 = magic rune value 1 = buff slot
|
uint16 AbsorbMagicAtt[2]; // 0 = magic rune value 1 = buff slot
|
||||||
uint16 MeleeRune[2]; // 0 = rune value 1 = buff slot
|
uint16 MeleeRune[2]; // 0 = rune value 1 = buff slot
|
||||||
bool NegateIfCombat; // Bool Drop buff if cast or melee
|
bool NegateIfCombat; // Bool Drop buff if cast or melee
|
||||||
|
int8 Screech; // -1 = Will be blocked if another Screech is +(1)
|
||||||
|
|
||||||
// AAs
|
// AAs
|
||||||
int8 Packrat; //weight reduction for items, 1 point = 10%
|
int8 Packrat; //weight reduction for items, 1 point = 10%
|
||||||
|
|||||||
@ -3415,9 +3415,14 @@ void EntityList::ReloadAllClientsTaskState(int TaskID)
|
|||||||
|
|
||||||
bool EntityList::IsMobInZone(Mob *who)
|
bool EntityList::IsMobInZone(Mob *who)
|
||||||
{
|
{
|
||||||
auto it = mob_list.find(who->GetID());
|
//We don't use mob_list.find(who) because this code needs to be able to handle dangling pointers for the quest code.
|
||||||
if (it != mob_list.end())
|
auto it = mob_list.begin();
|
||||||
return who == it->second;
|
while(it != mob_list.end()) {
|
||||||
|
if(it->second == who) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
++it;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
89
zone/mob.cpp
89
zone/mob.cpp
@ -276,6 +276,14 @@ Mob::Mob(const char* in_name,
|
|||||||
casting_spell_inventory_slot = 0;
|
casting_spell_inventory_slot = 0;
|
||||||
target = 0;
|
target = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) { projectile_spell_id[i] = 0; }
|
||||||
|
for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) { projectile_target_id[i] = 0; }
|
||||||
|
for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) { projectile_increment[i] = 0; }
|
||||||
|
for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) { projectile_x[i] = 0; }
|
||||||
|
for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) { projectile_y[i] = 0; }
|
||||||
|
for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) { projectile_z[i] = 0; }
|
||||||
|
projectile_timer.Disable();
|
||||||
|
|
||||||
memset(&itembonuses, 0, sizeof(StatBonuses));
|
memset(&itembonuses, 0, sizeof(StatBonuses));
|
||||||
memset(&spellbonuses, 0, sizeof(StatBonuses));
|
memset(&spellbonuses, 0, sizeof(StatBonuses));
|
||||||
memset(&aabonuses, 0, sizeof(StatBonuses));
|
memset(&aabonuses, 0, sizeof(StatBonuses));
|
||||||
@ -2080,27 +2088,35 @@ void Mob::SetAttackTimer() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Mob::CanThisClassDualWield(void) const
|
bool Mob::CanThisClassDualWield(void) const {
|
||||||
{
|
if(!IsClient()) {
|
||||||
if (!IsClient()) {
|
|
||||||
return(GetSkill(SkillDualWield) > 0);
|
return(GetSkill(SkillDualWield) > 0);
|
||||||
} else {
|
}
|
||||||
const ItemInst* inst = CastToClient()->GetInv().GetItem(SLOT_PRIMARY);
|
else if(CastToClient()->HasSkill(SkillDualWield)) {
|
||||||
|
const ItemInst* pinst = CastToClient()->GetInv().GetItem(SLOT_PRIMARY);
|
||||||
|
const ItemInst* sinst = CastToClient()->GetInv().GetItem(SLOT_SECONDARY);
|
||||||
|
|
||||||
// 2HS, 2HB, or 2HP
|
// 2HS, 2HB, or 2HP
|
||||||
if (inst && inst->IsType(ItemClassCommon)) {
|
if(pinst && pinst->IsWeapon()) {
|
||||||
const Item_Struct* item = inst->GetItem();
|
const Item_Struct* item = pinst->GetItem();
|
||||||
if ((item->ItemType == ItemType2HBlunt) || (item->ItemType == ItemType2HSlash) || (item->ItemType == ItemType2HPiercing))
|
|
||||||
|
if((item->ItemType == ItemType2HBlunt) || (item->ItemType == ItemType2HSlash) || (item->ItemType == ItemType2HPiercing))
|
||||||
return false;
|
return false;
|
||||||
} else {
|
|
||||||
//No weapon in hand... using hand-to-hand...
|
|
||||||
//only monks and beastlords? can dual wield their fists.
|
|
||||||
if(class_ != MONK && class_ != MONKGM && class_ != BEASTLORD && class_ != BEASTLORDGM) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (CastToClient()->HasSkill(SkillDualWield)); // No skill = no chance
|
// OffHand Weapon
|
||||||
|
if(sinst && !sinst->IsWeapon())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Dual-Wielding Empty Fists
|
||||||
|
if(!pinst && !sinst)
|
||||||
|
if(class_ != MONK && class_ != MONKGM && class_ != BEASTLORD && class_ != BEASTLORDGM)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Mob::CanThisClassDoubleAttack(void) const
|
bool Mob::CanThisClassDoubleAttack(void) const
|
||||||
@ -4358,6 +4374,49 @@ bool Mob::TryReflectSpell(uint32 spell_id)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Mob::SpellProjectileEffect()
|
||||||
|
{
|
||||||
|
bool time_disable = false;
|
||||||
|
|
||||||
|
for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) {
|
||||||
|
|
||||||
|
if (projectile_increment[i] == 0){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Mob* target = entity_list.GetMobID(projectile_target_id[i]);
|
||||||
|
|
||||||
|
float dist = 0;
|
||||||
|
|
||||||
|
if (target)
|
||||||
|
dist = target->CalculateDistance(projectile_x[i], projectile_y[i], projectile_z[i]);
|
||||||
|
|
||||||
|
int increment_end = 0;
|
||||||
|
increment_end = (dist / 10) - 1; //This pretty accurately determines end time for speed for 1.5 and timer of 250 ms
|
||||||
|
|
||||||
|
if (increment_end <= projectile_increment[i]){
|
||||||
|
|
||||||
|
if (target && IsValidSpell(projectile_spell_id[i]))
|
||||||
|
SpellOnTarget(projectile_spell_id[i], target, false, true, spells[projectile_spell_id[i]].ResistDiff, true);
|
||||||
|
|
||||||
|
projectile_spell_id[i] = 0;
|
||||||
|
projectile_target_id[i] = 0;
|
||||||
|
projectile_x[i] = 0, projectile_y[i] = 0, projectile_z[i] = 0;
|
||||||
|
projectile_increment[i] = 0;
|
||||||
|
time_disable = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
|
projectile_increment[i]++;
|
||||||
|
time_disable = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (time_disable)
|
||||||
|
projectile_timer.Disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void Mob::DoGravityEffect()
|
void Mob::DoGravityEffect()
|
||||||
{
|
{
|
||||||
Mob *caster = nullptr;
|
Mob *caster = nullptr;
|
||||||
|
|||||||
11
zone/mob.h
11
zone/mob.h
@ -192,6 +192,7 @@ public:
|
|||||||
virtual int32 GetActSpellCasttime(uint16 spell_id, int32 casttime);
|
virtual int32 GetActSpellCasttime(uint16 spell_id, int32 casttime);
|
||||||
float ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use_resist_override = false,
|
float ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use_resist_override = false,
|
||||||
int resist_override = 0, bool CharismaCheck = false, bool CharmTick = false, bool IsRoot = false);
|
int resist_override = 0, bool CharismaCheck = false, bool CharmTick = false, bool IsRoot = false);
|
||||||
|
int ResistPhysical(int level_diff, uint8 caster_level);
|
||||||
uint16 GetSpecializeSkillValue(uint16 spell_id) const;
|
uint16 GetSpecializeSkillValue(uint16 spell_id) const;
|
||||||
void SendSpellBarDisable();
|
void SendSpellBarDisable();
|
||||||
void SendSpellBarEnable(uint16 spellid);
|
void SendSpellBarEnable(uint16 spellid);
|
||||||
@ -222,6 +223,8 @@ public:
|
|||||||
uint16 CastingSpellID() const { return casting_spell_id; }
|
uint16 CastingSpellID() const { return casting_spell_id; }
|
||||||
bool DoCastingChecks();
|
bool DoCastingChecks();
|
||||||
bool TryDispel(uint8 caster_level, uint8 buff_level, int level_modifier);
|
bool TryDispel(uint8 caster_level, uint8 buff_level, int level_modifier);
|
||||||
|
void SpellProjectileEffect();
|
||||||
|
bool TrySpellProjectile(Mob* spell_target, uint16 spell_id);
|
||||||
|
|
||||||
//Buff
|
//Buff
|
||||||
void BuffProcess();
|
void BuffProcess();
|
||||||
@ -337,6 +340,7 @@ public:
|
|||||||
inline virtual int16 GetPR() const { return PR + itembonuses.PR + spellbonuses.PR; }
|
inline virtual int16 GetPR() const { return PR + itembonuses.PR + spellbonuses.PR; }
|
||||||
inline virtual int16 GetCR() const { return CR + itembonuses.CR + spellbonuses.CR; }
|
inline virtual int16 GetCR() const { return CR + itembonuses.CR + spellbonuses.CR; }
|
||||||
inline virtual int16 GetCorrup() const { return Corrup + itembonuses.Corrup + spellbonuses.Corrup; }
|
inline virtual int16 GetCorrup() const { return Corrup + itembonuses.Corrup + spellbonuses.Corrup; }
|
||||||
|
inline virtual int16 GetPhR() const { return PhR; }
|
||||||
inline StatBonuses GetItemBonuses() const { return itembonuses; }
|
inline StatBonuses GetItemBonuses() const { return itembonuses; }
|
||||||
inline StatBonuses GetSpellBonuses() const { return spellbonuses; }
|
inline StatBonuses GetSpellBonuses() const { return spellbonuses; }
|
||||||
inline StatBonuses GetAABonuses() const { return aabonuses; }
|
inline StatBonuses GetAABonuses() const { return aabonuses; }
|
||||||
@ -915,6 +919,7 @@ protected:
|
|||||||
int16 DR;
|
int16 DR;
|
||||||
int16 PR;
|
int16 PR;
|
||||||
int16 Corrup;
|
int16 Corrup;
|
||||||
|
int16 PhR;
|
||||||
bool moving;
|
bool moving;
|
||||||
int targeted;
|
int targeted;
|
||||||
bool findable;
|
bool findable;
|
||||||
@ -1040,6 +1045,12 @@ protected:
|
|||||||
uint8 bardsong_slot;
|
uint8 bardsong_slot;
|
||||||
uint32 bardsong_target_id;
|
uint32 bardsong_target_id;
|
||||||
|
|
||||||
|
Timer projectile_timer;
|
||||||
|
uint32 projectile_spell_id[MAX_SPELL_PROJECTILE];
|
||||||
|
uint16 projectile_target_id[MAX_SPELL_PROJECTILE];
|
||||||
|
uint8 projectile_increment[MAX_SPELL_PROJECTILE];
|
||||||
|
float projectile_x[MAX_SPELL_PROJECTILE], projectile_y[MAX_SPELL_PROJECTILE], projectile_z[MAX_SPELL_PROJECTILE];
|
||||||
|
|
||||||
float rewind_x;
|
float rewind_x;
|
||||||
float rewind_y;
|
float rewind_y;
|
||||||
float rewind_z;
|
float rewind_z;
|
||||||
|
|||||||
@ -447,7 +447,7 @@ int main(int argc, char** argv) {
|
|||||||
if (InterserverTimer.Check()) {
|
if (InterserverTimer.Check()) {
|
||||||
InterserverTimer.Start();
|
InterserverTimer.Start();
|
||||||
database.ping();
|
database.ping();
|
||||||
AsyncLoadVariables(dbasync, &database);
|
// AsyncLoadVariables(dbasync, &database);
|
||||||
entity_list.UpdateWho();
|
entity_list.UpdateWho();
|
||||||
if (worldserver.TryReconnect() && (!worldserver.Connected()))
|
if (worldserver.TryReconnect() && (!worldserver.Connected()))
|
||||||
worldserver.AsyncConnect();
|
worldserver.AsyncConnect();
|
||||||
|
|||||||
@ -158,6 +158,7 @@ NPC::NPC(const NPCType* d, Spawn2* in_respawn, float x, float y, float z, float
|
|||||||
FR = d->FR;
|
FR = d->FR;
|
||||||
PR = d->PR;
|
PR = d->PR;
|
||||||
Corrup = d->Corrup;
|
Corrup = d->Corrup;
|
||||||
|
PhR = d->PhR;
|
||||||
|
|
||||||
STR = d->STR;
|
STR = d->STR;
|
||||||
STA = d->STA;
|
STA = d->STA;
|
||||||
@ -659,6 +660,9 @@ bool NPC::Process()
|
|||||||
viral_timer_counter = 0;
|
viral_timer_counter = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(projectile_timer.Check())
|
||||||
|
SpellProjectileEffect();
|
||||||
|
|
||||||
if(spellbonuses.GravityEffect == 1) {
|
if(spellbonuses.GravityEffect == 1) {
|
||||||
if(gravity_timer.Check())
|
if(gravity_timer.Check())
|
||||||
DoGravityEffect();
|
DoGravityEffect();
|
||||||
@ -2059,6 +2063,8 @@ void NPC::CalcNPCResists() {
|
|||||||
PR = (GetLevel() * 11)/10;
|
PR = (GetLevel() * 11)/10;
|
||||||
if (!Corrup)
|
if (!Corrup)
|
||||||
Corrup = 15;
|
Corrup = 15;
|
||||||
|
if (!PhR)
|
||||||
|
PhR = 10;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -326,7 +326,13 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
|
|||||||
if(inuse)
|
if(inuse)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
Heal();
|
int32 val = 0;
|
||||||
|
val = 7500*effect_value;
|
||||||
|
val = caster->GetActSpellHealing(spell_id, val, this);
|
||||||
|
|
||||||
|
if (val > 0)
|
||||||
|
HealDamage(val, caster);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6014,3 +6020,73 @@ bool Mob::PassCastRestriction(bool UseCastRestriction, int16 value, bool IsDama
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Mob::TrySpellProjectile(Mob* spell_target, uint16 spell_id){
|
||||||
|
|
||||||
|
/*For mage 'Bolt' line and other various spells.
|
||||||
|
-This is mostly accurate for how the modern clients handle this effect.
|
||||||
|
-It was changed at some point to use an actual projectile as done here (opposed to a particle effect in classic)
|
||||||
|
-The projectile graphic appears to be that of 'Ball of Sunlight' ID 80648 and will be visible to anyone in SoF+
|
||||||
|
-There is no LOS check to prevent a bolt from being cast. If you don't have LOS your bolt simply goes into whatever barrier
|
||||||
|
and you lose your mana. If there is LOS the bolt will lock onto your target and the damage is applied when it hits the target.
|
||||||
|
-If your target moves the bolt moves with it in any direction or angle (consistent with other projectiles).
|
||||||
|
-The way this is written once a bolt is cast a timer checks the distance from the initial cast to the target repeatedly
|
||||||
|
and calculates at what predicted time the bolt should hit that target in client_process (therefore accounting for any target movement).
|
||||||
|
When bolt hits its predicted point the damage is then done to target.
|
||||||
|
Note: Projectile speed of 1 takes 3 seconds to go 100 distance units. Calculations are based on this constant.
|
||||||
|
Live Bolt speed: Projectile speed of X takes 5 seconds to go 300 distance units.
|
||||||
|
Pending Implementation: What this code can not do is prevent damage if the bolt hits a barrier after passing the initial LOS check
|
||||||
|
because the target has moved while the bolt is in motion. (it is rare to actual get this to occur on live in normal game play)
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!spell_target)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
uint8 anim = spells[spell_id].CastingAnim;
|
||||||
|
int bolt_id = -1;
|
||||||
|
|
||||||
|
//Make sure there is an avialable bolt to be cast.
|
||||||
|
for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) {
|
||||||
|
if (projectile_spell_id[i] == 0){
|
||||||
|
bolt_id = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bolt_id < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (CheckLosFN(spell_target)) {
|
||||||
|
|
||||||
|
projectile_spell_id[bolt_id] = spell_id;
|
||||||
|
projectile_target_id[bolt_id] = spell_target->GetID();
|
||||||
|
projectile_x[bolt_id] = GetX(), projectile_y[bolt_id] = GetY(), projectile_z[bolt_id] = GetZ();
|
||||||
|
projectile_increment[bolt_id] = 1;
|
||||||
|
projectile_timer.Start(250);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Only use fire graphic for fire spells.
|
||||||
|
if (spells[spell_id].resisttype == RESIST_FIRE) {
|
||||||
|
|
||||||
|
if (IsClient()){
|
||||||
|
if (CastToClient()->GetClientVersionBit() <= 4) //Titanium needs alternate graphic.
|
||||||
|
ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_Titanium)), false, 1.5);
|
||||||
|
else
|
||||||
|
ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_SOF)), false, 1.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_NPC)), false, 1.5);
|
||||||
|
|
||||||
|
if (spells[spell_id].CastingAnim == 64)
|
||||||
|
anim = 44; //Corrects for animation error.
|
||||||
|
}
|
||||||
|
|
||||||
|
//Pending other types of projectile graphics. (They will function but with a default arrow graphic for now)
|
||||||
|
else
|
||||||
|
ProjectileAnimation(spell_target,0, 1, 1.5);
|
||||||
|
|
||||||
|
DoAnim(anim, 0, true, IsClient() ? FilterPCSpells : FilterNPCSpells); //Override the default projectile animation.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
176
zone/spells.cpp
176
zone/spells.cpp
@ -1830,7 +1830,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check line of sight to target if it's a detrimental spell
|
// check line of sight to target if it's a detrimental spell
|
||||||
if(spell_target && IsDetrimentalSpell(spell_id) && !CheckLosFN(spell_target) && !IsHarmonySpell(spell_id))
|
if(spell_target && IsDetrimentalSpell(spell_id) && !CheckLosFN(spell_target) && !IsHarmonySpell(spell_id) && spells[spell_id].targettype != ST_TargetOptional)
|
||||||
{
|
{
|
||||||
mlog(SPELLS__CASTING, "Spell %d: cannot see target %s", spell_target->GetName());
|
mlog(SPELLS__CASTING, "Spell %d: cannot see target %s", spell_target->GetName());
|
||||||
Message_StringID(13,CANT_SEE_TARGET);
|
Message_StringID(13,CANT_SEE_TARGET);
|
||||||
@ -1895,7 +1895,12 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16
|
|||||||
if (isproc) {
|
if (isproc) {
|
||||||
SpellOnTarget(spell_id, spell_target, false, true, resist_adjust, true);
|
SpellOnTarget(spell_id, spell_target, false, true, resist_adjust, true);
|
||||||
} else {
|
} else {
|
||||||
if(!SpellOnTarget(spell_id, spell_target, false, true, resist_adjust, false)) {
|
if (spells[spell_id].targettype == ST_TargetOptional){
|
||||||
|
if (!TrySpellProjectile(spell_target, spell_id))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if(!SpellOnTarget(spell_id, spell_target, false, true, resist_adjust, false)) {
|
||||||
if(IsBuffSpell(spell_id) && IsBeneficialSpell(spell_id)) {
|
if(IsBuffSpell(spell_id) && IsBeneficialSpell(spell_id)) {
|
||||||
// Prevent mana usage/timers being set for beneficial buffs
|
// Prevent mana usage/timers being set for beneficial buffs
|
||||||
if(casting_spell_type == 1)
|
if(casting_spell_type == 1)
|
||||||
@ -1904,6 +1909,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(IsPlayerIllusionSpell(spell_id)
|
if(IsPlayerIllusionSpell(spell_id)
|
||||||
&& IsClient()
|
&& IsClient()
|
||||||
&& CastToClient()->CheckAAEffect(aaEffectProjectIllusion)){
|
&& CastToClient()->CheckAAEffect(aaEffectProjectIllusion)){
|
||||||
@ -2607,6 +2613,14 @@ int Mob::CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2,
|
|||||||
{
|
{
|
||||||
effect1 = sp1.effectid[i];
|
effect1 = sp1.effectid[i];
|
||||||
effect2 = sp2.effectid[i];
|
effect2 = sp2.effectid[i];
|
||||||
|
|
||||||
|
if (spellbonuses.Screech == 1) {
|
||||||
|
if (effect2 == SE_Screech && sp2.base[i] == -1) {
|
||||||
|
Message_StringID(MT_SpellFailure, SCREECH_BUFF_BLOCK, sp2.name);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(effect2 == SE_StackingCommand_Overwrite)
|
if(effect2 == SE_StackingCommand_Overwrite)
|
||||||
{
|
{
|
||||||
overwrite_effect = sp2.base[i];
|
overwrite_effect = sp2.base[i];
|
||||||
@ -3440,8 +3454,15 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect, bool use_r
|
|||||||
if(spell_effectiveness == 0 || !IsPartialCapableSpell(spell_id) )
|
if(spell_effectiveness == 0 || !IsPartialCapableSpell(spell_id) )
|
||||||
{
|
{
|
||||||
mlog(SPELLS__RESISTS, "Spell %d was completely resisted by %s", spell_id, spelltar->GetName());
|
mlog(SPELLS__RESISTS, "Spell %d was completely resisted by %s", spell_id, spelltar->GetName());
|
||||||
Message_StringID(MT_SpellFailure, TARGET_RESISTED, spells[spell_id].name);
|
|
||||||
spelltar->Message_StringID(MT_SpellFailure, YOU_RESIST, spells[spell_id].name);
|
if (spells[spell_id].resisttype == RESIST_PHYSICAL){
|
||||||
|
Message_StringID(MT_SpellFailure, PHYSICAL_RESIST_FAIL,spells[spell_id].name);
|
||||||
|
spelltar->Message_StringID(MT_SpellFailure, YOU_RESIST, spells[spell_id].name);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Message_StringID(MT_SpellFailure, TARGET_RESISTED, spells[spell_id].name);
|
||||||
|
spelltar->Message_StringID(MT_SpellFailure, YOU_RESIST, spells[spell_id].name);
|
||||||
|
}
|
||||||
|
|
||||||
if(spelltar->IsAIControlled()){
|
if(spelltar->IsAIControlled()){
|
||||||
int32 aggro = CheckAggroAmount(spell_id);
|
int32 aggro = CheckAggroAmount(spell_id);
|
||||||
@ -4190,67 +4211,83 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case RESIST_PHYSICAL:
|
case RESIST_PHYSICAL:
|
||||||
|
{
|
||||||
|
if (IsNPC())
|
||||||
|
target_resist = GetPhR();
|
||||||
|
else
|
||||||
|
target_resist = 0;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
//This is guessed but the others are right
|
|
||||||
target_resist = (GetSTA() / 4);
|
target_resist = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Setup our base resist chance.
|
//Setup our base resist chance.
|
||||||
int resist_chance = 0;
|
int resist_chance = 0;
|
||||||
|
int level_mod = 0;
|
||||||
|
|
||||||
//Adjust our resist chance based on level modifiers
|
//Adjust our resist chance based on level modifiers
|
||||||
int temp_level_diff = GetLevel() - caster->GetLevel();
|
int temp_level_diff = GetLevel() - caster->GetLevel();
|
||||||
if(IsNPC() && GetLevel() >= RuleI(Casting,ResistFalloff))
|
|
||||||
{
|
//Physical Resists are calclated using their own formula derived from extensive parsing.
|
||||||
int a = (RuleI(Casting,ResistFalloff)-1) - caster->GetLevel();
|
if (resist_type == RESIST_PHYSICAL) {
|
||||||
if(a > 0)
|
level_mod = ResistPhysical(temp_level_diff, caster->GetLevel());
|
||||||
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
|
|
||||||
|
if(IsNPC() && GetLevel() >= RuleI(Casting,ResistFalloff))
|
||||||
{
|
{
|
||||||
temp_level_diff = a;
|
int a = (RuleI(Casting,ResistFalloff)-1) - caster->GetLevel();
|
||||||
}
|
if(a > 0)
|
||||||
else
|
|
||||||
{
|
|
||||||
temp_level_diff = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(IsClient() && GetLevel() >= 21 && temp_level_diff > 15)
|
|
||||||
{
|
|
||||||
temp_level_diff = 15;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
temp_level_diff = a;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
temp_level_diff = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if(IsClient() && GetLevel() >= 21 && temp_level_diff > 15)
|
||||||
{
|
{
|
||||||
level_diff = GetLevel() - caster->GetLevel();
|
temp_level_diff = 15;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(IsNPC() && temp_level_diff < -9)
|
||||||
|
{
|
||||||
|
temp_level_diff = -9;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
//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);
|
||||||
}
|
}
|
||||||
level_mod += (2 * level_diff);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (CharismaCheck)
|
if (CharismaCheck)
|
||||||
@ -4397,6 +4434,43 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Mob::ResistPhysical(int level_diff, uint8 caster_level)
|
||||||
|
{
|
||||||
|
/* Physical resists use the standard level mod calculation in
|
||||||
|
conjunction with a resist fall off formula that greatly prevents you
|
||||||
|
from landing abilities on mobs that are higher level than you.
|
||||||
|
After level 12, every 4 levels gained the max level you can hit
|
||||||
|
your target without a sharp resist penalty is raised by 1.
|
||||||
|
Extensive parsing confirms this, along with baseline phyiscal resist rates used.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
if (level_diff == 0)
|
||||||
|
return level_diff;
|
||||||
|
|
||||||
|
int level_mod = 0;
|
||||||
|
|
||||||
|
if (level_diff > 0) {
|
||||||
|
|
||||||
|
int ResistFallOff = 0;
|
||||||
|
|
||||||
|
if (caster_level <= 12)
|
||||||
|
ResistFallOff = 3;
|
||||||
|
else
|
||||||
|
ResistFallOff = caster_level/4;
|
||||||
|
|
||||||
|
if (level_diff > ResistFallOff || level_diff >= 15)
|
||||||
|
level_mod = ((level_diff * 10) + level_diff)*2;
|
||||||
|
else
|
||||||
|
level_mod = level_diff * level_diff / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
level_mod = -(level_diff * level_diff / 2);
|
||||||
|
|
||||||
|
return level_mod;
|
||||||
|
}
|
||||||
|
|
||||||
int16 Mob::CalcResistChanceBonus()
|
int16 Mob::CalcResistChanceBonus()
|
||||||
{
|
{
|
||||||
int resistchance = spellbonuses.ResistSpellChance + itembonuses.ResistSpellChance;
|
int resistchance = spellbonuses.ResistSpellChance + itembonuses.ResistSpellChance;
|
||||||
|
|||||||
@ -1047,6 +1047,7 @@ const NPCType* ZoneDatabase::GetNPCType (uint32 id) {
|
|||||||
"npc_types.FR,"
|
"npc_types.FR,"
|
||||||
"npc_types.PR,"
|
"npc_types.PR,"
|
||||||
"npc_types.Corrup,"
|
"npc_types.Corrup,"
|
||||||
|
"npc_types.PhR,"
|
||||||
"npc_types.mindmg,"
|
"npc_types.mindmg,"
|
||||||
"npc_types.maxdmg,"
|
"npc_types.maxdmg,"
|
||||||
"npc_types.attack_count,"
|
"npc_types.attack_count,"
|
||||||
@ -1143,6 +1144,7 @@ const NPCType* ZoneDatabase::GetNPCType (uint32 id) {
|
|||||||
tmpNPCType->FR = atoi(row[r++]);
|
tmpNPCType->FR = atoi(row[r++]);
|
||||||
tmpNPCType->PR = atoi(row[r++]);
|
tmpNPCType->PR = atoi(row[r++]);
|
||||||
tmpNPCType->Corrup = atoi(row[r++]);
|
tmpNPCType->Corrup = atoi(row[r++]);
|
||||||
|
tmpNPCType->PhR = atoi(row[r++]);
|
||||||
tmpNPCType->min_dmg = atoi(row[r++]);
|
tmpNPCType->min_dmg = atoi(row[r++]);
|
||||||
tmpNPCType->max_dmg = atoi(row[r++]);
|
tmpNPCType->max_dmg = atoi(row[r++]);
|
||||||
tmpNPCType->attack_count = atoi(row[r++]);
|
tmpNPCType->attack_count = atoi(row[r++]);
|
||||||
|
|||||||
@ -75,6 +75,7 @@ struct NPCType
|
|||||||
int16 PR;
|
int16 PR;
|
||||||
int16 DR;
|
int16 DR;
|
||||||
int16 Corrup;
|
int16 Corrup;
|
||||||
|
int16 PhR;
|
||||||
uint8 haircolor;
|
uint8 haircolor;
|
||||||
uint8 beardcolor;
|
uint8 beardcolor;
|
||||||
uint8 eyecolor1; // the eyecolors always seem to be the same, maybe left and right eye?
|
uint8 eyecolor1; // the eyecolors always seem to be the same, maybe left and right eye?
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user