Merge pull request #139 from KayenEQ/master

-Implemented the ability to properly use live spell projectile graphics.
This commit is contained in:
Michael Cook 2014-04-10 14:06:53 -04:00
commit 983c7a9c91
16 changed files with 117 additions and 40 deletions

View File

@ -1,10 +1,29 @@
EQEMu Changelog (Started on Sept 24, 2003 15:50)
-------------------------------------------------------
== 04/10/2014 ==
Kayen: Added 'no_target_hotkey' field to npc_types table. This will prevent the NPC from being targeted with F8 (Warning: Will also turn it's name YELLOW)
Kayen: Added a rule to make all (Player cast) Swarm Pets not targetable with F8 (nearest NPC) by default (Warning: Will also turn pets names YELLOW). This is semi-hack but it works.
Kayen: Player cast swarm pets can now be healed and buffed consistent with live.
Optional SQL: utils/sql/git/optional/2014_04_10_SwarmPetNotTargetableWithHotKey.sql
Required SQL: utils/sql/git/required/2014_04_10_No_Target_With_Hotkey.sql
Note: For the required new npc_types field you DO NOT need to set values for swarm pets if you enable the above rule.
== 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 ==
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

View File

@ -135,6 +135,7 @@ RULE_CATEGORY_END()
RULE_CATEGORY( Pets )
RULE_REAL( Pets, AttackCommandRange, 150 )
RULE_BOOL( Pets, UnTargetableSwarmPet, false )
RULE_BOOL( Pets, SwarmPetNotTargetableWithHotKey, false ) //On SOF+ clients this a semi-hack to make swarm pets not F8 targetable.
RULE_CATEGORY_END()
RULE_CATEGORY( GM )
@ -307,6 +308,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_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_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( 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_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.');
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 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

@ -0,0 +1,4 @@
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Pets:SwarmPetNotTargetableWithHotKey', 'false', ' On SOF+ clients this a semi-hack to make swarm pets not F8 targetable. Warning: Turns pet names Yellow');

View File

@ -0,0 +1,3 @@
ALTER TABLE `npc_types` ADD `no_target_hotkey` tinyint( 1 ) UNSIGNED NOT NULL DEFAULT '0' AFTER `healscale`;

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
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.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))) {
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;
}

View File

@ -891,7 +891,8 @@ void Mob::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho)
ns->spawn.invis = (invisible || hidden) ? 1 : 0; // TODO: load this before spawning players
ns->spawn.NPC = IsClient() ? 0 : 1;
ns->spawn.IsMercenary = IsMerc() ? 1 : 0;
ns->spawn.IsMercenary = (IsMerc() || no_target_hotkey) ? 1 : 0;
ns->spawn.petOwnerId = ownerid;
ns->spawn.haircolor = haircolor;
@ -1626,7 +1627,7 @@ void Mob::SendAppearanceEffect(uint32 parm1, uint32 parm2, uint32 parm3, uint32
la->parm4 = parm4;
la->parm5 = parm5;
// Note that setting the b values to 0 will disable the related effect from the corresponding parameter.
// Setting the a value appears to have no affect at all.
// Setting the a value appears to have no affect at all.s
la->value1a = 1;
la->value1b = 1;
la->value2a = 1;

View File

@ -161,7 +161,7 @@ public:
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 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);
inline uint8 SeeInvisible() const { return see_invis; }
inline bool SeeInvisibleUndead() const { return see_invis_undead; }
@ -170,7 +170,7 @@ public:
bool IsInvisible(Mob* other = 0) const;
void SetInvisible(uint8 state);
bool AttackAnimation(SkillUseTypes &skillinuse, int Hand, const ItemInst* weapon);
//Song
bool UseBardSpellLogic(uint16 spell_id = 0xffff, int slot = -1);
bool ApplyNextBardPulse(uint16 spell_id, Mob *spell_target, uint16 slot);
@ -945,6 +945,7 @@ protected:
int16 petpower;
uint32 follow;
uint32 follow_dist;
bool no_target_hotkey;
uint8 gender;
uint16 race;

View File

@ -224,6 +224,8 @@ NPC::NPC(const NPCType* d, Spawn2* in_respawn, float x, float y, float z, float
p_depop = false;
loottable_id = d->loottable_id;
no_target_hotkey = d->no_target_hotkey;
primary_faction = 0;
SetNPCFactionID(d->npc_faction_id);
@ -1719,6 +1721,22 @@ bool Mob::HasNPCSpecialAtk(const char* parse) {
void NPC::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho)
{
Mob::FillSpawnStruct(ns, ForWho);
//Basic settings to make sure swarm pets work properly.
if (GetSwarmOwner()) {
Client *c = entity_list.GetClientByID(GetSwarmOwner());
if(c) {
SetAllowBeneficial(1); //Allow client cast swarm pets to be heal/buffed.
//This is a hack to allow CLIENT swarm pets NOT to be targeted with F8. Warning: Will turn name 'Yellow'!
if (RuleB(Pets, SwarmPetNotTargetableWithHotKey))
ns->spawn.IsMercenary = 1;
}
//NPC cast swarm pets should still be targetable with F8.
else
ns->spawn.IsMercenary = 0;
}
//Not recommended if using above (However, this will work better on older clients).
if (RuleB(Pets, UnTargetableSwarmPet)) {
if(GetOwnerID() || GetSwarmOwner()) {
ns->spawn.is_pet = 1;
@ -1867,6 +1885,12 @@ void NPC::ModifyNPCStat(const char *identifier, const char *newValue)
return;
}
if(id == "PhR")
{
PhR = atoi(val.c_str());
return;
}
if(id == "runspeed")
{
runspeed = (float)atof(val.c_str());

View File

@ -245,6 +245,7 @@ public:
void AddLootDrop(const Item_Struct*dbitem, ItemList* itemlistconst, int16 charges, uint8 minlevel, uint8 maxlevel, bool equipit, bool wearchange = false);
virtual void DoClassAttacks(Mob *target);
void CheckSignal();
inline bool IsTargetableWithHotkey() const { return no_target_hotkey; }
//waypoint crap
int GetMaxWp() const { return max_wp; }

View File

@ -6856,7 +6856,7 @@ XS(XS_Mob_ProjectileAnim); /* prototype to pass -Wmissing-prototypes */
XS(XS_Mob_ProjectileAnim)
{
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)");
{
@ -6868,6 +6868,7 @@ XS(XS_Mob_ProjectileAnim)
float angle = 0;
float tilt = 0;
float arc = 0;
char * IDFile = nullptr;
if (sv_derived_from(ST(0), "Mob")) {
IV tmp = SvIV((SV*)SvRV(ST(0)));
@ -6903,7 +6904,9 @@ XS(XS_Mob_ProjectileAnim)
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;
@ -8389,7 +8392,7 @@ XS(boot_Mob)
newXSproto(strcpy(buf, "CheckLoS"), XS_Mob_CheckLoS, file, "$$");
newXSproto(strcpy(buf, "CheckLoSToLoc"), XS_Mob_CheckLoSToLoc, 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, "SendAppearanceEffect"), XS_Mob_SendAppearanceEffect, 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);
}
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;
uint8 item_type = 0;
@ -1380,6 +1380,10 @@ void Mob::ProjectileAnimation(Mob* to, int item_id, bool IsArrow, float speed, f
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
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_type = item_type;
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->launch_angle = angle;
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) {
if(target == nullptr)
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: {
/* Root formula derived from extensive personal live parses - Kayen
ROOT has a 40% 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.
ROOT has a 70% chance to do a resist check to break.
*/
if (MakeRandomInt(0, 99) < RuleI(Spells, RootBreakCheckChance)){
@ -6064,26 +6061,35 @@ bool Mob::TrySpellProjectile(Mob* spell_target, uint16 spell_id){
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.
//This will use the correct graphic as defined in the player_1 field of spells_new table. Found in UF+ spell files.
if (RuleB(Spells, UseLiveSpellProjectileGFX)) {
ProjectileAnimation(spell_target,0, false, 1.5,0,0,0, spells[spell_id].player_1);
}
//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);
//This allows limited support for server using older spell files that do not contain data for bolt graphics.
else {
//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.
return true;

View File

@ -1099,7 +1099,8 @@ const NPCType* ZoneDatabase::GetNPCType (uint32 id) {
"npc_types.underwater,"
"npc_types.emoteid,"
"npc_types.spellscale,"
"npc_types.healscale";
"npc_types.healscale,"
"npc_types.no_target_hotkey";
MakeAnyLenString(&query, "%s FROM npc_types WHERE id=%d", basic_query, id);
@ -1191,7 +1192,7 @@ const NPCType* ZoneDatabase::GetNPCType (uint32 id) {
tmpNPCType->armor_tint[0] |= (atoi(row[r++]) & 0xFF) << 8;
tmpNPCType->armor_tint[0] |= (atoi(row[r++]) & 0xFF);
tmpNPCType->armor_tint[0] |= (tmpNPCType->armor_tint[0]) ? (0xFF << 24) : 0;
int i;
if (armor_tint_id > 0)
{
@ -1281,7 +1282,8 @@ const NPCType* ZoneDatabase::GetNPCType (uint32 id) {
tmpNPCType->emoteid = atoi(row[r++]);
tmpNPCType->spellscale = atoi(row[r++]);
tmpNPCType->healscale = atoi(row[r++]);
tmpNPCType->no_target_hotkey = atoi(row[r++]) == 1 ? true : false;
// If NPC with duplicate NPC id already in table,
// free item we attempted to add.
if (zone->npctable.find(tmpNPCType->npc_id) != zone->npctable.end())

View File

@ -120,6 +120,7 @@ struct NPCType
uint32 emoteid;
float spellscale;
float healscale;
bool no_target_hotkey;
};
/*