-Implemented the ability to properly use live spell projectile graphics.

This data is found in the player_1 field of the spells_new table.
-Rule for this set to be disabled by default.
-Enable IF your server uses an UF+ spell file
and your players use UF+ clients
-Otherwise your better off with alternative method/rules
already implemented so that all players can see the effect.
-Added ability for PERL ProjectileAnim function to only
need an IT#### and not an actual item id.
-If you want it in LUA somebody needs to add it.
- Change to wizard innate critical ratios based on parse data.
This commit is contained in:
KayenEQ 2014-04-09 05:17:36 -04:00
parent 8b2f325cd0
commit 35cd98c7a7
9 changed files with 64 additions and 34 deletions

View File

@ -1,10 +1,20 @@
EQEMu Changelog (Started on Sept 24, 2003 15:50) EQEMu Changelog (Started on Sept 24, 2003 15:50)
------------------------------------------------------- -------------------------------------------------------
== 04/09/2014 ==
Kayen: Implemented ability to use the actual live spell projectile graphics that are defined in the modern spell file.
*This is disabled by default. Recommend enabling if your server uses an UF+ spell file AND most of your players use UF+ clients.
Kayen: Expanded the PERL ProjectileAnim(mob, item_id, [IsArrow?, speed, angle, tilt, arc, IDFile]) so you can now just set the weapon model graphic IT####
Example: ProjectileAnim($npc, 0, 0, 1, 0, 0, 0, "IT10747") This will shoot an SK 2.0 sword.
Kayen: Updated wizard innate critical damage modifier to be from 20-70% of base damage (based on live parses)
Optional SQL: utils/sql/git/optional/2014_04_09_SpellProjectileRule.sql
Note: This sql also contains a query to check if your spell file is compatible.
== 04/06/2014 == == 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. 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: Notes:
See this thread for more information and to provide feedback: http://www.eqemulator.org/forums/showthread.php?p=229328#post229328 See this thread for more information and to provide feedback: http://www.eqemulator.org/forums/showthread.php?p=229328#post229328
== 04/05/2014 == == 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 Akkadius: Fix for the Fix for the Fix: Rule Combat:OneProcPerWeapon was created so that you can revert to the original proc functionality

View File

@ -307,6 +307,7 @@ RULE_INT ( Spells, SuccorFailChance, 2) //Determines chance for a succor spell n
RULE_INT ( Spells, FRProjectileItem_Titanium, 1113) // Item id for Titanium clients for Fire 'spell projectile'. 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_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_INT ( Spells, FRProjectileItem_NPC, 80684) // Item id for NPC Fire 'spell projectile'.
RULE_BOOL ( Spells, UseLiveSpellProjectileGFX, false) // Use spell projectile graphics set in the spells_new table (player_1). Server must be using UF+ spell file.
RULE_CATEGORY_END() RULE_CATEGORY_END()
RULE_CATEGORY( Combat ) RULE_CATEGORY( Combat )

View File

@ -1,3 +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_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_SOF', '80684', 'Item id for SOF 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.'); INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:FRProjectileItem_NPC', '80684', 'Item id for NPC to use for Fire spell projectile.');

View File

@ -0,0 +1,7 @@
-- Recommend enabling if your server uses an UF+ spell file and your players use UF+ client. This will give the proper graphics for all spell projectiles.
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:UseLiveSpellProjectileGFX', false, ' Use spell projectile graphics set in the spells_new table (player_1). Server must be using UF+ spell file.');
-- Use this query to check if your spell file is compatible
-- If it returns in the player_1 field IT##### it will work.
SELECT id,name,player_1 from spells_new WHERE targettype = 1;

View File

@ -79,7 +79,7 @@ int32 Client::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
if (spell_id == SPELL_IMP_HARM_TOUCH) //Improved Harm Touch if (spell_id == SPELL_IMP_HARM_TOUCH) //Improved Harm Touch
value -= GetAA(aaUnholyTouch) * 450; //Unholy Touch value -= GetAA(aaUnholyTouch) * 450; //Unholy Touch
int chance = RuleI(Spells, BaseCritChance); int chance = RuleI(Spells, BaseCritChance); //Wizard base critical chance is 2% (Does not scale with level)
chance += itembonuses.CriticalSpellChance + spellbonuses.CriticalSpellChance + aabonuses.CriticalSpellChance; chance += itembonuses.CriticalSpellChance + spellbonuses.CriticalSpellChance + aabonuses.CriticalSpellChance;
chance += itembonuses.FrenziedDevastation + spellbonuses.FrenziedDevastation + aabonuses.FrenziedDevastation; chance += itembonuses.FrenziedDevastation + spellbonuses.FrenziedDevastation + aabonuses.FrenziedDevastation;
@ -99,7 +99,7 @@ int32 Client::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
} }
else if (GetClass() == WIZARD && (GetLevel() >= RuleI(Spells, WizCritLevel)) && (MakeRandomInt(1,100) <= RuleI(Spells, WizCritChance))) { else if (GetClass() == WIZARD && (GetLevel() >= RuleI(Spells, WizCritLevel)) && (MakeRandomInt(1,100) <= RuleI(Spells, WizCritChance))) {
ratio += MakeRandomInt(1,100); //Wizard innate critical chance is calculated seperately from spell effect and is not a set ratio. ratio += MakeRandomInt(20,70); //Wizard innate critical chance is calculated seperately from spell effect and is not a set ratio. (20-70 is parse confirmed)
Critical = true; Critical = true;
} }

View File

@ -161,7 +161,7 @@ public:
virtual void WearChange(uint8 material_slot, uint16 texture, uint32 color); virtual void WearChange(uint8 material_slot, uint16 texture, uint32 color);
void DoAnim(const int animnum, int type=0, bool ackreq = true, eqFilterType filter = FilterNone); void DoAnim(const int animnum, int type=0, bool ackreq = true, eqFilterType filter = FilterNone);
void ProjectileAnimation(Mob* to, int item_id, bool IsArrow = false, float speed = 0, void ProjectileAnimation(Mob* to, int item_id, bool IsArrow = false, float speed = 0,
float angle = 0, float tilt = 0, float arc = 0); float angle = 0, float tilt = 0, float arc = 0, const char *IDFile = nullptr);
void ChangeSize(float in_size, bool bNoRestriction = false); void ChangeSize(float in_size, bool bNoRestriction = false);
inline uint8 SeeInvisible() const { return see_invis; } inline uint8 SeeInvisible() const { return see_invis; }
inline bool SeeInvisibleUndead() const { return see_invis_undead; } inline bool SeeInvisibleUndead() const { return see_invis_undead; }

View File

@ -6856,7 +6856,7 @@ XS(XS_Mob_ProjectileAnim); /* prototype to pass -Wmissing-prototypes */
XS(XS_Mob_ProjectileAnim) XS(XS_Mob_ProjectileAnim)
{ {
dXSARGS; dXSARGS;
if (items < 3 || items > 8) if (items < 3 || items > 9)
Perl_croak(aTHX_ "Usage: Mob::ProjectileAnim(THIS, mob, item_id, IsArrow?, speed, angle, tilt, arc)"); Perl_croak(aTHX_ "Usage: Mob::ProjectileAnim(THIS, mob, item_id, IsArrow?, speed, angle, tilt, arc)");
{ {
@ -6868,6 +6868,7 @@ XS(XS_Mob_ProjectileAnim)
float angle = 0; float angle = 0;
float tilt = 0; float tilt = 0;
float arc = 0; float arc = 0;
char * IDFile = nullptr;
if (sv_derived_from(ST(0), "Mob")) { if (sv_derived_from(ST(0), "Mob")) {
IV tmp = SvIV((SV*)SvRV(ST(0))); IV tmp = SvIV((SV*)SvRV(ST(0)));
@ -6903,7 +6904,9 @@ XS(XS_Mob_ProjectileAnim)
arc = (float)SvNV(ST(7)); arc = (float)SvNV(ST(7));
} }
THIS->ProjectileAnimation(mob, item_id, IsArrow, speed, angle, tilt, arc); if (items > 8) { IDFile = (char *)SvPV_nolen(ST(8)); }
THIS->ProjectileAnimation(mob, item_id, IsArrow, speed, angle, tilt, arc, IDFile);
} }
XSRETURN_EMPTY; XSRETURN_EMPTY;
@ -8389,7 +8392,7 @@ XS(boot_Mob)
newXSproto(strcpy(buf, "CheckLoS"), XS_Mob_CheckLoS, file, "$$"); newXSproto(strcpy(buf, "CheckLoS"), XS_Mob_CheckLoS, file, "$$");
newXSproto(strcpy(buf, "CheckLoSToLoc"), XS_Mob_CheckLoSToLoc, file, "$$$$;$"); newXSproto(strcpy(buf, "CheckLoSToLoc"), XS_Mob_CheckLoSToLoc, file, "$$$$;$");
newXSproto(strcpy(buf, "FindGroundZ"), XS_Mob_FindGroundZ, file, "$$$;$"); newXSproto(strcpy(buf, "FindGroundZ"), XS_Mob_FindGroundZ, file, "$$$;$");
newXSproto(strcpy(buf, "ProjectileAnim"), XS_Mob_ProjectileAnim, file, "$$$;$$$$$"); newXSproto(strcpy(buf, "ProjectileAnim"), XS_Mob_ProjectileAnim, file, "$$$;$$$$$$");
newXSproto(strcpy(buf, "HasNPCSpecialAtk"), XS_Mob_HasNPCSpecialAtk, file, "$$"); newXSproto(strcpy(buf, "HasNPCSpecialAtk"), XS_Mob_HasNPCSpecialAtk, file, "$$");
newXSproto(strcpy(buf, "SendAppearanceEffect"), XS_Mob_SendAppearanceEffect, file, "$$;$$$$"); newXSproto(strcpy(buf, "SendAppearanceEffect"), XS_Mob_SendAppearanceEffect, file, "$$;$$$$");
newXSproto(strcpy(buf, "SetFlyMode"), XS_Mob_SetFlyMode, file, "$$"); newXSproto(strcpy(buf, "SetFlyMode"), XS_Mob_SetFlyMode, file, "$$");

View File

@ -1346,7 +1346,7 @@ void Mob::SendItemAnimation(Mob *to, const Item_Struct *item, SkillUseTypes skil
safe_delete(outapp); safe_delete(outapp);
} }
void Mob::ProjectileAnimation(Mob* to, int item_id, bool IsArrow, float speed, float angle, float tilt, float arc) { void Mob::ProjectileAnimation(Mob* to, int item_id, bool IsArrow, float speed, float angle, float tilt, float arc, const char *IDFile) {
const Item_Struct* item = nullptr; const Item_Struct* item = nullptr;
uint8 item_type = 0; uint8 item_type = 0;
@ -1380,6 +1380,10 @@ void Mob::ProjectileAnimation(Mob* to, int item_id, bool IsArrow, float speed, f
arc = 50; arc = 50;
} }
const char *item_IDFile = item->IDFile;
if (IDFile && (strncmp(IDFile, "IT", 2) == 0))
item_IDFile = IDFile;
// See SendItemAnimation() for some notes on this struct // See SendItemAnimation() for some notes on this struct
EQApplicationPacket *outapp = new EQApplicationPacket(OP_SomeItemPacketMaybe, sizeof(Arrow_Struct)); EQApplicationPacket *outapp = new EQApplicationPacket(OP_SomeItemPacketMaybe, sizeof(Arrow_Struct));
@ -1393,7 +1397,7 @@ void Mob::ProjectileAnimation(Mob* to, int item_id, bool IsArrow, float speed, f
as->item_id = item->ID; as->item_id = item->ID;
as->item_type = item_type; as->item_type = item_type;
as->skill = 0; // Doesn't seem to have any effect as->skill = 0; // Doesn't seem to have any effect
strn0cpy(as->model_name, item->IDFile, 16); strn0cpy(as->model_name, item_IDFile, 16);
as->velocity = speed; as->velocity = speed;
as->launch_angle = angle; as->launch_angle = angle;
as->tilt = tilt; as->tilt = tilt;
@ -1406,7 +1410,6 @@ void Mob::ProjectileAnimation(Mob* to, int item_id, bool IsArrow, float speed, f
} }
void NPC::DoClassAttacks(Mob *target) { void NPC::DoClassAttacks(Mob *target) {
if(target == nullptr) if(target == nullptr)
return; //gotta have a target for all these return; //gotta have a target for all these

View File

@ -3351,10 +3351,7 @@ void Mob::DoBuffTic(uint16 spell_id, int slot, uint32 ticsremaining, uint8 caste
case SE_Root: { case SE_Root: {
/* Root formula derived from extensive personal live parses - Kayen /* Root formula derived from extensive personal live parses - Kayen
ROOT has a 40% chance to do a resist check to break. ROOT has a 70% chance to do a resist check to break.
Resist check has NO LOWER bounds.
If multiple roots on target. Root in first slot will be checked first to break from nukes.
If multiple roots on target and broken by spell. Roots are removed ONE at a time in order of buff slot.
*/ */
if (MakeRandomInt(0, 99) < RuleI(Spells, RootBreakCheckChance)){ if (MakeRandomInt(0, 99) < RuleI(Spells, RootBreakCheckChance)){
@ -6064,26 +6061,35 @@ bool Mob::TrySpellProjectile(Mob* spell_target, uint16 spell_id){
projectile_timer.Start(250); projectile_timer.Start(250);
} }
//Only use fire graphic for fire spells. //This will use the correct graphic as defined in the player_1 field of spells_new table. Found in UF+ spell files.
if (spells[spell_id].resisttype == RESIST_FIRE) { if (RuleB(Spells, UseLiveSpellProjectileGFX)) {
ProjectileAnimation(spell_target,0, false, 1.5,0,0,0, spells[spell_id].player_1);
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) //This allows limited support for server using older spell files that do not contain data for bolt graphics.
else else {
ProjectileAnimation(spell_target,0, 1, 1.5); //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);
}
//Default to an arrow if not using a mage bolt (Use up to date spell file and enable above rules for best results)
else
ProjectileAnimation(spell_target,0, 1, 1.5);
}
if (spells[spell_id].CastingAnim == 64)
anim = 44; //Corrects for animation error.
DoAnim(anim, 0, true, IsClient() ? FilterPCSpells : FilterNPCSpells); //Override the default projectile animation. DoAnim(anim, 0, true, IsClient() ? FilterPCSpells : FilterNPCSpells); //Override the default projectile animation.
return true; return true;