mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-30 15:01:29 +00:00
Merge branch 'master' into eqstream
This commit is contained in:
commit
0264c0d60a
@ -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
|
||||
|
||||
@ -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{
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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; }
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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*/
|
||||
};
|
||||
|
||||
@ -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*/
|
||||
};
|
||||
|
||||
@ -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{
|
||||
|
||||
@ -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{
|
||||
|
||||
@ -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*/
|
||||
};
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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.');
|
||||
@ -0,0 +1 @@
|
||||
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Bots:BotLevelsWithOwner', 'true', 'Auto-updates bots with ding.');
|
||||
@ -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');
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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))
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
146
zone/corpse.cpp
146
zone/corpse.cpp
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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()))
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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!
|
||||
|
||||
@ -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;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user