From 65109b0d5d7863c31666b3c329f10c035397663e Mon Sep 17 00:00:00 2001 From: LethalEncounter Date: Wed, 21 Oct 2020 19:37:26 -0400 Subject: [PATCH] Glowpaths/waypoints added Fixed spell error messages for DoF client Fixed food/drinks for DoF client Changed items to allow archetypes to use an item if all the classes in the archetype are allowed to use it Added a ton of LUA functions to support various gameplay features Loot icon only added to corpse if the corpse has loot Class skills will now rise appropriately with level Fixed various bugs --- DB/updates/spells_update_sep_27_2020.sql | 1 + EQ2/source/WorldServer/Commands/Commands.cpp | 123 +++++-- EQ2/source/WorldServer/Entity.cpp | 7 +- EQ2/source/WorldServer/Items/Items.cpp | 137 +++++++- EQ2/source/WorldServer/Items/Items.h | 6 + EQ2/source/WorldServer/LuaFunctions.cpp | 329 +++++++++++++------ EQ2/source/WorldServer/LuaFunctions.h | 13 + EQ2/source/WorldServer/LuaInterface.cpp | 17 + EQ2/source/WorldServer/LuaInterface.h | 1 + EQ2/source/WorldServer/NPC.cpp | 6 +- EQ2/source/WorldServer/Player.cpp | 94 ++++-- EQ2/source/WorldServer/Player.h | 8 +- EQ2/source/WorldServer/Quests.cpp | 11 +- EQ2/source/WorldServer/Quests.h | 3 + EQ2/source/WorldServer/Skills.cpp | 35 +- EQ2/source/WorldServer/Skills.h | 30 +- EQ2/source/WorldServer/Spawn.cpp | 13 +- EQ2/source/WorldServer/Spawn.h | 10 +- EQ2/source/WorldServer/SpellProcess.cpp | 53 ++- EQ2/source/WorldServer/SpellProcess.h | 2 +- EQ2/source/WorldServer/Spells.cpp | 24 +- EQ2/source/WorldServer/Spells.h | 5 +- EQ2/source/WorldServer/World.cpp | 72 +++- EQ2/source/WorldServer/World.h | 16 +- EQ2/source/WorldServer/WorldDatabase.cpp | 15 +- EQ2/source/WorldServer/client.cpp | 197 +++++++++-- EQ2/source/WorldServer/client.h | 20 +- EQ2/source/WorldServer/zoneserver.cpp | 23 +- EQ2/source/WorldServer/zoneserver.h | 2 +- EQ2/source/common/ConfigReader.cpp | 4 +- EQ2/source/common/PacketStruct.cpp | 19 ++ EQ2/source/common/PacketStruct.h | 13 + EQ2/source/common/misc.cpp | 7 + EQ2/source/common/misc.h | 1 + server/SpawnStructs.xml | 2 +- server/WorldStructs.xml | 121 +++++-- 36 files changed, 1164 insertions(+), 276 deletions(-) create mode 100644 DB/updates/spells_update_sep_27_2020.sql diff --git a/DB/updates/spells_update_sep_27_2020.sql b/DB/updates/spells_update_sep_27_2020.sql new file mode 100644 index 000000000..395aa0f2a --- /dev/null +++ b/DB/updates/spells_update_sep_27_2020.sql @@ -0,0 +1 @@ +ALTER TABLE `spells` ADD COLUMN `fade_message_others` VARCHAR(255) NOT NULL DEFAULT '' AFTER `fade_message`; \ No newline at end of file diff --git a/EQ2/source/WorldServer/Commands/Commands.cpp b/EQ2/source/WorldServer/Commands/Commands.cpp index 4c0da090f..61d81996a 100644 --- a/EQ2/source/WorldServer/Commands/Commands.cpp +++ b/EQ2/source/WorldServer/Commands/Commands.cpp @@ -1135,15 +1135,19 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie } case COMMAND_RELOADSTRUCTS: { client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Reloading Structs..."); + world.SetReloadingSubsystem("Structs"); configReader.ReloadStructs(); + world.RemoveReloadingSubSystem("Structs"); client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Done!"); break; } case COMMAND_RELOAD_QUESTS: { client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Reloading Quests..."); + world.SetReloadingSubsystem("Quests"); master_quest_list.Reload(); client_list.ReloadQuests(); zone_list.ReloadClientQuests(); + world.RemoveReloadingSubSystem("Quests"); client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Done!"); break; } @@ -1153,42 +1157,52 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie } case COMMAND_RELOAD_SPELLS: { client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Reloading Spells..."); + world.SetReloadingSubsystem("Spells"); zone_list.DeleteSpellProcess(); master_spell_list.Reload(); if (lua_interface) lua_interface->ReloadSpells(); zone_list.LoadSpellProcess(); - client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Done!"); + world.RemoveReloadingSubSystem("Spells"); + client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Done!"); break; } case COMMAND_RELOAD_GROUNDSPAWNS: { client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Reloading Groundspawn Entries..."); + world.SetReloadingSubsystem("GroundSpawns"); client->GetCurrentZone()->DeleteGroundSpawnItems(); client->GetCurrentZone()->LoadGroundSpawnEntries(); + world.RemoveReloadingSubSystem("GroundSpawns"); client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Done!"); break; } case COMMAND_RELOAD_ZONESCRIPTS: { client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Reloading Zone Scripts..."); + world.SetReloadingSubsystem("ZoneScripts"); world.ResetZoneScripts(); database.LoadZoneScriptData(); if (lua_interface) lua_interface->DestroyZoneScripts(); + world.RemoveReloadingSubSystem("ZoneScripts"); client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Done!"); break; } case COMMAND_RELOAD_ENTITYCOMMANDS: { client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Reloading Entity Commands..."); + world.SetReloadingSubsystem("EntityCommands"); client->GetCurrentZone()->ClearEntityCommands(); database.LoadEntityCommands(client->GetCurrentZone()); + world.RemoveReloadingSubSystem("EntityCommands"); client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Done!"); break; } case COMMAND_RELOAD_FACTIONS: { client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Reloading Factions..."); + world.SetReloadingSubsystem("Factions"); master_faction_list.Clear(); database.LoadFactionList(); + world.RemoveReloadingSubSystem("Factions"); client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Done!"); break; } @@ -1354,8 +1368,13 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie } else if (strcmp(sep->arg[0], "spellbook") == 0) { sint32 spell_id = atol(sep->arg[1]); - int8 tier = atoi(sep->arg[2]); - EQ2Packet* outapp = master_spell_list.GetSpellPacket(spell_id, tier, client, true, 0x2A); + int32 tier = atoi(sep->arg[2]); + if (tier > 255) { + SpellBookEntry* ent = client->GetPlayer()->GetSpellBookSpell(spell_id); + if (ent) + tier = ent->tier; + } + EQ2Packet* outapp = master_spell_list.GetSpellPacket(spell_id, (int8)tier, client, true, 0x2A); if (outapp) client->QueuePacket(outapp); else @@ -1386,7 +1405,7 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie LogWrite(COMMAND__DEBUG, 5, "Command", "Unknown Spell ID: %u", spell_id); int8 tier = client->GetPlayer()->GetSpellTier(spell_id); int8 type = 0; - if (client->GetVersion() <= 283) + if (client->GetVersion() <= 546) type = 1; EQ2Packet* outapp = master_spell_list.GetSpecialSpellPacket(spell_id, tier, client, true, type); if (outapp){ @@ -1910,8 +1929,6 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie new_level = 255; client->ChangeLevel(client->GetPlayer()->GetLevel(), new_level); - client->GetPlayer()->SetXP(1); - client->GetPlayer()->SetNeededXP(); } }else client->SimpleMessage(CHANNEL_COLOR_YELLOW,"Usage: /level {new_level}"); @@ -7746,6 +7763,12 @@ void Commands::Command_Toggle_AutoConsume(Client* client, Seperator* sep) { int8 slot = atoi(sep->arg[0]); int8 flag = atoi(sep->arg[1]); + if (client->GetVersion() <= 283) { + slot += 4; + } + else if (client->GetVersion() <= 546) { + slot += 2; + } if (slot == EQ2_FOOD_SLOT) { player->toggle_character_flag(CF_FOOD_AUTO_CONSUME); @@ -8450,8 +8473,8 @@ void Commands::Command_Test(Client* client, EQ2_16BitString* command_parms) { } } else if (atoi(sep->arg[0]) == 5) { - int16 offset = atoi(sep->arg[0]); - int32 value1 = atol(sep->arg[1]); + int16 offset = atoi(sep->arg[1]); + int32 value1 = atol(sep->arg[2]); EQ2Packet* outapp = client->GetPlayer()->GetPlayerInfo()->serialize(client->GetVersion(), offset, value1); client->QueuePacket(outapp); } @@ -8543,21 +8566,39 @@ void Commands::Command_Test(Client* client, EQ2_16BitString* command_parms) { client->QueuePacket(ret); } } - else if (atoi(sep->arg[0]) == 9) { - Spawn* spawn = client->GetPlayer()->GetTarget(); - if (spawn) { - if (sep->IsSet(3)) { - int32 amount = (int32)atoi(sep->arg[3]); - spawn->SetActivityStatus(amount); - } + else if (atoi(sep->arg[0]) == 9) { + PacketStruct* packet2 = configReader.getStruct("WS_DeathWindow", client->GetVersion()); + if (packet2) { + packet2->setArrayLengthByName("location_count", 1); + packet2->setArrayDataByName("location_ida", 1234); + packet2->setArrayDataByName("unknown2a", 3); + packet2->setArrayDataByName("zone_name", "Queen's Colony"); + packet2->setArrayDataByName("location_name", "Myrrin's Tower"); + packet2->setArrayDataByName("distance", 134); + EQ2Packet* app = packet2->serialize(); if (sep->IsSet(2)) { - int8 amount = (int8)atoi(sep->arg[2]); - spawn->SetLockedNoLoot(amount); - } - if (sep->IsSet(1)) { - sint8 amount = (sint8)atoi(sep->arg[1]); - spawn->AddIconValue(amount); + int8 offset = atoi(sep->arg[1]); + uchar* ptr2 = app->pBuffer; + ptr2 += offset; + if (sep->IsNumber(2)) { + int32 value1 = atol(sep->arg[2]); + if (value1 > 0xFFFF) + memcpy(ptr2, (uchar*)&value1, 4); + else if (value1 > 0xFF) + memcpy(ptr2, (uchar*)&value1, 2); + else + memcpy(ptr2, (uchar*)&value1, 1); + } + else { + int8 len = strlen(sep->arg[2]); + memcpy(ptr2, (uchar*)&len, 1); + ptr2 += 1; + memcpy(ptr2, sep->arg[2], len); + } } + DumpPacket(app); + client->QueuePacket(app); + safe_delete(packet2); } } else if (atoi(sep->arg[0]) == 10) { @@ -8609,34 +8650,44 @@ void Commands::Command_Test(Client* client, EQ2_16BitString* command_parms) { else if (atoi(sep->arg[0]) == 11) { PacketStruct* packet2 = configReader.getStruct("WS_QuestJournalReply", client->GetVersion()); if (packet2) { - packet2->setDataByName("quest_id", 5); + packet2->setDataByName("quest_id", 524); packet2->setDataByName("player_crc", 2900677088); - packet2->setDataByName("name", "Archetype Selection"); - packet2->setDataByName("description", "I have reported my profession to Garven Tralk"); + packet2->setDataByName("name", "Tasks aboard the Far Journey"); + packet2->setDataByName("description", "I completed all the tasks assigned to me by Captain Varlos aboard the Far Journey"); packet2->setDataByName("type", "Hallmark"); packet2->setDataByName("complete_header", "To complete this quest, I must do the following tasks:"); packet2->setDataByName("day", 19); packet2->setDataByName("month", 6); packet2->setDataByName("year", 20); - packet2->setDataByName("level", 2); - packet2->setDataByName("encounter_level", 4); - packet2->setDataByName("difficulty", 3); + packet2->setDataByName("level", 1); + packet2->setDataByName("encounter_level", 1); + packet2->setDataByName("difficulty", 1); packet2->setDataByName("time_obtained", Timer::GetUnixTimeStamp()); - packet2->setDataByName("timer_start", Timer::GetUnixTimeStamp()); - packet2->setDataByName("timer_duration", 300); - packet2->setDataByName("timer_running", 1); - packet2->setArrayLengthByName("task_groups_completed", 0); - packet2->setArrayLengthByName("num_task_groups", 1); - packet2->setArrayDataByName("task_group", "I need to talk to Garven Tralk"); - packet2->setSubArrayLengthByName("num_tasks", 1); + //packet2->setDataByName("timer_start", Timer::GetUnixTimeStamp()); + //packet2->setDataByName("timer_duration", 300); + //packet2->setDataByName("timer_running", 1); + packet2->setArrayLengthByName("task_groups_completed", 9); + packet2->setArrayLengthByName("num_task_groups", 10); + packet2->setArrayDataByName("task_group", "I spoke to Waulon as Captain Varlos had asked of me."); + packet2->setArrayDataByName("task_group", "I found Waulon's hat in one of the boxes.", 1); + packet2->setArrayDataByName("task_group", "I returned Waulon's hat.", 2); + packet2->setArrayDataByName("task_group", "I have spoken to Ingrid.", 3); + packet2->setArrayDataByName("task_group", "I purchased a Shard of Luclin.", 4); + packet2->setArrayDataByName("task_group", "I gave the Shard of Luclin to Ingrid.", 5); + packet2->setArrayDataByName("task_group", "I have spoken to Captain Varlos.", 6); + packet2->setArrayDataByName("task_group", "I killed the rats that Captain Varlos requested.", 7); + packet2->setArrayDataByName("task_group", "Captain Varlos has ordered you to kill the escaped goblin.", 8); + packet2->setArrayDataByName("task_group", "I killed the escaped goblin.", 9); + /*packet2->setSubArrayLengthByName("num_tasks", 1); packet2->setSubArrayDataByName("task", "I need to talk to Garven Tralk"); packet2->setSubArrayLengthByName("num_updates", 1); packet2->setSubArrayDataByName("update_currentval", 0); packet2->setSubArrayDataByName("update_maxval", 1); packet2->setSubArrayDataByName("icon", 11); - + */ packet2->setArrayDataByName("waypoint", 0xFFFFFFFF); packet2->setDataByName("journal_updated", 1); + packet2->setDataByName("bullets", 1); EQ2Packet* app = packet2->serialize(); if (sep->IsSet(2)) { int16 offset = atoi(sep->arg[1]); @@ -8666,7 +8717,7 @@ void Commands::Command_Test(Client* client, EQ2_16BitString* command_parms) { else if (atoi(sep->arg[0]) == 12) { PacketStruct* packet2 = configReader.getStruct("WS_QuestJournalReply", client->GetVersion()); if (packet2) { - packet2->setDataByName("quest_id", 5); + packet2->setDataByName("quest_id", 524); packet2->setDataByName("player_crc", 2900677088); packet2->setDataByName("name", "Archetype Selection"); packet2->setDataByName("description", "I have reported my profession to Garven Tralk."); diff --git a/EQ2/source/WorldServer/Entity.cpp b/EQ2/source/WorldServer/Entity.cpp index 501925b98..034f8f431 100644 --- a/EQ2/source/WorldServer/Entity.cpp +++ b/EQ2/source/WorldServer/Entity.cpp @@ -499,6 +499,7 @@ void Entity::DoRegenUpdate(){ if(GetPower() < GetTotalPower()){ if(regen_power_rate == 0) regen_power_rate = level + (int)(level/10) + 1; + cout << "regen_power_rate: " << regen_power_rate << endl; if((power + regen_power_rate) > GetTotalPower()) SetPower(GetTotalPower()); else @@ -1376,7 +1377,11 @@ float Entity::GetSpeed() { ret += stats[ITEM_STAT_OFFENSIVESPEED]; else if (stats.count(ITEM_STAT_SPEED) && stats.count(ITEM_STAT_MOUNTSPEED)) ret += max(stats[ITEM_STAT_SPEED], stats[ITEM_STAT_MOUNTSPEED]); - + else if (stats.count(ITEM_STAT_SPEED)) + ret += stats[ITEM_STAT_SPEED]; + else if (stats.count(ITEM_STAT_MOUNTSPEED)) + ret += stats[ITEM_STAT_MOUNTSPEED]; + ret *= speed_multiplier; return ret; } diff --git a/EQ2/source/WorldServer/Items/Items.cpp b/EQ2/source/WorldServer/Items/Items.cpp index 14385affe..a42a241de 100644 --- a/EQ2/source/WorldServer/Items/Items.cpp +++ b/EQ2/source/WorldServer/Items/Items.cpp @@ -992,12 +992,69 @@ void Item::SetItem(Item* old_item){ spell_tier = old_item->spell_tier; } +bool Item::CheckArchetypeAdvSubclass(int8 adventure_class, map* adv_class_levels) { + if (adventure_class > FIGHTER && adventure_class < ANIMALIST) { + int8 check = adventure_class % 10; + if (check == 2 || check == 5 || check == 8) { + int64 adv_classes = 0; + int16 level = 0; + for (int i = adventure_class + 1; i < adventure_class + 3; i++) { + if (adv_class_levels) { //need to match levels + if (level == 0) { + if (adv_class_levels->count(i) > 0) + level = adv_class_levels->at(i); + else + return false; + } + else{ + if (adv_class_levels->count(i) > 0 && adv_class_levels->at(i) != level) + return false; + } + } + else { + adv_classes = ((int64)2) << (i - 1); + if (!(generic_info.adventure_classes & adv_classes)) + return false; + } + } + return true; + } + } + return false; +} + +bool Item::CheckArchetypeAdvClass(int8 adventure_class, map* adv_class_levels) { + if (adventure_class == 1 || adventure_class == 11 || adventure_class == 21 || adventure_class == 31) { + //if the class is an archetype class and the subclasses have access, then allow + if (CheckArchetypeAdvSubclass(adventure_class + 1, adv_class_levels) && CheckArchetypeAdvSubclass(adventure_class + 4, adv_class_levels) && CheckArchetypeAdvSubclass(adventure_class + 7, adv_class_levels)) { + if (adv_class_levels) { + int16 level = 0; + for (int i = adventure_class + 1; i <= adventure_class + 7; i += 3) { + if (adv_class_levels->count(i+1) == 0 || adv_class_levels->count(i + 2) == 0) + return false; + if(level == 0) + level = adv_class_levels->at(i+1); + if (adv_class_levels->at(i+1) != level) //already verified the classes, just need to verify the subclasses have the same levels + return false; + } + + } + return true; + } + } + else if (CheckArchetypeAdvSubclass(adventure_class, adv_class_levels)) {//check archetype subclass + return true; + } + return false; +} + bool Item::CheckClass(int8 adventure_class, int8 tradeskill_class) { int64 adv_classes = ((int64)2) << (adventure_class - 1); int64 ts_classes = ((int64)2) << (tradeskill_class - 1); if( ((generic_info.adventure_classes & adv_classes) || generic_info.adventure_classes == 0) && ((generic_info.tradeskill_classes & ts_classes) || generic_info.tradeskill_classes == 0) ) return true; - return false; + //check arechtype classes as last resort + return CheckArchetypeAdvClass(adventure_class); } bool Item::CheckLevel(int8 adventure_class, int8 tradeskill_class, int16 level) { @@ -1720,7 +1777,7 @@ void Item::serialize(PacketStruct* packet, bool show_name, Player* player, int16 if(flags.length() > 0) packet->setSubstructDataByName("header_info", "flag_names", flags.c_str()); } - if(generic_info.adventure_classes > 0 || generic_info.tradeskill_classes > 0 || item_level_overrides.size() > 0){ + if (generic_info.adventure_classes > 0 || generic_info.tradeskill_classes > 0 || item_level_overrides.size() > 0) { //int64 classes = 0; int16 tmp_level = 0; map adv_class_levels; @@ -1736,18 +1793,18 @@ void Item::serialize(PacketStruct* packet, bool show_name, Player* player, int16 if (packet->GetVersion() >= 60000) temp += 2; - for(int32 i=0;i<=temp;i++){ + for (int32 i = 0; i <= temp; i++) { tmpVal = (int64)pow(2.0, (double)i); - if((generic_info.adventure_classes & tmpVal)){ + if ((generic_info.adventure_classes & tmpVal)) { //classes += 2 << (i - 1); classes += tmpVal; tmp_level = GetOverrideLevel(i, 255); - if(tmp_level == 0) + if (tmp_level == 0) adv_class_levels[i] = generic_info.adventure_default_level; else adv_class_levels[i] = tmp_level; } - if(tmpVal == 0) { + if (tmpVal == 0) { if (packet->GetVersion() >= 60000) classes = 576379072454289112; else if (packet->GetVersion() >= 57048) @@ -1758,27 +1815,73 @@ void Item::serialize(PacketStruct* packet, bool show_name, Player* player, int16 classes = 36024082983773912; } } - for(int i=ALCHEMIST + 1 - ARTISAN ;i>=0;i--){ + for (int i = ALCHEMIST + 1 - ARTISAN; i >= 0; i--) { //tmpVal = 2 << i; tmpVal = (int64)pow(2.0, (double)i); - if((generic_info.tradeskill_classes & tmpVal)){ - classes += pow(2, (i + ARTISAN )); + if ((generic_info.tradeskill_classes & tmpVal)) { + classes += pow(2, (i + ARTISAN)); //classes += 2 << (i+ARTISAN-1); tmp_level = GetOverrideLevel(i, 255); - if(tmp_level == 0) + if (tmp_level == 0) tradeskill_class_levels[i] = generic_info.tradeskill_default_level; else tradeskill_class_levels[i] = tmp_level; } } + bool simplified_display = false; + if (client->GetVersion() <= 546) { //simplify display (if possible) + map new_adv_class_levels; + for (int i = 1; i <= 31; i += 10) { + bool add_archetype = CheckArchetypeAdvClass(i, &adv_class_levels); + if (add_archetype) { + new_adv_class_levels[i] = 0; + } + else { + for (int x = 1; x <= 7; x += 3) { + if (CheckArchetypeAdvSubclass(i+x, &adv_class_levels)) { + new_adv_class_levels[i+x] = 0; + } + } + } + } + if (new_adv_class_levels.size() > 0) { + simplified_display = true; + int8 i = 0; + for (itr = new_adv_class_levels.begin(); itr != new_adv_class_levels.end(); itr++) { + i = itr->first; + if ((i % 10) == 1) { + int16 level = 0; + for (int x = i; x < i+10; x++) { + if (adv_class_levels.count(x) > 0) { + if(level == 0) + level = adv_class_levels.at(x); + adv_class_levels.erase(x); + } + } + adv_class_levels[i] = level; + } + else { + int16 level = 0; + for (int x = i+1; x < i + 3; x++) { + if (adv_class_levels.count(x) > 0) { + if (level == 0) + level = adv_class_levels.at(x); + adv_class_levels.erase(x); + } + } + adv_class_levels[i] = level; + } + } + } + } packet->setSubstructArrayLengthByName("header_info", "class_count", adv_class_levels.size() + tradeskill_class_levels.size()); int i = 0; - for(itr = adv_class_levels.begin(); itr != adv_class_levels.end(); itr++, i++){ + for (itr = adv_class_levels.begin(); itr != adv_class_levels.end(); itr++, i++) { packet->setArrayDataByName("adventure_class", itr->first, i); packet->setArrayDataByName("tradeskill_class", 255, i); - packet->setArrayDataByName("level", itr->second*10, i); + packet->setArrayDataByName("level", itr->second * 10, i); } - for(itr = tradeskill_class_levels.begin(); itr != tradeskill_class_levels.end(); itr++, i++){ + for (itr = tradeskill_class_levels.begin(); itr != tradeskill_class_levels.end(); itr++, i++) { packet->setArrayDataByName("adventure_class", 255, i); packet->setArrayDataByName("tradeskill_class", itr->first, i); packet->setArrayDataByName("level", itr->second, i); @@ -1838,6 +1941,14 @@ void Item::serialize(PacketStruct* packet, bool show_name, Player* player, int16 else if(slot == EQ2_DRINK_SLOT) slot = EQ2_ORIG_DRINK_SLOT; } + else if (client->GetVersion() <= 546) { + if (slot > EQ2_EARS_SLOT_1 && slot <= EQ2_WAIST_SLOT) //they added a second ear slot later, adjust for only 1 original slot + slot -= 1; + else if (slot == EQ2_FOOD_SLOT) + slot = EQ2_DOF_FOOD_SLOT; + else if (slot == EQ2_DRINK_SLOT) + slot = EQ2_DOF_DRINK_SLOT; + } packet->setArrayDataByName("slot", slot, i); } } diff --git a/EQ2/source/WorldServer/Items/Items.h b/EQ2/source/WorldServer/Items/Items.h index 1cb11fed9..8151b02eb 100644 --- a/EQ2/source/WorldServer/Items/Items.h +++ b/EQ2/source/WorldServer/Items/Items.h @@ -65,6 +65,8 @@ extern MasterItemList master_item_list; #define EQ2_BACK_SLOT 30 #define EQ2_ORIG_FOOD_SLOT 18 #define EQ2_ORIG_DRINK_SLOT 19 +#define EQ2_DOF_FOOD_SLOT 20 +#define EQ2_DOF_DRINK_SLOT 21 #define PRIMARY_SLOT 1 #define SECONDARY_SLOT 2 @@ -99,6 +101,8 @@ extern MasterItemList master_item_list; #define BACK_SLOT 1073741824 #define ORIG_FOOD_SLOT 524288 #define ORIG_DRINK_SLOT 1048576 +#define DOF_FOOD_SLOT 1048576 +#define DOF_DRINK_SLOT 2097152 #define CLASSIC_EQ_MAX_BAG_SLOTS 20 #define NUM_BANK_SLOTS 12 @@ -855,6 +859,8 @@ public: void AddLevelOverride(ItemLevelOverride* class_); bool CheckClassLevel(int8 adventure_class, int8 tradeskill_class, int16 level); bool CheckClass(int8 adventure_class, int8 tradeskill_class); + bool CheckArchetypeAdvClass(int8 adventure_class, map* adv_class_levels = 0); + bool CheckArchetypeAdvSubclass(int8 adventure_class, map* adv_class_levels = 0); bool CheckLevel(int8 adventure_class, int8 tradeskill_class, int16 level); void SetAppearance(int16 type, int8 red, int8 green, int8 blue, int8 highlight_red, int8 highlight_green, int8 highlight_blue); void SetAppearance(ItemAppearance* appearance); diff --git a/EQ2/source/WorldServer/LuaFunctions.cpp b/EQ2/source/WorldServer/LuaFunctions.cpp index c6a271e2f..c21bf04b1 100644 --- a/EQ2/source/WorldServer/LuaFunctions.cpp +++ b/EQ2/source/WorldServer/LuaFunctions.cpp @@ -337,6 +337,30 @@ int EQ2Emu_lua_ChangeHandIcon(lua_State* state) { return 0; } +//this function is used to force an update packet to be sent. +//Useful if certain calculated things change after the player is sent the spawn packet, like quest flags or player has access to an object now +int EQ2Emu_lua_SetVisualFlag(lua_State* state) { + if (!lua_interface) + return 0; + Spawn* spawn = lua_interface->GetSpawn(state); + if (spawn) { + spawn->vis_changed = true; + } + return 0; +} + +//this function is used to force an update packet to be sent. +//Useful if certain calculated things change after the player is sent the spawn packet, like quest flags or player has access to an object now +int EQ2Emu_lua_SetInfoFlag(lua_State* state) { + if (!lua_interface) + return 0; + Spawn* spawn = lua_interface->GetSpawn(state); + if (spawn) { + spawn->info_changed = true; + } + return 0; +} + int EQ2Emu_lua_SendStateCommand(lua_State* state) { if (!lua_interface) return 0; @@ -3207,6 +3231,27 @@ int EQ2Emu_lua_AddQuestSelectableRewardItem(lua_State* state) { return 0; } +int EQ2Emu_lua_HasQuestRewardItem(lua_State* state) { + if (!lua_interface) + return 0; + Quest* quest = lua_interface->GetQuest(state); + if (quest) { + int32 item_id = lua_interface->GetInt32Value(state, 2); + vector* items = quest->GetRewardItems(); + if (items) { + vector::iterator itr; + for (itr = items->begin(); itr != items->end(); itr++) { + if (*itr && (*itr)->details.item_id == item_id) { + lua_interface->SetBooleanValue(state, true); + return 1; + } + } + } + } + lua_interface->SetBooleanValue(state, false); + return 1; +} + int EQ2Emu_lua_AddQuestRewardItem(lua_State* state) { if (!lua_interface) return 0; @@ -3694,8 +3739,6 @@ int EQ2Emu_lua_GiveImmediateQuestReward(lua_State* state) { string factions_map_str = lua_interface->GetStringValue(state, 7); string text = lua_interface->GetStringValue(state, 8); int32 source_id = 0; - if (quest) - source_id = quest->GetQuestID(); if (playerSpawn && playerSpawn->IsPlayer()) { Player* player = (Player*)playerSpawn; Client* client = player->GetZone()->GetClientBySpawn(player); @@ -3710,7 +3753,7 @@ int EQ2Emu_lua_GiveImmediateQuestReward(lua_State* state) { Item* item = new Item(master_item_list.GetItem(itr->first)); if (item) { if (itr->second > 0) - item->stack_count = itr->second; + item->details.count = itr->second; reward_items.push_back(item); } } @@ -3734,93 +3777,9 @@ int EQ2Emu_lua_GiveImmediateQuestReward(lua_State* state) { const char* reward_type = "Quest Reward!"; if (!quest) reward_type = "Reward!"; - client->DisplayQuestRewards(0, coin, &reward_items, &selectable_reward_items, &faction_rewards, reward_type, status_points, text.c_str()); + client->DisplayQuestRewards(quest, coin, &reward_items, &selectable_reward_items, &faction_rewards, reward_type, status_points, text.c_str()); } } - /*PacketStruct* packet2 = configReader.getStruct("WS_QuestRewardPackMsg", client->GetVersion()); - if (packet2) { - player->AddCoins(coin); - client->PlaySound("coin_cha_ching"); - packet2->setSubstructDataByName("reward_data", "unknown1", 255); - if(quest) - packet2->setSubstructDataByName("reward_data", "reward", "Quest Reward!"); - else - packet2->setSubstructDataByName("reward_data", "reward", "Reward!"); - packet2->setSubstructDataByName("reward_data", "coin", coin); - if (player->GetGuild()) { - player->GetInfoStruct()->status_points += status_points; - packet2->setSubstructDataByName("reward_data", "status_points", status_points); - } - if (rewards_str.length() > 0) { - map rewards = ParseIntMap(rewards_str); - vector reward_items; - map::iterator itr; - for (itr = rewards.begin(); itr != rewards.end(); itr++) { - if (itr->first > 0) { - Item* item = new Item(master_item_list.GetItem(itr->first)); - if (item) { - if (itr->second > 0) - item->stack_count = itr->second; - reward_items.push_back(item); - } - } - } - packet2->setSubstructArrayLengthByName("reward_data", "num_rewards", reward_items.size()); - for (int i = 0; i < reward_items.size(); i++) { - Item* item = reward_items[i]; - packet2->setArrayDataByName("reward_id", item->details.item_id, i); - packet2->setItemArrayDataByName("item", item, client->GetPlayer(), i, 0, -1); - player->AddPendingItemReward(item); //item reference will be deleted after the player accepts it - } - } - if (select_rewards_str.length() > 0) { - map rewards = ParseIntMap(select_rewards_str); - vector reward_items; - map::iterator itr; - for (itr = rewards.begin(); itr != rewards.end(); itr++) { - if (itr->first > 0) { - Item* item = new Item(master_item_list.GetItem(itr->first)); - if (item) { - if (itr->second > 0) - item->stack_count = itr->second; - reward_items.push_back(item); - } - } - } - packet2->setSubstructArrayLengthByName("reward_data", "num_select_rewards", reward_items.size()); - for (int i = 0; i < reward_items.size(); i++) { - Item* item = reward_items[i]; - packet2->setArrayDataByName("select_reward_id", item->details.item_id, i); - packet2->setItemArrayDataByName("select_item", item, client->GetPlayer(), i, 0, -1); - player->AddPendingSelectableItemReward(source_id, item); //item reference will be deleted after the player selects one - } - } - if (factions_map_str.length() > 0) { - map faction_rewards = ParseSInt32Map(factions_map_str); - map::iterator itr; - map factions; - for (itr = faction_rewards.begin(); itr != faction_rewards.end(); itr++) { - Faction* faction = master_faction_list.GetFaction(itr->first); - if (faction) - factions[faction] = itr->second; - } - packet2->setSubstructArrayLengthByName("reward_data", "num_factions", factions.size()); - map::iterator faction_itr; - int8 i = 0; - for (faction_itr = factions.begin(); faction_itr != factions.end(); faction_itr++) { - packet2->setArrayDataByName("faction_name", faction_itr->first->name.c_str(), i); - sint32 amount = faction_itr->second; - packet2->setArrayDataByName("amount", amount, i); - if (amount > 0) - player->GetFactions()->IncreaseFaction(faction_itr->first->id, amount); - else - player->GetFactions()->DecreaseFaction(faction_itr->first->id, (amount * -1)); - i++; - } - } - client->QueuePacket(packet2->serialize()); - safe_delete(packet2); - }*/ return 0; } @@ -4099,22 +4058,41 @@ int EQ2Emu_lua_AddPrimaryEntityCommand(lua_State* state) { return 0; } +int EQ2Emu_lua_HasSpell(lua_State* state) { + if (!lua_interface) + return 0; + Spawn* player = lua_interface->GetSpawn(state); + int32 spellid = lua_interface->GetInt32Value(state, 2); + int16 tier = lua_interface->GetInt16Value(state, 3); + if (player && player->IsPlayer()) { + lua_interface->SetBooleanValue(state, ((Player*)player)->HasSpell(spellid, tier, true)); + return 1; + } + return 0; +} + int EQ2Emu_lua_AddSpellBookEntry(lua_State* state) { if (!lua_interface) return 0; Spawn* player = lua_interface->GetSpawn(state); int32 spellid = lua_interface->GetInt32Value(state, 2); int16 tier = lua_interface->GetInt16Value(state, 3); + int8 num_args = (int8)lua_interface->GetNumberOfArgs(state); + bool add_silently = lua_interface->GetBooleanValue(state, 4); + bool add_to_hotbar = true; + if (num_args > 4) { + add_to_hotbar = lua_interface->GetBooleanValue(state, 5); + } Spell* spell = master_spell_list.GetSpell(spellid, tier); if (player && spell && player->IsPlayer()) { - Client* client = player->GetZone()->GetClientBySpawn(player); + Client* client = player->GetClient(); if (client) { if (!client->GetPlayer()->HasSpell(spellid, tier - 1, true)) { Spell* spell = master_spell_list.GetSpell(spellid, tier); client->GetPlayer()->AddSpellBookEntry(spellid, 1, client->GetPlayer()->GetFreeSpellBookSlot(spell->GetSpellData()->spell_book_type), spell->GetSpellData()->spell_book_type, spell->GetSpellData()->linked_timer, true); client->GetPlayer()->UnlockSpell(spell); - client->SendSpellUpdate(spell); + client->SendSpellUpdate(spell, add_silently, add_to_hotbar); } else { @@ -4123,7 +4101,7 @@ int EQ2Emu_lua_AddSpellBookEntry(lua_State* state) { client->GetPlayer()->RemoveSpellBookEntry(spell->GetSpellID()); client->GetPlayer()->AddSpellBookEntry(spell->GetSpellID(), spell->GetSpellTier(), old_slot, spell->GetSpellData()->spell_book_type, spell->GetSpellData()->linked_timer, true); client->GetPlayer()->UnlockSpell(spell); - client->SendSpellUpdate(spell); + client->SendSpellUpdate(spell, add_silently, add_to_hotbar); } @@ -5423,6 +5401,51 @@ int EQ2Emu_lua_GetArchetypeName(lua_State* state) { return 0; } +int EQ2Emu_lua_SendWaypoints(lua_State* state) { + if (!lua_interface) + return 0; + Spawn* player = lua_interface->GetSpawn(state); + if (player && player->IsPlayer()) { + Client* client = player->GetClient(); + if (client) + client->SendWaypoints(); + } + return 0; +} + +int EQ2Emu_lua_AddWaypoint(lua_State* state) { + if (!lua_interface) + return 0; + Spawn* player = lua_interface->GetSpawn(state); + string name = lua_interface->GetStringValue(state, 2); + int32 type = lua_interface->GetInt32Value(state, 3); + if (type == 0) + type = 2; + if (name.length() > 0) { + if (player && player->IsPlayer()) { + Client* client = player->GetClient(); + if (client) + client->AddWaypoint(name, type); + } + } + return 0; +} + +int EQ2Emu_lua_RemoveWaypoint(lua_State* state) { + if (!lua_interface) + return 0; + Spawn* player = lua_interface->GetSpawn(state); + string name = lua_interface->GetStringValue(state, 2); + if (name.length() > 0) { + if (player && player->IsPlayer()) { + Client* client = player->GetClient(); + if (client) + client->RemoveWaypoint(name); + } + } + return 0; +} + int EQ2Emu_lua_AddWard(lua_State* state) { if (!lua_interface) return 0; @@ -7063,6 +7086,119 @@ int EQ2Emu_lua_SetSkillValue(lua_State* state) { return 0; } +int EQ2Emu_lua_HasSkill(lua_State* state) { + if (!lua_interface) + return 0; + Spawn* player = lua_interface->GetSpawn(state); + int32 skill_id = lua_interface->GetInt32Value(state, 2); + if (skill_id > 0 && player && player->IsPlayer()) { + lua_interface->SetBooleanValue(state, ((Player*)player)->skill_list.HasSkill(skill_id)); + return 1; + } + return 0; +} + +int EQ2Emu_lua_AddSkill(lua_State* state) { + if (!lua_interface) + return 0; + Spawn* player_spawn = lua_interface->GetSpawn(state); + int32 skill_id = lua_interface->GetInt32Value(state, 2); + int16 current_val = lua_interface->GetInt16Value(state, 3); + int16 max_val = lua_interface->GetInt16Value(state, 4); + bool more_to_add = lua_interface->GetBooleanValue(state, 5); + if (skill_id > 0 && current_val > 0 && max_val > 0) { + if (player_spawn && player_spawn->IsPlayer()) { + Player* player = (Player*)player_spawn; + bool added = false; + if (!player->skill_list.HasSkill(skill_id)) { + player->AddSkill(skill_id, current_val, max_val, true); + added = true; + } + if (!more_to_add) { //need to send update regardless, even if THIS skill wasn't added, otherwise if you have a list and the last item wasn't added but the previous ones were, it wouldn't send the update + Client* client = player->GetClient(); + if (client) { + EQ2Packet* packet = player->GetSkills()->GetSkillPacket(client->GetVersion()); + if (packet) + client->QueuePacket(packet); + } + } + if (added) { + lua_interface->SetBooleanValue(state, true); + return 1; + } + } + else { + lua_interface->LogError("%s: LUA AddSkill command error: Given spawn is not a player", lua_interface->GetScriptName(state)); + } + } + else { + lua_interface->LogError("%s: LUA AddSkill command error: Required parameters not set", lua_interface->GetScriptName(state)); + } + lua_interface->SetBooleanValue(state, false); + return 1; +} + +int EQ2Emu_lua_RemoveSkill(lua_State* state) { + if (!lua_interface) + return 0; + Spawn* player_spawn = lua_interface->GetSpawn(state); + int32 skill_id = lua_interface->GetInt32Value(state, 2); + bool more_to_remove = lua_interface->GetBooleanValue(state, 3); + if (skill_id > 0) { + if (player_spawn && player_spawn->IsPlayer()) { + Player* player = (Player*)player_spawn; + if (player->skill_list.HasSkill(skill_id)) { + player->RemovePlayerSkill(skill_id); + if (!more_to_remove) { + Client* client = player->GetClient(); + if (client) { + EQ2Packet* packet = player->GetSkills()->GetSkillPacket(client->GetVersion()); + if (packet) + client->QueuePacket(packet); + } + } + } + } + else { + lua_interface->LogError("%s: LUA RemoveSkill command error: Given spawn is not a player", lua_interface->GetScriptName(state)); + } + } + else { + lua_interface->LogError("%s: LUA RemoveSkill command error: skill_id not set", lua_interface->GetScriptName(state)); + } + return 0; +} + +int EQ2Emu_lua_IncreaseSkillCapsByType(lua_State* state) { + if (!lua_interface) + return 0; + Spawn* player_spawn = lua_interface->GetSpawn(state); + int8 skill_type = lua_interface->GetInt8Value(state, 2); + int16 amount = lua_interface->GetInt8Value(state, 3); + bool more_to_increase = lua_interface->GetBooleanValue(state, 4); + if (amount > 0 && skill_type < 100) { + if (player_spawn && player_spawn->IsPlayer()) { + Player* player = (Player*)player_spawn; + player->skill_list.IncreaseSkillCapsByType(skill_type, amount); + if (!more_to_increase) { + Client* client = player->GetClient(); + if (client) { + EQ2Packet* packet = player->GetSkills()->GetSkillPacket(client->GetVersion()); + if (packet) + client->QueuePacket(packet); + } + } + } + else { + lua_interface->LogError("%s: LUA IncreaseSkillCapsByType command error: Given spawn is not a player", lua_interface->GetScriptName(state)); + } + } + else { + lua_interface->LogError("%s: LUA IncreaseSkillCapsByType command error: Invalid parameters", lua_interface->GetScriptName(state)); + } + return 0; +} + int EQ2Emu_lua_GetSkill(lua_State* state) { if (!lua_interface) return 0; @@ -9218,9 +9354,6 @@ int EQ2Emu_lua_SetPlayerLevel(lua_State* state) { } client->ChangeLevel(client->GetPlayer()->GetLevel(), level); - client->GetPlayer()->SetXP(1); - client->GetPlayer()->SetNeededXP(); - return 0; } @@ -9977,7 +10110,7 @@ int EQ2Emu_lua_InstructionWindow(lua_State* state) { lua_interface->LogError("LUA InstructionWindow command error: could not find client"); return 0; } - if (text.length() == 0 || task1.length() == 0 || signal.length() == 0) { + if (text.length() == 0) { lua_interface->LogError("LUA InstructionWindow required parameters not given"); return 0; } @@ -10458,16 +10591,16 @@ int EQ2Emu_lua_GetAlignment(lua_State* state) { Spawn* spawn = lua_interface->GetSpawn(state); if (!spawn) { - lua_interface->LogError("%s: LUA SetAlignment command error: spawn is not valid", lua_interface->GetScriptName(state)); + lua_interface->LogError("%s: LUA GetAlignment command error: spawn is not valid", lua_interface->GetScriptName(state)); return 0; } if (!spawn->IsEntity()) { - lua_interface->LogError("%s: LUA SetAlignment command error: spawn is not an entity", lua_interface->GetScriptName(state)); + lua_interface->LogError("%s: LUA GetAlignment command error: spawn is not an entity", lua_interface->GetScriptName(state)); return 0; } - lua_interface->SetInt32Value(state, ((Entity*)spawn)->GetAlignment()); + lua_interface->SetSInt32Value(state, ((Entity*)spawn)->GetAlignment()); return 1; } diff --git a/EQ2/source/WorldServer/LuaFunctions.h b/EQ2/source/WorldServer/LuaFunctions.h index 7506f4edc..03f72eb8f 100644 --- a/EQ2/source/WorldServer/LuaFunctions.h +++ b/EQ2/source/WorldServer/LuaFunctions.h @@ -140,6 +140,8 @@ int EQ2Emu_lua_SetRequiredQuest(lua_State* state); int EQ2Emu_lua_SetRequiredHistory(lua_State* state); int EQ2Emu_lua_Despawn(lua_State* state); int EQ2Emu_lua_ChangeHandIcon(lua_State* state); +int EQ2Emu_lua_SetVisualFlag(lua_State* state); +int EQ2Emu_lua_SetInfoFlag(lua_State* state); int EQ2Emu_lua_AddHate(lua_State* state); int EQ2Emu_lua_GetZone(lua_State* state); int EQ2Emu_lua_GetZoneName(lua_State* state); @@ -202,6 +204,7 @@ int EQ2Emu_lua_SetServerControlFlag(lua_State* state); int EQ2Emu_lua_ToggleTracking(lua_State* state); int EQ2Emu_lua_AddPrimaryEntityCommand(lua_State* state); int EQ2Emu_lua_AddSpellBookEntry(lua_State* state); +int EQ2Emu_lua_HasSpell(lua_State* state); int EQ2Emu_lua_Attack(lua_State* state); int EQ2Emu_lua_ApplySpellVisual(lua_State* state); int EQ2Emu_lua_Interrupt(lua_State* state); @@ -232,6 +235,7 @@ int EQ2Emu_lua_AddQuestPrereqRace(lua_State* state); int EQ2Emu_lua_AddQuestPrereqModelType(lua_State* state); int EQ2Emu_lua_AddQuestPrereqTradeskillLevel(lua_State* state); int EQ2Emu_lua_AddQuestPrereqTradeskillClass(lua_State* state); +int EQ2Emu_lua_HasQuestRewardItem(lua_State* state); int EQ2Emu_lua_AddQuestRewardItem(lua_State* state); int EQ2Emu_lua_AddQuestSelectableRewardItem(lua_State* state); int EQ2Emu_lua_AddQuestRewardCoin(lua_State* state); @@ -311,6 +315,11 @@ int EQ2Emu_lua_GetTempVariable(lua_State* state); int EQ2Emu_lua_GiveQuestItem(lua_State*state); int EQ2Emu_lua_SetQuestRepeatable(lua_State* state); + +int EQ2Emu_lua_AddWaypoint(lua_State* state); +int EQ2Emu_lua_RemoveWaypoint(lua_State* state); +int EQ2Emu_lua_SendWaypoints(lua_State* state); + int EQ2Emu_lua_AddWard(lua_State* state); int EQ2Emu_lua_AddToWard(lua_State* state); int EQ2Emu_lua_RemoveWard(lua_State* state); @@ -355,6 +364,10 @@ int EQ2Emu_lua_SetSkillMaxValue(lua_State* state); int EQ2Emu_lua_SetSkillValue(lua_State* state); int EQ2Emu_lua_GetSkill(lua_State* state); int EQ2Emu_lua_GetSkillIDByName(lua_State* state); +int EQ2Emu_lua_HasSkill(lua_State* state); +int EQ2Emu_lua_AddSkill(lua_State* state); +int EQ2Emu_lua_RemoveSkill(lua_State* state); +int EQ2Emu_lua_IncreaseSkillCapsByType(lua_State* state); int EQ2Emu_lua_AddProc(lua_State* state); int EQ2Emu_lua_RemoveProc(lua_State* state); diff --git a/EQ2/source/WorldServer/LuaInterface.cpp b/EQ2/source/WorldServer/LuaInterface.cpp index d71e8d04b..c66244c8f 100644 --- a/EQ2/source/WorldServer/LuaInterface.cpp +++ b/EQ2/source/WorldServer/LuaInterface.cpp @@ -123,6 +123,10 @@ LuaInterface::~LuaInterface() { safe_delete(spell_delete_timer); } +int LuaInterface::GetNumberOfArgs(lua_State* state) { + return lua_gettop(state); +} + void LuaInterface::Process() { if(shutting_down) return; @@ -753,6 +757,7 @@ void LuaInterface::RegisterFunctions(lua_State* state) { lua_register(state, "Attack", EQ2Emu_lua_Attack); lua_register(state, "ApplySpellVisual", EQ2Emu_lua_ApplySpellVisual); + lua_register(state, "IsPlayer", EQ2Emu_lua_IsPlayer); lua_register(state, "FaceTarget", EQ2Emu_lua_FaceTarget); lua_register(state, "MoveToLocation", EQ2Emu_lua_MoveToLocation); @@ -831,6 +836,8 @@ void LuaInterface::RegisterFunctions(lua_State* state) { lua_register(state, "KillSpawnByDistance", EQ2Emu_lua_KillSpawnByDistance); lua_register(state, "Despawn", EQ2Emu_lua_Despawn); lua_register(state, "ChangeHandIcon", EQ2Emu_lua_ChangeHandIcon); + lua_register(state, "SetVisualFlag", EQ2Emu_lua_SetVisualFlag); + lua_register(state, "SetInfoFlag", EQ2Emu_lua_SetInfoFlag); lua_register(state, "IsBindAllowed", EQ2Emu_lua_IsBindAllowed); lua_register(state, "IsGateAllowed", EQ2Emu_lua_IsGateAllowed); lua_register(state, "Bind", EQ2Emu_lua_Bind); @@ -841,6 +848,7 @@ void LuaInterface::RegisterFunctions(lua_State* state) { lua_register(state, "ToggleTracking", EQ2Emu_lua_ToggleTracking); lua_register(state, "AddPrimaryEntityCommand", EQ2Emu_lua_AddPrimaryEntityCommand); lua_register(state, "AddSpellBookEntry", EQ2Emu_lua_AddSpellBookEntry); + lua_register(state, "HasSpell", EQ2Emu_lua_HasSpell); lua_register(state, "Interrupt", EQ2Emu_lua_Interrupt); lua_register(state, "Stealth", EQ2Emu_lua_Stealth); lua_register(state, "IsInvis", EQ2Emu_lua_IsInvis); @@ -868,6 +876,7 @@ void LuaInterface::RegisterFunctions(lua_State* state) { lua_register(state, "AddQuestPrereqTradeskillLevel", EQ2Emu_lua_AddQuestPrereqTradeskillLevel); lua_register(state, "AddQuestPrereqTradeskillClass", EQ2Emu_lua_AddQuestPrereqTradeskillClass); lua_register(state, "AddQuestSelectableRewardItem", EQ2Emu_lua_AddQuestSelectableRewardItem); + lua_register(state, "HasQuestRewardItem", EQ2Emu_lua_HasQuestRewardItem); lua_register(state, "AddQuestRewardItem", EQ2Emu_lua_AddQuestRewardItem); lua_register(state, "AddQuestRewardCoin", EQ2Emu_lua_AddQuestRewardCoin); lua_register(state, "AddQuestRewardFaction", EQ2Emu_lua_AddQuestRewardFaction); @@ -943,6 +952,10 @@ void LuaInterface::RegisterFunctions(lua_State* state) { lua_register(state, "GiveQuestItem", EQ2Emu_lua_GiveQuestItem); lua_register(state, "SetQuestRepeatable", EQ2Emu_lua_SetQuestRepeatable); + lua_register(state, "AddWaypoint", EQ2Emu_lua_AddWaypoint); + lua_register(state, "RemoveWaypoint", EQ2Emu_lua_RemoveWaypoint); + lua_register(state, "SendWaypoints", EQ2Emu_lua_SendWaypoints); + lua_register(state, "AddWard", EQ2Emu_lua_AddWard); lua_register(state, "AddToWard", EQ2Emu_lua_AddToWard); lua_register(state, "RemoveWard", EQ2Emu_lua_RemoveWard); @@ -985,6 +998,10 @@ void LuaInterface::RegisterFunctions(lua_State* state) { lua_register(state, "SetSkillValue", EQ2Emu_lua_SetSkillValue); lua_register(state, "GetSkill", EQ2Emu_lua_GetSkill); lua_register(state, "GetSkillIDByName", EQ2Emu_lua_GetSkillIDByName); + lua_register(state, "HasSkill", EQ2Emu_lua_HasSkill); + lua_register(state, "AddSkill", EQ2Emu_lua_AddSkill); + lua_register(state, "IncreaseSkillCapsByType", EQ2Emu_lua_IncreaseSkillCapsByType); + lua_register(state, "RemoveSkill", EQ2Emu_lua_RemoveSkill); lua_register(state, "AddProc", EQ2Emu_lua_AddProc); lua_register(state, "RemoveProc", EQ2Emu_lua_RemoveProc); lua_register(state, "Knockback", EQ2Emu_lua_Knockback); diff --git a/EQ2/source/WorldServer/LuaInterface.h b/EQ2/source/WorldServer/LuaInterface.h index 5f6460aa6..fc3d015b4 100644 --- a/EQ2/source/WorldServer/LuaInterface.h +++ b/EQ2/source/WorldServer/LuaInterface.h @@ -179,6 +179,7 @@ class LuaInterface { public: LuaInterface(); ~LuaInterface(); + int GetNumberOfArgs(lua_State* state); bool LoadLuaSpell(const char* name); bool LoadLuaSpell(string name); bool LoadItemScript(string name); diff --git a/EQ2/source/WorldServer/NPC.cpp b/EQ2/source/WorldServer/NPC.cpp index c8e107875..9a1d9f75b 100644 --- a/EQ2/source/WorldServer/NPC.cpp +++ b/EQ2/source/WorldServer/NPC.cpp @@ -228,14 +228,14 @@ void NPC::InCombat(bool val){ in_combat = val; if(val){ LogWrite(NPC__DEBUG, 3, "NPC", "'%s' engaged in combat with '%s'", this->GetName(), ( GetTarget() ) ? GetTarget()->GetName() : "Unknown" ); - SetLockedNoLoot(3); + SetLockedNoLoot(ENCOUNTER_STATE_LOCKED); AddIconValue(64); // In combat so lets set the NPC's speed to its max speed if (GetMaxSpeed() > 0) SetSpeed(GetMaxSpeed()); } else{ - SetLockedNoLoot(1); + SetLockedNoLoot(ENCOUNTER_STATE_AVAILABLE); RemoveIconValue(64); if (GetHP() > 0){ SetTempActionState(-1); //re-enable action states on exiting combat @@ -258,7 +258,7 @@ void NPC::InCombat(bool val){ } bool NPC::HandleUse(Client* client, string type){ - if(!client || type.length() == 0 || appearance.show_command_icon == 0) + if(!client || type.length() == 0 || (appearance.show_command_icon == 0 && appearance.display_hand_icon == 0)) return false; EntityCommand* entity_command = FindEntityCommand(type); if (entity_command) { diff --git a/EQ2/source/WorldServer/Player.cpp b/EQ2/source/WorldServer/Player.cpp index cba6bf855..6972e5216 100644 --- a/EQ2/source/WorldServer/Player.cpp +++ b/EQ2/source/WorldServer/Player.cpp @@ -1330,6 +1330,8 @@ EQ2Packet* PlayerInfo::serialize(int16 version, int16 modifyPos, int32 modifyVal packet->setSubstructDataByName("spell_effects", "expire_timestamp", expireTimestamp, i, 0); packet->setSubstructDataByName("spell_effects", "icon", info_struct->spell_effects[i].icon, i, 0); packet->setSubstructDataByName("spell_effects", "icon_type", info_struct->spell_effects[i].icon_backdrop, i, 0); + if(info_struct->spell_effects[i].spell && info_struct->spell_effects[i].spell->spell && info_struct->spell_effects[i].spell->spell->GetSpellData()->friendly_spell == 1) + packet->setSubstructDataByName("spell_effects", "cancellable", 1, i); } player->GetMaintainedMutex()->releasereadlock(__FUNCTION__, __LINE__); player->GetSpellEffectMutex()->releasereadlock(__FUNCTION__, __LINE__); @@ -1682,6 +1684,14 @@ int8 Player::ConvertSlotToClient(int8 slot, int16 version) { else if (slot > EQ2_EARS_SLOT_1 && slot <= EQ2_WAIST_SLOT) slot -= 1; } + else if (version <= 546) { + if (slot == EQ2_FOOD_SLOT) + slot = EQ2_DOF_FOOD_SLOT; + else if (slot == EQ2_DRINK_SLOT) + slot = EQ2_DOF_DRINK_SLOT; + else if (slot > EQ2_EARS_SLOT_1 && slot <= EQ2_WAIST_SLOT) + slot -= 1; + } return slot; } @@ -1694,6 +1704,14 @@ int8 Player::ConvertSlotFromClient(int8 slot, int16 version) { else if (slot > EQ2_EARS_SLOT_1 && slot <= EQ2_WAIST_SLOT) slot += 1; } + else if (version <= 546) { + if (slot == EQ2_DOF_FOOD_SLOT) + slot = EQ2_FOOD_SLOT; + else if (slot == EQ2_DOF_DRINK_SLOT) + slot = EQ2_DRINK_SLOT; + else if (slot > EQ2_EARS_SLOT_1 && slot <= EQ2_WAIST_SLOT) + slot += 1; + } return slot; } @@ -2197,7 +2215,7 @@ EQ2Packet* Player::GetQuickbarPacket(int16 version){ void Player::AddSpellBookEntry(int32 spell_id, int8 tier, sint32 slot, int32 type, int32 timer, bool save_needed){ SpellBookEntry* spell = new SpellBookEntry; - spell->status = 161; + spell->status = 169; spell->slot = slot; spell->spell_id = spell_id; spell->type = type; @@ -2442,13 +2460,21 @@ int8 Player::GetSpellSlot(int32 spell_id){ void Player::AddSkill(int32 skill_id, int16 current_val, int16 max_val, bool save_needed){ Skill* master_skill = master_skill_list.GetSkill(skill_id); - Skill* skill = new Skill(master_skill); - skill->current_val = current_val; - skill->previous_val = current_val; - skill->max_val = max_val; - if(save_needed) - skill->save_needed = true; - skill_list.AddSkill(skill); + if (master_skill) { + Skill* skill = new Skill(master_skill); + skill->current_val = current_val; + skill->previous_val = current_val; + skill->max_val = max_val; + if (save_needed) + skill->save_needed = true; + skill_list.AddSkill(skill); + } +} + +void Player::RemovePlayerSkill(int32 skill_id, bool save) { + Skill* skill = skill_list.GetSkill(skill_id); + if (skill) + RemoveSkillFromDB(skill, save); } void Player::RemoveSkillFromDB(Skill* skill, bool save) { @@ -2501,18 +2527,20 @@ void Player::LockAllSpells() { MSpellsBook.writelock(__FUNCTION__, __LINE__); for (itr = spells.begin(); itr != spells.end(); itr++) { if ((*itr)->type != SPELL_BOOK_TYPE_TRADESKILL) - AddSpellStatus((*itr), SPELL_STATUS_LOCK, false); + RemoveSpellStatus((*itr), SPELL_STATUS_LOCK, false); } MSpellsBook.releasewritelock(__FUNCTION__, __LINE__); } -void Player::UnlockAllSpells(bool modify_recast) { +void Player::UnlockAllSpells(bool modify_recast, Spell* exception) { vector::iterator itr; - + int32 exception_spell_id = 0; + if (exception) + exception_spell_id = exception->GetSpellID(); MSpellsBook.writelock(__FUNCTION__, __LINE__); for (itr = spells.begin(); itr != spells.end(); itr++) { - if ((*itr)->type != SPELL_BOOK_TYPE_TRADESKILL) + if ((*itr)->spell_id != exception_spell_id && (*itr)->type != SPELL_BOOK_TYPE_TRADESKILL) AddSpellStatus((*itr), SPELL_STATUS_LOCK, modify_recast); } @@ -2532,8 +2560,10 @@ void Player::LockSpell(Spell* spell, int16 recast) { } void Player::UnlockSpell(Spell* spell) { + if (spell->GetStayLocked()) + return; vector::iterator itr; - SpellBookEntry* spell2; + SpellBookEntry* spell2; MSpellsBook.writelock(__FUNCTION__, __LINE__); for (itr = spells.begin(); itr != spells.end(); itr++) { spell2 = *itr; @@ -2612,24 +2642,27 @@ void Player::ModifySpellStatus(SpellBookEntry* spell, sint16 value, bool modify_ spell->recast = recast; spell->recast_available = Timer::GetCurrentTime2() + (recast * 100); } - if (modify_recast || spell->recast_available <= Timer::GetCurrentTime2() || value == 4) + if (modify_recast || spell->recast_available <= Timer::GetCurrentTime2() || value == 4) { spell->status += value; // use set/remove spell status now + } } void Player::AddSpellStatus(SpellBookEntry* spell, sint16 value, bool modify_recast, int16 recast) { if (modify_recast) { spell->recast = recast; spell->recast_available = Timer::GetCurrentTime2() + (recast * 100); } - if (modify_recast || spell->recast_available <= Timer::GetCurrentTime2() || value == 4) + if (modify_recast || spell->recast_available <= Timer::GetCurrentTime2() || value == 4) { spell->status = spell->status | value; + } } void Player::RemoveSpellStatus(SpellBookEntry* spell, sint16 value, bool modify_recast, int16 recast) { if (modify_recast) { spell->recast = recast; spell->recast_available = Timer::GetCurrentTime2() + (recast * 100); } - if (modify_recast || spell->recast_available <= Timer::GetCurrentTime2() || value == 4) + if (modify_recast || spell->recast_available <= Timer::GetCurrentTime2() || value == 4) { spell->status = spell->status & ~value; + } } void Player::SetSpellStatus(Spell* spell, int8 status){ @@ -2790,9 +2823,11 @@ EQ2Packet* Player::GetSpellBookUpdatePacket(int16 version) { if (spell_entry->spell_id == 0) continue; spell = master_spell_list.GetSpell(spell_entry->spell_id, spell_entry->tier); - if (spell) { - if (spell_entry->recast_available == 0 || Timer::GetCurrentTime2() > spell_entry->recast_available) + if (spell) { + if (spell_entry->recast_available == 0 || Timer::GetCurrentTime2() > spell_entry->recast_available) { packet->setSubstructArrayDataByName("spells", "available", 1, 0, ptr); + } + packet->setSubstructArrayDataByName("spells", "spell_id", spell_entry->spell_id, 0, ptr); packet->setSubstructArrayDataByName("spells", "type", spell_entry->type, 0, ptr); packet->setSubstructArrayDataByName("spells", "recast_available", spell_entry->recast_available, 0, ptr); @@ -2801,9 +2836,8 @@ EQ2Packet* Player::GetSpellBookUpdatePacket(int16 version) { packet->setSubstructArrayDataByName("spells", "icon", (spell->GetSpellIcon() * -1) - 1, 0, ptr); packet->setSubstructArrayDataByName("spells", "icon_type", spell->GetSpellIconBackdrop(), 0, ptr); packet->setSubstructArrayDataByName("spells", "icon2", spell->GetSpellIconHeroicOp(), 0, ptr); - packet->setSubstructArrayDataByName("spells", "unique_id", (spell_entry->tier + 1) * -1, 0, ptr); + packet->setSubstructArrayDataByName("spells", "unique_id", (spell_entry->tier + 1) * -1, 0, ptr); //this is actually GetSpellNameCrc(spell->GetName()), but hijacking it for spell tier packet->setSubstructArrayDataByName("spells", "charges", 255, 0, ptr); - // Beastlord and Channeler spell support if (spell->GetSpellData()->savage_bar == 1) packet->setSubstructArrayDataByName("spells", "unknown6", 32, 0, ptr); // advantages @@ -3746,9 +3780,7 @@ bool Player::AddXP(int32 xp_amount){ return false; } xp_amount -= GetNeededXP() - GetXP(); - SetLevel(GetLevel() + 1); - SetXP(0); - SetNeededXP(); + SetLevel(GetLevel() + 1); } SetXP(GetXP() + xp_amount); GetPlayerInfo()->CalculateXPPercentages(); @@ -4039,7 +4071,7 @@ PacketStruct* Player::GetQuestJournalPacket(bool all_quests, int16 version, int3 if(!all_quests && !itr->second->GetUpdateRequired()) continue; quest = itr->second; - if(!quest->GetDeleted() && !quest->GetCompleted()) + if(!quest->GetDeleted()) packet->setArrayDataByName("active", 1, i); packet->setArrayDataByName("name", quest->GetName(), i); packet->setArrayDataByName("quest_type", quest->GetType(), i); @@ -4053,8 +4085,10 @@ PacketStruct* Player::GetQuestJournalPacket(bool all_quests, int16 version, int3 packet->setArrayDataByName("visible", 1, i); display_status += QUEST_DISPLAY_STATUS_COMPLETED; } - if (updated) + if (updated) { packet->setArrayDataByName("quest_updated", 1, i); + packet->setArrayDataByName("journal_updated", 1, i); + } packet->setArrayDataByName("quest_id", quest->GetQuestID(), i); packet->setArrayDataByName("day", quest->GetDay(), i); packet->setArrayDataByName("month", quest->GetMonth(), i); @@ -4102,7 +4136,7 @@ PacketStruct* Player::GetQuestJournalPacket(bool all_quests, int16 version, int3 //packet->setDataByName("unknown4", 0); packet->setDataByName("visible_quest_id", current_quest_id); } - MPlayerQuests.unlock(); + MPlayerQuests.unlock(); packet->setDataByName("player_crc", crc); packet->setDataByName("player_name", GetName()); packet->setDataByName("used_quests", total_quests_num - total_completed_quests); @@ -4144,7 +4178,7 @@ PacketStruct* Player::GetQuestJournalPacket(Quest* quest, int16 version, int32 c packet->setArrayDataByName("turned_in", 1); packet->setArrayDataByName("completed", 1); display_status += QUEST_DISPLAY_STATUS_COMPLETED; - } + } packet->setArrayDataByName("quest_id", quest->GetQuestID()); packet->setArrayDataByName("day", quest->GetDay()); packet->setArrayDataByName("month", quest->GetMonth()); @@ -4189,8 +4223,10 @@ PacketStruct* Player::GetQuestJournalPacket(Quest* quest, int16 version, int32 c packet->setArrayDataByName("repeatable", 1); packet->setArrayDataByName("display_status", display_status); - if (updated) - packet->setDataByName("quest_updated", 1); + if (updated) { + packet->setArrayDataByName("quest_updated", 1); + packet->setArrayDataByName("journal_updated", 1); + } packet->setDataByName("visible_quest_id", quest->GetQuestID()); packet->setDataByName("player_crc", crc); packet->setDataByName("player_name", GetName()); diff --git a/EQ2/source/WorldServer/Player.h b/EQ2/source/WorldServer/Player.h index 5e79e2163..ad40c3724 100644 --- a/EQ2/source/WorldServer/Player.h +++ b/EQ2/source/WorldServer/Player.h @@ -493,6 +493,7 @@ public: /// True if the player has enough coins bool HasCoins(int64 val); void AddSkill(int32 skill_id, int16 current_val, int16 max_val, bool save_needed = false); + void RemovePlayerSkill(int32 skill_id, bool save = false); void RemoveSkillFromDB(Skill* skill, bool save = false); void AddSpellBookEntry(int32 spell_id, int8 tier, sint32 slot, int32 type, int32 timer, bool save_needed = false); SpellBookEntry* GetSpellBookSpell(int32 spell_id); @@ -576,6 +577,11 @@ public: void ClearRemovedSpawn(Spawn* spawn); bool ShouldSendSpawn(Spawn* spawn); Client* client = 0; + void SetLevel(int16 level, bool setUpdateFlags = true) { + SetInfo(&appearance.level, level, setUpdateFlags); + SetXP(0); + SetNeededXP(); + } Spawn* GetSpawnWithPlayerID(int32 id){ Spawn* spawn = 0; @@ -890,7 +896,7 @@ public: void LockAllSpells(); /// Unlocks all Spells, Combat arts, and Abilities (not trade skill spells) - void UnlockAllSpells(bool modify_recast = false); + void UnlockAllSpells(bool modify_recast = false, Spell* exception = 0); /// Locks the given spell as well as all spells with a shared timer void LockSpell(Spell* spell, int16 recast); diff --git a/EQ2/source/WorldServer/Quests.cpp b/EQ2/source/WorldServer/Quests.cpp index 50873c5bd..cd9c48c1b 100644 --- a/EQ2/source/WorldServer/Quests.cpp +++ b/EQ2/source/WorldServer/Quests.cpp @@ -302,6 +302,7 @@ Quest::Quest(int32 in_id){ reward_coins = 0; reward_coins_max = 0; completed_flag = false; + has_sent_last_update = false; enc_level = 0; reward_exp = 0; reward_tsexp = 0; @@ -345,6 +346,7 @@ Quest::Quest(Quest* old_quest){ reward_tsexp = old_quest->reward_tsexp; generated_coin = old_quest->generated_coin; completed_flag = old_quest->completed_flag; + has_sent_last_update = old_quest->has_sent_last_update; yellow_name = old_quest->yellow_name; m_questFlags = old_quest->m_questFlags; id = old_quest->id; @@ -995,7 +997,7 @@ EQ2Packet* Quest::QuestJournalReply(int16 version, int32 player_crc, Player* pla packet->setDataByName("bullets", 1); if (old_completed_quest) { - if (version >= 1096) { + if (version >= 1096 || version == 546) { packet->setDataByName("complete", 1); packet->setDataByName("complete2", 1); packet->setDataByName("complete3", 1); @@ -1008,7 +1010,7 @@ EQ2Packet* Quest::QuestJournalReply(int16 version, int32 player_crc, Player* pla packet->setDataByName("unknown3", 1, 6); } } - else if (version >= 1096 && GetCompleted()) { + else if ((version >= 1096 || version == 546) && GetCompleted() && HasSentLastUpdate()) { //need to send last quest update before erasing all progress of the quest packet->setDataByName("complete", 1); packet->setDataByName("complete2", 1); packet->setDataByName("complete3", 1); @@ -1225,12 +1227,15 @@ EQ2Packet* Quest::QuestJournalReply(int16 version, int32 player_crc, Player* pla } } + if (GetCompleted()) { //mark the last update as being sent, next time we send the quest reply, it will only be a brief portion + SetSentLastUpdate(true); + } } MQuestSteps.unlock(); string reward_str = ""; - if (version >= 1096) + if (version >= 1096 || version == 546) reward_str = "reward_data_"; string tmp = reward_str + "reward"; packet->setDataByName(tmp.c_str(), "Quest Reward!"); diff --git a/EQ2/source/WorldServer/Quests.h b/EQ2/source/WorldServer/Quests.h index cf50dbef5..e18dd3b9e 100644 --- a/EQ2/source/WorldServer/Quests.h +++ b/EQ2/source/WorldServer/Quests.h @@ -226,6 +226,8 @@ public: Player* GetPlayer(); void SetPlayer(Player* in_player); bool GetCompleted(); + bool HasSentLastUpdate() { return has_sent_last_update; } + void SetSentLastUpdate(bool val) { has_sent_last_update = val; } void SetCompletedDescription(string desc); const char* GetCompletedDescription(); int32 GetExpReward(); @@ -312,6 +314,7 @@ protected: bool turned_in; bool update_needed; bool deleted; + bool has_sent_last_update; string completed_description; diff --git a/EQ2/source/WorldServer/Skills.cpp b/EQ2/source/WorldServer/Skills.cpp index 30af0798d..92659055e 100644 --- a/EQ2/source/WorldServer/Skills.cpp +++ b/EQ2/source/WorldServer/Skills.cpp @@ -155,6 +155,14 @@ map* PlayerSkillList::GetAllSkills(){ return &skills; } +void PlayerSkillList::SetSkillValuesByType(int8 type, int16 value, bool send_update) { + map::iterator itr; + for (itr = skills.begin(); itr != skills.end(); itr++) { + if (itr->second && itr->second->skill_type == type) + SetSkill(itr->second, value, send_update); + } +} + void PlayerSkillList::SetSkillCapsByType(int8 type, int16 value){ map::iterator itr; for(itr = skills.begin(); itr != skills.end(); itr++){ @@ -220,19 +228,20 @@ void PlayerSkillList::DecreaseSkill(int32 skill_id, int16 amount){ DecreaseSkill(GetSkill(skill_id), amount); } -void PlayerSkillList::SetSkill(Skill* skill, int16 value){ +void PlayerSkillList::SetSkill(Skill* skill, int16 value, bool send_update){ if(skill){ skill->previous_val = skill->current_val; skill->current_val = value; if(skill->current_val > skill->max_val) skill->max_val = skill->current_val; skill->save_needed = true; - AddSkillUpdateNeeded(skill); + if(send_update) + AddSkillUpdateNeeded(skill); } } -void PlayerSkillList::SetSkill(int32 skill_id, int16 value){ - SetSkill(GetSkill(skill_id), value); +void PlayerSkillList::SetSkill(int32 skill_id, int16 value, bool send_update){ + SetSkill(GetSkill(skill_id), value, send_update); } void PlayerSkillList::IncreaseSkillCap(Skill* skill, int16 amount){ @@ -319,9 +328,15 @@ EQ2Packet* PlayerSkillList::GetSkillPacket(int16 version){ PacketStruct* packet = configReader.getStruct("WS_UpdateSkillBook", version); if(packet){ if(packet_count < skills.size()){ - int16 size = 21 * skills.size() + 8; - if (version <= 283) { - size = 12 * skills.size()+6; + int16 size = 0; + if (version > 546) { + size = 21 * skills.size() + 8; + } + else if (version <= 283) { + size = 12 * skills.size() + 6; + } + else if (version <= 546) { + size = 21 * skills.size() + 7; } if(!orig_packet){ xor_packet = new uchar[size]; @@ -360,7 +375,11 @@ EQ2Packet* PlayerSkillList::GetSkillPacket(int16 version){ } packet->setArrayDataByName("skill_id", skill->skill_id, i); - packet->setArrayDataByName("type", skill->skill_type, i); + if (version <= 546 && skill->skill_type >= SKILL_TYPE_GENERAL) { //covert it to DOF types + packet->setArrayDataByName("type", skill->skill_type-2, i); + } + else + packet->setArrayDataByName("type", skill->skill_type, i); packet->setArrayDataByName("current_val", skill->current_val, i); packet->setArrayDataByName("base_val", skill->current_val, i);// skill-> packet->setArrayDataByName("skill_delta", 0, i);// skill_with_bonuses- skill->current_val diff --git a/EQ2/source/WorldServer/Skills.h b/EQ2/source/WorldServer/Skills.h index 80ab5a157..54c901c52 100644 --- a/EQ2/source/WorldServer/Skills.h +++ b/EQ2/source/WorldServer/Skills.h @@ -25,15 +25,29 @@ #include "../common/types.h" #include "MutexMap.h" -#define SKILL_TYPE_COMBAT 1 +#define SKILL_TYPE_WEAPONRY 1 #define SKILL_TYPE_SPELLCASTING 2 #define SKILL_TYPE_AVOIDANCE 3 +#define SKILL_TYPE_ARMOR 4 +#define SKILL_TYPE_SHIELD 5 #define SKILL_TYPE_HARVESTING 6 #define SKILL_TYPE_ARTISAN 7 #define SKILL_TYPE_CRAFTSMAN 8 #define SKILL_TYPE_OUTFITTER 9 #define SKILL_TYPE_SCHOLAR 10 -#define SKILL_TYPE_GENERAL 12 +#define SKILL_TYPE_GENERAL 13 +#define SKILL_TYPE_LANGUAGE 14 +#define SKILL_TYPE_CLASS 15 +#define SKILL_TYPE_COMBAT 16 +#define SKILL_TYPE_WEAPON 17 +#define SKILL_TYPE_TSKNOWLEDGE 18 + +#define SKILL_TYPE_GENERAL_DOF 11 +#define SKILL_TYPE_LANGUAGE_DOF 12 +#define SKILL_TYPE_CLASS_DOF 13 +#define SKILL_TYPE_COMBAT_DOF 14 +#define SKILL_TYPE_WEAPON_DOF 15 +#define SKILL_TYPE_TSKNOWLEDGE_DOF 16 #define SKILL_ID_SCULPTING 1039865549 #define SKILL_ID_FLETCHING 3076004370 @@ -44,6 +58,13 @@ #define SKILL_ID_SCRIBING 773137566 #define SKILL_ID_CHEMISTRY 2557647574 #define SKILL_ID_ARTIFICING 3330500131 +#define SKILL_ID_ARTIFICING 3330500131 + +//the following update the current_value to the max_value as soon as the max_value is updated +#define SKILL_ID_DUALWIELD 1852383242 +#define SKILL_ID_FISTS 3177806075 +#define SKILL_ID_DESTROYING 3429135390 +#define SKILL_ID_MAGIC_AFFINITY 2072844078 /* Each SkillBonus is comprised of multiple possible skill bonus values. This is so one spell can modify more than one skill */ @@ -107,8 +128,8 @@ public: void IncreaseSkill(int32 skill_id, int16 amount); void DecreaseSkill(Skill* skill, int16 amount); void DecreaseSkill(int32 skill_id, int16 amount); - void SetSkill(Skill* skill, int16 value); - void SetSkill(int32 skill_id, int16 value); + void SetSkill(Skill* skill, int16 value, bool send_update = true); + void SetSkill(int32 skill_id, int16 value, bool send_update = true); void IncreaseSkillCap(Skill* skill, int16 amount); void IncreaseSkillCap(int32 skill_id, int16 amount); @@ -119,6 +140,7 @@ public: void IncreaseAllSkillCaps(int16 value); void IncreaseSkillCapsByType(int8 type, int16 value); void SetSkillCapsByType(int8 type, int16 value); + void SetSkillValuesByType(int8 type, int16 value, bool send_update = true); void AddSkillUpdateNeeded(Skill* skill); void AddSkillBonus(int32 spell_id, int32 skill_id, float value); diff --git a/EQ2/source/WorldServer/Spawn.cpp b/EQ2/source/WorldServer/Spawn.cpp index bd03418f3..63bba7295 100644 --- a/EQ2/source/WorldServer/Spawn.cpp +++ b/EQ2/source/WorldServer/Spawn.cpp @@ -337,14 +337,14 @@ void Spawn::InitializeVisPacketData(Player* player, PacketStruct* vis_packet) { vis_flags += 4; } - if (version <= 546 && vis_flags > 0) + if (version <= 546 && (vis_flags > 1 || appearance.display_hand_icon > 0)) //interactable vis_flags = 1; - vis_packet->setDataByName("vis_flags", vis_flags); - if (MeetsSpawnAccessRequirements(player)) + if (MeetsSpawnAccessRequirements(player)) { vis_packet->setDataByName("hand_flag", appearance.display_hand_icon); + } else { if ((req_quests_override & 256) > 0) vis_packet->setDataByName("hand_flag", 1); @@ -1006,7 +1006,9 @@ EQ2Packet* Spawn::spawn_update_packet(Player* player, int16 version, bool overri ptr += pos_packet_size; memcpy(ptr, vis_changes ? vis_changes : &null_byte, tmp_vis_packet_size); - EQ2Packet* ret_packet = new EQ2Packet(OP_ClientCmdMsg, tmp, size); + EQ2Packet* ret_packet = 0; + if(info_packet_size + pos_packet_size + vis_packet_size > 0) + ret_packet = new EQ2Packet(OP_ClientCmdMsg, tmp, size); delete[] tmp; safe_delete_array(info_changes); safe_delete_array(vis_changes); @@ -2224,7 +2226,8 @@ void Spawn::InitializeInfoPacketData(Player* spawn, PacketStruct* packet) { } if (GetHP() <= 0 && IsEntity()) { packet->setDataByName("corpse", 1); - packet->setDataByName("loot_icon", 1); + if(HasLoot()) + packet->setDataByName("loot_icon", 1); } if (!IsPlayer()) packet->setDataByName("npc", 1); diff --git a/EQ2/source/WorldServer/Spawn.h b/EQ2/source/WorldServer/Spawn.h index f04dc9c6d..d9a6dce7a 100644 --- a/EQ2/source/WorldServer/Spawn.h +++ b/EQ2/source/WorldServer/Spawn.h @@ -152,6 +152,13 @@ #define INFO_VIS_FLAG_MOUNTED 4 #define INFO_VIS_FLAG_CROUCH 8 +#define ENCOUNTER_STATE_NONE 0 +#define ENCOUNTER_STATE_AVAILABLE 1 +#define ENCOUNTER_STATE_BROKEN 2 +#define ENCOUNTER_STATE_LOCKED 3 +#define ENCOUNTER_STATE_OVERMATCHED 4 +#define ENCOUNTER_STATE_NO_REWARD 5 + using namespace std; class Spell; class ZoneServer; @@ -293,6 +300,7 @@ public: entity_command->default_allow_list = default_allow_list; return entity_command; } + virtual Client* GetClient() { return 0; } void AddChangedZoneSpawn(); void AddPrimaryEntityCommand(const char* name, float distance, const char* command, const char* error_text, int16 cast_time, int32 spell_visual, bool defaultDenyList = false, Player* player = NULL); void RemovePrimaryEntityCommand(const char* command); @@ -446,7 +454,7 @@ public: void SetEncounterLevel(int8 enc_level, bool setUpdateFlags = true){ SetInfo(&appearance.encounter_level, enc_level, setUpdateFlags); } - void SetLevel(int16 level, bool setUpdateFlags = true){ + virtual void SetLevel(int16 level, bool setUpdateFlags = true){ SetInfo(&appearance.level, level, setUpdateFlags); } void SetTSLevel(int16 tradeskill_level, bool setUpdateFlags = true){ diff --git a/EQ2/source/WorldServer/SpellProcess.cpp b/EQ2/source/WorldServer/SpellProcess.cpp index e908b9deb..ddec877e1 100644 --- a/EQ2/source/WorldServer/SpellProcess.cpp +++ b/EQ2/source/WorldServer/SpellProcess.cpp @@ -392,12 +392,33 @@ bool SpellProcess::DeleteCasterSpell(LuaSpell* spell, string reason){ if(target && target->IsPlayer() && spell->spell->GetSpellData()->fade_message.length() > 0){ Client* client = target->GetZone()->GetClientBySpawn(target); if(client){ + bool send_to_sender = true; string fade_message = spell->spell->GetSpellData()->fade_message; if(fade_message.find("%t") != string::npos) - fade_message.replace(fade_message.find("%t"), 2, target->GetName()); + fade_message.replace(fade_message.find("%t"), 2, target->GetName()); client->Message(CHANNEL_SPELLS_OTHER, fade_message.c_str()); } } + if (target && target->IsPlayer() && spell->spell->GetSpellData()->fade_message.length() > 0) { + Client* client = target->GetZone()->GetClientBySpawn(target); + if (client) { + bool send_to_sender = true; + string fade_message_others = spell->spell->GetSpellData()->fade_message_others; + if (fade_message_others.find("%t") != string::npos) + fade_message_others.replace(fade_message_others.find("%t"), 2, target->GetName()); + if (fade_message_others.find("%c") != string::npos) + fade_message_others.replace(fade_message_others.find("%c"), 2, spell->caster->GetName()); + if (fade_message_others.find("%T") != string::npos) { + fade_message_others.replace(fade_message_others.find("%T"), 2, target->GetName()); + send_to_sender = false; + } + if (fade_message_others.find("%C") != string::npos) { + fade_message_others.replace(fade_message_others.find("%C"), 2, spell->caster->GetName()); + send_to_sender = false; + } + spell->caster->GetZone()->SimpleMessage(CHANNEL_SPELLS_OTHER, fade_message_others.c_str(), target, 50, send_to_sender); + } + } } spell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__); ret = true; @@ -522,7 +543,10 @@ void SpellProcess::SendStartCast(LuaSpell* spell, Client* client){ void SpellProcess::SendFinishedCast(LuaSpell* spell, Client* client){ if(client && spell && spell->spell){ - UnlockAllSpells(client); + if (spell->spell->GetSpellData()->cast_type == SPELL_CAST_TYPE_TOGGLE) + UnlockAllSpells(client, spell->spell); + else + UnlockAllSpells(client); if(spell->resisted && spell->spell->GetSpellData()->recast > 0) CheckRecast(spell->spell, client->GetPlayer(), 0.5); // half sec recast on resisted spells else if (!spell->interrupted && spell->spell->GetSpellData()->cast_type != SPELL_CAST_TYPE_TOGGLE) @@ -557,9 +581,9 @@ void SpellProcess::LockAllSpells(Client* client){ } } -void SpellProcess::UnlockAllSpells(Client* client){ +void SpellProcess::UnlockAllSpells(Client* client, Spell* exception){ if(client) - client->GetPlayer()->UnlockAllSpells(); + client->GetPlayer()->UnlockAllSpells(false, exception); } void SpellProcess::UnlockSpell(Client* client, Spell* spell){ @@ -1447,11 +1471,20 @@ bool SpellProcess::CastProcessedSpell(LuaSpell* spell, bool passive){ } if(spell->spell->GetSpellData()->effect_message.length() > 0){ string effect_message = spell->spell->GetSpellData()->effect_message; - if(effect_message.find("%t") < 0xFFFFFFFF) + bool send_to_sender = true; + if(effect_message.find("%t") != string::npos) effect_message.replace(effect_message.find("%t"), 2, target->GetName()); if (effect_message.find("%c") != string::npos) effect_message.replace(effect_message.find("%c"), 2, spell->caster->GetName()); - spell->caster->GetZone()->SimpleMessage(CHANNEL_SPELLS_OTHER, effect_message.c_str(), target, 50); + if (effect_message.find("%T") != string::npos) { + effect_message.replace(effect_message.find("%T"), 2, target->GetName()); + send_to_sender = false; + } + if (effect_message.find("%C") != string::npos) { + effect_message.replace(effect_message.find("%C"), 2, spell->caster->GetName()); + send_to_sender = false; + } + spell->caster->GetZone()->SimpleMessage(CHANNEL_SPELLS_OTHER, effect_message.c_str(), target, 50, send_to_sender); } target->GetZone()->CallSpawnScript(target, SPAWN_SCRIPT_CASTED_ON, spell->caster, spell->spell->GetName()); } @@ -1466,7 +1499,13 @@ bool SpellProcess::CastProcessedSpell(LuaSpell* spell, bool passive){ //LogWrite(SPELL__ERROR, 0, "Spell", "No precast function found for %s", ((Entity*)target)->GetName()); target = zone->GetSpawnByID(spell->targets.at(i)); - + if (!target && spell->targets.at(i) == spell->caster->GetID()) { + target = spell->caster; + } + if (!target) { + client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Zone has not finished loading process yet. Try again later."); + continue; + } if (i == 0 && !spell->spell->GetSpellData()->not_maintained) { spell->caster->AddMaintainedSpell(spell); //((Entity*)target)->AddMaintainedSpell(spell); diff --git a/EQ2/source/WorldServer/SpellProcess.h b/EQ2/source/WorldServer/SpellProcess.h index 582d74908..aa0b6f574 100644 --- a/EQ2/source/WorldServer/SpellProcess.h +++ b/EQ2/source/WorldServer/SpellProcess.h @@ -244,7 +244,7 @@ public: /// Unlock all the spells for the given client /// Client to unlock the spells for - void UnlockAllSpells(Client* client); + void UnlockAllSpells(Client* client, Spell* exception = 0); /// Unlock a single spell for the given client /// The client to unlock the spell for diff --git a/EQ2/source/WorldServer/Spells.cpp b/EQ2/source/WorldServer/Spells.cpp index e971e907a..f2bfcb792 100644 --- a/EQ2/source/WorldServer/Spells.cpp +++ b/EQ2/source/WorldServer/Spells.cpp @@ -82,6 +82,7 @@ Spell::Spell(Spell* host_spell) spell->duration_until_cancel = host_spell->GetSpellData()->duration_until_cancel; spell->effect_message = string(host_spell->GetSpellData()->effect_message); spell->fade_message = string(host_spell->GetSpellData()->fade_message); + spell->fade_message_others = string(host_spell->GetSpellData()->fade_message_others); spell->friendly_spell = host_spell->GetSpellData()->friendly_spell; spell->group_spell = host_spell->GetSpellData()->group_spell; @@ -774,8 +775,12 @@ void Spell::SetPacketInformation(PacketStruct* packet, Client* client, bool disp packet->setSubstructDataByName("spell_info", "tier", spell->tier); packet->setSubstructDataByName("spell_info", "power_req", power_req); packet->setSubstructDataByName("spell_info", "power_upkeep", spell->power_upkeep); - - packet->setSubstructDataByName("spell_info", "cast_time", spell->cast_time); + if (packet->GetVersion() <= 546) {//cast times are displayed differently on new clients + packet->setSubstructDataByName("spell_info", "cast_time", spell->cast_time/10); + } + else { + packet->setSubstructDataByName("spell_info", "cast_time", spell->cast_time); + } packet->setSubstructDataByName("spell_info", "recast", spell->recast); packet->setSubstructDataByName("spell_info", "radius", spell->radius); packet->setSubstructDataByName("spell_info", "req_concentration", spell->req_concentration); @@ -1095,10 +1100,10 @@ EQ2Packet* Spell::SerializeSpell(Client* client, bool display, bool trait_displa version = client->GetVersion(); if (!struct_name) struct_name = "WS_ExamineSpellInfo"; - if (version <= 283) { + if (version <= 546) { if (packet_type == 1) struct_name = "WS_ExamineEffectInfo"; - else if (!display) + else if (!display && version<=283) struct_name = "WS_ExaminePartialSpellInfo"; else struct_name = "WS_ExamineSpellInfo"; @@ -1501,6 +1506,11 @@ bool Spell::GetSpellData(lua_State* state, std::string field) lua_interface->SetStringValue(state, GetSpellData()->fade_message.c_str()); valSet = true; } + else if (field == "fade_message_others") + { + lua_interface->SetStringValue(state, GetSpellData()->fade_message_others.c_str()); + valSet = true; + } else if (field == "cast_type") { lua_interface->SetSInt32Value(state, GetSpellData()->cast_type); @@ -1883,6 +1893,12 @@ bool Spell::SetSpellData(lua_State* state, std::string field, int8 fieldArg) GetSpellData()->fade_message = fade_message; valSet = true; } + else if (field == "fade_message_others") + { + string fade_message_others = lua_interface->GetStringValue(state, fieldArg); + GetSpellData()->fade_message_others = fade_message_others; + valSet = true; + } else if (field == "cast_type") { int8 cast_type = lua_interface->GetInt8Value(state, fieldArg); diff --git a/EQ2/source/WorldServer/Spells.h b/EQ2/source/WorldServer/Spells.h index 81b530402..886c54287 100644 --- a/EQ2/source/WorldServer/Spells.h +++ b/EQ2/source/WorldServer/Spells.h @@ -259,6 +259,7 @@ struct SpellData{ EQ2_16BitString description; string success_message; string fade_message; + string fade_message_others; int8 cast_type; string lua_script; int32 call_frequency; @@ -335,7 +336,8 @@ public: bool CastWhileMezzed(); bool CastWhileStifled(); bool CastWhileFeared(); - + bool GetStayLocked() { return stay_locked; } + void StayLocked(bool val) { stay_locked = val; } vector effects; vector lua_data; @@ -343,6 +345,7 @@ public: void LockSpellInfo() { MSpellInfo.lock(); } void UnlockSpellInfo() { MSpellInfo.unlock(); } private: + bool stay_locked = false; bool heal_spell; bool buff_spell; bool damage_spell; diff --git a/EQ2/source/WorldServer/World.cpp b/EQ2/source/WorldServer/World.cpp index 0b6edf75a..814cad4aa 100644 --- a/EQ2/source/WorldServer/World.cpp +++ b/EQ2/source/WorldServer/World.cpp @@ -2276,6 +2276,43 @@ void World::PurgeStartingLists() MStartingLists.releasewritelock(); } +void World::SetReloadingSubsystem(string subsystem) { + MReloadingSubsystems.lock(); + reloading_subsystems[subsystem] = Timer::GetCurrentTime2(); + MReloadingSubsystems.unlock(); +} + +void World::RemoveReloadingSubSystem(string subsystem) { + MReloadingSubsystems.lock(); + if (reloading_subsystems.count(subsystem) > 0) + reloading_subsystems.erase(subsystem); + MReloadingSubsystems.unlock(); +} + +bool World::IsReloadingSubsystems() { + bool result = false; + MReloadingSubsystems.lock(); + result = reloading_subsystems.size() > 0; + MReloadingSubsystems.unlock(); + return result; +} + +map World::GetOldestReloadingSubsystem() { + map result; + MReloadingSubsystems.lock(); + int32 current_time = Timer::GetCurrentTime2(); + map::iterator itr; + int32 oldest = current_time; + string oldestname = ""; + for (itr = reloading_subsystems.begin(); itr != reloading_subsystems.end(); itr++) { + if (itr->second < oldest) + oldestname = itr->first; + } + result[oldestname] = oldest; + MReloadingSubsystems.unlock(); + return result; +} + void ZoneList::WatchdogHeartbeat() { list::iterator zone_iter; @@ -2290,9 +2327,8 @@ void ZoneList::WatchdogHeartbeat() { int32 curTime = Timer::GetCurrentTime2(); sint64 diff = (sint64)curTime - (sint64)tmp->GetWatchdogTime(); - if (diff > 60000) - { - tmp->SetWatchdogTime(Timer::GetCurrentTime2()); // reset so we don't continuously flood this heartbeat + if (diff > 120000) + { LogWrite(WORLD__ERROR, 1, "World", "Zone %s is hung for %i milliseconds.. attempting to cancel threads...", tmp->GetZoneName(), diff); #ifndef WIN32 tmp->CancelThreads(); @@ -2301,10 +2337,38 @@ void ZoneList::WatchdogHeartbeat() #endif MZoneList.releasewritelock(__FUNCTION__, __LINE__); match = true; - break; + break; + } + else if (diff > 90000 && !tmp->isZoneShuttingDown()) + { + tmp->SetWatchdogTime(Timer::GetCurrentTime2()); // reset so we don't continuously flood this heartbeat + map oldest_process = world.GetOldestReloadingSubsystem(); + if (oldest_process.size() > 0) { + map::iterator itr = oldest_process.begin(); + LogWrite(WORLD__ERROR, 1, "World", "Zone %s is hung for %i milliseconds.. while waiting for %s to reload...attempting shutdown", tmp->GetZoneName(), diff, itr->first); + } + else + LogWrite(WORLD__ERROR, 1, "World", "Zone %s is hung for %i milliseconds.. attempting shutdown", tmp->GetZoneName(), diff); + tmp->Shutdown(); + } + else if (diff > 60000) + { + if (world.IsReloadingSubsystems()) { + if (world.GetSuppressedWarningTime() == 0) { + world.SetSuppressedWarning(); + map oldest_process = world.GetOldestReloadingSubsystem(); + if (oldest_process.size() > 0) { + map::iterator itr = oldest_process.begin(); + LogWrite(WORLD__ERROR, 1, "World", "Zone %s is hung for %i milliseconds.. while waiting for %s to reload...", tmp->GetZoneName(), diff, itr->first); + } + } + continue; + } } else if (diff > 30000 && !tmp->isZoneShuttingDown()) { + if (world.IsReloadingSubsystems()) + continue; LogWrite(WORLD__ERROR, 1, "World", "Zone %s is hung for %i milliseconds.. attempting shutdown", tmp->GetZoneName(), diff); tmp->Shutdown(); } diff --git a/EQ2/source/WorldServer/World.h b/EQ2/source/WorldServer/World.h index a04de8065..d4bbe6797 100644 --- a/EQ2/source/WorldServer/World.h +++ b/EQ2/source/WorldServer/World.h @@ -474,7 +474,7 @@ class ZoneList { void ReloadClientQuests(); bool DepopFinished(); void Depop(); - void Repop(); + void Repop(); void DeleteSpellProcess(); void LoadSpellProcess(); void ProcessWhoQuery(const char* query, Client* client); @@ -613,10 +613,22 @@ public: multimap*> starting_skills; multimap*> starting_spells; Mutex MStartingLists; + void SetReloadingSubsystem(string subsystem); + void RemoveReloadingSubSystem(string subsystem); + + bool IsReloadingSubsystems(); + int32 GetSuppressedWarningTime() { + return suppressed_warning; + } + void SetSuppressedWarning() { suppressed_warning = Timer::GetCurrentTime2(); } + map GetOldestReloadingSubsystem(); + private: + int32 suppressed_warning = 0; + map reloading_subsystems; //void RemovePlayerFromGroup(PlayerGroup* group, GroupMemberInfo* info, bool erase = true); //void DeleteGroupMember(GroupMemberInfo* info); - + Mutex MReloadingSubsystems; Mutex MMerchantList; Mutex MSpawnScripts; Mutex MZoneScripts; diff --git a/EQ2/source/WorldServer/WorldDatabase.cpp b/EQ2/source/WorldServer/WorldDatabase.cpp index 85efa74ec..d844d3355 100644 --- a/EQ2/source/WorldServer/WorldDatabase.cpp +++ b/EQ2/source/WorldServer/WorldDatabase.cpp @@ -287,6 +287,12 @@ int32 WorldDatabase::LoadSkills() skill->description.data = string(row[3]); skill->description.size = skill->description.data.length(); skill->skill_type = strtoul(row[4], NULL, 0); + //these two need to be converted to the correct numbers + if(skill->skill_type == 13) + skill->skill_type = SKILL_TYPE_LANGUAGE; + else if(skill->skill_type == 12) + skill->skill_type = SKILL_TYPE_GENERAL; + skill->display = atoi(row[5]); master_skill_list.AddSkill(skill); total++; @@ -3284,6 +3290,7 @@ bool WorldDatabase::SaveSpawnEntry(Spawn* spawn, const char* spawn_location_name LogWrite(SPAWN__ERROR, 0, "Spawn", "Error in SaveSpawnEntry query '%s': %s", query2.GetQuery(), query2.GetError()); return false; } + spawn->SetSpawnLocationPlacementID(query2.GetLastInsertedID()); } return true; } @@ -3343,6 +3350,8 @@ int32 WorldDatabase::GetSpawnLocationCount(int32 location, Spawn* spawn){ MYSQL_RES* result = 0; if(spawn) result = query.RunQuery2(Q_SELECT, "SELECT count(id) FROM spawn_location_entry where spawn_location_id=%u and spawn_id=%u", location, spawn->GetDatabaseID()); + else + result = query.RunQuery2(Q_SELECT, "SELECT count(id) FROM spawn_location_entry where spawn_location_id=%u", location); if(result && mysql_num_rows(result) > 0){ MYSQL_ROW row; while(result && (row = mysql_fetch_row(result)) && row[0]){ @@ -4310,7 +4319,7 @@ void WorldDatabase::LoadSpells() int32 total = 0; map >* level_data = LoadSpellClasses(); - if( !database_new.Select(&result, "SELECT s.`id`, ts.spell_id, ts.index, `name`, `description`, `type`, `class_skill`, `mastery_skill`, `tier`, `is_aa`,`hp_req`, `power_req`,`power_by_level`, `cast_time`, `recast`, `radius`, `max_aoe_targets`, `req_concentration`, `range`, `duration1`, `duration2`, `resistibility`, `hp_upkeep`, `power_upkeep`, `duration_until_cancel`, `target_type`, `recovery`, `power_req_percent`, `hp_req_percent`, `icon`, `icon_heroic_op`, `icon_backdrop`, `success_message`, `fade_message`, `cast_type`, `lua_script`, `call_frequency`, `interruptable`, `spell_visual`, `effect_message`, `min_range`, `can_effect_raid`, `affect_only_group_members`, `hit_bonus`, `display_spell_tier`, `friendly_spell`, `group_spell`, `spell_book_type`, spell_type+0, s.is_active, savagery_req, savagery_req_percent, savagery_upkeep, dissonance_req, dissonance_req_percent, dissonance_upkeep, linked_timer_id, det_type, incurable, control_effect_type, cast_while_moving, casting_flags, persist_through_death, not_maintained, savage_bar, savage_bar_slot, soe_spell_crc, 0xffffffff-CRC32(s.`name`) as 'spell_name_crc' " + if( !database_new.Select(&result, "SELECT s.`id`, ts.spell_id, ts.index, `name`, `description`, `type`, `class_skill`, `mastery_skill`, `tier`, `is_aa`,`hp_req`, `power_req`,`power_by_level`, `cast_time`, `recast`, `radius`, `max_aoe_targets`, `req_concentration`, `range`, `duration1`, `duration2`, `resistibility`, `hp_upkeep`, `power_upkeep`, `duration_until_cancel`, `target_type`, `recovery`, `power_req_percent`, `hp_req_percent`, `icon`, `icon_heroic_op`, `icon_backdrop`, `success_message`, `fade_message`, `fade_message_others`, `cast_type`, `lua_script`, `call_frequency`, `interruptable`, `spell_visual`, `effect_message`, `min_range`, `can_effect_raid`, `affect_only_group_members`, `hit_bonus`, `display_spell_tier`, `friendly_spell`, `group_spell`, `spell_book_type`, spell_type+0, s.is_active, savagery_req, savagery_req_percent, savagery_upkeep, dissonance_req, dissonance_req_percent, dissonance_upkeep, linked_timer_id, det_type, incurable, control_effect_type, cast_while_moving, casting_flags, persist_through_death, not_maintained, savage_bar, savage_bar_slot, soe_spell_crc, 0xffffffff-CRC32(s.`name`) as 'spell_name_crc' " "FROM (spells s, spell_tiers st) " "LEFT JOIN spell_ts_ability_index ts " "ON s.`id` = ts.spell_id " @@ -4418,6 +4427,10 @@ void WorldDatabase::LoadSpells() if( message.length() > 0 ) data->fade_message = string(message); + message = result.GetStringStr("fade_message_others"); + if (message.length() > 0) + data->fade_message_others = string(message); + message = result.GetStringStr("effect_message"); if( message.length() > 0 ) data->effect_message = string(message); diff --git a/EQ2/source/WorldServer/client.cpp b/EQ2/source/WorldServer/client.cpp index 80127bbd2..dec0c93eb 100644 --- a/EQ2/source/WorldServer/client.cpp +++ b/EQ2/source/WorldServer/client.cpp @@ -1348,6 +1348,18 @@ bool Client::HandlePacket(EQApplicationPacket* app) { HandleLoot(app); break; } + case OP_WaypointSelectMsg: { + PacketStruct* packet = configReader.getStruct("WS_WaypointSelect", GetVersion()); + if (packet) { + if (packet->LoadPacketData(app->pBuffer, app->size)) { + int32 selection = packet->getType_int32_ByName("selection"); + if (selection > 0) { + SelectWaypoint(selection); + } + } + } + break; + } case OP_KnowledgeWindowSlotMappingMsg: { LogWrite(OPCODE__DEBUG, 1, "Opcode", "Opcode 0x%X (%i): OP_KnowledgeWindowSlotMappingMsg", opcode, opcode); PacketStruct* packet = configReader.getStruct("WS_SpellSlotMapping", GetVersion()); @@ -1572,9 +1584,18 @@ bool Client::HandlePacket(EQApplicationPacket* app) { else { EQ2_16BitString command = packet->getType_EQ2_16BitString_ByName("command"); if (command.size > 0) { - string command_name = command.data; - if (command_name.find(" ") < 0xFFFFFFFF) - command_name = command_name.substr(0, command_name.find(" ")); + string command_name = command.data; + if (command_name.find(" ") < 0xFFFFFFFF) { + if (GetVersion() <= 546) { //this version uses commands in the form "Buy From Merchant" instead of buy_from_merchant + string::size_type pos = command_name.find(" "); + while(pos != string::npos){ + command_name.replace(pos, 1, "_"); + pos = command_name.find(" "); + } + } + else + command_name = command_name.substr(0, command_name.find(" ")); + } int32 handler = commands.GetCommandHandler(command_name.c_str()); if (handler != 0xFFFFFFFF) { if (command.data == command_name) { @@ -1591,7 +1612,15 @@ bool Client::HandlePacket(EQApplicationPacket* app) { if (spawn && spawn->IsNPC()) { if (EntityCommandPrecheck(spawn, command.data.c_str())) { if (!((NPC*)spawn)->HandleUse(this, command.data)) { - LogWrite(WORLD__ERROR, 0, "World", "Unhandled command in OP_EntityVerbsVerbMsg: %s", command.data.c_str()); + command_name = command.data; + string::size_type pos = command_name.find(" "); + while (pos != string::npos) { + command_name.replace(pos, 1, "_"); + pos = command_name.find(" "); + } + if (!((NPC*)spawn)->HandleUse(this, command_name)) { //convert the spaces to underscores and see if that makes a difference + LogWrite(WORLD__ERROR, 0, "World", "Unhandled command in OP_EntityVerbsVerbMsg: %s", command.data.c_str()); + } } } } @@ -2458,7 +2487,8 @@ void Client::HandleSkillInfoRequest(EQApplicationPacket* app) { void Client::HandleExamineInfoRequest(EQApplicationPacket* app) { PacketStruct* request = 0; - + if (!app || app->size == 0) + return; //LogWrite(CCLIENT__DEBUG, 0, "Client", "Request2:"); //DumpPacket(app); @@ -3341,7 +3371,7 @@ void Client::SimpleMessage(int8 color, const char* message) { } } -void Client::SendSpellUpdate(Spell* spell) { +void Client::SendSpellUpdate(Spell* spell, bool add_silently, bool add_to_hotbar) { PacketStruct* packet = configReader.getStruct("WS_SpellGainedMsg", GetVersion()); if (packet) { int8 xxx = spell->GetSpellData()->is_aa; @@ -3349,6 +3379,10 @@ void Client::SendSpellUpdate(Spell* spell) { packet->setDataByName("spell_id", spell->GetSpellID()); packet->setDataByName("unique_id", spell->GetSpellData()->spell_name_crc); packet->setDataByName("spell_name", spell->GetName()); + if(add_silently) + packet->setDataByName("add_silently", 1); + if(add_to_hotbar) + packet->setDataByName("add_to_hotbar", 1); packet->setDataByName("unknown", xxx); packet->setDataByName("display_spell_tier", 1); packet->setDataByName("unknown3", 1); @@ -4085,8 +4119,9 @@ void Client::ChangeLevel(int16 old_level, int16 new_level) { } } - if (new_level > old_level) + if (new_level > old_level) { player->UpdatePlayerHistory(HISTORY_TYPE_XP, HISTORY_SUBTYPE_ADVENTURE, new_level, player->GetAdventureClass()); + } if (player->GetPet()) { NPC* pet = (NPC*)player->GetPet(); @@ -4165,13 +4200,37 @@ void Client::ChangeLevel(int16 old_level, int16 new_level) { LogWrite(WORLD__DEBUG, 0, "World", "Player: %s leveled from %u to %u", GetPlayer()->GetName(), old_level, new_level); int16 new_skill_cap = 5 * new_level; PlayerSkillList* player_skills = player->GetSkills(); + + player_skills->SetSkillCapsByType(SKILL_TYPE_ARMOR, new_skill_cap); + player_skills->SetSkillCapsByType(SKILL_TYPE_SHIELD, new_skill_cap); + //SKILL_TYPE_ARMOR/SKILL_TYPE_SHIELD always has the same current / max values + player_skills->SetSkillValuesByType(SKILL_TYPE_ARMOR, new_skill_cap, false); + player_skills->SetSkillValuesByType(SKILL_TYPE_SHIELD, new_skill_cap, false); + + player_skills->SetSkillCapsByType(SKILL_TYPE_CLASS, new_skill_cap); + player_skills->SetSkillCapsByType(SKILL_TYPE_WEAPON, new_skill_cap); + //SKILL_TYPE_CLASS/SKILL_TYPE_WEAPON always has the same current/max values + player_skills->SetSkillValuesByType(SKILL_TYPE_CLASS, new_skill_cap, false); + player_skills->SetSkillValuesByType(SKILL_TYPE_WEAPON, new_skill_cap, false); + player_skills->SetSkillCapsByType(SKILL_TYPE_COMBAT, new_skill_cap); + player_skills->SetSkillCapsByType(SKILL_TYPE_GENERAL, new_skill_cap); player_skills->SetSkillCapsByType(SKILL_TYPE_SPELLCASTING, new_skill_cap); player_skills->SetSkillCapsByType(SKILL_TYPE_AVOIDANCE, new_skill_cap); - player_skills->SetSkillCapsByType(SKILL_TYPE_GENERAL, new_skill_cap); + if (new_level > player->GetTSLevel()) player_skills->SetSkillCapsByType(SKILL_TYPE_HARVESTING, new_skill_cap); + //SKILL_ID_DUALWIELD, SKILL_ID_FISTS, SKILL_ID_DESTROYING, and SKILL_ID_MAGIC_AFFINITY always have the current_val equal to max_val + if (player_skills->HasSkill(SKILL_ID_DUALWIELD)) + player_skills->SetSkill(SKILL_ID_DUALWIELD, new_skill_cap); + if (player_skills->HasSkill(SKILL_ID_FISTS)) + player_skills->SetSkill(SKILL_ID_FISTS, new_skill_cap); + if (player_skills->HasSkill(SKILL_ID_DESTROYING)) + player_skills->SetSkill(SKILL_ID_DESTROYING, new_skill_cap); + if (player_skills->HasSkill(SKILL_ID_MAGIC_AFFINITY)) + player_skills->SetSkill(SKILL_ID_MAGIC_AFFINITY, new_skill_cap); + Guild* guild = GetPlayer()->GetGuild(); if (guild) { int8 event_type = 0; @@ -5042,8 +5101,8 @@ void Client::CheckQuestQueue() { for (itr = quest_queue.begin(); itr != quest_queue.end(); itr++) { queued_quest = *itr; SendQuestUpdateStepImmediately(queued_quest->quest, queued_quest->step, queued_quest->display_quest_helper); - //if(queued_quest->quest && queued_quest->quest->GetTurnedIn()) //update the journal so the old quest isn't the one displayed in the client's quest helper - // SendQuestJournal(); + if(queued_quest->quest && queued_quest->quest->GetTurnedIn()) //update the journal so the old quest isn't the one displayed in the client's quest helper + SendQuestJournal(); safe_delete(queued_quest); } quest_queue.clear(); @@ -5320,9 +5379,9 @@ void Client::SendQuestUpdate(Quest* quest) { step = updates->at(i); if (lua_interface && step->Complete() && quest->GetCompleteAction(step->GetStepID())) lua_interface->CallQuestFunction(quest, quest->GetCompleteAction(step->GetStepID()), player); - if (step->WasUpdated()) { - SendQuestJournal(false, 0, true); + if (step->WasUpdated()) { QueuePacket(quest->QuestJournalReply(GetVersion(), GetNameCRC(), player, step)); + SendQuestJournal(false, 0, true); } LogWrite(CCLIENT__DEBUG, 0, "Client", "Send Quest Journal..."); @@ -5384,17 +5443,21 @@ Quest* Client::GetPendingQuestAcceptance(int32 item_id) { MPendingQuestAccept.lock(); for (itr = pending_quest_accept.begin(); itr != pending_quest_accept.end(); itr++) { quest = *itr; - items = quest->GetSelectableRewardItems(); - if (items && items->size() > 0) { - for (int32 i = 0; i < items->size(); i++) { - if (items->at(i)->details.item_id == item_id) { - found_quest = true; - break; + items = quest->GetRewardItems(); + if (item_id == 0 && items && items->size() > 0) { + found_quest = true; + } + else { + items = quest->GetSelectableRewardItems(); + if (items && items->size() > 0) { + for (int32 i = 0; i < items->size(); i++) { + if (items->at(i)->details.item_id == item_id) { + found_quest = true; + break; + } } } } - else if (item_id == 0) - found_quest = true; if (found_quest) { pending_quest_accept.erase(itr); break; @@ -5455,10 +5518,10 @@ void Client::AcceptQuestReward(Quest* quest, int32 item_id) { void Client::DisplayQuestRewards(Quest* quest, int64 coin, vector* rewards, vector* selectable_rewards, map* factions, const char* header, int32 status_points, const char* text) { if (coin == 0 && (!rewards || rewards->size() == 0) && (!selectable_rewards || selectable_rewards->size() == 0) && (!factions || factions->size() == 0) && status_points == 0 && text == 0 && (!quest || (quest->GetCoinsReward() == 0 && quest->GetCoinsRewardMax() == 0))) { - if (quest) + /*if (quest) text = quest->GetName(); - else - return;//nothing to give + else*/ + return;//nothing to give } PacketStruct* packet2 = configReader.getStruct("WS_QuestRewardPackMsg", GetVersion()); if (packet2) { @@ -5478,12 +5541,14 @@ void Client::DisplayQuestRewards(Quest* quest, int64 coin, vector* reward if (rewarded_coin > coin) coin = rewarded_coin; if (!quest) { //this entire function is either for version <=546 or for quest rewards in middle of quest, so quest should be 0, otherwise quest will handle the rewards - player->AddCoins(coin); - PlaySound("coin_cha_ching"); + if (coin > 0) { + player->AddCoins(coin); + PlaySound("coin_cha_ching"); + } } packet2->setSubstructDataByName("reward_data", "unknown1", 255); packet2->setSubstructDataByName("reward_data", "reward", header); - packet2->setSubstructDataByName("reward_data", "coin", coin); + packet2->setSubstructDataByName("reward_data", "max_coin", coin); if (player->GetGuild()) { if (!quest) { //this entire function is either for version <=546 or for quest rewards in middle of quest, so quest should be 0, otherwise quest will handle the rewards player->GetInfoStruct()->status_points += status_points; @@ -5694,11 +5759,11 @@ void Client::GiveQuestReward(Quest* quest) { quest->IncrementCompleteCount(); player->AddCompletedQuest(quest); - + + DisplayQuestComplete(quest); LogWrite(CCLIENT__DEBUG, 0, "Client", "Send Quest Journal..."); SendQuestJournal(); player->RemoveQuest(quest->GetQuestID(), false); - DisplayQuestComplete(quest); if (quest->GetExpReward() > 0) { int16 level = player->GetLevel(); int32 xp = quest->GetExpReward(); @@ -8010,6 +8075,49 @@ void Client::SendIgnoreList() { } +void Client::AddWaypoint(string name, int8 type) { + waypoint_id++; + WaypointInfo info; + info.id = waypoint_id; + info.type = type; + waypoints[name] = info; +} + +void Client::SendWaypoints() { + PacketStruct* packet = configReader.getStruct("WS_WaypointUpdate", GetVersion()); + if (packet) { + packet->setArrayLengthByName("num_updates", waypoints.size()); + map::iterator itr; + int16 i = 0; + for (itr = waypoints.begin(); itr != waypoints.end(); itr++) { + packet->setArrayDataByName("waypoint_name", itr->first.c_str(), i); + packet->setArrayDataByName("waypoint_category", itr->second.type, i); + if(itr->second.type == 3) + packet->setArrayDataByName("spawn_id", 0xFFFFFFFF, i); + else + packet->setArrayDataByName("spawn_id", itr->second.id, i); + i++; + } + QueuePacket(packet->serialize()); + safe_delete(packet); + } +} + +void Client::SelectWaypoint(int32 id) { + string found_name = ""; + map::iterator itr; + for (itr = waypoints.begin(); itr != waypoints.end(); itr++) { + if (itr->second.id == id) { + found_name = itr->first; + break; + } + } + if (found_name.length() > 0) { + Spawn* spawn = current_zone->FindSpawn(player, found_name.c_str()); + ShowPathToTarget(spawn); + } +} + void Client::AddWaypoint(const char* waypoint_name, int8 waypoint_category, int32 spawn_id) { if (waypoint_name) { PacketStruct* packet = configReader.getStruct("WS_WaypointUpdate", GetVersion()); @@ -8024,7 +8132,37 @@ void Client::AddWaypoint(const char* waypoint_name, int8 waypoint_category, int3 safe_delete(packet); } } +} +void Client::ShowPathToTarget(Spawn* spawn) { + if (spawn && current_zone->pathing) { + bool partial = false; + bool stuck = false; + PathfinderOptions opts; + opts.smooth_path = true; + opts.step_size = 100.0f;//RuleR(Pathing, NavmeshStepSize); + opts.offset = spawn->GetYOffset() + 1.0f; + opts.flags = PathingNotDisabled ^ PathingZoneLine; + PacketStruct* packet = configReader.getStruct("WS_GlowPath", GetVersion()); + if (packet) { + auto path = current_zone->pathing->FindPath(glm::vec3(player->GetX(), player->GetZ(), player->GetY()), glm::vec3(spawn->GetX(), spawn->GetZ(), spawn->GetY()), partial, stuck, opts); + packet->setArrayLengthByName("num_points", path.size()); + int i = 0; + for (auto& node : path) + { + packet->setArrayDataByName("x", node.pos.x, i); + packet->setArrayDataByName("y", node.pos.z, i); + packet->setArrayDataByName("z", node.pos.y, i); + packet->setDataByName("waypoint_x", spawn->GetX()); + packet->setDataByName("waypoint_y", spawn->GetY()); + packet->setDataByName("waypoint_z", spawn->GetZ()); + i++; + } + if(i>0) + QueuePacket(packet->serialize()); + safe_delete(packet); + } + } } void Client::BeginWaypoint(const char* waypoint_name, float x, float y, float z) { @@ -9065,6 +9203,9 @@ bool Client::HandleNewLogin(int32 account_id, int32 access_code) GetCurrentZone()->AddClient(this); //add to zones client list zone_list.AddClientToMap(player->GetName(), this); + const char* zone_script = world.GetZoneScript(GetCurrentZone()->GetZoneID()); + if (zone_script && lua_interface) + lua_interface->RunZoneScript(zone_script, "new_client", GetCurrentZone(), GetPlayer()); } else { LogWrite(WORLD__ERROR, 0, "World", "Incompatible version: %i", version); diff --git a/EQ2/source/WorldServer/client.h b/EQ2/source/WorldServer/client.h index 88c01f14f..9b00b6313 100644 --- a/EQ2/source/WorldServer/client.h +++ b/EQ2/source/WorldServer/client.h @@ -134,6 +134,10 @@ struct IncomingPaperdollImage { int8 last_received_packet_index; int8 image_type; }; +struct WaypointInfo { + int32 id; + int8 type; +}; class Client { public: @@ -155,7 +159,7 @@ public: void HandleTellMessage(Client* from, const char* message); void SimpleMessage(int8 color, const char* message); void Message(int8 type, const char* message, ...); - void SendSpellUpdate(Spell* spell); + void SendSpellUpdate(Spell* spell, bool add_silently = false, bool add_to_hotbar = true); void Zone(ZoneServer* new_zone, bool set_coords = true); void Zone(const char* new_zone, bool set_coords = true); void Zone(int32 zoneid, bool set_coords = true); @@ -441,6 +445,18 @@ public: void SetRejoinGroupID(int32 id) { rejoin_group_id = id; } void TempRemoveGroup(); + + void SendWaypoints(); + + void AddWaypoint(string name, int8 type); + void RemoveWaypoint(string name) { + if (waypoints.count(name) > 0){ + waypoints.erase(name); + } + } + void SelectWaypoint(int32 id); + void ShowPathToTarget(Spawn* spawn); + private: void SavePlayerImages(); void SkillChanged(Skill* skill, int16 previous_value, int16 new_value); @@ -455,6 +471,8 @@ private: Mutex MQuestQueue; Mutex MDeletePlayer; vector* search_items; + int32 waypoint_id = 0; + map waypoints; Spawn* transport_spawn; Mutex MBuyBack; deque buy_back_items; diff --git a/EQ2/source/WorldServer/zoneserver.cpp b/EQ2/source/WorldServer/zoneserver.cpp index 7b61886e3..d848c966a 100644 --- a/EQ2/source/WorldServer/zoneserver.cpp +++ b/EQ2/source/WorldServer/zoneserver.cpp @@ -1328,6 +1328,7 @@ bool ZoneServer::Process() database.LoadTransporters(this); LogWrite(TRANSPORT__INFO, 0, "Transport", "-Loading Transporters complete!"); reloading = false; + world.RemoveReloadingSubSystem("Spawns"); } MSpawnGroupAssociation.writelock(__FUNCTION__, __LINE__); @@ -3160,14 +3161,14 @@ void ZoneServer::ClientProcess() } } -void ZoneServer::SimpleMessage(int8 type, const char* message, Spawn* from, float distance){ +void ZoneServer::SimpleMessage(int8 type, const char* message, Spawn* from, float distance, bool send_to_sender){ Client* client = 0; vector::iterator client_itr; MClientList.readlock(__FUNCTION__, __LINE__); for (client_itr = clients.begin(); client_itr != clients.end(); client_itr++) { client = *client_itr; - if(from && client && client->IsConnected() && from->GetDistance(client->GetPlayer()) <= distance){ + if(from && client && client->IsConnected() && (send_to_sender || from != client->GetPlayer()) && from->GetDistance(client->GetPlayer()) <= distance){ client->SimpleMessage(type, message); } } @@ -4856,6 +4857,23 @@ void ZoneServer::StartZoneInitialSpawnThread(Client* client){ } void ZoneServer::SendZoneSpawns(Client* client){ + int8 count = 0; + while (LoadingData && count <= 6000) { //sleep for max of 60 seconds (60000ms) while the maps are loading + count++; + Sleep(10); + } + count = 0; + int16 size = 0; + //give the spawn thread a tad bit of time to add the pending_spawns to spawn_list (up to 10 seconds) + while (count < 1000) { + MPendingSpawnListAdd.readlock(__FUNCTION__, __LINE__); + size = pending_spawn_list_add.size(); + MPendingSpawnListAdd.releasereadlock(__FUNCTION__, __LINE__); + if (size == 0) + break; + Sleep(10); + count++; + } initial_spawn_threads_active++; map::iterator itr; @@ -7492,6 +7510,7 @@ void ZoneServer::ReloadSpawns() { return; reloading = true; + world.SetReloadingSubsystem("Spawns"); // Let every one in the zone know what is happening HandleBroadcast("Reloading all spawns for this zone."); DeleteGlobalSpawns(); diff --git a/EQ2/source/WorldServer/zoneserver.h b/EQ2/source/WorldServer/zoneserver.h index 31e91fcd8..6c9daa9e9 100644 --- a/EQ2/source/WorldServer/zoneserver.h +++ b/EQ2/source/WorldServer/zoneserver.h @@ -275,7 +275,7 @@ public: void AddClient(Client* client); - void SimpleMessage(int8 type, const char* message, Spawn* from, float distance); + void SimpleMessage(int8 type, const char* message, Spawn* from, float distance, bool send_to_sender = true); void HandleChatMessage(Spawn* from, const char* to, int16 channel, const char* message, float distance = 0, const char* channel_name = 0, bool show_bubble = true, int32 language = 0); void HandleChatMessage(Client* client, Spawn* from, const char* to, int16 channel, const char* message, float distance = 0, const char* channel_name = 0, bool show_bubble = true, int32 language = 0); void HandleBroadcast(const char* message); diff --git a/EQ2/source/common/ConfigReader.cpp b/EQ2/source/common/ConfigReader.cpp index 5a0ce4e76..c487628bc 100644 --- a/EQ2/source/common/ConfigReader.cpp +++ b/EQ2/source/common/ConfigReader.cpp @@ -237,7 +237,9 @@ void ConfigReader::loadDataStruct(PacketStruct* packet, XMLNode parentNode, bool ds2->SetIfNotEqualsVariable(ds->GetIfNotEqualsVariable()); ds2->SetIfFlagNotSetVariable(ds->GetIfFlagNotSetVariable()); ds2->SetIfFlagSetVariable(ds->GetIfFlagSetVariable()); - ds2->SetIsOptional(ds->IsOptional()); + ds2->SetIsOptional(ds->IsOptional()); + ds2->AddIfSetVariable(if_variable); //add this if the modifier is on the piece that is including the substruct + ds2->AddIfNotSetVariable(if_not_variable); //add this if the modifier is on the piece that is including the substruct packet->add(ds2); } } diff --git a/EQ2/source/common/PacketStruct.cpp b/EQ2/source/common/PacketStruct.cpp index 9e1502629..bbdc9622a 100644 --- a/EQ2/source/common/PacketStruct.cpp +++ b/EQ2/source/common/PacketStruct.cpp @@ -351,6 +351,25 @@ int8 DataStruct::GetAddType() { void DataStruct::SetAddType(int8 new_type) { addType = new_type; } +string DataStruct::AppendVariable(string orig, const char* val) { + if (!val) + return orig; + if(orig.length() == 0) + return string(val); + if (orig.find(",") < 0xFFFFFFFF) { //has more than one already + string valstr = string(val); + vector* varnames = SplitString(orig, ','); + if (varnames) { + for (int32 i = 0; i < varnames->size(); i++) { + if (valstr.compare(varnames->at(i)) == 0) { + return orig; //already in the variable, no need to append + } + } + safe_delete(varnames); + } + } + return orig.append(",").append(val); +} int32 DataStruct::GetDataSizeInBytes() { int32 ret = 0; switch (type) { diff --git a/EQ2/source/common/PacketStruct.h b/EQ2/source/common/PacketStruct.h index 648e1cc35..55629d3af 100644 --- a/EQ2/source/common/PacketStruct.h +++ b/EQ2/source/common/PacketStruct.h @@ -113,6 +113,19 @@ public: bool IsSet(); bool IsOptional(); int32 GetDataSizeInBytes(); + string AppendVariable(string orig, const char* val); + void AddIfSetVariable(const char* val) { + if (val) { + if_set_variable = AppendVariable(if_set_variable, val); + is_set = true; + } + } + void AddIfNotSetVariable(const char* val) { + if (val) { + if_not_set_variable = AppendVariable(if_not_set_variable, val); + if_not_set = true; + } + } private: bool is_set; diff --git a/EQ2/source/common/misc.cpp b/EQ2/source/common/misc.cpp index 1fd401ca5..6f5daa020 100644 --- a/EQ2/source/common/misc.cpp +++ b/EQ2/source/common/misc.cpp @@ -283,6 +283,13 @@ bool alpha_check(unsigned char val){ return false; } +unsigned int GetSpellNameCrc(const char* src) { + if (!src) + return 0; + uLong crc = crc32(0L, Z_NULL, 0); + return crc32(crc, (unsigned const char*)src, strlen(src)); +} + int GetItemNameCrc(string item_name){ const char *src = item_name.c_str(); uLong crc = crc32(0L, Z_NULL, 0); diff --git a/EQ2/source/common/misc.h b/EQ2/source/common/misc.h index 5d6613079..4107eb906 100644 --- a/EQ2/source/common/misc.h +++ b/EQ2/source/common/misc.h @@ -59,6 +59,7 @@ string timestamp(time_t now=0); string long2ip(unsigned long ip); string pop_arg(string &s, string seps, bool obey_quotes); int EQsprintf(char *buffer, const char *pattern, const char *arg1, const char *arg2, const char *arg3, const char *arg4, const char *arg5, const char *arg6, const char *arg7, const char *arg8, const char *arg9); +unsigned int GetSpellNameCrc(const char* src); int GetItemNameCrc(string item_name); unsigned int GetNameCrc(string name); #endif diff --git a/server/SpawnStructs.xml b/server/SpawnStructs.xml index 0b226ed32..bb2b80232 100644 --- a/server/SpawnStructs.xml +++ b/server/SpawnStructs.xml @@ -520,7 +520,7 @@ - + diff --git a/server/WorldStructs.xml b/server/WorldStructs.xml index efa83b984..638efa8b6 100644 --- a/server/WorldStructs.xml +++ b/server/WorldStructs.xml @@ -407,6 +407,14 @@ to zero and treated like placeholders." /> + + + + + + + + @@ -422,6 +430,10 @@ to zero and treated like placeholders." /> + + + + @@ -433,7 +445,11 @@ to zero and treated like placeholders." /> - + + + + + @@ -459,8 +475,8 @@ to zero and treated like placeholders." /> - - + + @@ -501,6 +517,18 @@ to zero and treated like placeholders." /> + + + + + + + + + + + + @@ -1335,12 +1363,12 @@ to zero and treated like placeholders." /> + + - - @@ -3184,7 +3212,7 @@ to zero and treated like placeholders." /> - + @@ -3281,15 +3309,16 @@ to zero and treated like placeholders." /> - - - - - + + + + + + - - + + @@ -4721,6 +4750,14 @@ to zero and treated like placeholders." /> + + + + + + + + @@ -6092,6 +6129,10 @@ to zero and treated like placeholders." /> + + + + @@ -6661,7 +6702,7 @@ to zero and treated like placeholders." /> - + @@ -6911,7 +6952,7 @@ to zero and treated like placeholders." /> - + @@ -7147,7 +7188,7 @@ to zero and treated like placeholders." /> - + @@ -7175,7 +7216,7 @@ to zero and treated like placeholders." /> - + @@ -7514,6 +7555,14 @@ to zero and treated like placeholders." /> + + + + + + + + @@ -7524,6 +7573,9 @@ to zero and treated like placeholders." /> + + + @@ -7586,11 +7638,19 @@ to zero and treated like placeholders." /> - + + + - + + + + + + + @@ -7621,7 +7681,7 @@ to zero and treated like placeholders." /> - + @@ -18197,6 +18257,27 @@ to zero and treated like placeholders." /> + + + + + + + + + + + + + + + + + + + + +