Merge pull request #754 from fryguy503/disarm-implement

Disarm Support
This commit is contained in:
Michael Cook (mackal) 2018-07-21 23:43:25 -04:00 committed by GitHub
commit 87ea81cc36
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 221 additions and 0 deletions

View File

@ -1783,6 +1783,15 @@ struct CombatAbility_Struct {
uint32 m_skill;
};
// Disarm Struct incoming from Client [Size: 16]
struct Disarm_Struct
{
uint32 source;
uint32 target;
uint32 skill;
uint32 unknown;
};
//Instill Doubt
struct Instill_Doubt_Struct {
uint8 i_id;

View File

@ -2682,6 +2682,60 @@ void Client::LogMerchant(Client* player, Mob* merchant, uint32 quantity, uint32
}
}
void Client::Disarm(Client* disarmer, int chance) {
int16 slot = -1;
const EQEmu::ItemInstance *inst = this->GetInv().GetItem(EQEmu::invslot::slotPrimary);
if (inst && inst->IsWeapon()) {
slot = EQEmu::invslot::slotPrimary;
}
else {
inst = this->GetInv().GetItem(EQEmu::invslot::slotSecondary);
if (inst && inst->IsWeapon())
slot = EQEmu::invslot::slotSecondary;
}
if (slot != -1 && inst->IsClassCommon()) {
// We have an item that can be disarmed.
if (zone->random.Int(0, 1000) <= chance) {
// Find a free inventory slot
int16 slot_id = -1;
slot_id = m_inv.FindFreeSlot(false, true, inst->GetItem()->Size, inst->GetItem()->ItemType);
if (slot_id != -1)
{
EQEmu::ItemInstance *InvItem = m_inv.PopItem(slot);
if (InvItem) { // there should be no way it is not there, but check anyway
EQApplicationPacket* outapp = new EQApplicationPacket(OP_MoveItem, sizeof(MoveItem_Struct));
MoveItem_Struct* mi = (MoveItem_Struct*)outapp->pBuffer;
mi->from_slot = slot;
mi->to_slot = 0xFFFFFFFF;
if (inst->IsStackable()) // it should not be stackable
mi->number_in_stack = inst->GetCharges();
else
mi->number_in_stack = 0;
FastQueuePacket(&outapp); // this deletes item from the weapon slot on the client
if (PutItemInInventory(slot_id, *InvItem, true))
database.SaveInventory(this->CharacterID(), NULL, slot);
int matslot = slot == EQEmu::invslot::slotPrimary ? EQEmu::textures::weaponPrimary : EQEmu::textures::weaponSecondary;
if (matslot != -1)
SendWearChange(matslot);
}
Message_StringID(MT_Skills, DISARMED);
if (disarmer != this)
disarmer->Message_StringID(MT_Skills, DISARM_SUCCESS, this->GetCleanName());
if (chance != 1000)
disarmer->CheckIncreaseSkill(EQEmu::skills::SkillDisarm, nullptr, 4);
CalcBonuses();
// CalcEnduranceWeightFactor();
return;
}
disarmer->Message_StringID(MT_Skills, DISARM_FAILED);
if (chance != 1000)
disarmer->CheckIncreaseSkill(EQEmu::skills::SkillDisarm, nullptr, 2);
return;
}
}
disarmer->Message_StringID(MT_Skills, DISARM_FAILED);
}
bool Client::BindWound(Mob *bindmob, bool start, bool fail)
{
EQApplicationPacket *outapp = nullptr;

View File

@ -803,6 +803,7 @@ public:
void NPCSpawn(NPC *target_npc, const char *identifier, uint32 extra = 0);
void Disarm(Client* disarmer, int chance);
bool BindWound(Mob* bindmob, bool start, bool fail = false);
void SetTradeskillObject(Object* object) { m_tradeskill_object = object; }
Object* GetTradeskillObject() { return m_tradeskill_object; }

View File

@ -181,6 +181,7 @@ void MapOpcodes()
ConnectedOpcodes[OP_DeleteItem] = &Client::Handle_OP_DeleteItem;
ConnectedOpcodes[OP_DeleteSpawn] = &Client::Handle_OP_DeleteSpawn;
ConnectedOpcodes[OP_DeleteSpell] = &Client::Handle_OP_DeleteSpell;
ConnectedOpcodes[OP_Disarm] = &Client::Handle_OP_Disarm;
ConnectedOpcodes[OP_DisarmTraps] = &Client::Handle_OP_DisarmTraps;
ConnectedOpcodes[OP_DoGroupLeadershipAbility] = &Client::Handle_OP_DoGroupLeadershipAbility;
ConnectedOpcodes[OP_DuelResponse] = &Client::Handle_OP_DuelResponse;
@ -5324,6 +5325,97 @@ void Client::Handle_OP_DeleteSpawn(const EQApplicationPacket *app)
return;
}
void Client::Handle_OP_Disarm(const EQApplicationPacket *app) {
if (dead || bZoning) return;
if (!HasSkill(EQEmu::skills::SkillDisarm))
return;
if (app->size != sizeof(Disarm_Struct)) {
Log(Logs::General, Logs::Skills, "Size mismatch for Disarm_Struct packet");
return;
}
Disarm_Struct *disarm = (Disarm_Struct *)app->pBuffer;
if (!p_timers.Expired(&database, pTimerCombatAbility2, false)) {
Message(13, "Ability recovery time not yet met.");
return;
}
p_timers.Start(pTimerCombatAbility2, 8);
BreakInvis();
Mob* pmob = entity_list.GetMob(disarm->source);
Mob* tmob = entity_list.GetMob(disarm->target);
if (!pmob || !tmob)
return;
if (pmob->GetID() != GetID()) {
// Client sent a disarm request with an originator ID not matching their own ID.
char *hack_str = NULL;
MakeAnyLenString(&hack_str, "Player %s (%d) sent OP_Disarm with source ID of: %d", GetCleanName(), GetID(), pmob->GetID());
database.SetMQDetectionFlag(this->account_name, this->name, hack_str, zone->GetShortName());
safe_delete_array(hack_str);
return;
}
// No disarm on corpses
if (tmob->IsCorpse())
return;
// No target
if (!GetTarget())
return;
// Targets don't match (possible hack, but not flagging)
if (GetTarget() != tmob) {
return;
}
// Too far away
if (pmob->CalculateDistance(GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ()) > 400)
return;
// Can't see mob
//if (tmob->BehindMob(pmob))
// return;
// How can we disarm someone if we are feigned.
if (GetFeigned())
return;
// We can't disarm someone who is feigned.
if (tmob->IsClient() && tmob->CastToClient()->GetFeigned())
return;
if (GetTarget() == tmob && pmob == this->CastToMob() &&
disarm->skill == GetSkill(EQEmu::skills::SkillDisarm) && IsAttackAllowed(tmob)) {
int p_level = pmob->GetLevel() ? pmob->GetLevel() : 1;
int t_level = tmob->GetLevel() ? tmob->GetLevel() : 1;
// We have a disarmable target - sucess or fail, we always aggro the mob
if (tmob->IsNPC()) {
if (!tmob->CheckAggro(pmob)) {
zone->AddAggroMob();
tmob->AddToHateList(pmob, p_level);
}
else {
tmob->AddToHateList(pmob, p_level / 3);
}
}
int chance = GetSkill(EQEmu::skills::SkillDisarm); // (1% @ 0 skill) (11% @ 200 skill) - against even con
chance /= 2;
chance += 10;
// Modify chance based on level difference
float lvl_mod = p_level / t_level;
chance *= lvl_mod;
if (chance > 300)
chance = 300; // max chance of 30%
if (tmob->IsNPC()) {
tmob->CastToNPC()->Disarm(this, chance);
}
else if (tmob->IsClient()) {
tmob->CastToClient()->Disarm(this, chance);
}
return;
}
// Trying to disarm something we can't disarm
Message_StringID(MT_Skills, DISARM_NO_TARGET);
return;
}
void Client::Handle_OP_DeleteSpell(const EQApplicationPacket *app)
{
if (app->size != sizeof(DeleteSpell_Struct))

View File

@ -94,6 +94,7 @@
void Handle_OP_DeleteItem(const EQApplicationPacket *app);
void Handle_OP_DeleteSpawn(const EQApplicationPacket *app);
void Handle_OP_DeleteSpell(const EQApplicationPacket *app);
void Handle_OP_Disarm(const EQApplicationPacket *app);
void Handle_OP_DisarmTraps(const EQApplicationPacket *app);
void Handle_OP_DoGroupLeadershipAbility(const EQApplicationPacket *app);
void Handle_OP_DuelResponse(const EQApplicationPacket *app);

View File

@ -1691,6 +1691,65 @@ void NPC::PickPocket(Client* thief)
thief->SendPickPocketResponse(this, 0, PickPocketFailed);
}
void NPC::Disarm(Client* client, int chance) {
// disarm primary if available, otherwise disarm secondary
const EQEmu::ItemData* weapon = NULL;
uint8 eslot = 0xFF;
if (equipment[EQEmu::invslot::slotPrimary] != 0)
eslot = EQEmu::invslot::slotPrimary;
else if (equipment[EQEmu::invslot::slotSecondary] != 0)
eslot = EQEmu::invslot::slotSecondary;
if (eslot != 0xFF) {
if (zone->random.Int(0, 1000) <= chance) {
weapon = database.GetItem(equipment[eslot]);
if (weapon) {
if (!weapon->Magic && weapon->NoDrop == 255) {
int16 charges = -1;
ItemList::iterator cur, end;
cur = itemlist.begin();
end = itemlist.end();
// Get charges for the item in the loot table
for (; cur != end; cur++) {
ServerLootItem_Struct* citem = *cur;
if (citem->item_id == weapon->ID) {
charges = citem->charges;
break;
}
}
EQEmu::ItemInstance *inst = NULL;
inst = database.CreateItem(weapon->ID, charges);
// Remove item from loot table
RemoveItem(weapon->ID);
CalcBonuses();
if (inst) {
// create a ground item
Object* object = new Object(inst, this->GetX(), this->GetY(), this->GetZ(), 0.0f, 300000);
entity_list.AddObject(object, true);
object->StartDecay();
safe_delete(inst);
}
}
}
// Update Appearance
equipment[eslot] = 0;
int matslot = eslot == EQEmu::invslot::slotPrimary ? EQEmu::textures::weaponPrimary : EQEmu::textures::weaponSecondary;
if (matslot != -1)
SendWearChange(matslot);
if ((CastToMob()->GetBodyType() == BT_Humanoid || CastToMob()->GetBodyType() == BT_Summoned) && eslot == EQEmu::invslot::slotPrimary)
Say("Ahh! My weapon!");
client->Message_StringID(MT_Skills, DISARM_SUCCESS, this->GetCleanName());
if (chance != 1000)
client->CheckIncreaseSkill(EQEmu::skills::SkillDisarm, nullptr, 4);
return;
}
client->Message_StringID(MT_Skills, DISARM_FAILED);
if (chance != 1000)
client->CheckIncreaseSkill(EQEmu::skills::SkillDisarm, nullptr, 2);
return;
}
client->Message_StringID(MT_Skills, DISARM_FAILED);
}
void Mob::NPCSpecialAttacks(const char* parse, int permtag, bool reset, bool remove) {
if(reset)
{

View File

@ -278,6 +278,7 @@ public:
void SetTaunting(bool tog) {taunting = tog;}
bool IsTaunting() const { return taunting; }
void PickPocket(Client* thief);
void Disarm(Client* client, int chance);
void StartSwarmTimer(uint32 duration) { swarm_timer.Start(duration); }
void AddLootDrop(const EQEmu::ItemData*dbitem, ItemList* itemlistconst, int16 charges, uint8 minlevel, uint8 maxlevel, bool equipit, bool wearchange = false, uint32 aug1 = 0, uint32 aug2 = 0, uint32 aug3 = 0, uint32 aug4 = 0, uint32 aug5 = 0, uint32 aug6 = 0);
virtual void DoClassAttacks(Mob *target);

View File

@ -301,6 +301,7 @@
#define CORPSEDRAG_STOPALL 4065 //You stop dragging the corpses.
#define CORPSEDRAG_STOP 4066 //You stop dragging the corpse.
#define SOS_KEEPS_HIDDEN 4086 //Your Shroud of Stealth keeps you hidden from watchful eyes.␣␣
#define DISARM_NO_TARGET 4583 //You can't use disarm on that.
#define TARGET_TOO_CLOSE 4602 //You are too close to your target. Get farther away.
#define WHOALL_NO_RESULTS 5029 //There are no players in EverQuest that match those who filters.
#define TELL_QUEUED_MESSAGE 5045 //You told %1 '%T2. %3'
@ -446,6 +447,9 @@
#define TRY_ATTACKING_SOMEONE 12696 //Try attacking someone other than yourself, it's more productive
#define RANGED_TOO_CLOSE 12698 //Your target is too close to use a ranged weapon!
#define BACKSTAB_WEAPON 12874 //You need a piercing weapon as your primary weapon in order to backstab
#define DISARMED 12889 //You have been disarmed!
#define DISARM_SUCCESS 12890 //You disarmed %1!
#define DISARM_FAILED 12891 //Your attempt to disarm failed.
#define MORE_SKILLED_THAN_I 12931 //%1 tells you, 'You are more skilled than I! What could I possibly teach you?'
#define SURNAME_EXISTS 12939 //You already have a surname. Operation failed.
#define SURNAME_LEVEL 12940 //You can only submit a surname upon reaching the 20th level. Operation failed.