Merge pull request #1 from EQEmu/master

sync fork up with source
This commit is contained in:
gpanula 2015-05-01 22:49:57 -05:00
commit 79928c190b
28 changed files with 386 additions and 225 deletions

View File

@ -1,5 +1,10 @@
EQEMu Changelog (Started on Sept 24, 2003 15:50) EQEMu Changelog (Started on Sept 24, 2003 15:50)
------------------------------------------------------- -------------------------------------------------------
== 04/30/2015 ==
demonstar55: Implement mob and client melee push
You can set Combat:MeleePush to false to turn off or change Combat:MeleePushChance to increase the chance an NPC can be pushed
PCs are always pushed, need to do more testing to verify.
== 04/22/2015 == == 04/22/2015 ==
Uleat: Probable fix for 'Debug Assertion Failure' in Client::GarbleMessage() when calling the 'isalpha' macro. Uleat: Probable fix for 'Debug Assertion Failure' in Client::GarbleMessage() when calling the 'isalpha' macro.
ref: https://connect.microsoft.com/VisualStudio/feedback/details/932876/calling-isdigit-with-a-signed-char-1-results-in-a-assert-failure-in-debug-compiles ref: https://connect.microsoft.com/VisualStudio/feedback/details/932876/calling-isdigit-with-a-signed-char-1-results-in-a-assert-failure-in-debug-compiles

View File

@ -1160,7 +1160,7 @@ struct TargetReject_Struct {
struct PetCommand_Struct { struct PetCommand_Struct {
/*000*/ uint32 command; /*000*/ uint32 command;
/*004*/ uint32 unknown; /*004*/ uint32 target;
}; };
/* /*
@ -1318,9 +1318,9 @@ struct CombatDamage_Struct
/* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells /* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells
/* 05 */ uint16 spellid; /* 05 */ uint16 spellid;
/* 07 */ uint32 damage; /* 07 */ uint32 damage;
/* 11 */ uint32 unknown11; /* 11 */ float force;
/* 15 */ uint32 sequence; // see above notes in Action_Struct /* 15 */ float meleepush_xy; // see above notes in Action_Struct
/* 19 */ uint32 unknown19; /* 19 */ float meleepush_z;
/* 23 */ /* 23 */
}; };

View File

@ -658,7 +658,9 @@ namespace RoF
OUT(type); OUT(type);
OUT(spellid); OUT(spellid);
OUT(damage); OUT(damage);
eq->sequence = emu->sequence; OUT(force)
OUT(meleepush_xy);
OUT(meleepush_z)
FINISH_ENCODE(); FINISH_ENCODE();
} }
@ -4389,7 +4391,7 @@ namespace RoF
IN(type); IN(type);
IN(spellid); IN(spellid);
IN(damage); IN(damage);
emu->sequence = eq->sequence; IN(meleepush_xy);
FINISH_DIRECT_DECODE(); FINISH_DIRECT_DECODE();
} }
@ -4717,7 +4719,7 @@ namespace RoF
SETUP_DIRECT_DECODE(PetCommand_Struct, structs::PetCommand_Struct); SETUP_DIRECT_DECODE(PetCommand_Struct, structs::PetCommand_Struct);
IN(command); IN(command);
emu->unknown = eq->unknown04; IN(target);
FINISH_DIRECT_DECODE(); FINISH_DIRECT_DECODE();
} }

View File

@ -729,7 +729,9 @@ namespace RoF2
OUT(type); OUT(type);
OUT(spellid); OUT(spellid);
OUT(damage); OUT(damage);
eq->sequence = emu->sequence; OUT(force)
OUT(meleepush_xy);
OUT(meleepush_z)
FINISH_ENCODE(); FINISH_ENCODE();
} }
@ -4538,7 +4540,7 @@ namespace RoF2
IN(type); IN(type);
IN(spellid); IN(spellid);
IN(damage); IN(damage);
emu->sequence = eq->sequence; IN(meleepush_xy);
FINISH_DIRECT_DECODE(); FINISH_DIRECT_DECODE();
} }
@ -4864,7 +4866,7 @@ namespace RoF2
SETUP_DIRECT_DECODE(PetCommand_Struct, structs::PetCommand_Struct); SETUP_DIRECT_DECODE(PetCommand_Struct, structs::PetCommand_Struct);
IN(command); IN(command);
emu->unknown = eq->unknown04; IN(target);
FINISH_DIRECT_DECODE(); FINISH_DIRECT_DECODE();
} }

View File

@ -1293,7 +1293,7 @@ struct TargetReject_Struct {
struct PetCommand_Struct { struct PetCommand_Struct {
/*00*/ uint32 command; /*00*/ uint32 command;
/*04*/ uint32 unknown04; /*04*/ uint32 target;
/*08*/ uint32 unknown08; /*08*/ uint32 unknown08;
}; };
@ -1484,9 +1484,10 @@ struct CombatDamage_Struct
/* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells /* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells
/* 05 */ uint32 spellid; /* 05 */ uint32 spellid;
/* 09 */ int32 damage; /* 09 */ int32 damage;
/* 13 */ float unknown11; // cd cc cc 3d /* 13 */ float force; // cd cc cc 3d
/* 17 */ float sequence; // see above notes in Action_Struct /* 17 */ float meleepush_xy; // see above notes in Action_Struct
/* 21 */ uint8 unknown19[9]; // was [9] /* 21 */ float meleepush_z;
/* 25 */ uint8 unknown25[5]; // was [9]
/* 30 */ /* 30 */
}; };

View File

@ -1323,7 +1323,7 @@ struct TargetReject_Struct {
struct PetCommand_Struct { struct PetCommand_Struct {
/*00*/ uint32 command; /*00*/ uint32 command;
/*04*/ uint32 unknown04; /*04*/ uint32 target;
/*08*/ uint32 unknown08; /*08*/ uint32 unknown08;
}; };
@ -1514,9 +1514,10 @@ struct CombatDamage_Struct
/* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells /* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells
/* 05 */ uint32 spellid; /* 05 */ uint32 spellid;
/* 09 */ int32 damage; /* 09 */ int32 damage;
/* 13 */ float unknown11; // cd cc cc 3d /* 13 */ float force; // cd cc cc 3d
/* 17 */ float sequence; // see above notes in Action_Struct /* 17 */ float meleepush_xy; // see above notes in Action_Struct
/* 21 */ uint8 unknown19[9]; // was [9] /* 21 */ float meleepush_z;
/* 25 */ uint8 unknown25[5]; // was [9]
/* 30 */ /* 30 */
}; };

View File

@ -446,7 +446,9 @@ namespace SoD
OUT(type); OUT(type);
OUT(spellid); OUT(spellid);
OUT(damage); OUT(damage);
eq->sequence = emu->sequence; OUT(force)
OUT(meleepush_xy);
OUT(meleepush_z)
FINISH_ENCODE(); FINISH_ENCODE();
} }
@ -3349,7 +3351,7 @@ namespace SoD
default: default:
emu->command = eq->command; emu->command = eq->command;
} }
OUT(unknown); IN(target);
FINISH_DIRECT_DECODE(); FINISH_DIRECT_DECODE();
} }

View File

@ -1091,7 +1091,7 @@ struct TargetReject_Struct {
struct PetCommand_Struct { struct PetCommand_Struct {
/*000*/ uint32 command; /*000*/ uint32 command;
/*004*/ uint32 unknown; /*004*/ uint32 target;
}; };
/* /*
@ -1272,9 +1272,10 @@ struct CombatDamage_Struct
/* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells /* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells
/* 05 */ uint16 spellid; /* 05 */ uint16 spellid;
/* 07 */ int32 damage; /* 07 */ int32 damage;
/* 11 */ float unknown11; // cd cc cc 3d /* 11 */ float force; // cd cc cc 3d
/* 15 */ float sequence; // see above notes in Action_Struct /* 15 */ float meleepush_xy; // see above notes in Action_Struct
/* 19 */ uint8 unknown19[9]; // was [9] /* 19 */ float meleepush_z;
/* 23 */ uint8 unknown23[5]; // was [9]
/* 28 */ /* 28 */
}; };

View File

@ -426,7 +426,9 @@ namespace SoF
OUT(type); OUT(type);
OUT(spellid); OUT(spellid);
OUT(damage); OUT(damage);
eq->sequence = emu->sequence; OUT(force)
OUT(meleepush_xy);
OUT(meleepush_z)
FINISH_ENCODE(); FINISH_ENCODE();
} }
@ -2687,7 +2689,7 @@ namespace SoF
default: default:
emu->command = eq->command; emu->command = eq->command;
} }
OUT(unknown); IN(target);
FINISH_DIRECT_DECODE(); FINISH_DIRECT_DECODE();
} }

View File

@ -1068,7 +1068,7 @@ struct TargetReject_Struct {
struct PetCommand_Struct { struct PetCommand_Struct {
/*000*/ uint32 command; /*000*/ uint32 command;
/*004*/ uint32 unknown; /*004*/ uint32 target;
}; };
/* /*
@ -1249,9 +1249,10 @@ struct CombatDamage_Struct
/* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells /* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells
/* 05 */ uint16 spellid; /* 05 */ uint16 spellid;
/* 07 */ int32 damage; /* 07 */ int32 damage;
/* 11 */ float unknown11; // cd cc cc 3d /* 11 */ float force; // cd cc cc 3d
/* 15 */ float sequence; // see above notes in Action_Struct /* 15 */ float meleepush_xy; // see above notes in Action_Struct
/* 19 */ uint8 unknown19[9]; // was [9] /* 19 */ float meleepush_z;
/* 23 */ uint8 unknown23[5]; // was [9]
/* 28 */ /* 28 */
}; };

View File

@ -1942,7 +1942,7 @@ namespace Titanium
default: default:
emu->command = eq->command; emu->command = eq->command;
} }
OUT(unknown); IN(target);
FINISH_DIRECT_DECODE(); FINISH_DIRECT_DECODE();
} }

View File

@ -950,7 +950,7 @@ struct TargetReject_Struct {
struct PetCommand_Struct { struct PetCommand_Struct {
/*000*/ uint32 command; /*000*/ uint32 command;
/*004*/ uint32 unknown; /*004*/ uint32 target;
}; };
/* /*
@ -1101,9 +1101,9 @@ struct CombatDamage_Struct
/* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells /* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells
/* 05 */ uint16 spellid; /* 05 */ uint16 spellid;
/* 07 */ uint32 damage; /* 07 */ uint32 damage;
/* 11 */ uint32 unknown11; /* 11 */ float force;
/* 15 */ uint32 sequence; // see above notes in Action_Struct /* 15 */ float meleepush_xy; // see above notes in Action_Struct
/* 19 */ uint32 unknown19; /* 19 */ float meleepush_z;
/* 23 */ /* 23 */
}; };

View File

@ -581,7 +581,9 @@ namespace UF
OUT(type); OUT(type);
OUT(spellid); OUT(spellid);
OUT(damage); OUT(damage);
eq->sequence = emu->sequence; OUT(force)
OUT(meleepush_xy);
OUT(meleepush_z)
FINISH_ENCODE(); FINISH_ENCODE();
} }
@ -3356,7 +3358,7 @@ namespace UF
IN(type); IN(type);
IN(spellid); IN(spellid);
IN(damage); IN(damage);
emu->sequence = eq->sequence; IN(meleepush_xy);
FINISH_DIRECT_DECODE(); FINISH_DIRECT_DECODE();
} }
@ -3599,7 +3601,7 @@ namespace UF
SETUP_DIRECT_DECODE(PetCommand_Struct, structs::PetCommand_Struct); SETUP_DIRECT_DECODE(PetCommand_Struct, structs::PetCommand_Struct);
IN(command); IN(command);
IN(unknown); IN(target);
FINISH_DIRECT_DECODE(); FINISH_DIRECT_DECODE();
} }

View File

@ -1146,7 +1146,7 @@ struct TargetReject_Struct {
struct PetCommand_Struct { struct PetCommand_Struct {
/*000*/ uint32 command; /*000*/ uint32 command;
/*004*/ uint32 unknown; /*004*/ uint32 target;
}; };
/* /*
@ -1330,9 +1330,10 @@ struct CombatDamage_Struct
/* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells /* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells
/* 05 */ uint16 spellid; /* 05 */ uint16 spellid;
/* 07 */ int32 damage; /* 07 */ int32 damage;
/* 11 */ float unknown11; // cd cc cc 3d /* 11 */ float force; // cd cc cc 3d
/* 15 */ float sequence; // see above notes in Action_Struct /* 15 */ float meleepush_xy; // see above notes in Action_Struct
/* 19 */ uint8 unknown19[9]; // was [9] /* 19 */ float meleepush_z;
/* 23 */ uint8 unknown23[5]; // was [9]
/* 28 */ /* 28 */
}; };

View File

@ -47,6 +47,8 @@
#define IKSAR 128 #define IKSAR 128
#define VAHSHIR 130 #define VAHSHIR 130
#define CONTROLLED_BOAT 141 #define CONTROLLED_BOAT 141
#define MINOR_ILL_OBJ 142
#define TREE 143
#define IKSAR_SKELETON 161 #define IKSAR_SKELETON 161
#define FROGLOK 330 #define FROGLOK 330
#define FROGLOK2 74 // Not sure why /who all reports race as 74 for frogloks #define FROGLOK2 74 // Not sure why /who all reports race as 74 for frogloks

View File

@ -438,6 +438,8 @@ 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_BOOL ( Combat, OneProcPerWeapon, true) //If enabled, One proc per weapon per round
RULE_BOOL ( Combat, ProjectileDmgOnImpact, true) //If enabled, projectiles (ie arrows) will hit on impact, instead of instantly. RULE_BOOL ( Combat, ProjectileDmgOnImpact, true) //If enabled, projectiles (ie arrows) will hit on impact, instead of instantly.
RULE_BOOL ( Combat, MeleePush, true) // enable melee push
RULE_INT ( Combat, MeleePushChance, 50) // (NPCs) chance the target will be pushed. Made up, 100 actually isn't that bad
RULE_CATEGORY_END() RULE_CATEGORY_END()
RULE_CATEGORY( NPC ) RULE_CATEGORY( NPC )

View File

@ -55,3 +55,40 @@ bool EQEmu::IsSpecializedSkill(SkillUseTypes skill)
return false; return false;
} }
} }
float EQEmu::GetSkillMeleePushForce(SkillUseTypes skill)
{
// This is the force/magnitude of the push from an attack of this skill type
// You can find these numbers in the clients skill struct
switch (skill) {
case Skill1HBlunt:
case Skill1HSlashing:
case SkillHandtoHand:
case SkillThrowing:
return 0.1f;
case Skill2HBlunt:
case Skill2HSlashing:
case SkillEagleStrike:
case SkillKick:
case SkillTigerClaw:
//case Skill2HPiercing:
return 0.2f;
case SkillArchery:
return 0.15f;
case SkillBackstab:
case SkillBash:
return 0.3f;
case SkillDragonPunch:
case SkillRoundKick:
return 0.25f;
case SkillFlyingKick:
return 0.4f;
case Skill1HPiercing:
case SkillFrenzy:
return 0.05f;
case SkillIntimidation:
return 2.5f;
default:
return 0.0f;
}
}

View File

@ -270,6 +270,7 @@ typedef enum {
namespace EQEmu { namespace EQEmu {
bool IsTradeskill(SkillUseTypes skill); bool IsTradeskill(SkillUseTypes skill);
bool IsSpecializedSkill(SkillUseTypes skill); bool IsSpecializedSkill(SkillUseTypes skill);
float GetSkillMeleePushForce(SkillUseTypes skill);
} }
#endif #endif

View File

@ -0,0 +1,2 @@
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Combat:MeleePush', 'true', 'Turns on Melee Push.');
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Combat:MeleePushChance', '50', 'Chance that an NPC can be pushed from melee.');

View File

@ -982,14 +982,24 @@ int Mob::GetWeaponDamage(Mob *against, const ItemInst *weapon_item, uint32 *hate
return 0; return 0;
} }
else{ else{
if((GetClass() == MONK || GetClass() == BEASTLORD) && GetLevel() >= 30){ bool MagicGloves=false;
if (IsClient()) {
ItemInst *gloves=CastToClient()->GetInv().GetItem(MainHands);
if (gloves != nullptr) {
MagicGloves = gloves->GetItem()->Magic;
}
}
if((GetClass() == MONK || GetClass() == BEASTLORD)) {
if(MagicGloves || GetLevel() >= 30){
dmg = GetMonkHandToHandDamage(); dmg = GetMonkHandToHandDamage();
if (hate) *hate += dmg; if (hate) *hate += dmg;
} }
}
else if(GetOwner() && GetLevel() >= RuleI(Combat, PetAttackMagicLevel)){ //pets wouldn't actually use this but... else if(GetOwner() && GetLevel() >= RuleI(Combat, PetAttackMagicLevel)){ //pets wouldn't actually use this but...
dmg = 1; //it gives us an idea if we can hit dmg = 1; //it gives us an idea if we can hit
} }
else if(GetSpecialAbility(SPECATK_MAGICAL)){ else if(MagicGloves || GetSpecialAbility(SPECATK_MAGICAL)){
dmg = 1; dmg = 1;
} }
else else
@ -3700,6 +3710,23 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons
a->type = SkillDamageTypes[skill_used]; // was 0x1c a->type = SkillDamageTypes[skill_used]; // was 0x1c
a->damage = damage; a->damage = damage;
a->spellid = spell_id; a->spellid = spell_id;
a->meleepush_xy = attacker->GetHeading() * 2.0f;
if (RuleB(Combat, MeleePush) && damage > 0 && !IsRooted() &&
(IsClient() || zone->random.Roll(RuleI(Combat, MeleePushChance)))) {
a->force = EQEmu::GetSkillMeleePushForce(skill_used);
// update NPC stuff
auto new_pos = glm::vec3(m_Position.x + (a->force * std::sin(a->meleepush_xy) + m_Delta.x),
m_Position.y + (a->force * std::cos(a->meleepush_xy) + m_Delta.y), m_Position.z);
if (zone->zonemap->CheckLoS(glm::vec3(m_Position), new_pos)) { // If we have LoS on the new loc it should be reachable.
if (IsNPC()) {
// Is this adequate?
Teleport(new_pos);
SendPosUpdate();
}
} else {
a->force = 0.0f; // we couldn't move there, so lets not
}
}
//Note: if players can become pets, they will not receive damage messages of their own //Note: if players can become pets, they will not receive damage messages of their own
//this was done to simplify the code here (since we can only effectively skip one mob on queue) //this was done to simplify the code here (since we can only effectively skip one mob on queue)

View File

@ -1051,12 +1051,12 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s
if(quest_manager.ProximitySayInUse()) if(quest_manager.ProximitySayInUse())
entity_list.ProcessProximitySay(message, this, language); entity_list.ProcessProximitySay(message, this, language);
if (GetTarget() != 0 && GetTarget()->IsNPC()) { if (GetTarget() != 0 && GetTarget()->IsNPC() &&
!IsInvisible(GetTarget())) {
if(!GetTarget()->CastToNPC()->IsEngaged()) { if(!GetTarget()->CastToNPC()->IsEngaged()) {
CheckLDoNHail(GetTarget()); CheckLDoNHail(GetTarget());
CheckEmoteHail(GetTarget(), message); CheckEmoteHail(GetTarget(), message);
if(DistanceSquaredNoZ(m_Position, GetTarget()->GetPosition()) <= 200) { if(DistanceSquaredNoZ(m_Position, GetTarget()->GetPosition()) <= 200) {
NPC *tar = GetTarget()->CastToNPC(); NPC *tar = GetTarget()->CastToNPC();
parse->EventNPC(EVENT_SAY, tar->CastToNPC(), this, message, language); parse->EventNPC(EVENT_SAY, tar->CastToNPC(), this, message, language);

View File

@ -9752,6 +9752,7 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app)
char val1[20] = { 0 }; char val1[20] = { 0 };
PetCommand_Struct* pet = (PetCommand_Struct*)app->pBuffer; PetCommand_Struct* pet = (PetCommand_Struct*)app->pBuffer;
Mob* mypet = this->GetPet(); Mob* mypet = this->GetPet();
Mob *target = entity_list.GetMob(pet->target);
if (!mypet || pet->command == PET_LEADER) if (!mypet || pet->command == PET_LEADER)
{ {
@ -9799,22 +9800,22 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app)
switch (PetCommand) switch (PetCommand)
{ {
case PET_ATTACK: { case PET_ATTACK: {
if (!GetTarget()) if (!target)
break; break;
if (GetTarget()->IsMezzed()) { if (target->IsMezzed()) {
Message_StringID(10, CANNOT_WAKE, mypet->GetCleanName(), GetTarget()->GetCleanName()); Message_StringID(10, CANNOT_WAKE, mypet->GetCleanName(), target->GetCleanName());
break; break;
} }
if (mypet->IsFeared()) if (mypet->IsFeared())
break; //prevent pet from attacking stuff while feared break; //prevent pet from attacking stuff while feared
if (!mypet->IsAttackAllowed(GetTarget())) { if (!mypet->IsAttackAllowed(target)) {
mypet->Say_StringID(NOT_LEGAL_TARGET); mypet->Say_StringID(NOT_LEGAL_TARGET);
break; break;
} }
if ((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 2) || mypet->GetPetType() != petAnimation) { if ((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 2) || mypet->GetPetType() != petAnimation) {
if (GetTarget() != this && DistanceSquaredNoZ(mypet->GetPosition(), GetTarget()->GetPosition()) <= (RuleR(Pets, AttackCommandRange)*RuleR(Pets, AttackCommandRange))) { if (target != this && DistanceSquaredNoZ(mypet->GetPosition(), target->GetPosition()) <= (RuleR(Pets, AttackCommandRange)*RuleR(Pets, AttackCommandRange))) {
if (mypet->IsHeld()) { if (mypet->IsHeld()) {
if (!mypet->IsFocused()) { if (!mypet->IsFocused()) {
mypet->SetHeld(false); //break the hold and guard if we explicitly tell the pet to attack. mypet->SetHeld(false); //break the hold and guard if we explicitly tell the pet to attack.
@ -9822,12 +9823,12 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app)
mypet->SetPetOrder(SPO_Follow); mypet->SetPetOrder(SPO_Follow);
} }
else { else {
mypet->SetTarget(GetTarget()); mypet->SetTarget(target);
} }
} }
zone->AddAggroMob(); zone->AddAggroMob();
mypet->AddToHateList(GetTarget(), 1); mypet->AddToHateList(target, 1);
Message_StringID(MT_PetResponse, PET_ATTACKING, mypet->GetCleanName(), GetTarget()->GetCleanName()); Message_StringID(MT_PetResponse, PET_ATTACKING, mypet->GetCleanName(), target->GetCleanName());
} }
} }
break; break;

View File

@ -618,6 +618,7 @@ void EntityList::AddNPC(NPC *npc, bool SendSpawnPacket, bool dontqueue)
EQApplicationPacket *app = new EQApplicationPacket; EQApplicationPacket *app = new EQApplicationPacket;
npc->CreateSpawnPacket(app, npc); npc->CreateSpawnPacket(app, npc);
QueueClients(npc, app); QueueClients(npc, app);
npc->SendArmorAppearance();
safe_delete(app); safe_delete(app);
} else { } else {
NewSpawn_Struct *ns = new NewSpawn_Struct; NewSpawn_Struct *ns = new NewSpawn_Struct;
@ -726,10 +727,16 @@ void EntityList::CheckSpawnQueue()
EQApplicationPacket *outapp = 0; EQApplicationPacket *outapp = 0;
iterator.Reset(); iterator.Reset();
NewSpawn_Struct *ns;
while(iterator.MoreElements()) { while(iterator.MoreElements()) {
outapp = new EQApplicationPacket; outapp = new EQApplicationPacket;
Mob::CreateSpawnPacket(outapp, iterator.GetData()); ns = iterator.GetData();
Mob::CreateSpawnPacket(outapp, ns);
QueueClients(0, outapp); QueueClients(0, outapp);
auto it = npc_list.find(ns->spawn.spawnId);
NPC *pnpc = it->second;
pnpc->SendArmorAppearance();
safe_delete(outapp); safe_delete(outapp);
iterator.RemoveCurrent(); iterator.RemoveCurrent();
} }
@ -1149,20 +1156,40 @@ void EntityList::SendZoneSpawnsBulk(Client *client)
NewSpawn_Struct ns; NewSpawn_Struct ns;
Mob *spawn; Mob *spawn;
uint32 maxspawns = 100; uint32 maxspawns = 100;
EQApplicationPacket *app;
if (maxspawns > mob_list.size()) if (maxspawns > mob_list.size())
maxspawns = mob_list.size(); maxspawns = mob_list.size();
BulkZoneSpawnPacket *bzsp = new BulkZoneSpawnPacket(client, maxspawns); BulkZoneSpawnPacket *bzsp = new BulkZoneSpawnPacket(client, maxspawns);
int32 race=-1;
for (auto it = mob_list.begin(); it != mob_list.end(); ++it) { for (auto it = mob_list.begin(); it != mob_list.end(); ++it) {
spawn = it->second; spawn = it->second;
if (spawn && spawn->InZone()) { if (spawn && spawn->InZone()) {
if (spawn->IsClient() && (spawn->CastToClient()->GMHideMe(client) || if (spawn->IsClient() && (spawn->CastToClient()->GMHideMe(client) ||
spawn->CastToClient()->IsHoveringForRespawn())) spawn->CastToClient()->IsHoveringForRespawn()))
continue; continue;
race = spawn->GetRace();
// Illusion races on PCs don't work as a mass spawn
// But they will work as an add_spawn AFTER CLIENT_CONNECTED.
if (spawn->IsClient() && (race == MINOR_ILL_OBJ || race == TREE)) {
app = new EQApplicationPacket;
spawn->CreateSpawnPacket(app);
client->QueuePacket(app, true, Client::CLIENT_CONNECTED);
safe_delete(app);
}
else {
memset(&ns, 0, sizeof(NewSpawn_Struct)); memset(&ns, 0, sizeof(NewSpawn_Struct));
spawn->FillSpawnStruct(&ns, client); spawn->FillSpawnStruct(&ns, client);
bzsp->AddSpawn(&ns); bzsp->AddSpawn(&ns);
} }
// Despite being sent in the OP_ZoneSpawns packet, the client
// does not display worn armor correctly so display it.
spawn->SendArmorAppearance(client);
}
} }
safe_delete(bzsp); safe_delete(bzsp);
} }

View File

@ -1787,7 +1787,7 @@ bool Mob::IsPlayerRace(uint16 in_race) {
uint8 Mob::GetDefaultGender(uint16 in_race, uint8 in_gender) { uint8 Mob::GetDefaultGender(uint16 in_race, uint8 in_gender) {
if (Mob::IsPlayerRace(in_race) || in_race == 15 || in_race == 50 || in_race == 57 || in_race == 70 || in_race == 98 || in_race == 118) { if (Mob::IsPlayerRace(in_race) || in_race == 15 || in_race == 50 || in_race == 57 || in_race == 70 || in_race == 98 || in_race == 118 || in_race == 23) {
if (in_gender >= 2) { if (in_gender >= 2) {
// Male default for PC Races // Male default for PC Races
return 0; return 0;
@ -2552,7 +2552,35 @@ uint32 NPC::GetEquipment(uint8 material_slot) const
return equipment[invslot]; return equipment[invslot];
} }
void Mob::SendWearChange(uint8 material_slot) void Mob::SendArmorAppearance(Client *one_client)
{
// one_client of 0 means sent to all clients
//
// Despite the fact that OP_NewSpawn and OP_ZoneSpawns include the
// armor being worn and its mats, the client doesn't update the display
// on arrival of these packets reliably.
//
// Send Wear changes if mob is a PC race and item is an armor slot.
// The other packets work for primary/secondary.
if (IsPlayerRace(race))
{
if (!IsClient())
{
const Item_Struct *item;
for (int i=0; i< 7 ; ++i)
{
item=database.GetItem(GetEquipment(i));
if (item != 0)
{
SendWearChange(i,one_client);
}
}
}
}
}
void Mob::SendWearChange(uint8 material_slot, Client *one_client)
{ {
EQApplicationPacket* outapp = new EQApplicationPacket(OP_WearChange, sizeof(WearChange_Struct)); EQApplicationPacket* outapp = new EQApplicationPacket(OP_WearChange, sizeof(WearChange_Struct));
WearChange_Struct* wc = (WearChange_Struct*)outapp->pBuffer; WearChange_Struct* wc = (WearChange_Struct*)outapp->pBuffer;
@ -2564,7 +2592,15 @@ void Mob::SendWearChange(uint8 material_slot)
wc->color.Color = GetEquipmentColor(material_slot); wc->color.Color = GetEquipmentColor(material_slot);
wc->wear_slot_id = material_slot; wc->wear_slot_id = material_slot;
if (!one_client)
{
entity_list.QueueClients(this, outapp); entity_list.QueueClients(this, outapp);
}
else
{
one_client->QueuePacket(outapp, false, Client::CLIENT_CONNECTED);
}
safe_delete(outapp); safe_delete(outapp);
} }

View File

@ -171,7 +171,8 @@ public:
void SendAppearanceEffect(uint32 parm1, uint32 parm2, uint32 parm3, uint32 parm4, uint32 parm5, void SendAppearanceEffect(uint32 parm1, uint32 parm2, uint32 parm3, uint32 parm4, uint32 parm5,
Client *specific_target=nullptr); Client *specific_target=nullptr);
void SendTargetable(bool on, Client *specific_target = nullptr); void SendTargetable(bool on, Client *specific_target = nullptr);
virtual void SendWearChange(uint8 material_slot); virtual void SendArmorAppearance(Client *one_client = nullptr);
virtual void SendWearChange(uint8 material_slot, Client *one_client = nullptr);
virtual void SendTextureWC(uint8 slot, uint16 texture, uint32 hero_forge_model = 0, uint32 elite_material = 0, virtual void SendTextureWC(uint8 slot, uint16 texture, uint32 hero_forge_model = 0, uint32 elite_material = 0,
uint32 unknown06 = 0, uint32 unknown18 = 0); uint32 unknown06 = 0, uint32 unknown18 = 0);
virtual void SetSlotTint(uint8 material_slot, uint8 red_tint, uint8 green_tint, uint8 blue_tint); virtual void SetSlotTint(uint8 material_slot, uint8 red_tint, uint8 green_tint, uint8 blue_tint);

View File

@ -921,7 +921,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
cd->source = action->source; cd->source = action->source;
cd->type = action->type; cd->type = action->type;
cd->spellid = action->spell; cd->spellid = action->spell;
cd->sequence = action->sequence; cd->meleepush_xy = action->sequence;
CastToClient()->QueuePacket(action_packet); CastToClient()->QueuePacket(action_packet);
if(caster->IsClient() && caster != this) if(caster->IsClient() && caster != this)
@ -970,7 +970,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
cd->source = action->source; cd->source = action->source;
cd->type = action->type; cd->type = action->type;
cd->spellid = action->spell; cd->spellid = action->spell;
cd->sequence = action->sequence; cd->meleepush_xy = action->sequence;
CastToClient()->QueuePacket(action_packet); CastToClient()->QueuePacket(action_packet);
if(caster->IsClient() && caster != this) if(caster->IsClient() && caster != this)
@ -1006,7 +1006,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
cd->source = action->source; cd->source = action->source;
cd->type = action->type; cd->type = action->type;
cd->spellid = action->spell; cd->spellid = action->spell;
cd->sequence = action->sequence; cd->meleepush_xy = action->sequence;
CastToClient()->QueuePacket(action_packet); CastToClient()->QueuePacket(action_packet);
if(caster->IsClient() && caster != this) if(caster->IsClient() && caster != this)

View File

@ -1165,12 +1165,14 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, uint16 slot,
} }
else if (!bard_song_mode) else if (!bard_song_mode)
{ {
int noexpend;
for(int t_count = 0; t_count < 4; t_count++) { for(int t_count = 0; t_count < 4; t_count++) {
component = spells[spell_id].components[t_count]; component = spells[spell_id].components[t_count];
if (component == -1) noexpend = spells[spell_id].NoexpendReagent[t_count];
if (component == -1 || noexpend == component)
continue; continue;
component_count = spells[spell_id].component_counts[t_count]; component_count = spells[spell_id].component_counts[t_count];
Log.Out(Logs::Detail, Logs::Spells, "Spell %d: Consuming %d of spell component item id %d", spell_id, component, component_count); Log.Out(Logs::Detail, Logs::Spells, "Spell %d: Consuming %d of spell component item id %d", spell_id, component_count, component);
// Components found, Deleting // Components found, Deleting
// now we go looking for and deleting the items one by one // now we go looking for and deleting the items one by one
for(int s = 0; s < component_count; s++) for(int s = 0; s < component_count; s++)
@ -1478,7 +1480,10 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce
{ {
//invalid target //invalid target
Log.Out(Logs::Detail, Logs::Spells, "Spell %d canceled: invalid target of body type %d (undead)", spell_id, mob_body); Log.Out(Logs::Detail, Logs::Spells, "Spell %d canceled: invalid target of body type %d (undead)", spell_id, mob_body);
if(!spell_target)
Message_StringID(13,SPELL_NEED_TAR); Message_StringID(13,SPELL_NEED_TAR);
else
Message_StringID(13,CANNOT_AFFECT_NPC);
return false; return false;
} }
CastAction = SingleTarget; CastAction = SingleTarget;
@ -2561,7 +2566,7 @@ void Mob::BardPulse(uint16 spell_id, Mob *caster) {
cd->source = action->source; cd->source = action->source;
cd->type = DamageTypeSpell; cd->type = DamageTypeSpell;
cd->spellid = action->spell; cd->spellid = action->spell;
cd->sequence = action->sequence; cd->meleepush_xy = action->sequence;
cd->damage = 0; cd->damage = 0;
if(!IsEffectInSpell(spell_id, SE_BindAffinity)) if(!IsEffectInSpell(spell_id, SE_BindAffinity))
{ {
@ -3825,7 +3830,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect, bool use_r
cd->source = action->source; cd->source = action->source;
cd->type = action->type; cd->type = action->type;
cd->spellid = action->spell; cd->spellid = action->spell;
cd->sequence = action->sequence; cd->meleepush_xy = action->sequence;
cd->damage = 0; cd->damage = 0;
if(!IsEffectInSpell(spell_id, SE_BindAffinity)) if(!IsEffectInSpell(spell_id, SE_BindAffinity))
{ {

View File

@ -192,7 +192,7 @@ void Trap::Trigger(Mob* trigger)
int dmg = zone->random.Int(effectvalue, effectvalue2); int dmg = zone->random.Int(effectvalue, effectvalue2);
trigger->SetHP(trigger->GetHP() - dmg); trigger->SetHP(trigger->GetHP() - dmg);
a->damage = dmg; a->damage = dmg;
a->sequence = zone->random.Int(0, 1234567); a->meleepush_xy = zone->random.Int(0, 1234567);
a->source = GetHiddenTrigger()!=nullptr ? GetHiddenTrigger()->GetID() : trigger->GetID(); a->source = GetHiddenTrigger()!=nullptr ? GetHiddenTrigger()->GetID() : trigger->GetID();
a->spellid = 0; a->spellid = 0;
a->target = trigger->GetID(); a->target = trigger->GetID();