mirror of
https://github.com/EQEmu/Server.git
synced 2026-01-04 03:13:52 +00:00
Implement global loot system Fixes #619
This should allow us to emulate lives global tables The options available to filter tables are min_level, max_level, race, rare, raid, race, class, bodytype, and zone. race, class, bodytype, and zone are a pipe | separated list of IDs
This commit is contained in:
parent
0b97db9fd2
commit
c5e4bb08f4
@ -1,5 +1,15 @@
|
||||
EQEMu Changelog (Started on Sept 24, 2003 15:50)
|
||||
-------------------------------------------------------
|
||||
== 02/10/2018 ==
|
||||
mackal: Add Global Loot system
|
||||
|
||||
This will allow us to implement global loot similarly to how it works on live
|
||||
This system reuses our current loottable tables which the global_loot table references.
|
||||
The limits for the rules to govern if a table should be rolled are min level, max level, rare,
|
||||
raid, race, class, bodytype, and zone
|
||||
|
||||
race, class, bodytype, and zone are a pipe | separated list of IDs.
|
||||
|
||||
== 01/31/2018 ==
|
||||
Uleat: Re-work of Bot::AI_Process(). Overall behavior is much improved.
|
||||
- Removed a 'ton' of unneeded packet updates
|
||||
|
||||
@ -30,7 +30,7 @@
|
||||
Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
|
||||
*/
|
||||
|
||||
#define CURRENT_BINARY_DATABASE_VERSION 9118
|
||||
#define CURRENT_BINARY_DATABASE_VERSION 9119
|
||||
#ifdef BOTS
|
||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9018
|
||||
#else
|
||||
|
||||
@ -372,6 +372,7 @@
|
||||
9116|2017_12_16_GroundSpawn_Respawn_Timer.sql|SHOW COLUMNS FROM `ground_spawns` WHERE Field = 'respawn_timer' AND Type = 'int(11) unsigned'|empty|
|
||||
9117|2018_02_01_NPC_Spells_Min_Max_HP.sql|SHOW COLUMNS FROM `npc_spells_entries` LIKE 'min_hp'|empty|
|
||||
9118|2018_02_04_Charm_Stats.sql|SHOW COLUMNS FROM `npc_types` LIKE 'charm_ac'|empty|
|
||||
9119|2018_02_10_GlobalLoot.sql|SHOW TABLES LIKE 'global_loot'|empty|
|
||||
|
||||
# Upgrade conditions:
|
||||
# This won't be needed after this system is implemented, but it is used database that are not
|
||||
|
||||
19
utils/sql/git/required/2018_02_10_GlobalLoot.sql
Normal file
19
utils/sql/git/required/2018_02_10_GlobalLoot.sql
Normal file
@ -0,0 +1,19 @@
|
||||
ALTER TABLE `npc_types` ADD `skip_global_loot` TINYINT DEFAULT '0';
|
||||
ALTER TABLE `npc_types` ADD `rare_spawn` TINYINT DEFAULT '0';
|
||||
|
||||
CREATE TABLE global_loot (
|
||||
id INT NOT NULL AUTO_INCREMENT,
|
||||
description varchar(255),
|
||||
loottable_id INT NOT NULL,
|
||||
enabled TINYINT NOT NULL DEFAULT 1,
|
||||
min_level INT NOT NULL DEFAULT 0,
|
||||
max_level INT NOT NULL DEFAULT 0,
|
||||
rare TINYINT NULL,
|
||||
raid TINYINT NULL,
|
||||
race MEDIUMTEXT NULL,
|
||||
class MEDIUMTEXT NULL,
|
||||
bodytype MEDIUMTEXT NULL,
|
||||
zone MEDIUMTEXT NULL,
|
||||
PRIMARY KEY (id)
|
||||
);
|
||||
|
||||
@ -68,6 +68,7 @@ SET(zone_sources
|
||||
exp.cpp
|
||||
fearpath.cpp
|
||||
forage.cpp
|
||||
global_loot_manager.cpp
|
||||
groups.cpp
|
||||
guild.cpp
|
||||
guild_mgr.cpp
|
||||
@ -158,6 +159,7 @@ SET(zone_headers
|
||||
errmsg.h
|
||||
event_codes.h
|
||||
forage.h
|
||||
global_loot_manager.h
|
||||
groups.h
|
||||
guild_mgr.h
|
||||
hate_list.h
|
||||
|
||||
@ -358,9 +358,11 @@ int command_init(void)
|
||||
command_add("showbonusstats", "[item|spell|all] Shows bonus stats for target from items or spells. Shows both by default.", 50, command_showbonusstats) ||
|
||||
command_add("showbuffs", "- List buffs active on your target or you if no target", 50, command_showbuffs) ||
|
||||
command_add("shownumhits", "Shows buffs numhits for yourself.", 0, command_shownumhits) ||
|
||||
command_add("shownpcgloballoot", "Show GlobalLoot entires on this npc", 50, command_shownpcgloballoot) ||
|
||||
command_add("showskills", "- Show the values of your or your player target's skills", 50, command_showskills) ||
|
||||
command_add("showspellslist", "Shows spell list of targeted NPC", 100, command_showspellslist) ||
|
||||
command_add("showstats", "- Show details about you or your target", 50, command_showstats) ||
|
||||
command_add("showzonegloballoot", "Show GlobalLoot entires on this zone", 50, command_showzonegloballoot) ||
|
||||
command_add("shutdown", "- Shut this zone process down", 150, command_shutdown) ||
|
||||
command_add("size", "[size] - Change size of you or your target", 50, command_size) ||
|
||||
command_add("spawn", "[name] [race] [level] [material] [hp] [gender] [class] [priweapon] [secweapon] [merchantid] - Spawn an NPC", 10, command_spawn) ||
|
||||
@ -2458,7 +2460,9 @@ void command_npctypespawn(Client *c, const Seperator *sep)
|
||||
if (npc && sep->IsNumber(2))
|
||||
npc->SetNPCFactionID(atoi(sep->arg[2]));
|
||||
|
||||
npc->AddLootTable();
|
||||
npc->AddLootTable();
|
||||
if (npc->DropsGlobalLoot())
|
||||
npc->CheckGlobalLootTables();
|
||||
entity_list.AddNPC(npc);
|
||||
}
|
||||
else
|
||||
@ -3857,6 +3861,12 @@ void command_showstats(Client *c, const Seperator *sep)
|
||||
c->ShowStats(c);
|
||||
}
|
||||
|
||||
void command_showzonegloballoot(Client *c, const Seperator *sep)
|
||||
{
|
||||
c->Message(0, "GlobalLoot for %s (%d:%d)", zone->GetShortName(), zone->GetZoneID(), zone->GetInstanceVersion());
|
||||
zone->ShowZoneGlobalLoot(c);
|
||||
}
|
||||
|
||||
void command_mystats(Client *c, const Seperator *sep)
|
||||
{
|
||||
if (c->GetTarget() && c->GetPet()) {
|
||||
@ -10441,6 +10451,20 @@ void command_shownumhits(Client *c, const Seperator *sep)
|
||||
return;
|
||||
}
|
||||
|
||||
void command_shownpcgloballoot(Client *c, const Seperator *sep)
|
||||
{
|
||||
auto tar = c->GetTarget();
|
||||
|
||||
if (!tar || !tar->IsNPC()) {
|
||||
c->Message(0, "You must target an NPC to use this command.");
|
||||
return;
|
||||
}
|
||||
|
||||
auto npc = tar->CastToNPC();
|
||||
c->Message(0, "GlobalLoot for %s (%d)", npc->GetName(), npc->GetNPCTypeID());
|
||||
zone->ShowNPCGlobalLoot(c, npc);
|
||||
}
|
||||
|
||||
void command_tune(Client *c, const Seperator *sep)
|
||||
{
|
||||
//Work in progress - Kayen
|
||||
|
||||
@ -267,10 +267,12 @@ void command_setxp(Client *c, const Seperator *sep);
|
||||
void command_showbonusstats(Client *c, const Seperator *sep);
|
||||
void command_showbuffs(Client *c, const Seperator *sep);
|
||||
void command_shownumhits(Client *c, const Seperator *sep);
|
||||
void command_shownpcgloballoot(Client *c, const Seperator *sep);
|
||||
void command_showpetspell(Client *c, const Seperator *sep);
|
||||
void command_showskills(Client *c, const Seperator *sep);
|
||||
void command_showspellslist(Client *c, const Seperator *sep);
|
||||
void command_showstats(Client *c, const Seperator *sep);
|
||||
void command_showzonegloballoot(Client *c, const Seperator *sep);
|
||||
void command_shutdown(Client *c, const Seperator *sep);
|
||||
void command_size(Client *c, const Seperator *sep);
|
||||
void command_spawn(Client *c, const Seperator *sep);
|
||||
|
||||
@ -277,20 +277,23 @@ void Client::GoFish()
|
||||
food_id = database.GetZoneFishing(m_pp.zone_id, fishing_skill, npc_id, npc_chance);
|
||||
|
||||
//check for add NPC
|
||||
if(npc_chance > 0 && npc_id) {
|
||||
if(npc_chance < zone->random.Int(0, 99)) {
|
||||
const NPCType* tmp = database.LoadNPCTypesData(npc_id);
|
||||
if(tmp != nullptr) {
|
||||
auto positionNPC = GetPosition();
|
||||
positionNPC.x = positionNPC.x + 3;
|
||||
auto npc = new NPC(tmp, nullptr, positionNPC, FlyMode3);
|
||||
npc->AddLootTable();
|
||||
if (npc_chance > 0 && npc_id) {
|
||||
if (npc_chance < zone->random.Int(0, 99)) {
|
||||
const NPCType *tmp = database.LoadNPCTypesData(npc_id);
|
||||
if (tmp != nullptr) {
|
||||
auto positionNPC = GetPosition();
|
||||
positionNPC.x = positionNPC.x + 3;
|
||||
auto npc = new NPC(tmp, nullptr, positionNPC, FlyMode3);
|
||||
npc->AddLootTable();
|
||||
if (npc->DropsGlobalLoot())
|
||||
npc->CheckGlobalLootTables();
|
||||
|
||||
npc->AddToHateList(this, 1, 0, false); // no help yelling
|
||||
npc->AddToHateList(this, 1, 0, false); // no help yelling
|
||||
|
||||
entity_list.AddNPC(npc);
|
||||
entity_list.AddNPC(npc);
|
||||
|
||||
Message(MT_Emote, "You fish up a little more than you bargained for...");
|
||||
Message(MT_Emote,
|
||||
"You fish up a little more than you bargained for...");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
98
zone/global_loot_manager.cpp
Normal file
98
zone/global_loot_manager.cpp
Normal file
@ -0,0 +1,98 @@
|
||||
#include "global_loot_manager.h"
|
||||
#include "npc.h"
|
||||
#include "client.h"
|
||||
|
||||
std::vector<int> GlobalLootManager::GetGlobalLootTables(NPC *mob) const
|
||||
{
|
||||
// we may be able to add a cache here if performance is an issue, but for now
|
||||
// just return NRVO'd vector
|
||||
// The cache would have to be keyed by NPCType and level (for NPCs with Max Level set)
|
||||
std::vector<int> tables;
|
||||
|
||||
for (auto &e : m_entries) {
|
||||
if (e.PassesRules(mob)) {
|
||||
tables.push_back(e.GetLootTableID());
|
||||
}
|
||||
}
|
||||
|
||||
return tables;
|
||||
}
|
||||
|
||||
void GlobalLootManager::ShowZoneGlobalLoot(Client *to) const
|
||||
{
|
||||
for (auto &e : m_entries)
|
||||
to->Message(0, " %s : %d table %d", e.GetDescription().c_str(), e.GetID(), e.GetLootTableID());
|
||||
}
|
||||
|
||||
void GlobalLootManager::ShowNPCGlobalLoot(Client *to, NPC *who) const
|
||||
{
|
||||
for (auto &e : m_entries) {
|
||||
if (e.PassesRules(who))
|
||||
to->Message(0, " %s : %d table %d", e.GetDescription().c_str(), e.GetID(), e.GetLootTableID());
|
||||
}
|
||||
}
|
||||
|
||||
bool GlobalLootEntry::PassesRules(NPC *mob) const
|
||||
{
|
||||
bool bRace = false;
|
||||
bool bPassesRace = false;
|
||||
bool bBodyType = false;
|
||||
bool bPassesBodyType = false;
|
||||
bool bClass = false;
|
||||
bool bPassesClass = false;
|
||||
|
||||
for (auto &r : m_rules) {
|
||||
switch (r.type) {
|
||||
case GlobalLoot::RuleTypes::LevelMin:
|
||||
if (mob->GetLevel() < r.value)
|
||||
return false;
|
||||
break;
|
||||
case GlobalLoot::RuleTypes::LevelMax:
|
||||
if (mob->GetLevel() > r.value)
|
||||
return false;
|
||||
break;
|
||||
case GlobalLoot::RuleTypes::Raid: // value == 0 must not be raid, value != 0 must be raid
|
||||
if (mob->IsRaidTarget() && !r.value)
|
||||
return false;
|
||||
if (!mob->IsRaidTarget() && r.value)
|
||||
return false;
|
||||
break;
|
||||
case GlobalLoot::RuleTypes::Rare:
|
||||
if (mob->IsRareSpawn() && !r.value)
|
||||
return false;
|
||||
if (!mob->IsRareSpawn() && r.value)
|
||||
return false;
|
||||
break;
|
||||
case GlobalLoot::RuleTypes::Race: // can have multiple races per rule set
|
||||
bRace = true; // we must pass race
|
||||
if (mob->GetRace() == r.value)
|
||||
bPassesRace = true;
|
||||
break;
|
||||
case GlobalLoot::RuleTypes::Class: // can have multiple classes per rule set
|
||||
bClass = true; // we must pass class
|
||||
if (mob->GetClass() == r.value)
|
||||
bPassesClass = true;
|
||||
break;
|
||||
case GlobalLoot::RuleTypes::BodyType: // can have multiple bodytypes per rule set
|
||||
bBodyType = true; // we must pass BodyType
|
||||
if (mob->GetBodyType() == r.value)
|
||||
bPassesBodyType = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (bRace && !bPassesRace)
|
||||
return false;
|
||||
|
||||
if (bClass && !bPassesClass)
|
||||
return false;
|
||||
|
||||
if (bBodyType && !bPassesBodyType)
|
||||
return false;
|
||||
|
||||
// we abort as early as possible if we fail a rule, so if we get here, we passed
|
||||
return true;
|
||||
}
|
||||
|
||||
61
zone/global_loot_manager.h
Normal file
61
zone/global_loot_manager.h
Normal file
@ -0,0 +1,61 @@
|
||||
#ifndef GLOBAL_LOOT_MANAGER_H
|
||||
#define GLOBAL_LOOT_MANAGER_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
class NPC;
|
||||
class Client;
|
||||
|
||||
namespace GlobalLoot {
|
||||
|
||||
enum class RuleTypes {
|
||||
LevelMin = 0,
|
||||
LevelMax = 1,
|
||||
Race = 2,
|
||||
Class = 3,
|
||||
BodyType = 4,
|
||||
Rare = 5,
|
||||
Raid = 6,
|
||||
Max
|
||||
};
|
||||
|
||||
struct Rule {
|
||||
RuleTypes type;
|
||||
int value;
|
||||
Rule(RuleTypes t, int v) : type(t), value(v) { }
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
class GlobalLootEntry {
|
||||
int m_id;
|
||||
int m_loottable_id;
|
||||
std::string m_description;
|
||||
std::vector<GlobalLoot::Rule> m_rules;
|
||||
public:
|
||||
GlobalLootEntry(int id, int loottable, std::string des)
|
||||
: m_id(id), m_loottable_id(loottable), m_description(std::move(des))
|
||||
{ }
|
||||
bool PassesRules(NPC *mob) const;
|
||||
inline int GetLootTableID() const { return m_loottable_id; }
|
||||
inline int GetID() const { return m_id; }
|
||||
inline const std::string &GetDescription() const { return m_description; }
|
||||
inline void SetLootTableID(int in) { m_loottable_id = in; }
|
||||
inline void SetID(int in) { m_id = in; }
|
||||
inline void SetDescription(const std::string &in) { m_description = in; }
|
||||
inline void AddRule(GlobalLoot::RuleTypes rule, int value) { m_rules.emplace_back(rule, value); }
|
||||
};
|
||||
|
||||
class GlobalLootManager {
|
||||
std::vector<GlobalLootEntry> m_entries;
|
||||
|
||||
public:
|
||||
std::vector<int> GetGlobalLootTables(NPC *mob) const;
|
||||
inline void Clear() { m_entries.clear(); }
|
||||
inline void AddEntry(GlobalLootEntry &in) { m_entries.push_back(in); }
|
||||
void ShowZoneGlobalLoot(Client *to) const;
|
||||
void ShowNPCGlobalLoot(Client *to, NPC *who) const;
|
||||
};
|
||||
|
||||
#endif /* !GLOBAL_LOOT_MANAGER_H */
|
||||
@ -26,6 +26,7 @@
|
||||
#include "mob.h"
|
||||
#include "npc.h"
|
||||
#include "zonedb.h"
|
||||
#include "global_loot_manager.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <stdlib.h>
|
||||
@ -37,10 +38,14 @@
|
||||
// Queries the loottable: adds item & coin to the npc
|
||||
void ZoneDatabase::AddLootTableToNPC(NPC* npc,uint32 loottable_id, ItemList* itemlist, uint32* copper, uint32* silver, uint32* gold, uint32* plat) {
|
||||
const LootTable_Struct* lts = nullptr;
|
||||
*copper = 0;
|
||||
*silver = 0;
|
||||
*gold = 0;
|
||||
*plat = 0;
|
||||
// global loot passes nullptr for these
|
||||
bool bGlobal = copper == nullptr && silver == nullptr && gold == nullptr && plat == nullptr;
|
||||
if (!bGlobal) {
|
||||
*copper = 0;
|
||||
*silver = 0;
|
||||
*gold = 0;
|
||||
*plat = 0;
|
||||
}
|
||||
|
||||
lts = database.GetLootTable(loottable_id);
|
||||
if (!lts)
|
||||
@ -55,17 +60,19 @@ void ZoneDatabase::AddLootTableToNPC(NPC* npc,uint32 loottable_id, ItemList* ite
|
||||
}
|
||||
|
||||
uint32 cash = 0;
|
||||
if(max_cash > 0 && lts->avgcoin > 0 && EQEmu::ValueWithin(lts->avgcoin, min_cash, max_cash)) {
|
||||
float upper_chance = (float)(lts->avgcoin - min_cash) / (float)(max_cash - min_cash);
|
||||
float avg_cash_roll = (float)zone->random.Real(0.0, 1.0);
|
||||
if (!bGlobal) {
|
||||
if(max_cash > 0 && lts->avgcoin > 0 && EQEmu::ValueWithin(lts->avgcoin, min_cash, max_cash)) {
|
||||
float upper_chance = (float)(lts->avgcoin - min_cash) / (float)(max_cash - min_cash);
|
||||
float avg_cash_roll = (float)zone->random.Real(0.0, 1.0);
|
||||
|
||||
if(avg_cash_roll < upper_chance) {
|
||||
cash = zone->random.Int(lts->avgcoin, max_cash);
|
||||
if(avg_cash_roll < upper_chance) {
|
||||
cash = zone->random.Int(lts->avgcoin, max_cash);
|
||||
} else {
|
||||
cash = zone->random.Int(min_cash, lts->avgcoin);
|
||||
}
|
||||
} else {
|
||||
cash = zone->random.Int(min_cash, lts->avgcoin);
|
||||
cash = zone->random.Int(min_cash, max_cash);
|
||||
}
|
||||
} else {
|
||||
cash = zone->random.Int(min_cash, max_cash);
|
||||
}
|
||||
|
||||
if(cash != 0) {
|
||||
@ -80,6 +87,7 @@ void ZoneDatabase::AddLootTableToNPC(NPC* npc,uint32 loottable_id, ItemList* ite
|
||||
|
||||
*copper = cash;
|
||||
}
|
||||
|
||||
uint32 global_loot_multiplier = RuleI(Zone, GlobalLootMultiplier);
|
||||
|
||||
// Do items
|
||||
@ -444,3 +452,76 @@ void NPC::AddLootTable(uint32 ldid) {
|
||||
database.AddLootTableToNPC(this,ldid, &itemlist, &copper, &silver, &gold, &platinum);
|
||||
}
|
||||
}
|
||||
|
||||
void NPC::CheckGlobalLootTables()
|
||||
{
|
||||
auto tables = zone->GetGlobalLootTables(this);
|
||||
|
||||
for (auto &id : tables)
|
||||
database.AddLootTableToNPC(this, id, &itemlist, nullptr, nullptr, nullptr, nullptr);
|
||||
}
|
||||
|
||||
void ZoneDatabase::LoadGlobalLoot()
|
||||
{
|
||||
auto query = StringFormat("SELECT id, loottable_id, description, min_level, max_level, rare, raid, race, "
|
||||
"class, bodytype, zone FROM global_loot WHERE enabled = 1");
|
||||
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success() || results.RowCount() == 0)
|
||||
return;
|
||||
|
||||
// we might need this, lets not keep doing it in a loop
|
||||
auto zoneid = std::to_string(zone->GetZoneID());
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
// checking zone limits
|
||||
if (row[10]) {
|
||||
auto zones = SplitString(row[10], '|');
|
||||
|
||||
auto it = std::find(zones.begin(), zones.end(), zoneid);
|
||||
if (it == zones.end()) // not in here, skip
|
||||
continue;
|
||||
}
|
||||
|
||||
GlobalLootEntry e(atoi(row[0]), atoi(row[1]), row[2] ? row[2] : "");
|
||||
|
||||
auto min_level = atoi(row[3]);
|
||||
if (min_level)
|
||||
e.AddRule(GlobalLoot::RuleTypes::LevelMin, min_level);
|
||||
|
||||
auto max_level = atoi(row[4]);
|
||||
if (max_level)
|
||||
e.AddRule(GlobalLoot::RuleTypes::LevelMax, max_level);
|
||||
|
||||
// null is not used
|
||||
if (row[5])
|
||||
e.AddRule(GlobalLoot::RuleTypes::Rare, atoi(row[5]));
|
||||
|
||||
// null is not used
|
||||
if (row[6])
|
||||
e.AddRule(GlobalLoot::RuleTypes::Raid, atoi(row[6]));
|
||||
|
||||
if (row[7]) {
|
||||
auto races = SplitString(row[7], '|');
|
||||
|
||||
for (auto &r : races)
|
||||
e.AddRule(GlobalLoot::RuleTypes::Race, std::stoi(r));
|
||||
}
|
||||
|
||||
if (row[8]) {
|
||||
auto classes = SplitString(row[8], '|');
|
||||
|
||||
for (auto &c : classes)
|
||||
e.AddRule(GlobalLoot::RuleTypes::Class, std::stoi(c));
|
||||
}
|
||||
|
||||
if (row[9]) {
|
||||
auto bodytypes = SplitString(row[9], '|');
|
||||
|
||||
for (auto &b : bodytypes)
|
||||
e.AddRule(GlobalLoot::RuleTypes::Class, std::stoi(b));
|
||||
}
|
||||
|
||||
zone->AddGlobalLootEntry(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -266,6 +266,7 @@ Mob::Mob(const char* in_name,
|
||||
IsFullHP = (cur_hp == max_hp);
|
||||
qglobal = 0;
|
||||
spawned = false;
|
||||
rare_spawn = false;
|
||||
|
||||
InitializeBuffSlots();
|
||||
|
||||
|
||||
@ -689,6 +689,8 @@ public:
|
||||
void SetFollowDistance(uint32 dist) { follow_dist = dist; }
|
||||
uint32 GetFollowID() const { return follow; }
|
||||
uint32 GetFollowDistance() const { return follow_dist; }
|
||||
inline bool IsRareSpawn() const { return rare_spawn; }
|
||||
inline void SetRareSpawn(bool in) { rare_spawn = in; }
|
||||
|
||||
virtual void Message(uint32 type, const char* message, ...) { }
|
||||
virtual void Message_StringID(uint32 type, uint32 string_id, uint32 distance = 0) { }
|
||||
@ -1225,6 +1227,7 @@ protected:
|
||||
uint32 follow;
|
||||
uint32 follow_dist;
|
||||
bool no_target_hotkey;
|
||||
bool rare_spawn;
|
||||
|
||||
uint32 m_PlayerState;
|
||||
uint32 GetPlayerState() { return m_PlayerState; }
|
||||
|
||||
@ -245,6 +245,8 @@ NPC::NPC(const NPCType* d, Spawn2* in_respawn, const glm::vec4& position, int if
|
||||
roambox_delay = 1000;
|
||||
p_depop = false;
|
||||
loottable_id = d->loottable_id;
|
||||
skip_global_loot = d->skip_global_loot;
|
||||
rare_spawn = d->rare_spawn;
|
||||
|
||||
no_target_hotkey = d->no_target_hotkey;
|
||||
|
||||
@ -938,6 +940,7 @@ bool NPC::SpawnZoneController(){
|
||||
npc_type->d_melee_texture2 = 0;
|
||||
npc_type->merchanttype = 0;
|
||||
npc_type->bodytype = 11;
|
||||
npc_type->skip_global_loot = true;
|
||||
|
||||
if (RuleB(Zone, EnableZoneControllerGlobals)) {
|
||||
npc_type->qglobal = true;
|
||||
|
||||
@ -185,6 +185,7 @@ public:
|
||||
void AddItem(uint32 itemid, uint16 charges, bool equipitem = true, uint32 aug1 = 0, uint32 aug2 = 0, uint32 aug3 = 0, uint32 aug4 = 0, uint32 aug5 = 0, uint32 aug6 = 0);
|
||||
void AddLootTable();
|
||||
void AddLootTable(uint32 ldid);
|
||||
void CheckGlobalLootTables();
|
||||
void DescribeAggro(Client *towho, Mob *mob, bool verbose);
|
||||
void RemoveItem(uint32 item_id, uint16 quantity = 0, uint16 slot = 0);
|
||||
void CheckMinMaxLevel(Mob *them);
|
||||
@ -197,6 +198,7 @@ public:
|
||||
uint32 CountLoot();
|
||||
inline uint32 GetLoottableID() const { return loottable_id; }
|
||||
virtual void UpdateEquipmentLight();
|
||||
inline bool DropsGlobalLoot() const { return !skip_global_loot; }
|
||||
|
||||
inline uint32 GetCopper() const { return copper; }
|
||||
inline uint32 GetSilver() const { return silver; }
|
||||
@ -562,6 +564,7 @@ protected:
|
||||
|
||||
private:
|
||||
uint32 loottable_id;
|
||||
bool skip_global_loot;
|
||||
bool p_depop;
|
||||
};
|
||||
|
||||
|
||||
@ -210,6 +210,8 @@ Mob* QuestManager::spawn2(int npc_type, int grid, int unused, const glm::vec4& p
|
||||
{
|
||||
auto npc = new NPC(tmp, nullptr, position, FlyMode3);
|
||||
npc->AddLootTable();
|
||||
if (npc->DropsGlobalLoot())
|
||||
npc->CheckGlobalLootTables();
|
||||
entity_list.AddNPC(npc,true,true);
|
||||
if(grid > 0)
|
||||
{
|
||||
@ -232,6 +234,8 @@ Mob* QuestManager::unique_spawn(int npc_type, int grid, int unused, const glm::v
|
||||
{
|
||||
auto npc = new NPC(tmp, nullptr, position, FlyMode3);
|
||||
npc->AddLootTable();
|
||||
if (npc->DropsGlobalLoot())
|
||||
npc->CheckGlobalLootTables();
|
||||
entity_list.AddNPC(npc,true,true);
|
||||
if(grid > 0)
|
||||
{
|
||||
@ -308,6 +312,8 @@ Mob* QuestManager::spawn_from_spawn2(uint32 spawn2_id)
|
||||
|
||||
found_spawn->SetNPCPointer(npc);
|
||||
npc->AddLootTable();
|
||||
if (npc->DropsGlobalLoot())
|
||||
npc->CheckGlobalLootTables();
|
||||
npc->SetSp2(found_spawn->SpawnGroupID());
|
||||
entity_list.AddNPC(npc);
|
||||
entity_list.LimitAddNPC(npc);
|
||||
@ -1656,6 +1662,8 @@ void QuestManager::respawn(int npcTypeID, int grid) {
|
||||
{
|
||||
owner = new NPC(npcType, nullptr, owner->GetPosition(), FlyMode3);
|
||||
owner->CastToNPC()->AddLootTable();
|
||||
if (owner->CastToNPC()->DropsGlobalLoot())
|
||||
owner->CastToNPC()->CheckGlobalLootTables();
|
||||
entity_list.AddNPC(owner->CastToNPC(),true,true);
|
||||
if(grid > 0)
|
||||
owner->CastToNPC()->AssignWaypoints(grid);
|
||||
|
||||
@ -236,6 +236,8 @@ bool Spawn2::Process() {
|
||||
|
||||
npcthis = npc;
|
||||
npc->AddLootTable();
|
||||
if (npc->DropsGlobalLoot())
|
||||
npc->CheckGlobalLootTables();
|
||||
npc->SetSp2(spawngroup_id_);
|
||||
npc->SaveGuardPointAnim(anim);
|
||||
npc->SetAppearance((EmuAppearance)anim);
|
||||
|
||||
@ -168,6 +168,8 @@ void Trap::Trigger(Mob* trigger)
|
||||
auto spawnPosition = randomOffset + glm::vec4(m_Position, 0.0f);
|
||||
auto new_npc = new NPC(tmp, nullptr, spawnPosition, FlyMode3);
|
||||
new_npc->AddLootTable();
|
||||
if (new_npc->DropsGlobalLoot())
|
||||
new_npc->CheckGlobalLootTables();
|
||||
entity_list.AddNPC(new_npc);
|
||||
new_npc->AddToHateList(trigger,1);
|
||||
}
|
||||
@ -191,6 +193,8 @@ void Trap::Trigger(Mob* trigger)
|
||||
auto spawnPosition = randomOffset + glm::vec4(m_Position, 0.0f);
|
||||
auto new_npc = new NPC(tmp, nullptr, spawnPosition, FlyMode3);
|
||||
new_npc->AddLootTable();
|
||||
if (new_npc->DropsGlobalLoot())
|
||||
new_npc->CheckGlobalLootTables();
|
||||
entity_list.AddNPC(new_npc);
|
||||
new_npc->AddToHateList(trigger,1);
|
||||
}
|
||||
@ -555,4 +559,4 @@ void Trap::UpdateTrap(bool respawn, bool repopnow)
|
||||
{
|
||||
database.SetTrapData(this, repopnow);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -968,6 +968,8 @@ bool Zone::Init(bool iStaticZone) {
|
||||
|
||||
LoadAlternateAdvancement();
|
||||
|
||||
database.LoadGlobalLoot();
|
||||
|
||||
//Load merchant data
|
||||
zone->GetMerchantDataForZoneLoad();
|
||||
|
||||
@ -2229,6 +2231,8 @@ void Zone::DoAdventureActions()
|
||||
{
|
||||
NPC* npc = new NPC(tmp, nullptr, glm::vec4(ds->assa_x, ds->assa_y, ds->assa_z, ds->assa_h), FlyMode3);
|
||||
npc->AddLootTable();
|
||||
if (npc->DropsGlobalLoot())
|
||||
npc->CheckGlobalLootTables();
|
||||
entity_list.AddNPC(npc);
|
||||
npc->Shout("Rarrrgh!");
|
||||
did_adventure_actions = true;
|
||||
|
||||
@ -28,6 +28,7 @@
|
||||
#include "spawn2.h"
|
||||
#include "spawngroup.h"
|
||||
#include "aa_ability.h"
|
||||
#include "global_loot_manager.h"
|
||||
|
||||
struct ZonePoint
|
||||
{
|
||||
@ -269,6 +270,11 @@ public:
|
||||
void UpdateHotzone();
|
||||
std::unordered_map<int, item_tick_struct> tick_items;
|
||||
|
||||
inline std::vector<int> GetGlobalLootTables(NPC *mob) const { return m_global_loot.GetGlobalLootTables(mob); }
|
||||
inline void AddGlobalLootEntry(GlobalLootEntry &in) { return m_global_loot.AddEntry(in); }
|
||||
inline void ShowZoneGlobalLoot(Client *to) { m_global_loot.ShowZoneGlobalLoot(to); }
|
||||
inline void ShowNPCGlobalLoot(Client *to, NPC *who) { m_global_loot.ShowNPCGlobalLoot(to, who); }
|
||||
|
||||
// random object that provides random values for the zone
|
||||
EQEmu::Random random;
|
||||
|
||||
@ -347,6 +353,8 @@ private:
|
||||
QGlobalCache *qGlobals;
|
||||
|
||||
Timer hotzone_timer;
|
||||
|
||||
GlobalLootManager m_global_loot;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@ -1969,7 +1969,9 @@ const NPCType* ZoneDatabase::LoadNPCTypesData(uint32 npc_type_id, bool bulk_load
|
||||
"npc_types.charm_attack_delay, "
|
||||
"npc_types.charm_accuracy_rating, "
|
||||
"npc_types.charm_avoidance_rating, "
|
||||
"npc_types.charm_atk "
|
||||
"npc_types.charm_atk, "
|
||||
"npc_types.skip_global_loot, "
|
||||
"npc_types.rare_spawn "
|
||||
"FROM npc_types %s",
|
||||
where_condition.c_str()
|
||||
);
|
||||
@ -2156,6 +2158,9 @@ const NPCType* ZoneDatabase::LoadNPCTypesData(uint32 npc_type_id, bool bulk_load
|
||||
temp_npctype_data->charm_avoidance_rating = atoi(row[105]);
|
||||
temp_npctype_data->charm_atk = atoi(row[106]);
|
||||
|
||||
temp_npctype_data->skip_global_loot = atoi(row[107]) != 0;
|
||||
temp_npctype_data->rare_spawn = atoi(row[108]) != 0;
|
||||
|
||||
// If NPC with duplicate NPC id already in table,
|
||||
// free item we attempted to add.
|
||||
if (zone->npctable.find(temp_npctype_data->npc_id) != zone->npctable.end()) {
|
||||
|
||||
@ -424,6 +424,7 @@ public:
|
||||
uint32 GetMaxNPCSpellsID();
|
||||
uint32 GetMaxNPCSpellsEffectsID();
|
||||
bool GetAuraEntry(uint16 spell_id, AuraRecord &record);
|
||||
void LoadGlobalLoot();
|
||||
|
||||
DBnpcspells_Struct* GetNPCSpells(uint32 iDBSpellsID);
|
||||
DBnpcspellseffects_Struct* GetNPCSpellsEffects(uint32 iDBSpellsEffectsID);
|
||||
|
||||
@ -141,6 +141,8 @@ struct NPCType
|
||||
bool ignore_despawn;
|
||||
bool show_name; // should default on
|
||||
bool untargetable;
|
||||
bool skip_global_loot;
|
||||
bool rare_spawn;
|
||||
};
|
||||
|
||||
namespace player_lootitem {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user