diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index 3fe2430db..e9e7c5ef3 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -36,6 +36,7 @@ static const uint32 MAX_MERC_GRADES = 10; static const uint32 MAX_MERC_STANCES = 10; static const uint32 BLOCKED_BUFF_COUNT = 20; static const uint32 QUESTREWARD_COUNT = 8; +static const uint32 ADVANCED_LORE_LENGTH = 8192; /* @@ -2966,6 +2967,12 @@ struct ItemViewRequest_Struct { /*046*/ char unknown046[2]; }; +struct ItemAdvancedLoreText_Struct { + int32 item_id; + char item_name[64]; + char advanced_lore[ADVANCED_LORE_LENGTH]; +}; + struct LDONItemViewRequest_Struct { uint32 item_id; uint8 unknown004[4]; diff --git a/common/patches/rof2_structs.h b/common/patches/rof2_structs.h index cecafb767..10ce35654 100644 --- a/common/patches/rof2_structs.h +++ b/common/patches/rof2_structs.h @@ -4401,7 +4401,7 @@ struct SendAA_Struct { /*0104*/ uint32 special_category; /*0108*/ uint8 shroud; /*0109*/ uint8 unknown109; -/*0110*/ uint8 layonhands; // 1 for lay on hands -- doesn't seem to matter? +/*0110*/ uint8 reset_on_death; // timer is reset on death /*0111*/ uint8 unknown111; /*0112*/ uint32 total_abilities; /*0116*/ AA_Ability abilities[0]; diff --git a/common/patches/rof_structs.h b/common/patches/rof_structs.h index 7f466f09b..03f1a951a 100644 --- a/common/patches/rof_structs.h +++ b/common/patches/rof_structs.h @@ -4341,7 +4341,7 @@ struct SendAA_Struct { /*0104*/ uint32 special_category; /*0108*/ uint8 shroud; /*0109*/ uint8 unknown109; -/*0110*/ uint8 layonhands; // 1 for lay on hands -- doesn't seem to matter? +/*0110*/ uint8 reset_on_death; // timer is reset on death /*0111*/ uint8 unknown111; /*0112*/ uint32 total_abilities; /*0116*/ AA_Ability abilities[0]; diff --git a/common/patches/sod_structs.h b/common/patches/sod_structs.h index 0803432db..76a534e6a 100644 --- a/common/patches/sod_structs.h +++ b/common/patches/sod_structs.h @@ -3780,7 +3780,7 @@ struct SendAA_Struct { /*0092*/ uint32 special_category; /*0096*/ uint8 shroud; /*0097*/ uint8 unknown97; -/*0098*/ uint8 layonhands; // 1 for lay on hands -- doesn't seem to matter? +/*0098*/ uint8 reset_on_death; // timer is reset on death /*0099*/ uint8 unknown99; /*0100*/ uint32 total_abilities; /*0104*/ AA_Ability abilities[0]; diff --git a/common/patches/sof_structs.h b/common/patches/sof_structs.h index 0b175b643..1507630bd 100644 --- a/common/patches/sof_structs.h +++ b/common/patches/sof_structs.h @@ -3704,7 +3704,7 @@ struct SendAA_Struct { /*0088*/ uint32 aa_expansion; /*0092*/ uint32 special_category; /*0096*/ uint8 shroud; -/*0097*/ uint8 unknown97; +/*0097*/ uint8 reset_on_death; // timer is reset on death -- guess /*0098*/ uint32 total_abilities; /*0102*/ AA_Ability abilities[0]; }; diff --git a/common/patches/uf_structs.h b/common/patches/uf_structs.h index e165da794..b6eb414e1 100644 --- a/common/patches/uf_structs.h +++ b/common/patches/uf_structs.h @@ -3835,7 +3835,7 @@ struct SendAA_Struct { /*0092*/ uint32 special_category; /*0096*/ uint8 shroud; /*0097*/ uint8 unknown97; -/*0098*/ uint8 layonhands; // 1 for lay on hands -- doesn't seem to matter? +/*0098*/ uint8 reset_on_death; // timer is reset on death /*0099*/ uint8 unknown99; /*0100*/ uint32 total_abilities; /*0104*/ AA_Ability abilities[0]; diff --git a/common/ruletypes.h b/common/ruletypes.h index 8e5793bc3..b15d3fbf7 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -121,6 +121,7 @@ RULE_BOOL(Character, EnableAggroMeter, true, "Enable Aggro Meter, for users with RULE_BOOL(Character, KeepLevelOverMax, false, "Don't delevel a character that has somehow gone over the level cap") RULE_INT(Character, FoodLossPerUpdate, 32, "How much food/water you lose per stamina update") RULE_BOOL(Character, EnableHungerPenalties, false, "being hungry/thirsty has negative effects -- it does appear normal live servers do not have penalties") +RULE_BOOL(Character, EnableFoodRequirement, true, "if disabled, food is no longer required") RULE_INT(Character, BaseInstrumentSoftCap, 36, "Softcap for instrument mods, 36 commonly referred to as \"3.6\" as well") RULE_BOOL(Character, UseSpellFileSongCap, true, "When they removed the AA that increased the cap they removed the above and just use the spell field") RULE_INT(Character, BaseRunSpeedCap, 158, "Base Run Speed Cap, on live it's 158% which will give you a runspeed of 1.580 hard capped to 225") @@ -196,6 +197,7 @@ RULE_INT(Skills, SwimmingStartValue, 100, "") RULE_BOOL(Skills, TrainSenseHeading, false, "") RULE_INT(Skills, SenseHeadingStartValue, 200, "") RULE_BOOL(Skills, SelfLanguageLearning, true, "") +RULE_BOOL(Skills, RequireTomeHandin, false, "Disable click-to-learn and force turnin to Guild Master") RULE_CATEGORY_END() RULE_CATEGORY(Pets) diff --git a/common/version.h b/common/version.h index 13579c3d2..a950edbe0 100644 --- a/common/version.h +++ b/common/version.h @@ -34,7 +34,7 @@ * Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9149 +#define CURRENT_BINARY_DATABASE_VERSION 9150 #ifdef BOTS #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9026 diff --git a/utils/patches/patch_RoF.conf b/utils/patches/patch_RoF.conf index 9467ab337..31b091ff8 100644 --- a/utils/patches/patch_RoF.conf +++ b/utils/patches/patch_RoF.conf @@ -331,7 +331,7 @@ OP_LDoNButton=0x596e OP_SetStartCity=0x7936 # Was 0x2d1b OP_VoiceMacroIn=0x202e OP_VoiceMacroOut=0x3920 -OP_ItemViewUnknown=0x0b64 +OP_ItemAdvancedLoreText=0x0b64 OP_VetRewardsAvaliable=0x05d9 OP_VetClaimRequest=0xcdde OP_VetClaimReply=0x361b diff --git a/utils/patches/patch_RoF2.conf b/utils/patches/patch_RoF2.conf index f410a3193..a39fde999 100644 --- a/utils/patches/patch_RoF2.conf +++ b/utils/patches/patch_RoF2.conf @@ -448,6 +448,8 @@ OP_FinishWindow2=0x40ef OP_ItemVerifyRequest=0x189c OP_ItemVerifyReply=0x097b +OP_ItemAdvancedLoreText=0x023b + # merchant stuff OP_ShopPlayerSell=0x791b OP_ShopRequest=0x4fed diff --git a/utils/patches/patch_SoD.conf b/utils/patches/patch_SoD.conf index d836c6360..619f986c1 100644 --- a/utils/patches/patch_SoD.conf +++ b/utils/patches/patch_SoD.conf @@ -327,7 +327,7 @@ OP_LDoNButton=0x41b5 # C OP_SetStartCity=0x7bf6 # C OP_VoiceMacroIn=0x31b1 # C OP_VoiceMacroOut=0x7880 # C -OP_ItemViewUnknown=0x21c7 # C +OP_ItemAdvancedLoreText=0x21c7 # C OP_VetRewardsAvaliable=0x4e4e # C OP_VetClaimRequest=0x771f # C OP_VetClaimReply=0x2f95 # C diff --git a/utils/patches/patch_SoF.conf b/utils/patches/patch_SoF.conf index a10715918..6a5d41fb4 100644 --- a/utils/patches/patch_SoF.conf +++ b/utils/patches/patch_SoF.conf @@ -576,7 +576,7 @@ OP_QueryResponseThing=0x0000 # # realityincarnate: these are just here to stop annoying several thousand byte packet dumps OP_LoginUnknown1=0x22cf OP_LoginUnknown2=0x43ba -OP_ItemViewUnknown=0x4db4 +OP_ItemAdvancedLoreText=0x4db4 #Petition Opcodes OP_PetitionSearch=0x0000 #search term for petition diff --git a/utils/patches/patch_UF.conf b/utils/patches/patch_UF.conf index 71966dcbb..69d6bb1e5 100644 --- a/utils/patches/patch_UF.conf +++ b/utils/patches/patch_UF.conf @@ -336,7 +336,7 @@ OP_LDoNButton=0x1031 # C OP_SetStartCity=0x68f0 # C OP_VoiceMacroIn=0x1524 # C OP_VoiceMacroOut=0x1d99 # C -OP_ItemViewUnknown=0x4eb3 # C +OP_ItemAdvancedLoreText=0x4eb3 # C OP_VetRewardsAvaliable=0x0baa # C Mispelled? OP_VetClaimRequest=0x34f8 # C OP_VetClaimReply=0x6a5d # C diff --git a/utils/sql/db_update_manifest.txt b/utils/sql/db_update_manifest.txt index fdaa4a946..9d0aa9231 100644 --- a/utils/sql/db_update_manifest.txt +++ b/utils/sql/db_update_manifest.txt @@ -403,6 +403,7 @@ 9147|2020_01_24_grid_centerpoint_wp.sql|SHOW COLUMNS FROM `grid_entries` LIKE 'centerpoint'|empty| 9148|2020_01_28_corpse_guild_consent_id.sql|SHOW COLUMNS FROM `character_corpses` LIKE 'guild_consent_id'|empty| 9149|2020_02_06_globalloot.sql|SHOW COLUMNS FROM `global_loot` LIKE 'hot_zone'|empty| +9150|2020_02_06_aa_reset_on_death.sql|SHOW COLUMNS FROM `aa_ability` LIKE 'reset_on_death'|empty| # Upgrade conditions: # This won't be needed after this system is implemented, but it is used database that are not diff --git a/utils/sql/git/required/2020_02_06_aa_reset_on_death.sql b/utils/sql/git/required/2020_02_06_aa_reset_on_death.sql new file mode 100644 index 000000000..91edb5d4a --- /dev/null +++ b/utils/sql/git/required/2020_02_06_aa_reset_on_death.sql @@ -0,0 +1,2 @@ +ALTER TABLE `aa_ability` ADD `reset_on_death` TINYINT(4) NOT NULL DEFAULT '0'; +UPDATE `aa_ability` SET `reset_on_death` = '1' WHERE `id` = 6001; diff --git a/zone/aa.cpp b/zone/aa.cpp index 1383ad2f9..1ccc6f656 100644 --- a/zone/aa.cpp +++ b/zone/aa.cpp @@ -1023,6 +1023,24 @@ void Client::ResetAlternateAdvancementTimers() { safe_delete(outapp); } +void Client::ResetOnDeathAlternateAdvancement() { + for (const auto &aa : aa_ranks) { + auto ability_rank = zone->GetAlternateAdvancementAbilityAndRank(aa.first, aa.second.first); + auto ability = ability_rank.first; + auto rank = ability_rank.second; + + if (!ability) + continue; + + if (!rank) + continue; + + // since they're dying, we just need to clear the DB + if (ability->reset_on_death) + p_timers.Clear(&database, rank->spell_type + pTimerAAStart); + } +} + void Client::PurchaseAlternateAdvancementRank(int rank_id) { AA::Rank *rank = zone->GetAlternateAdvancementRank(rank_id); if(!rank) { @@ -1646,7 +1664,7 @@ bool ZoneDatabase::LoadAlternateAdvancementAbilities(std::unordered_maptype = atoi(row[8]); ability->charges = atoi(row[9]); ability->grant_only = atoi(row[10]) != 0 ? true : false; - ability->first_rank_id = atoi(row[11]); + ability->reset_on_death = atoi(row[11]) != 0 ? true : false; + ability->first_rank_id = atoi(row[12]); ability->first = nullptr; abilities[ability->id] = std::unique_ptr(ability); diff --git a/zone/aa_ability.h b/zone/aa_ability.h index 0d6a240c4..5ec08d986 100644 --- a/zone/aa_ability.h +++ b/zone/aa_ability.h @@ -50,6 +50,7 @@ public: int drakkin_heritage; int status; bool grant_only; + bool reset_on_death; int type; int charges; int first_rank_id; diff --git a/zone/attack.cpp b/zone/attack.cpp index 083176e39..4da160608 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -1852,6 +1852,17 @@ bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, EQEmu::skills::Sk BuffFadeDetrimental(); } + /* + Reset AA reuse timers that need to be, live-like this is only Lay on Hands + */ + ResetOnDeathAlternateAdvancement(); + + /* + Reset reuse timer for classic skill based Lay on Hands (For tit I guess) + */ + if (GetClass() == PALADIN) // we could check if it's not expired I guess, but should be fine not to + p_timers.Clear(&database, pTimerLayHands); + /* Finally, send em home diff --git a/zone/client.h b/zone/client.h index a0655584b..52af842be 100644 --- a/zone/client.h +++ b/zone/client.h @@ -838,6 +838,7 @@ public: void SendAlternateAdvancementTimers(); void ResetAlternateAdvancementTimer(int ability); void ResetAlternateAdvancementTimers(); + void ResetOnDeathAlternateAdvancement(); void SetAAPoints(uint32 points) { m_pp.aapoints = points; SendAlternateAdvancementStats(); } void AddAAPoints(uint32 points) { m_pp.aapoints += points; SendAlternateAdvancementStats(); } @@ -900,14 +901,14 @@ public: void BreakFeignDeathWhenCastOn(bool IsResisted); void LeaveGroup(); - bool Hungry() const {if (GetGM()) return false; return m_pp.hunger_level <= 3000;} - bool Thirsty() const {if (GetGM()) return false; return m_pp.thirst_level <= 3000;} + bool Hungry() const {if (GetGM() || !RuleB(Character, EnableFoodRequirement)) return false; return m_pp.hunger_level <= 3000;} + bool Thirsty() const {if (GetGM() || !RuleB(Character, EnableFoodRequirement)) return false; return m_pp.thirst_level <= 3000;} int32 GetHunger() const { return m_pp.hunger_level; } int32 GetThirst() const { return m_pp.thirst_level; } void SetHunger(int32 in_hunger); void SetThirst(int32 in_thirst); void SetConsumption(int32 in_hunger, int32 in_thirst); - bool IsStarved() const { if (GetGM() || !RuleB(Character, EnableHungerPenalties)) return false; return m_pp.hunger_level == 0 || m_pp.thirst_level == 0; } + bool IsStarved() const { if (GetGM() || !RuleB(Character, EnableFoodRequirement) || !RuleB(Character, EnableHungerPenalties)) return false; return m_pp.hunger_level == 0 || m_pp.thirst_level == 0; } bool CheckTradeLoreConflict(Client* other); bool CheckTradeNonDroppable(); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 0ea11e8ae..7173dd342 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -8653,7 +8653,7 @@ void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app) } else if (inst->IsClassCommon()) { - if (item->ItemType == EQEmu::item::ItemTypeSpell && (strstr((const char*)item->Name, "Tome of ") || strstr((const char*)item->Name, "Skill: "))) + if (!RuleB(Skills, RequireTomeHandin) && item->ItemType == EQEmu::item::ItemTypeSpell && (strstr((const char*)item->Name, "Tome of ") || strstr((const char*)item->Name, "Skill: "))) { DeleteItemInInventory(slot_id, 1, true); TrainDiscipline(item->ID); diff --git a/zone/spells.cpp b/zone/spells.cpp index a1e926a1c..5b8ee33f8 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -1230,7 +1230,7 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo // handle the components for traditional casters else { - if (!RuleB(Character, PetsUseReagents) && IsEffectInSpell(spell_id, SE_SummonPet)) { + if (!RuleB(Character, PetsUseReagents) && (IsEffectInSpell(spell_id, SE_SummonPet) || IsEffectInSpell(spell_id, SE_NecPet))) { //bypass reagent cost } else if(c->GetInv().HasItem(component, component_count, invWhereWorn|invWherePersonal) == -1) // item not found @@ -1263,7 +1263,7 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo return; } } - else if (!RuleB(Character, PetsUseReagents) && IsEffectInSpell(spell_id, SE_SummonPet)) { + else if (!RuleB(Character, PetsUseReagents) && (IsEffectInSpell(spell_id, SE_SummonPet) || IsEffectInSpell(spell_id, SE_NecPet))) { //bypass reagent cost } else if (!bard_song_mode)