Item/Merchant/Scribing/Inventory Updates
Fix #231 - item_description function which returns a string added to ItemScripts. Can allow setting of specialized red text, eg "You already know this language.". Completes 231 with the other ItemScripts support (item_difficulty) and scribing support which includes a new rule: RULE_INIT(R_Spells, RequirePreviousTierScribe, "0"); // requires step up apprentice -> apprentice (handcrafted?) -> journeyman (handcrafted?) -> adept -> expert -> master Fix #323 - check in main equipment (0) slots for bags only which addresses the appearance overriding bags issue.
This commit is contained in:
parent
017a9b80ee
commit
82ccc26642
11 changed files with 206 additions and 31 deletions
|
@ -1759,7 +1759,11 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
|
|||
Spell* spell = master_spell_list.GetSpell(item->skill_info->spell_id, item->skill_info->spell_tier);
|
||||
int8 old_slot = 0;
|
||||
if (spell) {
|
||||
if (!player->HasSpell(spell->GetSpellID(), spell->GetSpellTier(), true)) {
|
||||
|
||||
int16 tier_up = player->GetTierUp(spell->GetSpellTier());
|
||||
if (rule_manager.GetGlobalRule(R_Spells, RequirePreviousTierScribe)->GetInt8() && !player->HasSpell(spell->GetSpellID(), tier_up, false, true))
|
||||
client->SimpleMessage(CHANNEL_COLOR_RED, "You have not scribed the required previous version of this ability.");
|
||||
else if (!player->HasSpell(spell->GetSpellID(), spell->GetSpellTier(), true)) {
|
||||
old_slot = player->GetSpellSlot(spell->GetSpellID());
|
||||
player->RemoveSpellBookEntry(spell->GetSpellID());
|
||||
player->AddSpellBookEntry(spell->GetSpellID(), spell->GetSpellTier(), old_slot, spell->GetSpellData()->spell_book_type, spell->GetSpellData()->linked_timer, true);
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include "../Rules/Rules.h"
|
||||
|
||||
extern World world;
|
||||
extern MasterSpellList master_spell_list;
|
||||
|
@ -38,6 +39,7 @@ extern MasterQuestList master_quest_list;
|
|||
extern MasterRecipeList master_recipe_list;
|
||||
extern ConfigReader configReader;
|
||||
extern LuaInterface* lua_interface;
|
||||
extern RuleManager rule_manager;
|
||||
|
||||
MasterItemList::MasterItemList(){
|
||||
AddMappedItemStat(ITEM_STAT_ADORNING, std::string("adorning"));
|
||||
|
@ -2184,16 +2186,22 @@ void Item::serialize(PacketStruct* packet, bool show_name, Player* player, int16
|
|||
packet->setSubstructDataByName("header_info", "footer_type", 0);
|
||||
|
||||
spell->SetPacketInformation(packet, player->GetZone()->GetClientBySpawn(player));
|
||||
if (player->HasSpell(skill_info->spell_id, skill_info->spell_tier))
|
||||
if (player->HasSpell(skill_info->spell_id, skill_info->spell_tier, true))
|
||||
packet->setDataByName("scribed", 1);
|
||||
else
|
||||
|
||||
if (packet->GetVersion() >= 927){
|
||||
if (player->HasSpell(skill_info->spell_id, skill_info->spell_tier, true))
|
||||
packet->setAddToPacketByName("better_version", 1);// need to confirm
|
||||
packet->setAddToPacketByName("scribed_better_version", 1);// need to confirm
|
||||
}
|
||||
else
|
||||
packet->setAddToPacketByName("better_version", 0); //if not scribed
|
||||
packet->setDataByName("require_previous", 0);
|
||||
packet->setAddToPacketByName("scribed_better_version", 0); //if not scribed
|
||||
|
||||
// either don't require previous tier or check that we have the lower tier spells potentially
|
||||
int32 tier_up = player->GetTierUp(skill_info->spell_tier);
|
||||
if (!rule_manager.GetGlobalRule(R_Spells, RequirePreviousTierScribe)->GetInt8() || player->HasSpell(skill_info->spell_id, tier_up, false, true))
|
||||
packet->setDataByName("require_previous", 1, 0);
|
||||
// membership required
|
||||
//packet->setDataByName("unknown_1188_2_MJ", 1, 1);
|
||||
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -629,6 +629,33 @@ void LuaInterface::RemoveSpawnScript(const char* name) {
|
|||
MSpawnScripts.releasewritelock(__FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
bool LuaInterface::CallItemScript(lua_State* state, int8 num_parameters, std::string* returnValue) {
|
||||
if(shutting_down)
|
||||
return false;
|
||||
if(!state || lua_pcall(state, num_parameters, 1, 0) != 0){
|
||||
if (state){
|
||||
const char* err = lua_tostring(state, -1);
|
||||
LogError("%s: %s", GetScriptName(state), err);
|
||||
lua_pop(state, 1);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string result = std::string("");
|
||||
|
||||
if(lua_isstring(state, -1)){
|
||||
size_t size = 0;
|
||||
const char* str = lua_tolstring(state, -1, &size);
|
||||
if(str)
|
||||
result = string(str);
|
||||
}
|
||||
|
||||
if(returnValue)
|
||||
*returnValue = std::string(result);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LuaInterface::CallItemScript(lua_State* state, int8 num_parameters, sint64* returnValue) {
|
||||
if(shutting_down)
|
||||
return false;
|
||||
|
@ -2088,6 +2115,47 @@ bool LuaInterface::RunItemScript(string script_name, const char* function_name,
|
|||
return false;
|
||||
}
|
||||
|
||||
bool LuaInterface::RunItemScriptWithReturnString(string script_name, const char* function_name, Item* item, Spawn* spawn, std::string* returnValue) {
|
||||
if(!item)
|
||||
return false;
|
||||
lua_State* state = GetItemScript(script_name.c_str(), true, true);
|
||||
if(state){
|
||||
Mutex* mutex = GetItemScriptMutex(script_name.c_str());
|
||||
if(mutex)
|
||||
mutex->readlock(__FUNCTION__, __LINE__);
|
||||
else{
|
||||
LogError("Error getting lock for '%s'", script_name.c_str());
|
||||
UseItemScript(script_name.c_str(), state, false);
|
||||
return false;
|
||||
}
|
||||
lua_getglobal(state, function_name);
|
||||
if (!lua_isfunction(state, lua_gettop(state))){
|
||||
lua_pop(state, 1);
|
||||
mutex->releasereadlock(__FUNCTION__);
|
||||
UseItemScript(script_name.c_str(), state, false);
|
||||
return false;
|
||||
}
|
||||
SetItemValue(state, item);
|
||||
int8 num_parms = 1;
|
||||
if(spawn){
|
||||
SetSpawnValue(state, spawn);
|
||||
num_parms++;
|
||||
}
|
||||
if(!CallItemScript(state, num_parms, returnValue)){
|
||||
if(mutex)
|
||||
mutex->releasereadlock(__FUNCTION__, __LINE__);
|
||||
UseItemScript(script_name.c_str(), state, false);
|
||||
return false;
|
||||
}
|
||||
if(mutex)
|
||||
mutex->releasereadlock(__FUNCTION__, __LINE__);
|
||||
UseItemScript(script_name.c_str(), state, false);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool LuaInterface::RunSpawnScript(string script_name, const char* function_name, Spawn* npc, Spawn* spawn, const char* message, bool is_door_open) {
|
||||
if(!npc || spawn_scripts_reloading)
|
||||
|
|
|
@ -251,6 +251,8 @@ public:
|
|||
|
||||
void RemoveSpawnScript(const char* name);
|
||||
bool RunItemScript(string script_name, const char* function_name, Item* item, Spawn* spawn = 0, sint64* returnValue = 0);
|
||||
bool RunItemScriptWithReturnString(string script_name, const char* function_name, Item* item, Spawn* spawn = 0, std::string* returnValue = 0);
|
||||
bool CallItemScript(lua_State* state, int8 num_parameters, std::string* returnValue = 0);
|
||||
bool CallItemScript(lua_State* state, int8 num_parameters, sint64* returnValue = 0);
|
||||
bool RunSpawnScript(string script_name, const char* function_name, Spawn* npc, Spawn* spawn = 0, const char* message = 0, bool is_door_open = false);
|
||||
bool CallSpawnScript(lua_State* state, int8 num_parameters);
|
||||
|
|
|
@ -1828,8 +1828,8 @@ vector<EQ2Packet*> Player::UnequipItem(int16 index, sint32 bag_id, int8 slot, in
|
|||
bag_id = 0;
|
||||
}
|
||||
|
||||
if (item_list.items.count(bag_id) > 0 && item_list.items[bag_id][appearance_type].count(slot) > 0)
|
||||
to_item = item_list.items[bag_id][appearance_type][slot];
|
||||
if (item_list.items.count(bag_id) > 0 && item_list.items[bag_id][BASE_EQUIPMENT].count(slot) > 0)
|
||||
to_item = item_list.items[bag_id][BASE_EQUIPMENT][slot];
|
||||
if (to_item && equipList->CanItemBeEquippedInSlot(to_item, ConvertSlotFromClient(item->details.slot_id, version))) {
|
||||
equipList->RemoveItem(index);
|
||||
if(item->details.appearance_type)
|
||||
|
@ -2829,14 +2829,31 @@ vector<SpellBookEntry*>* Player::GetSpellsSaveNeeded(){
|
|||
return ret;
|
||||
}
|
||||
|
||||
bool Player::HasSpell(int32 spell_id, int8 tier, bool include_higher_tiers){
|
||||
int16 Player::GetTierUp(int16 tier)
|
||||
{
|
||||
switch(tier)
|
||||
{
|
||||
case 0:
|
||||
break;
|
||||
case 7:
|
||||
case 9:
|
||||
tier -= 2;
|
||||
break;
|
||||
default:
|
||||
tier -= 1;
|
||||
break;
|
||||
}
|
||||
|
||||
return tier;
|
||||
}
|
||||
bool Player::HasSpell(int32 spell_id, int8 tier, bool include_higher_tiers, bool include_possible_scribe){
|
||||
bool ret = false;
|
||||
vector<SpellBookEntry*>::iterator itr;
|
||||
MSpellsBook.lock();
|
||||
SpellBookEntry* spell = 0;
|
||||
for(itr = spells.begin(); itr != spells.end(); itr++){
|
||||
spell = *itr;
|
||||
if(spell->spell_id == spell_id && (tier == 255 || spell->tier == tier || (include_higher_tiers && spell->tier > tier))){
|
||||
if(spell->spell_id == spell_id && (tier == 255 || spell->tier == tier || (include_higher_tiers && spell->tier > tier) || (include_possible_scribe && tier <= spell->tier))){
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -682,7 +682,8 @@ public:
|
|||
void RemovePendingLootItem(int32 id, int32 item_id);
|
||||
void RemovePendingLootItems(int32 id);
|
||||
void AddPendingLootItems(int32 id, vector<Item*>* items);
|
||||
bool HasSpell(int32 spell_id, int8 tier = 255, bool include_higher_tiers = false);
|
||||
int16 GetTierUp(int16 tier);
|
||||
bool HasSpell(int32 spell_id, int8 tier = 255, bool include_higher_tiers = false, bool include_possible_scribe = false);
|
||||
bool HasRecipeBook(int32 recipe_id);
|
||||
void AddPlayerStatistic(int32 stat_id, sint32 stat_value, int32 stat_date);
|
||||
void UpdatePlayerStatistic(int32 stat_id, sint32 stat_value, bool overwrite = false);
|
||||
|
|
|
@ -333,6 +333,7 @@ void RuleManager::Init()
|
|||
RULE_INIT(R_Spells, EnableCrossZoneTargetBuffs, "0"); // enables/disables allowing cross zone target buffs
|
||||
RULE_INIT(R_Spells, PlayerSpellSaveStateWaitInterval, "100"); // time in milliseconds we wait before performing a save when the spell save trigger is activated, allows additional actions to take place until the cap is hit
|
||||
RULE_INIT(R_Spells, PlayerSpellSaveStateCap, "1000"); // sets a maximum wait time before we queue a spell state save to the DB, given a lot can go on in a short period with players especially in combat, maybe good to have this at a higher interval.
|
||||
RULE_INIT(R_Spells, RequirePreviousTierScribe, "0"); // requires step up apprentice -> apprentice (handcrafted?) -> journeyman (handcrafted?) -> adept -> expert -> master
|
||||
|
||||
RULE_INIT(R_Expansion, GlobalExpansionFlag, "0");
|
||||
RULE_INIT(R_Expansion, GlobalHolidayFlag, "0");
|
||||
|
|
|
@ -182,6 +182,7 @@ enum RuleType {
|
|||
EnableCrossZoneTargetBuffs,
|
||||
PlayerSpellSaveStateWaitInterval,
|
||||
PlayerSpellSaveStateCap,
|
||||
RequirePreviousTierScribe,
|
||||
|
||||
/* ZONE TIMERS */
|
||||
RegenTimer,
|
||||
|
|
|
@ -7009,10 +7009,7 @@ void Client::SendBuyMerchantList(bool sell) {
|
|||
|
||||
sint64 overrideValue = 0;
|
||||
if (item->GetItemScript() && lua_interface && lua_interface->RunItemScript(item->GetItemScript(), "item_difficulty", item, player, &overrideValue))
|
||||
{
|
||||
item_difficulty = (sint8)overrideValue;
|
||||
printf("Override difficulty: %i\n",item_difficulty);
|
||||
}
|
||||
|
||||
item_difficulty -= 6;
|
||||
if (item_difficulty < 0)
|
||||
|
@ -7022,8 +7019,12 @@ void Client::SendBuyMerchantList(bool sell) {
|
|||
packet->setArrayDataByName("quantity", ItemInfo.quantity, i);
|
||||
packet->setArrayDataByName("unknown5", 255, i);
|
||||
packet->setArrayDataByName("stack_size2", item->stack_count, i);
|
||||
if (GetVersion() <= 1096)
|
||||
packet->setArrayDataByName("description", item->description.c_str(), i);
|
||||
|
||||
std::string overrideValueStr;
|
||||
// classic client isn't properly tracking this field, DoF we don't have it identified yet, but no field to cause any issues (can add later if identified)
|
||||
if (GetVersion() >= 546 && item->GetItemScript() && lua_interface && lua_interface->RunItemScriptWithReturnString(item->GetItemScript(), "item_description", item, player, &overrideValueStr))
|
||||
packet->setArrayDataByName("description", overrideValueStr.c_str(), i);
|
||||
|
||||
// If no price set in the merchant_inventory table then use the old method
|
||||
if (ItemInfo.price_item_id == 0 && ItemInfo.price_item2_id == 0 && ItemInfo.price_coins == 0 && ItemInfo.price_status == 0 && ItemInfo.price_stationcash == 0) {
|
||||
sell_price = (int32)(item->sell_price * multiplier);
|
||||
|
@ -7191,10 +7192,7 @@ void Client::SendSellMerchantList(bool sell) {
|
|||
|
||||
sint64 overrideValue = 0;
|
||||
if (item->GetItemScript() && lua_interface && lua_interface->RunItemScript(item->GetItemScript(), "item_difficulty", item, player, &overrideValue))
|
||||
{
|
||||
item_difficulty = (sint8)overrideValue;
|
||||
printf("Override difficulty: %i\n",item_difficulty);
|
||||
}
|
||||
|
||||
item_difficulty -= 6;
|
||||
if (item_difficulty < 0)
|
||||
|
@ -7271,10 +7269,7 @@ void Client::SendBuyBackList(bool sell) {
|
|||
|
||||
sint64 overrideValue = 0;
|
||||
if (master_item->GetItemScript() && lua_interface && lua_interface->RunItemScript(master_item->GetItemScript(), "item_difficulty", master_item, player, &overrideValue))
|
||||
{
|
||||
item_difficulty = (sint8)overrideValue;
|
||||
printf("Override difficulty: %i\n",item_difficulty);
|
||||
}
|
||||
|
||||
item_difficulty -= 6;
|
||||
if (item_difficulty < 0)
|
||||
|
|
|
@ -261,6 +261,9 @@ void ZoneServer::Init()
|
|||
spawn_update.Start(rule_manager.GetGlobalRule(R_Zone, SpawnUpdateTimer)->GetInt16());
|
||||
LogWrite(ZONE__DEBUG, 0, "Zone", "SpawnUpdateTimer: %ims", rule_manager.GetGlobalRule(R_Zone, SpawnUpdateTimer)->GetInt16());
|
||||
|
||||
queue_updates.Start(rule_manager.GetGlobalRule(R_Zone, SpawnUpdateTimer)->GetInt16());
|
||||
LogWrite(ZONE__DEBUG, 0, "Zone", "QueueUpdateTimer(inherits SpawnUpdateTimer): %ims", rule_manager.GetGlobalRule(R_Zone, SpawnUpdateTimer)->GetInt16());
|
||||
|
||||
spawn_delete_timer = rule_manager.GetGlobalRule(R_Zone, SpawnDeleteTimer)->GetInt32();
|
||||
LogWrite(ZONE__DEBUG, 0, "Zone", "SpawnDeleteTimer: %ums", spawn_delete_timer);
|
||||
|
||||
|
@ -1596,6 +1599,8 @@ bool ZoneServer::SpawnProcess(){
|
|||
movementMgr->Process();
|
||||
MSpawnList.releasereadlock(__FUNCTION__, __LINE__);
|
||||
|
||||
if(queue_updates.Check())
|
||||
ProcessQueuedStateCommands();
|
||||
// Do other loops for spawns
|
||||
// tracking, client loop with spawn loop for each client that is tracking, change to a spawn_range_map loop instead of using the main spawn list?
|
||||
//if (tracking_timer.Check())
|
||||
|
@ -5459,15 +5464,7 @@ void ZoneServer::SendUpdateDefaultCommand(Spawn* spawn, const char* command, flo
|
|||
return;
|
||||
}
|
||||
|
||||
Client* client = 0;
|
||||
PacketStruct* packet = 0;
|
||||
vector<Client*>::iterator client_itr;
|
||||
|
||||
MClientList.readlock(__FUNCTION__, __LINE__);
|
||||
for (client_itr = clients.begin(); client_itr != clients.end(); client_itr++) {
|
||||
client = *client_itr;
|
||||
client->SendDefaultCommand(spawn, command, distance);
|
||||
}
|
||||
QueueDefaultCommand(spawn->GetID(), std::string(command), distance);
|
||||
|
||||
if (strlen(command)>0)
|
||||
spawn->SetPrimaryCommand(command, command, distance);
|
||||
|
@ -7897,3 +7894,75 @@ void ZoneServer::AddSpawnToGroup(Spawn* spawn, int32 group_id)
|
|||
groupList->Add(spawn->GetID());
|
||||
spawn->SetSpawnGroupID(group_id);
|
||||
}
|
||||
|
||||
void ZoneServer::QueueStateCommandToClients(int32 spawn_id, int32 state)
|
||||
{
|
||||
if(spawn_id < 1)
|
||||
return;
|
||||
|
||||
MLuaQueueStateCmd.lock();
|
||||
lua_queued_state_commands.insert(make_pair(spawn_id, state));
|
||||
MLuaQueueStateCmd.unlock();
|
||||
}
|
||||
|
||||
void ZoneServer::QueueDefaultCommand(int32 spawn_id, std::string command, float distance)
|
||||
{
|
||||
if(spawn_id < 1)
|
||||
return;
|
||||
|
||||
MLuaQueueStateCmd.lock();
|
||||
lua_spawn_update_command[spawn_id].insert(make_pair(command,distance));
|
||||
MLuaQueueStateCmd.unlock();
|
||||
}
|
||||
|
||||
void ZoneServer::ProcessQueuedStateCommands() // in a client list lock only
|
||||
{
|
||||
vector<Client*>::iterator itr;
|
||||
|
||||
MLuaQueueStateCmd.lock();
|
||||
|
||||
if(lua_queued_state_commands.size() > 0)
|
||||
{
|
||||
std::map<int32, int32>::iterator statecmds;
|
||||
for(statecmds = lua_queued_state_commands.begin(); statecmds != lua_queued_state_commands.end(); statecmds++)
|
||||
{
|
||||
Spawn* spawn = GetSpawnByID(statecmds->first, false);
|
||||
if(!spawn)
|
||||
continue;
|
||||
|
||||
MClientList.readlock(__FUNCTION__, __LINE__);
|
||||
for (itr = clients.begin(); itr != clients.end(); itr++) {
|
||||
Client* client = *itr;
|
||||
if (client && client->GetPlayer()->WasSentSpawn(spawn->GetID()) && !client->GetPlayer()->WasSpawnRemoved(spawn))
|
||||
ClientPacketFunctions::SendStateCommand(client, client->GetPlayer()->GetIDWithPlayerSpawn(spawn), statecmds->second);
|
||||
}
|
||||
MClientList.releasereadlock(__FUNCTION__, __LINE__);
|
||||
}
|
||||
lua_queued_state_commands.clear();
|
||||
}
|
||||
|
||||
if(lua_spawn_update_command.size() > 0)
|
||||
{
|
||||
std::map<int32, std::map<std::string,float>>::iterator updatecmds;
|
||||
for(updatecmds = lua_spawn_update_command.begin(); updatecmds != lua_spawn_update_command.end(); updatecmds++)
|
||||
{
|
||||
Spawn* spawn = GetSpawnByID(updatecmds->first, false);
|
||||
if(!spawn)
|
||||
continue;
|
||||
|
||||
std::map<std::string,float>::iterator innermap;
|
||||
for(innermap = lua_spawn_update_command[updatecmds->first].begin(); innermap != lua_spawn_update_command[updatecmds->first].end(); innermap++)
|
||||
{
|
||||
MClientList.readlock(__FUNCTION__, __LINE__);
|
||||
for (itr = clients.begin(); itr != clients.end(); itr++) {
|
||||
Client* client = *itr;
|
||||
if (client && client->GetPlayer()->WasSentSpawn(spawn->GetID()) && !client->GetPlayer()->WasSpawnRemoved(spawn))
|
||||
client->SendDefaultCommand(spawn, innermap->first.c_str(), innermap->second);
|
||||
}
|
||||
MClientList.releasereadlock(__FUNCTION__, __LINE__);
|
||||
}
|
||||
}
|
||||
lua_spawn_update_command.clear();
|
||||
}
|
||||
MLuaQueueStateCmd.unlock();
|
||||
}
|
|
@ -669,6 +669,10 @@ public:
|
|||
bool SendRemoveSpawn(Client* client, Spawn* spawn, PacketStruct* packet = 0, bool delete_spawn = false);
|
||||
|
||||
void AddSpawnToGroup(Spawn* spawn, int32 group_id);
|
||||
|
||||
void QueueStateCommandToClients(int32 spawn_id, int32 state);
|
||||
void QueueDefaultCommand(int32 spawn_id, std::string command, float distance);
|
||||
void ProcessQueuedStateCommands();
|
||||
private:
|
||||
#ifndef WIN32
|
||||
pthread_t ZoneThread;
|
||||
|
@ -843,6 +847,7 @@ private:
|
|||
Timer tracking_timer;
|
||||
Timer weatherTimer;
|
||||
Timer widget_timer;
|
||||
Timer queue_updates;
|
||||
|
||||
/* Enums */
|
||||
Instance_Type InstanceType;
|
||||
|
@ -947,6 +952,10 @@ private:
|
|||
|
||||
vector<int32> m_pendingSpawnRemove;
|
||||
Mutex MPendingSpawnRemoval;
|
||||
|
||||
std::map<int32, int32> lua_queued_state_commands;
|
||||
std::map<int32, std::map<std::string, float>> lua_spawn_update_command;
|
||||
std::mutex MLuaQueueStateCmd;
|
||||
public:
|
||||
Spawn* GetSpawn(int32 id);
|
||||
|
||||
|
|
Loading…
Reference in a new issue