Merge branch 'master' into eqstream

This commit is contained in:
KimLS 2016-12-21 14:26:59 -08:00
commit 0264c0d60a
41 changed files with 339 additions and 218 deletions

View File

@ -1,7 +1,12 @@
EQEMu Changelog (Started on Sept 24, 2003 15:50)
-------------------------------------------------------
== 21/01/2016 ==
Uleat: Disabled RoF+ clients from augmentation items not in their possessions slots (0-29, 9999, 251-330) to abate an exploit in the current code
== 12/03/2016 ==
Uleat: Added hack detection to trade code
- If illegal items are found in trade slots when the 'trade' button is clicked, the trade is cancelled and a message is sent to the offending player
- Future revisions will, at a minimum, log the player as a hacker once the quirks have been worked out
== 12/01/2016 ==
Uleat: Disabled RoF+ clients from augmenting items not in their possessions slots (0-29, 9999, 251-330) to abate an exploit in the current code
== 10/17/2016 ==
Uleat: Moved namespace ItemField from item_instance.h to shareddb.cpp - the only place it is used

View File

@ -1643,7 +1643,7 @@ struct LootingItem_Struct {
/*002*/ uint32 looter;
/*004*/ uint16 slot_id;
/*006*/ uint8 unknown3[2];
/*008*/ uint32 auto_loot;
/*008*/ int32 auto_loot;
};
struct GuildManageStatus_Struct{

View File

@ -293,18 +293,13 @@ bool EQEmu::InventoryProfile::DeleteItem(int16 slot_id, uint8 quantity)
}
// Checks All items in a bag for No Drop
bool EQEmu::InventoryProfile::CheckNoDrop(int16 slot_id) {
bool EQEmu::InventoryProfile::CheckNoDrop(int16 slot_id, bool recurse)
{
ItemInstance* inst = GetItem(slot_id);
if (!inst) return false;
if (!inst->GetItem()->NoDrop) return true;
if (inst->GetItem()->ItemClass == 1) {
for (uint8 i = inventory::containerBegin; i < inventory::ContainerCount; i++) {
ItemInstance* bagitem = GetItem(InventoryProfile::CalcSlotId(slot_id, i));
if (bagitem && !bagitem->GetItem()->NoDrop)
return true;
}
}
return false;
if (!inst)
return false;
return (!inst->IsDroppable(recurse));
}
// Remove item from bucket without memory delete

View File

@ -133,7 +133,7 @@ namespace EQEmu
bool DeleteItem(int16 slot_id, uint8 quantity = 0);
// Checks All items in a bag for No Drop
bool CheckNoDrop(int16 slot_id);
bool CheckNoDrop(int16 slot_id, bool recurse = true);
// Remove item from inventory (and take control of memory)
ItemInstance* PopItem(int16 slot_id);

View File

@ -539,16 +539,16 @@ EQEmu::ItemInstance* EQEmu::ItemInstance::GetOrnamentationAug(int32 ornamentatio
}
uint32 EQEmu::ItemInstance::GetOrnamentHeroModel(int32 material_slot) const {
uint32 HeroModel = 0;
if (m_ornament_hero_model > 0)
{
HeroModel = m_ornament_hero_model;
if (material_slot >= 0)
{
HeroModel = (m_ornament_hero_model * 100) + material_slot;
}
}
return HeroModel;
// Not a Hero Forge item.
if (m_ornament_hero_model == 0 || material_slot < 0)
return 0;
// Item is using an explicit Hero Forge ID
if (m_ornament_hero_model >= 1000)
return m_ornament_hero_model;
// Item is using a shorthand ID
return (m_ornament_hero_model * 100) + material_slot;
}
bool EQEmu::ItemInstance::UpdateOrnamentationInfo() {
@ -819,6 +819,32 @@ bool EQEmu::ItemInstance::IsSlotAllowed(int16 slot_id) const {
else { return false; }
}
bool EQEmu::ItemInstance::IsDroppable(bool recurse) const
{
if (!m_item)
return false;
/*if (m_ornamentidfile) // not implemented
return false;*/
if (m_attuned)
return false;
/*if (m_item->FVNoDrop != 0) // not implemented
return false;*/
if (m_item->NoDrop == 0)
return false;
if (recurse) {
for (auto iter : m_contents) {
if (!iter.second)
continue;
if (!iter.second->IsDroppable(recurse))
return false;
}
}
return true;
}
void EQEmu::ItemInstance::Initialize(SharedDatabase *db) {
// if there's no actual item, don't do anything
if (!m_item)

View File

@ -188,6 +188,8 @@ namespace EQEmu
bool IsSlotAllowed(int16 slot_id) const;
bool IsDroppable(bool recurse = true) const;
bool IsScaling() const { return m_scaling; }
bool IsEvolving() const { return (m_evolveLvl >= 1); }
uint32 GetExp() const { return m_exp; }

View File

@ -1492,17 +1492,6 @@ namespace RoF
FINISH_ENCODE();
}
ENCODE(OP_InterruptCast)
{
ENCODE_LENGTH_EXACT(InterruptCast_Struct);
SETUP_DIRECT_ENCODE(InterruptCast_Struct, structs::InterruptCast_Struct);
OUT(spawnid);
OUT(messageid);
FINISH_ENCODE();
}
ENCODE(OP_ItemLinkResponse) { ENCODE_FORWARD(OP_ItemPacket); }
ENCODE(OP_ItemPacket)

View File

@ -1560,17 +1560,6 @@ namespace RoF2
FINISH_ENCODE();
}
ENCODE(OP_InterruptCast)
{
ENCODE_LENGTH_EXACT(InterruptCast_Struct);
SETUP_DIRECT_ENCODE(InterruptCast_Struct, structs::InterruptCast_Struct);
OUT(spawnid);
OUT(messageid);
FINISH_ENCODE();
}
ENCODE(OP_ItemLinkResponse) { ENCODE_FORWARD(OP_ItemPacket); }
ENCODE(OP_ItemPacket)

View File

@ -85,7 +85,6 @@ E(OP_HPUpdate)
E(OP_Illusion)
E(OP_InspectBuffs)
E(OP_InspectRequest)
E(OP_InterruptCast)
E(OP_ItemLinkResponse)
E(OP_ItemPacket)
E(OP_ItemVerifyReply)

View File

@ -70,7 +70,6 @@ E(OP_HPUpdate)
E(OP_Illusion)
E(OP_InspectBuffs)
E(OP_InspectRequest)
E(OP_InterruptCast)
E(OP_ItemLinkResponse)
E(OP_ItemPacket)
E(OP_ItemVerifyReply)

View File

@ -2021,7 +2021,7 @@ struct LootingItem_Struct {
/*004*/ uint32 looter;
/*008*/ uint16 slot_id;
/*010*/ uint16 unknown10;
/*012*/ uint32 auto_loot;
/*012*/ int32 auto_loot;
/*016*/ uint32 unknown16;
/*020*/
};

View File

@ -1666,7 +1666,7 @@ struct LootingItem_Struct {
/*000*/ uint32 lootee;
/*004*/ uint32 looter;
/*008*/ uint32 slot_id;
/*012*/ uint32 auto_loot;
/*012*/ int32 auto_loot;
/*016*/ uint32 unknown16;
/*020*/
};

View File

@ -1648,7 +1648,7 @@ struct LootingItem_Struct {
/*002*/ uint32 looter;
/*004*/ uint16 slot_id;
/*006*/ uint8 unknown3[2];
/*008*/ uint32 auto_loot;
/*008*/ int32 auto_loot;
};
struct GuildManageStatus_Struct{

View File

@ -1420,7 +1420,7 @@ struct LootingItem_Struct {
/*002*/ uint32 looter;
/*004*/ uint16 slot_id;
/*006*/ uint8 unknown3[2];
/*008*/ uint32 auto_loot;
/*008*/ int32 auto_loot;
};
struct GuildManageStatus_Struct{

View File

@ -1707,7 +1707,7 @@ struct LootingItem_Struct {
/*000*/ uint32 lootee;
/*004*/ uint32 looter;
/*008*/ uint32 slot_id;
/*012*/ uint32 auto_loot;
/*012*/ int32 auto_loot;
/*016*/ uint32 unknown16;
/*020*/
};

View File

@ -556,12 +556,13 @@ RULE_REAL(Bots, ManaRegen, 2.0) // Adjust mana regen for bots, 1 is fast and hig
RULE_BOOL(Bots, PreferNoManaCommandSpells, true) // Give sorting priority to newer no-mana spells (i.e., 'Bind Affinity')
RULE_BOOL(Bots, QuestableSpawnLimit, false) // Optional quest method to manage bot spawn limits using the quest_globals name bot_spawn_limit, see: /bazaar/Aediles_Thrall.pl
RULE_BOOL(Bots, QuestableSpells, false) // Anita Thrall's (Anita_Thrall.pl) Bot Spell Scriber quests.
RULE_INT(Bots, SpawnLimit, 71) // Number of bots a character can have spawned at one time, You + 71 bots is a 12 group raid
RULE_INT(Bots, SpawnLimit, 71) // Number of bots a character can have spawned at one time, You + 71 bots is a 12 group pseudo-raid (bots are not raidable at this time)
RULE_BOOL(Bots, BotGroupXP, false) // Determines whether client gets xp for bots outside their group.
RULE_BOOL(Bots, BotBardUseOutOfCombatSongs, true) // Determines whether bard bots use additional out of combat songs (optional script)
RULE_BOOL(Bots, BotLevelsWithOwner, false) // Auto-updates spawned bots as owner levels/de-levels (false is original behavior)
RULE_BOOL(Bots, BotCharacterLevelEnabled, false) // Enables required level to spawn bots
RULE_INT(Bots, BotCharacterLevel, 0) // 0 as default (if level > this value you can spawn bots if BotCharacterLevelEnabled is true)
RULE_INT(Bots, CasterStopMeleeLevel, 13) // Level at which caster bots stop melee attacks
RULE_CATEGORY_END()
#endif

View File

@ -594,7 +594,7 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQEmu::InventoryProfile *inv)
inst->SetOrnamentIcon(ornament_icon);
inst->SetOrnamentationIDFile(ornament_idfile);
inst->SetOrnamentHeroModel(ornament_hero_model);
inst->SetOrnamentHeroModel(item->HerosForgeModel);
if (instnodrop ||
(((slot_id >= EQEmu::legacy::EQUIPMENT_BEGIN && slot_id <= EQEmu::legacy::EQUIPMENT_END) ||
@ -730,7 +730,7 @@ bool SharedDatabase::GetInventory(uint32 account_id, char *name, EQEmu::Inventor
inst->SetOrnamentIcon(ornament_icon);
inst->SetOrnamentationIDFile(ornament_idfile);
inst->SetOrnamentHeroModel(ornament_hero_model);
inst->SetOrnamentHeroModel(item->HerosForgeModel);
if (color > 0)
inst->SetColor(color);

View File

@ -409,8 +409,19 @@ bool IsPartialCapableSpell(uint16 spell_id)
if (spells[spell_id].no_partial_resist)
return false;
if (IsPureNukeSpell(spell_id))
return true;
// spell uses 600 (partial) scale if first effect is damage, else it uses 200 scale.
// this includes DoTs. no_partial_resist excludes spells like necro snares
for (int o = 0; o < EFFECT_COUNT; o++) {
auto tid = spells[spell_id].effectid[o];
if (IsBlankSpellEffect(spell_id, o))
continue;
if ((tid == SE_CurrentHPOnce || tid == SE_CurrentHP) && spells[spell_id].base[o] < 0)
return true;
return false;
}
return false;
}

View File

@ -502,7 +502,7 @@ typedef enum {
#define SE_HeadShotLevel 346 // implemented[AA] - HeadShot max level to kill
#define SE_DoubleRangedAttack 347 // implemented - chance at an additional archery attack (consumes arrow)
#define SE_LimitManaMin 348 // implemented
#define SE_ShieldEquipHateMod 349 // implemented[AA] Increase melee hate when wearing a shield.
#define SE_ShieldEquipDmgMod 349 // implemented[AA] Increase melee base damage (indirectly increasing hate) when wearing a shield.
#define SE_ManaBurn 350 // implemented - Drains mana for damage/heal at a defined ratio up to a defined maximum amount of mana.
//#define SE_PersistentEffect 351 // *not implemented. creates a trap/totem that casts a spell (spell id + base1?) when anything comes near it. can probably make a beacon for this
//#define SE_IncreaseTrapCount 352 // *not implemented - looks to be some type of invulnerability? Test ITC (8755)
@ -519,7 +519,7 @@ typedef enum {
#define SE_BandolierSlots 363 // *not implemented[AA] 'Battle Ready' expands the bandolier by one additional save slot per rank.
#define SE_TripleAttackChance 364 // implemented
#define SE_ProcOnSpellKillShot 365 // implemented - chance to trigger a spell on kill when the kill is caused by a specific spell with this effect in it (10470 Venin)
#define SE_ShieldEquipDmgMod 366 // implemented[AA] Damage modifier to melee if shield equiped. (base1 = dmg mod , base2 = ?) ie Shield Specialist AA
#define SE_GroupShielding 366 // *not implemented[AA] This gives you /shieldgroup
#define SE_SetBodyType 367 // implemented - set body type of base1 so it can be affected by spells that are limited to that type (Plant, Animal, Undead, etc)
//#define SE_FactionMod 368 // *not implemented - increases faction with base1 (faction id, live won't match up w/ ours) by base2
#define SE_CorruptionCounter 369 // implemented

View File

@ -144,7 +144,7 @@ void Config::Parse(const char *file_name)
*/
void Config::Tokenize(FILE *input, std::list<std::string> &tokens)
{
char c = fgetc(input);
auto c = fgetc(input);
std::string lexeme;
while(c != EOF)
@ -162,7 +162,7 @@ void Config::Tokenize(FILE *input, std::list<std::string> &tokens)
if(isalnum(c))
{
lexeme.append((const char *)&c, 1);
lexeme += c;
c = fgetc(input);
continue;
}
@ -193,14 +193,14 @@ void Config::Tokenize(FILE *input, std::list<std::string> &tokens)
lexeme.clear();
}
lexeme.append((const char *)&c, 1);
lexeme += c;
tokens.push_back(lexeme);
lexeme.clear();
break;
}
default:
{
lexeme.append((const char *)&c, 1);
lexeme += c;
}
}

View File

@ -150,7 +150,7 @@ OP_GMZoneRequest=0x62ac
OP_GMZoneRequest2=0x7e1a
OP_GMGoto=0x7d8e
OP_GMSearchCorpse=0x357c
OP_GMHideMe=0x79c5
OP_GMHideMe=0x2fab
OP_GMDelCorpse=0x607e
OP_GMApproval=0x6db5
OP_GMToggle=0x2097

View File

@ -1,4 +0,0 @@
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Bots:BotLevelsWithOwner', 'true', 'Auto-updates bots with ding.');
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (2, 'Bots:BotLevelsWithOwner', 'true', 'Auto-updates bots with ding.');
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (4, 'Bots:BotLevelsWithOwner', 'true', 'Auto-updates bots with ding.');
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (10, 'Bots:BotLevelsWithOwner', 'true', 'Auto-updates bots with ding.');

View File

@ -0,0 +1 @@
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Bots:BotLevelsWithOwner', 'true', 'Auto-updates bots with ding.');

View File

@ -0,0 +1,2 @@
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Bots:CasterStopMeleeLevel', '13', 'Level at which caster bots stop melee attacks');

View File

@ -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);

View File

@ -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:

View File

@ -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)

View File

@ -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();

View File

@ -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;

View File

@ -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))

View File

@ -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

View File

@ -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

View File

@ -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);
}

View File

@ -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);

View File

@ -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()))

View File

@ -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);
}

View File

@ -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;

View File

@ -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:

View File

@ -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);

View File

@ -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!

View File

@ -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;