mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-13 06:21:28 +00:00
commit
87ea81cc36
@ -1783,6 +1783,15 @@ struct CombatAbility_Struct {
|
|||||||
uint32 m_skill;
|
uint32 m_skill;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Disarm Struct incoming from Client [Size: 16]
|
||||||
|
struct Disarm_Struct
|
||||||
|
{
|
||||||
|
uint32 source;
|
||||||
|
uint32 target;
|
||||||
|
uint32 skill;
|
||||||
|
uint32 unknown;
|
||||||
|
};
|
||||||
|
|
||||||
//Instill Doubt
|
//Instill Doubt
|
||||||
struct Instill_Doubt_Struct {
|
struct Instill_Doubt_Struct {
|
||||||
uint8 i_id;
|
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)
|
bool Client::BindWound(Mob *bindmob, bool start, bool fail)
|
||||||
{
|
{
|
||||||
EQApplicationPacket *outapp = nullptr;
|
EQApplicationPacket *outapp = nullptr;
|
||||||
|
|||||||
@ -803,6 +803,7 @@ public:
|
|||||||
|
|
||||||
void NPCSpawn(NPC *target_npc, const char *identifier, uint32 extra = 0);
|
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);
|
bool BindWound(Mob* bindmob, bool start, bool fail = false);
|
||||||
void SetTradeskillObject(Object* object) { m_tradeskill_object = object; }
|
void SetTradeskillObject(Object* object) { m_tradeskill_object = object; }
|
||||||
Object* GetTradeskillObject() { return m_tradeskill_object; }
|
Object* GetTradeskillObject() { return m_tradeskill_object; }
|
||||||
|
|||||||
@ -181,6 +181,7 @@ void MapOpcodes()
|
|||||||
ConnectedOpcodes[OP_DeleteItem] = &Client::Handle_OP_DeleteItem;
|
ConnectedOpcodes[OP_DeleteItem] = &Client::Handle_OP_DeleteItem;
|
||||||
ConnectedOpcodes[OP_DeleteSpawn] = &Client::Handle_OP_DeleteSpawn;
|
ConnectedOpcodes[OP_DeleteSpawn] = &Client::Handle_OP_DeleteSpawn;
|
||||||
ConnectedOpcodes[OP_DeleteSpell] = &Client::Handle_OP_DeleteSpell;
|
ConnectedOpcodes[OP_DeleteSpell] = &Client::Handle_OP_DeleteSpell;
|
||||||
|
ConnectedOpcodes[OP_Disarm] = &Client::Handle_OP_Disarm;
|
||||||
ConnectedOpcodes[OP_DisarmTraps] = &Client::Handle_OP_DisarmTraps;
|
ConnectedOpcodes[OP_DisarmTraps] = &Client::Handle_OP_DisarmTraps;
|
||||||
ConnectedOpcodes[OP_DoGroupLeadershipAbility] = &Client::Handle_OP_DoGroupLeadershipAbility;
|
ConnectedOpcodes[OP_DoGroupLeadershipAbility] = &Client::Handle_OP_DoGroupLeadershipAbility;
|
||||||
ConnectedOpcodes[OP_DuelResponse] = &Client::Handle_OP_DuelResponse;
|
ConnectedOpcodes[OP_DuelResponse] = &Client::Handle_OP_DuelResponse;
|
||||||
@ -5324,6 +5325,97 @@ void Client::Handle_OP_DeleteSpawn(const EQApplicationPacket *app)
|
|||||||
return;
|
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)
|
void Client::Handle_OP_DeleteSpell(const EQApplicationPacket *app)
|
||||||
{
|
{
|
||||||
if (app->size != sizeof(DeleteSpell_Struct))
|
if (app->size != sizeof(DeleteSpell_Struct))
|
||||||
|
|||||||
@ -94,6 +94,7 @@
|
|||||||
void Handle_OP_DeleteItem(const EQApplicationPacket *app);
|
void Handle_OP_DeleteItem(const EQApplicationPacket *app);
|
||||||
void Handle_OP_DeleteSpawn(const EQApplicationPacket *app);
|
void Handle_OP_DeleteSpawn(const EQApplicationPacket *app);
|
||||||
void Handle_OP_DeleteSpell(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_DisarmTraps(const EQApplicationPacket *app);
|
||||||
void Handle_OP_DoGroupLeadershipAbility(const EQApplicationPacket *app);
|
void Handle_OP_DoGroupLeadershipAbility(const EQApplicationPacket *app);
|
||||||
void Handle_OP_DuelResponse(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);
|
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) {
|
void Mob::NPCSpecialAttacks(const char* parse, int permtag, bool reset, bool remove) {
|
||||||
if(reset)
|
if(reset)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -278,6 +278,7 @@ public:
|
|||||||
void SetTaunting(bool tog) {taunting = tog;}
|
void SetTaunting(bool tog) {taunting = tog;}
|
||||||
bool IsTaunting() const { return taunting; }
|
bool IsTaunting() const { return taunting; }
|
||||||
void PickPocket(Client* thief);
|
void PickPocket(Client* thief);
|
||||||
|
void Disarm(Client* client, int chance);
|
||||||
void StartSwarmTimer(uint32 duration) { swarm_timer.Start(duration); }
|
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);
|
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);
|
virtual void DoClassAttacks(Mob *target);
|
||||||
|
|||||||
@ -301,6 +301,7 @@
|
|||||||
#define CORPSEDRAG_STOPALL 4065 //You stop dragging the corpses.
|
#define CORPSEDRAG_STOPALL 4065 //You stop dragging the corpses.
|
||||||
#define CORPSEDRAG_STOP 4066 //You stop dragging the corpse.
|
#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 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 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 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'
|
#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 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 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 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 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_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.
|
#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