mirror of
https://github.com/EQEmu/Server.git
synced 2026-05-16 22:58:34 +00:00
[Feature] Faction Association (#2408)
* Add faction logging category Probably should use this for more things * Add FactionAssociation struct This is simply just a struct that contains an array of faction ids and multiplier. This can hold a maximum of 10 entries (Seru hit is 8, so 2 extra) this can be raised if need be. * Add database changes and other data point changes This is all the database changes and loading changes Included is an optional SQL that will be used as a starting point, there is likely errors or typos, but we will fix those as they are discovered. * Add Client::RewardFaction function This just takes the faction ID and the magnitude of the primary faction hit and calculates the rest. The minimum change will be either 1 or -1. We stop processing after we see an ID of 0 and assume there will be no later entries. The primary faction ID will always receive a hit even if there is no faction association entries * Add users of RewardFaction to NPC death, tasks, and QuestRewards This will only use the new system if the magnitude is set, otherwise we will just use the old system still * Add quest system calls and lua QuestReward support * Add #factionassociation command This just calls RewardFaction, mostly useful for debugging
This commit is contained in:
committed by
GitHub
parent
efe1879115
commit
2b4e555eae
+1
-1
@@ -2456,7 +2456,7 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
|
||||
|
||||
//do faction hits even if we are a merchant, so long as a player killed us
|
||||
if (!IsCharmed() && give_exp_client && !RuleB(NPC, EnableMeritBasedFaction))
|
||||
hate_list.DoFactionHits(GetNPCFactionID());
|
||||
hate_list.DoFactionHits(GetNPCFactionID(), GetPrimaryFaction(), GetFactionAmount());
|
||||
|
||||
bool IsLdonTreasure = (GetClass() == LDON_TREASURE);
|
||||
|
||||
|
||||
+40
-4
@@ -8760,7 +8760,7 @@ void Client::QuestReward(Mob* target, uint32 copper, uint32 silver, uint32 gold,
|
||||
int32 nfl_id = target->CastToNPC()->GetNPCFactionID();
|
||||
SetFactionLevel(CharacterID(), nfl_id, GetBaseClass(), GetBaseRace(), GetDeity(), true);
|
||||
qr->faction = target->CastToNPC()->GetPrimaryFaction();
|
||||
qr->faction_mod = 1; // Too lazy to get real value, not sure if this is even used by client anyhow.
|
||||
qr->faction_mod = 1; // Too lazy to get real value, not even used by client anyhow.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8780,7 +8780,7 @@ void Client::QuestReward(Mob* target, const QuestReward_Struct &reward, bool fac
|
||||
memcpy(qr, &reward, sizeof(QuestReward_Struct));
|
||||
|
||||
// not set in caller because reasons
|
||||
qr->mob_id = target ? target->GetID() : 0; // Entity ID for the from mob name
|
||||
qr->mob_id = target ? target->GetID() : 0; // Entity ID for the from mob name, tasks won't set this
|
||||
|
||||
if (reward.copper > 0 || reward.silver > 0 || reward.gold > 0 || reward.platinum > 0)
|
||||
AddMoneyToPP(reward.copper, reward.silver, reward.gold, reward.platinum);
|
||||
@@ -8789,6 +8789,12 @@ void Client::QuestReward(Mob* target, const QuestReward_Struct &reward, bool fac
|
||||
if (reward.item_id[i] > 0)
|
||||
SummonItem(reward.item_id[i], 0, 0, 0, 0, 0, 0, false, EQ::invslot::slotCursor);
|
||||
|
||||
// only process if both are valid
|
||||
// if we don't have a target here, we want to just reward, but if there is a target, need to check charm
|
||||
if (reward.faction && reward.faction_mod && (target == nullptr || !target->IsCharmed()))
|
||||
RewardFaction(reward.faction, reward.faction_mod);
|
||||
|
||||
// legacy support
|
||||
if (faction)
|
||||
{
|
||||
if (target && target->IsNPC() && !target->IsCharmed())
|
||||
@@ -8796,11 +8802,11 @@ void Client::QuestReward(Mob* target, const QuestReward_Struct &reward, bool fac
|
||||
int32 nfl_id = target->CastToNPC()->GetNPCFactionID();
|
||||
SetFactionLevel(CharacterID(), nfl_id, GetBaseClass(), GetBaseRace(), GetDeity(), true);
|
||||
qr->faction = target->CastToNPC()->GetPrimaryFaction();
|
||||
qr->faction_mod = 1; // Too lazy to get real value, not sure if this is even used by client anyhow.
|
||||
qr->faction_mod = 1; // Too lazy to get real value, not even used by client anyhow.
|
||||
}
|
||||
}
|
||||
|
||||
if (reward.exp_reward> 0)
|
||||
if (reward.exp_reward > 0)
|
||||
AddEXP(reward.exp_reward);
|
||||
|
||||
QueuePacket(outapp, true, Client::CLIENT_CONNECTED);
|
||||
@@ -8821,6 +8827,36 @@ void Client::CashReward(uint32 copper, uint32 silver, uint32 gold, uint32 platin
|
||||
QueuePacket(outapp.get());
|
||||
}
|
||||
|
||||
void Client::RewardFaction(int id, int amount)
|
||||
{
|
||||
// first we hit the primary faction, even without any associations
|
||||
SetFactionLevel2(CharacterID(), id, GetClass(), GetBaseRace(), GetDeity(), amount, false);
|
||||
|
||||
auto faction_assoc = content_db.GetFactionAssociationHit(id);
|
||||
// We could log here, but since it's actually expected for some not to have entries, it would be noisy.
|
||||
if (!faction_assoc) {
|
||||
return;
|
||||
}
|
||||
|
||||
// now hit them in order
|
||||
for (int i = 0; i < MAX_FACTION_ASSOC; ++i) {
|
||||
if (faction_assoc->hits[i].id <= 0) // we don't allow later entries
|
||||
break;
|
||||
if (faction_assoc->hits[i].multiplier == 0.0f) {
|
||||
LogFaction("Bad association multiplier for ID {} entry {}", id, i + 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
// value is truncated and min clamped to 1 (or -1)
|
||||
float temp = faction_assoc->hits[i].multiplier * amount;
|
||||
int sign = temp < 0.0f ? -1 : 1;
|
||||
int32 new_amount = std::max(1, static_cast<int32>(std::abs(temp))) * sign;
|
||||
|
||||
SetFactionLevel2(CharacterID(), faction_assoc->hits[i].id, GetClass(), GetBaseRace(), GetDeity(),
|
||||
new_amount, false);
|
||||
}
|
||||
}
|
||||
|
||||
void Client::SendHPUpdateMarquee(){
|
||||
if (!this || !IsClient() || !current_hp || !max_hp)
|
||||
return;
|
||||
|
||||
+2
-1
@@ -1613,8 +1613,9 @@ public:
|
||||
uint32 GetNextInvSnapshotTime() { return m_epp.next_invsnapshot_time; }
|
||||
|
||||
void QuestReward(Mob* target, uint32 copper = 0, uint32 silver = 0, uint32 gold = 0, uint32 platinum = 0, uint32 itemid = 0, uint32 exp = 0, bool faction = false);
|
||||
void QuestReward(Mob* target, const QuestReward_Struct &reward, bool faction); // TODO: Fix faction processing
|
||||
void QuestReward(Mob* target, const QuestReward_Struct &reward, bool faction = false);
|
||||
void CashReward(uint32 copper, uint32 silver, uint32 gold, uint32 platinum);
|
||||
void RewardFaction(int id, int amount);
|
||||
|
||||
void ResetHPUpdateTimer() { hpupdate_timer.Start(); }
|
||||
|
||||
|
||||
@@ -140,6 +140,7 @@ int command_init(void)
|
||||
command_add("endurance", "Restores your or your target's endurance.", AccountStatus::Guide, command_endurance) ||
|
||||
command_add("equipitem", "[slotid(0-21)] - Equip the item on your cursor into the specified slot", AccountStatus::Guide, command_equipitem) ||
|
||||
command_add("faction", "[Find (criteria | all ) | Review (criteria | all) | Reset (id)] - Resets Player's Faction", AccountStatus::QuestTroupe, command_faction) ||
|
||||
command_add("factionassociation", "[factionid] [amount] - triggers a faction hits via association", AccountStatus::GMLeadAdmin, command_faction_association) ||
|
||||
command_add("feature", "Change your or your target's feature's temporarily", AccountStatus::QuestTroupe, command_feature) ||
|
||||
command_add("findaliases", "[Search Criteria]- Searches for available command aliases, by alias or command", AccountStatus::Player, command_findaliases) ||
|
||||
command_add("findclass", "[Search Criteria] - Search for a class", AccountStatus::Guide, command_findclass) ||
|
||||
@@ -1146,6 +1147,7 @@ void command_bot(Client *c, const Seperator *sep)
|
||||
#include "gm_commands/spawnfix.cpp"
|
||||
#include "gm_commands/spawnstatus.cpp"
|
||||
#include "gm_commands/spellinfo.cpp"
|
||||
#include "gm_commands/faction_association.cpp"
|
||||
#include "gm_commands/stun.cpp"
|
||||
#include "gm_commands/summon.cpp"
|
||||
#include "gm_commands/summonburiedplayercorpse.cpp"
|
||||
|
||||
@@ -77,6 +77,7 @@ void command_enablerecipe(Client *c, const Seperator *sep);
|
||||
void command_endurance(Client *c, const Seperator *sep);
|
||||
void command_equipitem(Client *c, const Seperator *sep);
|
||||
void command_faction(Client *c, const Seperator *sep);
|
||||
void command_faction_association(Client *c, const Seperator *sep);
|
||||
void command_feature(Client *c, const Seperator *sep);
|
||||
void command_findaliases(Client *c, const Seperator *sep);
|
||||
void command_findclass(Client *c, const Seperator *sep);
|
||||
|
||||
@@ -689,6 +689,11 @@ void Perl__faction(int faction_id, int value, int temp)
|
||||
quest_manager.faction(faction_id, value, temp);
|
||||
}
|
||||
|
||||
void Perl__rewardfaction(int faction_id, int value)
|
||||
{
|
||||
quest_manager.rewardfaction(faction_id, value);
|
||||
}
|
||||
|
||||
void Perl__setsky(uint8 new_sky)
|
||||
{
|
||||
quest_manager.setsky(new_sky);
|
||||
@@ -4171,6 +4176,7 @@ void perl_register_quest()
|
||||
package.add("resettaskactivity", &Perl__resettaskactivity);
|
||||
package.add("respawn", &Perl__respawn);
|
||||
package.add("resume", &Perl__resume);
|
||||
package.add("rewardfaction", &Perl__rewardfaction);
|
||||
package.add("safemove", &Perl__safemove);
|
||||
package.add("save", &Perl__save);
|
||||
package.add("say", (void(*)(const char*))&Perl__say);
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
#include "../client.h"
|
||||
|
||||
void command_faction_association(Client *c, const Seperator *sep)
|
||||
{
|
||||
if (sep->argnum < 2 || !sep->IsNumber(1) || !sep->IsNumber(2)) {
|
||||
c->Message(Chat::White, "Usage: #factionassociation <factionid> <amount>");
|
||||
c->Message(Chat::White, " Defaults to self, unless target is also a client.");
|
||||
return;
|
||||
}
|
||||
|
||||
// default to self unless target is also a client
|
||||
auto target = c;
|
||||
if (c->GetTarget() && c->GetTarget()->IsClient()) {
|
||||
target = c->GetTarget()->CastToClient();
|
||||
}
|
||||
|
||||
target->RewardFaction(atoi(sep->arg[1]), atoi(sep->arg[2]));
|
||||
}
|
||||
+10
-4
@@ -257,8 +257,9 @@ bool HateList::RemoveEntFromHateList(Mob *in_entity)
|
||||
return is_found;
|
||||
}
|
||||
|
||||
void HateList::DoFactionHits(int64 npc_faction_level_id) {
|
||||
if (npc_faction_level_id <= 0)
|
||||
// so if faction_id and faction_value are set, we do RewardFaction, otherwise old stuff
|
||||
void HateList::DoFactionHits(int64 npc_faction_level_id, int32 faction_id, int32 faction_value) {
|
||||
if (npc_faction_level_id <= 0 && faction_id <= 0 && faction_value == 0)
|
||||
return;
|
||||
auto iterator = list.begin();
|
||||
while (iterator != list.end())
|
||||
@@ -270,8 +271,13 @@ void HateList::DoFactionHits(int64 npc_faction_level_id) {
|
||||
else
|
||||
client = nullptr;
|
||||
|
||||
if (client)
|
||||
client->SetFactionLevel(client->CharacterID(), npc_faction_level_id, client->GetBaseClass(), client->GetBaseRace(), client->GetDeity());
|
||||
if (client) {
|
||||
if (faction_id != 0 && faction_value != 0) {
|
||||
client->RewardFaction(faction_id, faction_value);
|
||||
} else {
|
||||
client->SetFactionLevel(client->CharacterID(), npc_faction_level_id, client->GetBaseClass(), client->GetBaseRace(), client->GetDeity());
|
||||
}
|
||||
}
|
||||
++iterator;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -73,7 +73,7 @@ public:
|
||||
bool in_is_frenzied = false,
|
||||
bool add_to_hate_list_if_not_exist = true
|
||||
);
|
||||
void DoFactionHits(int64 npc_faction_level_id);
|
||||
void DoFactionHits(int64 npc_faction_level_id, int32 faction_id, int32 faction_value);
|
||||
void IsEntityInFrenzyMode();
|
||||
void PrintHateListToClient(Client *c);
|
||||
void SetHateAmountOnEnt(Mob *other, int64 in_hate, uint64 in_damage);
|
||||
|
||||
+27
-3
@@ -436,6 +436,11 @@ void Lua_Client::SetFactionLevel2(uint32 char_id, int faction_id, int char_class
|
||||
self->SetFactionLevel2(char_id, faction_id, char_class, char_race, char_deity, value, temp);
|
||||
}
|
||||
|
||||
void Lua_Client::RewardFaction(int id, int amount) {
|
||||
Lua_Safe_Call_Void();
|
||||
self->RewardFaction(id, amount);
|
||||
}
|
||||
|
||||
int Lua_Client::GetRawItemAC() {
|
||||
Lua_Safe_Call_Int();
|
||||
return self->GetRawItemAC();
|
||||
@@ -1761,9 +1766,27 @@ void Lua_Client::QuestReward(Lua_Mob target, luabind::adl::object reward) {
|
||||
|
||||
cur = reward["faction"];
|
||||
if (luabind::type(cur) != LUA_TNIL) {
|
||||
try {
|
||||
faction = luabind::object_cast<bool>(cur);
|
||||
} catch (luabind::cast_failed &) {
|
||||
// if it's a table it will be {faction, faction_mod}
|
||||
if (luabind::type(cur) == LUA_TTABLE) {
|
||||
auto item = cur[1];
|
||||
if (luabind::type(item) != LUA_TNIL) {
|
||||
try {
|
||||
quest_reward.faction = luabind::object_cast<uint32>(item);
|
||||
} catch (luabind::cast_failed &) {
|
||||
}
|
||||
}
|
||||
item = cur[2];
|
||||
if (luabind::type(item) != LUA_TNIL) {
|
||||
try {
|
||||
quest_reward.faction_mod = luabind::object_cast<uint32>(item);
|
||||
} catch (luabind::cast_failed &) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
faction = luabind::object_cast<bool>(cur);
|
||||
} catch (luabind::cast_failed &) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2922,6 +2945,7 @@ luabind::scope lua_register_client() {
|
||||
.def("ResetCastbarCooldownBySpellID", (void(Lua_Client::*)(uint32))&Lua_Client::ResetCastbarCooldownBySpellID)
|
||||
.def("ResetDisciplineTimer", (void(Lua_Client::*)(uint32))&Lua_Client::ResetDisciplineTimer)
|
||||
.def("ResetTrade", (void(Lua_Client::*)(void))&Lua_Client::ResetTrade)
|
||||
.def("RewardFaction", (void(Lua_Client::*)(int,int))&Lua_Client::RewardFaction)
|
||||
.def("Save", (void(Lua_Client::*)(int))&Lua_Client::Save)
|
||||
.def("Save", (void(Lua_Client::*)(void))&Lua_Client::Save)
|
||||
.def("SaveBackup", (void(Lua_Client::*)(void))&Lua_Client::SaveBackup)
|
||||
|
||||
@@ -132,6 +132,7 @@ public:
|
||||
int GetFactionLevel(uint32 char_id, uint32 npc_id, uint32 race, uint32 class_, uint32 deity, uint32 faction, Lua_NPC npc);
|
||||
void SetFactionLevel(uint32 char_id, uint32 npc_id, int char_class, int char_race, int char_deity);
|
||||
void SetFactionLevel2(uint32 char_id, int faction_id, int char_class, int char_race, int char_deity, int value, int temp);
|
||||
void RewardFaction(int id, int amount);
|
||||
int GetRawItemAC();
|
||||
uint32 AccountID();
|
||||
const char *AccountName();
|
||||
|
||||
@@ -315,6 +315,11 @@ int main(int argc, char** argv) {
|
||||
LogError("Loading npcs faction lists failed!");
|
||||
return 1;
|
||||
}
|
||||
LogInfo("Loading faction association hits");
|
||||
if (!content_db.LoadFactionAssociation(hotfix_name)) {
|
||||
LogError("Loading faction association hits failed!");
|
||||
return 1;
|
||||
}
|
||||
LogInfo("Loading loot tables");
|
||||
if (!database.LoadLoot(hotfix_name)) {
|
||||
LogError("Loading loot failed!");
|
||||
|
||||
@@ -259,6 +259,7 @@ NPC::NPC(const NPCType *npc_type_data, Spawn2 *in_respawn, const glm::vec4 &posi
|
||||
rare_spawn = npc_type_data->rare_spawn;
|
||||
no_target_hotkey = npc_type_data->no_target_hotkey;
|
||||
primary_faction = 0;
|
||||
faction_amount = npc_type_data->faction_amount;
|
||||
|
||||
SetNPCFactionID(npc_type_data->npc_faction_id);
|
||||
|
||||
|
||||
@@ -278,6 +278,9 @@ public:
|
||||
inline int32 GetPrimaryFaction() const
|
||||
{ return primary_faction; }
|
||||
|
||||
inline int32 GetFactionAmount() const
|
||||
{ return faction_amount; }
|
||||
|
||||
int64 GetNPCHate(Mob *in_ent)
|
||||
{ return hate_list.GetEntHateAmount(in_ent); }
|
||||
|
||||
@@ -561,6 +564,7 @@ protected:
|
||||
|
||||
int32 npc_faction_id;
|
||||
int32 primary_faction;
|
||||
int32 faction_amount;
|
||||
|
||||
Timer attacked_timer; //running while we are being attacked (damaged)
|
||||
Timer swarm_timer;
|
||||
|
||||
@@ -1350,6 +1350,15 @@ void QuestManager::faction(int faction_id, int faction_value, int temp) {
|
||||
}
|
||||
}
|
||||
|
||||
void QuestManager::rewardfaction(int faction_id, int faction_value) {
|
||||
QuestManagerCurrentQuestVars();
|
||||
if (initiator && initiator->IsClient()) {
|
||||
if (faction_id != 0 && faction_value != 0) {
|
||||
initiator->RewardFaction(faction_id, faction_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QuestManager::setsky(uint8 new_sky) {
|
||||
QuestManagerCurrentQuestVars();
|
||||
if (zone)
|
||||
|
||||
@@ -150,6 +150,7 @@ public:
|
||||
void attacknpctype(int npc_type_id);
|
||||
void save();
|
||||
void faction(int faction_id, int faction_value, int temp);
|
||||
void rewardfaction(int faction_id, int faction_value);
|
||||
void setsky(uint8 new_sky);
|
||||
void setguild(uint32 new_guild_id, uint8 new_rank);
|
||||
void CreateGuild(const char *guild_name, const char *leader);
|
||||
|
||||
@@ -995,6 +995,9 @@ void ClientTaskState::RewardTask(Client *client, const TaskInformation *task_inf
|
||||
client->Message(Chat::Yellow, task_information->completion_emote.c_str());
|
||||
}
|
||||
|
||||
// TODO: this function should sometimes use QuestReward_Struct and CashReward_Struct
|
||||
// assumption is they use QuestReward_Struct when there is more than 1 thing getting rewarded
|
||||
|
||||
const EQ::ItemData *item_data;
|
||||
std::vector<int> reward_list;
|
||||
|
||||
@@ -1029,7 +1032,7 @@ void ClientTaskState::RewardTask(Client *client, const TaskInformation *task_inf
|
||||
}
|
||||
|
||||
// just use normal NPC faction ID stuff
|
||||
if (task_information->faction_reward) {
|
||||
if (task_information->faction_reward && task_information->faction_amount == 0) {
|
||||
client->SetFactionLevel(
|
||||
client->CharacterID(),
|
||||
task_information->faction_reward,
|
||||
@@ -1037,6 +1040,11 @@ void ClientTaskState::RewardTask(Client *client, const TaskInformation *task_inf
|
||||
client->GetBaseRace(),
|
||||
client->GetDeity()
|
||||
);
|
||||
} else if (task_information->faction_reward != 0 && task_information->faction_amount != 0) {
|
||||
client->RewardFaction(
|
||||
task_information->faction_reward,
|
||||
task_information->faction_amount
|
||||
);
|
||||
}
|
||||
|
||||
if (task_information->cash_reward) {
|
||||
|
||||
@@ -92,6 +92,7 @@ bool TaskManager::LoadTasks(int single_task)
|
||||
task_data.reward_points = task.reward_points;
|
||||
task_data.reward_point_type = static_cast<AltCurrencyType>(task.reward_point_type);
|
||||
task_data.faction_reward = task.faction_reward;
|
||||
task_data.faction_amount = task.faction_amount;
|
||||
task_data.min_level = task.minlevel;
|
||||
task_data.max_level = task.maxlevel;
|
||||
task_data.level_spread = task.level_spread;
|
||||
|
||||
@@ -3186,6 +3186,11 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p)
|
||||
LogError("Loading npcs faction lists failed!");
|
||||
}
|
||||
|
||||
LogInfo("Loading faction association hits");
|
||||
if (!content_db.LoadFactionAssociation(hotfix_name)) {
|
||||
LogError("Loading faction association hits failed!");
|
||||
}
|
||||
|
||||
LogInfo("Loading loot tables");
|
||||
if (!content_db.LoadLoot(hotfix_name)) {
|
||||
LogError("Loading loot failed!");
|
||||
|
||||
+1
-1
@@ -2385,7 +2385,7 @@ const NPCType *ZoneDatabase::LoadNPCTypesData(uint32 npc_type_id, bool bulk_load
|
||||
t->skip_auto_scale = false; // hardcoded here for now
|
||||
t->hp_regen_per_second = n.hp_regen_per_second;
|
||||
t->heroic_strikethrough = n.heroic_strikethrough;
|
||||
|
||||
t->faction_amount = n.faction_amount;
|
||||
|
||||
// If NPC with duplicate NPC id already in table,
|
||||
// free item we attempted to add.
|
||||
|
||||
@@ -54,6 +54,7 @@ struct NPCType
|
||||
uint32 npc_spells_id;
|
||||
uint32 npc_spells_effects_id;
|
||||
int32 npc_faction_id;
|
||||
int32 faction_amount; // faction association magnitude, will use primary faction
|
||||
uint32 merchanttype;
|
||||
uint32 alt_currency_type;
|
||||
uint32 adventure_template;
|
||||
|
||||
Reference in New Issue
Block a user