mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-16 05:11:29 +00:00
-Implemented live like spell projectiles (ie mage bolts).
-See function in spells.cpp for more info on bolt behavior. -This works reasonably well, but still room for improvements. -Rules are for setting what item id is used for the projectile since live uses an item id from SOF+ I added alternate item graphic for titanium clients. -Note: Max number of projectiles (set at 10) is a made up value in most situations it would be nearly impossible to have more than 3 bolts in the air at the same time. This values gives enough wiggle room that no server should have an issue though. -Small fix to SE_CompleteHeal
This commit is contained in:
parent
bb541eeb60
commit
2cdd50b9e9
@ -1,5 +1,11 @@
|
|||||||
EQEMu Changelog (Started on Sept 24, 2003 15:50)
|
EQEMu Changelog (Started on Sept 24, 2003 15:50)
|
||||||
-------------------------------------------------------
|
-------------------------------------------------------
|
||||||
|
== 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 )
|
||||||
|
|||||||
@ -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, '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, 'FRProjectileItem_NPC', '80684', 'Item id for Titanium clients for Fire spell projectile.');
|
||||||
@ -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())
|
||||||
|
|||||||
51
zone/mob.cpp
51
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));
|
||||||
@ -4358,6 +4366,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;
|
||||||
|
|||||||
@ -252,6 +252,7 @@ public:
|
|||||||
void SendBuffsToClient(Client *c);
|
void SendBuffsToClient(Client *c);
|
||||||
inline Buffs_Struct* GetBuffs() { return buffs; }
|
inline Buffs_Struct* GetBuffs() { return buffs; }
|
||||||
void DoGravityEffect();
|
void DoGravityEffect();
|
||||||
|
void SpellProjectileEffect();
|
||||||
void DamageShield(Mob* other, bool spell_ds = false);
|
void DamageShield(Mob* other, bool spell_ds = false);
|
||||||
int32 RuneAbsorb(int32 damage, uint16 type);
|
int32 RuneAbsorb(int32 damage, uint16 type);
|
||||||
bool FindBuff(uint16 spellid);
|
bool FindBuff(uint16 spellid);
|
||||||
@ -1040,6 +1041,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;
|
||||||
|
|||||||
@ -659,6 +659,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();
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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);
|
||||||
@ -1868,6 +1868,70 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*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 && spells[spell_id].targettype == ST_TargetOptional){
|
||||||
|
|
||||||
|
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);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Switch #2 - execute the spell
|
// Switch #2 - execute the spell
|
||||||
//
|
//
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user