mirror of
https://github.com/EQEmu/Server.git
synced 2026-05-17 03:08:26 +00:00
Merge branch 'master' into eqstream
This commit is contained in:
+5
-5
@@ -1072,6 +1072,11 @@ bool Client::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, b
|
||||
//if weapon damage > 0 then we know we can hit the target with this weapon
|
||||
//otherwise we cannot and we set the damage to -5 later on
|
||||
if(weapon_damage > 0){
|
||||
auto shield_inc = spellbonuses.ShieldEquipDmgMod + itembonuses.ShieldEquipDmgMod + aabonuses.ShieldEquipDmgMod;
|
||||
if (shield_inc > 0 && HasShieldEquiped() && Hand == EQEmu::inventory::slotPrimary) {
|
||||
weapon_damage = weapon_damage * (100 + shield_inc) / 100;
|
||||
hate = hate * (100 + shield_inc) / 100;
|
||||
}
|
||||
|
||||
//Berserker Berserk damage bonus
|
||||
if(IsBerserk() && GetClass() == BERSERKER){
|
||||
@@ -2291,11 +2296,6 @@ void Mob::AddToHateList(Mob* other, uint32 hate /*= 0*/, int32 damage /*= 0*/, b
|
||||
// Spell Casting Subtlety etc
|
||||
int hatemod = 100 + other->spellbonuses.hatemod + other->itembonuses.hatemod + other->aabonuses.hatemod;
|
||||
|
||||
int32 shieldhatemod = other->spellbonuses.ShieldEquipHateMod + other->itembonuses.ShieldEquipHateMod + other->aabonuses.ShieldEquipHateMod;
|
||||
|
||||
if (shieldhatemod && other->HasShieldEquiped())
|
||||
hatemod += shieldhatemod;
|
||||
|
||||
if(hatemod < 1)
|
||||
hatemod = 1;
|
||||
hate = ((hate * (hatemod))/100);
|
||||
|
||||
+5
-23
@@ -940,12 +940,8 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon)
|
||||
case SE_ShieldBlock:
|
||||
newbon->ShieldBlock += base1;
|
||||
break;
|
||||
case SE_ShieldEquipHateMod:
|
||||
newbon->ShieldEquipHateMod += base1;
|
||||
break;
|
||||
case SE_ShieldEquipDmgMod:
|
||||
newbon->ShieldEquipDmgMod[0] += base1;
|
||||
newbon->ShieldEquipDmgMod[1] += base2;
|
||||
newbon->ShieldEquipDmgMod += base1;
|
||||
break;
|
||||
case SE_SecondaryDmgInc:
|
||||
newbon->SecondaryDmgInc = true;
|
||||
@@ -2655,13 +2651,8 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne
|
||||
new_bonus->ShieldBlock += effect_value;
|
||||
break;
|
||||
|
||||
case SE_ShieldEquipHateMod:
|
||||
new_bonus->ShieldEquipHateMod += effect_value;
|
||||
break;
|
||||
|
||||
case SE_ShieldEquipDmgMod:
|
||||
new_bonus->ShieldEquipDmgMod[0] += effect_value;
|
||||
new_bonus->ShieldEquipDmgMod[1] += base2;
|
||||
new_bonus->ShieldEquipDmgMod += effect_value;
|
||||
break;
|
||||
|
||||
case SE_BlockBehind:
|
||||
@@ -4555,19 +4546,10 @@ void Mob::NegateSpellsBonuses(uint16 spell_id)
|
||||
itembonuses.DoubleRangedAttack = effect_value;
|
||||
break;
|
||||
|
||||
case SE_ShieldEquipHateMod:
|
||||
spellbonuses.ShieldEquipHateMod = effect_value;
|
||||
aabonuses.ShieldEquipHateMod = effect_value;
|
||||
itembonuses.ShieldEquipHateMod = effect_value;
|
||||
break;
|
||||
|
||||
case SE_ShieldEquipDmgMod:
|
||||
spellbonuses.ShieldEquipDmgMod[0] = effect_value;
|
||||
spellbonuses.ShieldEquipDmgMod[1] = effect_value;
|
||||
aabonuses.ShieldEquipDmgMod[0] = effect_value;
|
||||
aabonuses.ShieldEquipDmgMod[1] = effect_value;
|
||||
itembonuses.ShieldEquipDmgMod[0] = effect_value;
|
||||
itembonuses.ShieldEquipDmgMod[1] = effect_value;
|
||||
spellbonuses.ShieldEquipDmgMod = effect_value;
|
||||
aabonuses.ShieldEquipDmgMod = effect_value;
|
||||
itembonuses.ShieldEquipDmgMod = effect_value;
|
||||
break;
|
||||
|
||||
case SE_TriggerMeleeThreshold:
|
||||
|
||||
+2
-2
@@ -2391,7 +2391,7 @@ void Bot::AI_Process() {
|
||||
}
|
||||
}
|
||||
atCombatRange = true;
|
||||
} else if(IsBotCaster() && GetLevel() > 12) {
|
||||
} else if(IsBotCaster() && GetLevel() >= RuleI(Bots, CasterStopMeleeLevel)) {
|
||||
if(IsBotCasterCombatRange(GetTarget()))
|
||||
atCombatRange = true;
|
||||
}
|
||||
@@ -2440,7 +2440,7 @@ void Bot::AI_Process() {
|
||||
if(GetTarget()->GetHPRatio() <= 99.0f)
|
||||
BotRangedAttack(GetTarget());
|
||||
}
|
||||
else if(!IsBotArcher() && (!(IsBotCaster() && GetLevel() > 12)) && GetTarget() && !IsStunned() && !IsMezzed() && (GetAppearance() != eaDead)) {
|
||||
else if(!IsBotArcher() && (!(IsBotCaster() && GetLevel() >= RuleI(Bots, CasterStopMeleeLevel))) && GetTarget() && !IsStunned() && !IsMezzed() && (GetAppearance() != eaDead)) {
|
||||
// we can't fight if we don't have a target, are stun/mezzed or dead..
|
||||
// Stop attacking if the target is enraged
|
||||
if((IsEngaged() && !BehindMob(GetTarget(), GetX(), GetY()) && GetTarget()->IsEnraged()) || GetBotStance() == BotStancePassive)
|
||||
|
||||
+2
-1
@@ -825,7 +825,7 @@ public:
|
||||
bool SummonItem(uint32 item_id, int16 charges = -1, uint32 aug1 = 0, uint32 aug2 = 0, uint32 aug3 = 0, uint32 aug4 = 0, uint32 aug5 = 0, uint32 aug6 = 0, bool attuned = false, uint16 to_slot = EQEmu::inventory::slotCursor, uint32 ornament_icon = 0, uint32 ornament_idfile = 0, uint32 ornament_hero_model = 0);
|
||||
void SetStats(uint8 type,int16 set_val);
|
||||
void IncStats(uint8 type,int16 increase_val);
|
||||
void DropItem(int16 slot_id);
|
||||
void DropItem(int16 slot_id, bool recurse = true);
|
||||
|
||||
int GetItemLinkHash(const EQEmu::ItemInstance* inst); // move to ItemData..or make use of the pre-calculated database field
|
||||
|
||||
@@ -855,6 +855,7 @@ public:
|
||||
void SetConsumption(int32 in_hunger, int32 in_thirst);
|
||||
|
||||
bool CheckTradeLoreConflict(Client* other);
|
||||
bool CheckTradeNonDroppable();
|
||||
void LinkDead();
|
||||
void Insight(uint32 t_id);
|
||||
bool CheckDoubleAttack();
|
||||
|
||||
+27
-1
@@ -5003,6 +5003,9 @@ void Client::Handle_OP_CrashDump(const EQApplicationPacket *app)
|
||||
|
||||
void Client::Handle_OP_CreateObject(const EQApplicationPacket *app)
|
||||
{
|
||||
if (Log.log_settings[Logs::Inventory].is_category_enabled)
|
||||
Log.Out(Logs::Detail, Logs::Inventory, "Handle_OP_CreateObject() [psize: %u] %s", app->size, DumpPacketToString(app).c_str());
|
||||
|
||||
DropItem(EQEmu::inventory::slotCursor);
|
||||
return;
|
||||
}
|
||||
@@ -13287,7 +13290,6 @@ void Client::Handle_OP_TradeAcceptClick(const EQApplicationPacket *app)
|
||||
other->trade->state = TradeCompleting;
|
||||
trade->state = TradeCompleting;
|
||||
|
||||
// should we do this for NoDrop items as well?
|
||||
if (CheckTradeLoreConflict(other) || other->CheckTradeLoreConflict(this)) {
|
||||
Message_StringID(13, TRADE_CANCEL_LORE);
|
||||
other->Message_StringID(13, TRADE_CANCEL_LORE);
|
||||
@@ -13296,6 +13298,26 @@ void Client::Handle_OP_TradeAcceptClick(const EQApplicationPacket *app)
|
||||
other->trade->Reset();
|
||||
trade->Reset();
|
||||
}
|
||||
else if (CheckTradeNonDroppable()) {
|
||||
Message_StringID(13, TRADE_HAS_BEEN_CANCELLED);
|
||||
other->Message_StringID(13, TRADE_HAS_BEEN_CANCELLED);
|
||||
this->FinishTrade(this);
|
||||
other->FinishTrade(other);
|
||||
other->trade->Reset();
|
||||
trade->Reset();
|
||||
Message(15, "Hacking activity detected in trade transaction.");
|
||||
// TODO: query (this) as a hacker
|
||||
}
|
||||
else if (other->CheckTradeNonDroppable()) {
|
||||
Message_StringID(13, TRADE_HAS_BEEN_CANCELLED);
|
||||
other->Message_StringID(13, TRADE_HAS_BEEN_CANCELLED);
|
||||
this->FinishTrade(this);
|
||||
other->FinishTrade(other);
|
||||
other->trade->Reset();
|
||||
trade->Reset();
|
||||
other->Message(15, "Hacking activity detected in trade transaction.");
|
||||
// TODO: query (other) as a hacker
|
||||
}
|
||||
else {
|
||||
// Audit trade to database for both trade streams
|
||||
other->trade->LogTrade();
|
||||
@@ -14030,6 +14052,10 @@ void Client::Handle_OP_WearChange(const EQApplicationPacket *app)
|
||||
if (wc->spawn_id != GetID())
|
||||
return;
|
||||
|
||||
// Hero Forge ID needs to be fixed here as RoF2 appears to send an incorrect value.
|
||||
if (wc->hero_forge_model != 0 && wc->wear_slot_id >= 0 && wc->wear_slot_id < EQEmu::textures::weaponPrimary)
|
||||
wc->hero_forge_model = GetHerosForgeModel(wc->wear_slot_id);
|
||||
|
||||
// we could maybe ignore this and just send our own from moveitem
|
||||
entity_list.QueueClients(this, app, true);
|
||||
return;
|
||||
|
||||
@@ -393,6 +393,7 @@ bool Client::Process() {
|
||||
{
|
||||
EQEmu::ItemInstance *wpn = GetInv().GetItem(EQEmu::inventory::slotPrimary);
|
||||
TryWeaponProc(wpn, auto_attack_target, EQEmu::inventory::slotPrimary);
|
||||
TriggerDefensiveProcs(auto_attack_target, EQEmu::inventory::slotPrimary, false);
|
||||
|
||||
DoAttackRounds(auto_attack_target, EQEmu::inventory::slotPrimary);
|
||||
if (CheckAATimer(aaTimerRampage))
|
||||
|
||||
+4
-13
@@ -745,24 +745,15 @@ void command_wc(Client *c, const Seperator *sep)
|
||||
uint32 hero_forge_model = 0;
|
||||
uint32 wearslot = atoi(sep->arg[1]);
|
||||
|
||||
// Hero Forge
|
||||
if (sep->argnum > 2)
|
||||
{
|
||||
hero_forge_model = atoi(sep->arg[3]);
|
||||
if (hero_forge_model > 0)
|
||||
{
|
||||
// Conversion to simplify the command arguments
|
||||
// Hero's Forge model is actually model * 1000 + texture * 100 + wearslot
|
||||
hero_forge_model *= 1000;
|
||||
hero_forge_model += (atoi(sep->arg[2]) * 100);
|
||||
hero_forge_model += wearslot;
|
||||
|
||||
// For Hero's Forge, slot 7 is actually for Robes, but it still needs to use slot 1 in the packet
|
||||
if (wearslot == 7)
|
||||
{
|
||||
wearslot = 1;
|
||||
}
|
||||
if (hero_forge_model != 0 && hero_forge_model < 1000) {
|
||||
// Shorthand Hero Forge ID. Otherwise use the value the user entered.
|
||||
hero_forge_model = (hero_forge_model * 100) + wearslot;
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
// Leaving here to add color option to the #wc command eventually
|
||||
|
||||
+1
-2
@@ -457,8 +457,7 @@ struct StatBonuses {
|
||||
int32 ItemATKCap; // Raise item attack cap
|
||||
int32 FinishingBlow[2]; // Chance to do a finishing blow for specified damage amount.
|
||||
uint32 FinishingBlowLvl[2]; // Sets max level an NPC can be affected by FB. (base1 = lv, base2= ???)
|
||||
int32 ShieldEquipHateMod; // Hate mod when shield equiped.
|
||||
int32 ShieldEquipDmgMod[2]; // Damage mod when shield equiped. 0 = damage modifier 1 = Unknown
|
||||
int32 ShieldEquipDmgMod; // Increases weapon's base damage by base1 % when shield is equipped (indirectly increasing hate)
|
||||
bool TriggerOnValueAmount; // Triggers off various different conditions, bool to check if client has effect.
|
||||
int8 StunBashChance; // chance to stun with bash.
|
||||
int8 IncreaseChanceMemwipe; // increases chance to memory wipe
|
||||
|
||||
+85
-61
@@ -36,7 +36,6 @@ Child of the Mob class.
|
||||
#include "../common/string_util.h"
|
||||
#include "../common/say_link.h"
|
||||
|
||||
#include "client.h"
|
||||
#include "corpse.h"
|
||||
#include "entity.h"
|
||||
#include "groups.h"
|
||||
@@ -1063,77 +1062,86 @@ void Corpse::MakeLootRequestPackets(Client* client, const EQApplicationPacket* a
|
||||
SendLootReqErrorPacket(client, LootResponse::LootAll);
|
||||
}
|
||||
|
||||
void Corpse::LootItem(Client* client, const EQApplicationPacket* app) {
|
||||
/* This gets sent no matter what as a sort of ACK */
|
||||
client->QueuePacket(app);
|
||||
void Corpse::LootItem(Client *client, const EQApplicationPacket *app)
|
||||
{
|
||||
auto lootitem = (LootingItem_Struct *)app->pBuffer;
|
||||
|
||||
if (!loot_cooldown_timer.Check()) {
|
||||
client->QueuePacket(app);
|
||||
SendEndLootErrorPacket(client);
|
||||
//unlock corpse for others
|
||||
if (this->being_looted_by = client->GetID()) {
|
||||
being_looted_by = 0xFFFFFFFF;
|
||||
}
|
||||
// unlock corpse for others
|
||||
if (IsBeingLootedBy(client))
|
||||
ResetLooter();
|
||||
return;
|
||||
}
|
||||
|
||||
/* To prevent item loss for a player using 'Loot All' who doesn't have inventory space for all their items. */
|
||||
if (RuleB(Character, CheckCursorEmptyWhenLooting) && !client->GetInv().CursorEmpty()) {
|
||||
client->Message(13, "You may not loot an item while you have an item on your cursor.");
|
||||
client->QueuePacket(app);
|
||||
SendEndLootErrorPacket(client);
|
||||
/* Unlock corpse for others */
|
||||
if (this->being_looted_by = client->GetID()) {
|
||||
being_looted_by = 0xFFFFFFFF;
|
||||
}
|
||||
if (IsBeingLootedBy(client))
|
||||
ResetLooter();
|
||||
return;
|
||||
}
|
||||
|
||||
LootingItem_Struct* lootitem = (LootingItem_Struct*)app->pBuffer;
|
||||
|
||||
if (this->being_looted_by != client->GetID()) {
|
||||
if (!IsBeingLootedBy(client)) {
|
||||
client->Message(13, "Error: Corpse::LootItem: BeingLootedBy != client");
|
||||
client->QueuePacket(app);
|
||||
SendEndLootErrorPacket(client);
|
||||
return;
|
||||
}
|
||||
if (IsPlayerCorpse() && !CanPlayerLoot(client->CharacterID()) && !become_npc && (char_id != client->CharacterID() && client->Admin() < 150)) {
|
||||
|
||||
if (IsPlayerCorpse() && !CanPlayerLoot(client->CharacterID()) && !become_npc &&
|
||||
(char_id != client->CharacterID() && client->Admin() < 150)) {
|
||||
client->Message(13, "Error: This is a player corpse and you dont own it.");
|
||||
client->QueuePacket(app);
|
||||
SendEndLootErrorPacket(client);
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_locked && client->Admin() < 100) {
|
||||
client->QueuePacket(app);
|
||||
SendLootReqErrorPacket(client, LootResponse::SomeoneElse);
|
||||
client->Message(13, "Error: Corpse locked by GM.");
|
||||
return;
|
||||
}
|
||||
if (IsPlayerCorpse() && (char_id != client->CharacterID()) && CanPlayerLoot(client->CharacterID()) && GetPlayerKillItem() == 0){
|
||||
|
||||
if (IsPlayerCorpse() && (char_id != client->CharacterID()) && CanPlayerLoot(client->CharacterID()) &&
|
||||
GetPlayerKillItem() == 0) {
|
||||
client->Message(13, "Error: You cannot loot any more items from this corpse.");
|
||||
client->QueuePacket(app);
|
||||
SendEndLootErrorPacket(client);
|
||||
being_looted_by = 0xFFFFFFFF;
|
||||
ResetLooter();
|
||||
return;
|
||||
}
|
||||
const EQEmu::ItemData* item = 0;
|
||||
|
||||
const EQEmu::ItemData *item = 0;
|
||||
EQEmu::ItemInstance *inst = 0;
|
||||
ServerLootItem_Struct* item_data = nullptr, *bag_item_data[10];
|
||||
ServerLootItem_Struct *item_data = nullptr, *bag_item_data[10];
|
||||
|
||||
memset(bag_item_data, 0, sizeof(bag_item_data));
|
||||
if (GetPlayerKillItem() > 1){
|
||||
if (GetPlayerKillItem() > 1) {
|
||||
item = database.GetItem(GetPlayerKillItem());
|
||||
}
|
||||
else if (GetPlayerKillItem() == -1 || GetPlayerKillItem() == 1){
|
||||
item_data = GetItem(lootitem->slot_id - EQEmu::legacy::CORPSE_BEGIN); //dont allow them to loot entire bags of items as pvp reward
|
||||
}
|
||||
else{
|
||||
} else if (GetPlayerKillItem() == -1 || GetPlayerKillItem() == 1) {
|
||||
item_data =
|
||||
GetItem(lootitem->slot_id -
|
||||
EQEmu::legacy::CORPSE_BEGIN); // dont allow them to loot entire bags of items as pvp reward
|
||||
} else {
|
||||
item_data = GetItem(lootitem->slot_id - EQEmu::legacy::CORPSE_BEGIN, bag_item_data);
|
||||
}
|
||||
|
||||
if (GetPlayerKillItem()<=1 && item_data != 0) {
|
||||
if (GetPlayerKillItem() <= 1 && item_data != 0) {
|
||||
item = database.GetItem(item_data->item_id);
|
||||
}
|
||||
|
||||
if (item != 0) {
|
||||
if (item_data){
|
||||
inst = database.CreateItem(item, item_data ? item_data->charges : 0, item_data->aug_1, item_data->aug_2, item_data->aug_3, item_data->aug_4, item_data->aug_5, item_data->aug_6, item_data->attuned);
|
||||
}
|
||||
else {
|
||||
if (item_data) {
|
||||
inst = database.CreateItem(item, item_data ? item_data->charges : 0, item_data->aug_1,
|
||||
item_data->aug_2, item_data->aug_3, item_data->aug_4,
|
||||
item_data->aug_5, item_data->aug_6, item_data->attuned);
|
||||
} else {
|
||||
inst = database.CreateItem(item);
|
||||
}
|
||||
}
|
||||
@@ -1141,8 +1149,9 @@ void Corpse::LootItem(Client* client, const EQApplicationPacket* app) {
|
||||
if (client && inst) {
|
||||
if (client->CheckLoreConflict(item)) {
|
||||
client->Message_StringID(0, LOOT_LORE_ERROR);
|
||||
client->QueuePacket(app);
|
||||
SendEndLootErrorPacket(client);
|
||||
being_looted_by = 0;
|
||||
ResetLooter();
|
||||
delete inst;
|
||||
return;
|
||||
}
|
||||
@@ -1153,8 +1162,9 @@ void Corpse::LootItem(Client* client, const EQApplicationPacket* app) {
|
||||
if (itm) {
|
||||
if (client->CheckLoreConflict(itm->GetItem())) {
|
||||
client->Message_StringID(0, LOOT_LORE_ERROR);
|
||||
client->QueuePacket(app);
|
||||
SendEndLootErrorPacket(client);
|
||||
being_looted_by = 0;
|
||||
ResetLooter();
|
||||
delete inst;
|
||||
return;
|
||||
}
|
||||
@@ -1165,21 +1175,32 @@ void Corpse::LootItem(Client* client, const EQApplicationPacket* app) {
|
||||
char buf[88];
|
||||
char q_corpse_name[64];
|
||||
strcpy(q_corpse_name, corpse_name);
|
||||
snprintf(buf, 87, "%d %d %s", inst->GetItem()->ID, inst->GetCharges(), EntityList::RemoveNumbers(q_corpse_name));
|
||||
snprintf(buf, 87, "%d %d %s", inst->GetItem()->ID, inst->GetCharges(),
|
||||
EntityList::RemoveNumbers(q_corpse_name));
|
||||
buf[87] = '\0';
|
||||
std::vector<EQEmu::Any> args;
|
||||
args.push_back(inst);
|
||||
args.push_back(this);
|
||||
parse->EventPlayer(EVENT_LOOT, client, buf, 0, &args);
|
||||
if (parse->EventPlayer(EVENT_LOOT, client, buf, 0, &args) != 0) {
|
||||
lootitem->auto_loot = -1;
|
||||
client->Message_StringID(CC_Red, LOOT_NOT_ALLOWED, inst->GetItem()->Name);
|
||||
client->QueuePacket(app);
|
||||
delete inst;
|
||||
return;
|
||||
}
|
||||
// do we want this to have a fail option too?
|
||||
parse->EventItem(EVENT_LOOT, client, inst, this, buf, 0);
|
||||
|
||||
// safe to ACK now
|
||||
client->QueuePacket(app);
|
||||
|
||||
if (!IsPlayerCorpse() && RuleB(Character, EnableDiscoveredItems)) {
|
||||
if (client && !client->GetGM() && !client->IsDiscovered(inst->GetItem()->ID))
|
||||
client->DiscoverItem(inst->GetItem()->ID);
|
||||
}
|
||||
|
||||
if (zone->adv_data) {
|
||||
ServerZoneAdventureDataReply_Struct *ad = (ServerZoneAdventureDataReply_Struct*)zone->adv_data;
|
||||
ServerZoneAdventureDataReply_Struct *ad = (ServerZoneAdventureDataReply_Struct *)zone->adv_data;
|
||||
if (ad->type == Adventure_Collect && !IsPlayerCorpse()) {
|
||||
if (ad->data_id == inst->GetItem()->ID) {
|
||||
zone->DoAdventureCountIncrease();
|
||||
@@ -1188,11 +1209,10 @@ void Corpse::LootItem(Client* client, const EQApplicationPacket* app) {
|
||||
}
|
||||
|
||||
/* First add it to the looter - this will do the bag contents too */
|
||||
if (lootitem->auto_loot) {
|
||||
if (lootitem->auto_loot > 0) {
|
||||
if (!client->AutoPutLootInInventory(*inst, true, true, bag_item_data))
|
||||
client->PutLootInInventory(EQEmu::inventory::slotCursor, *inst, bag_item_data);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
client->PutLootInInventory(EQEmu::inventory::slotCursor, *inst, bag_item_data);
|
||||
}
|
||||
|
||||
@@ -1201,9 +1221,11 @@ void Corpse::LootItem(Client* client, const EQApplicationPacket* app) {
|
||||
client->UpdateTasksForItem(ActivityLoot, item->ID);
|
||||
|
||||
/* Remove it from Corpse */
|
||||
if (item_data){
|
||||
/* Delete needs to be before RemoveItem because its deletes the pointer for item_data/bag_item_data */
|
||||
database.DeleteItemOffCharacterCorpse(this->corpse_db_id, item_data->equip_slot, item_data->item_id);
|
||||
if (item_data) {
|
||||
/* Delete needs to be before RemoveItem because its deletes the pointer for
|
||||
* item_data/bag_item_data */
|
||||
database.DeleteItemOffCharacterCorpse(this->corpse_db_id, item_data->equip_slot,
|
||||
item_data->item_id);
|
||||
/* Delete Item Instance */
|
||||
RemoveItem(item_data->lootslot);
|
||||
}
|
||||
@@ -1212,8 +1234,11 @@ void Corpse::LootItem(Client* client, const EQApplicationPacket* app) {
|
||||
if (item->IsClassBag() && (GetPlayerKillItem() != -1 || GetPlayerKillItem() != 1)) {
|
||||
for (int i = EQEmu::inventory::containerBegin; i < EQEmu::inventory::ContainerCount; i++) {
|
||||
if (bag_item_data[i]) {
|
||||
/* Delete needs to be before RemoveItem because its deletes the pointer for item_data/bag_item_data */
|
||||
database.DeleteItemOffCharacterCorpse(this->corpse_db_id, bag_item_data[i]->equip_slot, bag_item_data[i]->item_id);
|
||||
/* Delete needs to be before RemoveItem because its deletes the pointer for
|
||||
* item_data/bag_item_data */
|
||||
database.DeleteItemOffCharacterCorpse(this->corpse_db_id,
|
||||
bag_item_data[i]->equip_slot,
|
||||
bag_item_data[i]->item_id);
|
||||
/* Delete Item Instance */
|
||||
RemoveItem(bag_item_data[i]);
|
||||
}
|
||||
@@ -1224,38 +1249,37 @@ void Corpse::LootItem(Client* client, const EQApplicationPacket* app) {
|
||||
SetPlayerKillItemID(0);
|
||||
}
|
||||
|
||||
/* Send message with item link to groups and such */
|
||||
EQEmu::SayLinkEngine linker;
|
||||
linker.SetLinkType(EQEmu::saylink::SayLinkItemInst);
|
||||
linker.SetItemInst(inst);
|
||||
/* Send message with item link to groups and such */
|
||||
EQEmu::SayLinkEngine linker;
|
||||
linker.SetLinkType(EQEmu::saylink::SayLinkItemInst);
|
||||
linker.SetItemInst(inst);
|
||||
|
||||
auto item_link = linker.GenerateLink();
|
||||
auto item_link = linker.GenerateLink();
|
||||
|
||||
client->Message_StringID(MT_LootMessages, LOOTED_MESSAGE, item_link.c_str());
|
||||
client->Message_StringID(MT_LootMessages, LOOTED_MESSAGE, item_link.c_str());
|
||||
|
||||
if (!IsPlayerCorpse()) {
|
||||
if (!IsPlayerCorpse()) {
|
||||
Group *g = client->GetGroup();
|
||||
if(g != nullptr) {
|
||||
g->GroupMessage_StringID(client, MT_LootMessages, OTHER_LOOTED_MESSAGE, client->GetName(), item_link.c_str());
|
||||
}
|
||||
else {
|
||||
if (g != nullptr) {
|
||||
g->GroupMessage_StringID(client, MT_LootMessages, OTHER_LOOTED_MESSAGE,
|
||||
client->GetName(), item_link.c_str());
|
||||
} else {
|
||||
Raid *r = client->GetRaid();
|
||||
if(r != nullptr) {
|
||||
r->RaidMessage_StringID(client, MT_LootMessages, OTHER_LOOTED_MESSAGE, client->GetName(), item_link.c_str());
|
||||
if (r != nullptr) {
|
||||
r->RaidMessage_StringID(client, MT_LootMessages, OTHER_LOOTED_MESSAGE,
|
||||
client->GetName(), item_link.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
SendEndLootErrorPacket(client);
|
||||
safe_delete(inst);
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsPlayerCorpse()){
|
||||
if (IsPlayerCorpse()) {
|
||||
client->SendItemLink(inst);
|
||||
}
|
||||
else{
|
||||
} else {
|
||||
client->SendItemLink(inst, true);
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -20,8 +20,8 @@
|
||||
#define CORPSE_H
|
||||
|
||||
#include "mob.h"
|
||||
#include "client.h"
|
||||
|
||||
class Client;
|
||||
class EQApplicationPacket;
|
||||
class Group;
|
||||
class NPC;
|
||||
@@ -118,6 +118,7 @@ class Corpse : public Mob {
|
||||
inline bool IsLocked() { return is_locked; }
|
||||
inline void ResetLooter() { being_looted_by = 0xFFFFFFFF; }
|
||||
inline bool IsBeingLooted() { return (being_looted_by != 0xFFFFFFFF); }
|
||||
inline bool IsBeingLootedBy(Client *c) { return being_looted_by == c->GetID(); }
|
||||
|
||||
/* Mob */
|
||||
void FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho);
|
||||
|
||||
+7
-1
@@ -306,7 +306,13 @@ void Client::GoFish()
|
||||
|
||||
const EQEmu::ItemData* food_item = database.GetItem(food_id);
|
||||
|
||||
Message_StringID(MT_Skills, FISHING_SUCCESS);
|
||||
if (food_item->ItemType != EQEmu::item::ItemTypeFood) {
|
||||
Message_StringID(MT_Skills, FISHING_SUCCESS);
|
||||
}
|
||||
else {
|
||||
Message_StringID(MT_Skills, FISHING_SUCCESS_FISH_NAME, food_item->Name);
|
||||
}
|
||||
|
||||
EQEmu::ItemInstance* inst = database.CreateItem(food_item, 1);
|
||||
if(inst != nullptr) {
|
||||
if(CheckLoreConflict(inst->GetItem()))
|
||||
|
||||
+59
-3
@@ -594,10 +594,37 @@ bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2,
|
||||
}
|
||||
|
||||
// Drop item from inventory to ground (generally only dropped from SLOT_CURSOR)
|
||||
void Client::DropItem(int16 slot_id)
|
||||
void Client::DropItem(int16 slot_id, bool recurse)
|
||||
{
|
||||
if(GetInv().CheckNoDrop(slot_id) && RuleI(World, FVNoDropFlag) == 0 ||
|
||||
RuleI(Character, MinStatusForNoDropExemptions) < Admin() && RuleI(World, FVNoDropFlag) == 2) {
|
||||
Log.Out(Logs::General, Logs::Inventory, "'%s' (char_id: %u) Attempting to drop item from slot %i on the ground",
|
||||
GetCleanName(), CharacterID(), slot_id);
|
||||
|
||||
if(GetInv().CheckNoDrop(slot_id, recurse) && RuleI(World, FVNoDropFlag) == 0 ||
|
||||
RuleI(Character, MinStatusForNoDropExemptions) < Admin() && RuleI(World, FVNoDropFlag) == 2)
|
||||
{
|
||||
auto invalid_drop = m_inv.GetItem(slot_id);
|
||||
if (!invalid_drop) {
|
||||
Log.Out(Logs::General, Logs::Inventory, "Error in InventoryProfile::CheckNoDrop() - returned 'true' for empty slot");
|
||||
}
|
||||
else {
|
||||
if (Log.log_settings[Logs::Inventory].is_category_enabled) {
|
||||
Log.Out(Logs::General, Logs::Inventory, "DropItem() Hack detected - full item parse:");
|
||||
Log.Out(Logs::General, Logs::Inventory, "depth: 0, Item: '%s' (id: %u), IsDroppable: %s",
|
||||
(invalid_drop->GetItem() ? invalid_drop->GetItem()->Name : "null data"), invalid_drop->GetID(), invalid_drop->IsDroppable(false));
|
||||
|
||||
for (auto iter1 : *invalid_drop->GetContents()) { // depth 1
|
||||
Log.Out(Logs::General, Logs::Inventory, "-depth: 1, Item: '%s' (id: %u), IsDroppable: %s",
|
||||
(iter1.second->GetItem() ? iter1.second->GetItem()->Name : "null data"), iter1.second->GetID(), iter1.second->IsDroppable(false));
|
||||
|
||||
for (auto iter2 : *iter1.second->GetContents()) { // depth 2
|
||||
Log.Out(Logs::General, Logs::Inventory, "--depth: 2, Item: '%s' (id: %u), IsDroppable: %s",
|
||||
(iter2.second->GetItem() ? iter2.second->GetItem()->Name : "null data"), iter2.second->GetID(), iter2.second->IsDroppable(false));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
invalid_drop = nullptr;
|
||||
|
||||
database.SetHackerFlag(this->AccountName(), this->GetCleanName(), "Tried to drop an item on the ground that was nodrop!");
|
||||
GetInv().DeleteItem(slot_id);
|
||||
return;
|
||||
@@ -606,12 +633,39 @@ void Client::DropItem(int16 slot_id)
|
||||
// Take control of item in client inventory
|
||||
EQEmu::ItemInstance *inst = m_inv.PopItem(slot_id);
|
||||
if(inst) {
|
||||
if (Log.log_settings[Logs::Inventory].is_category_enabled) {
|
||||
Log.Out(Logs::General, Logs::Inventory, "DropItem() Processing - full item parse:");
|
||||
Log.Out(Logs::General, Logs::Inventory, "depth: 0, Item: '%s' (id: %u), IsDroppable: %s",
|
||||
(inst->GetItem() ? inst->GetItem()->Name : "null data"), inst->GetID(), inst->IsDroppable(false));
|
||||
|
||||
if (!inst->IsDroppable(false))
|
||||
Log.Out(Logs::General, Logs::Error, "Non-droppable item being processed for drop by '%s'", GetCleanName());
|
||||
|
||||
for (auto iter1 : *inst->GetContents()) { // depth 1
|
||||
Log.Out(Logs::General, Logs::Inventory, "-depth: 1, Item: '%s' (id: %u), IsDroppable: %s",
|
||||
(iter1.second->GetItem() ? iter1.second->GetItem()->Name : "null data"), iter1.second->GetID(), iter1.second->IsDroppable(false));
|
||||
|
||||
if (!iter1.second->IsDroppable(false))
|
||||
Log.Out(Logs::General, Logs::Error, "Non-droppable item being processed for drop by '%s'", GetCleanName());
|
||||
|
||||
for (auto iter2 : *iter1.second->GetContents()) { // depth 2
|
||||
Log.Out(Logs::General, Logs::Inventory, "--depth: 2, Item: '%s' (id: %u), IsDroppable: %s",
|
||||
(iter2.second->GetItem() ? iter2.second->GetItem()->Name : "null data"), iter2.second->GetID(), iter2.second->IsDroppable(false));
|
||||
|
||||
if (!iter2.second->IsDroppable(false))
|
||||
Log.Out(Logs::General, Logs::Error, "Non-droppable item being processed for drop by '%s'", GetCleanName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int i = parse->EventItem(EVENT_DROP_ITEM, this, inst, nullptr, "", slot_id);
|
||||
if(i != 0) {
|
||||
Log.Out(Logs::General, Logs::Inventory, "Item drop handled by [EVENT_DROP_ITEM]");
|
||||
safe_delete(inst);
|
||||
}
|
||||
} else {
|
||||
// Item doesn't exist in inventory!
|
||||
Log.Out(Logs::General, Logs::Inventory, "DropItem() - No item found in slot %i", slot_id);
|
||||
Message(13, "Error: Item not found in slot %i", slot_id);
|
||||
return;
|
||||
}
|
||||
@@ -633,6 +687,8 @@ void Client::DropItem(int16 slot_id)
|
||||
entity_list.AddObject(object, true);
|
||||
object->StartDecay();
|
||||
|
||||
Log.Out(Logs::General, Logs::Inventory, "Item drop handled ut assolet");
|
||||
|
||||
safe_delete(inst);
|
||||
}
|
||||
|
||||
|
||||
@@ -4668,9 +4668,6 @@ int16 Mob::GetMeleeDamageMod_SE(uint16 skill)
|
||||
dmg_mod += itembonuses.DamageModifier2[EQEmu::skills::HIGHEST_SKILL + 1] + spellbonuses.DamageModifier2[EQEmu::skills::HIGHEST_SKILL + 1] + aabonuses.DamageModifier2[EQEmu::skills::HIGHEST_SKILL + 1] +
|
||||
itembonuses.DamageModifier2[skill] + spellbonuses.DamageModifier2[skill] + aabonuses.DamageModifier2[skill];
|
||||
|
||||
if (HasShieldEquiped() && !IsOffHandAtk())
|
||||
dmg_mod += itembonuses.ShieldEquipDmgMod[0] + spellbonuses.ShieldEquipDmgMod[0] + aabonuses.ShieldEquipDmgMod[0];
|
||||
|
||||
if(dmg_mod < -100)
|
||||
dmg_mod = -100;
|
||||
|
||||
|
||||
@@ -2038,7 +2038,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
#endif
|
||||
if(IsNPC()) {
|
||||
if(GetLevel() <= 52)
|
||||
CastToNPC()->Depop();
|
||||
CastToNPC()->Depop(true);
|
||||
else
|
||||
Message(13, "Your target is too high level to be affected by this spell.");
|
||||
}
|
||||
@@ -2982,8 +2982,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
case SE_FcHealAmtIncoming:
|
||||
case SE_LimitManaMax:
|
||||
case SE_DoubleRangedAttack:
|
||||
case SE_ShieldEquipHateMod:
|
||||
case SE_ShieldEquipDmgMod:
|
||||
case SE_GroupShielding:
|
||||
case SE_TriggerOnReqTarget:
|
||||
case SE_LimitRace:
|
||||
case SE_FcLimitUse:
|
||||
|
||||
+22
-17
@@ -230,23 +230,6 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot,
|
||||
}
|
||||
}
|
||||
|
||||
// check for fizzle
|
||||
// note that CheckFizzle itself doesn't let NPCs fizzle,
|
||||
// but this code allows for it.
|
||||
if(slot < CastingSlot::MaxGems && !CheckFizzle(spell_id))
|
||||
{
|
||||
int fizzle_msg = IsBardSong(spell_id) ? MISS_NOTE : SPELL_FIZZLE;
|
||||
InterruptSpell(fizzle_msg, 0x121, spell_id);
|
||||
|
||||
uint32 use_mana = ((spells[spell_id].mana) / 4);
|
||||
Log.Out(Logs::Detail, Logs::Spells, "Spell casting canceled: fizzled. %d mana has been consumed", use_mana);
|
||||
|
||||
// fizzle 1/4 the mana away
|
||||
SetMana(GetMana() - use_mana);
|
||||
TryTriggerOnValueAmount(false, true);
|
||||
return(false);
|
||||
}
|
||||
|
||||
if (HasActiveSong() && IsBardSong(spell_id)) {
|
||||
Log.Out(Logs::Detail, Logs::Spells, "Casting a new song while singing a song. Killing old song %d.", bardsong);
|
||||
//Note: this does NOT tell the client
|
||||
@@ -366,6 +349,28 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot,
|
||||
}
|
||||
casting_spell_aa_id = aa_id;
|
||||
|
||||
// check for fizzle
|
||||
// note that CheckFizzle itself doesn't let NPCs fizzle,
|
||||
// but this code allows for it.
|
||||
if (slot < CastingSlot::MaxGems && !CheckFizzle(spell_id)) {
|
||||
int fizzle_msg = IsBardSong(spell_id) ? MISS_NOTE : SPELL_FIZZLE;
|
||||
|
||||
uint32 use_mana = ((spells[spell_id].mana) / 4);
|
||||
Log.Out(Logs::Detail, Logs::Spells, "Spell casting canceled: fizzled. %d mana has been consumed", use_mana);
|
||||
|
||||
// fizzle 1/4 the mana away
|
||||
Mob::SetMana(GetMana() - use_mana); // We send StopCasting which will update mana
|
||||
StopCasting();
|
||||
|
||||
Message_StringID(MT_SpellFailure, fizzle_msg);
|
||||
entity_list.FilteredMessageClose_StringID(
|
||||
this, true, 200, MT_SpellFailure, IsClient() ? FilterPCSpells : FilterNPCSpells,
|
||||
fizzle_msg == MISS_NOTE ? MISSED_NOTE_OTHER : SPELL_FIZZLE_OTHER, GetName());
|
||||
|
||||
TryTriggerOnValueAmount(false, true);
|
||||
return(false);
|
||||
}
|
||||
|
||||
SaveSpellLoc();
|
||||
Log.Out(Logs::Detail, Logs::Spells, "Casting %d Started at (%.3f,%.3f,%.3f)", spell_id, m_SpellLocation.x, m_SpellLocation.y, m_SpellLocation.z);
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
#define FISHING_FAILED 168 //You didn't catch anything.
|
||||
#define FISHING_POLE_BROKE 169 //Your fishing pole broke!
|
||||
#define FISHING_SUCCESS 170 //You caught, something...
|
||||
#define FISHING_SUCCESS_FISH_NAME 421 //You caught %1!
|
||||
#define FISHING_SPILL_BEER 171 //You spill your beer while bringing in your line.
|
||||
#define FISHING_LOST_BAIT 172 //You lost your bait!
|
||||
#define SPELL_FIZZLE 173 //Your spell fizzles!
|
||||
@@ -255,6 +256,7 @@
|
||||
#define MEMBER_OF_YOUR_GUILD 1429
|
||||
#define OFFICER_OF_YOUR_GUILD 1430
|
||||
#define LEADER_OF_YOUR_GUILD 1431
|
||||
#define TRADE_HAS_BEEN_CANCELLED 1449
|
||||
#define RECEIVED_PLATINUM 1452 //You receive %1 Platinum from %2.
|
||||
#define RECEIVED_GOLD 1453 //You receive %1 Gold from %2.
|
||||
#define RECEIVED_SILVER 1454 //You receive %1 Silver from %2.
|
||||
@@ -277,6 +279,7 @@
|
||||
#define TRADESKILL_MISSING_COMPONENTS 3456 //Sorry, but you don't have everything you need for this recipe in your general inventory.
|
||||
#define TRADESKILL_LEARN_RECIPE 3457 //You have learned the recipe %1!
|
||||
#define EXPEDITION_MIN_REMAIN 3551 //You only have %1 minutes remaining before this expedition comes to an end.
|
||||
#define LOOT_NOT_ALLOWED 3562 //You are not allowed to loot the item: %1.
|
||||
#define NO_CAST_ON_PET 4045 //You cannot cast this spell on your pet.
|
||||
#define REWIND_WAIT 4059 //You must wait a bit longer before using the rewind command again.
|
||||
#define CORPSEDRAG_LIMIT 4061 //You are already dragging as much as you can!
|
||||
|
||||
+27
-13
@@ -967,23 +967,37 @@ bool Client::CheckTradeLoreConflict(Client* other)
|
||||
{
|
||||
if (!other)
|
||||
return true;
|
||||
// Move each trade slot into free inventory slot
|
||||
for (int16 i = EQEmu::legacy::TRADE_BEGIN; i <= EQEmu::legacy::TRADE_END; i++){
|
||||
const EQEmu::ItemInstance* inst = m_inv[i];
|
||||
|
||||
if (inst && inst->GetItem()) {
|
||||
if (other->CheckLoreConflict(inst->GetItem()))
|
||||
return true;
|
||||
}
|
||||
for (int16 index = EQEmu::legacy::TRADE_BEGIN; index <= EQEmu::legacy::TRADE_END; ++index) {
|
||||
const EQEmu::ItemInstance* inst = m_inv[index];
|
||||
if (!inst || !inst->GetItem())
|
||||
continue;
|
||||
|
||||
if (other->CheckLoreConflict(inst->GetItem()))
|
||||
return true;
|
||||
}
|
||||
|
||||
for (int16 i = EQEmu::legacy::TRADE_BAGS_BEGIN; i <= EQEmu::legacy::TRADE_BAGS_END; i++){
|
||||
const EQEmu::ItemInstance* inst = m_inv[i];
|
||||
for (int16 index = EQEmu::legacy::TRADE_BAGS_BEGIN; index <= EQEmu::legacy::TRADE_BAGS_END; ++index) {
|
||||
const EQEmu::ItemInstance* inst = m_inv[index];
|
||||
if (!inst || !inst->GetItem())
|
||||
continue;
|
||||
|
||||
if (inst && inst->GetItem()) {
|
||||
if (other->CheckLoreConflict(inst->GetItem()))
|
||||
return true;
|
||||
}
|
||||
if (other->CheckLoreConflict(inst->GetItem()))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Client::CheckTradeNonDroppable()
|
||||
{
|
||||
for (int16 index = EQEmu::legacy::TRADE_BEGIN; index <= EQEmu::legacy::TRADE_END; ++index){
|
||||
const EQEmu::ItemInstance* inst = m_inv[index];
|
||||
if (!inst)
|
||||
continue;
|
||||
|
||||
if (!inst->IsDroppable())
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
Reference in New Issue
Block a user