mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-12 22:01:30 +00:00
commit
87ea81cc36
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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; }
|
||||
|
||||
@ -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))
|
||||
|
||||
@ -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);
|
||||
|
||||
59
zone/npc.cpp
59
zone/npc.cpp
@ -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)
|
||||
{
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user