From 8f18974af4d3eced0b9930182fc34ebc39ed342d Mon Sep 17 00:00:00 2001 From: EpicTaru Date: Fri, 13 Oct 2023 13:34:38 -0500 Subject: [PATCH 001/103] [core] Map>Utils>Various A thru C files: Russian to English --- src/map/utils/attackutils.cpp | 21 +---- src/map/utils/battleutils.cpp | 142 +++++++++++++++++----------------- src/map/utils/charutils.cpp | 69 ++++++++--------- src/map/utils/charutils.h | 96 +++++++++++------------ 4 files changed, 154 insertions(+), 174 deletions(-) diff --git a/src/map/utils/attackutils.cpp b/src/map/utils/attackutils.cpp index 519866d1536..158f7e5049d 100644 --- a/src/map/utils/attackutils.cpp +++ b/src/map/utils/attackutils.cpp @@ -30,7 +30,7 @@ namespace attackutils { /************************************************************************ * * - * Multihit calculator. * + * Multihit calculator. * * * ************************************************************************/ uint8 getHitCount(uint8 hits) @@ -227,14 +227,9 @@ namespace attackutils } break; } - return std::min(num, 8); // не более восьми ударов за одну атаку + return std::min(num, 8); // no more than eight hits per attack } - /************************************************************************ - * * - * Is parried. * - * * - ************************************************************************/ bool IsParried(CBattleEntity* PAttacker, CBattleEntity* PDefender) { if (facing(PDefender->loc.p, PAttacker->loc.p, 64)) @@ -244,11 +239,6 @@ namespace attackutils return false; } - /************************************************************************ - * * - * Is guarded. * - * * - ************************************************************************/ bool IsGuarded(CBattleEntity* PAttacker, CBattleEntity* PDefender) { if (facing(PDefender->loc.p, PAttacker->loc.p, 64)) @@ -258,11 +248,6 @@ namespace attackutils return false; } - /************************************************************************ - * * - * Is blocked. * - * * - ************************************************************************/ bool IsBlocked(CBattleEntity* PAttacker, CBattleEntity* PDefender) { if (facing(PDefender->loc.p, PAttacker->loc.p, 64) && !PDefender->StatusEffectContainer->HasPreventActionEffect()) @@ -274,7 +259,7 @@ namespace attackutils /************************************************************************ * * - * Handles damage multiplier, relic weapons etc. * + * Check for damage multiplier, relic weapons etc. * * * ************************************************************************/ uint32 CheckForDamageMultiplier(CCharEntity* PChar, CItemWeapon* PWeapon, uint32 damage, PHYSICAL_ATTACK_TYPE attackType, uint8 weaponSlot) diff --git a/src/map/utils/battleutils.cpp b/src/map/utils/battleutils.cpp index 420c0ae98b0..d16c7313506 100644 --- a/src/map/utils/battleutils.cpp +++ b/src/map/utils/battleutils.cpp @@ -77,7 +77,9 @@ #include "zoneutils.h" /************************************************************************ - * lists used in battleutils * + * * + * Lists used in battleutils * + * * ************************************************************************/ std::array, 100> g_SkillTable; @@ -91,17 +93,8 @@ std::unordered_map g_PPetSkillList; // List of pet std::array, MAX_SKILLTYPE> g_PWeaponSkillsList; std::unordered_map> g_PMobSkillLists; // List of mob skills defined from mob_skill_lists.sql -/************************************************************************ - * battleutils * - ************************************************************************/ - namespace battleutils { - /************************************************************************ - * * - * * - * * - ************************************************************************/ const float worldAngleMinDistance = 0.5f; const uint8 worldAngleMaxDeviance = 4; @@ -147,7 +140,9 @@ namespace battleutils } /************************************************************************ - * Load Weapon Skills from database * + * * + * Load Skills List * + * * ************************************************************************/ void LoadWeaponSkillsList() @@ -190,12 +185,6 @@ namespace battleutils } } - /************************************************************************ - * * - * Load Mob Skills from database * - * * - ************************************************************************/ - void LoadMobSkillsList() { // Load all mob skills @@ -250,12 +239,6 @@ namespace battleutils } } - /************************************************************************ - * * - * Load Pet Skills from database * - * * - ************************************************************************/ - void LoadPetSkillsList() { // Load all pet skills @@ -315,7 +298,9 @@ namespace battleutils } /************************************************************************ - * Clear Weapon Skills List * + * * + * Clear Up (Free) Skills List * + * * ************************************************************************/ void FreeWeaponSkillsList() @@ -326,9 +311,6 @@ namespace battleutils } } - /************************************************************************ - * Clear Mob Skills List * - ************************************************************************/ void FreeMobSkillList() { for (auto& mobskill : g_PMobSkillList) @@ -337,9 +319,6 @@ namespace battleutils } } - /************************************************************************ - * Clear Pet Skills List * - ************************************************************************/ void FreePetSkillList() { for (auto& petskill : g_PPetSkillList) @@ -350,7 +329,9 @@ namespace battleutils } /************************************************************************ - * Get Skill Rank By SkillId and JobId * + * * + * Get Skill Rank By SkillId and JobId * + * * ************************************************************************/ uint8 GetSkillRank(SKILLTYPE SkillID, JOBTYPE JobID) @@ -359,7 +340,9 @@ namespace battleutils } /************************************************************************ - * Return Max Skill by SkillType, JobType, and level * + * * + * Return Max Skill by SkillType, JobType, and Level * + * * ************************************************************************/ uint16 GetMaxSkill(SKILLTYPE SkillID, JOBTYPE JobID, uint8 level) @@ -517,7 +500,7 @@ namespace battleutils /************************************************************************ * * - * Get Mob Skills by list id * + * Get Mob Skills by List Id * * * ************************************************************************/ @@ -755,7 +738,7 @@ namespace battleutils /************************************************************************ * * - * Calculates Spike Damage * + * Calculate Spike Damage * * * ************************************************************************/ @@ -1105,7 +1088,7 @@ namespace battleutils /************************************************************************ * * - * Handles Enspell effect and damage * + * Handle Enspell effect and damage * * * ************************************************************************/ @@ -1489,7 +1472,7 @@ namespace battleutils /************************************************************************ * * - * Handles Ranged weapon's additional effects (e.g. Bolts) * + * Handle Ranged weapon's additional effects (e.g. Bolts) * * * ************************************************************************/ @@ -1784,18 +1767,21 @@ namespace battleutils return false; } - /*********************************************************************** - Calculates the block rate of the defender - Incorporates testing and data from: - http://www.ffxiah.com/forum/topic/21671/paladin-faq-info-and-trade-studies/34/#2581818 - https://docs.google.com/spreadsheet/ccc?key=0AkX3maplDraRdFdCZHI2OU93aVgtWlZhN3ozZEtnakE#gid=0 - http://www.ffxionline.com/forums/paladin/55139-shield-data-size-2-vs-size-3-a.html - https://www.bg-wiki.com/ffxi/Shield_Skill - Base calculations - does not floor - https://www.ffxiah.com/forum/topic/53625/make-paladin-great-again/6/#3434884 - Palisade +base block rate + /************************************************************************************************************* + * * + * Calculate the block rate of the defender * + * Incorporates testing and data from: * + * http://www.ffxiah.com/forum/topic/21671/paladin-faq-info-and-trade-studies/34/#2581818 * + * https://docs.google.com/spreadsheet/ccc?key=0AkX3maplDraRdFdCZHI2OU93aVgtWlZhN3ozZEtnakE#gid=0 * + * http://www.ffxionline.com/forums/paladin/55139-shield-data-size-2-vs-size-3-a.html * + * https://www.bg-wiki.com/ffxi/Shield_Skill - Base calculations - does not floor * + * https://www.ffxiah.com/forum/topic/53625/make-paladin-great-again/6/#3434884 - Palisade +base block rate * + * * + * Base block rates are (small to large shield type) 55% -> 50% -> 45% -> 30% * + * Aegis is a special case, having the base block rate of a size 2 type. * + * * + *************************************************************************************************************/ - Base block rates are (small to large shield type) 55% -> 50% -> 45% -> 30% - Aegis is a special case, having the base block rate of a size 2 type. - ************************************************************************/ float GetBlockRate(CBattleEntity* PAttacker, CBattleEntity* PDefender) { float base = 0; @@ -2034,7 +2020,7 @@ namespace battleutils /************************************************************************ * * - * Calculates damage based on damage and resistance to damage type * + * Calculate damage based on damage and resistance to damage type * * * ************************************************************************/ @@ -2348,7 +2334,7 @@ namespace battleutils /************************************************************************ * * - * Handles Damage from Weaponskills (dmg type reductions calced in lua) * + * Handle Damage from Weaponskills (dmg type reductions calced in lua) * * * ************************************************************************/ @@ -2501,7 +2487,7 @@ namespace battleutils /************************************************************************ * * - * Handles Damage from Spells (dmg type reductions calced in lua) * + * Handle Damage from Spells (dmg type reductions calced in lua) * * * ************************************************************************/ @@ -2534,7 +2520,7 @@ namespace battleutils /************************************************************************ * * - * Handles Damage from Swipe/Lunge (dmg type reductions calced in lua) * + * Handle Damage from Swipe/Lunge (dmg type reductions calced in lua) * * * ************************************************************************/ @@ -2557,7 +2543,7 @@ namespace battleutils /************************************************************************ * * - * Calculate Probability attack will hit (20% min cap - 95~99% max cap) * + * Calculate Probability attack will hit (20% min - 95~99% max cap) * * attackNumber: 0=main, 1=sub, 2=kick * * * ************************************************************************/ @@ -2698,7 +2684,7 @@ namespace battleutils /************************************************************************ * * - * Crit Rate * + * Calculate Crit Hit Rate * * * ************************************************************************/ @@ -2816,7 +2802,7 @@ namespace battleutils /************************************************************************ * * - * Ranged Crit Rate * + * Calculate Ranged Crit Hit Rate * * * ************************************************************************/ @@ -3086,7 +3072,9 @@ namespace battleutils } /************************************************************************ + * * * Formula for Strength * + * * ************************************************************************/ int32 GetFSTR(CBattleEntity* PAttacker, CBattleEntity* PDefender, uint8 SlotID) @@ -3397,6 +3385,7 @@ namespace battleutils * * * Returns the number of hits for multihit weapons if applicable * * (Keeping this for backwards compatibility with the old system) * + * * ************************************************************************/ uint8 CheckMultiHits(CBattleEntity* PEntity, CItemWeapon* PWeapon) @@ -3479,7 +3468,7 @@ namespace battleutils /************************************************************************ * * - * * + * Chance Shadows (Blink/Utsusemi) will proc * * * ************************************************************************/ @@ -3630,7 +3619,7 @@ namespace battleutils /************************************************************************ * * - * Gets SkillChain Effect * + * Get SkillChain Effect * * * ************************************************************************/ #define PAIR(x, y) (((x) << 8) + (y)) @@ -4153,7 +4142,8 @@ namespace battleutils /************************************************************************ * * - * Для всех сущностей, за исключением персонажей, по умолчанию true * + * Handle NIN tool usage * + * (for all entities except characters, default to true) * * * ************************************************************************/ @@ -4382,7 +4372,7 @@ namespace battleutils /************************************************************************ * * - * Adds enmity to PSource for all the MOB targets who have * + * Add enmity to PSource for all the MOB targets who have * * PTarget on their enmity list. * * * ************************************************************************/ @@ -4446,7 +4436,7 @@ namespace battleutils /************************************************************************ * * - * Transfer Enmity (used with ACCOMPLICE & COLLABORATOR ability type) * + * Transfer Enmity (used with ACCOMPLICE & COLLABORATOR ability type) * * * ************************************************************************/ @@ -4463,9 +4453,10 @@ namespace battleutils /************************************************************************ * * - * Effect from soul eater * + * Handle Soul Eater effect * * * ************************************************************************/ + uint16 doSoulEaterEffect(CCharEntity* m_PChar, uint32 damage) { if (m_PChar->StatusEffectContainer->HasStatusEffect(EFFECT_SOULEATER)) @@ -4506,9 +4497,10 @@ namespace battleutils /************************************************************************ * * - * Samurai get merit storeTP value * + * Calculate Samurai Store TP value (from merit) * * * ************************************************************************/ + uint8 getStoreTPbonusFromMerit(CBattleEntity* PEntity) { if (PEntity->objtype == TYPE_PC) @@ -4523,9 +4515,10 @@ namespace battleutils /************************************************************************ * * - * Samurai overwhelm damage bonus * + * Calculate Samurai Overwhelm damage bonus * * * ************************************************************************/ + int32 getOverWhelmDamageBonus(CCharEntity* m_PChar, CBattleEntity* PDefender, int32 damage) { if (m_PChar->objtype == TYPE_PC) // Some mobskills use TakeWeaponskillDamage function, which calls upon this one. @@ -4564,7 +4557,7 @@ namespace battleutils /************************************************************************ * * - * get barrage shot count * + * Calculate/Handle Barrage shot count * * * ************************************************************************/ @@ -4640,7 +4633,7 @@ namespace battleutils /************************************************************************ * * - * Jump DRG Job ability * + * Calculate DRG Jump ability total damage * * * ************************************************************************/ @@ -4803,7 +4796,7 @@ namespace battleutils /************************************************************************ * * - * Entity charms another * + * Calculate BST Charm duration * * * ************************************************************************/ @@ -4977,7 +4970,7 @@ namespace battleutils /************************************************************************ * * - * Returns the percentage chance that one entity has to charm another. * + * Return the percentage chance that one entity has to charm another. * * * ************************************************************************/ @@ -5086,7 +5079,7 @@ namespace battleutils /************************************************************************ * * - * calculate if charm is successful * + * Calculate Charm success Rate * * * ************************************************************************/ @@ -5645,9 +5638,10 @@ namespace battleutils /************************************************************************ * * - * handle the /assist command * + * Handle the /assist command * * * ************************************************************************/ + void assistTarget(CCharEntity* PChar, uint16 TargID) { // get the entity we want to assist @@ -6022,9 +6016,10 @@ namespace battleutils /************************************************************************ * * - * Does the wild card effect to a specific character * + * Add the COR Wild Card effect to a specific character * * * ************************************************************************/ + void DoWildCardToEntity(CCharEntity* PCaster, CCharEntity* PTarget, uint8 roll) { auto TotalRecasts = PTarget->PRecastContainer->GetRecastList(RECAST_ABILITY)->size(); @@ -6117,9 +6112,10 @@ namespace battleutils /************************************************************************ * * - * Get the Snapshot shot time reduction * + * Get the Snapshot shot time reduction * * * ************************************************************************/ + int16 GetSnapshotReduction(CBattleEntity* battleEntity, int16 delay) { auto SnapShotReductionPercent{ battleEntity->getMod(Mod::SNAP_SHOT) }; @@ -6143,9 +6139,10 @@ namespace battleutils /************************************************************************ * * - * Get any ranged attack bonuses here * + * Get any ranged attack bonuses here * * * ************************************************************************/ + int32 GetRangedAttackBonuses(CBattleEntity* battleEntity) { if (battleEntity->objtype != TYPE_PC) @@ -6166,9 +6163,10 @@ namespace battleutils /************************************************************************ * * - * Get any ranged accuracy bonuses here * + * Get any ranged accuracy bonuses here * * * ************************************************************************/ + int32 GetRangedAccuracyBonuses(CBattleEntity* battleEntity) { if (battleEntity->objtype != TYPE_PC) diff --git a/src/map/utils/charutils.cpp b/src/map/utils/charutils.cpp index 03b34d60027..d4d2e14ad2b 100644 --- a/src/map/utils/charutils.cpp +++ b/src/map/utils/charutils.cpp @@ -115,12 +115,6 @@ static constexpr int32 ExpTableRowCount = 60; std::array, ExpTableRowCount> g_ExpTable; std::array g_ExpPerLevel; -/************************************************************************ - * * - * * - * * - ************************************************************************/ - namespace charutils { /************************************************************************ @@ -190,7 +184,7 @@ namespace charutils int32 subLevelOver10 = std::clamp(slvl - 10, 0, 20); // + 1HP for each level after 10 (/ 2) int32 subLevelOver30 = (slvl < 30 ? 0 : slvl - 30); // + 1HP for each level after 30 - // Расчет Racestat Jobstat Bonusstat Sjobstat + // Calculate Racestat Jobstat Bonusstat Sjobstat // Calculation of race grade = grade::GetRaceGrades(race, 0); @@ -998,7 +992,7 @@ namespace charutils if (PItem->isType(ITEM_FURNISHING) && (PItem->getLocationID() == LOC_MOGSAFE || PItem->getLocationID() == LOC_MOGSAFE2)) { - if (((CItemFurnishing*)PItem)->isInstalled()) // способ узнать, что предмет действительно установлен + if (((CItemFurnishing*)PItem)->isInstalled()) // Check if furniture (furnishing) item is actually installed { PChar->getStorage(LOC_STORAGE)->AddBuff(((CItemFurnishing*)PItem)->getStorage()); } @@ -1147,7 +1141,7 @@ namespace charutils /************************************************************************ * * - * We send a list of current / completed quests and missions. * + * Send lists of current / completed quests and missions. * * * ************************************************************************/ @@ -1200,7 +1194,7 @@ namespace charutils /************************************************************************ * * - * We send the character all its inventory * + * Send the character all its inventory * * * ************************************************************************/ @@ -1611,7 +1605,7 @@ namespace charutils /************************************************************************ * * - * Check the possibility of trade between the characters * + * Check the possibility of trade between characters * * * ************************************************************************/ @@ -1676,8 +1670,8 @@ namespace charutils /************************************************************************ * * - * Remove from the character equipped item without updating the external * - * species. Used as an auxiliary function in a bundle with others * + * Remove equipped item from character without updating the external * + * species (used as an auxiliary function in a bundle with others) * * * ************************************************************************/ @@ -1875,7 +1869,7 @@ namespace charutils /************************************************************************ * * - * We are trying to equip the subject in compliance with all conditions * + * Try to equip the subject in compliance with all conditions * * * ************************************************************************/ @@ -2685,7 +2679,7 @@ namespace charutils /************************************************************************ * * - * Check the feature of the character wearing the items equipped on it * + * Check the feature of the character wearing the items equipped on it * * * ************************************************************************/ @@ -2926,7 +2920,7 @@ namespace charutils /************************************************************************ * * * Collect the work table of the character's abilities.With zero level * - * There must be 2H abilities.On this condition, sift them for SJOB * + * There must be 2H abilities .On this condition, sift them for SJOB * * * ************************************************************************/ @@ -3023,7 +3017,7 @@ namespace charutils /************************************************************************ * * * Collect the work table of the character skills based on real. * - * Add restrictions, note the skills of the main profession (rank! = 0) * + * Add restrictions, note the skills of the main job (rank! = 0) * * * ************************************************************************/ @@ -3236,7 +3230,7 @@ namespace charutils /************************************************************************ * * - * Пытаемся увеличить значение умения * + * Try to increase the value of the skill * * * ************************************************************************/ @@ -3311,7 +3305,7 @@ namespace charutils uint8 SkillAmount = 1; uint8 tier = std::min(1 + (Diff / 5), 5); - for (uint8 i = 0; i < 4; ++i) // 1 + 4 возможных дополнительных (максимум 5) + for (uint8 i = 0; i < 4; ++i) // 1 + 4 possible additional ones (maximum 5) { random = xirand::GetRandomNumber(1.); @@ -3512,7 +3506,7 @@ namespace charutils /************************************************************************ * * - * Методы для работы с заклинаниями * + * Methods for working with spells * * * ************************************************************************/ @@ -3652,7 +3646,7 @@ namespace charutils /************************************************************************ * * - * Методы для работы с основными способностями * + * Methods for working with basic abilities * * * ************************************************************************/ @@ -3729,10 +3723,11 @@ namespace charutils } /************************************************************************ - * - * Pet Command Functions - * - *************************************************************************/ + * * + * Pet Command Functions * + * * + ************************************************************************/ + int32 hasPetAbility(CCharEntity* PChar, uint16 AbilityID) { return hasBit(AbilityID, PChar->m_PetCommands, sizeof(PChar->m_PetCommands)); @@ -3750,7 +3745,7 @@ namespace charutils /************************************************************************ * * - * Инициализируем таблицу опыта * + * Initialize the experience (exp) table * * * ************************************************************************/ @@ -3794,9 +3789,10 @@ namespace charutils /************************************************************************ * * - * Returns mob difficulty according to level difference * + * Return mob difficulty according to level difference * * * ************************************************************************/ + EMobDifficulty CheckMob(uint8 charlvl, uint8 moblvl) { uint32 baseExp = GetBaseExp(charlvl, moblvl); @@ -3853,7 +3849,7 @@ namespace charutils /************************************************************************ * * - * Узнаем количество опыта, необходимое для получения следующего уровня * + * Calculate the amount of experience required to get the next level * * * ************************************************************************/ @@ -4603,6 +4599,7 @@ namespace charutils * 1 means no exp loss. A value of 0 means full exp loss. * * * ************************************************************************/ + void DelExperiencePoints(CCharEntity* PChar, float retainPercent, uint16 forcedXpLoss) { TracyZoneScoped; @@ -4714,7 +4711,7 @@ namespace charutils /************************************************************************ * * - * Добавляем очки опытка указанному персонажу * + * Add experience points to the specified character * * * ************************************************************************/ @@ -4943,7 +4940,7 @@ namespace charutils /************************************************************************ * * - * Establish a restriction of character level * + * Establish a restriction of character level * * * ************************************************************************/ @@ -5132,7 +5129,7 @@ namespace charutils /************************************************************************ * * - * Cохраняем список колючевых предметов * + * Save list of key items * * * ************************************************************************/ @@ -5361,7 +5358,7 @@ namespace charutils /************************************************************************ * * - * Saves character nation changes * + * Save character's nation changes * * * ************************************************************************/ @@ -5378,7 +5375,7 @@ namespace charutils /************************************************************************ * * - * Saves characters current campaign allegiance * + * Save character's current campaign allegiance * * * ************************************************************************/ @@ -5395,7 +5392,7 @@ namespace charutils /************************************************************************ * * - * Saves character's current moghancement + * Saves character's current moghancement * * * ************************************************************************/ @@ -5412,7 +5409,7 @@ namespace charutils /************************************************************************ * * - * Сохраняем текущие уровни профессий персонажа * + * Save the current levels of the character's jobs * * * ************************************************************************/ @@ -5614,7 +5611,7 @@ namespace charutils /************************************************************************ * * - * Save Teleports - Homepoints, outposts, maws, etc * + * Save Teleports - (homepoints, outposts, maws, etc) * * * ************************************************************************/ diff --git a/src/map/utils/charutils.h b/src/map/utils/charutils.h index 3231a67cf76..6380177b91a 100644 --- a/src/map/utils/charutils.h +++ b/src/map/utils/charutils.h @@ -131,27 +131,27 @@ namespace charutils void AddItemToRecycleBin(CCharEntity* PChar, uint32 container, uint8 slotID, uint8 quantity); void EmptyRecycleBin(CCharEntity* PChar); - bool hasKeyItem(CCharEntity* PChar, uint16 KeyItemID); // проверяем наличие ключевого предмета - bool seenKeyItem(CCharEntity* PChar, uint16 KeyItemID); // проверяем, было ли описание ключевого предмета прочитано - void unseenKeyItem(CCharEntity* PChar, uint16 KeyItemID); // Attempt to remove keyitem from seen list - void addKeyItem(CCharEntity* PChar, uint16 KeyItemID); // добавляем ключевой предмет - void delKeyItem(CCharEntity* PChar, uint16 KeyItemID); // улаляем ключевой предмет + bool hasKeyItem(CCharEntity* PChar, uint16 KeyItemID); // checking the presence of a key item + bool seenKeyItem(CCharEntity* PChar, uint16 KeyItemID); // checking whether the description of the key item has been read + void unseenKeyItem(CCharEntity* PChar, uint16 KeyItemID); // attempt to remove keyitem from seen list + void addKeyItem(CCharEntity* PChar, uint16 KeyItemID); // add a key item + void delKeyItem(CCharEntity* PChar, uint16 KeyItemID); // delete a key item - int32 hasSpell(CCharEntity* PChar, uint16 SpellID); // проверяем наличие заклинания - int32 addSpell(CCharEntity* PChar, uint16 SpellID); // добавляем заклинание - int32 delSpell(CCharEntity* PChar, uint16 SpellID); // улаляем заклинание + int32 hasSpell(CCharEntity* PChar, uint16 SpellID); // checking for the presence of a spell + int32 addSpell(CCharEntity* PChar, uint16 SpellID); // add a spell + int32 delSpell(CCharEntity* PChar, uint16 SpellID); // delete a spell - int32 hasLearnedAbility(CCharEntity* PChar, uint16 AbilityID); // проверяем наличие заклинания - int32 addLearnedAbility(CCharEntity* PChar, uint16 AbilityID); // добавляем заклинание - int32 delLearnedAbility(CCharEntity* PChar, uint16 AbilityID); // улаляем заклинание + int32 hasLearnedAbility(CCharEntity* PChar, uint16 AbilityID); // checking for the presence of a learned ability + int32 addLearnedAbility(CCharEntity* PChar, uint16 AbilityID); // add a learned ability + int32 delLearnedAbility(CCharEntity* PChar, uint16 AbilityID); // delete a learned ability bool hasLearnedWeaponskill(CCharEntity* PChar, uint8 wsUnlockId); void addLearnedWeaponskill(CCharEntity* PChar, uint8 wsUnlockId); void delLearnedWeaponskill(CCharEntity* PChar, uint8 wsUnlockId); - int32 hasAbility(CCharEntity* PChar, uint16 AbilityID); // проверяем наличие ключевого предмета - int32 addAbility(CCharEntity* PChar, uint16 AbilityID); // добавляем ключевой предмет - int32 delAbility(CCharEntity* PChar, uint16 AbilityID); // улаляем ключевой предмет + int32 hasAbility(CCharEntity* PChar, uint16 AbilityID); // checking the presence of an ability + int32 addAbility(CCharEntity* PChar, uint16 AbilityID); // add an ability + int32 delAbility(CCharEntity* PChar, uint16 AbilityID); // delete an ability int32 hasTitle(CCharEntity* PChar, uint16 Title); int32 addTitle(CCharEntity* PChar, uint16 Title); @@ -170,40 +170,40 @@ namespace charutils int32 hasWeaponSkill(CCharEntity* PChar, uint16 WeaponSkillID); // declaration of function to check for weapon skill int32 delWeaponSkill(CCharEntity* PChar, uint16 WeaponSkillID); // declaration of function to delete weapon skill - void SaveCharJob(CCharEntity* PChar, JOBTYPE job); // сохраняем уровень для выбранной профессий персонажа - void SaveCharExp(CCharEntity* PChar, JOBTYPE job); // сохраняем опыт для выбранной профессии персонажа - void SaveCharEquip(CCharEntity* PChar); // сохраняем экипировку и внешний вид персонажа - void SaveCharLook(CCharEntity* PChar); // Saves a character's appearance based on style locking. - void SaveCharPosition(CCharEntity* PChar); // сохраняем позицию персонажа - // void SaveCharLinkshells(CCharEntity* PChar); // TODO - void SaveMissionsList(CCharEntity* PChar); // Save the missions list - void SaveEminenceData(CCharEntity* PChar); // Save Eminence Record (RoE) data - void SaveQuestsList(CCharEntity* PChar); // сохраняем список ксевтов - void SaveFame(CCharEntity* PChar); // Save area fame / reputation - void SaveZonesVisited(CCharEntity* PChar); // сохраняем посещенные зоны - void SaveKeyItems(CCharEntity* PChar); // сохраняем ключевые предметы - void SaveCharInventoryCapacity(CCharEntity* PChar); // Save Character inventory capacity - void SaveSpell(CCharEntity* PChar, uint16 spellID); // сохраняем выученные заклинания - void DeleteSpell(CCharEntity* PChar, uint16 spellID); // - void SaveLearnedAbilities(CCharEntity* PChar); // saved learned abilities (corsair rolls) - void SaveTitles(CCharEntity* PChar); // сохраняем заслуженные звания - void SaveCharStats(CCharEntity* PChar); // сохраняем флаги, текущие значения жихней, маны и профессий - void SaveCharGMLevel(CCharEntity* PChar); // saves the char's gm level and nameflags - void SaveMentorFlag(CCharEntity* PChar); // saves the char's mentor flag - void SaveJobMasterDisplay(CCharEntity* PChar); // Saves the char's job master display status - void SaveMenuConfigFlags(CCharEntity* PChar); // saves the char's unnamed flags - void SaveChatFilterFlags(CCharEntity* PChar); // saves the char's chat filters - void SaveLanguages(CCharEntity* PChar); // saves the char's language preference - void SaveCharNation(CCharEntity* PChar); // Save the character's nation of allegiance. - void SaveCampaignAllegiance(CCharEntity* PChar); // Save the character's campaign allegiance. - void SaveCharMoghancement(CCharEntity* PChar); // Save the character's current moghancement - void SaveCharSkills(CCharEntity* PChar, uint8 skillID); // сохраняем указанный skill персонажа - void SaveTeleport(CCharEntity* PChar, TELEPORT_TYPE type); // Homepoints, outposts, etc - void SaveDeathTime(CCharEntity* PChar); // Saves when this character last died. - void SavePlayTime(CCharEntity* PChar); // Saves this characters total play time. - bool hasMogLockerAccess(CCharEntity* PChar); // true if have access, false otherwise. - - uint8 getQuestStatus(CCharEntity* PChar, uint8 log, uint8 quest); // Get Quest status. Used in FishingUtils.cpp, allows to fish quest specific mobs, like PLD AF NM. + void SaveCharJob(CCharEntity* PChar, JOBTYPE job); // save the level for the selected character's jobs + void SaveCharExp(CCharEntity* PChar, JOBTYPE job); // save experience for the selected character’s chosen job + void SaveCharEquip(CCharEntity* PChar); // preserve the character’s equipment and appearance + void SaveCharLook(CCharEntity* PChar); // saves a character's appearance based on style locking + void SaveCharPosition(CCharEntity* PChar); // save the character's position (x/y/z) + // void SaveCharLinkshells(CCharEntity* PChar); // TODO: save the character's linkshells + void SaveMissionsList(CCharEntity* PChar); // save the missions list + void SaveEminenceData(CCharEntity* PChar); // save Eminence Record (RoE) data + void SaveQuestsList(CCharEntity* PChar); // save the list of quests + void SaveFame(CCharEntity* PChar); // save area fame / reputation + void SaveZonesVisited(CCharEntity* PChar); // save visited areas + void SaveKeyItems(CCharEntity* PChar); // save key items + void SaveCharInventoryCapacity(CCharEntity* PChar); // save Character inventory capacity + void SaveSpell(CCharEntity* PChar, uint16 spellID); // save learned spells + void DeleteSpell(CCharEntity* PChar, uint16 spellID); + void SaveLearnedAbilities(CCharEntity* PChar); // save learned abilities (e.g., corsair rolls) + void SaveTitles(CCharEntity* PChar); // save character's titles + void SaveCharStats(CCharEntity* PChar); // save flags, current values of character stats (jobs/HP/MP/etc.) + void SaveCharGMLevel(CCharEntity* PChar); // save the character's gm level and nameflags + void SaveMentorFlag(CCharEntity* PChar); // save the character's mentor flag + void SaveJobMasterDisplay(CCharEntity* PChar); // Save the character's job master display status + void SaveMenuConfigFlags(CCharEntity* PChar); // save the character's unnamed flags + void SaveChatFilterFlags(CCharEntity* PChar); // save the character's chat filters + void SaveLanguages(CCharEntity* PChar); // save the character's language preference + void SaveCharNation(CCharEntity* PChar); // save the character's nation of allegiance + void SaveCampaignAllegiance(CCharEntity* PChar); // save the character's campaign allegiance + void SaveCharMoghancement(CCharEntity* PChar); // save the character's current moghancement + void SaveCharSkills(CCharEntity* PChar, uint8 skillID); // save the character's skills + void SaveTeleport(CCharEntity* PChar, TELEPORT_TYPE type); // save the character's teleports (homepoints, outposts, maws, etc) + void SaveDeathTime(CCharEntity* PChar); // save when this character last died + void SavePlayTime(CCharEntity* PChar); // save this character's total play time + bool hasMogLockerAccess(CCharEntity* PChar); // true if have access, false otherwise + + uint8 getQuestStatus(CCharEntity* PChar, uint8 log, uint8 quest); // Get Quest status (used in FishingUtils.cpp, allows to fish quest specific mobs, like PLD AF NM) float AddExpBonus(CCharEntity* PChar, float exp); From c16f203d417e072adbef7b96895af8fe2f865648 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Tue, 26 Sep 2023 10:02:15 +0100 Subject: [PATCH 002/103] Start on Monstrosity --- scripts/commands/monstrosity.lua | 19 ++++ scripts/globals/monstrosity.lua | 68 ++++++++++++ .../hiddenQuests/Monstrosity_Unlock.lua | 103 ++++++++++++++++++ scripts/zones/Feretory/Zone.lua | 12 +- settings/default/main.lua | 3 + src/map/CMakeLists.txt | 2 + src/map/entities/charentity.cpp | 3 +- src/map/entities/charentity.h | 4 +- src/map/lua/lua_baseentity.cpp | 47 -------- src/map/lua/lua_baseentity.h | 19 ++-- src/map/monstrosity.cpp | 61 +++++++++++ src/map/monstrosity.h | 46 ++++++++ src/map/packets/char.cpp | 5 +- src/map/packets/char_appearance.cpp | 5 +- src/map/packets/char_health.cpp | 7 ++ src/map/packets/char_update.cpp | 10 ++ src/map/packets/monipulator1.cpp | 20 +++- src/map/packets/monipulator2.cpp | 4 + src/map/packets/zone_in.cpp | 34 ++++-- src/map/zone.cpp | 3 + 20 files changed, 393 insertions(+), 82 deletions(-) create mode 100644 scripts/commands/monstrosity.lua create mode 100644 scripts/globals/monstrosity.lua create mode 100644 scripts/quests/hiddenQuests/Monstrosity_Unlock.lua create mode 100644 src/map/monstrosity.cpp create mode 100644 src/map/monstrosity.h diff --git a/scripts/commands/monstrosity.lua b/scripts/commands/monstrosity.lua new file mode 100644 index 00000000000..ea1fc728989 --- /dev/null +++ b/scripts/commands/monstrosity.lua @@ -0,0 +1,19 @@ +local commandObj = {} + +commandObj.cmdprops = +{ + permission = 1, + parameters = '' +} + +commandObj.onTrigger = function(player) + if player:getCharVar('MONSTROSITY_START') == 1 then + player:setCharVar('MONSTROSITY_START', 0) + else + player:setCharVar('MONSTROSITY_START', 1) + end + + player:setPos(player:getXPos(), player:getYPos(), player:getZPos(), player:getRotPos(), player:getZoneID()) +end + +return commandObj diff --git a/scripts/globals/monstrosity.lua b/scripts/globals/monstrosity.lua new file mode 100644 index 00000000000..cbb17150e40 --- /dev/null +++ b/scripts/globals/monstrosity.lua @@ -0,0 +1,68 @@ +----------------------------------- +-- Monstrosity +----------------------------------- +require('scripts/globals/npc_util') +require('scripts/globals/quests') +----------------------------------- +xi = xi or {} +xi.monstrosity = xi.monstrosity or {} + +----------------------------------- +-- Odyssean Passage +----------------------------------- + +xi.monstrosity.odysseanPassageOnTrigger = function(player, npc) + -- TODO: Handle xi.settings.main.ENABLE_MONSTROSITY + + -- TODO: If passage in the overworld, do logic X + local isMonstrosityUnlocked = player:hasKeyItem(xi.keyItem.RING_OF_SUPERNAL_DISJUNCTION) + if isMonstrosityUnlocked then + player:startEvent(883) + end + + -- TODO: If passage in Feretory, do logic Y + -- In Feretory: Event 5 (0, 0, 0, 0, 0, 0, 0, 0) +end + +----------------------------------- +-- Feretory +----------------------------------- + +xi.monstrosity.feretoryOnZoneIn = function(player, prevZone) + -- TODO: Handle xi.settings.main.ENABLE_MONSTROSITY + + -- TODO: Rabbit, Mandy, and Lizard are all unlocked to begin with, but you + -- : start as whatever matching item you traded + + local cs = -1 + + player:setPos(-358.000, -3.400, -440.00, 63) + + return cs +end + +xi.monstrosity.feretoryOnEventUpdate = function(player, csid, option, npc) +end + +xi.monstrosity.feretoryOnEventFinish = function(player, csid, option, npc) +end + +----------------------------------- +-- Aengus (Feretory NPC) +----------------------------------- +-- Event 13 (As Lizard: 0, 0, 2, 0, 2, 90, 0, 0) + +----------------------------------- +-- Teyrnon (Feretory NPC) +----------------------------------- +-- Event 7 (As Lizard: 0, 0, 0, 0, 0, 0, 0, 0) + +----------------------------------- +-- Maccus (Feretory NPC) +----------------------------------- +-- Event 9 (As Lizard: 285, 2, 2, 0, 0, 0, 0, 0) + +----------------------------------- +-- Suibhne (Feretory NPC) +----------------------------------- +-- Event 11 (As Lizard: 1, 1, 0, 0, 0, 0, 0, 0) diff --git a/scripts/quests/hiddenQuests/Monstrosity_Unlock.lua b/scripts/quests/hiddenQuests/Monstrosity_Unlock.lua new file mode 100644 index 00000000000..709e7dc8077 --- /dev/null +++ b/scripts/quests/hiddenQuests/Monstrosity_Unlock.lua @@ -0,0 +1,103 @@ +----------------------------------- +-- Unlocking Monstrosity +----------------------------------- + +local quest = HiddenQuest:new('MonstrosityUnlock') + +-- TODO: Handle xi.settings.main.ENABLE_MONSTROSITY +-- TODO: Hide completion behind a UniqueEvent flag + +quest.sections = +{ + -- Intro chatter + { + check = function(player, questVars, vars) + return quest:getVar(player, 'Prog') == 0 + end, + + [xi.zone.PASHHOW_MARSHLANDS] = + { + ['Suspicious_Hume'] = + { + onTrigger = function(player, npc) + return quest:progressEvent(40) + end, + }, + + onEventFinish = + { + [40] = function(player, csid, option, npc) + if option == 0 then + quest:setVar(player, 'Prog', 1) + end + end, + }, + }, + }, + + -- Reminder + { + check = function(player, questVars, vars) + return quest:getVar(player, 'Prog') == 1 + end, + + [xi.zone.PASHHOW_MARSHLANDS] = + { + ['Suspicious_Hume'] = + { + onTrigger = function(player, npc) + return quest:progressEvent(41) + end, + }, + }, + + [xi.zone.PORT_WINDURST] = + { + ['Suspicious_Tarutaru'] = + { + onTrade = function(player, npc, trade) + if + npcUtil.tradeHasExactly(trade, xi.item.LIZARD_TAIL) + then + return quest:progressEvent(881, 926, 0, 0, 0, 0, 0, 0, 4) + end + end, + + -- Reminder + onTrigger = function(player, npc) + return quest:progressEvent(880) + end, + }, + + onEventFinish = + { + [881] = function(player, csid, option, npc) + if option == 0 then + quest:setVar(player, 'Prog', 2) + npcUtil.giveKeyItem(player, xi.ki.RING_OF_SUPERNAL_DISJUNCTION) + end + end, + }, + }, + }, + + -- Intro chatter + { + check = function(player, questVars, vars) + return quest:getVar(player, 'Prog') == 2 + end, + + [xi.zone.PORT_WINDURST] = + { + ['Suspicious_Tarutaru'] = + { + -- Reminder + onTrigger = function(player, npc) + return quest:progressEvent(882) + end, + }, + }, + }, +} + +return quest diff --git a/scripts/zones/Feretory/Zone.lua b/scripts/zones/Feretory/Zone.lua index dbd7762b148..c28aa4ad28e 100644 --- a/scripts/zones/Feretory/Zone.lua +++ b/scripts/zones/Feretory/Zone.lua @@ -1,26 +1,28 @@ ----------------------------------- -- Zone: Feretory ----------------------------------- +require('scripts/globals/monstrosity') +----------------------------------- local zoneObject = {} zoneObject.onInitialize = function(zone) + -- Unused end zoneObject.onZoneIn = function(player, prevZone) - local cs = -1 - - player:setPos(-358.000, -3.400, -440.00, 63) - - return cs + return xi.monstrosity.feretoryOnZoneIn(player, prevZone) end zoneObject.onTriggerAreaEnter = function(player, triggerArea) + -- Unused end zoneObject.onEventUpdate = function(player, csid, option, npc) + xi.monstrosity.feretoryOnEventUpdate(player, csid, option, npc) end zoneObject.onEventFinish = function(player, csid, option, npc) + xi.monstrosity.feretoryOnEventFinish(player, csid, option, npc) end return zoneObject diff --git a/settings/default/main.lua b/settings/default/main.lua index bcad16499a4..b9b3ddd6692 100644 --- a/settings/default/main.lua +++ b/settings/default/main.lua @@ -69,6 +69,9 @@ xi.settings.main = -- VoidWalker ENABLE_VOIDWALKER = 1, + -- VoidWalker + ENABLE_MONSTROSITY = 1, + -- TREASURE CASKETS -- Retail droprate = 0.1 (10%) with no other effects active -- Set to 0 to disable caskets. diff --git a/src/map/CMakeLists.txt b/src/map/CMakeLists.txt index 2d0d14b2f73..fcdbdd1102a 100644 --- a/src/map/CMakeLists.txt +++ b/src/map/CMakeLists.txt @@ -88,6 +88,8 @@ set(SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/mobskill.h ${CMAKE_CURRENT_SOURCE_DIR}/modifier.cpp ${CMAKE_CURRENT_SOURCE_DIR}/modifier.h + ${CMAKE_CURRENT_SOURCE_DIR}/monstrosity.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/monstrosity.h ${CMAKE_CURRENT_SOURCE_DIR}/navmesh.cpp ${CMAKE_CURRENT_SOURCE_DIR}/navmesh.h ${CMAKE_CURRENT_SOURCE_DIR}/notoriety_container.cpp diff --git a/src/map/entities/charentity.cpp b/src/map/entities/charentity.cpp index 774f3ddb9d5..3bcd3b7eba4 100644 --- a/src/map/entities/charentity.cpp +++ b/src/map/entities/charentity.cpp @@ -173,8 +173,9 @@ CCharEntity::CCharEntity() m_mkeCurrent = 0; m_asaCurrent = 0; + m_PMonstrosity = nullptr; + m_Costume = 0; - m_Monstrosity = 0; m_hasTractor = 0; m_hasRaise = 0; m_weaknessLvl = 0; diff --git a/src/map/entities/charentity.h b/src/map/entities/charentity.h index 8650e0707dc..90f671cbbe0 100644 --- a/src/map/entities/charentity.h +++ b/src/map/entities/charentity.h @@ -23,6 +23,7 @@ along with this program. If not, see http://www.gnu.org/licenses/ #define _CHARENTITY_H #include "event_info.h" +#include "monstrosity.h" #include "packets/char.h" #include "packets/entity_update.h" @@ -447,10 +448,11 @@ class CCharEntity : public CBattleEntity EntityID_t BazaarID{}; // Pointer to the bazaar we are browsing. BazaarList_t BazaarCustomers; // Array holding the IDs of the current customers + std::unique_ptr m_PMonstrosity; + uint32 m_InsideTriggerAreaID; // The ID of the trigger area the character is inside uint8 m_LevelRestriction; // Character level limit uint16 m_Costume; - uint16 m_Monstrosity; // Monstrosity model ID uint32 m_AHHistoryTimestamp; uint32 m_DeathTimestamp; time_point m_deathSyncTime; // Timer used for sending an update packet at a regular interval while the character is dead diff --git a/src/map/lua/lua_baseentity.cpp b/src/map/lua/lua_baseentity.cpp index f038895f80d..d94a1535258 100644 --- a/src/map/lua/lua_baseentity.cpp +++ b/src/map/lua/lua_baseentity.cpp @@ -5191,50 +5191,6 @@ uint16 CLuaBaseEntity::getCostume() return PChar->m_Costume; } -/************************************************************************ - * Function: getCostume2() - * Purpose : Sets or returns a monstrosity costume - * Example : player:costume2( costumeId ) - * Notes : Not currently implemented - ************************************************************************/ - -uint16 CLuaBaseEntity::getCostume2() -{ - if (m_PBaseEntity->objtype != TYPE_PC) - { - ShowWarning("Invalid entity type calling function (%s).", m_PBaseEntity->GetName()); - return 0; - } - - auto* PChar = static_cast(m_PBaseEntity); - return PChar->m_Monstrosity; -} - -/************************************************************************ - * Function: setCostume2() - * Purpose : Sets or returns a monstrosity costume - * Example : player:costume2( costumeId ) - * Notes : Not currently implemented - ************************************************************************/ - -void CLuaBaseEntity::setCostume2(uint16 costume) -{ - if (m_PBaseEntity->objtype != TYPE_PC) - { - ShowWarning("Invalid entity type calling function (%s).", m_PBaseEntity->GetName()); - return; - } - - auto* PChar = static_cast(m_PBaseEntity); - - if (PChar->m_Monstrosity != costume && PChar->status != STATUS_TYPE::SHUTDOWN && PChar->status != STATUS_TYPE::DISAPPEAR) - { - PChar->m_Monstrosity = costume; - PChar->updatemask |= UPDATE_LOOK; - PChar->pushPacket(new CCharAppearancePacket(PChar)); - } -} - /************************************************************************ * Function: getAnimation() * Purpose : Returns the assigned default animation of an entity @@ -17096,9 +17052,6 @@ void CLuaBaseEntity::Register() SOL_REGISTER("setModelId", CLuaBaseEntity::setModelId); SOL_REGISTER("setCostume", CLuaBaseEntity::setCostume); SOL_REGISTER("getCostume", CLuaBaseEntity::getCostume); - SOL_REGISTER("getCostume2", CLuaBaseEntity::getCostume2); - SOL_REGISTER("setCostume2", CLuaBaseEntity::setCostume2); - SOL_REGISTER("getAnimation", CLuaBaseEntity::getAnimation); SOL_REGISTER("setAnimation", CLuaBaseEntity::setAnimation); SOL_REGISTER("getAnimationSub", CLuaBaseEntity::getAnimationSub); diff --git a/src/map/lua/lua_baseentity.h b/src/map/lua/lua_baseentity.h index bba583a83d8..80fd7c94c9d 100644 --- a/src/map/lua/lua_baseentity.h +++ b/src/map/lua/lua_baseentity.h @@ -271,16 +271,15 @@ class CLuaBaseEntity void setModelId(uint16 modelId, sol::object const& slotObj); void setCostume(uint16 costume); uint16 getCostume(); - uint16 getCostume2(); // monstrosity costume - void setCostume2(uint16 costume); - uint8 getAnimation(); - void setAnimation(uint8 animation); - uint8 getAnimationSub(); - void setAnimationSub(uint8 animationsub); - bool getCallForHelpFlag() const; - void setCallForHelpFlag(bool cfh); - bool getCallForHelpBlocked() const; - void setCallForHelpBlocked(bool blocked); + + uint8 getAnimation(); + void setAnimation(uint8 animation); + uint8 getAnimationSub(); + void setAnimationSub(uint8 animationsub); + bool getCallForHelpFlag() const; + void setCallForHelpFlag(bool cfh); + bool getCallForHelpBlocked() const; + void setCallForHelpBlocked(bool blocked); // Player Status uint8 getNation(); diff --git a/src/map/monstrosity.cpp b/src/map/monstrosity.cpp new file mode 100644 index 00000000000..1c5b5d65d9e --- /dev/null +++ b/src/map/monstrosity.cpp @@ -0,0 +1,61 @@ +/* +=========================================================================== + + Copyright (c) 2023 LandSandBoat Dev Teams + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see http://www.gnu.org/licenses/ + +=========================================================================== +*/ + +#include "monstrosity.h" + +#include "entities/charentity.h" +#include "utils/charutils.h" + +void monstrosity::HandleZoneIn(CCharEntity* PChar) +{ + // TODO: Check we're about to enter monstrosity, charvar, flag, etc. + if (charutils::GetCharVar(PChar, "MONSTROSITY_START") == 1) + { + PChar->m_PMonstrosity = std::make_unique(72, 11); + PChar->updatemask |= UPDATE_LOOK; + } +} + +uint32 monstrosity::GetPackedMonstrosityName(CCharEntity* PChar) +{ + // Monstrosity Name Ids? + // If populated, the monstrosity icon will appear + uint8 a = 0x1F; + + // Mob Type + // 0x80: Scorpion + // 0x81: Mandragora + uint8 b = 0x81; + + // Adjective 1 (optional) + // 01: Abashed + // CD: Tempest + // F5: Zenith + // F6: Zero + uint8 c = 0xF6; + + // Adjective 2 + // Same values as above + uint8 d = 0xCD; + + // Packed as LE + return (d << 24) + (c << 16) + (b << 8) + (a << 0); +} diff --git a/src/map/monstrosity.h b/src/map/monstrosity.h new file mode 100644 index 00000000000..d718c54d0a8 --- /dev/null +++ b/src/map/monstrosity.h @@ -0,0 +1,46 @@ +/* +=========================================================================== + + Copyright (c) 2023 LandSandBoat Dev Teams + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see http://www.gnu.org/licenses/ + +=========================================================================== +*/ + +#pragma once + +#include "common/cbasetypes.h" + +class CCharEntity; + +namespace monstrosity +{ + struct MonstrosityData_t + { + public: + uint8 Face; + uint8 Race; + + MonstrosityData_t(uint8 face, uint8 race) + : Face(face) + , Race(race) + { + // + } + }; + + void HandleZoneIn(CCharEntity* PChar); + uint32 GetPackedMonstrosityName(CCharEntity* PChar); +} // namespace monstrosity diff --git a/src/map/packets/char.cpp b/src/map/packets/char.cpp index 0e42b7ed3dc..20c29927d35 100644 --- a/src/map/packets/char.cpp +++ b/src/map/packets/char.cpp @@ -179,9 +179,10 @@ void CCharPacket::updateWith(CCharEntity* PChar, ENTITYUPDATE type, uint8 update ref(0x56) = look->sub + 0x7000; ref(0x58) = look->ranged + 0x8000; - if (PChar->m_Monstrosity != 0) + if (PChar->m_PMonstrosity != nullptr) { - ref(0x48) = PChar->m_Monstrosity; + ref(0x48) = PChar->m_PMonstrosity->Face; + ref(0x49) = PChar->m_PMonstrosity->Race; ref(0x58) = 0xFFFF; } } diff --git a/src/map/packets/char_appearance.cpp b/src/map/packets/char_appearance.cpp index e891fcbd5e7..8dd28e1265d 100644 --- a/src/map/packets/char_appearance.cpp +++ b/src/map/packets/char_appearance.cpp @@ -41,9 +41,10 @@ CCharAppearancePacket::CCharAppearancePacket(CCharEntity* PChar) ref(0x12) = look->sub + 0x7000; ref(0x14) = look->ranged + 0x8000; - if (PChar->m_Monstrosity != 0) + if (PChar->m_PMonstrosity != nullptr) { - ref(0x04) = PChar->m_Monstrosity; + ref(0x04) = PChar->m_PMonstrosity->Face; + ref(0x05) = PChar->m_PMonstrosity->Race; ref(0x14) = 0xFFFF; } } diff --git a/src/map/packets/char_health.cpp b/src/map/packets/char_health.cpp index 60b9120ffcc..823414f2230 100644 --- a/src/map/packets/char_health.cpp +++ b/src/map/packets/char_health.cpp @@ -26,6 +26,8 @@ #include "entities/charentity.h" #include "entities/trustentity.h" +#include "monstrosity.h" + CCharHealthPacket::CCharHealthPacket(CCharEntity* PChar) { this->setType(0xDF); @@ -42,6 +44,11 @@ CCharHealthPacket::CCharHealthPacket(CCharEntity* PChar) ref(0x16) = PChar->GetHPP(); ref(0x17) = PChar->GetMPP(); + if (PChar->m_PMonstrosity != nullptr) + { + ref(0x1C) = monstrosity::GetPackedMonstrosityName(PChar); + } + if (!(PChar->nameflags.flags & FLAG_ANON)) { ref(0x20) = PChar->GetMJob(); diff --git a/src/map/packets/char_update.cpp b/src/map/packets/char_update.cpp index d0f71c2b05f..9f23dbce60f 100644 --- a/src/map/packets/char_update.cpp +++ b/src/map/packets/char_update.cpp @@ -52,6 +52,7 @@ CCharUpdatePacket::CCharUpdatePacket(CCharEntity* PChar) { ref(0x2D) = 0x80; } + if (PChar->StatusEffectContainer->HasStatusEffect(EFFECT_SNEAK)) { ref(0x38) = 0x04; @@ -61,6 +62,7 @@ CCharUpdatePacket::CCharUpdatePacket(CCharEntity* PChar) { ref(0x38) |= 0x10; // Mentor flag. } + if (PChar->isNewPlayer()) { ref(0x38) |= 0x08; // New player ? @@ -81,10 +83,12 @@ CCharUpdatePacket::CCharUpdatePacket(CCharEntity* PChar) ref(0x32) = (LSColor.G << 4) + 15; ref(0x33) = (LSColor.B << 4) + 15; } + if (PChar->PPet != nullptr) { ref(0x34) = PChar->PPet->targid << 3; } + // Status flag: bit 4: frozen anim (terror), // bit 6/7/8 related to Ballista (6 set - normal, 7 set san d'oria, 6+7 set bastok, 8 set windurst) uint8 flag = (static_cast(PChar->allegiance) << 5); @@ -107,6 +111,7 @@ CCharUpdatePacket::CCharUpdatePacket(CCharEntity* PChar) { ref(0x4A) = PChar->hookDelay; } + ref(0x4C) = PChar->StatusEffectContainer->m_Flags; // GEO bubble effects, changes bubble effect depending on what effect is activated. @@ -125,4 +130,9 @@ CCharUpdatePacket::CCharUpdatePacket(CCharEntity* PChar) ref(0x29) |= static_cast(PChar->StatusEffectContainer->GetStatusEffect(EFFECT_MOUNTED)->GetSubPower()); ref(0x5B) = PChar->StatusEffectContainer->GetStatusEffect(EFFECT_MOUNTED)->GetPower(); } + + if (PChar->m_PMonstrosity != nullptr) + { + ref(0x54) = monstrosity::GetPackedMonstrosityName(PChar); + } } diff --git a/src/map/packets/monipulator1.cpp b/src/map/packets/monipulator1.cpp index 53226c21b12..b3e13007f73 100644 --- a/src/map/packets/monipulator1.cpp +++ b/src/map/packets/monipulator1.cpp @@ -22,6 +22,7 @@ #include "common/socket.h" #include "monipulator1.h" +#include "utils/charutils.h" CMonipulatorPacket1::CMonipulatorPacket1(CCharEntity* PChar) { @@ -31,12 +32,19 @@ CMonipulatorPacket1::CMonipulatorPacket1(CCharEntity* PChar) ref(0x04) = 0x03; // Update Type ref(0x06) = 0xD8; // Variable Data Size - ref(0x08) = 0; // Species - ref(0x0A) = 0; // Flags? - ref(0x0C) = 0; // Monstrosity Rank (0 = Mon, 1 = NM, 2 = HNM) + ref(0x08) = (0xFCFE); // Species + ref(0x0A) = (0x0B45); // Flags? + ref(0x0C) = 0; // Monstrosity Rank (0 = Mon, 1 = NM, 2 = HNM) - ref(0x12) = 0; // Infamy + ref(0x10) = (0x0074); + ref(0x12) = charutils::GetPoints(PChar, "infamy"); - std::memset(data + 0x1C, 0, 64); // Instinct Battlefield 1 - std::memset(data + 0x5C, 0, 128); // Monster Level Char Field + std::array instinct{ 0 }; + + std::array levels{ 0 }; + levels[1] = 0x01; + levels[18] = 0x01; + + std::memcpy(data + 0x1C, instinct.data(), 64); // Instinct Battlefield 1 + std::memcpy(data + 0x5C, levels.data(), 128); // Monster Level Char Field } diff --git a/src/map/packets/monipulator2.cpp b/src/map/packets/monipulator2.cpp index 05782b01e78..818bc36dcfe 100644 --- a/src/map/packets/monipulator2.cpp +++ b/src/map/packets/monipulator2.cpp @@ -32,4 +32,8 @@ CMonipulatorPacket2::CMonipulatorPacket2(CCharEntity* PChar) std::array packet2 = { 0x04, 0x00, 0xB0 }; memcpy(data + (0x04), &packet2, sizeof(packet2)); + + ref(0x86) = 0x0A; + + ref(0x88) = 0x3F; } diff --git a/src/map/packets/zone_in.cpp b/src/map/packets/zone_in.cpp index 3a45e95a472..22e6224a39a 100644 --- a/src/map/packets/zone_in.cpp +++ b/src/map/packets/zone_in.cpp @@ -118,7 +118,7 @@ CZoneInPacket::CZoneInPacket(CCharEntity* PChar, const EventInfo* currentEvent) this->setType(0x0A); this->setSize(0x104); - // It is necessary to work Manaklipper + // It is necessary to work Manaclipper // The last 8 bytes are similar for a while // unsigned char packet [] = { // 0x0D, 0x3A, 0x0C, 0x00, 0x11, 0x00, 0x19, 0x00, 0x02, 0xE4, 0x93, 0x10, 0x91, 0xE5, 0x93, 0x10}; // 0x2a = 0x10 @@ -130,18 +130,24 @@ CZoneInPacket::CZoneInPacket(CCharEntity* PChar, const EventInfo* currentEvent) ref(0x04) = PChar->id; ref(0x08) = PChar->targid; - memcpy(data + (0x84), PChar->GetName().c_str(), PChar->GetName().size()); + // 0x0A = Padding ref(0x0B) = PChar->loc.p.rotation; ref(0x0C) = PChar->loc.p.x; ref(0x10) = PChar->loc.p.y; ref(0x14) = PChar->loc.p.z; + // 0x18 = Run Count + + // 0x1A = Target Index + ref(0x1C) = PChar->GetSpeed(); ref(0x1D) = PChar->speedsub; ref(0x1E) = PChar->GetHPP(); ref(0x1F) = PChar->animation; + // 0x20 = Character Gender and Size + if (PChar->StatusEffectContainer->HasStatusEffect(EFFECT_MOUNTED)) { ref(0x20) = static_cast(PChar->StatusEffectContainer->GetStatusEffect(EFFECT_MOUNTED)->GetSubPower()); @@ -149,6 +155,10 @@ CZoneInPacket::CZoneInPacket(CCharEntity* PChar, const EventInfo* currentEvent) ref(0x21) = PChar->GetGender() * 128 + (1 << PChar->look.size); + ref(0x28) = 0x0100; // "Always 0x100" + + // 0x2A = Zone Animation + look_t* look = (PChar->getStyleLocked() ? &PChar->mainlook : &PChar->look); ref(0x44) = look->face; ref(0x45) = look->race; @@ -161,12 +171,6 @@ CZoneInPacket::CZoneInPacket(CCharEntity* PChar, const EventInfo* currentEvent) ref(0x52) = look->sub + 0x7000; ref(0x54) = look->ranged + 0x8000; - if (PChar->m_Monstrosity != 0) - { - ref(0x44) = PChar->m_Monstrosity; - ref(0x54) = 0xFFFF; - } - ref(0x56) = PChar->PInstance ? PChar->PInstance->GetBackgroundMusicDay() : PChar->loc.zone->GetBackgroundMusicDay(); ref(0x58) = PChar->PInstance ? PChar->PInstance->GetBackgroundMusicNight() : PChar->loc.zone->GetBackgroundMusicNight(); ref(0x5A) = PChar->PInstance ? PChar->PInstance->GetSoloBattleMusic() : PChar->loc.zone->GetSoloBattleMusic(); @@ -220,6 +224,9 @@ CZoneInPacket::CZoneInPacket(CCharEntity* PChar, const EventInfo* currentEvent) ref(0xAF) = PChar->loc.zone->CanUseMisc(MISC_MOGMENU); // flag allows you to use Mog Menu outside Mog House } + auto const& nameStr = PChar->GetName(); + std::memcpy(data + 0x84, nameStr.data(), nameStr.size()); + ref(0xA0) = PChar->GetPlayTime(); // time spent by the character in the game from the moment of creation ref(0xAE) = GetMogHouseLeavingFlag(PChar); @@ -249,4 +256,15 @@ CZoneInPacket::CZoneInPacket(CCharEntity* PChar, const EventInfo* currentEvent) ref(0xF8) = PChar->chatFilterFlags; ref(0x100) = 0x01; // observed: RoZ = 3, CoP = 5, ToAU = 9, WoTG = 11, SoA/original areas = 1 + + if (PChar->m_PMonstrosity != nullptr) + { + // TODO: These make the spawn in cleaner, but then the name doesn't work correctly + // ref(0x04) = PChar->m_PMonstrosity->Face; + // ref(0x05) = PChar->m_PMonstrosity->Race; + // ref(0x14) = 0xFFFF; + + // Enable Monstrosity menu options + ref(0x7E) = 0x1F; + } } diff --git a/src/map/zone.cpp b/src/map/zone.cpp index a8760573ea2..2de4ca596a9 100644 --- a/src/map/zone.cpp +++ b/src/map/zone.cpp @@ -40,6 +40,7 @@ #include "linkshell.h" #include "map.h" #include "message.h" +#include "monstrosity.h" #include "notoriety_container.h" #include "party.h" #include "spell.h" @@ -1095,6 +1096,8 @@ void CZone::CharZoneIn(CCharEntity* PChar) PChar->PLatentEffectContainer->CheckLatentsZone(); + monstrosity::HandleZoneIn(PChar); + charutils::ReadHistory(PChar); moduleutils::OnCharZoneIn(PChar); From 7e95fd1f90f2f9a910664af393cbdc1708311078 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Wed, 27 Sep 2023 11:36:13 +0100 Subject: [PATCH 003/103] Work on instincts --- scripts/commands/monstrosity.lua | 147 +++++++++++++++++++++++++++++++ src/map/lua/lua_baseentity.cpp | 37 ++++++++ src/map/lua/lua_baseentity.h | 2 + src/map/monstrosity.cpp | 35 ++++++++ src/map/monstrosity.h | 17 ++-- src/map/packets/monipulator1.cpp | 34 ++++--- src/map/packets/monipulator2.cpp | 32 +++++-- 7 files changed, 279 insertions(+), 25 deletions(-) diff --git a/scripts/commands/monstrosity.lua b/scripts/commands/monstrosity.lua index ea1fc728989..2969bb7184f 100644 --- a/scripts/commands/monstrosity.lua +++ b/scripts/commands/monstrosity.lua @@ -6,7 +6,108 @@ commandObj.cmdprops = parameters = '' } +local monstrositySpecies = +{ + RABBIT = 1, + BEHEMOTH = 2, + TIGER = 3, + SHEEP = 4, + RAM = 5, + DHALMEL = 6, + COEURL = 7, + OPO_OPO = 8, + MANTICORE = 9, + BUFFALO = 10, + MARID = 11, + CERBERUS = 12, + GNOLE = 13, + + -- No 14? + + FUNGUAR = 15, + TREANT_SAPLING = 16, + MORBOL = 17, + MANDRAGORA = 18, + SABOTENDER = 19, + FLYTRAP = 20, + GOOBBUE = 21, + RAFFLESIA = 22, + PANOPT = 23, + + -- No 24-26? + + BEE = 27, + BEETLE = 28, + CRAWLER = 29, + FLY = 30, + SCORPION = 31, + SPIDER = 32, + ANTLION = 33, + DIREMITE = 34, + CHIGOE = 35, + WAMOURACAMPA = 36, + LADYBUG = 37, + GNAT = 38, + + -- No 39-42? + + LIZARD = 43, + RAPTOR = 44, + ADAMANTOISE = 45, + BUGARD = 46, + EFT = 47, + WIVRE = 48, + PEISTE = 49, + + -- No 50-52? + + SLIME = 52, + HECTEYES = 53, + FLAN = 54, + SLUG = 56, + SANDWORM = 57, + LEECH = 58, + + -- No 59? + + CRAB = 60, + PUGIL = 61, + SEA_MONK = 62, + URAGNITE = 63, + OROBON = 64, + RUSZOR = 65, + TOAD = 66, + + -- No 67-68 + + BIRD = 69, + COCKATRICE = 70, + ROC = 71, + BAT = 72, + HIPPOGRYPH = 73, + APKALLU = 74, + COLIBRI = 75, + AMPHIPTERE = 76, + + -- Others are not part of the regular structure, but it's useful to map them here anyway. + -- Using arbitrary offsets! + DQ_SLIME = 77, + FFXIV_SPRIGGAN = 81, +} + +local monstrosityVariants = +{ + -- Rabbit + ONYX_RABBIT = 0, + ALABASTER_RABBIT = 1, + LAPINION = 2, + + -- Behemoth + ELSAMOTH = 3, +} + commandObj.onTrigger = function(player) + --[[ if player:getCharVar('MONSTROSITY_START') == 1 then player:setCharVar('MONSTROSITY_START', 0) else @@ -14,6 +115,52 @@ commandObj.onTrigger = function(player) end player:setPos(player:getXPos(), player:getYPos(), player:getZPos(), player:getRotPos(), player:getZoneID()) + ]] + local data = + { + face = 74, + race = 11, + + -- 1 byte per entry, mapped out to monstrositySpecies table + levels = + { + }, + + -- Bitfield + instincts = + { + }, + + variants = + { + }, + } + + -- Set all levels to 99 + for _, val in pairs(monstrositySpecies) do + data.levels[val] = 99 + end + + -- Instincts + -- NOTE: Since this is a bitfield, it's zero-indexed! + for key, val in pairs(monstrositySpecies) do + local speciesKey = val + local speciesLevel = data.levels[val] + local byteOffset = math.floor(speciesKey / 4) + local unlockAmount = math.floor(speciesLevel / 30) + local shiftAmount = (speciesKey * 2) % 8 + -- print(key, speciesKey, speciesLevel, unlockAmount, byteOffset .. ':' .. shiftAmount) + if byteOffset < 64 then + data.instincts[byteOffset] = bit.bor(data.instincts[byteOffset] or 0, bit.lshift(unlockAmount, shiftAmount)) + else + print("byteOffset out of range") + end + end + + -- Variants + data.variants[0x00] = bit.bor(data.variants[0x00] or 0, bit.lshift(0xFF, 0)) + + player:setMonstrosity(data); end return commandObj diff --git a/src/map/lua/lua_baseentity.cpp b/src/map/lua/lua_baseentity.cpp index d94a1535258..13f2da2075f 100644 --- a/src/map/lua/lua_baseentity.cpp +++ b/src/map/lua/lua_baseentity.cpp @@ -6306,6 +6306,41 @@ void CLuaBaseEntity::addJobTraits(uint8 jobID, uint8 level) } } +void CLuaBaseEntity::setMonstrosity(sol::table table) +{ + auto* PChar = static_cast(m_PBaseEntity); + if (PChar->m_PMonstrosity == nullptr) + { + return; + } + + PChar->m_PMonstrosity->Face = table.get("face"); + PChar->m_PMonstrosity->Race = table.get("race"); + + for (auto const& [keyObj, valObj] : table.get("levels")) + { + uint8 key = keyObj.as(); + uint8 val = valObj.as(); + PChar->m_PMonstrosity->levels[key] = val; + } + + for (auto const& [keyObj, valObj] : table.get("instincts")) + { + uint8 key = keyObj.as(); + uint8 val = valObj.as(); + PChar->m_PMonstrosity->instincts[key] = val; + } + + for (auto const& [keyObj, valObj] : table.get("variants")) + { + uint8 key = keyObj.as(); + uint8 val = valObj.as(); + PChar->m_PMonstrosity->variants[key] = val; + } + + monstrosity::SendFullMonstrosityUpdate(PChar); +} + /************************************************************************ * Function: getTitle() * Purpose : Returns the integer value of the player's current title @@ -17109,6 +17144,8 @@ void CLuaBaseEntity::Register() SOL_REGISTER("levelRestriction", CLuaBaseEntity::levelRestriction); SOL_REGISTER("addJobTraits", CLuaBaseEntity::addJobTraits); + SOL_REGISTER("setMonstrosity", CLuaBaseEntity::setMonstrosity); + // Player Titles and Fame SOL_REGISTER("getTitle", CLuaBaseEntity::getTitle); SOL_REGISTER("hasTitle", CLuaBaseEntity::hasTitle); diff --git a/src/map/lua/lua_baseentity.h b/src/map/lua/lua_baseentity.h index 80fd7c94c9d..318dcb1076e 100644 --- a/src/map/lua/lua_baseentity.h +++ b/src/map/lua/lua_baseentity.h @@ -330,6 +330,8 @@ class CLuaBaseEntity uint8 levelRestriction(sol::object const& level); // Establish/return current level restriction void addJobTraits(uint8 jobID, uint8 level); + void setMonstrosity(sol::table table); + // Player Titles and Fame uint16 getTitle(); bool hasTitle(uint16 titleID); diff --git a/src/map/monstrosity.cpp b/src/map/monstrosity.cpp index 1c5b5d65d9e..d3a4f834242 100644 --- a/src/map/monstrosity.cpp +++ b/src/map/monstrosity.cpp @@ -22,8 +22,31 @@ #include "monstrosity.h" #include "entities/charentity.h" + +#include "packets/monipulator1.h" +#include "packets/monipulator2.h" + #include "utils/charutils.h" +monstrosity::MonstrosityData_t::MonstrosityData_t(uint8 face, uint8 race) +: Face(face) +, Race(race) +{ + // TODO: Populate instinct and levels from db + // Starting levels + levels[0] = 0x01; + levels[1] = 0x00; + levels[2] = 0x00; + + instincts[0] = 0x00; + instincts[1] = 0x00; + instincts[2] = 0x00; + + variants[0] = 0x00; + variants[1] = 0x00; + variants[2] = 0x00; +} + void monstrosity::HandleZoneIn(CCharEntity* PChar) { // TODO: Check we're about to enter monstrosity, charvar, flag, etc. @@ -59,3 +82,15 @@ uint32 monstrosity::GetPackedMonstrosityName(CCharEntity* PChar) // Packed as LE return (d << 24) + (c << 16) + (b << 8) + (a << 0); } + +void monstrosity::SendFullMonstrosityUpdate(CCharEntity* PChar) +{ + if (PChar->m_PMonstrosity == nullptr) + { + return; + } + + PChar->pushPacket(new CMonipulatorPacket1(PChar)); + PChar->pushPacket(new CMonipulatorPacket2(PChar)); + PChar->updatemask |= UPDATE_LOOK; +} diff --git a/src/map/monstrosity.h b/src/map/monstrosity.h index d718c54d0a8..3b7277af5df 100644 --- a/src/map/monstrosity.h +++ b/src/map/monstrosity.h @@ -23,6 +23,8 @@ #include "common/cbasetypes.h" +#include + class CCharEntity; namespace monstrosity @@ -30,17 +32,20 @@ namespace monstrosity struct MonstrosityData_t { public: + MonstrosityData_t(uint8 face, uint8 race); + + // TODO: Should these be a single uint16? uint8 Face; uint8 Race; - MonstrosityData_t(uint8 face, uint8 race) - : Face(face) - , Race(race) - { - // - } + // TODO: Extend this to be large enough to hold Slime and Spriggan levels + // : but don't use sizeof() with this structure. + std::array levels{ 0 }; + std::array instincts{ 0 }; + std::array variants{ 0 }; }; void HandleZoneIn(CCharEntity* PChar); uint32 GetPackedMonstrosityName(CCharEntity* PChar); + void SendFullMonstrosityUpdate(CCharEntity* PChar); } // namespace monstrosity diff --git a/src/map/packets/monipulator1.cpp b/src/map/packets/monipulator1.cpp index b3e13007f73..5b711067a2b 100644 --- a/src/map/packets/monipulator1.cpp +++ b/src/map/packets/monipulator1.cpp @@ -19,9 +19,11 @@ =========================================================================== */ -#include "common/socket.h" - #include "monipulator1.h" + +#include "common/socket.h" +#include "entities/charentity.h" +#include "monstrosity.h" #include "utils/charutils.h" CMonipulatorPacket1::CMonipulatorPacket1(CCharEntity* PChar) @@ -29,22 +31,28 @@ CMonipulatorPacket1::CMonipulatorPacket1(CCharEntity* PChar) this->setType(0x63); this->setSize(0xDC); + if (PChar->m_PMonstrosity == nullptr) + { + return; + } + ref(0x04) = 0x03; // Update Type ref(0x06) = 0xD8; // Variable Data Size - ref(0x08) = (0xFCFE); // Species - ref(0x0A) = (0x0B45); // Flags? - ref(0x0C) = 0; // Monstrosity Rank (0 = Mon, 1 = NM, 2 = HNM) + ref(0x08) = 0xFCFE; // Species? Also seen 0x3F1F + // ref(0x0A) = 0x0B45; // Flags? Also seen 0x0B46 - ref(0x10) = (0x0074); - ref(0x12) = charutils::GetPoints(PChar, "infamy"); + ref(0x0C) = 0; // Monstrosity Rank (0 = Mon, 1 = NM, 2 = HNM) - std::array instinct{ 0 }; + // Falculties? + // ref(0x10) = 0xFF; + // ref(0x11) = 0xFF; + + ref(0x12) = charutils::GetPoints(PChar, "infamy"); - std::array levels{ 0 }; - levels[1] = 0x01; - levels[18] = 0x01; + // Bitpacked 2-bit values. 0 = no instincts from that species, 1 == first instinct, 2 == first and second instinct, 3 == first, second, and third instinct. + std::memcpy(data + 0x1C, PChar->m_PMonstrosity->instincts.data(), 64); // Instinct Bitfield 1 - std::memcpy(data + 0x1C, instinct.data(), 64); // Instinct Battlefield 1 - std::memcpy(data + 0x5C, levels.data(), 128); // Monster Level Char Field + // Mapped onto the item ID for these creatures. (00 doesn't exist, 01 is rabbit, 02 is behemoth, etc.) + std::memcpy(data + 0x5C, PChar->m_PMonstrosity->levels.data(), 128); // Monster Level Bitfield } diff --git a/src/map/packets/monipulator2.cpp b/src/map/packets/monipulator2.cpp index 818bc36dcfe..4c13edf85bf 100644 --- a/src/map/packets/monipulator2.cpp +++ b/src/map/packets/monipulator2.cpp @@ -19,21 +19,41 @@ =========================================================================== */ -#include "common/socket.h" - #include "monipulator2.h" +#include "common/socket.h" +#include "entities/charentity.h" +#include "monstrosity.h" + CMonipulatorPacket2::CMonipulatorPacket2(CCharEntity* PChar) { this->setType(0x63); this->setSize(0xB4); - memset(data + 4, 0, PACKET_SIZE - 4); + if (PChar->m_PMonstrosity == nullptr) + { + return; + } + std::memset(data + 4, 0, PACKET_SIZE - 4); + + // TODO: What are these? std::array packet2 = { 0x04, 0x00, 0xB0 }; - memcpy(data + (0x04), &packet2, sizeof(packet2)); + std::memcpy(data + (0x04), &packet2, sizeof(packet2)); + + // NOTE: SE added these after-the-fact, so they're not sent in Monipulator1, but + // : we store their data there anyway. + // Slime Level + ref(0x86) = PChar->m_PMonstrosity->levels[80]; + + // Spriggan Level + ref(0x87) = PChar->m_PMonstrosity->levels[81]; - ref(0x86) = 0x0A; + // Bitpacked 2-bit values. 0 = no instincts from that species, 1 == first instinct, 2 == first and second instinct, 3 == first, second, and third instinct. + // Contains job/race instincts from the 0x03 set. Has 8 unused bytes. This is a 1:1 mapping. + // TODO: Hook up to use latter parts of instincts array + // std::memcpy(data + 0x88, PChar->m_PMonstrosity->instincts.data(), 12); // Instinct Bitfield 3 - ref(0x88) = 0x3F; + // Does not show normal monsters, only variants. Bit is 1 if the variant is owned. Length is an estimation including the possible padding. + std::memcpy(data + 0x94, PChar->m_PMonstrosity->variants.data(), 32); // Variants Bitfield } From 2b86d2009bd31b142d3395af7251e32816a24d3f Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Wed, 27 Sep 2023 13:21:38 +0100 Subject: [PATCH 004/103] Add Feretory NPCs --- scripts/zones/Feretory/npcs/Aengus.lua | 28 +++++++++++++++ scripts/zones/Feretory/npcs/Maccus.lua | 23 ++++++++++++ .../zones/Feretory/npcs/Odyssean_Passage.lua | 22 ++++++++++++ scripts/zones/Feretory/npcs/Suibhne.lua | 33 +++++++++++++++++ scripts/zones/Feretory/npcs/Teyrnon.lua | 36 +++++++++++++++++++ 5 files changed, 142 insertions(+) create mode 100644 scripts/zones/Feretory/npcs/Aengus.lua create mode 100644 scripts/zones/Feretory/npcs/Maccus.lua create mode 100644 scripts/zones/Feretory/npcs/Odyssean_Passage.lua create mode 100644 scripts/zones/Feretory/npcs/Suibhne.lua create mode 100644 scripts/zones/Feretory/npcs/Teyrnon.lua diff --git a/scripts/zones/Feretory/npcs/Aengus.lua b/scripts/zones/Feretory/npcs/Aengus.lua new file mode 100644 index 00000000000..565851991d8 --- /dev/null +++ b/scripts/zones/Feretory/npcs/Aengus.lua @@ -0,0 +1,28 @@ +----------------------------------- +-- Area: Feretory +-- NPC: Aengus +-- !pos TODO +----------------------------------- +require('scripts/globals/monstrosity') +----------------------------------- +local entity = {} + +entity.onTrade = function(player, npc, trade) +end + +entity.onTrigger = function(player, npc) + player:startEvent(13, 0, 0, 2, 0, 2, 90, 0, 0) +end + +entity.onEventUpdate = function(player, csid, option, npc) + print('update', csid, option) +end + +entity.onEventFinish = function(player, csid, option, npc) + print('finish', csid, option) + if csid == 13 and option == 1 then + -- Selected: Enter Belligerency + end +end + +return entity diff --git a/scripts/zones/Feretory/npcs/Maccus.lua b/scripts/zones/Feretory/npcs/Maccus.lua new file mode 100644 index 00000000000..d2a623676fd --- /dev/null +++ b/scripts/zones/Feretory/npcs/Maccus.lua @@ -0,0 +1,23 @@ +----------------------------------- +-- Area: Feretory +-- NPC: Maccus +-- !pos TODO +----------------------------------- +local entity = {} + +entity.onTrade = function(player, npc, trade) +end + +entity.onTrigger = function(player, npc) + player:startEvent(9, 285, 2, 2, 0, 0, 0, 0, 0) +end + +entity.onEventUpdate = function(player, csid, option, npc) + print('update', csid, option) +end + +entity.onEventFinish = function(player, csid, option, npc) + print('finish', csid, option) +end + +return entity diff --git a/scripts/zones/Feretory/npcs/Odyssean_Passage.lua b/scripts/zones/Feretory/npcs/Odyssean_Passage.lua new file mode 100644 index 00000000000..92f09aa8226 --- /dev/null +++ b/scripts/zones/Feretory/npcs/Odyssean_Passage.lua @@ -0,0 +1,22 @@ +----------------------------------- +-- Area: Feretory +-- NPC: Odyssean Passage +-- !pos TODO +----------------------------------- +local entity = {} + +entity.onTrade = function(player, npc, trade) +end + +entity.onTrigger = function(player, npc) +end + +entity.onEventUpdate = function(player, csid, option, npc) + print('update', csid, option) +end + +entity.onEventFinish = function(player, csid, option, npc) + print('finish', csid, option) +end + +return entity diff --git a/scripts/zones/Feretory/npcs/Suibhne.lua b/scripts/zones/Feretory/npcs/Suibhne.lua new file mode 100644 index 00000000000..d6ce9d48689 --- /dev/null +++ b/scripts/zones/Feretory/npcs/Suibhne.lua @@ -0,0 +1,33 @@ +----------------------------------- +-- Area: Feretory +-- NPC: Suibhne +-- !pos TODO +----------------------------------- +local entity = {} + +entity.onTrade = function(player, npc, trade) +end + +entity.onTrigger = function(player, npc) + player:startEvent(11, 1, 1, 0, 0, 0, 0, 0, 0) +end + +entity.onEventUpdate = function(player, csid, option, npc) + print('update', csid, option) +end + +entity.onEventFinish = function(player, csid, option, npc) + print('finish', csid, option) + -- Answers: + -- 1) 4. Teyrnon + -- 2) 3. Suibhne + -- 3) 1. Aengus + if csid == 11 and option == 2 then + -- Quiz failed + elseif csid == 11 and option == 6029313 then + -- Quiz succeeded + -- TODO: Unlock Bee (MON) + end +end + +return entity diff --git a/scripts/zones/Feretory/npcs/Teyrnon.lua b/scripts/zones/Feretory/npcs/Teyrnon.lua new file mode 100644 index 00000000000..92f8e6d2408 --- /dev/null +++ b/scripts/zones/Feretory/npcs/Teyrnon.lua @@ -0,0 +1,36 @@ +----------------------------------- +-- Area: Feretory +-- NPC: Teyrnon +-- !pos TODO +----------------------------------- +local entity = {} + +entity.onTrade = function(player, npc, trade) +end + +entity.onTrigger = function(player, npc) + player:startEvent(7, 0, 0, 0, 0, 0, 0, 0, 0) +end + +entity.onEventUpdate = function(player, csid, option, npc) + print('update', csid, option) + if csid == 7 and option == 0 then -- Monsters Menu + player:updateEvent(0, 0, 0, 0, 0, 0, 0, 0) + elseif csid == 7 and option == 1 then -- Instinct menu + player:updateEvent(0, 0, 0, 0, 0, 0, 0, 0) + end +end + +entity.onEventFinish = function(player, csid, option, npc) + print('finish', csid, option) + -- Support Menu: + -- option 3: Dedication 1 + -- option 259: Dedication 2 + -- option 515: Regen + -- option 771: Refresh + -- option 1027: Protect + -- option 1283: Shell + -- option 1539: Haste +end + +return entity From 46ff63f323e42d1fd6a065f01476156f94db11ca Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Wed, 27 Sep 2023 16:32:13 +0100 Subject: [PATCH 005/103] Map out variants --- scripts/commands/monstrosity.lua | 204 ++++++++++++++++++++++++++++++- 1 file changed, 201 insertions(+), 3 deletions(-) diff --git a/scripts/commands/monstrosity.lua b/scripts/commands/monstrosity.lua index 2969bb7184f..0d4d7e047cc 100644 --- a/scripts/commands/monstrosity.lua +++ b/scripts/commands/monstrosity.lua @@ -91,10 +91,11 @@ local monstrositySpecies = -- Others are not part of the regular structure, but it's useful to map them here anyway. -- Using arbitrary offsets! - DQ_SLIME = 77, + DQ_SLIME = 80, FFXIV_SPRIGGAN = 81, } +-- NOTE: This was mapped by hand, the gaps might be mistakes! local monstrosityVariants = { -- Rabbit @@ -104,6 +105,192 @@ local monstrosityVariants = -- Behemoth ELSAMOTH = 3, + + -- Tiger + LEGENDARY_TIGER = 5, + SMILODON = 6, + + -- Sheep + KARAKUL = 7, + + -- Coeurl + LYNX = 10, + COLLARED_LYNX = 11, + + -- Manticore + LEGENDARY_MANTICORE = 12, + + -- Cerberus + ORTHRUS = 13, + + -- Gnole + BIPEDAL_GNOLE = 14, + + -- Funguar + COPPERCAP = 15, + + -- Treant Sapling + TREANT = 16, + FLOWERING_TREANT = 17, + SCARLET_TINGED_TREANT = 18, + BARREN_TREANT = 19, + NECKLACED_TREANT = 20, + + -- Morbol + PYGMY_MORBOL = 21, + SCARE_MORBOL = 22, + AMERETAT = 23, + PURBOL = 24, + + -- Mandragora + KORRIGAN = 25, + LYCOPODIUM = 26, + PYGMY_MANDRAGORA = 27, + ADENIUM = 28, + PACHYPODIUM = 29, + ENLIGHTENED_MANDRAGORA = 30, + NEW_YEAR_MANDRAGORA = 31, + + -- Sabotender + SABOTENDER_FLORIDO = 32, + + -- Rafflesia + MITRASTEMA = 33, + + -- Bee + VERMILLION_AND_ONYX_BEE = 34, + ZAFFRE_BEE = 35, + + -- Beetle + ONYX_BEETLE = 36, + GAMBOGE_BEETLE = 37, + + -- Crawler + ERUCA = 38, + EMERALD_CRAWLER = 39, + PYGMY_EMERALD_CRAWLER = 40, + + -- Fly + VERMILLION_FLY = 41, + + -- Scorpion + SCOLOPENDRID = 42, + UNUSUAL_SCOLOPENDRID = 43, + + -- Spider + RETICULATED_SPIDER = 44, + VERMILLION_AND_ONYX_SPIDER = 45, + + -- Antlion + ONYX_ANTLION = 46, + FORMICEROS = 47, + + -- Diremite + ARUNDIMITE = 48, + + -- Chigoe + AZURE_CHIGOE = 49, + + -- Wamouracampa + COILED_WAMOURACAMPA = 50, + + -- Wamoura + WAMOURA = 51, + CORAL_WAMOURA = 52, + + -- Ladybug + GOLD_LADYBUG = 53, + + -- Gnat + MIDGE = 54, + + -- Lizard + ASHEN_LIZARD = 59, + + -- Raptor + EMERALD_RAPTOR = 60, + VERMILLION_RAPTOR = 61, + + -- Adamantoise + PYGMY_ADAMANTOISE = 62, + LEGENDARY_ADAMANTOISE = 63, + FERROMANTOISE = 64, + + -- Bugard + ABYSSOBUGARD = 65, + + -- Eft + TARICHUK = 66, + + -- Wivre + UNUSUAL_WIVRE = 67, + + -- Peiste + SIBILUS = 68, + + -- Slime + CLOT = 73, + GOLD_SLIME = 74, + BOIL = 75, + + -- Flan + GOLD_FLAN = 76, + BLANCMANGE = 77, + + -- Sandworm + PYGMY_SANDWORM = 78, + GIGAWORM = 79, + + -- Leech + AZURE_LEECH = 80, + OBDELLA = 81, + + -- Crab + VERMILLION_CRAB = 84, + BASKET_BURDENED_CRAB = 85, + VERMILLION_BASKET_BURDENED_CRAB = 86, + PORTER_CRAB = 87, + + -- Pugil + JAGIL = 88, + + -- Sea Monk + AZURE_SEA_MONK = 89, + + -- Uragnite + LIMASCABRA = 90, + + -- Orobon + PYGMY_OROBON = 91, + OGREBON = 92, + + -- Toad + AZURE_TOAD = 93, + VERMILLION_TOAD = 94, + + -- Bird + ONYX_BIRD = 95, + + -- Cockatrice + ZIZ = 96, + + -- Roc + LEGENDARY_ROC = 97, + GAGANA = 98, + + -- Bat + BATS = 99, + VERMILLION_BAT = 100, + VERMILLION_BATS = 101, + + -- Apkallu + INGUZA = 102, + + -- Colibri + TOUCALIBRI = 103, + + -- Amphiptere + SANGUIPTERE = 104, } commandObj.onTrigger = function(player) @@ -143,7 +330,7 @@ commandObj.onTrigger = function(player) -- Instincts -- NOTE: Since this is a bitfield, it's zero-indexed! - for key, val in pairs(monstrositySpecies) do + for _, val in pairs(monstrositySpecies) do local speciesKey = val local speciesLevel = data.levels[val] local byteOffset = math.floor(speciesKey / 4) @@ -158,7 +345,18 @@ commandObj.onTrigger = function(player) end -- Variants - data.variants[0x00] = bit.bor(data.variants[0x00] or 0, bit.lshift(0xFF, 0)) + -- Force unlock all + for _, val in pairs(monstrosityVariants) do + local speciesKey = val + local byteOffset = math.floor(speciesKey / 8) + local shiftAmount = speciesKey % 8 + print(speciesKey, byteOffset, shiftAmount) + if byteOffset < 32 then + data.variants[byteOffset] = bit.bor(data.variants[byteOffset] or 0, bit.lshift(0x01, shiftAmount)) + else + print("byteOffset out of range") + end + end player:setMonstrosity(data); end From dc853c33403b8f3505c45e072afac41f2e4895e6 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Wed, 27 Sep 2023 16:32:33 +0100 Subject: [PATCH 006/103] Stub equip with SmallPacket0x102 --- src/map/monstrosity.cpp | 22 ++++++++++------------ src/map/monstrosity.h | 3 +++ src/map/packet_system.cpp | 8 +++++++- 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/map/monstrosity.cpp b/src/map/monstrosity.cpp index d3a4f834242..04165e48ff8 100644 --- a/src/map/monstrosity.cpp +++ b/src/map/monstrosity.cpp @@ -33,18 +33,6 @@ monstrosity::MonstrosityData_t::MonstrosityData_t(uint8 face, uint8 race) , Race(race) { // TODO: Populate instinct and levels from db - // Starting levels - levels[0] = 0x01; - levels[1] = 0x00; - levels[2] = 0x00; - - instincts[0] = 0x00; - instincts[1] = 0x00; - instincts[2] = 0x00; - - variants[0] = 0x00; - variants[1] = 0x00; - variants[2] = 0x00; } void monstrosity::HandleZoneIn(CCharEntity* PChar) @@ -94,3 +82,13 @@ void monstrosity::SendFullMonstrosityUpdate(CCharEntity* PChar) PChar->pushPacket(new CMonipulatorPacket2(PChar)); PChar->updatemask |= UPDATE_LOOK; } + +void monstrosity::HandleEquipChangePacket(CCharEntity* PChar, CBasicPacket& data) +{ + if (PChar->loc.zone->GetID() != ZONE_FERETORY || PChar->m_PMonstrosity == nullptr) + { + return; + } + + // TODO +} diff --git a/src/map/monstrosity.h b/src/map/monstrosity.h index 3b7277af5df..3efbbb607dc 100644 --- a/src/map/monstrosity.h +++ b/src/map/monstrosity.h @@ -23,6 +23,8 @@ #include "common/cbasetypes.h" +#include "packets/basic.h" + #include class CCharEntity; @@ -48,4 +50,5 @@ namespace monstrosity void HandleZoneIn(CCharEntity* PChar); uint32 GetPackedMonstrosityName(CCharEntity* PChar); void SendFullMonstrosityUpdate(CCharEntity* PChar); + void HandleEquipChangePacket(CCharEntity* PChar, CBasicPacket& data); } // namespace monstrosity diff --git a/src/map/packet_system.cpp b/src/map/packet_system.cpp index 9ac21b3859b..28397cc3ded 100644 --- a/src/map/packet_system.cpp +++ b/src/map/packet_system.cpp @@ -49,6 +49,7 @@ along with this program. If not, see http://www.gnu.org/licenses/ #include "map.h" #include "message.h" #include "mob_modifier.h" +#include "monstrosity.h" #include "notoriety_container.h" #include "packet_system.h" #include "party.h" @@ -80,6 +81,7 @@ along with this program. If not, see http://www.gnu.org/licenses/ #include "lua/luautils.h" #include "packets/auction_house.h" +#include "packets/basic.h" #include "packets/bazaar_check.h" #include "packets/bazaar_close.h" #include "packets/bazaar_confirmation.h" @@ -7246,7 +7248,7 @@ void SmallPacket0x100(map_session_data_t* const PSession, CCharEntity* const PCh /************************************************************************ * * - * Set Blue Magic Spells * + * Set Blue Magic Spells / PUP Attachments / MON equip * * * ************************************************************************/ @@ -7389,6 +7391,10 @@ void SmallPacket0x102(map_session_data_t* const PSession, CCharEntity* const PCh PChar->pushPacket(new CCharJobExtraPacket(PChar, false)); puppetutils::SaveAutomaton(PChar); } + else if (PChar->loc.zone->GetID() == ZONE_FERETORY && PChar->m_PMonstrosity != nullptr) + { + monstrosity::HandleEquipChangePacket(PChar, data); + } } /************************************************************************ From 0888f39e8ff02c600d71631393d95c556570d837 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Wed, 27 Sep 2023 17:34:50 +0100 Subject: [PATCH 007/103] Hook up Slime & Spriggan instincts and variants --- scripts/commands/monstrosity.lua | 105 ++++++++++++++++++------------- src/map/packets/monipulator1.cpp | 6 +- src/map/packets/monipulator2.cpp | 7 +-- 3 files changed, 67 insertions(+), 51 deletions(-) diff --git a/scripts/commands/monstrosity.lua b/scripts/commands/monstrosity.lua index 0d4d7e047cc..57b6e364fe7 100644 --- a/scripts/commands/monstrosity.lua +++ b/scripts/commands/monstrosity.lua @@ -89,10 +89,8 @@ local monstrositySpecies = COLIBRI = 75, AMPHIPTERE = 76, - -- Others are not part of the regular structure, but it's useful to map them here anyway. - -- Using arbitrary offsets! - DQ_SLIME = 80, - FFXIV_SPRIGGAN = 81, + DQ_SLIME = 126, + FFXIV_SPRIGGAN = 127, } -- NOTE: This was mapped by hand, the gaps might be mistakes! @@ -130,26 +128,26 @@ local monstrosityVariants = COPPERCAP = 15, -- Treant Sapling - TREANT = 16, - FLOWERING_TREANT = 17, + TREANT = 16, + FLOWERING_TREANT = 17, SCARLET_TINGED_TREANT = 18, - BARREN_TREANT = 19, - NECKLACED_TREANT = 20, + BARREN_TREANT = 19, + NECKLACED_TREANT = 20, -- Morbol PYGMY_MORBOL = 21, SCARE_MORBOL = 22, - AMERETAT = 23, - PURBOL = 24, + AMERETAT = 23, + PURBOL = 24, -- Mandragora - KORRIGAN = 25, - LYCOPODIUM = 26, - PYGMY_MANDRAGORA = 27, - ADENIUM = 28, - PACHYPODIUM = 29, + KORRIGAN = 25, + LYCOPODIUM = 26, + PYGMY_MANDRAGORA = 27, + ADENIUM = 28, + PACHYPODIUM = 29, ENLIGHTENED_MANDRAGORA = 30, - NEW_YEAR_MANDRAGORA = 31, + NEW_YEAR_MANDRAGORA = 31, -- Sabotender SABOTENDER_FLORIDO = 32, @@ -159,31 +157,31 @@ local monstrosityVariants = -- Bee VERMILLION_AND_ONYX_BEE = 34, - ZAFFRE_BEE = 35, + ZAFFRE_BEE = 35, -- Beetle - ONYX_BEETLE = 36, + ONYX_BEETLE = 36, GAMBOGE_BEETLE = 37, -- Crawler - ERUCA = 38, - EMERALD_CRAWLER = 39, + ERUCA = 38, + EMERALD_CRAWLER = 39, PYGMY_EMERALD_CRAWLER = 40, -- Fly VERMILLION_FLY = 41, -- Scorpion - SCOLOPENDRID = 42, + SCOLOPENDRID = 42, UNUSUAL_SCOLOPENDRID = 43, -- Spider - RETICULATED_SPIDER = 44, + RETICULATED_SPIDER = 44, VERMILLION_AND_ONYX_SPIDER = 45, -- Antlion ONYX_ANTLION = 46, - FORMICEROS = 47, + FORMICEROS = 47, -- Diremite ARUNDIMITE = 48, @@ -195,7 +193,7 @@ local monstrosityVariants = COILED_WAMOURACAMPA = 50, -- Wamoura - WAMOURA = 51, + WAMOURA = 51, CORAL_WAMOURA = 52, -- Ladybug @@ -208,13 +206,13 @@ local monstrosityVariants = ASHEN_LIZARD = 59, -- Raptor - EMERALD_RAPTOR = 60, + EMERALD_RAPTOR = 60, VERMILLION_RAPTOR = 61, -- Adamantoise - PYGMY_ADAMANTOISE = 62, + PYGMY_ADAMANTOISE = 62, LEGENDARY_ADAMANTOISE = 63, - FERROMANTOISE = 64, + FERROMANTOISE = 64, -- Bugard ABYSSOBUGARD = 65, @@ -229,27 +227,27 @@ local monstrosityVariants = SIBILUS = 68, -- Slime - CLOT = 73, + CLOT = 73, GOLD_SLIME = 74, - BOIL = 75, + BOIL = 75, -- Flan - GOLD_FLAN = 76, + GOLD_FLAN = 76, BLANCMANGE = 77, -- Sandworm PYGMY_SANDWORM = 78, - GIGAWORM = 79, + GIGAWORM = 79, -- Leech AZURE_LEECH = 80, - OBDELLA = 81, + OBDELLA = 81, -- Crab - VERMILLION_CRAB = 84, - BASKET_BURDENED_CRAB = 85, + VERMILLION_CRAB = 84, + BASKET_BURDENED_CRAB = 85, VERMILLION_BASKET_BURDENED_CRAB = 86, - PORTER_CRAB = 87, + PORTER_CRAB = 87, -- Pugil JAGIL = 88, @@ -262,10 +260,10 @@ local monstrosityVariants = -- Orobon PYGMY_OROBON = 91, - OGREBON = 92, + OGREBON = 92, -- Toad - AZURE_TOAD = 93, + AZURE_TOAD = 93, VERMILLION_TOAD = 94, -- Bird @@ -276,11 +274,11 @@ local monstrosityVariants = -- Roc LEGENDARY_ROC = 97, - GAGANA = 98, + GAGANA = 98, -- Bat - BATS = 99, - VERMILLION_BAT = 100, + BATS = 99, + VERMILLION_BAT = 100, VERMILLION_BATS = 101, -- Apkallu @@ -291,6 +289,14 @@ local monstrosityVariants = -- Amphiptere SANGUIPTERE = 104, + + -- Slime + SHE_SLIME = 252, + METAL_SLIME = 253, + + -- Spriggan + SPRIGGAN_C = 254, + SPRIGGAN_G = 255, } commandObj.onTrigger = function(player) @@ -309,15 +315,17 @@ commandObj.onTrigger = function(player) race = 11, -- 1 byte per entry, mapped out to monstrositySpecies table + -- (0 - 127) levels = { }, - -- Bitfield + -- Bitfield (0 - 63) instincts = { }, + -- Bitfield (0 - 31) variants = { }, @@ -325,7 +333,7 @@ commandObj.onTrigger = function(player) -- Set all levels to 99 for _, val in pairs(monstrositySpecies) do - data.levels[val] = 99 + data.levels[val] = 30 end -- Instincts @@ -336,7 +344,14 @@ commandObj.onTrigger = function(player) local byteOffset = math.floor(speciesKey / 4) local unlockAmount = math.floor(speciesLevel / 30) local shiftAmount = (speciesKey * 2) % 8 - -- print(key, speciesKey, speciesLevel, unlockAmount, byteOffset .. ':' .. shiftAmount) + + -- Special case for writing Slime & Spriggan data at the end of the 64-byte array + if byteOffset == 31 then + byteOffset = 63 + end + + -- print(speciesKey, speciesLevel, unlockAmount, byteOffset .. ':' .. shiftAmount) + if byteOffset < 64 then data.instincts[byteOffset] = bit.bor(data.instincts[byteOffset] or 0, bit.lshift(unlockAmount, shiftAmount)) else @@ -350,7 +365,9 @@ commandObj.onTrigger = function(player) local speciesKey = val local byteOffset = math.floor(speciesKey / 8) local shiftAmount = speciesKey % 8 - print(speciesKey, byteOffset, shiftAmount) + + -- print(speciesKey, byteOffset, shiftAmount) + if byteOffset < 32 then data.variants[byteOffset] = bit.bor(data.variants[byteOffset] or 0, bit.lshift(0x01, shiftAmount)) else diff --git a/src/map/packets/monipulator1.cpp b/src/map/packets/monipulator1.cpp index 5b711067a2b..ff4f87c908e 100644 --- a/src/map/packets/monipulator1.cpp +++ b/src/map/packets/monipulator1.cpp @@ -39,13 +39,13 @@ CMonipulatorPacket1::CMonipulatorPacket1(CCharEntity* PChar) ref(0x04) = 0x03; // Update Type ref(0x06) = 0xD8; // Variable Data Size - ref(0x08) = 0xFCFE; // Species? Also seen 0x3F1F - // ref(0x0A) = 0x0B45; // Flags? Also seen 0x0B46 + ref(0x08) = 0x2412; //0xFCFE; // Species? Also seen 0x3F1F + ref(0x0A) = 0x190C; // Flags? Also seen 0x0B46 ref(0x0C) = 0; // Monstrosity Rank (0 = Mon, 1 = NM, 2 = HNM) // Falculties? - // ref(0x10) = 0xFF; + // ref(0x10) = 0xEC; // ref(0x11) = 0xFF; ref(0x12) = charutils::GetPoints(PChar, "infamy"); diff --git a/src/map/packets/monipulator2.cpp b/src/map/packets/monipulator2.cpp index 4c13edf85bf..486bee835da 100644 --- a/src/map/packets/monipulator2.cpp +++ b/src/map/packets/monipulator2.cpp @@ -41,13 +41,12 @@ CMonipulatorPacket2::CMonipulatorPacket2(CCharEntity* PChar) std::array packet2 = { 0x04, 0x00, 0xB0 }; std::memcpy(data + (0x04), &packet2, sizeof(packet2)); - // NOTE: SE added these after-the-fact, so they're not sent in Monipulator1, but - // : we store their data there anyway. + // NOTE: SE added these after-the-fact, so they're not sent in Monipulator1 and they're at the end of the array! // Slime Level - ref(0x86) = PChar->m_PMonstrosity->levels[80]; + ref(0x86) = PChar->m_PMonstrosity->levels[126]; // Spriggan Level - ref(0x87) = PChar->m_PMonstrosity->levels[81]; + ref(0x87) = PChar->m_PMonstrosity->levels[127]; // Bitpacked 2-bit values. 0 = no instincts from that species, 1 == first instinct, 2 == first and second instinct, 3 == first, second, and third instinct. // Contains job/race instincts from the 0x03 set. Has 8 unused bytes. This is a 1:1 mapping. From 593daef34585fcdb6c57716ef79dcb51cb1eed8c Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Wed, 27 Sep 2023 17:59:01 +0100 Subject: [PATCH 008/103] Map out purchasableInstincts --- scripts/commands/monstrosity.lua | 55 +++++++++++++++++++++++++++++++- src/map/packets/monipulator1.cpp | 9 ++++-- src/map/packets/monipulator2.cpp | 4 +-- 3 files changed, 62 insertions(+), 6 deletions(-) diff --git a/scripts/commands/monstrosity.lua b/scripts/commands/monstrosity.lua index 57b6e364fe7..e83970aa447 100644 --- a/scripts/commands/monstrosity.lua +++ b/scripts/commands/monstrosity.lua @@ -299,6 +299,45 @@ local monstrosityVariants = SPRIGGAN_G = 255, } +local purchasableInstincts = +{ + -- Default + HUME_I = 0, + ELVAAN_I = 1, + TARU_I = 2, + MITHRA_I = 3, + GALKA_I = 4, + + HUME_II = 5, + ELVAAN_II = 6, + TARU_II = 7, + MITHRA_II = 8, + GALKA_II = 9, + + WAR = 10, + MNK = 11, + WHM = 12, + BLM = 13, + RDM = 14, + THF = 15, + PLD = 16, + DRK = 17, + BST = 18, + BRD = 19, + RNG = 20, + SAM = 21, + NIN = 22, + DRG = 23, + SMN = 24, + BLU = 25, + COR = 26, + PUP = 27, + DNC = 28, + SCH = 29, + GEO = 30, + RUN = 31, +} + commandObj.onTrigger = function(player) --[[ if player:getCharVar('MONSTROSITY_START') == 1 then @@ -336,7 +375,7 @@ commandObj.onTrigger = function(player) data.levels[val] = 30 end - -- Instincts + -- Instincts by MON level -- NOTE: Since this is a bitfield, it's zero-indexed! for _, val in pairs(monstrositySpecies) do local speciesKey = val @@ -359,6 +398,20 @@ commandObj.onTrigger = function(player) end end + -- Instincts (Purchasable) + for _, val in pairs(purchasableInstincts) do + local byteOffset = 20 + math.floor(val / 8) + local shiftAmount = val % 8 + + -- print(val, byteOffset, shiftAmount) + + if byteOffset >= 20 and byteOffset < 24 then + data.instincts[byteOffset] = bit.bor(data.instincts[byteOffset] or 0, bit.lshift(0x01, shiftAmount)) + else + print("byteOffset out of range") + end + end + -- Variants -- Force unlock all for _, val in pairs(monstrosityVariants) do diff --git a/src/map/packets/monipulator1.cpp b/src/map/packets/monipulator1.cpp index ff4f87c908e..d69c29eceb2 100644 --- a/src/map/packets/monipulator1.cpp +++ b/src/map/packets/monipulator1.cpp @@ -44,12 +44,15 @@ CMonipulatorPacket1::CMonipulatorPacket1(CCharEntity* PChar) ref(0x0C) = 0; // Monstrosity Rank (0 = Mon, 1 = NM, 2 = HNM) - // Falculties? - // ref(0x10) = 0xEC; - // ref(0x11) = 0xFF; + // Unknown + ref(0x10) = 0xEC; + ref(0x11) = 0x00; ref(0x12) = charutils::GetPoints(PChar, "infamy"); + // Unknown + ref(0x14) = 0x2C; + // Bitpacked 2-bit values. 0 = no instincts from that species, 1 == first instinct, 2 == first and second instinct, 3 == first, second, and third instinct. std::memcpy(data + 0x1C, PChar->m_PMonstrosity->instincts.data(), 64); // Instinct Bitfield 1 diff --git a/src/map/packets/monipulator2.cpp b/src/map/packets/monipulator2.cpp index 486bee835da..5c51b840223 100644 --- a/src/map/packets/monipulator2.cpp +++ b/src/map/packets/monipulator2.cpp @@ -50,8 +50,8 @@ CMonipulatorPacket2::CMonipulatorPacket2(CCharEntity* PChar) // Bitpacked 2-bit values. 0 = no instincts from that species, 1 == first instinct, 2 == first and second instinct, 3 == first, second, and third instinct. // Contains job/race instincts from the 0x03 set. Has 8 unused bytes. This is a 1:1 mapping. - // TODO: Hook up to use latter parts of instincts array - // std::memcpy(data + 0x88, PChar->m_PMonstrosity->instincts.data(), 12); // Instinct Bitfield 3 + // Since this has 8 unused bytes, we're only going to use 4 from instincts[20:23] + std::memcpy(data + 0x88, PChar->m_PMonstrosity->instincts.data() + 20, 4); // Instinct Bitfield 3 // Does not show normal monsters, only variants. Bit is 1 if the variant is owned. Length is an estimation including the possible padding. std::memcpy(data + 0x94, PChar->m_PMonstrosity->variants.data(), 32); // Variants Bitfield From d1e439f755aa4e6c41475e29dba4b1e48ef9bca8 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Wed, 27 Sep 2023 22:27:57 +0100 Subject: [PATCH 009/103] Progress checkin --- scripts/commands/monstrosity.lua | 5 +- .../zones/Feretory/npcs/Odyssean_Passage.lua | 3 + src/map/lua/lua_baseentity.cpp | 3 - src/map/monstrosity.cpp | 49 ++++++- src/map/monstrosity.h | 8 + src/map/packets/char_job_extra.cpp | 138 +++++++++++++++++- src/map/packets/char_jobs.cpp | 25 ++++ src/map/packets/monipulator1.cpp | 2 +- 8 files changed, 221 insertions(+), 12 deletions(-) diff --git a/scripts/commands/monstrosity.lua b/scripts/commands/monstrosity.lua index e83970aa447..7e030ab19cc 100644 --- a/scripts/commands/monstrosity.lua +++ b/scripts/commands/monstrosity.lua @@ -350,9 +350,6 @@ commandObj.onTrigger = function(player) ]] local data = { - face = 74, - race = 11, - -- 1 byte per entry, mapped out to monstrositySpecies table -- (0 - 127) levels = @@ -372,7 +369,7 @@ commandObj.onTrigger = function(player) -- Set all levels to 99 for _, val in pairs(monstrositySpecies) do - data.levels[val] = 30 + data.levels[val] = 99 end -- Instincts by MON level diff --git a/scripts/zones/Feretory/npcs/Odyssean_Passage.lua b/scripts/zones/Feretory/npcs/Odyssean_Passage.lua index 92f09aa8226..8946f0e0e97 100644 --- a/scripts/zones/Feretory/npcs/Odyssean_Passage.lua +++ b/scripts/zones/Feretory/npcs/Odyssean_Passage.lua @@ -9,14 +9,17 @@ entity.onTrade = function(player, npc, trade) end entity.onTrigger = function(player, npc) + player:startEvent(5, 0, 0, 0, 0, 1, 0, 0, 0) end entity.onEventUpdate = function(player, csid, option, npc) print('update', csid, option) + player:updateEvent(0, 0, 0, 0, 1, 0, 0, 0) end entity.onEventFinish = function(player, csid, option, npc) print('finish', csid, option) + -- Option 1: Leave? end return entity diff --git a/src/map/lua/lua_baseentity.cpp b/src/map/lua/lua_baseentity.cpp index 13f2da2075f..58982d1bdf6 100644 --- a/src/map/lua/lua_baseentity.cpp +++ b/src/map/lua/lua_baseentity.cpp @@ -6314,9 +6314,6 @@ void CLuaBaseEntity::setMonstrosity(sol::table table) return; } - PChar->m_PMonstrosity->Face = table.get("face"); - PChar->m_PMonstrosity->Race = table.get("race"); - for (auto const& [keyObj, valObj] : table.get("levels")) { uint8 key = keyObj.as(); diff --git a/src/map/monstrosity.cpp b/src/map/monstrosity.cpp index 04165e48ff8..bf211b89ea5 100644 --- a/src/map/monstrosity.cpp +++ b/src/map/monstrosity.cpp @@ -23,6 +23,8 @@ #include "entities/charentity.h" +#include "packets/char_jobs.h" +#include "packets/char_job_extra.h" #include "packets/monipulator1.h" #include "packets/monipulator2.h" @@ -31,6 +33,9 @@ monstrosity::MonstrosityData_t::MonstrosityData_t(uint8 face, uint8 race) : Face(face) , Race(race) +, Species(0x0001) +, Name1(0x00) +, Name2(0x00) { // TODO: Populate instinct and levels from db } @@ -61,11 +66,11 @@ uint32 monstrosity::GetPackedMonstrosityName(CCharEntity* PChar) // CD: Tempest // F5: Zenith // F6: Zero - uint8 c = 0xF6; + uint8 c = PChar->m_PMonstrosity->Name1; // Adjective 2 // Same values as above - uint8 d = 0xCD; + uint8 d = PChar->m_PMonstrosity->Name2; // Packed as LE return (d << 24) + (c << 16) + (b << 8) + (a << 0); @@ -78,8 +83,15 @@ void monstrosity::SendFullMonstrosityUpdate(CCharEntity* PChar) return; } + // TODO: Safety checks: + // : The species box on the UI should never be empty - everything breaks if that happens. + // : We should detect a bad state and fall back to being a Lv1 Bunny if that happens. + PChar->pushPacket(new CMonipulatorPacket1(PChar)); PChar->pushPacket(new CMonipulatorPacket2(PChar)); + PChar->pushPacket(new CCharJobsPacket(PChar)); + PChar->pushPacket(new CCharJobExtraPacket(PChar, true)); + PChar->pushPacket(new CCharJobExtraPacket(PChar, false)); PChar->updatemask |= UPDATE_LOOK; } @@ -90,5 +102,36 @@ void monstrosity::HandleEquipChangePacket(CCharEntity* PChar, CBasicPacket& data return; } - // TODO + // TODO: Validate that we have the species/instinct that we're trying to equip + + // TODO: Validate that we'll have enough points to hold our instincts when we equip + + PChar->m_PMonstrosity->Species = data.ref(0x0C); + + // Remove All + if (data.ref(0x16) == 0xFFFF) + { + for (std::size_t idx = 0; idx < 12; idx +=2) + { + PChar->m_PMonstrosity->EquippedInstincts[idx] = 0x0000; + } + } + else // Set + { + for (std::size_t idx = 0; idx < 12; idx +=2) + { + if (data.ref(0x10 + idx) != 0) + { + PChar->m_PMonstrosity->EquippedInstincts[idx] = data.ref(0x10 + idx); + } + } + } + + // TODO: Unset individual instincts + + PChar->m_PMonstrosity->Name1 = data.ref(0x28); + PChar->m_PMonstrosity->Name2 = data.ref(0x29); + + // TODO: Is this too much traffic? + SendFullMonstrosityUpdate(PChar); } diff --git a/src/map/monstrosity.h b/src/map/monstrosity.h index 3efbbb607dc..ec1ca0fd8e9 100644 --- a/src/map/monstrosity.h +++ b/src/map/monstrosity.h @@ -40,6 +40,14 @@ namespace monstrosity uint8 Face; uint8 Race; + // TODO: Whats the link between look (Face+Race), Species, and Flags? + uint16 Species; + + uint8 Name1; + uint8 Name2; + + std::array EquippedInstincts{ 0 }; + // TODO: Extend this to be large enough to hold Slime and Spriggan levels // : but don't use sizeof() with this structure. std::array levels{ 0 }; diff --git a/src/map/packets/char_job_extra.cpp b/src/map/packets/char_job_extra.cpp index 0f1df2bc4fc..3bfa926707f 100644 --- a/src/map/packets/char_job_extra.cpp +++ b/src/map/packets/char_job_extra.cpp @@ -29,13 +29,19 @@ #include "entities/automatonentity.h" #include "entities/charentity.h" #include "merit.h" +#include "monstrosity.h" + +namespace +{ + uint8 JOB_MON = 23; +} // namespace CCharJobExtraPacket::CCharJobExtraPacket(CCharEntity* PChar, bool mjob) { this->setType(0x44); this->setSize(0xA0); - JOBTYPE job = JOB_NON; + uint8 job = JOB_NON; if (mjob) { @@ -46,6 +52,11 @@ CCharJobExtraPacket::CCharJobExtraPacket(CCharEntity* PChar, bool mjob) job = PChar->GetSJob(); } + if (PChar->loc.zone->GetID() == ZONE_FERETORY && PChar->m_PMonstrosity != nullptr) + { + job = JOB_MON; + } + ref(0x04) = job; if (!mjob) { @@ -127,4 +138,129 @@ CCharJobExtraPacket::CCharJobExtraPacket(CCharEntity* PChar, bool mjob) ref(0x9C) = PChar->getMod(Mod::AUTO_ELEM_CAPACITY); } + else if (job == JOB_MON && PChar->loc.zone->GetID() == ZONE_FERETORY && PChar->m_PMonstrosity != nullptr) + { + /* + | 0 1 2 3 4 5 6 7 8 9 A B C D E F | 0123456789ABCDEF + -----+---------------------------------------------------- -+------------------ + 0 | 44 50 55 01 17 00 00 00 FD 01 00 FF FA 02 05 03 | DPU............. + 10 | 07 03 09 03 00 00 00 00 00 00 00 00 00 00 00 00 | ................ + 20 | 00 00 00 00 09 7D 08 00 28 04 64 02 33 00 64 20 | .....}..(.d.3.d + 30 | 00 6C FB 38 00 00 20 00 00 00 00 00 20 A0 03 00 | .l.8.. ..... ... + 40 | 4C DC E3 28 00 00 00 00 00 00 00 00 00 00 00 00 | L..(............ + 50 | 00 00 00 00 FD 81 09 7D 00 06 00 00 7B 00 00 00 | .......}....{... + 60 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ + 70 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ + 80 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ + 90 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ + */ + + ref(0x08) = PChar->m_PMonstrosity->Species; + + for (std::size_t idx = 0; idx < 12; idx += 2) + { + ref(0x0C + idx) = PChar->m_PMonstrosity->EquippedInstincts[idx]; + } + + /* + ref(0x0C) = 0x00; + ref(0x0D) = 0x03; + ref(0x0E) = 0x00; + ref(0x0F) = 0x03; + + + ref(0x10) = 0x07; + ref(0x11) = 0x03; + ref(0x12) = 0x09; + ref(0x13) = 0x03; + ref(0x14) = 0x00; + ref(0x15) = 0x00; + ref(0x16) = 0x00; + ref(0x17) = 0x00; + ref(0x18) = 0x00; + ref(0x19) = 0x00; + ref(0x1A) = 0x00; + ref(0x1B) = 0x00; + ref(0x1C) = 0x00; + ref(0x1D) = 0x00; + ref(0x1E) = 0x00; + ref(0x1F) = 0x00; + + ref(0x20) = 0x00; + ref(0x21) = 0x00; + ref(0x22) = 0x00; + ref(0x23) = 0x00; + ref(0x24) = 0x09; + ref(0x25) = 0x7D; + ref(0x26) = 0x08; + ref(0x27) = 0x00; + ref(0x28) = 0x28; + ref(0x29) = 0x04; + ref(0x2A) = 0x64; + ref(0x2B) = 0x02; + ref(0x2C) = 0x33; + ref(0x2D) = 0x00; + ref(0x2E) = 0x64; + ref(0x2F) = 0x20; + + ref(0x30) = 0x00; + ref(0x31) = 0x6C; + ref(0x32) = 0xFB; + ref(0x33) = 0x38; + ref(0x34) = 0x00; + ref(0x35) = 0x00; + ref(0x36) = 0x20; + ref(0x37) = 0x00; + ref(0x38) = 0x00; + ref(0x39) = 0x00; + ref(0x3A) = 0x00; + ref(0x3B) = 0x00; + ref(0x3C) = 0x20; + ref(0x3D) = 0xA0; + ref(0x3E) = 0x03; + ref(0x3F) = 0x00; + + ref(0x40) = 0x4C; + ref(0x41) = 0xDC; + ref(0x42) = 0xE3; + ref(0x43) = 0x28; + ref(0x44) = 0x00; + ref(0x45) = 0x00; + ref(0x46) = 0x00; + ref(0x47) = 0x00; + ref(0x48) = 0x00; + ref(0x49) = 0x00; + ref(0x4A) = 0x00; + ref(0x4B) = 0x00; + ref(0x4C) = 0x00; + ref(0x4D) = 0x00; + ref(0x4E) = 0x00; + ref(0x4F) = 0x00; + + ref(0x50) = 0x00; + ref(0x51) = 0x00; + ref(0x52) = 0x00; + ref(0x53) = 0x00; + ref(0x54) = 0xFD; + ref(0x55) = 0x81; + ref(0x56) = 0x09; + ref(0x57) = 0x7D; + ref(0x58) = 0x00; + ref(0x59) = 0x06; + ref(0x5A) = 0x00; + ref(0x5B) = 0x00; + ref(0x5C) = 0x7B; + ref(0x5D) = 0x00; + ref(0x5E) = 0x00; + ref(0x5F) = 0x00; + + // Random things from other packet caps + ref(0x6C) = 0x01; + ref(0x6E) = 0x01; + ref(0x77) = 0x01; + ref(0x87) = 0x24; + ref(0x8A) = 0x28; + ref(0x96) = 0x19; + */ + } } diff --git a/src/map/packets/char_jobs.cpp b/src/map/packets/char_jobs.cpp index 69a0a398deb..5ba7794be79 100644 --- a/src/map/packets/char_jobs.cpp +++ b/src/map/packets/char_jobs.cpp @@ -25,6 +25,7 @@ #include "char_jobs.h" #include "entities/charentity.h" +#include "monstrosity.h" CCharJobsPacket::CCharJobsPacket(CCharEntity* PChar) { @@ -56,4 +57,28 @@ CCharJobsPacket::CCharJobsPacket(CCharEntity* PChar) ref(0x68) = 0; // Is job mastered, and has Master Breaker KI ref(0x6D) = 0; // Master Level + + if (PChar->m_PMonstrosity) + { + ref(0x08) = 0x17; // JOB_MON + ref(0x0B) = 0x17; // JOB_MON + + ref(0x10) = 0x01; + /* + ref(0x2E) = 0x07; + ref(0x30) = 0x07; + ref(0x32) = 0x0A; + ref(0x34) = 0x07; + ref(0x36) = 0x04; + ref(0x38) = 0x07; + ref(0x3A) = 0x04; + + ref(0x48) = 0x01; + + ref(0x5F) = 0x10; // MON level + ref(0x64) = 0x03; + ref(0x66) = 0x02; + ref(0x69) = 0x20; + */ + } } diff --git a/src/map/packets/monipulator1.cpp b/src/map/packets/monipulator1.cpp index d69c29eceb2..2d641f1d1d5 100644 --- a/src/map/packets/monipulator1.cpp +++ b/src/map/packets/monipulator1.cpp @@ -39,7 +39,7 @@ CMonipulatorPacket1::CMonipulatorPacket1(CCharEntity* PChar) ref(0x04) = 0x03; // Update Type ref(0x06) = 0xD8; // Variable Data Size - ref(0x08) = 0x2412; //0xFCFE; // Species? Also seen 0x3F1F + ref(0x08) = PChar->m_PMonstrosity->Species; //0x2412; //0xFCFE; // Species? Also seen 0x3F1F ref(0x0A) = 0x190C; // Flags? Also seen 0x0B46 ref(0x0C) = 0; // Monstrosity Rank (0 = Mon, 1 = NM, 2 = HNM) From c18568486765a24d86bc852bdc330bcf719d885d Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Wed, 27 Sep 2023 22:42:54 +0100 Subject: [PATCH 010/103] Add note about Monstrosity quest --- scripts/quests/hiddenQuests/Monstrosity_Unlock.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/quests/hiddenQuests/Monstrosity_Unlock.lua b/scripts/quests/hiddenQuests/Monstrosity_Unlock.lua index 709e7dc8077..a23278b8c86 100644 --- a/scripts/quests/hiddenQuests/Monstrosity_Unlock.lua +++ b/scripts/quests/hiddenQuests/Monstrosity_Unlock.lua @@ -2,10 +2,11 @@ -- Unlocking Monstrosity ----------------------------------- +-- TODO: There is a Monstrosity Quest in "Other Areas", we should be using that! + local quest = HiddenQuest:new('MonstrosityUnlock') -- TODO: Handle xi.settings.main.ENABLE_MONSTROSITY --- TODO: Hide completion behind a UniqueEvent flag quest.sections = { From 1b1ee5c9af03fa78bbac7aae74ee4eacf9f57a5c Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Fri, 29 Sep 2023 17:23:41 +0100 Subject: [PATCH 011/103] More progress check in --- scripts/commands/monstrosity.lua | 16 +++++++- src/map/lua/lua_baseentity.cpp | 4 ++ src/map/monstrosity.cpp | 61 ++++++++++++++++++++++------- src/map/monstrosity.h | 14 +++---- src/map/packets/char.cpp | 3 +- src/map/packets/char_appearance.cpp | 3 +- src/map/packets/monipulator1.cpp | 2 +- src/map/packets/zone_in.cpp | 3 +- 8 files changed, 74 insertions(+), 32 deletions(-) diff --git a/scripts/commands/monstrosity.lua b/scripts/commands/monstrosity.lua index 7e030ab19cc..1349ef0c016 100644 --- a/scripts/commands/monstrosity.lua +++ b/scripts/commands/monstrosity.lua @@ -3,7 +3,7 @@ local commandObj = {} commandObj.cmdprops = { permission = 1, - parameters = '' + parameters = 'i' } local monstrositySpecies = @@ -338,7 +338,8 @@ local purchasableInstincts = RUN = 31, } -commandObj.onTrigger = function(player) +commandObj.onTrigger = function(player, n) + print(n) --[[ if player:getCharVar('MONSTROSITY_START') == 1 then player:setCharVar('MONSTROSITY_START', 0) @@ -348,8 +349,19 @@ commandObj.onTrigger = function(player) player:setPos(player:getXPos(), player:getYPos(), player:getZPos(), player:getRotPos(), player:getZoneID()) ]] + + -- 0x010C: Rabbit + -- 0x010D: Onyx Rabbit + -- 0x010E: Alabaster Rabbit + -- 0x0791: Lapinion + + -- 0x0B48: New Years Mandragora + local data = { + look = 0x0791, + name = n, + -- 1 byte per entry, mapped out to monstrositySpecies table -- (0 - 127) levels = diff --git a/src/map/lua/lua_baseentity.cpp b/src/map/lua/lua_baseentity.cpp index 58982d1bdf6..6fd8dc6de14 100644 --- a/src/map/lua/lua_baseentity.cpp +++ b/src/map/lua/lua_baseentity.cpp @@ -6314,6 +6314,10 @@ void CLuaBaseEntity::setMonstrosity(sol::table table) return; } + PChar->m_PMonstrosity->Look = table.get("look"); + + PChar->m_PMonstrosity->NameBase = table.get("name"); + for (auto const& [keyObj, valObj] : table.get("levels")) { uint8 key = keyObj.as(); diff --git a/src/map/monstrosity.cpp b/src/map/monstrosity.cpp index bf211b89ea5..bf485407773 100644 --- a/src/map/monstrosity.cpp +++ b/src/map/monstrosity.cpp @@ -21,8 +21,11 @@ #include "monstrosity.h" +#include "common/logging.h" + #include "entities/charentity.h" +#include "packets/char_appearance.h" #include "packets/char_jobs.h" #include "packets/char_job_extra.h" #include "packets/monipulator1.h" @@ -30,12 +33,13 @@ #include "utils/charutils.h" -monstrosity::MonstrosityData_t::MonstrosityData_t(uint8 face, uint8 race) -: Face(face) -, Race(race) -, Species(0x0001) -, Name1(0x00) -, Name2(0x00) +monstrosity::MonstrosityData_t::MonstrosityData_t() +: Species(0x0001) +, Flags(0x0B46) +, Look(0xFD81) +, NameBase(0x00) +, NamePrefix1(0x00) +, NamePrefix2(0x00) { // TODO: Populate instinct and levels from db } @@ -45,7 +49,7 @@ void monstrosity::HandleZoneIn(CCharEntity* PChar) // TODO: Check we're about to enter monstrosity, charvar, flag, etc. if (charutils::GetCharVar(PChar, "MONSTROSITY_START") == 1) { - PChar->m_PMonstrosity = std::make_unique(72, 11); + PChar->m_PMonstrosity = std::make_unique(); PChar->updatemask |= UPDATE_LOOK; } } @@ -54,23 +58,23 @@ uint32 monstrosity::GetPackedMonstrosityName(CCharEntity* PChar) { // Monstrosity Name Ids? // If populated, the monstrosity icon will appear - uint8 a = 0x1F; + // uint8 a = 0x1F; // Mob Type // 0x80: Scorpion // 0x81: Mandragora - uint8 b = 0x81; + uint16 a = PChar->m_PMonstrosity->NameBase; // Adjective 1 (optional) // 01: Abashed // CD: Tempest // F5: Zenith // F6: Zero - uint8 c = PChar->m_PMonstrosity->Name1; + uint8 c = PChar->m_PMonstrosity->NamePrefix1; // Adjective 2 // Same values as above - uint8 d = PChar->m_PMonstrosity->Name2; + uint8 d = PChar->m_PMonstrosity->NamePrefix2; // Packed as LE return (d << 24) + (c << 16) + (b << 8) + (a << 0); @@ -92,6 +96,7 @@ void monstrosity::SendFullMonstrosityUpdate(CCharEntity* PChar) PChar->pushPacket(new CCharJobsPacket(PChar)); PChar->pushPacket(new CCharJobExtraPacket(PChar, true)); PChar->pushPacket(new CCharJobExtraPacket(PChar, false)); + PChar->pushPacket(new CCharAppearancePacket(PChar)); PChar->updatemask |= UPDATE_LOOK; } @@ -106,7 +111,28 @@ void monstrosity::HandleEquipChangePacket(CCharEntity* PChar, CBasicPacket& data // TODO: Validate that we'll have enough points to hold our instincts when we equip - PChar->m_PMonstrosity->Species = data.ref(0x0C); + PChar->m_PMonstrosity->Species = data.ref(0x0C); + // 0x0D holds the variant index + + // -- 0x010C: Rabbit + // -- 0x010D: Onyx Rabbit + // -- 0x010E: Alabaster Rabbit + // -- 0x0791: Lapinion + std::unordered_map lookMap = + { + { 0x0001, 0x010C }, + { 0x0100, 0x010D }, + }; + + // TODO: Move this information to a db table that's loaded at startup: + // { mon_id, species_code, look, family, { starting_stats } } + + PChar->m_PMonstrosity->Look = lookMap[PChar->m_PMonstrosity->Species]; + + ShowInfo(fmt::format("Species: {}, Flags: {}, Looks: {}", + PChar->m_PMonstrosity->Species, + PChar->m_PMonstrosity->Flags, + PChar->m_PMonstrosity->Look).c_str()); // Remove All if (data.ref(0x16) == 0xFFFF) @@ -127,10 +153,15 @@ void monstrosity::HandleEquipChangePacket(CCharEntity* PChar, CBasicPacket& data } } - // TODO: Unset individual instincts + std::string EquipStr = ""; + for (std::size_t idx = 0; idx < 12; ++idx) + { + EquipStr += fmt::format("{}: {}, ", idx, PChar->m_PMonstrosity->EquippedInstincts[idx]); + } + ShowInfo(EquipStr.c_str()); - PChar->m_PMonstrosity->Name1 = data.ref(0x28); - PChar->m_PMonstrosity->Name2 = data.ref(0x29); + PChar->m_PMonstrosity->NamePrefix1 = data.ref(0x28); + PChar->m_PMonstrosity->NamePrefix2 = data.ref(0x29); // TODO: Is this too much traffic? SendFullMonstrosityUpdate(PChar); diff --git a/src/map/monstrosity.h b/src/map/monstrosity.h index ec1ca0fd8e9..6c7f8acc347 100644 --- a/src/map/monstrosity.h +++ b/src/map/monstrosity.h @@ -34,17 +34,15 @@ namespace monstrosity struct MonstrosityData_t { public: - MonstrosityData_t(uint8 face, uint8 race); + MonstrosityData_t(); - // TODO: Should these be a single uint16? - uint8 Face; - uint8 Race; - - // TODO: Whats the link between look (Face+Race), Species, and Flags? uint16 Species; + uint16 Flags; + uint16 Look; - uint8 Name1; - uint8 Name2; + uint16 NameBase; + uint8 NamePrefix1; + uint8 NamePrefix2; std::array EquippedInstincts{ 0 }; diff --git a/src/map/packets/char.cpp b/src/map/packets/char.cpp index 20c29927d35..654056fad1c 100644 --- a/src/map/packets/char.cpp +++ b/src/map/packets/char.cpp @@ -181,8 +181,7 @@ void CCharPacket::updateWith(CCharEntity* PChar, ENTITYUPDATE type, uint8 update if (PChar->m_PMonstrosity != nullptr) { - ref(0x48) = PChar->m_PMonstrosity->Face; - ref(0x49) = PChar->m_PMonstrosity->Race; + ref(0x48) = PChar->m_PMonstrosity->Look; ref(0x58) = 0xFFFF; } } diff --git a/src/map/packets/char_appearance.cpp b/src/map/packets/char_appearance.cpp index 8dd28e1265d..47435f9b28b 100644 --- a/src/map/packets/char_appearance.cpp +++ b/src/map/packets/char_appearance.cpp @@ -43,8 +43,7 @@ CCharAppearancePacket::CCharAppearancePacket(CCharEntity* PChar) if (PChar->m_PMonstrosity != nullptr) { - ref(0x04) = PChar->m_PMonstrosity->Face; - ref(0x05) = PChar->m_PMonstrosity->Race; + ref(0x04) = PChar->m_PMonstrosity->Look; ref(0x14) = 0xFFFF; } } diff --git a/src/map/packets/monipulator1.cpp b/src/map/packets/monipulator1.cpp index 2d641f1d1d5..eccf778add1 100644 --- a/src/map/packets/monipulator1.cpp +++ b/src/map/packets/monipulator1.cpp @@ -40,7 +40,7 @@ CMonipulatorPacket1::CMonipulatorPacket1(CCharEntity* PChar) ref(0x06) = 0xD8; // Variable Data Size ref(0x08) = PChar->m_PMonstrosity->Species; //0x2412; //0xFCFE; // Species? Also seen 0x3F1F - ref(0x0A) = 0x190C; // Flags? Also seen 0x0B46 + ref(0x0A) = PChar->m_PMonstrosity->Flags; ref(0x0C) = 0; // Monstrosity Rank (0 = Mon, 1 = NM, 2 = HNM) diff --git a/src/map/packets/zone_in.cpp b/src/map/packets/zone_in.cpp index 22e6224a39a..580e1779bb8 100644 --- a/src/map/packets/zone_in.cpp +++ b/src/map/packets/zone_in.cpp @@ -260,8 +260,7 @@ CZoneInPacket::CZoneInPacket(CCharEntity* PChar, const EventInfo* currentEvent) if (PChar->m_PMonstrosity != nullptr) { // TODO: These make the spawn in cleaner, but then the name doesn't work correctly - // ref(0x04) = PChar->m_PMonstrosity->Face; - // ref(0x05) = PChar->m_PMonstrosity->Race; + // ref(0x04) = PChar->m_PMonstrosity->Look; // ref(0x14) = 0xFFFF; // Enable Monstrosity menu options From e21ce800d14cd0097de7fc30993d7475c2881bf5 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Fri, 29 Sep 2023 17:45:40 +0100 Subject: [PATCH 012/103] Sstart laying out sql tables --- sql/monstrosity_exp_table.sql | 0 sql/monstrosity_instincts.sql | 0 sql/monstrosity_skills.sql | 0 sql/monstrosity_species.sql | 15 +++++++++++++++ sql/monstrosity_traits.sql | 0 src/map/monstrosity.h | 3 --- 6 files changed, 15 insertions(+), 3 deletions(-) create mode 100644 sql/monstrosity_exp_table.sql create mode 100644 sql/monstrosity_instincts.sql create mode 100644 sql/monstrosity_skills.sql create mode 100644 sql/monstrosity_species.sql create mode 100644 sql/monstrosity_traits.sql diff --git a/sql/monstrosity_exp_table.sql b/sql/monstrosity_exp_table.sql new file mode 100644 index 00000000000..e69de29bb2d diff --git a/sql/monstrosity_instincts.sql b/sql/monstrosity_instincts.sql new file mode 100644 index 00000000000..e69de29bb2d diff --git a/sql/monstrosity_skills.sql b/sql/monstrosity_skills.sql new file mode 100644 index 00000000000..e69de29bb2d diff --git a/sql/monstrosity_species.sql b/sql/monstrosity_species.sql new file mode 100644 index 00000000000..83788954e0d --- /dev/null +++ b/sql/monstrosity_species.sql @@ -0,0 +1,15 @@ + +DROP TABLE IF EXISTS `monstrosity_species`; + +CREATE TABLE `monstrosity_species` ( + `monstrosity_id` smallint(30) DEFAULT NULL, -- Used as the shift offset into the monstrosity.levels array + `species_id` smallint(5) unsigned NOT NULL, -- Species ID sent from the client + `name_id` smallint(3) unsigned NOT NULL, -- Name ID sent from the client (re-used for variants) + `look` smallint(3) unsigned NOT NULL, -- Look data sent from the client + `name` -- Human-readable name. Only used for this file and logging. + Stats, etc. -- Lv1 stats for the mob + PRIMARY KEY (`monstrosity_id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; + +INSERT INTO `monstrosity_species` VALUES (1, 0x0001, 0x010C, "Rabbit", ); + diff --git a/sql/monstrosity_traits.sql b/sql/monstrosity_traits.sql new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/map/monstrosity.h b/src/map/monstrosity.h index 6c7f8acc347..38646584b6c 100644 --- a/src/map/monstrosity.h +++ b/src/map/monstrosity.h @@ -45,9 +45,6 @@ namespace monstrosity uint8 NamePrefix2; std::array EquippedInstincts{ 0 }; - - // TODO: Extend this to be large enough to hold Slime and Spriggan levels - // : but don't use sizeof() with this structure. std::array levels{ 0 }; std::array instincts{ 0 }; std::array variants{ 0 }; From 7df6f765c44b1e223a8a52f926c0178b4cb7a8eb Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Fri, 29 Sep 2023 21:02:26 +0100 Subject: [PATCH 013/103] Basic Loading --- sql/monstrosity_species.sql | 85 +++++++++++++++++++++++++++++++++---- src/map/monstrosity.cpp | 50 +++++++++++++++++++++- src/map/monstrosity.h | 6 +++ 3 files changed, 132 insertions(+), 9 deletions(-) diff --git a/sql/monstrosity_species.sql b/sql/monstrosity_species.sql index 83788954e0d..e5e4a48899f 100644 --- a/sql/monstrosity_species.sql +++ b/sql/monstrosity_species.sql @@ -2,14 +2,83 @@ DROP TABLE IF EXISTS `monstrosity_species`; CREATE TABLE `monstrosity_species` ( - `monstrosity_id` smallint(30) DEFAULT NULL, -- Used as the shift offset into the monstrosity.levels array - `species_id` smallint(5) unsigned NOT NULL, -- Species ID sent from the client - `name_id` smallint(3) unsigned NOT NULL, -- Name ID sent from the client (re-used for variants) - `look` smallint(3) unsigned NOT NULL, -- Look data sent from the client - `name` -- Human-readable name. Only used for this file and logging. - Stats, etc. -- Lv1 stats for the mob - PRIMARY KEY (`monstrosity_id`) + `monstrosity_id` smallint(30) DEFAULT NULL, -- Used as the shift offset into the monstrosity.levels array + `name` -- Human-readable name. Only used for this file and logging. + -- `species_id` smallint(5) unsigned NOT NULL, -- Species ID sent from the client + -- `name_id` smallint(3) unsigned NOT NULL, -- Name ID sent from the client (re-used for variants) + -- `look` smallint(3) unsigned NOT NULL, -- Look data sent from the client + -- `family_id` smallint(3) unsigned NOT NULL, + PRIMARY KEY (`monstrosity_id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; -INSERT INTO `monstrosity_species` VALUES (1, 0x0001, 0x010C, "Rabbit", ); +INSERT INTO `monstrosity_species` VALUES (1, "RABBIT"); +INSERT INTO `monstrosity_species` VALUES (2, "BEHEMOTH"); +INSERT INTO `monstrosity_species` VALUES (3, "TIGER"); +INSERT INTO `monstrosity_species` VALUES (4, "SHEEP"); +INSERT INTO `monstrosity_species` VALUES (5, "RAM"); +INSERT INTO `monstrosity_species` VALUES (6, "DHALMEL"); +INSERT INTO `monstrosity_species` VALUES (7, "COEURL"); +INSERT INTO `monstrosity_species` VALUES (8, "OPO_OPO"); +INSERT INTO `monstrosity_species` VALUES (9, "MANTICORE"); +INSERT INTO `monstrosity_species` VALUES (10, "BUFFALO"); +INSERT INTO `monstrosity_species` VALUES (11, "MARID"); +INSERT INTO `monstrosity_species` VALUES (12, "CERBERUS"); +INSERT INTO `monstrosity_species` VALUES (13, "GNOLE"); +INSERT INTO `monstrosity_species` VALUES (15, "FUNGUAR"); +INSERT INTO `monstrosity_species` VALUES (16, "TREANT_SAPLING"); +INSERT INTO `monstrosity_species` VALUES (17, "MORBOL"); +INSERT INTO `monstrosity_species` VALUES (18, "MANDRAGORA"); +INSERT INTO `monstrosity_species` VALUES (19, "SABOTENDER"); +INSERT INTO `monstrosity_species` VALUES (20, "FLYTRAP"); +INSERT INTO `monstrosity_species` VALUES (21, "GOOBBUE"); +INSERT INTO `monstrosity_species` VALUES (22, "RAFFLESIA"); +INSERT INTO `monstrosity_species` VALUES (23, "PANOPT"); + +INSERT INTO `monstrosity_species` VALUES (27, "BEE"); +INSERT INTO `monstrosity_species` VALUES (28, "BEETLE"); +INSERT INTO `monstrosity_species` VALUES (29, "CRAWLER"); +INSERT INTO `monstrosity_species` VALUES (30, "FLY"); +INSERT INTO `monstrosity_species` VALUES (31, "SCORPION"); +INSERT INTO `monstrosity_species` VALUES (32, "SPIDER"); +INSERT INTO `monstrosity_species` VALUES (33, "ANTLION"); +INSERT INTO `monstrosity_species` VALUES (34, "DIREMITE"); +INSERT INTO `monstrosity_species` VALUES (35, "CHIGOE"); +INSERT INTO `monstrosity_species` VALUES (36, "WAMOURACAMPA"); +INSERT INTO `monstrosity_species` VALUES (37, "LADYBUG"); +INSERT INTO `monstrosity_species` VALUES (38, "GNAT"); + +INSERT INTO `monstrosity_species` VALUES (43, "LIZARD"); +INSERT INTO `monstrosity_species` VALUES (44, "RAPTOR"); +INSERT INTO `monstrosity_species` VALUES (45, "ADAMANTOISE"); +INSERT INTO `monstrosity_species` VALUES (46, "BUGARD"); +INSERT INTO `monstrosity_species` VALUES (47, "EFT"); +INSERT INTO `monstrosity_species` VALUES (48, "WIVRE"); +INSERT INTO `monstrosity_species` VALUES (49, "PEISTE"); + +INSERT INTO `monstrosity_species` VALUES (52, "SLIME"); +INSERT INTO `monstrosity_species` VALUES (53, "HECTEYES"); +INSERT INTO `monstrosity_species` VALUES (54, "FLAN"); +INSERT INTO `monstrosity_species` VALUES (56, "SLUG"); +INSERT INTO `monstrosity_species` VALUES (57, "SANDWORM"); +INSERT INTO `monstrosity_species` VALUES (58, "LEECH"); + +INSERT INTO `monstrosity_species` VALUES (60, "CRAB"); +INSERT INTO `monstrosity_species` VALUES (61, "PUGIL"); +INSERT INTO `monstrosity_species` VALUES (62, "SEA_MONK"); +INSERT INTO `monstrosity_species` VALUES (63, "URAGNITE"); +INSERT INTO `monstrosity_species` VALUES (64, "OROBON"); +INSERT INTO `monstrosity_species` VALUES (65, "RUSZOR"); +INSERT INTO `monstrosity_species` VALUES (66, "TOAD"); + +INSERT INTO `monstrosity_species` VALUES (69, "BIRD"); +INSERT INTO `monstrosity_species` VALUES (70, "COCKATRICE"); +INSERT INTO `monstrosity_species` VALUES (71, "ROC"); +INSERT INTO `monstrosity_species` VALUES (72, "BAT"); +INSERT INTO `monstrosity_species` VALUES (73, "HIPPOGRYPH"); +INSERT INTO `monstrosity_species` VALUES (74, "APKALLU"); +INSERT INTO `monstrosity_species` VALUES (75, "COLIBRI"); +INSERT INTO `monstrosity_species` VALUES (76, "AMPHIPTERE"); + +INSERT INTO `monstrosity_species` VALUES (126, "DQ_SLIME"); +INSERT INTO `monstrosity_species` VALUES (127, "FFXIV_SPRIGGAN"); diff --git a/src/map/monstrosity.cpp b/src/map/monstrosity.cpp index bf485407773..eeddd143472 100644 --- a/src/map/monstrosity.cpp +++ b/src/map/monstrosity.cpp @@ -22,6 +22,7 @@ #include "monstrosity.h" #include "common/logging.h" +#include "common/sql.h" #include "entities/charentity.h" @@ -33,6 +34,19 @@ #include "utils/charutils.h" +extern std::unique_ptr sql; + +struct MonstrositySpeciesRow +{ + uint8 speciesId; + std::string name; +}; + +namespace +{ + std::unordered_map gMonstrositySpeciesMap; +} // namespace + monstrosity::MonstrosityData_t::MonstrosityData_t() : Species(0x0001) , Flags(0x0B46) @@ -44,6 +58,23 @@ monstrosity::MonstrosityData_t::MonstrosityData_t() // TODO: Populate instinct and levels from db } +void monstrosity::LoadStaticData() +{ + int32 ret = sql->Query("SELECT monstrosity_id, name FROM monstrosity_species;"); + if (ret != SQL_ERROR && sql->NumRows() != 0) + { + while (sql->NextRow() == SQL_SUCCESS) + { + MonstrositySpeciesRow row; + + row.speciesId = static_cast(sql->GetUIntData(0)); + row.name = sql->GetStringData(1); + + gMonstrositySpeciesMap[row.speciesId] = row; + } + } +} + void monstrosity::HandleZoneIn(CCharEntity* PChar) { // TODO: Check we're about to enter monstrosity, charvar, flag, etc. @@ -77,7 +108,7 @@ uint32 monstrosity::GetPackedMonstrosityName(CCharEntity* PChar) uint8 d = PChar->m_PMonstrosity->NamePrefix2; // Packed as LE - return (d << 24) + (c << 16) + (b << 8) + (a << 0); + return (d << 24) + (c << 16) + (a << 0); } void monstrosity::SendFullMonstrosityUpdate(CCharEntity* PChar) @@ -166,3 +197,20 @@ void monstrosity::HandleEquipChangePacket(CCharEntity* PChar, CBasicPacket& data // TODO: Is this too much traffic? SendFullMonstrosityUpdate(PChar); } + +void monstrosity::MaxAllLevels(CCharEntity* PChar) +{ + +} + + +void monstrosity::UnlockAllInstincts(CCharEntity* PChar) +{ + +} + + +void UnlockAllVariants(CCharEntity* PChar) +{ + +} diff --git a/src/map/monstrosity.h b/src/map/monstrosity.h index 38646584b6c..048e8b6e81f 100644 --- a/src/map/monstrosity.h +++ b/src/map/monstrosity.h @@ -50,8 +50,14 @@ namespace monstrosity std::array variants{ 0 }; }; + void LoadStaticData(); void HandleZoneIn(CCharEntity* PChar); uint32 GetPackedMonstrosityName(CCharEntity* PChar); void SendFullMonstrosityUpdate(CCharEntity* PChar); void HandleEquipChangePacket(CCharEntity* PChar, CBasicPacket& data); + + // Debug + void MaxAllLevels(CCharEntity* PChar); + void UnlockAllInstincts(CCharEntity* PChar); + void UnlockAllVariants(CCharEntity* PChar); } // namespace monstrosity From e4ba5dd853573f0af2a85fe81e7cd1176cac2b34 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Sat, 30 Sep 2023 14:34:18 +0100 Subject: [PATCH 014/103] Core-based level and instinct unlocks --- sql/monstrosity_species.sql | 4 +-- src/map/monstrosity.cpp | 59 +++++++++++++++++++++++++++++++++++-- 2 files changed, 57 insertions(+), 6 deletions(-) diff --git a/sql/monstrosity_species.sql b/sql/monstrosity_species.sql index e5e4a48899f..bf5801bf769 100644 --- a/sql/monstrosity_species.sql +++ b/sql/monstrosity_species.sql @@ -1,9 +1,7 @@ - DROP TABLE IF EXISTS `monstrosity_species`; - CREATE TABLE `monstrosity_species` ( `monstrosity_id` smallint(30) DEFAULT NULL, -- Used as the shift offset into the monstrosity.levels array - `name` -- Human-readable name. Only used for this file and logging. + `name` varchar(30) DEFAULT NULL, -- Human-readable name. Only used for this file and logging. -- `species_id` smallint(5) unsigned NOT NULL, -- Species ID sent from the client -- `name_id` smallint(3) unsigned NOT NULL, -- Name ID sent from the client (re-used for variants) -- `look` smallint(3) unsigned NOT NULL, -- Look data sent from the client diff --git a/src/map/monstrosity.cpp b/src/map/monstrosity.cpp index eeddd143472..94c20583778 100644 --- a/src/map/monstrosity.cpp +++ b/src/map/monstrosity.cpp @@ -77,11 +77,18 @@ void monstrosity::LoadStaticData() void monstrosity::HandleZoneIn(CCharEntity* PChar) { + // TODO: Move to map.cpp + LoadStaticData(); + // TODO: Check we're about to enter monstrosity, charvar, flag, etc. if (charutils::GetCharVar(PChar, "MONSTROSITY_START") == 1) { PChar->m_PMonstrosity = std::make_unique(); PChar->updatemask |= UPDATE_LOOK; + + MaxAllLevels(PChar); + UnlockAllInstincts(PChar); + UnlockAllVariants(PChar); } } @@ -200,17 +207,63 @@ void monstrosity::HandleEquipChangePacket(CCharEntity* PChar, CBasicPacket& data void monstrosity::MaxAllLevels(CCharEntity* PChar) { - + ShowInfo("MaxAllLevels"); + for (auto const& [speciesId, entry] : gMonstrositySpeciesMap) + { + PChar->m_PMonstrosity->levels[speciesId] = 99; + } } void monstrosity::UnlockAllInstincts(CCharEntity* PChar) { + ShowInfo("UnlockAllInstincts"); + + // Level based + for (auto const& [speciesId, entry] : gMonstrositySpeciesMap) + { + uint8 level = PChar->m_PMonstrosity->levels[speciesId]; + uint8 byteOffset = speciesId / 4; + uint8 unlockAmount = level / 30; + uint8 shiftAmount = (speciesId * 2) % 8; + + // Special case for writing Slime & Spriggan data at the end of the 64-byte array + if (byteOffset == 31) + { + byteOffset = 63; + } + + if (byteOffset < 64) + { + PChar->m_PMonstrosity->instincts[byteOffset] |= (unlockAmount << shiftAmount); + } + else + { + ShowError("byteOffset out of range"); + } + } + + // Instincts (Purchasable) + for (uint8 idx = 0; idx < 32; ++idx) + { + uint8 byteOffset = 20 + (idx / 8); + uint8 shiftAmount = idx % 8; + // There is a gap in the instincts bitpack, so we put the purchase information + // for these instincts in there. Sneaky sneaky. + if (byteOffset >= 20 && byteOffset < 24) + { + PChar->m_PMonstrosity->instincts[byteOffset] |= (0x01 << shiftAmount); + } + else + { + ShowError("byteOffset out of range"); + } + } } -void UnlockAllVariants(CCharEntity* PChar) +void monstrosity::UnlockAllVariants(CCharEntity* PChar) { - + ShowInfo("UnlockAllVariants"); } From eb940a478125b21fc0eb91c099b71ad4d89a6bbb Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Sat, 30 Sep 2023 16:53:48 +0100 Subject: [PATCH 015/103] Correct basename hookup --- src/map/monstrosity.cpp | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/map/monstrosity.cpp b/src/map/monstrosity.cpp index 94c20583778..52e2b54ccd5 100644 --- a/src/map/monstrosity.cpp +++ b/src/map/monstrosity.cpp @@ -50,8 +50,8 @@ namespace monstrosity::MonstrosityData_t::MonstrosityData_t() : Species(0x0001) , Flags(0x0B46) -, Look(0xFD81) -, NameBase(0x00) +, Look(0x010C) +, NameBase(0x8001) , NamePrefix1(0x00) , NamePrefix2(0x00) { @@ -94,14 +94,12 @@ void monstrosity::HandleZoneIn(CCharEntity* PChar) uint32 monstrosity::GetPackedMonstrosityName(CCharEntity* PChar) { - // Monstrosity Name Ids? - // If populated, the monstrosity icon will appear - // uint8 a = 0x1F; - - // Mob Type - // 0x80: Scorpion - // 0x81: Mandragora - uint16 a = PChar->m_PMonstrosity->NameBase; + // Monstrosity Name Ids + // Rabbit: 0x8001 + // Mandragora: 0x8012 + // Rabbit: 0x802B + // Slime: 0x80FE + uint16 a = 0x8000 | PChar->m_PMonstrosity->Species; // Adjective 1 (optional) // 01: Abashed @@ -222,7 +220,7 @@ void monstrosity::UnlockAllInstincts(CCharEntity* PChar) // Level based for (auto const& [speciesId, entry] : gMonstrositySpeciesMap) { - uint8 level = PChar->m_PMonstrosity->levels[speciesId]; + uint8 level = 99; uint8 byteOffset = speciesId / 4; uint8 unlockAmount = level / 30; uint8 shiftAmount = (speciesId * 2) % 8; From 8a63578cbb309589d8f8b318132d412473eff981 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Sat, 30 Sep 2023 17:47:45 +0100 Subject: [PATCH 016/103] Species and Look --- scripts/commands/monstrosity.lua | 8 +- sql/monstrosity_species.sql | 132 +++++++++++++++---------------- src/map/lua/lua_baseentity.cpp | 16 ++-- src/map/monstrosity.cpp | 77 +++++++++--------- src/map/packets/monipulator1.cpp | 4 +- 5 files changed, 112 insertions(+), 125 deletions(-) diff --git a/scripts/commands/monstrosity.lua b/scripts/commands/monstrosity.lua index 1349ef0c016..7f8b663277c 100644 --- a/scripts/commands/monstrosity.lua +++ b/scripts/commands/monstrosity.lua @@ -3,7 +3,7 @@ local commandObj = {} commandObj.cmdprops = { permission = 1, - parameters = 'i' + parameters = '' } local monstrositySpecies = @@ -338,8 +338,7 @@ local purchasableInstincts = RUN = 31, } -commandObj.onTrigger = function(player, n) - print(n) +commandObj.onTrigger = function(player) --[[ if player:getCharVar('MONSTROSITY_START') == 1 then player:setCharVar('MONSTROSITY_START', 0) @@ -359,9 +358,6 @@ commandObj.onTrigger = function(player, n) local data = { - look = 0x0791, - name = n, - -- 1 byte per entry, mapped out to monstrositySpecies table -- (0 - 127) levels = diff --git a/sql/monstrosity_species.sql b/sql/monstrosity_species.sql index bf5801bf769..1b12a317e5c 100644 --- a/sql/monstrosity_species.sql +++ b/sql/monstrosity_species.sql @@ -2,81 +2,79 @@ DROP TABLE IF EXISTS `monstrosity_species`; CREATE TABLE `monstrosity_species` ( `monstrosity_id` smallint(30) DEFAULT NULL, -- Used as the shift offset into the monstrosity.levels array `name` varchar(30) DEFAULT NULL, -- Human-readable name. Only used for this file and logging. - -- `species_id` smallint(5) unsigned NOT NULL, -- Species ID sent from the client - -- `name_id` smallint(3) unsigned NOT NULL, -- Name ID sent from the client (re-used for variants) - -- `look` smallint(3) unsigned NOT NULL, -- Look data sent from the client + `look` smallint(3) unsigned NOT NULL, -- Look data sent from the client -- `family_id` smallint(3) unsigned NOT NULL, PRIMARY KEY (`monstrosity_id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; -INSERT INTO `monstrosity_species` VALUES (1, "RABBIT"); -INSERT INTO `monstrosity_species` VALUES (2, "BEHEMOTH"); -INSERT INTO `monstrosity_species` VALUES (3, "TIGER"); -INSERT INTO `monstrosity_species` VALUES (4, "SHEEP"); -INSERT INTO `monstrosity_species` VALUES (5, "RAM"); -INSERT INTO `monstrosity_species` VALUES (6, "DHALMEL"); -INSERT INTO `monstrosity_species` VALUES (7, "COEURL"); -INSERT INTO `monstrosity_species` VALUES (8, "OPO_OPO"); -INSERT INTO `monstrosity_species` VALUES (9, "MANTICORE"); -INSERT INTO `monstrosity_species` VALUES (10, "BUFFALO"); -INSERT INTO `monstrosity_species` VALUES (11, "MARID"); -INSERT INTO `monstrosity_species` VALUES (12, "CERBERUS"); -INSERT INTO `monstrosity_species` VALUES (13, "GNOLE"); +INSERT INTO `monstrosity_species` VALUES (1, "RABBIT", 0x010C); +INSERT INTO `monstrosity_species` VALUES (2, "BEHEMOTH", 0x0194); -- Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (3, "TIGER", 0x0134); -- Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (4, "SHEEP", 0x0154); -- Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (5, "RAM", 0x0000); +INSERT INTO `monstrosity_species` VALUES (6, "DHALMEL", 0x0000); +INSERT INTO `monstrosity_species` VALUES (7, "COEURL", 0x0000); +INSERT INTO `monstrosity_species` VALUES (8, "OPO_OPO", 0x0000); +INSERT INTO `monstrosity_species` VALUES (9, "MANTICORE", 0x0000); +INSERT INTO `monstrosity_species` VALUES (10, "BUFFALO", 0x0000); +INSERT INTO `monstrosity_species` VALUES (11, "MARID", 0x0000); +INSERT INTO `monstrosity_species` VALUES (12, "CERBERUS", 0x0000); +INSERT INTO `monstrosity_species` VALUES (13, "GNOLE", 0x0000); -INSERT INTO `monstrosity_species` VALUES (15, "FUNGUAR"); -INSERT INTO `monstrosity_species` VALUES (16, "TREANT_SAPLING"); -INSERT INTO `monstrosity_species` VALUES (17, "MORBOL"); -INSERT INTO `monstrosity_species` VALUES (18, "MANDRAGORA"); -INSERT INTO `monstrosity_species` VALUES (19, "SABOTENDER"); -INSERT INTO `monstrosity_species` VALUES (20, "FLYTRAP"); -INSERT INTO `monstrosity_species` VALUES (21, "GOOBBUE"); -INSERT INTO `monstrosity_species` VALUES (22, "RAFFLESIA"); -INSERT INTO `monstrosity_species` VALUES (23, "PANOPT"); +INSERT INTO `monstrosity_species` VALUES (15, "FUNGUAR", 0x0000); +INSERT INTO `monstrosity_species` VALUES (16, "TREANT_SAPLING", 0x0000); +INSERT INTO `monstrosity_species` VALUES (17, "MORBOL", 0x0000); +INSERT INTO `monstrosity_species` VALUES (18, "MANDRAGORA", 0x012C); +INSERT INTO `monstrosity_species` VALUES (19, "SABOTENDER", 0x0000); +INSERT INTO `monstrosity_species` VALUES (20, "FLYTRAP", 0x0000); +INSERT INTO `monstrosity_species` VALUES (21, "GOOBBUE", 0x0000); +INSERT INTO `monstrosity_species` VALUES (22, "RAFFLESIA", 0x0000); +INSERT INTO `monstrosity_species` VALUES (23, "PANOPT", 0x0000); -INSERT INTO `monstrosity_species` VALUES (27, "BEE"); -INSERT INTO `monstrosity_species` VALUES (28, "BEETLE"); -INSERT INTO `monstrosity_species` VALUES (29, "CRAWLER"); -INSERT INTO `monstrosity_species` VALUES (30, "FLY"); -INSERT INTO `monstrosity_species` VALUES (31, "SCORPION"); -INSERT INTO `monstrosity_species` VALUES (32, "SPIDER"); -INSERT INTO `monstrosity_species` VALUES (33, "ANTLION"); -INSERT INTO `monstrosity_species` VALUES (34, "DIREMITE"); -INSERT INTO `monstrosity_species` VALUES (35, "CHIGOE"); -INSERT INTO `monstrosity_species` VALUES (36, "WAMOURACAMPA"); -INSERT INTO `monstrosity_species` VALUES (37, "LADYBUG"); -INSERT INTO `monstrosity_species` VALUES (38, "GNAT"); +INSERT INTO `monstrosity_species` VALUES (27, "BEE", 0x0000); +INSERT INTO `monstrosity_species` VALUES (28, "BEETLE", 0x0000); +INSERT INTO `monstrosity_species` VALUES (29, "CRAWLER", 0x0000); +INSERT INTO `monstrosity_species` VALUES (30, "FLY", 0x0000); +INSERT INTO `monstrosity_species` VALUES (31, "SCORPION", 0x0000); +INSERT INTO `monstrosity_species` VALUES (32, "SPIDER", 0x0000); +INSERT INTO `monstrosity_species` VALUES (33, "ANTLION", 0x0000); +INSERT INTO `monstrosity_species` VALUES (34, "DIREMITE", 0x0000); +INSERT INTO `monstrosity_species` VALUES (35, "CHIGOE", 0x0000); +INSERT INTO `monstrosity_species` VALUES (36, "WAMOURACAMPA", 0x0000); +INSERT INTO `monstrosity_species` VALUES (37, "LADYBUG", 0x0000); +INSERT INTO `monstrosity_species` VALUES (38, "GNAT", 0x0000); -INSERT INTO `monstrosity_species` VALUES (43, "LIZARD"); -INSERT INTO `monstrosity_species` VALUES (44, "RAPTOR"); -INSERT INTO `monstrosity_species` VALUES (45, "ADAMANTOISE"); -INSERT INTO `monstrosity_species` VALUES (46, "BUGARD"); -INSERT INTO `monstrosity_species` VALUES (47, "EFT"); -INSERT INTO `monstrosity_species` VALUES (48, "WIVRE"); -INSERT INTO `monstrosity_species` VALUES (49, "PEISTE"); +INSERT INTO `monstrosity_species` VALUES (43, "LIZARD", 0x0148); +INSERT INTO `monstrosity_species` VALUES (44, "RAPTOR", 0x0000); +INSERT INTO `monstrosity_species` VALUES (45, "ADAMANTOISE", 0x0000); +INSERT INTO `monstrosity_species` VALUES (46, "BUGARD", 0x0000); +INSERT INTO `monstrosity_species` VALUES (47, "EFT", 0x0000); +INSERT INTO `monstrosity_species` VALUES (48, "WIVRE", 0x0000); +INSERT INTO `monstrosity_species` VALUES (49, "PEISTE", 0x0000); -INSERT INTO `monstrosity_species` VALUES (52, "SLIME"); -INSERT INTO `monstrosity_species` VALUES (53, "HECTEYES"); -INSERT INTO `monstrosity_species` VALUES (54, "FLAN"); -INSERT INTO `monstrosity_species` VALUES (56, "SLUG"); -INSERT INTO `monstrosity_species` VALUES (57, "SANDWORM"); -INSERT INTO `monstrosity_species` VALUES (58, "LEECH"); +INSERT INTO `monstrosity_species` VALUES (52, "SLIME", 0x0000); +INSERT INTO `monstrosity_species` VALUES (53, "HECTEYES", 0x0000); +INSERT INTO `monstrosity_species` VALUES (54, "FLAN", 0x0000); +INSERT INTO `monstrosity_species` VALUES (56, "SLUG", 0x0000); +INSERT INTO `monstrosity_species` VALUES (57, "SANDWORM", 0x0000); +INSERT INTO `monstrosity_species` VALUES (58, "LEECH", 0x0000); -INSERT INTO `monstrosity_species` VALUES (60, "CRAB"); -INSERT INTO `monstrosity_species` VALUES (61, "PUGIL"); -INSERT INTO `monstrosity_species` VALUES (62, "SEA_MONK"); -INSERT INTO `monstrosity_species` VALUES (63, "URAGNITE"); -INSERT INTO `monstrosity_species` VALUES (64, "OROBON"); -INSERT INTO `monstrosity_species` VALUES (65, "RUSZOR"); -INSERT INTO `monstrosity_species` VALUES (66, "TOAD"); +INSERT INTO `monstrosity_species` VALUES (60, "CRAB", 0x0000); +INSERT INTO `monstrosity_species` VALUES (61, "PUGIL", 0x0000); +INSERT INTO `monstrosity_species` VALUES (62, "SEA_MONK", 0x0000); +INSERT INTO `monstrosity_species` VALUES (63, "URAGNITE", 0x0000); +INSERT INTO `monstrosity_species` VALUES (64, "OROBON", 0x0000); +INSERT INTO `monstrosity_species` VALUES (65, "RUSZOR", 0x0000); +INSERT INTO `monstrosity_species` VALUES (66, "TOAD", 0x0000); -INSERT INTO `monstrosity_species` VALUES (69, "BIRD"); -INSERT INTO `monstrosity_species` VALUES (70, "COCKATRICE"); -INSERT INTO `monstrosity_species` VALUES (71, "ROC"); -INSERT INTO `monstrosity_species` VALUES (72, "BAT"); -INSERT INTO `monstrosity_species` VALUES (73, "HIPPOGRYPH"); -INSERT INTO `monstrosity_species` VALUES (74, "APKALLU"); -INSERT INTO `monstrosity_species` VALUES (75, "COLIBRI"); -INSERT INTO `monstrosity_species` VALUES (76, "AMPHIPTERE"); +INSERT INTO `monstrosity_species` VALUES (69, "BIRD", 0x0000); +INSERT INTO `monstrosity_species` VALUES (70, "COCKATRICE", 0x0000); +INSERT INTO `monstrosity_species` VALUES (71, "ROC", 0x0000); +INSERT INTO `monstrosity_species` VALUES (72, "BAT", 0x0000); +INSERT INTO `monstrosity_species` VALUES (73, "HIPPOGRYPH", 0x0000); +INSERT INTO `monstrosity_species` VALUES (74, "APKALLU", 0x0000); +INSERT INTO `monstrosity_species` VALUES (75, "COLIBRI", 0x0000); +INSERT INTO `monstrosity_species` VALUES (76, "AMPHIPTERE", 0x0000); -INSERT INTO `monstrosity_species` VALUES (126, "DQ_SLIME"); -INSERT INTO `monstrosity_species` VALUES (127, "FFXIV_SPRIGGAN"); +INSERT INTO `monstrosity_species` VALUES (126, "DQ_SLIME", 0x0B41); +INSERT INTO `monstrosity_species` VALUES (127, "FFXIV_SPRIGGAN", 0x0B5D); -- Look guessed not capped diff --git a/src/map/lua/lua_baseentity.cpp b/src/map/lua/lua_baseentity.cpp index 6fd8dc6de14..21314a870c5 100644 --- a/src/map/lua/lua_baseentity.cpp +++ b/src/map/lua/lua_baseentity.cpp @@ -6314,28 +6314,24 @@ void CLuaBaseEntity::setMonstrosity(sol::table table) return; } - PChar->m_PMonstrosity->Look = table.get("look"); - - PChar->m_PMonstrosity->NameBase = table.get("name"); - for (auto const& [keyObj, valObj] : table.get("levels")) { - uint8 key = keyObj.as(); - uint8 val = valObj.as(); + uint8 key = keyObj.as(); + uint8 val = valObj.as(); PChar->m_PMonstrosity->levels[key] = val; } for (auto const& [keyObj, valObj] : table.get("instincts")) { - uint8 key = keyObj.as(); - uint8 val = valObj.as(); + uint8 key = keyObj.as(); + uint8 val = valObj.as(); PChar->m_PMonstrosity->instincts[key] = val; } for (auto const& [keyObj, valObj] : table.get("variants")) { - uint8 key = keyObj.as(); - uint8 val = valObj.as(); + uint8 key = keyObj.as(); + uint8 val = valObj.as(); PChar->m_PMonstrosity->variants[key] = val; } diff --git a/src/map/monstrosity.cpp b/src/map/monstrosity.cpp index 52e2b54ccd5..94af23daf42 100644 --- a/src/map/monstrosity.cpp +++ b/src/map/monstrosity.cpp @@ -27,8 +27,8 @@ #include "entities/charentity.h" #include "packets/char_appearance.h" -#include "packets/char_jobs.h" #include "packets/char_job_extra.h" +#include "packets/char_jobs.h" #include "packets/monipulator1.h" #include "packets/monipulator2.h" @@ -38,8 +38,9 @@ extern std::unique_ptr sql; struct MonstrositySpeciesRow { - uint8 speciesId; + uint8 monstrosityId; std::string name; + uint16 look; }; namespace @@ -49,7 +50,7 @@ namespace monstrosity::MonstrosityData_t::MonstrosityData_t() : Species(0x0001) -, Flags(0x0B46) +, Flags(0x0B44) , Look(0x010C) , NameBase(0x8001) , NamePrefix1(0x00) @@ -60,17 +61,18 @@ monstrosity::MonstrosityData_t::MonstrosityData_t() void monstrosity::LoadStaticData() { - int32 ret = sql->Query("SELECT monstrosity_id, name FROM monstrosity_species;"); + int32 ret = sql->Query("SELECT monstrosity_id, name, look FROM monstrosity_species;"); if (ret != SQL_ERROR && sql->NumRows() != 0) { while (sql->NextRow() == SQL_SUCCESS) { MonstrositySpeciesRow row; - row.speciesId = static_cast(sql->GetUIntData(0)); - row.name = sql->GetStringData(1); + row.monstrosityId = static_cast(sql->GetUIntData(0)); + row.name = sql->GetStringData(1); + row.look = static_cast(sql->GetUIntData(2)); - gMonstrositySpeciesMap[row.speciesId] = row; + gMonstrositySpeciesMap[row.monstrosityId] = row; } } } @@ -148,39 +150,26 @@ void monstrosity::HandleEquipChangePacket(CCharEntity* PChar, CBasicPacket& data // TODO: Validate that we'll have enough points to hold our instincts when we equip PChar->m_PMonstrosity->Species = data.ref(0x0C); - // 0x0D holds the variant index - - // -- 0x010C: Rabbit - // -- 0x010D: Onyx Rabbit - // -- 0x010E: Alabaster Rabbit - // -- 0x0791: Lapinion - std::unordered_map lookMap = - { - { 0x0001, 0x010C }, - { 0x0100, 0x010D }, - }; - - // TODO: Move this information to a db table that's loaded at startup: - // { mon_id, species_code, look, family, { starting_stats } } - PChar->m_PMonstrosity->Look = lookMap[PChar->m_PMonstrosity->Species]; + PChar->m_PMonstrosity->Look = gMonstrositySpeciesMap[PChar->m_PMonstrosity->Species].look; ShowInfo(fmt::format("Species: {}, Flags: {}, Looks: {}", - PChar->m_PMonstrosity->Species, - PChar->m_PMonstrosity->Flags, - PChar->m_PMonstrosity->Look).c_str()); + PChar->m_PMonstrosity->Species, + PChar->m_PMonstrosity->Flags, + PChar->m_PMonstrosity->Look) + .c_str()); // Remove All if (data.ref(0x16) == 0xFFFF) { - for (std::size_t idx = 0; idx < 12; idx +=2) + for (std::size_t idx = 0; idx < 12; idx += 2) { PChar->m_PMonstrosity->EquippedInstincts[idx] = 0x0000; } } else // Set { - for (std::size_t idx = 0; idx < 12; idx +=2) + for (std::size_t idx = 0; idx < 12; idx += 2) { if (data.ref(0x10 + idx) != 0) { @@ -205,25 +194,21 @@ void monstrosity::HandleEquipChangePacket(CCharEntity* PChar, CBasicPacket& data void monstrosity::MaxAllLevels(CCharEntity* PChar) { - ShowInfo("MaxAllLevels"); - for (auto const& [speciesId, entry] : gMonstrositySpeciesMap) + for (auto const& [monstrosityId, entry] : gMonstrositySpeciesMap) { - PChar->m_PMonstrosity->levels[speciesId] = 99; + PChar->m_PMonstrosity->levels[monstrosityId] = 99; } } - void monstrosity::UnlockAllInstincts(CCharEntity* PChar) { - ShowInfo("UnlockAllInstincts"); - // Level based - for (auto const& [speciesId, entry] : gMonstrositySpeciesMap) + for (auto const& [monstrosityId, entry] : gMonstrositySpeciesMap) { - uint8 level = 99; - uint8 byteOffset = speciesId / 4; + uint8 level = 99; + uint8 byteOffset = monstrosityId / 4; uint8 unlockAmount = level / 30; - uint8 shiftAmount = (speciesId * 2) % 8; + uint8 shiftAmount = (monstrosityId * 2) % 8; // Special case for writing Slime & Spriggan data at the end of the 64-byte array if (byteOffset == 31) @@ -244,7 +229,7 @@ void monstrosity::UnlockAllInstincts(CCharEntity* PChar) // Instincts (Purchasable) for (uint8 idx = 0; idx < 32; ++idx) { - uint8 byteOffset = 20 + (idx / 8); + uint8 byteOffset = 20 + (idx / 8); uint8 shiftAmount = idx % 8; // There is a gap in the instincts bitpack, so we put the purchase information @@ -260,8 +245,20 @@ void monstrosity::UnlockAllInstincts(CCharEntity* PChar) } } - void monstrosity::UnlockAllVariants(CCharEntity* PChar) { - ShowInfo("UnlockAllVariants"); + for (uint8 idx = 0; idx < 256; ++idx) + { + uint8 byteOffset = idx / 8; + uint8 shiftAmount = idx % 8; + + if (byteOffset < 32) + { + PChar->m_PMonstrosity->variants[byteOffset] |= (0x01 << shiftAmount); + } + else + { + ShowError("byteOffset out of range"); + } + } } diff --git a/src/map/packets/monipulator1.cpp b/src/map/packets/monipulator1.cpp index eccf778add1..bde6947edf8 100644 --- a/src/map/packets/monipulator1.cpp +++ b/src/map/packets/monipulator1.cpp @@ -39,7 +39,7 @@ CMonipulatorPacket1::CMonipulatorPacket1(CCharEntity* PChar) ref(0x04) = 0x03; // Update Type ref(0x06) = 0xD8; // Variable Data Size - ref(0x08) = PChar->m_PMonstrosity->Species; //0x2412; //0xFCFE; // Species? Also seen 0x3F1F + ref(0x08) = PChar->m_PMonstrosity->Species; // 0x2412; //0xFCFE; // Species? Also seen 0x3F1F ref(0x0A) = PChar->m_PMonstrosity->Flags; ref(0x0C) = 0; // Monstrosity Rank (0 = Mon, 1 = NM, 2 = HNM) @@ -57,5 +57,5 @@ CMonipulatorPacket1::CMonipulatorPacket1(CCharEntity* PChar) std::memcpy(data + 0x1C, PChar->m_PMonstrosity->instincts.data(), 64); // Instinct Bitfield 1 // Mapped onto the item ID for these creatures. (00 doesn't exist, 01 is rabbit, 02 is behemoth, etc.) - std::memcpy(data + 0x5C, PChar->m_PMonstrosity->levels.data(), 128); // Monster Level Bitfield + std::memcpy(data + 0x5C, PChar->m_PMonstrosity->levels.data(), 128); // Monster Level Bitfield } From 09360cea82f8d18f135e22afe4738dfbf755f600 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Sat, 30 Sep 2023 19:10:40 +0100 Subject: [PATCH 017/103] Fix latter half of equip slots --- src/map/monstrosity.cpp | 82 +++++++++++++----------------- src/map/packets/char_job_extra.cpp | 4 +- 2 files changed, 37 insertions(+), 49 deletions(-) diff --git a/src/map/monstrosity.cpp b/src/map/monstrosity.cpp index 94af23daf42..eac87f3172c 100644 --- a/src/map/monstrosity.cpp +++ b/src/map/monstrosity.cpp @@ -96,26 +96,12 @@ void monstrosity::HandleZoneIn(CCharEntity* PChar) uint32 monstrosity::GetPackedMonstrosityName(CCharEntity* PChar) { - // Monstrosity Name Ids - // Rabbit: 0x8001 - // Mandragora: 0x8012 - // Rabbit: 0x802B - // Slime: 0x80FE uint16 a = 0x8000 | PChar->m_PMonstrosity->Species; - - // Adjective 1 (optional) - // 01: Abashed - // CD: Tempest - // F5: Zenith - // F6: Zero - uint8 c = PChar->m_PMonstrosity->NamePrefix1; - - // Adjective 2 - // Same values as above - uint8 d = PChar->m_PMonstrosity->NamePrefix2; + uint8 b = PChar->m_PMonstrosity->NamePrefix1; + uint8 c = PChar->m_PMonstrosity->NamePrefix2; // Packed as LE - return (d << 24) + (c << 16) + (a << 0); + return (c << 24) + (b << 16) + (a << 0); } void monstrosity::SendFullMonstrosityUpdate(CCharEntity* PChar) @@ -149,44 +135,46 @@ void monstrosity::HandleEquipChangePacket(CCharEntity* PChar, CBasicPacket& data // TODO: Validate that we'll have enough points to hold our instincts when we equip - PChar->m_PMonstrosity->Species = data.ref(0x0C); - - PChar->m_PMonstrosity->Look = gMonstrositySpeciesMap[PChar->m_PMonstrosity->Species].look; - - ShowInfo(fmt::format("Species: {}, Flags: {}, Looks: {}", - PChar->m_PMonstrosity->Species, - PChar->m_PMonstrosity->Flags, - PChar->m_PMonstrosity->Look) - .c_str()); - - // Remove All - if (data.ref(0x16) == 0xFFFF) + uint8 flag = data.ref(0x0A); + if (flag == 0x01) // Species Change { - for (std::size_t idx = 0; idx < 12; idx += 2) - { - PChar->m_PMonstrosity->EquippedInstincts[idx] = 0x0000; - } + PChar->m_PMonstrosity->Species = data.ref(0x0C); + PChar->m_PMonstrosity->Look = gMonstrositySpeciesMap[PChar->m_PMonstrosity->Species].look; } - else // Set + else if (flag == 0x04) // Instinct Change { - for (std::size_t idx = 0; idx < 12; idx += 2) + // Remove All + if (data.ref(0x16) == 0xFFFF) { - if (data.ref(0x10 + idx) != 0) + for (std::size_t idx = 0; idx < 12; ++idx) { - PChar->m_PMonstrosity->EquippedInstincts[idx] = data.ref(0x10 + idx); + PChar->m_PMonstrosity->EquippedInstincts[idx] = 0x0000; + } + } + else // Set + { + for (std::size_t idx = 0; idx < 12; ++idx) + { + uint16 value = data.ref(0x10 + (idx * 2)); + if (value != 0) + { + PChar->m_PMonstrosity->EquippedInstincts[idx] = value; + } } } } - - std::string EquipStr = ""; - for (std::size_t idx = 0; idx < 12; ++idx) + else if (flag == 0x08) // Name Change { - EquipStr += fmt::format("{}: {}, ", idx, PChar->m_PMonstrosity->EquippedInstincts[idx]); + PChar->m_PMonstrosity->NamePrefix1 = data.ref(0x28); + PChar->m_PMonstrosity->NamePrefix2 = data.ref(0x29); } - ShowInfo(EquipStr.c_str()); - PChar->m_PMonstrosity->NamePrefix1 = data.ref(0x28); - PChar->m_PMonstrosity->NamePrefix2 = data.ref(0x29); + // std::string EquipStr = ""; + // for (std::size_t idx = 0; idx < 12; ++idx) + // { + // EquipStr += fmt::format("{}: {}, ", idx, PChar->m_PMonstrosity->EquippedInstincts[idx]); + // } + // ShowInfo(EquipStr.c_str()); // TODO: Is this too much traffic? SendFullMonstrosityUpdate(PChar); @@ -247,10 +235,10 @@ void monstrosity::UnlockAllInstincts(CCharEntity* PChar) void monstrosity::UnlockAllVariants(CCharEntity* PChar) { - for (uint8 idx = 0; idx < 256; ++idx) + for (std::size_t idx = 0; idx < 256; ++idx) { - uint8 byteOffset = idx / 8; - uint8 shiftAmount = idx % 8; + uint8 byteOffset = static_cast(idx) / 8; + uint8 shiftAmount = static_cast(idx) % 8; if (byteOffset < 32) { diff --git a/src/map/packets/char_job_extra.cpp b/src/map/packets/char_job_extra.cpp index 3bfa926707f..36543a3dd44 100644 --- a/src/map/packets/char_job_extra.cpp +++ b/src/map/packets/char_job_extra.cpp @@ -157,9 +157,9 @@ CCharJobExtraPacket::CCharJobExtraPacket(CCharEntity* PChar, bool mjob) ref(0x08) = PChar->m_PMonstrosity->Species; - for (std::size_t idx = 0; idx < 12; idx += 2) + for (std::size_t idx = 0; idx < 12; ++idx) { - ref(0x0C + idx) = PChar->m_PMonstrosity->EquippedInstincts[idx]; + ref(0x0C + (idx * 2)) = PChar->m_PMonstrosity->EquippedInstincts[idx]; } /* From ba9ed4070065770b0bc0d69aa392a4633dd42724 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Sun, 1 Oct 2023 12:34:10 +0100 Subject: [PATCH 018/103] Remove job extra comments --- src/map/packets/char_job_extra.cpp | 117 +---------------------------- 1 file changed, 2 insertions(+), 115 deletions(-) diff --git a/src/map/packets/char_job_extra.cpp b/src/map/packets/char_job_extra.cpp index 36543a3dd44..8586f34c42c 100644 --- a/src/map/packets/char_job_extra.cpp +++ b/src/map/packets/char_job_extra.cpp @@ -140,21 +140,6 @@ CCharJobExtraPacket::CCharJobExtraPacket(CCharEntity* PChar, bool mjob) } else if (job == JOB_MON && PChar->loc.zone->GetID() == ZONE_FERETORY && PChar->m_PMonstrosity != nullptr) { - /* - | 0 1 2 3 4 5 6 7 8 9 A B C D E F | 0123456789ABCDEF - -----+---------------------------------------------------- -+------------------ - 0 | 44 50 55 01 17 00 00 00 FD 01 00 FF FA 02 05 03 | DPU............. - 10 | 07 03 09 03 00 00 00 00 00 00 00 00 00 00 00 00 | ................ - 20 | 00 00 00 00 09 7D 08 00 28 04 64 02 33 00 64 20 | .....}..(.d.3.d - 30 | 00 6C FB 38 00 00 20 00 00 00 00 00 20 A0 03 00 | .l.8.. ..... ... - 40 | 4C DC E3 28 00 00 00 00 00 00 00 00 00 00 00 00 | L..(............ - 50 | 00 00 00 00 FD 81 09 7D 00 06 00 00 7B 00 00 00 | .......}....{... - 60 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ - 70 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ - 80 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ - 90 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ................ - */ - ref(0x08) = PChar->m_PMonstrosity->Species; for (std::size_t idx = 0; idx < 12; ++idx) @@ -162,105 +147,7 @@ CCharJobExtraPacket::CCharJobExtraPacket(CCharEntity* PChar, bool mjob) ref(0x0C + (idx * 2)) = PChar->m_PMonstrosity->EquippedInstincts[idx]; } - /* - ref(0x0C) = 0x00; - ref(0x0D) = 0x03; - ref(0x0E) = 0x00; - ref(0x0F) = 0x03; - - - ref(0x10) = 0x07; - ref(0x11) = 0x03; - ref(0x12) = 0x09; - ref(0x13) = 0x03; - ref(0x14) = 0x00; - ref(0x15) = 0x00; - ref(0x16) = 0x00; - ref(0x17) = 0x00; - ref(0x18) = 0x00; - ref(0x19) = 0x00; - ref(0x1A) = 0x00; - ref(0x1B) = 0x00; - ref(0x1C) = 0x00; - ref(0x1D) = 0x00; - ref(0x1E) = 0x00; - ref(0x1F) = 0x00; - - ref(0x20) = 0x00; - ref(0x21) = 0x00; - ref(0x22) = 0x00; - ref(0x23) = 0x00; - ref(0x24) = 0x09; - ref(0x25) = 0x7D; - ref(0x26) = 0x08; - ref(0x27) = 0x00; - ref(0x28) = 0x28; - ref(0x29) = 0x04; - ref(0x2A) = 0x64; - ref(0x2B) = 0x02; - ref(0x2C) = 0x33; - ref(0x2D) = 0x00; - ref(0x2E) = 0x64; - ref(0x2F) = 0x20; - - ref(0x30) = 0x00; - ref(0x31) = 0x6C; - ref(0x32) = 0xFB; - ref(0x33) = 0x38; - ref(0x34) = 0x00; - ref(0x35) = 0x00; - ref(0x36) = 0x20; - ref(0x37) = 0x00; - ref(0x38) = 0x00; - ref(0x39) = 0x00; - ref(0x3A) = 0x00; - ref(0x3B) = 0x00; - ref(0x3C) = 0x20; - ref(0x3D) = 0xA0; - ref(0x3E) = 0x03; - ref(0x3F) = 0x00; - - ref(0x40) = 0x4C; - ref(0x41) = 0xDC; - ref(0x42) = 0xE3; - ref(0x43) = 0x28; - ref(0x44) = 0x00; - ref(0x45) = 0x00; - ref(0x46) = 0x00; - ref(0x47) = 0x00; - ref(0x48) = 0x00; - ref(0x49) = 0x00; - ref(0x4A) = 0x00; - ref(0x4B) = 0x00; - ref(0x4C) = 0x00; - ref(0x4D) = 0x00; - ref(0x4E) = 0x00; - ref(0x4F) = 0x00; - - ref(0x50) = 0x00; - ref(0x51) = 0x00; - ref(0x52) = 0x00; - ref(0x53) = 0x00; - ref(0x54) = 0xFD; - ref(0x55) = 0x81; - ref(0x56) = 0x09; - ref(0x57) = 0x7D; - ref(0x58) = 0x00; - ref(0x59) = 0x06; - ref(0x5A) = 0x00; - ref(0x5B) = 0x00; - ref(0x5C) = 0x7B; - ref(0x5D) = 0x00; - ref(0x5E) = 0x00; - ref(0x5F) = 0x00; - - // Random things from other packet caps - ref(0x6C) = 0x01; - ref(0x6E) = 0x01; - ref(0x77) = 0x01; - ref(0x87) = 0x24; - ref(0x8A) = 0x28; - ref(0x96) = 0x19; - */ + // 0x24: Name1? + // 0x25: Name2? } } From f8bf7d4ad8899796bd7d550ef1ea050ddf1d8c70 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Wed, 4 Oct 2023 15:42:51 +0100 Subject: [PATCH 019/103] Fix latter instinct equipping --- src/map/monstrosity.cpp | 15 +++++++++++++-- src/map/packets/char_job_extra.cpp | 2 +- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/map/monstrosity.cpp b/src/map/monstrosity.cpp index eac87f3172c..4cf6dd9e8b0 100644 --- a/src/map/monstrosity.cpp +++ b/src/map/monstrosity.cpp @@ -140,6 +140,13 @@ void monstrosity::HandleEquipChangePacket(CCharEntity* PChar, CBasicPacket& data { PChar->m_PMonstrosity->Species = data.ref(0x0C); PChar->m_PMonstrosity->Look = gMonstrositySpeciesMap[PChar->m_PMonstrosity->Species].look; + + // TODO: Detect variants and don't remove instincts + // Remove All + for (std::size_t idx = 0; idx < 12; ++idx) + { + PChar->m_PMonstrosity->EquippedInstincts[idx] = 0x0000; + } } else if (flag == 0x04) // Instinct Change { @@ -148,7 +155,11 @@ void monstrosity::HandleEquipChangePacket(CCharEntity* PChar, CBasicPacket& data { for (std::size_t idx = 0; idx < 12; ++idx) { - PChar->m_PMonstrosity->EquippedInstincts[idx] = 0x0000; + uint16 value = data.ref(0x10 + (idx * 2)); + if (value != 0) + { + PChar->m_PMonstrosity->EquippedInstincts[idx] = 0x0000; + } } } else // Set @@ -158,7 +169,7 @@ void monstrosity::HandleEquipChangePacket(CCharEntity* PChar, CBasicPacket& data uint16 value = data.ref(0x10 + (idx * 2)); if (value != 0) { - PChar->m_PMonstrosity->EquippedInstincts[idx] = value; + PChar->m_PMonstrosity->EquippedInstincts[idx] = value == 0xFFFF ? 0x0000 : value; } } } diff --git a/src/map/packets/char_job_extra.cpp b/src/map/packets/char_job_extra.cpp index 8586f34c42c..5b40c4df3fb 100644 --- a/src/map/packets/char_job_extra.cpp +++ b/src/map/packets/char_job_extra.cpp @@ -144,7 +144,7 @@ CCharJobExtraPacket::CCharJobExtraPacket(CCharEntity* PChar, bool mjob) for (std::size_t idx = 0; idx < 12; ++idx) { - ref(0x0C + (idx * 2)) = PChar->m_PMonstrosity->EquippedInstincts[idx]; + ref(0x0C + (idx * 2)) = PChar->m_PMonstrosity->EquippedInstincts[idx]; } // 0x24: Name1? From 576799efb28fbef2bf6569a869727e8eb6e013bf Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Wed, 4 Oct 2023 18:54:24 +0100 Subject: [PATCH 020/103] Species data entry --- documentation/model_ids.txt | 2 +- scripts/commands/monstrosity.lua | 88 --------- sql/monstrosity_species.sql | 304 +++++++++++++++++++++++-------- src/map/monstrosity.cpp | 61 ++++--- src/map/monstrosity.h | 1 + 5 files changed, 266 insertions(+), 190 deletions(-) diff --git a/documentation/model_ids.txt b/documentation/model_ids.txt index ee19de00538..be74336b34d 100644 --- a/documentation/model_ids.txt +++ b/documentation/model_ids.txt @@ -2453,8 +2453,8 @@ ID Number Costume Name 2468 Altana??? 2500 2678 Arciela -2882 FF14 Spriggan 2881 DQ Slime +2882 FF14 Spriggan 2907 DQ She-Slime (Red Slime) 2908 DQ Metal Slime 2909 FF14 Spriggan diff --git a/scripts/commands/monstrosity.lua b/scripts/commands/monstrosity.lua index 7f8b663277c..16a2106a930 100644 --- a/scripts/commands/monstrosity.lua +++ b/scripts/commands/monstrosity.lua @@ -339,7 +339,6 @@ local purchasableInstincts = } commandObj.onTrigger = function(player) - --[[ if player:getCharVar('MONSTROSITY_START') == 1 then player:setCharVar('MONSTROSITY_START', 0) else @@ -347,93 +346,6 @@ commandObj.onTrigger = function(player) end player:setPos(player:getXPos(), player:getYPos(), player:getZPos(), player:getRotPos(), player:getZoneID()) - ]] - - -- 0x010C: Rabbit - -- 0x010D: Onyx Rabbit - -- 0x010E: Alabaster Rabbit - -- 0x0791: Lapinion - - -- 0x0B48: New Years Mandragora - - local data = - { - -- 1 byte per entry, mapped out to monstrositySpecies table - -- (0 - 127) - levels = - { - }, - - -- Bitfield (0 - 63) - instincts = - { - }, - - -- Bitfield (0 - 31) - variants = - { - }, - } - - -- Set all levels to 99 - for _, val in pairs(monstrositySpecies) do - data.levels[val] = 99 - end - - -- Instincts by MON level - -- NOTE: Since this is a bitfield, it's zero-indexed! - for _, val in pairs(monstrositySpecies) do - local speciesKey = val - local speciesLevel = data.levels[val] - local byteOffset = math.floor(speciesKey / 4) - local unlockAmount = math.floor(speciesLevel / 30) - local shiftAmount = (speciesKey * 2) % 8 - - -- Special case for writing Slime & Spriggan data at the end of the 64-byte array - if byteOffset == 31 then - byteOffset = 63 - end - - -- print(speciesKey, speciesLevel, unlockAmount, byteOffset .. ':' .. shiftAmount) - - if byteOffset < 64 then - data.instincts[byteOffset] = bit.bor(data.instincts[byteOffset] or 0, bit.lshift(unlockAmount, shiftAmount)) - else - print("byteOffset out of range") - end - end - - -- Instincts (Purchasable) - for _, val in pairs(purchasableInstincts) do - local byteOffset = 20 + math.floor(val / 8) - local shiftAmount = val % 8 - - -- print(val, byteOffset, shiftAmount) - - if byteOffset >= 20 and byteOffset < 24 then - data.instincts[byteOffset] = bit.bor(data.instincts[byteOffset] or 0, bit.lshift(0x01, shiftAmount)) - else - print("byteOffset out of range") - end - end - - -- Variants - -- Force unlock all - for _, val in pairs(monstrosityVariants) do - local speciesKey = val - local byteOffset = math.floor(speciesKey / 8) - local shiftAmount = speciesKey % 8 - - -- print(speciesKey, byteOffset, shiftAmount) - - if byteOffset < 32 then - data.variants[byteOffset] = bit.bor(data.variants[byteOffset] or 0, bit.lshift(0x01, shiftAmount)) - else - print("byteOffset out of range") - end - end - - player:setMonstrosity(data); end return commandObj diff --git a/sql/monstrosity_species.sql b/sql/monstrosity_species.sql index 1b12a317e5c..3e486626539 100644 --- a/sql/monstrosity_species.sql +++ b/sql/monstrosity_species.sql @@ -1,80 +1,232 @@ DROP TABLE IF EXISTS `monstrosity_species`; CREATE TABLE `monstrosity_species` ( - `monstrosity_id` smallint(30) DEFAULT NULL, -- Used as the shift offset into the monstrosity.levels array - `name` varchar(30) DEFAULT NULL, -- Human-readable name. Only used for this file and logging. - `look` smallint(3) unsigned NOT NULL, -- Look data sent from the client - -- `family_id` smallint(3) unsigned NOT NULL, - PRIMARY KEY (`monstrosity_id`) + `monstrosity_id` smallint(30) unsigned NOT NULL, + `monstrosity_species_code` smallint(30) unsigned NOT NULL, + `name` varchar(60) DEFAULT NULL, + `look` smallint(3) unsigned NOT NULL, + PRIMARY KEY (`monstrosity_id`, `monstrosity_species_code`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; -INSERT INTO `monstrosity_species` VALUES (1, "RABBIT", 0x010C); -INSERT INTO `monstrosity_species` VALUES (2, "BEHEMOTH", 0x0194); -- Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (3, "TIGER", 0x0134); -- Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (4, "SHEEP", 0x0154); -- Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (5, "RAM", 0x0000); -INSERT INTO `monstrosity_species` VALUES (6, "DHALMEL", 0x0000); -INSERT INTO `monstrosity_species` VALUES (7, "COEURL", 0x0000); -INSERT INTO `monstrosity_species` VALUES (8, "OPO_OPO", 0x0000); -INSERT INTO `monstrosity_species` VALUES (9, "MANTICORE", 0x0000); -INSERT INTO `monstrosity_species` VALUES (10, "BUFFALO", 0x0000); -INSERT INTO `monstrosity_species` VALUES (11, "MARID", 0x0000); -INSERT INTO `monstrosity_species` VALUES (12, "CERBERUS", 0x0000); -INSERT INTO `monstrosity_species` VALUES (13, "GNOLE", 0x0000); - -INSERT INTO `monstrosity_species` VALUES (15, "FUNGUAR", 0x0000); -INSERT INTO `monstrosity_species` VALUES (16, "TREANT_SAPLING", 0x0000); -INSERT INTO `monstrosity_species` VALUES (17, "MORBOL", 0x0000); -INSERT INTO `monstrosity_species` VALUES (18, "MANDRAGORA", 0x012C); -INSERT INTO `monstrosity_species` VALUES (19, "SABOTENDER", 0x0000); -INSERT INTO `monstrosity_species` VALUES (20, "FLYTRAP", 0x0000); -INSERT INTO `monstrosity_species` VALUES (21, "GOOBBUE", 0x0000); -INSERT INTO `monstrosity_species` VALUES (22, "RAFFLESIA", 0x0000); -INSERT INTO `monstrosity_species` VALUES (23, "PANOPT", 0x0000); - -INSERT INTO `monstrosity_species` VALUES (27, "BEE", 0x0000); -INSERT INTO `monstrosity_species` VALUES (28, "BEETLE", 0x0000); -INSERT INTO `monstrosity_species` VALUES (29, "CRAWLER", 0x0000); -INSERT INTO `monstrosity_species` VALUES (30, "FLY", 0x0000); -INSERT INTO `monstrosity_species` VALUES (31, "SCORPION", 0x0000); -INSERT INTO `monstrosity_species` VALUES (32, "SPIDER", 0x0000); -INSERT INTO `monstrosity_species` VALUES (33, "ANTLION", 0x0000); -INSERT INTO `monstrosity_species` VALUES (34, "DIREMITE", 0x0000); -INSERT INTO `monstrosity_species` VALUES (35, "CHIGOE", 0x0000); -INSERT INTO `monstrosity_species` VALUES (36, "WAMOURACAMPA", 0x0000); -INSERT INTO `monstrosity_species` VALUES (37, "LADYBUG", 0x0000); -INSERT INTO `monstrosity_species` VALUES (38, "GNAT", 0x0000); - -INSERT INTO `monstrosity_species` VALUES (43, "LIZARD", 0x0148); -INSERT INTO `monstrosity_species` VALUES (44, "RAPTOR", 0x0000); -INSERT INTO `monstrosity_species` VALUES (45, "ADAMANTOISE", 0x0000); -INSERT INTO `monstrosity_species` VALUES (46, "BUGARD", 0x0000); -INSERT INTO `monstrosity_species` VALUES (47, "EFT", 0x0000); -INSERT INTO `monstrosity_species` VALUES (48, "WIVRE", 0x0000); -INSERT INTO `monstrosity_species` VALUES (49, "PEISTE", 0x0000); - -INSERT INTO `monstrosity_species` VALUES (52, "SLIME", 0x0000); -INSERT INTO `monstrosity_species` VALUES (53, "HECTEYES", 0x0000); -INSERT INTO `monstrosity_species` VALUES (54, "FLAN", 0x0000); -INSERT INTO `monstrosity_species` VALUES (56, "SLUG", 0x0000); -INSERT INTO `monstrosity_species` VALUES (57, "SANDWORM", 0x0000); -INSERT INTO `monstrosity_species` VALUES (58, "LEECH", 0x0000); - -INSERT INTO `monstrosity_species` VALUES (60, "CRAB", 0x0000); -INSERT INTO `monstrosity_species` VALUES (61, "PUGIL", 0x0000); -INSERT INTO `monstrosity_species` VALUES (62, "SEA_MONK", 0x0000); -INSERT INTO `monstrosity_species` VALUES (63, "URAGNITE", 0x0000); -INSERT INTO `monstrosity_species` VALUES (64, "OROBON", 0x0000); -INSERT INTO `monstrosity_species` VALUES (65, "RUSZOR", 0x0000); -INSERT INTO `monstrosity_species` VALUES (66, "TOAD", 0x0000); - -INSERT INTO `monstrosity_species` VALUES (69, "BIRD", 0x0000); -INSERT INTO `monstrosity_species` VALUES (70, "COCKATRICE", 0x0000); -INSERT INTO `monstrosity_species` VALUES (71, "ROC", 0x0000); -INSERT INTO `monstrosity_species` VALUES (72, "BAT", 0x0000); -INSERT INTO `monstrosity_species` VALUES (73, "HIPPOGRYPH", 0x0000); -INSERT INTO `monstrosity_species` VALUES (74, "APKALLU", 0x0000); -INSERT INTO `monstrosity_species` VALUES (75, "COLIBRI", 0x0000); -INSERT INTO `monstrosity_species` VALUES (76, "AMPHIPTERE", 0x0000); - -INSERT INTO `monstrosity_species` VALUES (126, "DQ_SLIME", 0x0B41); -INSERT INTO `monstrosity_species` VALUES (127, "FFXIV_SPRIGGAN", 0x0B5D); -- Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (1, 1, "Rabbit", 0x010C); +INSERT INTO `monstrosity_species` VALUES (1, 256, "Onyx Rabbit", 0x010D); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (1, 257, "Alabaster Rabbit", 0x010E); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (1, 258, "Lapinion (Rabbit)", 0x0791); -- TODO: Look guessed not capped + +INSERT INTO `monstrosity_species` VALUES (2, 2, "Behemoth", 0x0194); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (2, 259, "Elasamoth (Behemoth)", 0x0195); -- TODO: Look guessed not capped + +INSERT INTO `monstrosity_species` VALUES (3, 3, "Tiger", 0x0134); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (3, 261, "Legendary Tiger", 0x0134); -- TODO: Look guessed not capped, this is the wrong id +INSERT INTO `monstrosity_species` VALUES (3, 262, "Smilodon (Tiger)", 0x0134); -- TODO: Look guessed not capped, this is the wrong id + +INSERT INTO `monstrosity_species` VALUES (4, 4, "Sheep", 0x0154); -- TODO: Look guessed not capped, this is the wrong id +INSERT INTO `monstrosity_species` VALUES (4, 263, "Karakul (Sheep)", 0x0154); -- TODO: Look guessed not capped, this is the wrong id + +INSERT INTO `monstrosity_species` VALUES (5, 5, "Ram (Sheep)", 0x0000); + +INSERT INTO `monstrosity_species` VALUES (6, 6, "Dhalmel", 0x0000); + +INSERT INTO `monstrosity_species` VALUES (7, 7, "Coeurl", 0x0000); +INSERT INTO `monstrosity_species` VALUES (7, 266, "Lynx (Coeurl)", 0x0000); +INSERT INTO `monstrosity_species` VALUES (7, 267, "Collared Lynx (Coeurl)", 0x0000); + +INSERT INTO `monstrosity_species` VALUES (8, 8, "Opo-opo", 0x0000); + +INSERT INTO `monstrosity_species` VALUES (9, 9, "Manticore", 0x0000); +INSERT INTO `monstrosity_species` VALUES (9, 268, "Legendary Manticore", 0x0000); + +INSERT INTO `monstrosity_species` VALUES (10, 10, "Buffalo", 0x0000); + +INSERT INTO `monstrosity_species` VALUES (11, 11, "Marid", 0x0000); + +INSERT INTO `monstrosity_species` VALUES (12, 12, "Cerberus", 0x0000); +INSERT INTO `monstrosity_species` VALUES (12, 269, "Orthrus (Cerberus)", 0x0000); + +INSERT INTO `monstrosity_species` VALUES (13, 13, "Gnole", 0x0000); +INSERT INTO `monstrosity_species` VALUES (13, 270, "Bipedal Gnole", 0x0000); + +INSERT INTO `monstrosity_species` VALUES (15, 15, "Funguar", 0x0000); +INSERT INTO `monstrosity_species` VALUES (15, 271, "Coppercap (Funguar)", 0x0000); + +INSERT INTO `monstrosity_species` VALUES (16, 16, "Treant Sapling", 0x0000); +INSERT INTO `monstrosity_species` VALUES (16, 272, "Treant", 0x0000); +INSERT INTO `monstrosity_species` VALUES (16, 273, "Flowering Treant", 0x0000); +INSERT INTO `monstrosity_species` VALUES (16, 274, "Scarlet-tinged Treant", 0x0000); +INSERT INTO `monstrosity_species` VALUES (16, 275, "Barren Treant", 0x0000); +INSERT INTO `monstrosity_species` VALUES (16, 276, "Necklaced Treant", 0x0000); + +INSERT INTO `monstrosity_species` VALUES (17, 17, "Morbol", 0x0000); +INSERT INTO `monstrosity_species` VALUES (17, 277, "Pygmy Morbol", 0x0000); +INSERT INTO `monstrosity_species` VALUES (17, 278, "Scarce Morbol", 0x0000); +INSERT INTO `monstrosity_species` VALUES (17, 279, "Ameretat (Morbol)", 0x0000); +INSERT INTO `monstrosity_species` VALUES (17, 280, "Purbol (Morbol)", 0x0000); + +INSERT INTO `monstrosity_species` VALUES (18, 18, "Mandragora", 0x012C); +INSERT INTO `monstrosity_species` VALUES (18, 281, "Korrigan (Mandragora)", 0x012C); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (18, 282, "Lycopodium (Mandragora)", 0x012C); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (18, 283, "Pygmy Mandragora", 0x012C); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (18, 284, "Adenium (Mandragora)", 0x012C); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (18, 285, "Pachypodium (Mandragora)", 0x012C); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (18, 286, "Enlightened Mandragora", 0x012C); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (18, 287, "New Year Mandragora", 0x0B48); + +INSERT INTO `monstrosity_species` VALUES (19, 19, "Sabotender", 0x0000); +INSERT INTO `monstrosity_species` VALUES (19, 288, "Sabotender Florido", 0x0000); + +INSERT INTO `monstrosity_species` VALUES (20, 20, "Flytrap", 0x0000); + +INSERT INTO `monstrosity_species` VALUES (21, 21, "Goobbue", 0x0000); + +INSERT INTO `monstrosity_species` VALUES (22, 22, "Rafflesia", 0x0000); +INSERT INTO `monstrosity_species` VALUES (22, 289, "Mitrastema (Rafflesia)", 0x0000); + +INSERT INTO `monstrosity_species` VALUES (23, 23, "Panopt", 0x0000); + +INSERT INTO `monstrosity_species` VALUES (27, 27, "Bee", 0x0000); +INSERT INTO `monstrosity_species` VALUES (27, 290, "Vermillion and Onyx Bee", 0x0000); +INSERT INTO `monstrosity_species` VALUES (27, 291, "Zaffre Bee", 0x0000); + +INSERT INTO `monstrosity_species` VALUES (28, 28, "Beetle", 0x0000); +INSERT INTO `monstrosity_species` VALUES (28, 292, "Onyx Beetle", 0x0000); +INSERT INTO `monstrosity_species` VALUES (28, 293, "Gamboge Beetle", 0x0000); + +INSERT INTO `monstrosity_species` VALUES (29, 29, "Crawler", 0x0000); +INSERT INTO `monstrosity_species` VALUES (29, 294, "Eruca (Crawler)", 0x0000); +INSERT INTO `monstrosity_species` VALUES (29, 295, "Emerald Crawler", 0x0000); +INSERT INTO `monstrosity_species` VALUES (29, 296, "Pygmy Emerald Crawler", 0x0000); + +INSERT INTO `monstrosity_species` VALUES (30, 30, "Fly", 0x0000); +INSERT INTO `monstrosity_species` VALUES (30, 297, "Vermillion Fly", 0x0000); + +INSERT INTO `monstrosity_species` VALUES (31, 31, "Scorpion", 0x0000); +INSERT INTO `monstrosity_species` VALUES (31, 298, "Scolopendrid (Scorpion)", 0x0000); +INSERT INTO `monstrosity_species` VALUES (31, 299, "Unusual Scolopendrid (Scorpion)", 0x0000); + +INSERT INTO `monstrosity_species` VALUES (32, 32, "Spider", 0x0000); +INSERT INTO `monstrosity_species` VALUES (32, 300, "Reticulated Spider", 0x0000); +INSERT INTO `monstrosity_species` VALUES (32, 301, "Vermillion and Onyx Spider", 0x0000); + +INSERT INTO `monstrosity_species` VALUES (33, 33, "Antlion", 0x0000); +INSERT INTO `monstrosity_species` VALUES (33, 302, "Onyx Antlion", 0x0000); +INSERT INTO `monstrosity_species` VALUES (33, 303, "Formiceros (Antlion)", 0x0000); + +INSERT INTO `monstrosity_species` VALUES (34, 34, "Diremite", 0x0000); +INSERT INTO `monstrosity_species` VALUES (34, 304, "Arundemite (Diremite)", 0x0000); + +INSERT INTO `monstrosity_species` VALUES (35, 35, "Chigoe", 0x0000); +INSERT INTO `monstrosity_species` VALUES (35, 305, "Azure Chigoe", 0x0000); + +INSERT INTO `monstrosity_species` VALUES (36, 36, "Wamouracampa (Wamoura larva)", 0x0000); +INSERT INTO `monstrosity_species` VALUES (36, 306, "Coiled Wamouracampa (Wamoura larva)", 0x0000); +INSERT INTO `monstrosity_species` VALUES (36, 307, "Wamoura", 0x0000); +INSERT INTO `monstrosity_species` VALUES (36, 308, "Coral Wamoura", 0x0000); + +INSERT INTO `monstrosity_species` VALUES (37, 37, "Ladybug", 0x0000); +INSERT INTO `monstrosity_species` VALUES (37, 309, "Gold Ladybug", 0x0000); + +INSERT INTO `monstrosity_species` VALUES (38, 38, "Gnat", 0x0000); +INSERT INTO `monstrosity_species` VALUES (38, 310, "Midge (Gnat)", 0x0000); + +INSERT INTO `monstrosity_species` VALUES (43, 43, "Lizard", 0x0148); +INSERT INTO `monstrosity_species` VALUES (43, 315, "Ashen Lizard", 0x0148); -- TODO: Look guessed not capped + +INSERT INTO `monstrosity_species` VALUES (44, 44, "Raptor", 0x0000); +INSERT INTO `monstrosity_species` VALUES (44, 316, "Emerald Raptor", 0x0000); +INSERT INTO `monstrosity_species` VALUES (44, 317, "Vermillion Raptor", 0x0000); + +INSERT INTO `monstrosity_species` VALUES (45, 45, "Adamantoise", 0x0000); +INSERT INTO `monstrosity_species` VALUES (45, 318, "Pygmy Adamantoise", 0x0000); +INSERT INTO `monstrosity_species` VALUES (45, 319, "Legendary Adamantoise", 0x0000); +INSERT INTO `monstrosity_species` VALUES (45, 320, "Ferromantoise (Adamantoise)", 0x0000); + +INSERT INTO `monstrosity_species` VALUES (46, 46, "Bugard", 0x0000); +INSERT INTO `monstrosity_species` VALUES (46, 321, "Abyssobugard (Bugard)", 0x0000); + +INSERT INTO `monstrosity_species` VALUES (47, 47, "Eft", 0x0000); +INSERT INTO `monstrosity_species` VALUES (47, 322, "Tarichuk (Eft)", 0x0000); + +INSERT INTO `monstrosity_species` VALUES (48, 48, "Wivre", 0x0000); +INSERT INTO `monstrosity_species` VALUES (48, 323, "Unusual Wivre", 0x0000); + +INSERT INTO `monstrosity_species` VALUES (49, 49, "Peiste", 0x0000); +INSERT INTO `monstrosity_species` VALUES (49, 324, "Sibilus (Peiste)", 0x0000); + +INSERT INTO `monstrosity_species` VALUES (52, 52, "Slime", 0x0000); +INSERT INTO `monstrosity_species` VALUES (52, 329, "Clot (Slime)", 0x0000); +INSERT INTO `monstrosity_species` VALUES (52, 330, "Gold Slime", 0x0000); +INSERT INTO `monstrosity_species` VALUES (52, 331, "Boil (Slime)", 0x0000); + +INSERT INTO `monstrosity_species` VALUES (53, 53, "Hecteyes", 0x0000); + +INSERT INTO `monstrosity_species` VALUES (54, 54, "Flan", 0x0000); +INSERT INTO `monstrosity_species` VALUES (54, 332, "Gold Flan", 0x0000); +INSERT INTO `monstrosity_species` VALUES (54, 333, "Blancmange (Flan)", 0x0000); + +INSERT INTO `monstrosity_species` VALUES (56, 56, "Slug", 0x0000); + +INSERT INTO `monstrosity_species` VALUES (57, 57, "Sandworm", 0x0000); +INSERT INTO `monstrosity_species` VALUES (57, 334, "Pygmy Sandworm", 0x0000); +INSERT INTO `monstrosity_species` VALUES (57, 335, "Gigaworm (Sandworm)", 0x0000); + +INSERT INTO `monstrosity_species` VALUES (58, 58, "Leech", 0x0000); +INSERT INTO `monstrosity_species` VALUES (58, 336, "Azure Leech", 0x0000); +INSERT INTO `monstrosity_species` VALUES (58, 337, "Obdella (Leech)", 0x0000); + +INSERT INTO `monstrosity_species` VALUES (60, 60, "Crab", 0x0000); +INSERT INTO `monstrosity_species` VALUES (60, 340, "Vermillion Crab", 0x0000); +INSERT INTO `monstrosity_species` VALUES (60, 341, "Basket-burdened Crab", 0x0000); +INSERT INTO `monstrosity_species` VALUES (60, 342, "Vermillion Basket-burdened Crab", 0x0000); +INSERT INTO `monstrosity_species` VALUES (60, 343, "Porter Crab (Crab)", 0x0000); + +INSERT INTO `monstrosity_species` VALUES (61, 61, "Pugil", 0x0000); +INSERT INTO `monstrosity_species` VALUES (61, 344, "Jagil (Pugil)", 0x0000); + +INSERT INTO `monstrosity_species` VALUES (62, 62, "Sea Monk", 0x0000); +INSERT INTO `monstrosity_species` VALUES (62, 345, "Azure Sea Monk", 0x0000); + +INSERT INTO `monstrosity_species` VALUES (63, 63, "Uragnite", 0x0000); +INSERT INTO `monstrosity_species` VALUES (63, 346, "Limascabra (Uragnite)", 0x0000); + +INSERT INTO `monstrosity_species` VALUES (64, 64, "Orobon", 0x0000); +INSERT INTO `monstrosity_species` VALUES (64, 347, "Pygmy Orobon", 0x0000); +INSERT INTO `monstrosity_species` VALUES (64, 348, "Ogrebon (Orobon)", 0x0000); + +INSERT INTO `monstrosity_species` VALUES (65, 65, "Ruszor", 0x0000); + +INSERT INTO `monstrosity_species` VALUES (66, 66, "Toad", 0x0000); +INSERT INTO `monstrosity_species` VALUES (66, 349, "Azure Toad", 0x0000); +INSERT INTO `monstrosity_species` VALUES (66, 350, "Vermillion Toad", 0x0000); + +INSERT INTO `monstrosity_species` VALUES (69, 69, "Bird", 0x0000); +INSERT INTO `monstrosity_species` VALUES (69, 351, "Onyx Bird", 0x0000); + +INSERT INTO `monstrosity_species` VALUES (70, 70, "Cockatrice", 0x0000); +INSERT INTO `monstrosity_species` VALUES (70, 352, "Ziz (Cockatrice)", 0x0000); + +INSERT INTO `monstrosity_species` VALUES (71, 71, "Roc", 0x0000); +INSERT INTO `monstrosity_species` VALUES (71, 353, "Legendary Roc", 0x0000); +INSERT INTO `monstrosity_species` VALUES (71, 354, "Gagana (Roc)", 0x0000); + +INSERT INTO `monstrosity_species` VALUES (72, 72, "Bat", 0x0000); +INSERT INTO `monstrosity_species` VALUES (72, 355, "Bats", 0x0000); +INSERT INTO `monstrosity_species` VALUES (72, 356, "Vermillion Bat", 0x0000); +INSERT INTO `monstrosity_species` VALUES (72, 357, "Vermillion Bats", 0x0000); + +INSERT INTO `monstrosity_species` VALUES (73, 73, "Hippogryph", 0x0000); + +INSERT INTO `monstrosity_species` VALUES (74, 74, "Apkallu", 0x0000); +INSERT INTO `monstrosity_species` VALUES (74, 358, "Inguza (Apkallu)", 0x0000); + +INSERT INTO `monstrosity_species` VALUES (75, 75, "Colibri", 0x0000); +INSERT INTO `monstrosity_species` VALUES (75, 359, "Toucalibri (Colibri)", 0x0000); + +INSERT INTO `monstrosity_species` VALUES (76, 76, "Amphiptere", 0x0000); +INSERT INTO `monstrosity_species` VALUES (76, 360, "Sanguiptere (Amphiptere)", 0x0000); + +INSERT INTO `monstrosity_species` VALUES (126, 254, "DQ Slime", 0x0B41); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (126, 508, "DQ She-Slime", 0x0B5B); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (126, 509, "DQ Metal Slime", 0x0B5C); -- TODO: Look guessed not capped + +INSERT INTO `monstrosity_species` VALUES (127, 255, "FFXIV Spriggan", 0x0B42); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (127, 510, "FFXIV Spriggan.C", 0x0B5D); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (127, 511, "FFXIV Spriggan.G", 0x0B5E); -- TODO: Look guessed not capped diff --git a/src/map/monstrosity.cpp b/src/map/monstrosity.cpp index 4cf6dd9e8b0..87ae11a0a07 100644 --- a/src/map/monstrosity.cpp +++ b/src/map/monstrosity.cpp @@ -39,17 +39,19 @@ extern std::unique_ptr sql; struct MonstrositySpeciesRow { uint8 monstrosityId; + uint16 monstrositySpeciesCode; std::string name; uint16 look; }; namespace { - std::unordered_map gMonstrositySpeciesMap; + std::unordered_map gMonstrosityDataBySpeciesMap; } // namespace monstrosity::MonstrosityData_t::MonstrosityData_t() -: Species(0x0001) +: MonstrosityId(0x01) +, Species(0x0001) , Flags(0x0B44) , Look(0x010C) , NameBase(0x8001) @@ -61,18 +63,19 @@ monstrosity::MonstrosityData_t::MonstrosityData_t() void monstrosity::LoadStaticData() { - int32 ret = sql->Query("SELECT monstrosity_id, name, look FROM monstrosity_species;"); + int32 ret = sql->Query("SELECT monstrosity_id, monstrosity_species_code, name, look FROM monstrosity_species;"); if (ret != SQL_ERROR && sql->NumRows() != 0) { while (sql->NextRow() == SQL_SUCCESS) { MonstrositySpeciesRow row; - row.monstrosityId = static_cast(sql->GetUIntData(0)); - row.name = sql->GetStringData(1); - row.look = static_cast(sql->GetUIntData(2)); + row.monstrosityId = static_cast(sql->GetUIntData(0)); + row.monstrositySpeciesCode = static_cast(sql->GetUIntData(1)); + row.name = sql->GetStringData(2); + row.look = static_cast(sql->GetUIntData(3)); - gMonstrositySpeciesMap[row.monstrosityId] = row; + gMonstrosityDataBySpeciesMap[row.monstrositySpeciesCode] = row; } } } @@ -115,6 +118,8 @@ void monstrosity::SendFullMonstrosityUpdate(CCharEntity* PChar) // : The species box on the UI should never be empty - everything breaks if that happens. // : We should detect a bad state and fall back to being a Lv1 Bunny if that happens. + // TODO: Only send model change packets when the model actually changes - otherwise it disappears! + PChar->pushPacket(new CMonipulatorPacket1(PChar)); PChar->pushPacket(new CMonipulatorPacket2(PChar)); PChar->pushPacket(new CCharJobsPacket(PChar)); @@ -138,14 +143,26 @@ void monstrosity::HandleEquipChangePacket(CCharEntity* PChar, CBasicPacket& data uint8 flag = data.ref(0x0A); if (flag == 0x01) // Species Change { - PChar->m_PMonstrosity->Species = data.ref(0x0C); - PChar->m_PMonstrosity->Look = gMonstrositySpeciesMap[PChar->m_PMonstrosity->Species].look; + auto previousId = PChar->m_PMonstrosity->MonstrosityId; - // TODO: Detect variants and don't remove instincts - // Remove All - for (std::size_t idx = 0; idx < 12; ++idx) + auto newSpecies = data.ref(0x0C); + + auto data = gMonstrosityDataBySpeciesMap[newSpecies]; + + // For debugging and data entry + ShowInfo(fmt::format("Species: {}: {}", newSpecies, data.name)); + + PChar->m_PMonstrosity->Species = newSpecies; + + PChar->m_PMonstrosity->MonstrosityId = data.monstrosityId; + PChar->m_PMonstrosity->Look = data.look; + + if (PChar->m_PMonstrosity->MonstrosityId != previousId) { - PChar->m_PMonstrosity->EquippedInstincts[idx] = 0x0000; + for (std::size_t idx = 0; idx < 12; ++idx) + { + PChar->m_PMonstrosity->EquippedInstincts[idx] = 0x0000; + } } } else if (flag == 0x04) // Instinct Change @@ -169,6 +186,7 @@ void monstrosity::HandleEquipChangePacket(CCharEntity* PChar, CBasicPacket& data uint16 value = data.ref(0x10 + (idx * 2)); if (value != 0) { + ShowInfo(fmt::format("{}: {}", idx, value)); PChar->m_PMonstrosity->EquippedInstincts[idx] = value == 0xFFFF ? 0x0000 : value; } } @@ -180,34 +198,27 @@ void monstrosity::HandleEquipChangePacket(CCharEntity* PChar, CBasicPacket& data PChar->m_PMonstrosity->NamePrefix2 = data.ref(0x29); } - // std::string EquipStr = ""; - // for (std::size_t idx = 0; idx < 12; ++idx) - // { - // EquipStr += fmt::format("{}: {}, ", idx, PChar->m_PMonstrosity->EquippedInstincts[idx]); - // } - // ShowInfo(EquipStr.c_str()); - // TODO: Is this too much traffic? SendFullMonstrosityUpdate(PChar); } void monstrosity::MaxAllLevels(CCharEntity* PChar) { - for (auto const& [monstrosityId, entry] : gMonstrositySpeciesMap) + for (auto const& [_, entry] : gMonstrosityDataBySpeciesMap) { - PChar->m_PMonstrosity->levels[monstrosityId] = 99; + PChar->m_PMonstrosity->levels[entry.monstrosityId] = 99; } } void monstrosity::UnlockAllInstincts(CCharEntity* PChar) { // Level based - for (auto const& [monstrosityId, entry] : gMonstrositySpeciesMap) + for (auto const& [_, entry] : gMonstrosityDataBySpeciesMap) { uint8 level = 99; - uint8 byteOffset = monstrosityId / 4; + uint8 byteOffset = entry.monstrosityId / 4; uint8 unlockAmount = level / 30; - uint8 shiftAmount = (monstrosityId * 2) % 8; + uint8 shiftAmount = (entry.monstrosityId * 2) % 8; // Special case for writing Slime & Spriggan data at the end of the 64-byte array if (byteOffset == 31) diff --git a/src/map/monstrosity.h b/src/map/monstrosity.h index 048e8b6e81f..db457b4826b 100644 --- a/src/map/monstrosity.h +++ b/src/map/monstrosity.h @@ -36,6 +36,7 @@ namespace monstrosity public: MonstrosityData_t(); + uint8 MonstrosityId; uint16 Species; uint16 Flags; uint16 Look; From 3bea09df4df34151ef13fffb8f2e5c3bb5063bba Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Wed, 4 Oct 2023 21:15:16 +0100 Subject: [PATCH 021/103] SQL data entry --- scripts/commands/monstrosity.lua | 332 ------------------------------ sql/monstrosity_exp_table.sql | 109 ++++++++++ sql/monstrosity_instinct_mods.sql | 23 +++ sql/monstrosity_instincts.sql | 298 +++++++++++++++++++++++++++ 4 files changed, 430 insertions(+), 332 deletions(-) create mode 100644 sql/monstrosity_instinct_mods.sql diff --git a/scripts/commands/monstrosity.lua b/scripts/commands/monstrosity.lua index 16a2106a930..ea1fc728989 100644 --- a/scripts/commands/monstrosity.lua +++ b/scripts/commands/monstrosity.lua @@ -6,338 +6,6 @@ commandObj.cmdprops = parameters = '' } -local monstrositySpecies = -{ - RABBIT = 1, - BEHEMOTH = 2, - TIGER = 3, - SHEEP = 4, - RAM = 5, - DHALMEL = 6, - COEURL = 7, - OPO_OPO = 8, - MANTICORE = 9, - BUFFALO = 10, - MARID = 11, - CERBERUS = 12, - GNOLE = 13, - - -- No 14? - - FUNGUAR = 15, - TREANT_SAPLING = 16, - MORBOL = 17, - MANDRAGORA = 18, - SABOTENDER = 19, - FLYTRAP = 20, - GOOBBUE = 21, - RAFFLESIA = 22, - PANOPT = 23, - - -- No 24-26? - - BEE = 27, - BEETLE = 28, - CRAWLER = 29, - FLY = 30, - SCORPION = 31, - SPIDER = 32, - ANTLION = 33, - DIREMITE = 34, - CHIGOE = 35, - WAMOURACAMPA = 36, - LADYBUG = 37, - GNAT = 38, - - -- No 39-42? - - LIZARD = 43, - RAPTOR = 44, - ADAMANTOISE = 45, - BUGARD = 46, - EFT = 47, - WIVRE = 48, - PEISTE = 49, - - -- No 50-52? - - SLIME = 52, - HECTEYES = 53, - FLAN = 54, - SLUG = 56, - SANDWORM = 57, - LEECH = 58, - - -- No 59? - - CRAB = 60, - PUGIL = 61, - SEA_MONK = 62, - URAGNITE = 63, - OROBON = 64, - RUSZOR = 65, - TOAD = 66, - - -- No 67-68 - - BIRD = 69, - COCKATRICE = 70, - ROC = 71, - BAT = 72, - HIPPOGRYPH = 73, - APKALLU = 74, - COLIBRI = 75, - AMPHIPTERE = 76, - - DQ_SLIME = 126, - FFXIV_SPRIGGAN = 127, -} - --- NOTE: This was mapped by hand, the gaps might be mistakes! -local monstrosityVariants = -{ - -- Rabbit - ONYX_RABBIT = 0, - ALABASTER_RABBIT = 1, - LAPINION = 2, - - -- Behemoth - ELSAMOTH = 3, - - -- Tiger - LEGENDARY_TIGER = 5, - SMILODON = 6, - - -- Sheep - KARAKUL = 7, - - -- Coeurl - LYNX = 10, - COLLARED_LYNX = 11, - - -- Manticore - LEGENDARY_MANTICORE = 12, - - -- Cerberus - ORTHRUS = 13, - - -- Gnole - BIPEDAL_GNOLE = 14, - - -- Funguar - COPPERCAP = 15, - - -- Treant Sapling - TREANT = 16, - FLOWERING_TREANT = 17, - SCARLET_TINGED_TREANT = 18, - BARREN_TREANT = 19, - NECKLACED_TREANT = 20, - - -- Morbol - PYGMY_MORBOL = 21, - SCARE_MORBOL = 22, - AMERETAT = 23, - PURBOL = 24, - - -- Mandragora - KORRIGAN = 25, - LYCOPODIUM = 26, - PYGMY_MANDRAGORA = 27, - ADENIUM = 28, - PACHYPODIUM = 29, - ENLIGHTENED_MANDRAGORA = 30, - NEW_YEAR_MANDRAGORA = 31, - - -- Sabotender - SABOTENDER_FLORIDO = 32, - - -- Rafflesia - MITRASTEMA = 33, - - -- Bee - VERMILLION_AND_ONYX_BEE = 34, - ZAFFRE_BEE = 35, - - -- Beetle - ONYX_BEETLE = 36, - GAMBOGE_BEETLE = 37, - - -- Crawler - ERUCA = 38, - EMERALD_CRAWLER = 39, - PYGMY_EMERALD_CRAWLER = 40, - - -- Fly - VERMILLION_FLY = 41, - - -- Scorpion - SCOLOPENDRID = 42, - UNUSUAL_SCOLOPENDRID = 43, - - -- Spider - RETICULATED_SPIDER = 44, - VERMILLION_AND_ONYX_SPIDER = 45, - - -- Antlion - ONYX_ANTLION = 46, - FORMICEROS = 47, - - -- Diremite - ARUNDIMITE = 48, - - -- Chigoe - AZURE_CHIGOE = 49, - - -- Wamouracampa - COILED_WAMOURACAMPA = 50, - - -- Wamoura - WAMOURA = 51, - CORAL_WAMOURA = 52, - - -- Ladybug - GOLD_LADYBUG = 53, - - -- Gnat - MIDGE = 54, - - -- Lizard - ASHEN_LIZARD = 59, - - -- Raptor - EMERALD_RAPTOR = 60, - VERMILLION_RAPTOR = 61, - - -- Adamantoise - PYGMY_ADAMANTOISE = 62, - LEGENDARY_ADAMANTOISE = 63, - FERROMANTOISE = 64, - - -- Bugard - ABYSSOBUGARD = 65, - - -- Eft - TARICHUK = 66, - - -- Wivre - UNUSUAL_WIVRE = 67, - - -- Peiste - SIBILUS = 68, - - -- Slime - CLOT = 73, - GOLD_SLIME = 74, - BOIL = 75, - - -- Flan - GOLD_FLAN = 76, - BLANCMANGE = 77, - - -- Sandworm - PYGMY_SANDWORM = 78, - GIGAWORM = 79, - - -- Leech - AZURE_LEECH = 80, - OBDELLA = 81, - - -- Crab - VERMILLION_CRAB = 84, - BASKET_BURDENED_CRAB = 85, - VERMILLION_BASKET_BURDENED_CRAB = 86, - PORTER_CRAB = 87, - - -- Pugil - JAGIL = 88, - - -- Sea Monk - AZURE_SEA_MONK = 89, - - -- Uragnite - LIMASCABRA = 90, - - -- Orobon - PYGMY_OROBON = 91, - OGREBON = 92, - - -- Toad - AZURE_TOAD = 93, - VERMILLION_TOAD = 94, - - -- Bird - ONYX_BIRD = 95, - - -- Cockatrice - ZIZ = 96, - - -- Roc - LEGENDARY_ROC = 97, - GAGANA = 98, - - -- Bat - BATS = 99, - VERMILLION_BAT = 100, - VERMILLION_BATS = 101, - - -- Apkallu - INGUZA = 102, - - -- Colibri - TOUCALIBRI = 103, - - -- Amphiptere - SANGUIPTERE = 104, - - -- Slime - SHE_SLIME = 252, - METAL_SLIME = 253, - - -- Spriggan - SPRIGGAN_C = 254, - SPRIGGAN_G = 255, -} - -local purchasableInstincts = -{ - -- Default - HUME_I = 0, - ELVAAN_I = 1, - TARU_I = 2, - MITHRA_I = 3, - GALKA_I = 4, - - HUME_II = 5, - ELVAAN_II = 6, - TARU_II = 7, - MITHRA_II = 8, - GALKA_II = 9, - - WAR = 10, - MNK = 11, - WHM = 12, - BLM = 13, - RDM = 14, - THF = 15, - PLD = 16, - DRK = 17, - BST = 18, - BRD = 19, - RNG = 20, - SAM = 21, - NIN = 22, - DRG = 23, - SMN = 24, - BLU = 25, - COR = 26, - PUP = 27, - DNC = 28, - SCH = 29, - GEO = 30, - RUN = 31, -} - commandObj.onTrigger = function(player) if player:getCharVar('MONSTROSITY_START') == 1 then player:setCharVar('MONSTROSITY_START', 0) diff --git a/sql/monstrosity_exp_table.sql b/sql/monstrosity_exp_table.sql index e69de29bb2d..57751cf4a0e 100644 --- a/sql/monstrosity_exp_table.sql +++ b/sql/monstrosity_exp_table.sql @@ -0,0 +1,109 @@ +DROP TABLE IF EXISTS `monstrosity_exp_table`; +CREATE TABLE IF NOT EXISTS `monstrosity_exp_table` ( + `level` tinyint(2) NOT NULL, + `amount` smallint(4) unsigned NOT NULL DEFAULT '0', + PRIMARY KEY (`level`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; + +-- https://www.bg-wiki.com/ffxi/Category:Monstrosity +INSERT INTO `monstrosity_exp_table` VALUES (1, 300); +INSERT INTO `monstrosity_exp_table` VALUES (2, 400); +INSERT INTO `monstrosity_exp_table` VALUES (3, 500); +INSERT INTO `monstrosity_exp_table` VALUES (4, 700); +INSERT INTO `monstrosity_exp_table` VALUES (5, 800); +INSERT INTO `monstrosity_exp_table` VALUES (6, 800); +INSERT INTO `monstrosity_exp_table` VALUES (7, 900); +INSERT INTO `monstrosity_exp_table` VALUES (8, 1000); +INSERT INTO `monstrosity_exp_table` VALUES (9, 1100); +INSERT INTO `monstrosity_exp_table` VALUES (10, 1200); +INSERT INTO `monstrosity_exp_table` VALUES (11, 1350); +INSERT INTO `monstrosity_exp_table` VALUES (12, 1500); +INSERT INTO `monstrosity_exp_table` VALUES (13, 1650); +INSERT INTO `monstrosity_exp_table` VALUES (14, 1800); +INSERT INTO `monstrosity_exp_table` VALUES (15, 1950); +INSERT INTO `monstrosity_exp_table` VALUES (16, 2100); +INSERT INTO `monstrosity_exp_table` VALUES (17, 2250); +INSERT INTO `monstrosity_exp_table` VALUES (18, 2400); +INSERT INTO `monstrosity_exp_table` VALUES (19, 2550); +INSERT INTO `monstrosity_exp_table` VALUES (20, 2700); +INSERT INTO `monstrosity_exp_table` VALUES (21, 2850); +INSERT INTO `monstrosity_exp_table` VALUES (22, 3000); +INSERT INTO `monstrosity_exp_table` VALUES (23, 3100); +INSERT INTO `monstrosity_exp_table` VALUES (24, 3200); +INSERT INTO `monstrosity_exp_table` VALUES (25, 3300); +INSERT INTO `monstrosity_exp_table` VALUES (26, 3400); +INSERT INTO `monstrosity_exp_table` VALUES (27, 3500); +INSERT INTO `monstrosity_exp_table` VALUES (28, 3600); +INSERT INTO `monstrosity_exp_table` VALUES (29, 3700); +INSERT INTO `monstrosity_exp_table` VALUES (30, 3800); +INSERT INTO `monstrosity_exp_table` VALUES (31, 3900); +INSERT INTO `monstrosity_exp_table` VALUES (32, 4000); +INSERT INTO `monstrosity_exp_table` VALUES (33, 4100); +INSERT INTO `monstrosity_exp_table` VALUES (34, 4200); +INSERT INTO `monstrosity_exp_table` VALUES (35, 4300); +INSERT INTO `monstrosity_exp_table` VALUES (36, 4400); +INSERT INTO `monstrosity_exp_table` VALUES (37, 4500); +INSERT INTO `monstrosity_exp_table` VALUES (38, 4600); +INSERT INTO `monstrosity_exp_table` VALUES (39, 4700); +INSERT INTO `monstrosity_exp_table` VALUES (40, 4800); +INSERT INTO `monstrosity_exp_table` VALUES (41, 4900); +INSERT INTO `monstrosity_exp_table` VALUES (42, 5000); +INSERT INTO `monstrosity_exp_table` VALUES (43, 5100); +INSERT INTO `monstrosity_exp_table` VALUES (44, 5200); +INSERT INTO `monstrosity_exp_table` VALUES (45, 5300); +INSERT INTO `monstrosity_exp_table` VALUES (46, 5400); +INSERT INTO `monstrosity_exp_table` VALUES (47, 5500); +INSERT INTO `monstrosity_exp_table` VALUES (48, 5600); +INSERT INTO `monstrosity_exp_table` VALUES (49, 5700); +INSERT INTO `monstrosity_exp_table` VALUES (50, 5800); +INSERT INTO `monstrosity_exp_table` VALUES (51, 6000); +INSERT INTO `monstrosity_exp_table` VALUES (52, 6400); +INSERT INTO `monstrosity_exp_table` VALUES (53, 6800); +INSERT INTO `monstrosity_exp_table` VALUES (54, 7200); +INSERT INTO `monstrosity_exp_table` VALUES (55, 7600); +INSERT INTO `monstrosity_exp_table` VALUES (56, 8000); +INSERT INTO `monstrosity_exp_table` VALUES (57, 8400); +INSERT INTO `monstrosity_exp_table` VALUES (58, 8800); +INSERT INTO `monstrosity_exp_table` VALUES (59, 9200); +INSERT INTO `monstrosity_exp_table` VALUES (60, 9600); +INSERT INTO `monstrosity_exp_table` VALUES (61, 10000); +INSERT INTO `monstrosity_exp_table` VALUES (62, 10500); +INSERT INTO `monstrosity_exp_table` VALUES (63, 11000); +INSERT INTO `monstrosity_exp_table` VALUES (64, 11500); +INSERT INTO `monstrosity_exp_table` VALUES (65, 12000); +INSERT INTO `monstrosity_exp_table` VALUES (66, 12500); +INSERT INTO `monstrosity_exp_table` VALUES (67, 13000); +INSERT INTO `monstrosity_exp_table` VALUES (68, 13500); +INSERT INTO `monstrosity_exp_table` VALUES (69, 14000); +INSERT INTO `monstrosity_exp_table` VALUES (70, 14500); +INSERT INTO `monstrosity_exp_table` VALUES (71, 15000); +INSERT INTO `monstrosity_exp_table` VALUES (72, 15500); +INSERT INTO `monstrosity_exp_table` VALUES (73, 16000); +INSERT INTO `monstrosity_exp_table` VALUES (74, 16500); +INSERT INTO `monstrosity_exp_table` VALUES (75, 17000); +INSERT INTO `monstrosity_exp_table` VALUES (76, 17500); +INSERT INTO `monstrosity_exp_table` VALUES (77, 18000); +INSERT INTO `monstrosity_exp_table` VALUES (78, 18500); +INSERT INTO `monstrosity_exp_table` VALUES (79, 19000); +INSERT INTO `monstrosity_exp_table` VALUES (80, 19500); +INSERT INTO `monstrosity_exp_table` VALUES (81, 20000); +INSERT INTO `monstrosity_exp_table` VALUES (82, 20500); +INSERT INTO `monstrosity_exp_table` VALUES (83, 21000); +INSERT INTO `monstrosity_exp_table` VALUES (84, 21500); +INSERT INTO `monstrosity_exp_table` VALUES (85, 22000); +INSERT INTO `monstrosity_exp_table` VALUES (86, 22500); +INSERT INTO `monstrosity_exp_table` VALUES (87, 23000); +INSERT INTO `monstrosity_exp_table` VALUES (88, 23500); +INSERT INTO `monstrosity_exp_table` VALUES (89, 24000); + +-- TODO: These are guessed, what are the exp requirements for these? +INSERT INTO `monstrosity_exp_table` VALUES (90, 24500); +INSERT INTO `monstrosity_exp_table` VALUES (91, 25000); +INSERT INTO `monstrosity_exp_table` VALUES (92, 25500); +INSERT INTO `monstrosity_exp_table` VALUES (93, 26000); +INSERT INTO `monstrosity_exp_table` VALUES (94, 26500); +INSERT INTO `monstrosity_exp_table` VALUES (95, 27000); +INSERT INTO `monstrosity_exp_table` VALUES (96, 27500); +INSERT INTO `monstrosity_exp_table` VALUES (97, 28000); +INSERT INTO `monstrosity_exp_table` VALUES (98, 28500); +INSERT INTO `monstrosity_exp_table` VALUES (99, 29000); diff --git a/sql/monstrosity_instinct_mods.sql b/sql/monstrosity_instinct_mods.sql new file mode 100644 index 00000000000..545aaefc644 --- /dev/null +++ b/sql/monstrosity_instinct_mods.sql @@ -0,0 +1,23 @@ +DROP TABLE IF EXISTS `monstrosity_instinct_mods`; +CREATE TABLE `monstrosity_instinct_mods` ( + `monstrosity_instinct_id` smallint(5) unsigned NOT NULL, + `modId` smallint(5) unsigned NOT NULL, + `value` smallint(5) NOT NULL DEFAULT 0, + PRIMARY KEY (`monstrosity_instinct_id`,`modId`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; + +-- Rabbit instinct I +INSERT INTO `monstrosity_instinct_mods` VALUES (3,3,2); -- HPP: 2 +INSERT INTO `monstrosity_instinct_mods` VALUES (3,11,5); -- AGI: 5 +INSERT INTO `monstrosity_instinct_mods` VALUES (3,23,10); -- ATT: 10 + +-- Rabbit instinct II +INSERT INTO `monstrosity_instinct_mods` VALUES (4,3,3); -- HPP: 3 +INSERT INTO `monstrosity_instinct_mods` VALUES (4,9,5); -- DEX: 5 +INSERT INTO `monstrosity_instinct_mods` VALUES (4,68,10); -- EVA: 15 + +-- Rabbit instinct III +INSERT INTO `monstrosity_instinct_mods` VALUES (5,9,5); -- DEX: 5 +INSERT INTO `monstrosity_instinct_mods` VALUES (5,11,5); -- AGI: 5 +INSERT INTO `monstrosity_instinct_mods` VALUES (5,1,20); -- DEF: 20 +INSERT INTO `monstrosity_instinct_mods` VALUES (5,1,288); -- DOUBLE_ATTACK: 2 diff --git a/sql/monstrosity_instincts.sql b/sql/monstrosity_instincts.sql index e69de29bb2d..8f08a38a11d 100644 --- a/sql/monstrosity_instincts.sql +++ b/sql/monstrosity_instincts.sql @@ -0,0 +1,298 @@ +DROP TABLE IF EXISTS `monstrosity_instincts`; +CREATE TABLE `monstrosity_instincts` ( + `monstrosity_instinct_id` smallint(30) unsigned NOT NULL, + `cost` smallint(30) unsigned NOT NULL, + `name` varchar(60) DEFAULT NULL, + PRIMARY KEY (`monstrosity_instinct_id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; + +INSERT INTO `monstrosity_instincts` VALUES (3, 2, "Rabbit instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (4, 4, "Rabbit instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (5, 6, "Rabbit instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (6, 10, "Behemoth instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (7, 12, "Behemoth instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (8, 14, "Behemoth instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES ( 9, 4, "Tiger instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (10, 6, "Tiger instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (11, 8, "Tiger instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (12, 4, "Sheep instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (13, 6, "Sheep instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (14, 8, "Sheep instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (15, 4, "Ram instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (16, 6, "Ram instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (17, 8, "Ram instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (18, 4, "Dhalmel instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (19, 6, "Dhalmel instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (20, 8, "Dhalmel instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (21, 6, "Coeurl instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (22, 8, "Coeurl instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (23, 10, "Coeurl instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (24, 8, "Opo-opo instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (25, 10, "Opo-opo instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (26, 12, "Opo-opo instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (27, 6, "Manticore instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (28, 8, "Manticore instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (29, 10, "Manticore instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (30, 6, "Buffalo instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (31, 8, "Buffalo instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (32, 10, "Buffalo instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (33, 8, "Marid instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (34, 10, "Marid instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (35, 12, "Marid instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (36, 10, "Cerberus instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (37, 12, "Cerberus instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (38, 14, "Cerberus instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (39, 8, "Gnole instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (40, 10, "Gnole instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (41, 12, "Gnole instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (45, 4, "Funguar instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (46, 6, "Funguar instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (47, 8, "Funguar instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (48, 2, "Treant instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (49, 4, "Treant instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (50, 6, "Treant instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (51, 6, "Morbol instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (52, 8, "Morbol instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (53, 10, "Morbol instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (54, 2, "Mandragora instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (55, 4, "Mandragora instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (56, 6, "Mandragora instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (57, 4, "Sabotender instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (58, 6, "Sabotender instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (59, 8, "Sabotender instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (60, 6, "Flytrap instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (61, 8, "Flytrap instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (62, 10, "Flytrap instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (63, 4, "Goobbue instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (64, 6, "Goobbue instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (65, 8, "Goobbue instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (66, 8, "Rafflesia instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (67, 10, "Rafflesia instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (68, 12, "Rafflesia instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (69, 8, "Panopt instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (70, 10, "Panopt instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (71, 12, "Panopt instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (81, 2, "Bee instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (82, 4, "Bee instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (83, 6, "Bee instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (84, 2, "Beetle instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (85, 4, "Beetle instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (86, 6, "Beetle instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (87, 4, "Crawler instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (88, 6, "Crawler instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (89, 8, "Crawler instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (90, 4, "Fly instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (91, 6, "Fly instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (92, 8, "Fly instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (93, 4, "Scorpion instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (94, 6, "Scorpion instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (95, 8, "Scorpion instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (96, 6, "Spider instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (97, 8, "Spider instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (98, 10, "Spider instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES ( 99, 6, "Antlion instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (100, 8, "Antlion instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (101, 10, "Antlion instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (102, 8, "Diremite instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (103, 10, "Diremite instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (104, 12, "Diremite instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (105, 6, "Chigoe instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (106, 8, "Chigoe instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (107, 10, "Chigoe instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (108, 6, "Wamoura instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (109, 8, "Wamoura instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (110, 10, "Wamoura instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (111, 6, "Ladybug instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (112, 8, "Ladybug instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (113, 10, "Ladybug instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (114, 8, "Gnat instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (115, 10, "Gnat instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (116, 12, "Gnat instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (129, 2, "Lizard instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (130, 4, "Lizard instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (131, 6, "Lizard instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (132, 2, "Raptor instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (133, 4, "Raptor instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (134, 6, "Raptor instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (135, 8, "Adamantoise instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (136, 10, "Adamantoise instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (137, 12, "Adamantoise instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (138, 4, "Bugard instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (139, 6, "Bugard instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (140, 8, "Bugard instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (141, 4, "Eft instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (142, 6, "Eft instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (143, 8, "Eft instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (144, 6, "Wivre instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (145, 8, "Wivre instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (146, 10, "Wivre instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (147, 6, "Peiste instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (148, 8, "Peiste instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (149, 10, "Peiste instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (156, 4, "Slime instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (157, 6, "Slime instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (158, 8, "Slime instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (159, 4, "Hecteyes instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (160, 6, "Hecteyes instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (161, 8, "Hecteyes instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (162, 6, "Flan instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (163, 8, "Flan instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (164, 10, "Flan instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (168, 4, "Slug instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (169, 6, "Slug instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (170, 20, "Slug instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (171, 6, "Sandworm instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (172, 8, "Sandworm instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (173, 10, "Sandworm instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (174, 2, "Leech instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (175, 4, "Leech instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (176, 6, "Leech instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (180, 4, "Crab instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (181, 5, "Crab instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (182, 6, "Crab instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (183, 3, "Pugil instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (184, 4, "Pugil instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (185, 5, "Pugil instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (186, 5, "Sea Monk instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (187, 7, "Sea Monk instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (188, 9, "Sea Monk instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (189, 4, "Uragnite instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (190, 8, "Uragnite instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (191, 16, "Uragnite instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (192, 7, "Orobon instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (193, 8, "Orobon instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (194, 10, "Orobon instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (195, 8, "Ruszor instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (196, 10, "Ruszor instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (197, 15, "Ruszor instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (198, 2, "Toad instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (199, 5, "Toad instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (200, 30, "Toad instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (207, 3, "Bird instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (208, 5, "Bird instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (209, 7, "Bird instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (210, 2, "Cockatrice instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (211, 4, "Cockatrice instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (212, 6, "Cockatrice instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (213, 5, "Roc instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (214, 7, "Roc instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (215, 9, "Roc instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (216, 2, "Bat instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (217, 4, "Bat instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (218, 6, "Bat instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (219, 6, "Hippogryph instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (220, 8, "Hippogryph instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (221, 10, "Hippogryph instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (222, 8, "Apkallu instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (223, 10, "Apkallu instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (224, 12, "Apkallu instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (225, 6, "Colibri instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (226, 8, "Colibri instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (227, 10, "Colibri instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (228, 8, "Amphiptere instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (229, 10, "Amphiptere instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (230, 12, "Amphiptere instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (762, 1, "Astoltian slime instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (763, 2, "Astoltian slime instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (764, 3, "Astoltian slime instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (765, 1, "Eorzean spriggan instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (766, 2, "Eorzean spriggan instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (767, 3, "Eorzean spriggan instinct III"); + +INSERT INTO `monstrosity_instincts` VALUES (768, 3, "Hume's instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (769, 3, "Elvaan's instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (770, 3, "Tarutaru's instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (771, 3, "Mithra's instinct I"); +INSERT INTO `monstrosity_instincts` VALUES (772, 3, "Galka's instinct I"); + +INSERT INTO `monstrosity_instincts` VALUES (773, 3, "Hume's instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (774, 3, "Elvaan's instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (775, 3, "Tarutaru's instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (776, 3, "Mithra's instinct II"); +INSERT INTO `monstrosity_instincts` VALUES (777, 3, "Galka's instinct II"); + +INSERT INTO `monstrosity_instincts` VALUES (778, 15, "Warrior's instinct"); +INSERT INTO `monstrosity_instincts` VALUES (779, 15, "Monk's instinct"); +INSERT INTO `monstrosity_instincts` VALUES (780, 15, "White mage's instinct"); +INSERT INTO `monstrosity_instincts` VALUES (781, 15, "Black mage's instinct"); +INSERT INTO `monstrosity_instincts` VALUES (782, 15, "Red mage's instinct"); +INSERT INTO `monstrosity_instincts` VALUES (783, 15, "Thief's instinct"); +INSERT INTO `monstrosity_instincts` VALUES (784, 15, "Paladin's instinct"); +INSERT INTO `monstrosity_instincts` VALUES (785, 15, "Dark knight's instinct"); +INSERT INTO `monstrosity_instincts` VALUES (786, 15, "Beastmaster's instinct"); +INSERT INTO `monstrosity_instincts` VALUES (787, 15, "Bard's instinct"); +INSERT INTO `monstrosity_instincts` VALUES (788, 15, "Ranger's instinct"); +INSERT INTO `monstrosity_instincts` VALUES (789, 15, "Samurai's instinct"); +INSERT INTO `monstrosity_instincts` VALUES (790, 15, "Ninja's instinct"); +INSERT INTO `monstrosity_instincts` VALUES (791, 15, "Dragoon's instinct"); +INSERT INTO `monstrosity_instincts` VALUES (792, 15, "Summoner's instinct"); +INSERT INTO `monstrosity_instincts` VALUES (793, 15, "Blue mage's instinct"); +INSERT INTO `monstrosity_instincts` VALUES (794, 15, "Corsair's instinct"); +INSERT INTO `monstrosity_instincts` VALUES (795, 15, "Puppetmaster's instinct"); +INSERT INTO `monstrosity_instincts` VALUES (796, 15, "Dancer's instinct"); +INSERT INTO `monstrosity_instincts` VALUES (797, 15, "Scholar's instinct"); +INSERT INTO `monstrosity_instincts` VALUES (798, 15, "Geomancer's instinct"); +INSERT INTO `monstrosity_instincts` VALUES (799, 15, "Rune fencer's instinct"); From 9a0d5ef55f0d740d5bc88332ee49311c24a9fa39 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Wed, 4 Oct 2023 21:37:15 +0100 Subject: [PATCH 022/103] Add note about instinct points --- src/map/monstrosity.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/map/monstrosity.cpp b/src/map/monstrosity.cpp index 87ae11a0a07..8a47dd2fb7c 100644 --- a/src/map/monstrosity.cpp +++ b/src/map/monstrosity.cpp @@ -139,6 +139,7 @@ void monstrosity::HandleEquipChangePacket(CCharEntity* PChar, CBasicPacket& data // TODO: Validate that we have the species/instinct that we're trying to equip // TODO: Validate that we'll have enough points to hold our instincts when we equip + // NOTE: The amount of pointer per level is level + 10, this is set in the client uint8 flag = data.ref(0x0A); if (flag == 0x01) // Species Change From 8770daea9f17887d199893a3d1b02091dae181bc Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Wed, 4 Oct 2023 21:59:30 +0100 Subject: [PATCH 023/103] Remove NameBase --- .../zones/Feretory/npcs/Odyssean_Passage.lua | 3 ++- src/map/map.cpp | 3 +++ src/map/monstrosity.cpp | 22 +++++++++++++++---- src/map/monstrosity.h | 4 ++-- 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/scripts/zones/Feretory/npcs/Odyssean_Passage.lua b/scripts/zones/Feretory/npcs/Odyssean_Passage.lua index 8946f0e0e97..28492cbc012 100644 --- a/scripts/zones/Feretory/npcs/Odyssean_Passage.lua +++ b/scripts/zones/Feretory/npcs/Odyssean_Passage.lua @@ -19,7 +19,8 @@ end entity.onEventFinish = function(player, csid, option, npc) print('finish', csid, option) - -- Option 1: Leave? + -- Option 1: Leave & Teleport to last city zone + -- Option 529: Teleport to Al'Taieu end return entity diff --git a/src/map/map.cpp b/src/map/map.cpp index b3c8734d421..e44b7405668 100755 --- a/src/map/map.cpp +++ b/src/map/map.cpp @@ -43,6 +43,7 @@ along with this program. If not, see http://www.gnu.org/licenses/ #include "linkshell.h" #include "message.h" #include "mob_spell_list.h" +#include "monstrosity.h" #include "packet_guard.h" #include "packet_system.h" #include "roe.h" @@ -274,6 +275,8 @@ int32 do_init(int32 argc, char** argv) fishingutils::InitializeFishingSystem(); instanceutils::LoadInstanceList(); + monstrosity::LoadStaticData(); + ShowInfo("do_init: server is binding with port %u", map_port == 0 ? settings::get("network.MAP_PORT") : map_port); map_fd = makeBind_udp(INADDR_ANY, map_port == 0 ? settings::get("network.MAP_PORT") : map_port); diff --git a/src/map/monstrosity.cpp b/src/map/monstrosity.cpp index 8a47dd2fb7c..fed926b4d08 100644 --- a/src/map/monstrosity.cpp +++ b/src/map/monstrosity.cpp @@ -54,15 +54,32 @@ monstrosity::MonstrosityData_t::MonstrosityData_t() , Species(0x0001) , Flags(0x0B44) , Look(0x010C) -, NameBase(0x8001) , NamePrefix1(0x00) , NamePrefix2(0x00) { // TODO: Populate instinct and levels from db + + // To load: + // Currrent MonstrosityId + // Current Species + // Current SpeciesExp + // Current NamePrefix1 + // Current NamePrefix2 + // Current EquippedInstincts + // Current levels + // Current instincts + // Current variants + + // Can be inferred/looked up: + // Current Look + // Current Flags (?) // Maybe these should be in the static data, if its mob size flags + // Current SpeciesLevel (levels[MonstrosityId]) } void monstrosity::LoadStaticData() { + ShowInfo("Loading Monstrosity data"); + int32 ret = sql->Query("SELECT monstrosity_id, monstrosity_species_code, name, look FROM monstrosity_species;"); if (ret != SQL_ERROR && sql->NumRows() != 0) { @@ -82,9 +99,6 @@ void monstrosity::LoadStaticData() void monstrosity::HandleZoneIn(CCharEntity* PChar) { - // TODO: Move to map.cpp - LoadStaticData(); - // TODO: Check we're about to enter monstrosity, charvar, flag, etc. if (charutils::GetCharVar(PChar, "MONSTROSITY_START") == 1) { diff --git a/src/map/monstrosity.h b/src/map/monstrosity.h index db457b4826b..0cfa3902740 100644 --- a/src/map/monstrosity.h +++ b/src/map/monstrosity.h @@ -41,7 +41,6 @@ namespace monstrosity uint16 Flags; uint16 Look; - uint16 NameBase; uint8 NamePrefix1; uint8 NamePrefix2; @@ -51,7 +50,8 @@ namespace monstrosity std::array variants{ 0 }; }; - void LoadStaticData(); + void LoadStaticData(); + void HandleZoneIn(CCharEntity* PChar); uint32 GetPackedMonstrosityName(CCharEntity* PChar); void SendFullMonstrosityUpdate(CCharEntity* PChar); From bde14be1e84cde237ce98ae9bcb7f22bfc90334c Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Wed, 4 Oct 2023 22:25:26 +0100 Subject: [PATCH 024/103] Load more static data --- .../hiddenQuests/Monstrosity_Unlock.lua | 2 +- src/map/monstrosity.cpp | 48 +++++++++++++++++-- 2 files changed, 44 insertions(+), 6 deletions(-) diff --git a/scripts/quests/hiddenQuests/Monstrosity_Unlock.lua b/scripts/quests/hiddenQuests/Monstrosity_Unlock.lua index a23278b8c86..bac1f5294c6 100644 --- a/scripts/quests/hiddenQuests/Monstrosity_Unlock.lua +++ b/scripts/quests/hiddenQuests/Monstrosity_Unlock.lua @@ -13,7 +13,7 @@ quest.sections = -- Intro chatter { check = function(player, questVars, vars) - return quest:getVar(player, 'Prog') == 0 + return settings.main.ENABLE_MONSTROSITY == 1 and quest:getVar(player, 'Prog') == 0 end, [xi.zone.PASHHOW_MARSHLANDS] = diff --git a/src/map/monstrosity.cpp b/src/map/monstrosity.cpp index fed926b4d08..228719f9aff 100644 --- a/src/map/monstrosity.cpp +++ b/src/map/monstrosity.cpp @@ -44,9 +44,18 @@ struct MonstrositySpeciesRow uint16 look; }; +struct MonstrosityInstinctRow +{ + uint16 monstrosityInstinctId; + uint8 cost; + std::string name; + std::vector mods; +}; + namespace { - std::unordered_map gMonstrosityDataBySpeciesMap; + std::unordered_map gMonstrositySpeciesMap; + std::unordered_map gMonstrosityInstinctMap; } // namespace monstrosity::MonstrosityData_t::MonstrosityData_t() @@ -92,7 +101,36 @@ void monstrosity::LoadStaticData() row.name = sql->GetStringData(2); row.look = static_cast(sql->GetUIntData(3)); - gMonstrosityDataBySpeciesMap[row.monstrositySpeciesCode] = row; + gMonstrositySpeciesMap[row.monstrositySpeciesCode] = row; + } + } + + ret = sql->Query("SELECT monstrosity_instinct_id, cost, name FROM monstrosity_instincts;"); + if (ret != SQL_ERROR && sql->NumRows() != 0) + { + while (sql->NextRow() == SQL_SUCCESS) + { + MonstrosityInstinctRow row; + + row.monstrosityInstinctId = static_cast(sql->GetUIntData(0)); + row.cost = static_cast(sql->GetUIntData(1)); + row.name = sql->GetStringData(2); + + gMonstrosityInstinctMap[row.monstrosityInstinctId] = row; + } + } + + for (auto& [_, entry] : gMonstrosityInstinctMap) + { + ret = sql->Query("SELECT monstrosity_instinct_id, modId, value FROM monstrosity_instinct_mods WHERE monstrosity_instinct_id = %d;", entry.monstrosityInstinctId); + if (ret != SQL_ERROR && sql->NumRows() != 0) + { + while (sql->NextRow() == SQL_SUCCESS) + { + auto mod = static_cast(sql->GetUIntData(0)); + auto val = static_cast(sql->GetIntData(1)); + entry.mods.emplace_back(CModifier(mod, val)); + } } } } @@ -162,7 +200,7 @@ void monstrosity::HandleEquipChangePacket(CCharEntity* PChar, CBasicPacket& data auto newSpecies = data.ref(0x0C); - auto data = gMonstrosityDataBySpeciesMap[newSpecies]; + auto data = gMonstrositySpeciesMap[newSpecies]; // For debugging and data entry ShowInfo(fmt::format("Species: {}: {}", newSpecies, data.name)); @@ -219,7 +257,7 @@ void monstrosity::HandleEquipChangePacket(CCharEntity* PChar, CBasicPacket& data void monstrosity::MaxAllLevels(CCharEntity* PChar) { - for (auto const& [_, entry] : gMonstrosityDataBySpeciesMap) + for (auto const& [_, entry] : gMonstrositySpeciesMap) { PChar->m_PMonstrosity->levels[entry.monstrosityId] = 99; } @@ -228,7 +266,7 @@ void monstrosity::MaxAllLevels(CCharEntity* PChar) void monstrosity::UnlockAllInstincts(CCharEntity* PChar) { // Level based - for (auto const& [_, entry] : gMonstrosityDataBySpeciesMap) + for (auto const& [_, entry] : gMonstrositySpeciesMap) { uint8 level = 99; uint8 byteOffset = entry.monstrosityId / 4; From af63e80ac3e74d5a53642ced44def776730ae69d Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Wed, 4 Oct 2023 22:42:12 +0100 Subject: [PATCH 025/103] Add SetLevel --- src/map/monstrosity.cpp | 45 +++++++++++++++++++++++++++++++---------- src/map/monstrosity.h | 6 ++++-- 2 files changed, 38 insertions(+), 13 deletions(-) diff --git a/src/map/monstrosity.cpp b/src/map/monstrosity.cpp index 228719f9aff..73cf8ae1fb7 100644 --- a/src/map/monstrosity.cpp +++ b/src/map/monstrosity.cpp @@ -54,15 +54,15 @@ struct MonstrosityInstinctRow namespace { - std::unordered_map gMonstrositySpeciesMap; + std::unordered_map gMonstrositySpeciesMap; std::unordered_map gMonstrosityInstinctMap; } // namespace monstrosity::MonstrosityData_t::MonstrosityData_t() -: MonstrosityId(0x01) -, Species(0x0001) -, Flags(0x0B44) -, Look(0x010C) +: MonstrosityId(0x01) // Rabbit +, Species(0x0001) // Rabbit +, Flags(0x0B44) // ? +, Look(0x010C) // Rabbit , NamePrefix1(0x00) , NamePrefix2(0x00) { @@ -112,9 +112,9 @@ void monstrosity::LoadStaticData() { MonstrosityInstinctRow row; - row.monstrosityInstinctId = static_cast(sql->GetUIntData(0)); - row.cost = static_cast(sql->GetUIntData(1)); - row.name = sql->GetStringData(2); + row.monstrosityInstinctId = static_cast(sql->GetUIntData(0)); + row.cost = static_cast(sql->GetUIntData(1)); + row.name = sql->GetStringData(2); gMonstrosityInstinctMap[row.monstrosityInstinctId] = row; } @@ -203,7 +203,7 @@ void monstrosity::HandleEquipChangePacket(CCharEntity* PChar, CBasicPacket& data auto data = gMonstrositySpeciesMap[newSpecies]; // For debugging and data entry - ShowInfo(fmt::format("Species: {}: {}", newSpecies, data.name)); + // ShowInfo(fmt::format("Species: {}: {}", newSpecies, data.name)); PChar->m_PMonstrosity->Species = newSpecies; @@ -239,8 +239,24 @@ void monstrosity::HandleEquipChangePacket(CCharEntity* PChar, CBasicPacket& data uint16 value = data.ref(0x10 + (idx * 2)); if (value != 0) { - ShowInfo(fmt::format("{}: {}", idx, value)); - PChar->m_PMonstrosity->EquippedInstincts[idx] = value == 0xFFFF ? 0x0000 : value; + if (value == 0xFFFF) + { + // Remove + PChar->m_PMonstrosity->EquippedInstincts[idx] = 0x0000; + } + else + { + auto maybeInstinct = gMonstrosityInstinctMap.find(value); + if (maybeInstinct != gMonstrosityInstinctMap.end()) + { + auto instinct = (*maybeInstinct).second; + + // TODO: Check: + // instinct.cost + + PChar->m_PMonstrosity->EquippedInstincts[idx] = value; + } + } } } } @@ -255,6 +271,13 @@ void monstrosity::HandleEquipChangePacket(CCharEntity* PChar, CBasicPacket& data SendFullMonstrosityUpdate(PChar); } +void monstrosity::SetLevel(CCharEntity* PChar, uint8 id, uint8 level) +{ + // TODO: Validate id and level + // TODO: If not unlocked, unlock whatever id is + PChar->m_PMonstrosity->levels[id] = level; +} + void monstrosity::MaxAllLevels(CCharEntity* PChar) { for (auto const& [_, entry] : gMonstrositySpeciesMap) diff --git a/src/map/monstrosity.h b/src/map/monstrosity.h index 0cfa3902740..e986f04f8a9 100644 --- a/src/map/monstrosity.h +++ b/src/map/monstrosity.h @@ -41,8 +41,8 @@ namespace monstrosity uint16 Flags; uint16 Look; - uint8 NamePrefix1; - uint8 NamePrefix2; + uint8 NamePrefix1; + uint8 NamePrefix2; std::array EquippedInstincts{ 0 }; std::array levels{ 0 }; @@ -57,6 +57,8 @@ namespace monstrosity void SendFullMonstrosityUpdate(CCharEntity* PChar); void HandleEquipChangePacket(CCharEntity* PChar, CBasicPacket& data); + void SetLevel(CCharEntity* PChar, uint8 id, uint8 level); + // Debug void MaxAllLevels(CCharEntity* PChar); void UnlockAllInstincts(CCharEntity* PChar); From 7cb3d100e490279121abd2d8dfcd7905450e8aa9 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Thu, 5 Oct 2023 11:06:17 +0100 Subject: [PATCH 026/103] Read and write to db --- sql/char_monstrosity.sql | 14 +++ sql/char_unlocks.sql | 3 + sql/monstrosity_instinct_mods.sql | 4 +- src/common/sql.h | 10 ++ src/map/lua/lua_baseentity.cpp | 22 ++-- src/map/monstrosity.cpp | 184 +++++++++++++++++++++++++---- src/map/monstrosity.h | 5 + src/map/packets/char_job_extra.cpp | 4 +- 8 files changed, 206 insertions(+), 40 deletions(-) create mode 100644 sql/char_monstrosity.sql diff --git a/sql/char_monstrosity.sql b/sql/char_monstrosity.sql new file mode 100644 index 00000000000..e59c2e3fe2e --- /dev/null +++ b/sql/char_monstrosity.sql @@ -0,0 +1,14 @@ +DROP TABLE IF EXISTS `char_monstrosity`; +CREATE TABLE `char_monstrosity` ( + `charid` int(10) unsigned NOT NULL, + `current_monstrosity_id` smallint(3) unsigned NOT NULL DEFAULT 0, + `current_monstrosity_species` smallint(3) unsigned NOT NULL DEFAULT 0, + `current_monstrosity_name_prefix_1` smallint(3) unsigned NOT NULL DEFAULT 0, + `current_monstrosity_name_prefix_2` smallint(3) unsigned NOT NULL DEFAULT 0, + `current_exp` int(3) unsigned NOT NULL DEFAULT 0, + `equip` blob DEFAULT NULL, + `levels` blob DEFAULT NULL, + `instincts` blob DEFAULT NULL, + `variants` blob DEFAULT NULL, + PRIMARY KEY (`charid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; diff --git a/sql/char_unlocks.sql b/sql/char_unlocks.sql index 3c6e7c8aa98..b4b0b53ce51 100644 --- a/sql/char_unlocks.sql +++ b/sql/char_unlocks.sql @@ -20,5 +20,8 @@ CREATE TABLE `char_unlocks` ( `eschan_portals` blob DEFAULT NULL, `claimed_deeds` blob DEFAULT NULL, `unique_event` blob DEFAULT NULL, + `monstrosity_levels` blob DEFAULT NULL, + `monstrosity_instincts` blob DEFAULT NULL, + `monstrosity_variants` blob DEFAULT NULL, PRIMARY KEY (`charid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; diff --git a/sql/monstrosity_instinct_mods.sql b/sql/monstrosity_instinct_mods.sql index 545aaefc644..9ca74d8bfb4 100644 --- a/sql/monstrosity_instinct_mods.sql +++ b/sql/monstrosity_instinct_mods.sql @@ -17,7 +17,7 @@ INSERT INTO `monstrosity_instinct_mods` VALUES (4,9,5); -- DEX: 5 INSERT INTO `monstrosity_instinct_mods` VALUES (4,68,10); -- EVA: 15 -- Rabbit instinct III +INSERT INTO `monstrosity_instinct_mods` VALUES (5,1,20); -- DEF: 20 INSERT INTO `monstrosity_instinct_mods` VALUES (5,9,5); -- DEX: 5 INSERT INTO `monstrosity_instinct_mods` VALUES (5,11,5); -- AGI: 5 -INSERT INTO `monstrosity_instinct_mods` VALUES (5,1,20); -- DEF: 20 -INSERT INTO `monstrosity_instinct_mods` VALUES (5,1,288); -- DOUBLE_ATTACK: 2 +INSERT INTO `monstrosity_instinct_mods` VALUES (5,288,2); -- DOUBLE_ATTACK: 2 diff --git a/src/common/sql.h b/src/common/sql.h index a18ea533383..8f35554e05c 100644 --- a/src/common/sql.h +++ b/src/common/sql.h @@ -185,6 +185,16 @@ class SqlConnection std::string GetStringData(size_t col); + template + void GetBlobData(size_t col, T& destination) + { + // TTODO: Fix me + size_t length = 0; + char* buffer = nullptr; + GetData(col, &buffer, &length); + // std::memcpy((void*)destination, buffer, (length > sizeof(T) ? sizeof(T) : length)); + } + /// Frees the result of the query. void FreeResult(); diff --git a/src/map/lua/lua_baseentity.cpp b/src/map/lua/lua_baseentity.cpp index 21314a870c5..4b021f5db8b 100644 --- a/src/map/lua/lua_baseentity.cpp +++ b/src/map/lua/lua_baseentity.cpp @@ -6308,31 +6308,31 @@ void CLuaBaseEntity::addJobTraits(uint8 jobID, uint8 level) void CLuaBaseEntity::setMonstrosity(sol::table table) { - auto* PChar = static_cast(m_PBaseEntity); - if (PChar->m_PMonstrosity == nullptr) + auto* PChar = dynamic_cast(m_PBaseEntity); + if (PChar == nullptr || PChar->m_PMonstrosity == nullptr) { return; } for (auto const& [keyObj, valObj] : table.get("levels")) { - uint8 key = keyObj.as(); - uint8 val = valObj.as(); - PChar->m_PMonstrosity->levels[key] = val; + uint8 key = keyObj.as(); + uint8 val = valObj.as(); + PChar->m_PMonstrosity->levels[key] |= val; } for (auto const& [keyObj, valObj] : table.get("instincts")) { - uint8 key = keyObj.as(); - uint8 val = valObj.as(); - PChar->m_PMonstrosity->instincts[key] = val; + uint8 key = keyObj.as(); + uint8 val = valObj.as(); + PChar->m_PMonstrosity->instincts[key] |= val; } for (auto const& [keyObj, valObj] : table.get("variants")) { - uint8 key = keyObj.as(); - uint8 val = valObj.as(); - PChar->m_PMonstrosity->variants[key] = val; + uint8 key = keyObj.as(); + uint8 val = valObj.as(); + PChar->m_PMonstrosity->variants[key] |= val; } monstrosity::SendFullMonstrosityUpdate(PChar); diff --git a/src/map/monstrosity.cpp b/src/map/monstrosity.cpp index 73cf8ae1fb7..e17c1afd8ef 100644 --- a/src/map/monstrosity.cpp +++ b/src/map/monstrosity.cpp @@ -63,26 +63,10 @@ monstrosity::MonstrosityData_t::MonstrosityData_t() , Species(0x0001) // Rabbit , Flags(0x0B44) // ? , Look(0x010C) // Rabbit -, NamePrefix1(0x00) -, NamePrefix2(0x00) +, NamePrefix1(0x00) // Nothing +, NamePrefix2(0x00) // Nothing +, CurrentExp(0) // No exp { - // TODO: Populate instinct and levels from db - - // To load: - // Currrent MonstrosityId - // Current Species - // Current SpeciesExp - // Current NamePrefix1 - // Current NamePrefix2 - // Current EquippedInstincts - // Current levels - // Current instincts - // Current variants - - // Can be inferred/looked up: - // Current Look - // Current Flags (?) // Maybe these should be in the static data, if its mob size flags - // Current SpeciesLevel (levels[MonstrosityId]) } void monstrosity::LoadStaticData() @@ -135,22 +119,146 @@ void monstrosity::LoadStaticData() } } +void monstrosity::ReadMonstrosityData(CCharEntity* PChar) +{ + auto data = std::make_unique(); + + auto ret = sql->Query("SELECT charid, current_monstrosity_id, current_monstrosity_species, current_monstrosity_name_prefix_1, current_monstrosity_name_prefix_2, current_exp, equip, levels, instincts, variants FROM char_monstrosity WHERE charid = %d LIMIT 1;", PChar->id); + if (ret != SQL_ERROR && sql->NumRows() != 0) + { + while (sql->NextRow() == SQL_SUCCESS) + { + // charid: 0 + data->MonstrosityId = static_cast(sql->GetUIntData(1)); + data->Species = static_cast(sql->GetUIntData(2)); + data->Look = gMonstrositySpeciesMap[data->Species].look; + + data->NamePrefix1 = static_cast(sql->GetUIntData(3)); + data->NamePrefix2 = static_cast(sql->GetUIntData(4)); + data->CurrentExp = static_cast(sql->GetUIntData(5)); + + // TODO: Make these a template in sql.h + // equip + { + size_t length = 0; + char* buffer = nullptr; + sql->GetData(6, &buffer, &length); + std::memcpy(&data->EquippedInstincts[0], buffer, (length > sizeof(data->EquippedInstincts) ? sizeof(data->EquippedInstincts) : length)); + } + + // levels + { + size_t length = 0; + char* buffer = nullptr; + sql->GetData(7, &buffer, &length); + std::memcpy(&data->levels[0], buffer, (length > sizeof(data->levels) ? sizeof(data->levels) : length)); + } + + // instincts + { + size_t length = 0; + char* buffer = nullptr; + sql->GetData(8, &buffer, &length); + std::memcpy(&data->instincts[0], buffer, (length > sizeof(data->instincts) ? sizeof(data->instincts) : length)); + } + + // variants + { + size_t length = 0; + char* buffer = nullptr; + sql->GetData(9, &buffer, &length); + std::memcpy(&data->variants[0], buffer, (length > sizeof(data->variants) ? sizeof(data->variants) : length)); + } + + // TODO: + auto level = data->levels[data->MonstrosityId]; + std::ignore = level; + } + } + + PChar->m_PMonstrosity = std::move(data); +} + +void monstrosity::WriteMonstrosityData(CCharEntity* PChar) +{ + if (PChar->m_PMonstrosity == nullptr) + { + return; + } + + const char* Query = "UPDATE char_monstrosity SET " + "current_monstrosity_id = '%d', " + "current_monstrosity_species = '%d', " + "current_monstrosity_name_prefix_1 = '%d', " + "current_monstrosity_name_prefix_2 = '%d', " + "current_exp = '%d', " + "equip = '%s', " + "levels = '%s', " + "instincts = '%s', " + "variants = '%s'" + "WHERE charid = %u;"; + + // TODO: Make these a template in sql.h + char equipEscaped[sizeof(PChar->m_PMonstrosity->EquippedInstincts) * 2 + 1]; + { + char dataBlob[sizeof(PChar->m_PMonstrosity->EquippedInstincts)]; + std::memcpy(dataBlob, &PChar->m_PMonstrosity->EquippedInstincts[0], sizeof(dataBlob)); + sql->EscapeStringLen(equipEscaped, dataBlob, sizeof(dataBlob)); + } + + char levelsEscaped[sizeof(PChar->m_PMonstrosity->levels) * 2 + 1]; + { + char dataBlob[sizeof(PChar->m_PMonstrosity->levels)]; + std::memcpy(dataBlob, &PChar->m_PMonstrosity->levels[0], sizeof(dataBlob)); + sql->EscapeStringLen(levelsEscaped, dataBlob, sizeof(dataBlob)); + } + + char instinctsEscaped[sizeof(PChar->m_PMonstrosity->instincts) * 2 + 1]; + { + char dataBlob[sizeof(PChar->m_PMonstrosity->instincts)]; + std::memcpy(dataBlob, &PChar->m_PMonstrosity->instincts[0], sizeof(dataBlob)); + sql->EscapeStringLen(instinctsEscaped, dataBlob, sizeof(dataBlob)); + } + + char variantsEscaped[sizeof(PChar->m_PMonstrosity->variants) * 2 + 1]; + { + char dataBlob[sizeof(PChar->m_PMonstrosity->variants)]; + std::memcpy(dataBlob, &PChar->m_PMonstrosity->variants[0], sizeof(dataBlob)); + sql->EscapeStringLen(variantsEscaped, dataBlob, sizeof(dataBlob)); + } + + sql->Query(Query, + PChar->m_PMonstrosity->MonstrosityId, + PChar->m_PMonstrosity->Species, + PChar->m_PMonstrosity->NamePrefix1, + PChar->m_PMonstrosity->NamePrefix2, + PChar->m_PMonstrosity->CurrentExp, + equipEscaped, + levelsEscaped, + instinctsEscaped, + variantsEscaped, + PChar->id); +} + void monstrosity::HandleZoneIn(CCharEntity* PChar) { // TODO: Check we're about to enter monstrosity, charvar, flag, etc. if (charutils::GetCharVar(PChar, "MONSTROSITY_START") == 1) { - PChar->m_PMonstrosity = std::make_unique(); - PChar->updatemask |= UPDATE_LOOK; + // Populates PChar->m_PMonstrosity + ReadMonstrosityData(PChar); - MaxAllLevels(PChar); - UnlockAllInstincts(PChar); - UnlockAllVariants(PChar); + PChar->updatemask |= UPDATE_LOOK; } } uint32 monstrosity::GetPackedMonstrosityName(CCharEntity* PChar) { + if (PChar->m_PMonstrosity == nullptr) + { + return 0x00000000; + } + uint16 a = 0x8000 | PChar->m_PMonstrosity->Species; uint8 b = PChar->m_PMonstrosity->NamePrefix1; uint8 c = PChar->m_PMonstrosity->NamePrefix2; @@ -220,6 +328,11 @@ void monstrosity::HandleEquipChangePacket(CCharEntity* PChar, CBasicPacket& data } else if (flag == 0x04) // Instinct Change { + // TODO: + // NOTE: This is set by the client + auto maxPoints = PChar->m_PMonstrosity->levels[PChar->m_PMonstrosity->MonstrosityId] + 10; + std::ignore = maxPoints; + // Remove All if (data.ref(0x16) == 0xFFFF) { @@ -267,12 +380,18 @@ void monstrosity::HandleEquipChangePacket(CCharEntity* PChar, CBasicPacket& data PChar->m_PMonstrosity->NamePrefix2 = data.ref(0x29); } + WriteMonstrosityData(PChar); + // TODO: Is this too much traffic? SendFullMonstrosityUpdate(PChar); } void monstrosity::SetLevel(CCharEntity* PChar, uint8 id, uint8 level) { + if (PChar->m_PMonstrosity == nullptr) + { + return; + } // TODO: Validate id and level // TODO: If not unlocked, unlock whatever id is PChar->m_PMonstrosity->levels[id] = level; @@ -280,14 +399,24 @@ void monstrosity::SetLevel(CCharEntity* PChar, uint8 id, uint8 level) void monstrosity::MaxAllLevels(CCharEntity* PChar) { + if (PChar->m_PMonstrosity == nullptr) + { + return; + } + for (auto const& [_, entry] : gMonstrositySpeciesMap) { - PChar->m_PMonstrosity->levels[entry.monstrosityId] = 99; + SetLevel(PChar, entry.monstrosityId, 99); } } void monstrosity::UnlockAllInstincts(CCharEntity* PChar) { + if (PChar->m_PMonstrosity == nullptr) + { + return; + } + // Level based for (auto const& [_, entry] : gMonstrositySpeciesMap) { @@ -333,6 +462,11 @@ void monstrosity::UnlockAllInstincts(CCharEntity* PChar) void monstrosity::UnlockAllVariants(CCharEntity* PChar) { + if (PChar->m_PMonstrosity == nullptr) + { + return; + } + for (std::size_t idx = 0; idx < 256; ++idx) { uint8 byteOffset = static_cast(idx) / 8; diff --git a/src/map/monstrosity.h b/src/map/monstrosity.h index e986f04f8a9..476df7c8a6a 100644 --- a/src/map/monstrosity.h +++ b/src/map/monstrosity.h @@ -44,6 +44,8 @@ namespace monstrosity uint8 NamePrefix1; uint8 NamePrefix2; + uint32 CurrentExp; + std::array EquippedInstincts{ 0 }; std::array levels{ 0 }; std::array instincts{ 0 }; @@ -52,6 +54,9 @@ namespace monstrosity void LoadStaticData(); + void ReadMonstrosityData(CCharEntity* PChar); + void WriteMonstrosityData(CCharEntity* PChar); + void HandleZoneIn(CCharEntity* PChar); uint32 GetPackedMonstrosityName(CCharEntity* PChar); void SendFullMonstrosityUpdate(CCharEntity* PChar); diff --git a/src/map/packets/char_job_extra.cpp b/src/map/packets/char_job_extra.cpp index 5b40c4df3fb..abdd703b63d 100644 --- a/src/map/packets/char_job_extra.cpp +++ b/src/map/packets/char_job_extra.cpp @@ -52,7 +52,7 @@ CCharJobExtraPacket::CCharJobExtraPacket(CCharEntity* PChar, bool mjob) job = PChar->GetSJob(); } - if (PChar->loc.zone->GetID() == ZONE_FERETORY && PChar->m_PMonstrosity != nullptr) + if (PChar->m_PMonstrosity != nullptr) { job = JOB_MON; } @@ -138,7 +138,7 @@ CCharJobExtraPacket::CCharJobExtraPacket(CCharEntity* PChar, bool mjob) ref(0x9C) = PChar->getMod(Mod::AUTO_ELEM_CAPACITY); } - else if (job == JOB_MON && PChar->loc.zone->GetID() == ZONE_FERETORY && PChar->m_PMonstrosity != nullptr) + else if (job == JOB_MON && PChar->m_PMonstrosity != nullptr) { ref(0x08) = PChar->m_PMonstrosity->Species; From 1bbd9ed51b2cf700a6fa0ba3d828bd6aec1a7716 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Thu, 5 Oct 2023 14:53:14 +0100 Subject: [PATCH 027/103] Improve setMonstrosity binding --- scripts/globals/monstrosity.lua | 31 +++++++++++ .../hiddenQuests/Monstrosity_Unlock.lua | 6 ++- scripts/zones/Feretory/npcs/Maccus.lua | 2 + .../zones/Feretory/npcs/Odyssean_Passage.lua | 2 + scripts/zones/Feretory/npcs/Suibhne.lua | 2 + scripts/zones/Feretory/npcs/Teyrnon.lua | 2 + src/map/lua/lua_baseentity.cpp | 54 ++++++++++++++----- src/map/monstrosity.cpp | 26 +++++++++ src/map/monstrosity.h | 4 +- src/map/packet_system.cpp | 7 +++ 10 files changed, 121 insertions(+), 15 deletions(-) diff --git a/scripts/globals/monstrosity.lua b/scripts/globals/monstrosity.lua index cbb17150e40..15e3e7b8333 100644 --- a/scripts/globals/monstrosity.lua +++ b/scripts/globals/monstrosity.lua @@ -7,6 +7,37 @@ require('scripts/globals/quests') xi = xi or {} xi.monstrosity = xi.monstrosity or {} +----------------------------------- +-- Enums +----------------------------------- + +xi.monstrosity.species = +{ + RABBIT = 1, + MANDRAGORA = 18, + LIZARD = 43, +} + +----------------------------------- +-- Helpers +----------------------------------- + +xi.monstrosity.unlockStartingMONs = function(player, choice) + local data = + { + monstrosityId = choice, + species = choice, + levels = + { + [xi.monstrosity.species.RABBIT] = 1, + [xi.monstrosity.species.MANDRAGORA] = 1, + [xi.monstrosity.species.LIZARD] = 1, + } + } + + player:setMonstrosity(data) +end + ----------------------------------- -- Odyssean Passage ----------------------------------- diff --git a/scripts/quests/hiddenQuests/Monstrosity_Unlock.lua b/scripts/quests/hiddenQuests/Monstrosity_Unlock.lua index bac1f5294c6..e23143c4a22 100644 --- a/scripts/quests/hiddenQuests/Monstrosity_Unlock.lua +++ b/scripts/quests/hiddenQuests/Monstrosity_Unlock.lua @@ -13,7 +13,7 @@ quest.sections = -- Intro chatter { check = function(player, questVars, vars) - return settings.main.ENABLE_MONSTROSITY == 1 and quest:getVar(player, 'Prog') == 0 + return xi.settings.main.ENABLE_MONSTROSITY == 1 and quest:getVar(player, 'Prog') == 0 end, [xi.zone.PASHHOW_MARSHLANDS] = @@ -99,6 +99,10 @@ quest.sections = }, }, }, + + -- On complete: + -- xi.monstrosity.unlockStartingMONs(player, xi.monstrosity.species.MANDRAGORA) + -- Then zone to Feretory } return quest diff --git a/scripts/zones/Feretory/npcs/Maccus.lua b/scripts/zones/Feretory/npcs/Maccus.lua index d2a623676fd..b34eb6c90ae 100644 --- a/scripts/zones/Feretory/npcs/Maccus.lua +++ b/scripts/zones/Feretory/npcs/Maccus.lua @@ -3,6 +3,8 @@ -- NPC: Maccus -- !pos TODO ----------------------------------- +require('scripts/globals/monstrosity') +----------------------------------- local entity = {} entity.onTrade = function(player, npc, trade) diff --git a/scripts/zones/Feretory/npcs/Odyssean_Passage.lua b/scripts/zones/Feretory/npcs/Odyssean_Passage.lua index 28492cbc012..f3255080970 100644 --- a/scripts/zones/Feretory/npcs/Odyssean_Passage.lua +++ b/scripts/zones/Feretory/npcs/Odyssean_Passage.lua @@ -3,6 +3,8 @@ -- NPC: Odyssean Passage -- !pos TODO ----------------------------------- +require('scripts/globals/monstrosity') +----------------------------------- local entity = {} entity.onTrade = function(player, npc, trade) diff --git a/scripts/zones/Feretory/npcs/Suibhne.lua b/scripts/zones/Feretory/npcs/Suibhne.lua index d6ce9d48689..b8ba13c936d 100644 --- a/scripts/zones/Feretory/npcs/Suibhne.lua +++ b/scripts/zones/Feretory/npcs/Suibhne.lua @@ -3,6 +3,8 @@ -- NPC: Suibhne -- !pos TODO ----------------------------------- +require('scripts/globals/monstrosity') +----------------------------------- local entity = {} entity.onTrade = function(player, npc, trade) diff --git a/scripts/zones/Feretory/npcs/Teyrnon.lua b/scripts/zones/Feretory/npcs/Teyrnon.lua index 92f8e6d2408..558fd2c07f0 100644 --- a/scripts/zones/Feretory/npcs/Teyrnon.lua +++ b/scripts/zones/Feretory/npcs/Teyrnon.lua @@ -3,6 +3,8 @@ -- NPC: Teyrnon -- !pos TODO ----------------------------------- +require('scripts/globals/monstrosity') +----------------------------------- local entity = {} entity.onTrade = function(player, npc, trade) diff --git a/src/map/lua/lua_baseentity.cpp b/src/map/lua/lua_baseentity.cpp index 4b021f5db8b..91bc44fedfa 100644 --- a/src/map/lua/lua_baseentity.cpp +++ b/src/map/lua/lua_baseentity.cpp @@ -6309,32 +6309,60 @@ void CLuaBaseEntity::addJobTraits(uint8 jobID, uint8 level) void CLuaBaseEntity::setMonstrosity(sol::table table) { auto* PChar = dynamic_cast(m_PBaseEntity); - if (PChar == nullptr || PChar->m_PMonstrosity == nullptr) + if (PChar == nullptr) { return; } - for (auto const& [keyObj, valObj] : table.get("levels")) + // NOTE: This will populate m_PMonstrosity if it doesn't exist + monstrosity::ReadMonstrosityData(PChar); + + if (table["monstrosityId"].valid()) + { + PChar->m_PMonstrosity->MonstrosityId = table.get("monstrosityId"); + } + + if (table["species"].valid()) + { + PChar->m_PMonstrosity->Species = table.get("species"); + } + + if (table["flags"].valid()) + { + PChar->m_PMonstrosity->Flags = table.get("flags"); + } + + if (table["levels"].valid()) { - uint8 key = keyObj.as(); - uint8 val = valObj.as(); - PChar->m_PMonstrosity->levels[key] |= val; + for (auto const& [keyObj, valObj] : table.get("levels")) + { + uint8 key = keyObj.as(); + uint8 val = valObj.as(); + PChar->m_PMonstrosity->levels[key] = val; + } } - for (auto const& [keyObj, valObj] : table.get("instincts")) + if (table["instincts"].valid()) { - uint8 key = keyObj.as(); - uint8 val = valObj.as(); - PChar->m_PMonstrosity->instincts[key] |= val; + for (auto const& [keyObj, valObj] : table.get("instincts")) + { + uint8 key = keyObj.as(); + uint8 val = valObj.as(); + PChar->m_PMonstrosity->instincts[key] |= val; + } } - for (auto const& [keyObj, valObj] : table.get("variants")) + if (table["variants"].valid()) { - uint8 key = keyObj.as(); - uint8 val = valObj.as(); - PChar->m_PMonstrosity->variants[key] |= val; + for (auto const& [keyObj, valObj] : table.get("variants")) + { + uint8 key = keyObj.as(); + uint8 val = valObj.as(); + PChar->m_PMonstrosity->variants[key] |= val; + } } + monstrosity::WriteMonstrosityData(PChar); monstrosity::SendFullMonstrosityUpdate(PChar); } diff --git a/src/map/monstrosity.cpp b/src/map/monstrosity.cpp index e17c1afd8ef..7bdb6514f72 100644 --- a/src/map/monstrosity.cpp +++ b/src/map/monstrosity.cpp @@ -186,6 +186,21 @@ void monstrosity::WriteMonstrosityData(CCharEntity* PChar) return; } + // TODO: Ensure there's a row to write to + { + bool needToCreate = false; + int32 ret = sql->Query("SELECT * FROM char_monstrosity WHERE charid = %d;", PChar->id); + if (ret != SQL_ERROR && sql->NumRows() == 0) + { + needToCreate = true; + } + + if (needToCreate) + { + sql->Query("INSERT INTO char_monstrosity VALUES (%d, 1, 1, 0, 0, 0, 0, 0, 0, 0);;", PChar->id); + } + } + const char* Query = "UPDATE char_monstrosity SET " "current_monstrosity_id = '%d', " "current_monstrosity_species = '%d', " @@ -248,6 +263,9 @@ void monstrosity::HandleZoneIn(CCharEntity* PChar) // Populates PChar->m_PMonstrosity ReadMonstrosityData(PChar); + // This handles !monstrosity GM command, is this needed? + WriteMonstrosityData(PChar); + PChar->updatemask |= UPDATE_LOOK; } } @@ -274,6 +292,9 @@ void monstrosity::SendFullMonstrosityUpdate(CCharEntity* PChar) return; } + // Make sure look is up to date before we send packets + PChar->m_PMonstrosity->Look = gMonstrositySpeciesMap[PChar->m_PMonstrosity->Species].look; + // TODO: Safety checks: // : The species box on the UI should never be empty - everything breaks if that happens. // : We should detect a bad state and fall back to being a Lv1 Bunny if that happens. @@ -289,6 +310,11 @@ void monstrosity::SendFullMonstrosityUpdate(CCharEntity* PChar) PChar->updatemask |= UPDATE_LOOK; } +void monstrosity::HandleMonsterSkillActionPacket(CCharEntity* PChar, CBasicPacket& data) +{ + // TODO: +} + void monstrosity::HandleEquipChangePacket(CCharEntity* PChar, CBasicPacket& data) { if (PChar->loc.zone->GetID() != ZONE_FERETORY || PChar->m_PMonstrosity == nullptr) diff --git a/src/map/monstrosity.h b/src/map/monstrosity.h index 476df7c8a6a..7fda732279d 100644 --- a/src/map/monstrosity.h +++ b/src/map/monstrosity.h @@ -60,7 +60,9 @@ namespace monstrosity void HandleZoneIn(CCharEntity* PChar); uint32 GetPackedMonstrosityName(CCharEntity* PChar); void SendFullMonstrosityUpdate(CCharEntity* PChar); - void HandleEquipChangePacket(CCharEntity* PChar, CBasicPacket& data); + + void HandleMonsterSkillActionPacket(CCharEntity* PChar, CBasicPacket& data); + void HandleEquipChangePacket(CCharEntity* PChar, CBasicPacket& data); void SetLevel(CCharEntity* PChar, uint8 id, uint8 level); diff --git a/src/map/packet_system.cpp b/src/map/packet_system.cpp index 28397cc3ded..e1ea8dd90d4 100644 --- a/src/map/packet_system.cpp +++ b/src/map/packet_system.cpp @@ -849,6 +849,8 @@ void SmallPacket0x01A(map_session_data_t* const PSession, CCharEntity* const PCh return "Ballista - Scout"; case 0x18: return "Blockaid"; + case 0x19: + return "Monstrosity Monster Skill"; case 0x1A: return "Mounts"; default: @@ -1175,6 +1177,11 @@ void SmallPacket0x01A(map_session_data_t* const PSession, CCharEntity* const PCh } } break; + case 0x19: // Monstrosity Monster Skill + { + monstrosity::HandleMonsterSkillActionPacket(PChar, data); + } + break; case 0x1A: // mounts { uint8 MountID = data.ref(0x0C); From 75e60d074ef15e18abf5bfa9896753d262fc2803 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Thu, 5 Oct 2023 18:24:48 +0100 Subject: [PATCH 028/103] Mods from instincts --- scripts/globals/monstrosity.lua | 4 ++++ sql/monstrosity_instinct_mods.sql | 25 +++++++++++++++++++++++ src/map/monstrosity.cpp | 33 +++++++++++++++++++++++++++++-- src/map/packets/monipulator2.cpp | 1 - 4 files changed, 60 insertions(+), 3 deletions(-) diff --git a/scripts/globals/monstrosity.lua b/scripts/globals/monstrosity.lua index 15e3e7b8333..ddebf07f659 100644 --- a/scripts/globals/monstrosity.lua +++ b/scripts/globals/monstrosity.lua @@ -32,6 +32,10 @@ xi.monstrosity.unlockStartingMONs = function(player, choice) [xi.monstrosity.species.RABBIT] = 1, [xi.monstrosity.species.MANDRAGORA] = 1, [xi.monstrosity.species.LIZARD] = 1, + }, + instincts = + { + [20] = 0x1F, -- Unlock all first tier player race instincts } } diff --git a/sql/monstrosity_instinct_mods.sql b/sql/monstrosity_instinct_mods.sql index 9ca74d8bfb4..27bf9f2b7b5 100644 --- a/sql/monstrosity_instinct_mods.sql +++ b/sql/monstrosity_instinct_mods.sql @@ -21,3 +21,28 @@ INSERT INTO `monstrosity_instinct_mods` VALUES (5,1,20); -- DEF: 20 INSERT INTO `monstrosity_instinct_mods` VALUES (5,9,5); -- DEX: 5 INSERT INTO `monstrosity_instinct_mods` VALUES (5,11,5); -- AGI: 5 INSERT INTO `monstrosity_instinct_mods` VALUES (5,288,2); -- DOUBLE_ATTACK: 2 + +-- Hume's instinct I +INSERT INTO `monstrosity_instinct_mods` VALUES (768,8,2); -- STR: 2 +INSERT INTO `monstrosity_instinct_mods` VALUES (768,9,2); -- DEX: 2 +INSERT INTO `monstrosity_instinct_mods` VALUES (768,10,2); -- VIT: 2 +INSERT INTO `monstrosity_instinct_mods` VALUES (768,11,2); -- AGI: 2 +INSERT INTO `monstrosity_instinct_mods` VALUES (768,12,2); -- INT: 2 +INSERT INTO `monstrosity_instinct_mods` VALUES (768,13,2); -- MND: 2 +INSERT INTO `monstrosity_instinct_mods` VALUES (768,14,2); -- CHR: 2 + +-- Elvaan's instinct I +INSERT INTO `monstrosity_instinct_mods` VALUES (769,8,3); -- STR: 3 +INSERT INTO `monstrosity_instinct_mods` VALUES (769,13,3); -- MND: 3 + +-- Tarutaru's instinct I +INSERT INTO `monstrosity_instinct_mods` VALUES (770,6,2); -- MPP: 3 +INSERT INTO `monstrosity_instinct_mods` VALUES (770,12,3); -- INT: 3 + +-- Mithra's instinct I +INSERT INTO `monstrosity_instinct_mods` VALUES (771,9,3); -- DEX: 3 +INSERT INTO `monstrosity_instinct_mods` VALUES (771,11,3); -- AGI: 3 + +-- Galka's instinct I +INSERT INTO `monstrosity_instinct_mods` VALUES (772,3,2); -- HPP: 2 +INSERT INTO `monstrosity_instinct_mods` VALUES (772,10,3); -- VIT: 3 diff --git a/src/map/monstrosity.cpp b/src/map/monstrosity.cpp index 7bdb6514f72..960a75c4e92 100644 --- a/src/map/monstrosity.cpp +++ b/src/map/monstrosity.cpp @@ -29,6 +29,7 @@ #include "packets/char_appearance.h" #include "packets/char_job_extra.h" #include "packets/char_jobs.h" +#include "packets/char_stats.h" #include "packets/monipulator1.h" #include "packets/monipulator2.h" @@ -111,8 +112,9 @@ void monstrosity::LoadStaticData() { while (sql->NextRow() == SQL_SUCCESS) { - auto mod = static_cast(sql->GetUIntData(0)); - auto val = static_cast(sql->GetIntData(1)); + std::ignore = static_cast(sql->GetUIntData(0)); // id + auto mod = static_cast(sql->GetUIntData(1)); + auto val = static_cast(sql->GetIntData(2)); entry.mods.emplace_back(CModifier(mod, val)); } } @@ -266,6 +268,20 @@ void monstrosity::HandleZoneIn(CCharEntity* PChar) // This handles !monstrosity GM command, is this needed? WriteMonstrosityData(PChar); + // Add stats from equipped instincts + for (auto instinctId : PChar->m_PMonstrosity->EquippedInstincts) + { + auto maybeInstinct = gMonstrosityInstinctMap.find(instinctId); + if (maybeInstinct != gMonstrosityInstinctMap.end()) + { + auto instinct = (*maybeInstinct).second; + for (auto const& mod : instinct.mods) + { + PChar->addModifier(mod.getModID(), mod.getModAmount()); + } + } + } + PChar->updatemask |= UPDATE_LOOK; } } @@ -307,6 +323,7 @@ void monstrosity::SendFullMonstrosityUpdate(CCharEntity* PChar) PChar->pushPacket(new CCharJobExtraPacket(PChar, true)); PChar->pushPacket(new CCharJobExtraPacket(PChar, false)); PChar->pushPacket(new CCharAppearancePacket(PChar)); + PChar->pushPacket(new CCharStatsPacket(PChar)); PChar->updatemask |= UPDATE_LOOK; } @@ -354,6 +371,8 @@ void monstrosity::HandleEquipChangePacket(CCharEntity* PChar, CBasicPacket& data } else if (flag == 0x04) // Instinct Change { + auto previousEquipped = PChar->m_PMonstrosity->EquippedInstincts; + // TODO: // NOTE: This is set by the client auto maxPoints = PChar->m_PMonstrosity->levels[PChar->m_PMonstrosity->MonstrosityId] + 10; @@ -382,6 +401,11 @@ void monstrosity::HandleEquipChangePacket(CCharEntity* PChar, CBasicPacket& data { // Remove PChar->m_PMonstrosity->EquippedInstincts[idx] = 0x0000; + + for (auto const& mod : gMonstrosityInstinctMap[previousEquipped[idx]].mods) + { + PChar->delModifier(mod.getModID(), mod.getModAmount()); + } } else { @@ -394,6 +418,11 @@ void monstrosity::HandleEquipChangePacket(CCharEntity* PChar, CBasicPacket& data // instinct.cost PChar->m_PMonstrosity->EquippedInstincts[idx] = value; + + for (auto const& mod : instinct.mods) + { + PChar->addModifier(mod.getModID(), mod.getModAmount()); + } } } } diff --git a/src/map/packets/monipulator2.cpp b/src/map/packets/monipulator2.cpp index 5c51b840223..26028f5d291 100644 --- a/src/map/packets/monipulator2.cpp +++ b/src/map/packets/monipulator2.cpp @@ -48,7 +48,6 @@ CMonipulatorPacket2::CMonipulatorPacket2(CCharEntity* PChar) // Spriggan Level ref(0x87) = PChar->m_PMonstrosity->levels[127]; - // Bitpacked 2-bit values. 0 = no instincts from that species, 1 == first instinct, 2 == first and second instinct, 3 == first, second, and third instinct. // Contains job/race instincts from the 0x03 set. Has 8 unused bytes. This is a 1:1 mapping. // Since this has 8 unused bytes, we're only going to use 4 from instincts[20:23] std::memcpy(data + 0x88, PChar->m_PMonstrosity->instincts.data() + 20, 4); // Instinct Bitfield 3 From e9251e9baae31d20f310d60b498bbb6168663244 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Thu, 5 Oct 2023 18:43:46 +0100 Subject: [PATCH 029/103] Return missing OR symbol --- src/map/lua/lua_baseentity.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/map/lua/lua_baseentity.cpp b/src/map/lua/lua_baseentity.cpp index 91bc44fedfa..90f711d9d79 100644 --- a/src/map/lua/lua_baseentity.cpp +++ b/src/map/lua/lua_baseentity.cpp @@ -6338,7 +6338,7 @@ void CLuaBaseEntity::setMonstrosity(sol::table table) { uint8 key = keyObj.as(); uint8 val = valObj.as(); - PChar->m_PMonstrosity->levels[key] = val; + PChar->m_PMonstrosity->levels[key] |= val; } } From cb5ef7034246b6c501d87950dee66add05761875 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Fri, 6 Oct 2023 10:18:34 +0100 Subject: [PATCH 030/103] Move NPC logic into global --- scripts/globals/monstrosity.lua | 105 +++++++++++++++++- scripts/zones/Feretory/npcs/Aengus.lua | 10 +- scripts/zones/Feretory/npcs/Maccus.lua | 7 +- .../zones/Feretory/npcs/Odyssean_Passage.lua | 10 +- scripts/zones/Feretory/npcs/Suibhne.lua | 17 +-- scripts/zones/Feretory/npcs/Teyrnon.lua | 20 +--- 6 files changed, 121 insertions(+), 48 deletions(-) diff --git a/scripts/globals/monstrosity.lua b/scripts/globals/monstrosity.lua index ddebf07f659..cf4663cd26e 100644 --- a/scripts/globals/monstrosity.lua +++ b/scripts/globals/monstrosity.lua @@ -46,6 +46,9 @@ end -- Odyssean Passage ----------------------------------- +xi.monstrosity.odysseanPassageOnTrade = function(player, npc, trade) +end + xi.monstrosity.odysseanPassageOnTrigger = function(player, npc) -- TODO: Handle xi.settings.main.ENABLE_MONSTROSITY @@ -59,6 +62,17 @@ xi.monstrosity.odysseanPassageOnTrigger = function(player, npc) -- In Feretory: Event 5 (0, 0, 0, 0, 0, 0, 0, 0) end +xi.monstrosity.odysseanPassageOnEventUpdate = function(player, csid, option, npc) + print('update', csid, option) + player:updateEvent(0, 0, 0, 0, 1, 0, 0, 0) +end + +xi.monstrosity.odysseanPassageOnEventFinish = function(player, csid, option, npc) + print('finish', csid, option) + -- Option 1: Leave & Teleport to last city zone + -- Option 529: Teleport to Al'Taieu +end + ----------------------------------- -- Feretory ----------------------------------- @@ -85,19 +99,102 @@ end ----------------------------------- -- Aengus (Feretory NPC) ----------------------------------- --- Event 13 (As Lizard: 0, 0, 2, 0, 2, 90, 0, 0) + +xi.monstrosity.aengusOnTrade = function(player, npc, trade) +end + +xi.monstrosity.aengusOnTrigger = function(player, npc) + -- Event 13 (As Lizard: 0, 0, 2, 0, 2, 90, 0, 0) + player:startEvent(13, 0, 0, 2, 0, 2, 90, 0, 0) +end + +xi.monstrosity.aengusOnEventUpdate = function(player, csid, option, npc) + print('update', csid, option) +end + +xi.monstrosity.aengusOnEventFinish = function(player, csid, option, npc) + print('finish', csid, option) + if csid == 13 and option == 1 then + -- Selected: Enter Belligerency + end +end ----------------------------------- -- Teyrnon (Feretory NPC) ----------------------------------- --- Event 7 (As Lizard: 0, 0, 0, 0, 0, 0, 0, 0) + +xi.monstrosity.teyrnonOnTrade = function(player, npc, trade) +end + +xi.monstrosity.teyrnonOnTrigger = function(player, npc) + player:startEvent(7, 0, 0, 0, 0, 0, 0, 0, 0) +end + +xi.monstrosity.teyrnonOnEventUpdate = function(player, csid, option, npc) + print('update', csid, option) + if csid == 7 and option == 0 then -- Monsters Menu + player:updateEvent(0, 0, 0, 0, 0, 0, 0, 0) + elseif csid == 7 and option == 1 then -- Instinct menu + player:updateEvent(0, 0, 0, 0, 0, 0, 0, 0) + end +end + +xi.monstrosity.teyrnonOnEventFinish = function(player, csid, option, npc) + print('finish', csid, option) + -- Support Menu: + -- option 3: Dedication 1 + -- option 259: Dedication 2 + -- option 515: Regen + -- option 771: Refresh + -- option 1027: Protect + -- option 1283: Shell + -- option 1539: Haste +end ----------------------------------- -- Maccus (Feretory NPC) ----------------------------------- --- Event 9 (As Lizard: 285, 2, 2, 0, 0, 0, 0, 0) + +xi.monstrosity.maccusOnTrade = function(player, npc, trade) +end + +xi.monstrosity.maccusOnTrigger = function(player, npc) + player:startEvent(9, 285, 2, 2, 0, 0, 0, 0, 0) +end + +xi.monstrosity.maccusOnEventUpdate = function(player, csid, option, npc) + print('update', csid, option) +end + +xi.monstrosity.maccusOnEventFinish = function(player, csid, option, npc) + print('finish', csid, option) +end ----------------------------------- -- Suibhne (Feretory NPC) ----------------------------------- --- Event 11 (As Lizard: 1, 1, 0, 0, 0, 0, 0, 0) + +xi.monstrosity.suibhneOnTrade = function(player, npc, trade) +end + +xi.monstrosity.suibhneOnTrigger = function(player, npc) + player:startEvent(11, 1, 1, 0, 0, 0, 0, 0, 0) +end + +xi.monstrosity.suibhneOnEventUpdate = function(player, csid, option, npc) + print('update', csid, option) +end + +xi.monstrosity.suibhneOnEventFinish = function(player, csid, option, npc) + print('finish', csid, option) + -- Answers: + -- 1) 4. Teyrnon + -- 2) 3. Suibhne + -- 3) 1. Aengus + if csid == 11 and option == 2 then + -- Quiz failed + elseif csid == 11 and option == 6029313 then + -- Quiz succeeded + -- TODO: Unlock Bee (MON) + end +end diff --git a/scripts/zones/Feretory/npcs/Aengus.lua b/scripts/zones/Feretory/npcs/Aengus.lua index 565851991d8..53cc6a99171 100644 --- a/scripts/zones/Feretory/npcs/Aengus.lua +++ b/scripts/zones/Feretory/npcs/Aengus.lua @@ -8,21 +8,19 @@ require('scripts/globals/monstrosity') local entity = {} entity.onTrade = function(player, npc, trade) + xi.monstrosity.aengusOnTrade(player, npc, trade) end entity.onTrigger = function(player, npc) - player:startEvent(13, 0, 0, 2, 0, 2, 90, 0, 0) + xi.monstrosity.aengusOnTrigger(player, npc) end entity.onEventUpdate = function(player, csid, option, npc) - print('update', csid, option) + xi.monstrosity.aengusOnEventUpdate(player, csid, option, npc) end entity.onEventFinish = function(player, csid, option, npc) - print('finish', csid, option) - if csid == 13 and option == 1 then - -- Selected: Enter Belligerency - end + xi.monstrosity.aengusOnEventFinish(player, csid, option, npc) end return entity diff --git a/scripts/zones/Feretory/npcs/Maccus.lua b/scripts/zones/Feretory/npcs/Maccus.lua index b34eb6c90ae..e56ee4d347c 100644 --- a/scripts/zones/Feretory/npcs/Maccus.lua +++ b/scripts/zones/Feretory/npcs/Maccus.lua @@ -8,18 +8,19 @@ require('scripts/globals/monstrosity') local entity = {} entity.onTrade = function(player, npc, trade) + xi.monstrosity.maccusOnTrade(player, npc, trade) end entity.onTrigger = function(player, npc) - player:startEvent(9, 285, 2, 2, 0, 0, 0, 0, 0) + xi.monstrosity.maccusOnTrigger(player, npc) end entity.onEventUpdate = function(player, csid, option, npc) - print('update', csid, option) + xi.monstrosity.maccusOnEventUpdate(player, csid, option, npc) end entity.onEventFinish = function(player, csid, option, npc) - print('finish', csid, option) + xi.monstrosity.maccusOnEventFinish(player, csid, option, npc) end return entity diff --git a/scripts/zones/Feretory/npcs/Odyssean_Passage.lua b/scripts/zones/Feretory/npcs/Odyssean_Passage.lua index f3255080970..737ebe8be22 100644 --- a/scripts/zones/Feretory/npcs/Odyssean_Passage.lua +++ b/scripts/zones/Feretory/npcs/Odyssean_Passage.lua @@ -8,21 +8,19 @@ require('scripts/globals/monstrosity') local entity = {} entity.onTrade = function(player, npc, trade) + xi.monstrosity.odysseanPassageOnTrade(player, npc, trade) end entity.onTrigger = function(player, npc) - player:startEvent(5, 0, 0, 0, 0, 1, 0, 0, 0) + xi.monstrosity.odysseanPassageOnTrigger(player, npc) end entity.onEventUpdate = function(player, csid, option, npc) - print('update', csid, option) - player:updateEvent(0, 0, 0, 0, 1, 0, 0, 0) + xi.monstrosity.odysseanPassageOnEventUpdate(player, csid, option, npc) end entity.onEventFinish = function(player, csid, option, npc) - print('finish', csid, option) - -- Option 1: Leave & Teleport to last city zone - -- Option 529: Teleport to Al'Taieu + xi.monstrosity.odysseanPassageOnEventFinish(player, csid, option, npc) end return entity diff --git a/scripts/zones/Feretory/npcs/Suibhne.lua b/scripts/zones/Feretory/npcs/Suibhne.lua index b8ba13c936d..0588fc943ff 100644 --- a/scripts/zones/Feretory/npcs/Suibhne.lua +++ b/scripts/zones/Feretory/npcs/Suibhne.lua @@ -8,28 +8,19 @@ require('scripts/globals/monstrosity') local entity = {} entity.onTrade = function(player, npc, trade) + xi.monstrosity.suibhneOnTrade(player, npc, trade) end entity.onTrigger = function(player, npc) - player:startEvent(11, 1, 1, 0, 0, 0, 0, 0, 0) + xi.monstrosity.suibhneOnTrigger(player, npc) end entity.onEventUpdate = function(player, csid, option, npc) - print('update', csid, option) + xi.monstrosity.suibhneOnEventUpdate(player, csid, option, npc) end entity.onEventFinish = function(player, csid, option, npc) - print('finish', csid, option) - -- Answers: - -- 1) 4. Teyrnon - -- 2) 3. Suibhne - -- 3) 1. Aengus - if csid == 11 and option == 2 then - -- Quiz failed - elseif csid == 11 and option == 6029313 then - -- Quiz succeeded - -- TODO: Unlock Bee (MON) - end + xi.monstrosity.suibhneOnEventFinish(player, csid, option, npc) end return entity diff --git a/scripts/zones/Feretory/npcs/Teyrnon.lua b/scripts/zones/Feretory/npcs/Teyrnon.lua index 558fd2c07f0..1cfb270e35a 100644 --- a/scripts/zones/Feretory/npcs/Teyrnon.lua +++ b/scripts/zones/Feretory/npcs/Teyrnon.lua @@ -8,31 +8,19 @@ require('scripts/globals/monstrosity') local entity = {} entity.onTrade = function(player, npc, trade) + xi.monstrosity.teyrnonOnTrade(player, npc, trade) end entity.onTrigger = function(player, npc) - player:startEvent(7, 0, 0, 0, 0, 0, 0, 0, 0) + xi.monstrosity.teyrnonOnTrigger(player, npc) end entity.onEventUpdate = function(player, csid, option, npc) - print('update', csid, option) - if csid == 7 and option == 0 then -- Monsters Menu - player:updateEvent(0, 0, 0, 0, 0, 0, 0, 0) - elseif csid == 7 and option == 1 then -- Instinct menu - player:updateEvent(0, 0, 0, 0, 0, 0, 0, 0) - end + xi.monstrosity.teyrnonOnEventUpdate(player, csid, option, npc) end entity.onEventFinish = function(player, csid, option, npc) - print('finish', csid, option) - -- Support Menu: - -- option 3: Dedication 1 - -- option 259: Dedication 2 - -- option 515: Regen - -- option 771: Refresh - -- option 1027: Protect - -- option 1283: Shell - -- option 1539: Haste + xi.monstrosity.teyrnonOnEventFinish(player, csid, option, npc) end return entity From 656856c06c29053b62eb23e64ae318452d01f4a4 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Fri, 6 Oct 2023 10:22:00 +0100 Subject: [PATCH 031/103] Make monstrosity player data protected --- tools/dbtool.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/dbtool.py b/tools/dbtool.py index 3b31aa91ff3..62f390ba5fc 100644 --- a/tools/dbtool.py +++ b/tools/dbtool.py @@ -196,6 +196,7 @@ def load_into_dict(filename, settings): "char_job_points.sql", "char_look.sql", "char_merit.sql", + "char_monstrosity.sql", "char_pet.sql", "char_points.sql", "char_profile.sql", From b361a83b80de71a3702134c6bf9543480a51aea9 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Fri, 6 Oct 2023 11:43:00 +0100 Subject: [PATCH 032/103] Remove old quest --- .../hiddenQuests/Monstrosity_Unlock.lua | 108 ------------------ 1 file changed, 108 deletions(-) delete mode 100644 scripts/quests/hiddenQuests/Monstrosity_Unlock.lua diff --git a/scripts/quests/hiddenQuests/Monstrosity_Unlock.lua b/scripts/quests/hiddenQuests/Monstrosity_Unlock.lua deleted file mode 100644 index e23143c4a22..00000000000 --- a/scripts/quests/hiddenQuests/Monstrosity_Unlock.lua +++ /dev/null @@ -1,108 +0,0 @@ ------------------------------------ --- Unlocking Monstrosity ------------------------------------ - --- TODO: There is a Monstrosity Quest in "Other Areas", we should be using that! - -local quest = HiddenQuest:new('MonstrosityUnlock') - --- TODO: Handle xi.settings.main.ENABLE_MONSTROSITY - -quest.sections = -{ - -- Intro chatter - { - check = function(player, questVars, vars) - return xi.settings.main.ENABLE_MONSTROSITY == 1 and quest:getVar(player, 'Prog') == 0 - end, - - [xi.zone.PASHHOW_MARSHLANDS] = - { - ['Suspicious_Hume'] = - { - onTrigger = function(player, npc) - return quest:progressEvent(40) - end, - }, - - onEventFinish = - { - [40] = function(player, csid, option, npc) - if option == 0 then - quest:setVar(player, 'Prog', 1) - end - end, - }, - }, - }, - - -- Reminder - { - check = function(player, questVars, vars) - return quest:getVar(player, 'Prog') == 1 - end, - - [xi.zone.PASHHOW_MARSHLANDS] = - { - ['Suspicious_Hume'] = - { - onTrigger = function(player, npc) - return quest:progressEvent(41) - end, - }, - }, - - [xi.zone.PORT_WINDURST] = - { - ['Suspicious_Tarutaru'] = - { - onTrade = function(player, npc, trade) - if - npcUtil.tradeHasExactly(trade, xi.item.LIZARD_TAIL) - then - return quest:progressEvent(881, 926, 0, 0, 0, 0, 0, 0, 4) - end - end, - - -- Reminder - onTrigger = function(player, npc) - return quest:progressEvent(880) - end, - }, - - onEventFinish = - { - [881] = function(player, csid, option, npc) - if option == 0 then - quest:setVar(player, 'Prog', 2) - npcUtil.giveKeyItem(player, xi.ki.RING_OF_SUPERNAL_DISJUNCTION) - end - end, - }, - }, - }, - - -- Intro chatter - { - check = function(player, questVars, vars) - return quest:getVar(player, 'Prog') == 2 - end, - - [xi.zone.PORT_WINDURST] = - { - ['Suspicious_Tarutaru'] = - { - -- Reminder - onTrigger = function(player, npc) - return quest:progressEvent(882) - end, - }, - }, - }, - - -- On complete: - -- xi.monstrosity.unlockStartingMONs(player, xi.monstrosity.species.MANDRAGORA) - -- Then zone to Feretory -} - -return quest From 2007512b35c8b4ffc711613a0495980606207a4f Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Fri, 6 Oct 2023 11:43:10 +0100 Subject: [PATCH 033/103] Add addallmonstrosity command --- scripts/commands/addallmonstrosity.lua | 69 ++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 scripts/commands/addallmonstrosity.lua diff --git a/scripts/commands/addallmonstrosity.lua b/scripts/commands/addallmonstrosity.lua new file mode 100644 index 00000000000..3acb68e9fbd --- /dev/null +++ b/scripts/commands/addallmonstrosity.lua @@ -0,0 +1,69 @@ +----------------------------------- +-- func: addallmonstrosity +-- desc: Adds all species, instincts, and variants for monstrosity +----------------------------------- +local commandObj = {} + +commandObj.cmdprops = +{ + permission = 1, + parameters = 's' +} + +local function error(player, msg) + player:PrintToPlayer(msg) + player:PrintToPlayer('!addallmonstrosity (player)') +end + +commandObj.onTrigger = function(player, target) + -- validate target + local targ + if target == nil then + targ = player + else + targ = GetPlayerByName(target) + if targ == nil then + error(player, string.format('Player named "%s" not found!', target)) + return + end + end + + local data = + { + levels = + { + [xi.monstrosity.species.RABBIT] = 1, + [xi.monstrosity.species.MANDRAGORA] = 1, + [xi.monstrosity.species.LIZARD] = 1, + }, + instincts = + { + [00] = 0xFF, + [01] = 0xFF, + [02] = 0xFF, + [03] = 0xFF, + [04] = 0xFF, + [05] = 0xFF, + [06] = 0xFF, + [07] = 0xFF, + [08] = 0xFF, + [09] = 0xFF, + [10] = 0xFF, + [11] = 0xFF, + [12] = 0xFF, + + [20] = 0xFF, -- Unlock all first tier player race instincts + }, + variants = + { + [00] = 0xFF, + [01] = 0xFF, + } + } + + targ:setMonstrosity(data) + + player:PrintToPlayer(string.format('%s now has all monstrosity data.', targ:getName())) +end + +return commandObj From a397dc62e33aaa9975e8ff0a940730b85528f131 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Fri, 6 Oct 2023 11:43:23 +0100 Subject: [PATCH 034/103] Instinct mods for mandy and lizard --- sql/monstrosity_instinct_mods.sql | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/sql/monstrosity_instinct_mods.sql b/sql/monstrosity_instinct_mods.sql index 27bf9f2b7b5..ce25384dafc 100644 --- a/sql/monstrosity_instinct_mods.sql +++ b/sql/monstrosity_instinct_mods.sql @@ -22,6 +22,37 @@ INSERT INTO `monstrosity_instinct_mods` VALUES (5,9,5); -- DEX: 5 INSERT INTO `monstrosity_instinct_mods` VALUES (5,11,5); -- AGI: 5 INSERT INTO `monstrosity_instinct_mods` VALUES (5,288,2); -- DOUBLE_ATTACK: 2 +-- Mandragora instinct I +INSERT INTO `monstrosity_instinct_mods` VALUES (54,9,5); -- DEX: 5 +INSERT INTO `monstrosity_instinct_mods` VALUES (54,25,10); -- ACC: 5 +INSERT INTO `monstrosity_instinct_mods` VALUES (54,384,100); -- HASTE_GEAR: 100 + +-- Mandragora instinct II +INSERT INTO `monstrosity_instinct_mods` VALUES (55,1,10); -- DEF: 10 +INSERT INTO `monstrosity_instinct_mods` VALUES (55,13,5); -- MND: 5 +INSERT INTO `monstrosity_instinct_mods` VALUES (55,23,10); -- ATT: 10 +INSERT INTO `monstrosity_instinct_mods` VALUES (55,288,1); -- DOUBLE_ATTACK: 1 + +-- Mandragora instinct III +INSERT INTO `monstrosity_instinct_mods` VALUES (56,3,3); -- HPP: 3 +INSERT INTO `monstrosity_instinct_mods` VALUES (56,289,3); -- SUBTLE_BLOW: 3 +INSERT INTO `monstrosity_instinct_mods` VALUES (56,384,300); -- HASTE_GEAR: 300 + +-- Lizard instinct I +INSERT INTO `monstrosity_instinct_mods` VALUES (129,3,1); -- HPP: 1 +INSERT INTO `monstrosity_instinct_mods` VALUES (129,10,5); -- VIT: 5 +INSERT INTO `monstrosity_instinct_mods` VALUES (129,1,20); -- DEF: 20 + +-- Lizard instinct II +INSERT INTO `monstrosity_instinct_mods` VALUES (130,8,4); -- STR: 4 +INSERT INTO `monstrosity_instinct_mods` VALUES (130,23,14); -- ATT: 14 +INSERT INTO `monstrosity_instinct_mods` VALUES (130,73,3); -- STORETP: 3 + +-- Lizard instinct III +INSERT INTO `monstrosity_instinct_mods` VALUES (131,3,10); -- HPP: 10 +INSERT INTO `monstrosity_instinct_mods` VALUES (131,62,1); -- ATTP: 1 +INSERT INTO `monstrosity_instinct_mods` VALUES (131,1,30); -- DEF: 30 + -- Hume's instinct I INSERT INTO `monstrosity_instinct_mods` VALUES (768,8,2); -- STR: 2 INSERT INTO `monstrosity_instinct_mods` VALUES (768,9,2); -- DEX: 2 From 10e404e6e827c8573efc949726452f4f68269183 Mon Sep 17 00:00:00 2001 From: claywar Date: Fri, 6 Oct 2023 08:20:19 -0400 Subject: [PATCH 035/103] Interaction - Quest: Monstrosity Add TODO --- scripts/globals/quests.lua | 1 + scripts/quests/otherAreas/Monstrosity.lua | 235 ++++++++++++++++++ .../Northern_San_dOria/DefaultActions.lua | 58 ++--- scripts/zones/Northern_San_dOria/IDs.lua | 1 + scripts/zones/Port_Bastok/DefaultActions.lua | 62 ++--- scripts/zones/Port_Bastok/IDs.lua | 1 + .../zones/Port_Windurst/DefaultActions.lua | 32 +-- scripts/zones/Port_Windurst/IDs.lua | 1 + sql/npc_list.sql | 2 +- 9 files changed, 319 insertions(+), 74 deletions(-) create mode 100644 scripts/quests/otherAreas/Monstrosity.lua diff --git a/scripts/globals/quests.lua b/scripts/globals/quests.lua index 8bbd49fe024..cbb004a997f 100644 --- a/scripts/globals/quests.lua +++ b/scripts/globals/quests.lua @@ -547,6 +547,7 @@ xi.quest.id = PICTURE_PERFECT = 31, WAKING_THE_BEAST = 32, SURVIVAL_OF_THE_WISEST = 33, + MONSTROSITY = 34, -- + Converted A_HARD_DAYS_KNIGHT = 64, -- + Converted X_MARKS_THE_SPOT = 65, A_BITTER_PAST = 66, diff --git a/scripts/quests/otherAreas/Monstrosity.lua b/scripts/quests/otherAreas/Monstrosity.lua new file mode 100644 index 00000000000..b243789f0e5 --- /dev/null +++ b/scripts/quests/otherAreas/Monstrosity.lua @@ -0,0 +1,235 @@ +----------------------------------- +-- Monstrosity +----------------------------------- +-- Log ID: 4, Quest ID: 34 +-- Suspicious Hume : !pos -491.817 24.988 -618.552 109 +----------------------------------- + +local quest = Quest:new(xi.quest.log_id.OTHER_AREAS, xi.quest.id.otherAreas.MONSTROSITY) + +quest.reward = {} + +local baseNpcEvents = +{ + [xi.zone.PASHHOW_MARSHLANDS] = 40, + [xi.zone.NORTHERN_SAN_DORIA] = 882, + [xi.zone.PORT_BASTOK] = 418, + [xi.zone.PORT_WINDURST] = 880, +} + +local tradeItems = +{ + xi.item.LIZARD_TAIL, + xi.item.RABBIT_HIDE, + xi.item.TWO_LEAF_MANDRAGORA_BUD, +} + +local suspiciousCityNpc = +{ + onTrade = function(player, npc, trade) + if player:hasKeyItem(xi.ki.RING_OF_SUPERNAL_DISJUNCTION) then + return + end + + for _, itemId in ipairs(tradeItems) do + if npcUtil.tradeHasExactly(trade, itemId) then + return quest:progressEvent(baseNpcEvents[player:getZoneID()] + 1, itemId) + end + end + end, + + onTrigger = function(player, npc) + local baseEvent = baseNpcEvents[player:getZoneID()] + + if not player:hasKeyItem(xi.ki.RING_OF_SUPERNAL_DISJUNCTION) then + return quest:event(baseEvent) + else + return quest:event(baseEvent + 2) + end + end, +} + +local tradeEventFinish = function(player, csid, option, npc) + npcUtil.giveKeyItem(player, xi.ki.RING_OF_SUPERNAL_DISJUNCTION) +end + +local odysseanPassageNpc = +{ + onTrigger = function(player, npc) + if player:hasKeyItem(xi.ki.RING_OF_SUPERNAL_DISJUNCTION) then + return quest:progressEvent(baseNpcEvents[player:getZoneID()] + 3) + end + end, +} + +local odysseanPassageOnEventFinish = function(player, csid, option, npc) + if option == 1 then + player:setPos(-358, -3.4, -440, 64, xi.zone.FERETORY) + end +end + +quest.sections = +{ + { + check = function(player, status, vars) + return status == QUEST_AVAILABLE + end, + + [xi.zone.PASHHOW_MARSHLANDS] = + { + ['Suspicious_Hume'] = quest:progressEvent(40), + + onEventFinish = + { + [40] = function(player, csid, option, npc) + quest:begin(player) + end, + }, + }, + }, + + { + check = function(player, status, vars) + return status == QUEST_ACCEPTED + end, + + [xi.zone.NORTHERN_SAN_DORIA] = + { + ['Odyssean_Passage'] = odysseanPassageNpc, + ['Suspicious_Elvaan'] = suspiciousCityNpc, + + onEventFinish = + { + [883] = tradeEventFinish, + [885] = odysseanPassageOnEventFinish, + }, + }, + + [xi.zone.PASHHOW_MARSHLANDS] = + { + ['Odyssean_Passage'] = odysseanPassageNpc, + ['Suspicious_Hume'] = + { + onTrigger = function(player, npc) + if not player:hasKeyItem(xi.ki.RING_OF_SUPERNAL_DISJUNCTION) then + return quest:event(41) + else + return quest:event(42) + end + end, + }, + + onEventFinish = + { + [43] = odysseanPassageOnEventFinish, + }, + }, + + [xi.zone.PORT_BASTOK] = + { + ['Odyssean_Passage'] = odysseanPassageNpc, + ['Suspicious_Galka'] = suspiciousCityNpc, + + onEventFinish = + { + [419] = tradeEventFinish, + [421] = odysseanPassageOnEventFinish, + }, + }, + + [xi.zone.PORT_WINDURST] = + { + ['Odyssean_Passage'] = odysseanPassageNpc, + ['Suspicious_Tarutaru'] = suspiciousCityNpc, + + onEventFinish = + { + [881] = tradeEventFinish, + [883] = odysseanPassageOnEventFinish, + }, + }, + + [xi.zone.FERETORY] = + { + onZoneIn = + { + function(player, prevZone) + return 2 + end, + }, + + onEventUpdate = + { + [2] = function(player, csid, option, npc) + if option == 1 then + -- TODO: Give monstrosity things here or earlier, and wire up this update + player:updateEvent(7, 10, 2, 1024, 2, 0, 0, 0) + end + end, + }, + + onEventFinish = + { + [2] = function(player, csid, option, npc) + quest:complete(player) + end, + }, + }, + }, + + -- NOTE: The default events for the Suspicious NPCs require additional work to support + -- the items that they can provide, and will most likely need support in the monstrosity + -- global, along with event update/finish wiring here. + { + check = function(player, status, vars) + return status == QUEST_COMPLETED + end, + + [xi.zone.PASHHOW_MARSHLANDS] = + { + + ['Odyssean_Passage'] = odysseanPassageNpc, + ['Suspicious_Hume'] = quest:event(45):replaceDefault(), + + onEventFinish = + { + [43] = odysseanPassageOnEventFinish, + } + }, + + [xi.zone.NORTHERN_SAN_DORIA] = + { + ['Odyssean_Passage'] = odysseanPassageNpc, + ['Suspicious_Elvaan'] = quest:event(886):replaceDefault(), -- TODO: 886 may be once per zone or once, 887 after + + onEventFinish = + { + [885] = odysseanPassageOnEventFinish, + } + }, + + [xi.zone.PORT_BASTOK] = + { + ['Odyssean_Passage'] = odysseanPassageNpc, + ['Suspicious_Galka'] = quest:event(422):replaceDefault(), + + onEventFinish = + { + [421] = odysseanPassageOnEventFinish, + } + }, + + [xi.zone.PORT_WINDURST] = + { + ['Odyssean_Passage'] = odysseanPassageNpc, + ['Suspicious_Tarutaru'] = quest:event(884):replaceDefault(), + + onEventFinish = + { + [883] = odysseanPassageOnEventFinish, + } + }, + }, +} + +return quest diff --git a/scripts/zones/Northern_San_dOria/DefaultActions.lua b/scripts/zones/Northern_San_dOria/DefaultActions.lua index 3e3c6ce353d..b03b160238d 100644 --- a/scripts/zones/Northern_San_dOria/DefaultActions.lua +++ b/scripts/zones/Northern_San_dOria/DefaultActions.lua @@ -1,32 +1,34 @@ local ID = zones[xi.zone.NORTHERN_SAN_DORIA] return { - ['Abeaule'] = { text = ID.text.ABEAULE_DIALOG_THANKS }, - ['Abioleget'] = { text = ID.text.ABIOLEGET_DIALOG }, - ['Ailbeche'] = { event = 868 }, - ['Aivedoir'] = { text = ID.text.AIVEDOIR_DIALOG }, - ['Arienh'] = { text = ID.text.ARIENH_DIALOG }, - ['Charlaimagnat'] = { event = 702 }, - ['Chasalvige'] = { event = 6 }, - ['Emilia'] = { text = ID.text.EMILIA_DIALOG }, - ['Eperdur'] = { event = 678 }, - ['Fittesegat'] = { text = ID.text.FITTESEGAT_DIALOG }, - ['Gilipese'] = { text = ID.text.GILIPESE_DIALOG }, - ['Guilerme'] = { text = ID.text.GUILERME_DIALOG }, - ['Helaku'] = { event = 541 }, - ['Hinaree'] = { event = 580 }, - ['Ishwar'] = { text = ID.text.ISHWAR_DIALOG }, - ['Kasaroro'] = { event = 548 }, - ['Malfine'] = { text = ID.text.MALFINE_DIALOG }, - ['Maurine'] = { text = ID.text.MAURINE_DIALOG }, -- NOTE: These are two different NPCs - ['Maurinne'] = { text = ID.text.MAURINNE_DIALOG }, - ['Miageau'] = { event = 517 }, - ['Morjean'] = { event = 601 }, - ['Nouveil'] = { event = 574 }, - ['Pellimie'] = { text = ID.text.PELLIMIE_DIALOG }, - ['Pepigort'] = { text = ID.text.PEPIGORT_DIALOG }, - ['Phaviane'] = { text = ID.text.PHAVIANE_DIALOG }, - ['Prerivon'] = { text = ID.text.PRERIVON_DIALOG }, - ['Rodaillece'] = { text = ID.text.RODAILLECE_DIALOG }, - ['Sochiene'] = { text = ID.text.SOCHIENE_DIALOG }, + ['Abeaule'] = { text = ID.text.ABEAULE_DIALOG_THANKS }, + ['Abioleget'] = { text = ID.text.ABIOLEGET_DIALOG }, + ['Ailbeche'] = { event = 868 }, + ['Aivedoir'] = { text = ID.text.AIVEDOIR_DIALOG }, + ['Arienh'] = { text = ID.text.ARIENH_DIALOG }, + ['Charlaimagnat'] = { event = 702 }, + ['Chasalvige'] = { event = 6 }, + ['Emilia'] = { text = ID.text.EMILIA_DIALOG }, + ['Eperdur'] = { event = 678 }, + ['Fittesegat'] = { text = ID.text.FITTESEGAT_DIALOG }, + ['Gilipese'] = { text = ID.text.GILIPESE_DIALOG }, + ['Guilerme'] = { text = ID.text.GUILERME_DIALOG }, + ['Helaku'] = { event = 541 }, + ['Hinaree'] = { event = 580 }, + ['Ishwar'] = { text = ID.text.ISHWAR_DIALOG }, + ['Kasaroro'] = { event = 548 }, + ['Malfine'] = { text = ID.text.MALFINE_DIALOG }, + ['Maurine'] = { text = ID.text.MAURINE_DIALOG }, -- NOTE: These are two different NPCs + ['Maurinne'] = { text = ID.text.MAURINNE_DIALOG }, + ['Miageau'] = { event = 517 }, + ['Morjean'] = { event = 601 }, + ['Nouveil'] = { event = 574 }, + ['Odyssean_Passage'] = { messageSpecial = ID.text.NOTHING_HAPPENS }, + ['Pellimie'] = { text = ID.text.PELLIMIE_DIALOG }, + ['Pepigort'] = { text = ID.text.PEPIGORT_DIALOG }, + ['Phaviane'] = { text = ID.text.PHAVIANE_DIALOG }, + ['Prerivon'] = { text = ID.text.PRERIVON_DIALOG }, + ['Rodaillece'] = { text = ID.text.RODAILLECE_DIALOG }, + ['Sochiene'] = { text = ID.text.SOCHIENE_DIALOG }, + ['Suspicious_Elvaan'] = { event = 881 }, } diff --git a/scripts/zones/Northern_San_dOria/IDs.lua b/scripts/zones/Northern_San_dOria/IDs.lua index efa80bb027c..91ced81add3 100644 --- a/scripts/zones/Northern_San_dOria/IDs.lua +++ b/scripts/zones/Northern_San_dOria/IDs.lua @@ -122,6 +122,7 @@ zones[xi.zone.NORTHERN_SAN_DORIA] = FRAGMENT_FAR_TOO_SMALL = 18119, -- You obtain . However, it is far too small to house an adequate amount of energy. Alone, it serves no purpose. FRAGMENTS_MELD = 18120, -- The tiny fragments of Lilisette's memory meld together to form ! RETRIEVE_DIALOG_ID = 18155, -- You retrieve from the porter moogle's care. + NOTHING_HAPPENS = 18321, -- Nothing Happens... COMMON_SENSE_SURVIVAL = 18501, -- It appears that you have arrived at a new survival guide provided by the Adventurers' Mutual Aid Network. Common sense dictates that you should now be able to teleport here from similar tomes throughout the world. MAP_MARKER_TUTORIAL = 18623, -- Selecting Map from the main menu opens the map of the area in which you currently reside. Select Markers and press the right arrow key to see all the markers placed on your map. }, diff --git a/scripts/zones/Port_Bastok/DefaultActions.lua b/scripts/zones/Port_Bastok/DefaultActions.lua index 28a70629286..61981605416 100644 --- a/scripts/zones/Port_Bastok/DefaultActions.lua +++ b/scripts/zones/Port_Bastok/DefaultActions.lua @@ -1,34 +1,36 @@ local ID = zones[xi.zone.PORT_BASTOK] return { - ['Agapito'] = { event = 17 }, - ['Bartolomeo'] = { text = ID.text.ARRIVING_PASSENGER_DIALOG }, - ['Carmelo'] = { event = 182 }, - ['Clarion_Star'] = { event = 442 }, - ['Corann'] = { event = 38 }, - ['Dehlner'] = { event = 46 }, - ['Ehrhard'] = { event = 47 }, - ['Ensetsu'] = { event = 27 }, - ['Evi'] = { event = 21 }, - ['Ferrol'] = { event = 254 }, - ['Gudav'] = { event = 31 }, - ['Hilda'] = { event = 48 }, - ['Juroro'] = { event = 253 }, - ['Kaede'] = { event = 26 }, - ['Kagetora'] = { event = 23 }, - ['Kurando'] = { event = 28 }, - ['Latifah'] = { event = 13 }, - ['Mine_Konte'] = { event = 42 }, - ['Oggbi'] = { event = 230 }, - ['Otto'] = { event = 20 }, - ['Panana'] = { event = 43 }, - ['Paujean'] = { event = 25 }, - ['Powhatan'] = { text = ID.text.POWHATAN_DIALOG_1 }, - ['Qiji'] = { event = 33 }, - ['Rafaela'] = { event = 22 }, - ['Romilda'] = { event = 34 }, - ['Ronan'] = { event = 37 }, - ['Steel_Bones'] = { event = 29 }, - ['Tete'] = { event = 35 }, - ['Yazan'] = { event = 190 }, + ['Agapito'] = { event = 17 }, + ['Bartolomeo'] = { text = ID.text.ARRIVING_PASSENGER_DIALOG }, + ['Carmelo'] = { event = 182 }, + ['Clarion_Star'] = { event = 442 }, + ['Corann'] = { event = 38 }, + ['Dehlner'] = { event = 46 }, + ['Ehrhard'] = { event = 47 }, + ['Ensetsu'] = { event = 27 }, + ['Evi'] = { event = 21 }, + ['Ferrol'] = { event = 254 }, + ['Gudav'] = { event = 31 }, + ['Hilda'] = { event = 48 }, + ['Juroro'] = { event = 253 }, + ['Kaede'] = { event = 26 }, + ['Kagetora'] = { event = 23 }, + ['Kurando'] = { event = 28 }, + ['Latifah'] = { event = 13 }, + ['Mine_Konte'] = { event = 42 }, + ['Odyssean_Passage'] = { messageSpecial = ID.text.NOTHING_HAPPENS }, + ['Oggbi'] = { event = 230 }, + ['Otto'] = { event = 20 }, + ['Panana'] = { event = 43 }, + ['Paujean'] = { event = 25 }, + ['Powhatan'] = { text = ID.text.POWHATAN_DIALOG_1 }, + ['Qiji'] = { event = 33 }, + ['Rafaela'] = { event = 22 }, + ['Romilda'] = { event = 34 }, + ['Ronan'] = { event = 37 }, + ['Steel_Bones'] = { event = 29 }, + ['Suspicious_Galka'] = { event = 417 }, + ['Tete'] = { event = 35 }, + ['Yazan'] = { event = 190 }, } diff --git a/scripts/zones/Port_Bastok/IDs.lua b/scripts/zones/Port_Bastok/IDs.lua index 7be0e34213a..8cd96c5655d 100644 --- a/scripts/zones/Port_Bastok/IDs.lua +++ b/scripts/zones/Port_Bastok/IDs.lua @@ -76,6 +76,7 @@ zones[xi.zone.PORT_BASTOK] = OBTAINED_GUILD_POINTS = 12691, -- Obtained: guild points. OBTAINED_NUM_KEYITEMS = 13084, -- Obtained key item: ! NOT_ACQUAINTED = 13086, -- I'm sorry, but I don't believe we're acquainted. Please leave me be. + NOTHING_HAPPENS = 13237, -- Nothing Happens... }, mob = { diff --git a/scripts/zones/Port_Windurst/DefaultActions.lua b/scripts/zones/Port_Windurst/DefaultActions.lua index 09e2b150068..fcfd6f487ac 100644 --- a/scripts/zones/Port_Windurst/DefaultActions.lua +++ b/scripts/zones/Port_Windurst/DefaultActions.lua @@ -1,18 +1,20 @@ --- local ID = zones[xi.zone.PORT_WINDURST] +local ID = zones[xi.zone.PORT_WINDURST] return { - ['Ada'] = { event = 44 }, - ['Eki_Kamalabi'] = { event = 10005 }, - ['Gold_Skull'] = { event = 43 }, - ['Gomada-Vulmada'] = { event = 363 }, - ['Hakkuru-Rinkuru'] = { event = 224 }, - ['Josef'] = { event = 45 }, - ['Kohlo-Lakolo'] = { event = 361 }, - ['Melek'] = { event = 42 }, - ['Papo-Hopo'] = { event = 362 }, - ['Pichichi'] = { event = 364 }, - ['Pyo_Nzon'] = { event = 366 }, - ['Shanruru'] = { event = 367 }, - ['Tokaka'] = { event = 207 }, - ['Yafa_Yaa'] = { event = 365 }, + ['Ada'] = { event = 44 }, + ['Eki_Kamalabi'] = { event = 10005 }, + ['Gold_Skull'] = { event = 43 }, + ['Gomada-Vulmada'] = { event = 363 }, + ['Hakkuru-Rinkuru'] = { event = 224 }, + ['Josef'] = { event = 45 }, + ['Kohlo-Lakolo'] = { event = 361 }, + ['Melek'] = { event = 42 }, + ['Odyssean_Passage'] = { messageSpecial = ID.text.NOTHING_HAPPENS }, + ['Papo-Hopo'] = { event = 362 }, + ['Pichichi'] = { event = 364 }, + ['Pyo_Nzon'] = { event = 366 }, + ['Shanruru'] = { event = 367 }, + ['Suspicious_Tarutaru'] = { event = 879 }, + ['Tokaka'] = { event = 207 }, + ['Yafa_Yaa'] = { event = 365 }, } diff --git a/scripts/zones/Port_Windurst/IDs.lua b/scripts/zones/Port_Windurst/IDs.lua index f036fd1993c..f2454559a78 100644 --- a/scripts/zones/Port_Windurst/IDs.lua +++ b/scripts/zones/Port_Windurst/IDs.lua @@ -72,6 +72,7 @@ zones[xi.zone.PORT_WINDURST] = RETRIEVE_DIALOG_ID = 15915, -- You retrieve from the porter moogle's care. OBTAINED_NUM_KEYITEMS = 15957, -- Obtained key item: ! NOT_ACQUAINTED = 15959, -- I'm sorry, but I don't believe we're acquainted. Please leave me be. + NOTHING_HAPPENS = 16117, -- Nothing Happens... COMMON_SENSE_SURVIVAL = 16329, -- It appears that you have arrived at a new survival guide provided by the Adventurers' Mutual Aid Network. Common sense dictates that you should now be able to teleport here from similar tomes throughout the world. }, mob = diff --git a/sql/npc_list.sql b/sql/npc_list.sql index c3d68e95624..ba2b426be04 100644 --- a/sql/npc_list.sql +++ b/sql/npc_list.sql @@ -35758,7 +35758,7 @@ INSERT INTO `npc_list` VALUES (17940524,'Achieve_Master','Achieve Master',0,0.00 INSERT INTO `npc_list` VALUES (17940526,'csnpc','',0,0.000,0.000,0.000,0,50,50,0,0,0,2,2051,0x0000340000000000000000000000000000000000,0,'SOA',1); -- ------------------------------------------------------------ --- Ferretory (Zone 285) +-- Feretory (Zone 285) -- ------------------------------------------------------------ INSERT INTO `npc_list` VALUES (17944579,'Moogle','Moogle',0,0.000,0.000,0.000,0,40,40,0,0,0,2,3,0x0000520000000000000000000000000000000000,0,'SOA',0); From 6b2a65b92025fdaf23651002c5d9b8ed38959df6 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Fri, 6 Oct 2023 16:35:45 +0100 Subject: [PATCH 036/103] Instinct mods for bee --- scripts/commands/addallmonstrosity.lua | 16 +- scripts/globals/monstrosity.lua | 318 ++++++++++++++++++++++++- sql/monstrosity_instinct_mods.sql | 15 ++ sql/monstrosity_species.sql | 12 +- 4 files changed, 350 insertions(+), 11 deletions(-) diff --git a/scripts/commands/addallmonstrosity.lua b/scripts/commands/addallmonstrosity.lua index 3acb68e9fbd..fc3f19bebf3 100644 --- a/scripts/commands/addallmonstrosity.lua +++ b/scripts/commands/addallmonstrosity.lua @@ -36,7 +36,7 @@ commandObj.onTrigger = function(player, target) [xi.monstrosity.species.MANDRAGORA] = 1, [xi.monstrosity.species.LIZARD] = 1, }, - instincts = + instincts = -- std::array instincts { [00] = 0xFF, [01] = 0xFF, @@ -51,8 +51,20 @@ commandObj.onTrigger = function(player, target) [10] = 0xFF, [11] = 0xFF, [12] = 0xFF, + [13] = 0xFF, + [14] = 0xFF, + [15] = 0xFF, + [16] = 0xFF, + [17] = 0xFF, + [18] = 0xFF, - [20] = 0xFF, -- Unlock all first tier player race instincts + [20] = 0xFF, + [21] = 0xFF, + [22] = 0xFF, + [23] = 0xFF, + + [62] = 0xFF, + [63] = 0xFF, }, variants = { diff --git a/scripts/globals/monstrosity.lua b/scripts/globals/monstrosity.lua index cf4663cd26e..e43ed0116e2 100644 --- a/scripts/globals/monstrosity.lua +++ b/scripts/globals/monstrosity.lua @@ -13,9 +13,321 @@ xi.monstrosity = xi.monstrosity or {} xi.monstrosity.species = { - RABBIT = 1, - MANDRAGORA = 18, - LIZARD = 43, + RABBIT = 1, + BEHEMOTH = 2, + TIGER = 3, + SHEEP = 4, + RAM = 5, + DHALMEL = 6, + COEURL = 7, + OPO_OPO = 8, + MANTICORE = 9, + BUFFALO = 10, + MARID = 11, + CERBERUS = 12, + GNOLE = 13, + + FUNGUAR = 15, + TREANT_SAPLING = 16, + MORBOL = 17, + MANDRAGORA = 18, + SABOTENDER = 19, + FLYTRAP = 20, + GOOBBUE = 21, + RAFFLESIA = 22, + PANOPT = 23, + + BEE = 27, + BEETLE = 28, + CRAWLER = 29, + FLY = 30, + SCORPION = 31, + SPIDER = 32, + ANTLION = 33, + DIREMITE = 34, + CHIGOE = 35, + WAMOURACAMPA = 36, + LADYBUG = 37, + GNAT = 38, + + LIZARD = 43, + RAPTOR = 44, + ADAMANTOISE = 45, + BUGARD = 46, + EFT = 47, + WIVRE = 48, + PEISTE = 49, + + SLIME = 52, + HECTEYES = 53, + FLAN = 54, + SLUG = 56, + SANDWORM = 57, + LEECH = 58, + + CRAB = 60, + PUGIL = 61, + SEA_MONK = 62, + URAGNITE = 63, + OROBON = 64, + RUSZOR = 65, + TOAD = 66, + + BIRD = 69, + COCKATRICE = 70, + ROC = 71, + BAT = 72, + HIPPOGRYPH = 73, + APKALLU = 74, + COLIBRI = 75, + AMPHIPTERE = 76, + + ASTOLTIAN_SLIME = 126, + EORZEAN_SPRIGGAN = 127, +} + +xi.monstrosity.variants = +{ + -- Rabbit + ONYX_RABBIT = 0, + ALABASTER_RABBIT = 1, + LAPINION = 2, + + -- Behemoth + ELSAMOTH = 3, + + -- Tiger + LEGENDARY_TIGER = 5, + SMILODON = 6, + + -- Sheep + KARAKUL = 7, + + -- Coeurl + LYNX = 10, + COLLARED_LYNX = 11, + + -- Manticore + LEGENDARY_MANTICORE = 12, + + -- Cerberus + ORTHRUS = 13, + + -- Gnole + BIPEDAL_GNOLE = 14, + + -- Funguar + COPPERCAP = 15, + + -- Treant Sapling + TREANT = 16, + FLOWERING_TREANT = 17, + SCARLET_TINGED_TREANT = 18, + BARREN_TREANT = 19, + NECKLACED_TREANT = 20, + + -- Morbol + PYGMY_MORBOL = 21, + SCARE_MORBOL = 22, + AMERETAT = 23, + PURBOL = 24, + + -- Mandragora + KORRIGAN = 25, + LYCOPODIUM = 26, + PYGMY_MANDRAGORA = 27, + ADENIUM = 28, + PACHYPODIUM = 29, + ENLIGHTENED_MANDRAGORA = 30, + NEW_YEAR_MANDRAGORA = 31, + + -- Sabotender + SABOTENDER_FLORIDO = 32, + + -- Rafflesia + MITRASTEMA = 33, + + -- Bee + VERMILLION_AND_ONYX_BEE = 34, + ZAFFRE_BEE = 35, + + -- Beetle + ONYX_BEETLE = 36, + GAMBOGE_BEETLE = 37, + + -- Crawler + ERUCA = 38, + EMERALD_CRAWLER = 39, + PYGMY_EMERALD_CRAWLER = 40, + + -- Fly + VERMILLION_FLY = 41, + + -- Scorpion + SCOLOPENDRID = 42, + UNUSUAL_SCOLOPENDRID = 43, + + -- Spider + RETICULATED_SPIDER = 44, + VERMILLION_AND_ONYX_SPIDER = 45, + + -- Antlion + ONYX_ANTLION = 46, + FORMICEROS = 47, + + -- Diremite + ARUNDIMITE = 48, + + -- Chigoe + AZURE_CHIGOE = 49, + + -- Wamouracampa + COILED_WAMOURACAMPA = 50, + + -- Wamoura + WAMOURA = 51, + CORAL_WAMOURA = 52, + + -- Ladybug + GOLD_LADYBUG = 53, + + -- Gnat + MIDGE = 54, + + -- Lizard + ASHEN_LIZARD = 59, + + -- Raptor + EMERALD_RAPTOR = 60, + VERMILLION_RAPTOR = 61, + + -- Adamantoise + PYGMY_ADAMANTOISE = 62, + LEGENDARY_ADAMANTOISE = 63, + FERROMANTOISE = 64, + + -- Bugard + ABYSSOBUGARD = 65, + + -- Eft + TARICHUK = 66, + + -- Wivre + UNUSUAL_WIVRE = 67, + + -- Peiste + SIBILUS = 68, + + -- Slime + CLOT = 73, + GOLD_SLIME = 74, + BOIL = 75, + + -- Flan + GOLD_FLAN = 76, + BLANCMANGE = 77, + + -- Sandworm + PYGMY_SANDWORM = 78, + GIGAWORM = 79, + + -- Leech + AZURE_LEECH = 80, + OBDELLA = 81, + + -- Crab + VERMILLION_CRAB = 84, + BASKET_BURDENED_CRAB = 85, + VERMILLION_BASKET_BURDENED_CRAB = 86, + PORTER_CRAB = 87, + + -- Pugil + JAGIL = 88, + + -- Sea Monk + AZURE_SEA_MONK = 89, + + -- Uragnite + LIMASCABRA = 90, + + -- Orobon + PYGMY_OROBON = 91, + OGREBON = 92, + + -- Toad + AZURE_TOAD = 93, + VERMILLION_TOAD = 94, + + -- Bird + ONYX_BIRD = 95, + + -- Cockatrice + ZIZ = 96, + + -- Roc + LEGENDARY_ROC = 97, + GAGANA = 98, + + -- Bat + BATS = 99, + VERMILLION_BAT = 100, + VERMILLION_BATS = 101, + + -- Apkallu + INGUZA = 102, + + -- Colibri + TOUCALIBRI = 103, + + -- Amphiptere + SANGUIPTERE = 104, + + -- Slime + SHE_SLIME = 252, + METAL_SLIME = 253, + + -- Spriggan + SPRIGGAN_C = 254, + SPRIGGAN_G = 255, +} + +xi.monstrosity.purchasableInstincts = +{ + -- Default (0x1F) + HUME_I = 0, + ELVAAN_I = 1, + TARU_I = 2, + MITHRA_I = 3, + GALKA_I = 4, + + HUME_II = 5, + ELVAAN_II = 6, + TARU_II = 7, + MITHRA_II = 8, + GALKA_II = 9, + + WAR = 10, + MNK = 11, + WHM = 12, + BLM = 13, + RDM = 14, + THF = 15, + PLD = 16, + DRK = 17, + BST = 18, + BRD = 19, + RNG = 20, + SAM = 21, + NIN = 22, + DRG = 23, + SMN = 24, + BLU = 25, + COR = 26, + PUP = 27, + DNC = 28, + SCH = 29, + GEO = 30, + RUN = 31, } ----------------------------------- diff --git a/sql/monstrosity_instinct_mods.sql b/sql/monstrosity_instinct_mods.sql index ce25384dafc..80f0995f93c 100644 --- a/sql/monstrosity_instinct_mods.sql +++ b/sql/monstrosity_instinct_mods.sql @@ -38,6 +38,21 @@ INSERT INTO `monstrosity_instinct_mods` VALUES (56,3,3); -- HPP: 3 INSERT INTO `monstrosity_instinct_mods` VALUES (56,289,3); -- SUBTLE_BLOW: 3 INSERT INTO `monstrosity_instinct_mods` VALUES (56,384,300); -- HASTE_GEAR: 300 +-- Bee instinct I +INSERT INTO `monstrosity_instinct_mods` VALUES (81,11,5); -- AGI: 5 +INSERT INTO `monstrosity_instinct_mods` VALUES (81,23,10); -- ATT: 10 +INSERT INTO `monstrosity_instinct_mods` VALUES (81,68,5); -- EVA: 5 + +-- Bee instinct II +INSERT INTO `monstrosity_instinct_mods` VALUES (82,3,2); -- HPP: 2 +INSERT INTO `monstrosity_instinct_mods` VALUES (82,5,50); -- MP: 50 +INSERT INTO `monstrosity_instinct_mods` VALUES (82,12,5); -- INT: 5 + +-- Bee instinct III +INSERT INTO `monstrosity_instinct_mods` VALUES (83,8,10); -- STR: 10 +INSERT INTO `monstrosity_instinct_mods` VALUES (83,10,5); -- VIT: 5 +INSERT INTO `monstrosity_instinct_mods` VALUES (83,384,300); -- HASTE_GEAR: 300 + -- Lizard instinct I INSERT INTO `monstrosity_instinct_mods` VALUES (129,3,1); -- HPP: 1 INSERT INTO `monstrosity_instinct_mods` VALUES (129,10,5); -- VIT: 5 diff --git a/sql/monstrosity_species.sql b/sql/monstrosity_species.sql index 3e486626539..2db4f9c74b6 100644 --- a/sql/monstrosity_species.sql +++ b/sql/monstrosity_species.sql @@ -223,10 +223,10 @@ INSERT INTO `monstrosity_species` VALUES (75, 359, "Toucalibri (Colibri)", 0x000 INSERT INTO `monstrosity_species` VALUES (76, 76, "Amphiptere", 0x0000); INSERT INTO `monstrosity_species` VALUES (76, 360, "Sanguiptere (Amphiptere)", 0x0000); -INSERT INTO `monstrosity_species` VALUES (126, 254, "DQ Slime", 0x0B41); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (126, 508, "DQ She-Slime", 0x0B5B); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (126, 509, "DQ Metal Slime", 0x0B5C); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (126, 254, "Astoltian Slime", 0x0B41); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (126, 508, "Astoltian She-Slime", 0x0B5B); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (126, 509, "Astoltian Metal Slime", 0x0B5C); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (127, 255, "FFXIV Spriggan", 0x0B42); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (127, 510, "FFXIV Spriggan.C", 0x0B5D); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (127, 511, "FFXIV Spriggan.G", 0x0B5E); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (127, 255, "Eorzean Spriggan", 0x0B42); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (127, 510, "Eorzean Spriggan.C", 0x0B5D); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (127, 511, "Eorzean Spriggan.G", 0x0B5E); -- TODO: Look guessed not capped From c2d52a6d691bd595a8237380209183b9cd21743b Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Fri, 6 Oct 2023 16:44:00 +0100 Subject: [PATCH 037/103] Add settings check to Monstrosity quest --- scripts/quests/otherAreas/Monstrosity.lua | 2 +- settings/default/main.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/quests/otherAreas/Monstrosity.lua b/scripts/quests/otherAreas/Monstrosity.lua index b243789f0e5..6c1fa6ffaa3 100644 --- a/scripts/quests/otherAreas/Monstrosity.lua +++ b/scripts/quests/otherAreas/Monstrosity.lua @@ -72,7 +72,7 @@ quest.sections = { { check = function(player, status, vars) - return status == QUEST_AVAILABLE + return xi.settings.main.ENABLE_MONSTROSITY == 1 and status == QUEST_AVAILABLE end, [xi.zone.PASHHOW_MARSHLANDS] = diff --git a/settings/default/main.lua b/settings/default/main.lua index b9b3ddd6692..da6767fec54 100644 --- a/settings/default/main.lua +++ b/settings/default/main.lua @@ -69,7 +69,7 @@ xi.settings.main = -- VoidWalker ENABLE_VOIDWALKER = 1, - -- VoidWalker + -- Monstrosity ENABLE_MONSTROSITY = 1, -- TREASURE CASKETS From b8f90fcd5634232628c10738f6783e8d15e83b7e Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Fri, 6 Oct 2023 17:03:48 +0100 Subject: [PATCH 038/103] Add MON unlock to quest --- scripts/quests/otherAreas/Monstrosity.lua | 14 ++++++++++---- src/map/lua/lua_baseentity.cpp | 14 +++++++++++++- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/scripts/quests/otherAreas/Monstrosity.lua b/scripts/quests/otherAreas/Monstrosity.lua index 6c1fa6ffaa3..b30d599ce72 100644 --- a/scripts/quests/otherAreas/Monstrosity.lua +++ b/scripts/quests/otherAreas/Monstrosity.lua @@ -19,9 +19,9 @@ local baseNpcEvents = local tradeItems = { - xi.item.LIZARD_TAIL, - xi.item.RABBIT_HIDE, - xi.item.TWO_LEAF_MANDRAGORA_BUD, + { xi.item.LIZARD_TAIL, xi.monstrosity.species.LIZARD }, + { xi.item.RABBIT_HIDE, xi.monstrosity.species.RABBIT }, + { xi.item.TWO_LEAF_MANDRAGORA_BUD, xi.monstrosity.species.MANDRAGORA }, } local suspiciousCityNpc = @@ -31,8 +31,10 @@ local suspiciousCityNpc = return end - for _, itemId in ipairs(tradeItems) do + for _, entry in ipairs(tradeItems) do + local itemId = entry[1] if npcUtil.tradeHasExactly(trade, itemId) then + player:setLocalVar('MONSTROSITY_UNLOCK', entry[2]) return quest:progressEvent(baseNpcEvents[player:getZoneID()] + 1, itemId) end end @@ -51,6 +53,10 @@ local suspiciousCityNpc = local tradeEventFinish = function(player, csid, option, npc) npcUtil.giveKeyItem(player, xi.ki.RING_OF_SUPERNAL_DISJUNCTION) + local species = player:getLocalVar('MONSTROSITY_UNLOCK') + if species > 0 then + xi.monstrosity.unlockStartingMONs(player, species) + end end local odysseanPassageNpc = diff --git a/src/map/lua/lua_baseentity.cpp b/src/map/lua/lua_baseentity.cpp index 90f711d9d79..a5cab9b37f1 100644 --- a/src/map/lua/lua_baseentity.cpp +++ b/src/map/lua/lua_baseentity.cpp @@ -6314,6 +6314,8 @@ void CLuaBaseEntity::setMonstrosity(sol::table table) return; } + bool startsWithMonstrosityData = PChar->m_PMonstrosity != nullptr; + // NOTE: This will populate m_PMonstrosity if it doesn't exist monstrosity::ReadMonstrosityData(PChar); @@ -6363,7 +6365,17 @@ void CLuaBaseEntity::setMonstrosity(sol::table table) } monstrosity::WriteMonstrosityData(PChar); - monstrosity::SendFullMonstrosityUpdate(PChar); + + // If we didn't start with Monstrosity data, we should wipe it out now so we + // don't change modes + if (!startsWithMonstrosityData) + { + PChar->m_PMonstrosity = nullptr; + } + else + { + monstrosity::SendFullMonstrosityUpdate(PChar); + } } /************************************************************************ From a7a4b53a6125175f23f24966eb5f75ca5a9f19a4 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Fri, 6 Oct 2023 17:44:17 +0100 Subject: [PATCH 039/103] Send MON name in char update packet --- src/map/packets/char.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/map/packets/char.cpp b/src/map/packets/char.cpp index 654056fad1c..1bec30a5667 100644 --- a/src/map/packets/char.cpp +++ b/src/map/packets/char.cpp @@ -163,7 +163,7 @@ void CCharPacket::updateWith(CCharEntity* PChar, ENTITYUPDATE type, uint8 update ref(0x42) = 0x50 + PChar->StatusEffectContainer->GetStatusEffect(EFFECT_COLURE_ACTIVE)->GetPower(); } - ref(0x43) = 0x04; + ref(0x43) = 0x04; // Seen as 0x0C and 0x06 in Monstrosity? if (updatemask & UPDATE_LOOK) { @@ -181,6 +181,7 @@ void CCharPacket::updateWith(CCharEntity* PChar, ENTITYUPDATE type, uint8 update if (PChar->m_PMonstrosity != nullptr) { + ref(0x3E) = monstrosity::GetPackedMonstrosityName(PChar); ref(0x48) = PChar->m_PMonstrosity->Look; ref(0x58) = 0xFFFF; } From 909fca0db0cbfb4628bbf047d6f17b0b115e269e Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Fri, 6 Oct 2023 18:30:33 +0100 Subject: [PATCH 040/103] Monstrosity /checkname messages --- src/map/packet_system.cpp | 7 +++++++ src/map/packets/message_standard.cpp | 8 ++++++++ src/map/packets/message_standard.h | 2 ++ 3 files changed, 17 insertions(+) diff --git a/src/map/packet_system.cpp b/src/map/packet_system.cpp index e1ea8dd90d4..b245c0eff0a 100644 --- a/src/map/packet_system.cpp +++ b/src/map/packet_system.cpp @@ -6243,6 +6243,13 @@ void SmallPacket0x0DD(map_session_data_t* const PSession, CCharEntity* const PCh { CCharEntity* PTarget = (CCharEntity*)PEntity; + if (PTarget->m_PMonstrosity) + { + PChar->pushPacket(new CMessageStandardPacket(PTarget, 0, 0, MsgStd::MonstrosityCheckOut)); + PTarget->pushPacket(new CMessageStandardPacket(PChar, 0, 0, MsgStd::MonstrosityCheckIn)); + return; + } + if (!PChar->m_isGMHidden || (PChar->m_isGMHidden && PTarget->m_GMlevel >= PChar->m_GMlevel)) { PTarget->pushPacket(new CMessageStandardPacket(PChar, 0, 0, MsgStd::Examine)); diff --git a/src/map/packets/message_standard.cpp b/src/map/packets/message_standard.cpp index fd53a9d2b16..972bcb2b99b 100644 --- a/src/map/packets/message_standard.cpp +++ b/src/map/packets/message_standard.cpp @@ -81,6 +81,14 @@ CMessageStandardPacket::CMessageStandardPacket(CCharEntity* PChar, uint32 param0 ref(0x0C) = 0x10; + snprintf((char*)data + (0x0D), 24, "string2 %s", PChar->GetName().c_str()); + } + else if (MessageID == MsgStd::MonstrosityCheckIn || MessageID == MsgStd::MonstrosityCheckOut) + { + this->setSize(0x20); + + ref(0x0A) = static_cast(MessageID); + snprintf((char*)data + (0x0D), 24, "string2 %s", PChar->GetName().c_str()); } } diff --git a/src/map/packets/message_standard.h b/src/map/packets/message_standard.h index 2098c7f259a..75d346d2a48 100644 --- a/src/map/packets/message_standard.h +++ b/src/map/packets/message_standard.h @@ -99,6 +99,8 @@ enum class MsgStd CannotHere = 256, // You cannot use that command in this area. HeadgearShow = 260, HeadgearHide = 261, + MonstrosityCheckOut = 263, // This monster is currently possessed by ! + MonstrosityCheckIn = 264, // stares at you intently, evidently aware that you have discovered its true form. TrustCannotJoinParty = 265, // You are unable to join a party whose leader currently has an alter ego present. TrustCannotJoinAlliance = 266, // You are unable to join an alliance whose leader currently has an alter ego present. StyleLockOn = 267, // Style lock mode enabled. From 1f332824168b96a5fa360adaf9ffe90498524d4d Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Fri, 6 Oct 2023 19:25:11 +0100 Subject: [PATCH 041/103] Hook up death menu --- src/map/monstrosity.cpp | 44 +++++++++++++++++++++++++++++++++++++-- src/map/monstrosity.h | 2 ++ src/map/packet_system.cpp | 8 +++++++ 3 files changed, 52 insertions(+), 2 deletions(-) diff --git a/src/map/monstrosity.cpp b/src/map/monstrosity.cpp index 960a75c4e92..dc8a55987e2 100644 --- a/src/map/monstrosity.cpp +++ b/src/map/monstrosity.cpp @@ -34,6 +34,7 @@ #include "packets/monipulator2.h" #include "utils/charutils.h" +#include "utils/zoneutils.h" extern std::unique_ptr sql; @@ -190,8 +191,8 @@ void monstrosity::WriteMonstrosityData(CCharEntity* PChar) // TODO: Ensure there's a row to write to { - bool needToCreate = false; - int32 ret = sql->Query("SELECT * FROM char_monstrosity WHERE charid = %d;", PChar->id); + bool needToCreate = false; + int32 ret = sql->Query("SELECT * FROM char_monstrosity WHERE charid = %d;", PChar->id); if (ret != SQL_ERROR && sql->NumRows() == 0) { needToCreate = true; @@ -452,6 +453,45 @@ void monstrosity::SetLevel(CCharEntity* PChar, uint8 id, uint8 level) PChar->m_PMonstrosity->levels[id] = level; } +void monstrosity::HandleDeathMenu(CCharEntity* PChar, uint8 type) +{ + if (PChar->m_PMonstrosity == nullptr) + { + return; + } + + // remove weakness on homepoint + // PChar->StatusEffectContainer->DelStatusEffectSilent(EFFECT_WEAKNESS); + // PChar->StatusEffectContainer->DelStatusEffectSilent(EFFECT_LEVEL_SYNC); + + PChar->SetDeathTimestamp(0); + + PChar->health.hp = PChar->GetMaxHP(); + PChar->health.mp = PChar->GetMaxMP(); + + PChar->status = STATUS_TYPE::DISAPPEAR; + PChar->animation = ANIMATION_NONE; + PChar->updatemask |= UPDATE_HP; + + PChar->clearPacketList(); + + // Monstrosity death menu: + // 2: Retry + // 1: Cancel + if (type == 1) + { + // Return to Feretory + PChar->loc.destination = ZONE_FERETORY; + } + else if (type == 2) + { + // Restart this zone with Gestation effect + PChar->loc.destination = PChar->loc.zone->GetID(); + } + + charutils::SendToZone(PChar, 2, zoneutils::GetZoneIPP(PChar->loc.destination)); +} + void monstrosity::MaxAllLevels(CCharEntity* PChar) { if (PChar->m_PMonstrosity == nullptr) diff --git a/src/map/monstrosity.h b/src/map/monstrosity.h index 7fda732279d..f648895eec1 100644 --- a/src/map/monstrosity.h +++ b/src/map/monstrosity.h @@ -66,6 +66,8 @@ namespace monstrosity void SetLevel(CCharEntity* PChar, uint8 id, uint8 level); + void HandleDeathMenu(CCharEntity* PChar, uint8 type); + // Debug void MaxAllLevels(CCharEntity* PChar); void UnlockAllInstincts(CCharEntity* PChar); diff --git a/src/map/packet_system.cpp b/src/map/packet_system.cpp index b245c0eff0a..016543dca09 100644 --- a/src/map/packet_system.cpp +++ b/src/map/packet_system.cpp @@ -1009,6 +1009,14 @@ void SmallPacket0x01A(map_session_data_t* const PSession, CCharEntity* const PCh { return; } + + if (PChar->m_PMonstrosity) + { + auto type = data.ref(0x0C); + monstrosity::HandleDeathMenu(PChar, type); + return; + } + PChar->setCharVar("expLost", 0); charutils::HomePoint(PChar); } From f2e6371351df97349c9497bc685ce91677e5f4c1 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Sat, 7 Oct 2023 10:00:17 +0100 Subject: [PATCH 042/103] Hook up sql helpers: ObjectToBlobString, GetBlobData --- src/common/sql.h | 17 ++++++-- src/map/monstrosity.cpp | 91 +++++++++++------------------------------ 2 files changed, 37 insertions(+), 71 deletions(-) diff --git a/src/common/sql.h b/src/common/sql.h index 8f35554e05c..8100890b288 100644 --- a/src/common/sql.h +++ b/src/common/sql.h @@ -186,13 +186,24 @@ class SqlConnection std::string GetStringData(size_t col); template - void GetBlobData(size_t col, T& destination) + void GetBlobData(size_t col, T* destination) { - // TTODO: Fix me size_t length = 0; char* buffer = nullptr; GetData(col, &buffer, &length); - // std::memcpy((void*)destination, buffer, (length > sizeof(T) ? sizeof(T) : length)); + std::memcpy(destination, buffer, (length > sizeof(T) ? sizeof(T) : length)); + } + + template + std::string ObjectToBlobString(T* destination) + { + char buffer[sizeof(T) * 2 + 1]; + { + char dataBlob[sizeof(T)]; + std::memcpy(dataBlob, destination, sizeof(dataBlob)); + EscapeStringLen(buffer, dataBlob, sizeof(dataBlob)); + } + return std::string(buffer); } /// Frees the result of the query. diff --git a/src/map/monstrosity.cpp b/src/map/monstrosity.cpp index dc8a55987e2..7276aca35fa 100644 --- a/src/map/monstrosity.cpp +++ b/src/map/monstrosity.cpp @@ -69,6 +69,11 @@ monstrosity::MonstrosityData_t::MonstrosityData_t() , NamePrefix2(0x00) // Nothing , CurrentExp(0) // No exp { + levels[1] = 1; // Rabbit + levels[18] = 1; // Mandragora + levels[43] = 1; // Lizard + + instincts[20] = 0x1F; // Default player race instincts } void monstrosity::LoadStaticData() @@ -140,38 +145,10 @@ void monstrosity::ReadMonstrosityData(CCharEntity* PChar) data->NamePrefix2 = static_cast(sql->GetUIntData(4)); data->CurrentExp = static_cast(sql->GetUIntData(5)); - // TODO: Make these a template in sql.h - // equip - { - size_t length = 0; - char* buffer = nullptr; - sql->GetData(6, &buffer, &length); - std::memcpy(&data->EquippedInstincts[0], buffer, (length > sizeof(data->EquippedInstincts) ? sizeof(data->EquippedInstincts) : length)); - } - - // levels - { - size_t length = 0; - char* buffer = nullptr; - sql->GetData(7, &buffer, &length); - std::memcpy(&data->levels[0], buffer, (length > sizeof(data->levels) ? sizeof(data->levels) : length)); - } - - // instincts - { - size_t length = 0; - char* buffer = nullptr; - sql->GetData(8, &buffer, &length); - std::memcpy(&data->instincts[0], buffer, (length > sizeof(data->instincts) ? sizeof(data->instincts) : length)); - } - - // variants - { - size_t length = 0; - char* buffer = nullptr; - sql->GetData(9, &buffer, &length); - std::memcpy(&data->variants[0], buffer, (length > sizeof(data->variants) ? sizeof(data->variants) : length)); - } + sql->GetBlobData(6, &data->EquippedInstincts); + sql->GetBlobData(7, &data->levels); + sql->GetBlobData(8, &data->instincts); + sql->GetBlobData(9, &data->variants); // TODO: auto level = data->levels[data->MonstrosityId]; @@ -189,6 +166,7 @@ void monstrosity::WriteMonstrosityData(CCharEntity* PChar) return; } + /* // TODO: Ensure there's a row to write to { bool needToCreate = false; @@ -203,8 +181,10 @@ void monstrosity::WriteMonstrosityData(CCharEntity* PChar) sql->Query("INSERT INTO char_monstrosity VALUES (%d, 1, 1, 0, 0, 0, 0, 0, 0, 0);;", PChar->id); } } + */ - const char* Query = "UPDATE char_monstrosity SET " + const char* Query = "REPLACE INTO char_monstrosity SET " + "charid = '%u', " "current_monstrosity_id = '%d', " "current_monstrosity_species = '%d', " "current_monstrosity_name_prefix_1 = '%d', " @@ -213,49 +193,24 @@ void monstrosity::WriteMonstrosityData(CCharEntity* PChar) "equip = '%s', " "levels = '%s', " "instincts = '%s', " - "variants = '%s'" - "WHERE charid = %u;"; + "variants = '%s';"; - // TODO: Make these a template in sql.h - char equipEscaped[sizeof(PChar->m_PMonstrosity->EquippedInstincts) * 2 + 1]; - { - char dataBlob[sizeof(PChar->m_PMonstrosity->EquippedInstincts)]; - std::memcpy(dataBlob, &PChar->m_PMonstrosity->EquippedInstincts[0], sizeof(dataBlob)); - sql->EscapeStringLen(equipEscaped, dataBlob, sizeof(dataBlob)); - } - - char levelsEscaped[sizeof(PChar->m_PMonstrosity->levels) * 2 + 1]; - { - char dataBlob[sizeof(PChar->m_PMonstrosity->levels)]; - std::memcpy(dataBlob, &PChar->m_PMonstrosity->levels[0], sizeof(dataBlob)); - sql->EscapeStringLen(levelsEscaped, dataBlob, sizeof(dataBlob)); - } - - char instinctsEscaped[sizeof(PChar->m_PMonstrosity->instincts) * 2 + 1]; - { - char dataBlob[sizeof(PChar->m_PMonstrosity->instincts)]; - std::memcpy(dataBlob, &PChar->m_PMonstrosity->instincts[0], sizeof(dataBlob)); - sql->EscapeStringLen(instinctsEscaped, dataBlob, sizeof(dataBlob)); - } - - char variantsEscaped[sizeof(PChar->m_PMonstrosity->variants) * 2 + 1]; - { - char dataBlob[sizeof(PChar->m_PMonstrosity->variants)]; - std::memcpy(dataBlob, &PChar->m_PMonstrosity->variants[0], sizeof(dataBlob)); - sql->EscapeStringLen(variantsEscaped, dataBlob, sizeof(dataBlob)); - } + auto equipEscaped = sql->ObjectToBlobString(&PChar->m_PMonstrosity->EquippedInstincts); + auto levelsEscaped = sql->ObjectToBlobString(&PChar->m_PMonstrosity->levels); + auto instinctsEscaped = sql->ObjectToBlobString(&PChar->m_PMonstrosity->instincts); + auto variantsEscaped = sql->ObjectToBlobString(&PChar->m_PMonstrosity->variants); sql->Query(Query, + PChar->id, PChar->m_PMonstrosity->MonstrosityId, PChar->m_PMonstrosity->Species, PChar->m_PMonstrosity->NamePrefix1, PChar->m_PMonstrosity->NamePrefix2, PChar->m_PMonstrosity->CurrentExp, - equipEscaped, - levelsEscaped, - instinctsEscaped, - variantsEscaped, - PChar->id); + equipEscaped.c_str(), + levelsEscaped.c_str(), + instinctsEscaped.c_str(), + variantsEscaped.c_str()); } void monstrosity::HandleZoneIn(CCharEntity* PChar) From ed79b62d377c0370dff977e817e1f7ad7ecd0fc7 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Sat, 7 Oct 2023 10:00:31 +0100 Subject: [PATCH 043/103] Add char_monstrosity to triggers --- sql/triggers.sql | 1 + 1 file changed, 1 insertion(+) diff --git a/sql/triggers.sql b/sql/triggers.sql index 6d166cce4eb..b0d61a485d9 100644 --- a/sql/triggers.sql +++ b/sql/triggers.sql @@ -64,6 +64,7 @@ BEGIN DELETE FROM `char_job_points` WHERE `charid` = OLD.charid; DELETE FROM `char_look` WHERE `charid` = OLD.charid; DELETE FROM `char_merit` WHERE `charid` = OLD.charid; + DELETE FROM `char_monstrosity` WHERE `charid` = OLD.charid; DELETE FROM `char_pet` WHERE `charid` = OLD.charid; DELETE FROM `char_points` WHERE `charid` = OLD.charid; DELETE FROM `char_profile` WHERE `charid` = OLD.charid; From 5dd726c5f48f3f6c1fc6c824abbfdc4751a3bd09 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Sat, 7 Oct 2023 10:12:47 +0100 Subject: [PATCH 044/103] Make NamePrefix2 change correctly --- src/map/monstrosity.cpp | 36 ++++++++++++++---------------------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/src/map/monstrosity.cpp b/src/map/monstrosity.cpp index 7276aca35fa..dc59fa84553 100644 --- a/src/map/monstrosity.cpp +++ b/src/map/monstrosity.cpp @@ -166,23 +166,6 @@ void monstrosity::WriteMonstrosityData(CCharEntity* PChar) return; } - /* - // TODO: Ensure there's a row to write to - { - bool needToCreate = false; - int32 ret = sql->Query("SELECT * FROM char_monstrosity WHERE charid = %d;", PChar->id); - if (ret != SQL_ERROR && sql->NumRows() == 0) - { - needToCreate = true; - } - - if (needToCreate) - { - sql->Query("INSERT INTO char_monstrosity VALUES (%d, 1, 1, 0, 0, 0, 0, 0, 0, 0);;", PChar->id); - } - } - */ - const char* Query = "REPLACE INTO char_monstrosity SET " "charid = '%u', " "current_monstrosity_id = '%d', " @@ -238,6 +221,13 @@ void monstrosity::HandleZoneIn(CCharEntity* PChar) } } + if (PChar->loc.zone->GetID() != ZONE_FERETORY) + { + // TODO: Add Gestation effect + } + + // TODO: Lua binding + PChar->updatemask |= UPDATE_LOOK; } } @@ -285,7 +275,9 @@ void monstrosity::SendFullMonstrosityUpdate(CCharEntity* PChar) void monstrosity::HandleMonsterSkillActionPacket(CCharEntity* PChar, CBasicPacket& data) { - // TODO: + // TODO + + // TODO: Lua binding } void monstrosity::HandleEquipChangePacket(CCharEntity* PChar, CBasicPacket& data) @@ -309,9 +301,6 @@ void monstrosity::HandleEquipChangePacket(CCharEntity* PChar, CBasicPacket& data auto data = gMonstrositySpeciesMap[newSpecies]; - // For debugging and data entry - // ShowInfo(fmt::format("Species: {}: {}", newSpecies, data.name)); - PChar->m_PMonstrosity->Species = newSpecies; PChar->m_PMonstrosity->MonstrosityId = data.monstrosityId; @@ -385,9 +374,12 @@ void monstrosity::HandleEquipChangePacket(CCharEntity* PChar, CBasicPacket& data } } } - else if (flag == 0x08) // Name Change + else if (flag == 0x08) // Name Change 1 { PChar->m_PMonstrosity->NamePrefix1 = data.ref(0x28); + } + else if (flag == 0x10) // Name Change 2 + { PChar->m_PMonstrosity->NamePrefix2 = data.ref(0x29); } From cbf145a23c269779d47f19d2ddba34c78aa6e353 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Sat, 7 Oct 2023 11:27:18 +0100 Subject: [PATCH 045/103] Add proxy JOB_MON and notes --- src/map/entities/battleentity.h | 6 +++++- src/map/packets/char_job_extra.cpp | 10 +--------- src/map/packets/char_jobs.cpp | 26 ++++++-------------------- src/map/packets/monipulator1.cpp | 2 +- src/map/packets/monipulator2.cpp | 2 +- 5 files changed, 14 insertions(+), 32 deletions(-) diff --git a/src/map/entities/battleentity.h b/src/map/entities/battleentity.h index aaed5b16ef0..e044956a0e3 100644 --- a/src/map/entities/battleentity.h +++ b/src/map/entities/battleentity.h @@ -92,9 +92,13 @@ enum JOBTYPE JOB_DNC = 19, JOB_SCH = 20, JOB_GEO = 21, - JOB_RUN = 22 + JOB_RUN = 22, + JOB_MON = 23, // See note below (in battleentity.h) }; +// NOTE: Comparisons to this are specified with operator< (less than), so that JOB_MON is never reached. +// JOB_MON is not a full job, but it is used in a couple of places while Monstrosity is in use. +// It doesn't have its own (direct) stat tiers, merits, traits, etc. so we don't want to iterate to it. #define MAX_JOBTYPE 23 enum SKILLTYPE diff --git a/src/map/packets/char_job_extra.cpp b/src/map/packets/char_job_extra.cpp index abdd703b63d..ff13386b814 100644 --- a/src/map/packets/char_job_extra.cpp +++ b/src/map/packets/char_job_extra.cpp @@ -31,17 +31,12 @@ #include "merit.h" #include "monstrosity.h" -namespace -{ - uint8 JOB_MON = 23; -} // namespace - CCharJobExtraPacket::CCharJobExtraPacket(CCharEntity* PChar, bool mjob) { this->setType(0x44); this->setSize(0xA0); - uint8 job = JOB_NON; + JOBTYPE job = JOB_NON; if (mjob) { @@ -146,8 +141,5 @@ CCharJobExtraPacket::CCharJobExtraPacket(CCharEntity* PChar, bool mjob) { ref(0x0C + (idx * 2)) = PChar->m_PMonstrosity->EquippedInstincts[idx]; } - - // 0x24: Name1? - // 0x25: Name2? } } diff --git a/src/map/packets/char_jobs.cpp b/src/map/packets/char_jobs.cpp index 5ba7794be79..bbbbb045181 100644 --- a/src/map/packets/char_jobs.cpp +++ b/src/map/packets/char_jobs.cpp @@ -60,25 +60,11 @@ CCharJobsPacket::CCharJobsPacket(CCharEntity* PChar) if (PChar->m_PMonstrosity) { - ref(0x08) = 0x17; // JOB_MON - ref(0x0B) = 0x17; // JOB_MON - - ref(0x10) = 0x01; - /* - ref(0x2E) = 0x07; - ref(0x30) = 0x07; - ref(0x32) = 0x0A; - ref(0x34) = 0x07; - ref(0x36) = 0x04; - ref(0x38) = 0x07; - ref(0x3A) = 0x04; - - ref(0x48) = 0x01; - - ref(0x5F) = 0x10; // MON level - ref(0x64) = 0x03; - ref(0x66) = 0x02; - ref(0x69) = 0x20; - */ + ref(0x08) = static_cast(JOB_MON); + ref(0x0B) = static_cast(JOB_MON); + + // ref(0x10) = 0x01; // ? + + // ref(0x5F) = 0x10; // MON level ? } } diff --git a/src/map/packets/monipulator1.cpp b/src/map/packets/monipulator1.cpp index bde6947edf8..51199a3f103 100644 --- a/src/map/packets/monipulator1.cpp +++ b/src/map/packets/monipulator1.cpp @@ -39,7 +39,7 @@ CMonipulatorPacket1::CMonipulatorPacket1(CCharEntity* PChar) ref(0x04) = 0x03; // Update Type ref(0x06) = 0xD8; // Variable Data Size - ref(0x08) = PChar->m_PMonstrosity->Species; // 0x2412; //0xFCFE; // Species? Also seen 0x3F1F + ref(0x08) = PChar->m_PMonstrosity->Species; ref(0x0A) = PChar->m_PMonstrosity->Flags; ref(0x0C) = 0; // Monstrosity Rank (0 = Mon, 1 = NM, 2 = HNM) diff --git a/src/map/packets/monipulator2.cpp b/src/map/packets/monipulator2.cpp index 26028f5d291..a3cef1ebf78 100644 --- a/src/map/packets/monipulator2.cpp +++ b/src/map/packets/monipulator2.cpp @@ -39,7 +39,7 @@ CMonipulatorPacket2::CMonipulatorPacket2(CCharEntity* PChar) // TODO: What are these? std::array packet2 = { 0x04, 0x00, 0xB0 }; - std::memcpy(data + (0x04), &packet2, sizeof(packet2)); + std::memcpy(data + 0x04, &packet2, sizeof(packet2)); // NOTE: SE added these after-the-fact, so they're not sent in Monipulator1 and they're at the end of the array! // Slime Level From 9d6f75aaa09f8ad606d8c1bae7279dacc6bab267 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Sat, 7 Oct 2023 11:27:30 +0100 Subject: [PATCH 046/103] Fill out addallmonstrosity command --- scripts/commands/addallmonstrosity.lua | 47 +------------- scripts/globals/monstrosity.lua | 87 +++++++++++++++++++++++--- 2 files changed, 81 insertions(+), 53 deletions(-) diff --git a/scripts/commands/addallmonstrosity.lua b/scripts/commands/addallmonstrosity.lua index fc3f19bebf3..6c4aa630822 100644 --- a/scripts/commands/addallmonstrosity.lua +++ b/scripts/commands/addallmonstrosity.lua @@ -28,52 +28,7 @@ commandObj.onTrigger = function(player, target) end end - local data = - { - levels = - { - [xi.monstrosity.species.RABBIT] = 1, - [xi.monstrosity.species.MANDRAGORA] = 1, - [xi.monstrosity.species.LIZARD] = 1, - }, - instincts = -- std::array instincts - { - [00] = 0xFF, - [01] = 0xFF, - [02] = 0xFF, - [03] = 0xFF, - [04] = 0xFF, - [05] = 0xFF, - [06] = 0xFF, - [07] = 0xFF, - [08] = 0xFF, - [09] = 0xFF, - [10] = 0xFF, - [11] = 0xFF, - [12] = 0xFF, - [13] = 0xFF, - [14] = 0xFF, - [15] = 0xFF, - [16] = 0xFF, - [17] = 0xFF, - [18] = 0xFF, - - [20] = 0xFF, - [21] = 0xFF, - [22] = 0xFF, - [23] = 0xFF, - - [62] = 0xFF, - [63] = 0xFF, - }, - variants = - { - [00] = 0xFF, - [01] = 0xFF, - } - } - - targ:setMonstrosity(data) + xi.monstrosity.unlockAll(targ) player:PrintToPlayer(string.format('%s now has all monstrosity data.', targ:getName())) end diff --git a/scripts/globals/monstrosity.lua b/scripts/globals/monstrosity.lua index e43ed0116e2..c78afabe41e 100644 --- a/scripts/globals/monstrosity.lua +++ b/scripts/globals/monstrosity.lua @@ -339,19 +339,92 @@ xi.monstrosity.unlockStartingMONs = function(player, choice) { monstrosityId = choice, species = choice, - levels = + } + + player:setMonstrosity(data) +end + +----------------------------------- +-- Debug +----------------------------------- + +xi.monstrosity.unlockAll = function(player) + local data = + { + -- 1 byte per entry, mapped out to species table + -- (0 - 127) + levels = { - [xi.monstrosity.species.RABBIT] = 1, - [xi.monstrosity.species.MANDRAGORA] = 1, - [xi.monstrosity.species.LIZARD] = 1, }, + + -- Bitfield (0 - 63) instincts = { - [20] = 0x1F, -- Unlock all first tier player race instincts - } + }, + + -- Bitfield (0 - 31) + variants = + { + }, } - player:setMonstrosity(data) + -- Set all levels to 99 + for _, val in pairs(xi.monstrosity.species) do + data.levels[val] = 99 + end + + -- Instincts by MON level + -- NOTE: Since this is a bitfield, it's zero-indexed! + for _, val in pairs(xi.monstrosity.species) do + local speciesKey = val + local speciesLevel = data.levels[val] + local byteOffset = math.floor(speciesKey / 4) + local unlockAmount = math.floor(speciesLevel / 30) + local shiftAmount = (speciesKey * 2) % 8 + + -- Special case for writing Slime & Spriggan data at the end of the 64-byte array + if byteOffset == 31 then + byteOffset = 63 + end + + if byteOffset < 64 then + data.instincts[byteOffset] = bit.bor(data.instincts[byteOffset] or 0, bit.lshift(unlockAmount, shiftAmount)) + else + print("byteOffset out of range") + end + end + + -- Instincts (Purchasable) + for _, val in pairs(xi.monstrosity.purchasableInstincts) do + local byteOffset = 20 + math.floor(val / 8) + local shiftAmount = val % 8 + + -- print(val, byteOffset, shiftAmount) + + if byteOffset >= 20 and byteOffset < 24 then + data.instincts[byteOffset] = bit.bor(data.instincts[byteOffset] or 0, bit.lshift(0x01, shiftAmount)) + else + print("byteOffset out of range") + end + end + + -- Variants + -- Force unlock all + for _, val in pairs(xi.monstrosity.variants) do + local speciesKey = val + local byteOffset = math.floor(speciesKey / 8) + local shiftAmount = speciesKey % 8 + + -- print(speciesKey, byteOffset, shiftAmount) + + if byteOffset < 32 then + data.variants[byteOffset] = bit.bor(data.variants[byteOffset] or 0, bit.lshift(0x01, shiftAmount)) + else + print("byteOffset out of range") + end + end + + player:setMonstrosity(data); end ----------------------------------- From 31d5273119412067c306216c89f1785fba58af79 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Sat, 7 Oct 2023 11:33:58 +0100 Subject: [PATCH 047/103] Don't show other players in Feretory --- src/map/zone_entities.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/map/zone_entities.cpp b/src/map/zone_entities.cpp index 222cb272ddd..501ce5b6f5a 100644 --- a/src/map/zone_entities.cpp +++ b/src/map/zone_entities.cpp @@ -740,6 +740,19 @@ void CZoneEntities::SpawnPCs(CCharEntity* PChar) continue; } + // TODO: This is a temporary fix so that Feretory _seems_ like a solo zone. + // We need a better solution for this that properly supports: + // - Shared moghouse (both floors) + // - Mog Garden + // - Feretory + // and the NPCs that exist per-player in those zones (Garden, Music Items in MH, etc.) + // Despawn character if it's a hidden GM, is in a different mog house, or if player is in a conflict while other is not, or too far up/down + if (PChar->loc.zone->GetID() == ZONE_FERETORY) + { + toRemove.emplace_back(pc); + continue; + } + // Despawn character if it's currently spawned and is far away float charDistance = distance(PChar->loc.p, pc->loc.p); if (charDistance >= CHARACTER_DESPAWN_DISTANCE) From 2ec513d8c793650756720e1b66575cf53e08e0ed Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Sat, 7 Oct 2023 11:45:44 +0100 Subject: [PATCH 048/103] Clean up monstrosity global --- scripts/globals/monstrosity.lua | 38 ++++++++++++++++----------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/scripts/globals/monstrosity.lua b/scripts/globals/monstrosity.lua index c78afabe41e..64f81935269 100644 --- a/scripts/globals/monstrosity.lua +++ b/scripts/globals/monstrosity.lua @@ -349,6 +349,13 @@ end ----------------------------------- xi.monstrosity.unlockAll = function(player) + -- Complete quest + local logId = xi.quest.log_id.OTHER_AREAS + player:completeQuest(logId, xi.quest.id[xi.quest.area[logId]].MONSTROSITY) + + -- Add Monstrosity key item + player:addKeyItem(xi.keyItem.RING_OF_SUPERNAL_DISJUNCTION) + local data = { -- 1 byte per entry, mapped out to species table @@ -399,8 +406,6 @@ xi.monstrosity.unlockAll = function(player) local byteOffset = 20 + math.floor(val / 8) local shiftAmount = val % 8 - -- print(val, byteOffset, shiftAmount) - if byteOffset >= 20 and byteOffset < 24 then data.instincts[byteOffset] = bit.bor(data.instincts[byteOffset] or 0, bit.lshift(0x01, shiftAmount)) else @@ -415,8 +420,6 @@ xi.monstrosity.unlockAll = function(player) local byteOffset = math.floor(speciesKey / 8) local shiftAmount = speciesKey % 8 - -- print(speciesKey, byteOffset, shiftAmount) - if byteOffset < 32 then data.variants[byteOffset] = bit.bor(data.variants[byteOffset] or 0, bit.lshift(0x01, shiftAmount)) else @@ -424,6 +427,7 @@ xi.monstrosity.unlockAll = function(player) end end + -- Set data player:setMonstrosity(data); end @@ -448,12 +452,12 @@ xi.monstrosity.odysseanPassageOnTrigger = function(player, npc) end xi.monstrosity.odysseanPassageOnEventUpdate = function(player, csid, option, npc) - print('update', csid, option) + -- print('update', csid, option) player:updateEvent(0, 0, 0, 0, 1, 0, 0, 0) end xi.monstrosity.odysseanPassageOnEventFinish = function(player, csid, option, npc) - print('finish', csid, option) + -- print('finish', csid, option) -- Option 1: Leave & Teleport to last city zone -- Option 529: Teleport to Al'Taieu end @@ -463,11 +467,6 @@ end ----------------------------------- xi.monstrosity.feretoryOnZoneIn = function(player, prevZone) - -- TODO: Handle xi.settings.main.ENABLE_MONSTROSITY - - -- TODO: Rabbit, Mandy, and Lizard are all unlocked to begin with, but you - -- : start as whatever matching item you traded - local cs = -1 player:setPos(-358.000, -3.400, -440.00, 63) @@ -489,16 +488,15 @@ xi.monstrosity.aengusOnTrade = function(player, npc, trade) end xi.monstrosity.aengusOnTrigger = function(player, npc) - -- Event 13 (As Lizard: 0, 0, 2, 0, 2, 90, 0, 0) player:startEvent(13, 0, 0, 2, 0, 2, 90, 0, 0) end xi.monstrosity.aengusOnEventUpdate = function(player, csid, option, npc) - print('update', csid, option) + -- print('update', csid, option) end xi.monstrosity.aengusOnEventFinish = function(player, csid, option, npc) - print('finish', csid, option) + -- print('finish', csid, option) if csid == 13 and option == 1 then -- Selected: Enter Belligerency end @@ -516,7 +514,7 @@ xi.monstrosity.teyrnonOnTrigger = function(player, npc) end xi.monstrosity.teyrnonOnEventUpdate = function(player, csid, option, npc) - print('update', csid, option) + -- print('update', csid, option) if csid == 7 and option == 0 then -- Monsters Menu player:updateEvent(0, 0, 0, 0, 0, 0, 0, 0) elseif csid == 7 and option == 1 then -- Instinct menu @@ -525,7 +523,7 @@ xi.monstrosity.teyrnonOnEventUpdate = function(player, csid, option, npc) end xi.monstrosity.teyrnonOnEventFinish = function(player, csid, option, npc) - print('finish', csid, option) + -- print('finish', csid, option) -- Support Menu: -- option 3: Dedication 1 -- option 259: Dedication 2 @@ -548,11 +546,11 @@ xi.monstrosity.maccusOnTrigger = function(player, npc) end xi.monstrosity.maccusOnEventUpdate = function(player, csid, option, npc) - print('update', csid, option) + -- print('update', csid, option) end xi.monstrosity.maccusOnEventFinish = function(player, csid, option, npc) - print('finish', csid, option) + -- print('finish', csid, option) end ----------------------------------- @@ -567,11 +565,11 @@ xi.monstrosity.suibhneOnTrigger = function(player, npc) end xi.monstrosity.suibhneOnEventUpdate = function(player, csid, option, npc) - print('update', csid, option) + -- print('update', csid, option) end xi.monstrosity.suibhneOnEventFinish = function(player, csid, option, npc) - print('finish', csid, option) + -- print('finish', csid, option) -- Answers: -- 1) 4. Teyrnon -- 2) 3. Suibhne From 7d9c84243ddf8950f04b462f4848c1aa137c5fc7 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Sat, 7 Oct 2023 12:23:27 +0100 Subject: [PATCH 049/103] First pass at Gestation effect --- scripts/effects/gestation.lua | 30 ++++++++++++++++++++++++++++++ src/map/monstrosity.cpp | 24 +++++++++++++++++++++--- 2 files changed, 51 insertions(+), 3 deletions(-) create mode 100644 scripts/effects/gestation.lua diff --git a/scripts/effects/gestation.lua b/scripts/effects/gestation.lua new file mode 100644 index 00000000000..ebc7ca6aab4 --- /dev/null +++ b/scripts/effects/gestation.lua @@ -0,0 +1,30 @@ +----------------------------------- +-- xi.effect.GESTATION +-- https://ffxiclopedia.fandom.com/wiki/Gestation +-- +-- Effects +-- Gestation is a combination of the following: +-- - Completely undetectable, even to True Sight and True Hearing monsters +-- - Quickening +-- - Unable take any actions (attacks, spells, job abilities, etc.) +-- +-- Duration +-- - Outside Belligerency: 18 hours +-- - During Belligerency: 1 minute +-- +-- How to remove the effect +-- - Wait for the effect to wear off +-- - Remove manually +----------------------------------- +local effectObject = {} + +effectObject.onEffectGain = function(target, effect) +end + +effectObject.onEffectTick = function(target, effect) +end + +effectObject.onEffectLose = function(target, effect) +end + +return effectObject diff --git a/src/map/monstrosity.cpp b/src/map/monstrosity.cpp index dc59fa84553..a9eb6dbfad7 100644 --- a/src/map/monstrosity.cpp +++ b/src/map/monstrosity.cpp @@ -36,6 +36,9 @@ #include "utils/charutils.h" #include "utils/zoneutils.h" +#include "status_effect.h" +#include "status_effect_container.h" + extern std::unique_ptr sql; struct MonstrositySpeciesRow @@ -223,10 +226,24 @@ void monstrosity::HandleZoneIn(CCharEntity* PChar) if (PChar->loc.zone->GetID() != ZONE_FERETORY) { - // TODO: Add Gestation effect - } + CStatusEffect* PEffect = new CStatusEffect(EFFECT::EFFECT_GESTATION, EFFECT::EFFECT_GESTATION, 0, 0, 0); + + // TODO: You cannot attack while you're in Gest., you have to click it off - // TODO: Lua binding + // TODO: Move these into the db + PEffect->AddEffectFlag(EFFECTFLAG_INVISIBLE); + PEffect->AddEffectFlag(EFFECTFLAG_DEATH); + PEffect->AddEffectFlag(EFFECTFLAG_ATTACK); + PEffect->AddEffectFlag(EFFECTFLAG_MAGIC_BEGIN); + PEffect->AddEffectFlag(EFFECTFLAG_DETECTABLE); + PEffect->AddEffectFlag(EFFECTFLAG_LOGOUT); + PEffect->AddEffectFlag(EFFECTFLAG_ON_ZONE); + + // NOTE: It DOES say the effect wears off + // PEffect->AddEffectFlag(EFFECTFLAG_NO_LOSS_MESSAGE); + + PChar->StatusEffectContainer->AddStatusEffect(PEffect, true); + } PChar->updatemask |= UPDATE_LOOK; } @@ -428,6 +445,7 @@ void monstrosity::HandleDeathMenu(CCharEntity* PChar, uint8 type) if (type == 1) { // Return to Feretory + // TODO: This should return you to the Odyssean Passage you entered with, not Feretory! PChar->loc.destination = ZONE_FERETORY; } else if (type == 2) From df015fd1f1a871355cf7a3463b557edaf4cecf62 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Sat, 7 Oct 2023 12:27:35 +0100 Subject: [PATCH 050/103] Add note about zone_in on retail --- src/map/packets/zone_in.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/map/packets/zone_in.cpp b/src/map/packets/zone_in.cpp index 580e1779bb8..0ece18a6061 100644 --- a/src/map/packets/zone_in.cpp +++ b/src/map/packets/zone_in.cpp @@ -259,6 +259,9 @@ CZoneInPacket::CZoneInPacket(CCharEntity* PChar, const EventInfo* currentEvent) if (PChar->m_PMonstrosity != nullptr) { + // NOTE: When you log in on retail, your model is correct immediately, but the name "pops in" a tick + // or two later. + // TODO: These make the spawn in cleaner, but then the name doesn't work correctly // ref(0x04) = PChar->m_PMonstrosity->Look; // ref(0x14) = 0xFFFF; From 2ad7460773cac738ee33ec49dea15f3399991bbd Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Sat, 7 Oct 2023 12:35:01 +0100 Subject: [PATCH 051/103] Fix issue with name visibility after Gestation --- src/map/packets/char.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/map/packets/char.cpp b/src/map/packets/char.cpp index 1bec30a5667..d60069bc2af 100644 --- a/src/map/packets/char.cpp +++ b/src/map/packets/char.cpp @@ -178,19 +178,19 @@ void CCharPacket::updateWith(CCharEntity* PChar, ENTITYUPDATE type, uint8 update ref(0x54) = look->main + 0x6000; ref(0x56) = look->sub + 0x7000; ref(0x58) = look->ranged + 0x8000; - - if (PChar->m_PMonstrosity != nullptr) - { - ref(0x3E) = monstrosity::GetPackedMonstrosityName(PChar); - ref(0x48) = PChar->m_PMonstrosity->Look; - ref(0x58) = 0xFFFF; - } } if (updatemask & UPDATE_NAME) { memcpy(data + (0x5A), PChar->GetName().c_str(), PChar->GetName().size()); } + + if (PChar->m_PMonstrosity != nullptr && (updatemask & UPDATE_HP || updatemask & UPDATE_LOOK)) + { + ref(0x3E) = monstrosity::GetPackedMonstrosityName(PChar); + ref(0x48) = PChar->m_PMonstrosity->Look; + ref(0x58) = 0xFFFF; + } } break; default: From e12d91e6f4891785c275a6fe48e0a09b2dfe7776 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Sat, 7 Oct 2023 12:53:28 +0100 Subject: [PATCH 052/103] Fix issues with zone_in model appearance --- src/map/packets/zone_in.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/map/packets/zone_in.cpp b/src/map/packets/zone_in.cpp index 0ece18a6061..cd36fb81d75 100644 --- a/src/map/packets/zone_in.cpp +++ b/src/map/packets/zone_in.cpp @@ -259,14 +259,13 @@ CZoneInPacket::CZoneInPacket(CCharEntity* PChar, const EventInfo* currentEvent) if (PChar->m_PMonstrosity != nullptr) { - // NOTE: When you log in on retail, your model is correct immediately, but the name "pops in" a tick - // or two later. - - // TODO: These make the spawn in cleaner, but then the name doesn't work correctly - // ref(0x04) = PChar->m_PMonstrosity->Look; - // ref(0x14) = 0xFFFF; + // look_t data from above + ref(0x44) = PChar->m_PMonstrosity->Look; + ref(0x54) = 0xFFFF; // Enable Monstrosity menu options - ref(0x7E) = 0x1F; + ref(0x7E) = 0x1F; + + ref(0x98) = monstrosity::GetPackedMonstrosityName(PChar); } } From d9e6870fd0b278e7059ee82bda8483d34324d6f1 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Sat, 7 Oct 2023 12:57:48 +0100 Subject: [PATCH 053/103] Add Gestation timer --- src/map/monstrosity.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/map/monstrosity.cpp b/src/map/monstrosity.cpp index a9eb6dbfad7..3c9bb0d23c0 100644 --- a/src/map/monstrosity.cpp +++ b/src/map/monstrosity.cpp @@ -226,7 +226,8 @@ void monstrosity::HandleZoneIn(CCharEntity* PChar) if (PChar->loc.zone->GetID() != ZONE_FERETORY) { - CStatusEffect* PEffect = new CStatusEffect(EFFECT::EFFECT_GESTATION, EFFECT::EFFECT_GESTATION, 0, 0, 0); + // TODO: If belligerency, timer is 60s + CStatusEffect* PEffect = new CStatusEffect(EFFECT::EFFECT_GESTATION, EFFECT::EFFECT_GESTATION, 0, 0, 64800); // 18 hours // TODO: You cannot attack while you're in Gest., you have to click it off @@ -236,9 +237,11 @@ void monstrosity::HandleZoneIn(CCharEntity* PChar) PEffect->AddEffectFlag(EFFECTFLAG_ATTACK); PEffect->AddEffectFlag(EFFECTFLAG_MAGIC_BEGIN); PEffect->AddEffectFlag(EFFECTFLAG_DETECTABLE); - PEffect->AddEffectFlag(EFFECTFLAG_LOGOUT); PEffect->AddEffectFlag(EFFECTFLAG_ON_ZONE); + // TODO: Does the timer survive logout, does it tick while offline, does it reset? + // PEffect->AddEffectFlag(EFFECTFLAG_LOGOUT); + // NOTE: It DOES say the effect wears off // PEffect->AddEffectFlag(EFFECTFLAG_NO_LOSS_MESSAGE); From 6b43d655821e9b659572b15f0028ef49b1794f54 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Sat, 7 Oct 2023 14:27:12 +0100 Subject: [PATCH 054/103] Block actions while in gestation --- src/map/packet_system.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/map/packet_system.cpp b/src/map/packet_system.cpp index 016543dca09..bca5a8e7cf1 100644 --- a/src/map/packet_system.cpp +++ b/src/map/packet_system.cpp @@ -795,14 +795,21 @@ void SmallPacket0x01A(map_session_data_t* const PSession, CCharEntity* const PCh uint16 TargID = data.ref(0x08); uint8 action = data.ref(0x0A); - position_t actionOffset = { + position_t actionOffset = + { data.ref(0x10), data.ref(0x14), data.ref(0x18), - 0, // packet only contains x/y/z - 0, // + 0, // moving (packet only contains x/y/z) + 0, // rotation (packet only contains x/y/z) }; + // Monstrosity: Can't really do anything while under Gestation until you click it off + if (PChar->StatusEffectContainer->HasStatusEffect(EFFECT_GESTATION)) + { + return; + } + constexpr auto actionToStr = [](uint8 actionIn) { switch (actionIn) From 42e3698fc74261740db1f6b876f554f455865788 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Sat, 7 Oct 2023 15:39:59 +0100 Subject: [PATCH 055/103] Stop MON triggering NPCs --- src/map/packet_system.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/map/packet_system.cpp b/src/map/packet_system.cpp index bca5a8e7cf1..cbc6ae14f4e 100644 --- a/src/map/packet_system.cpp +++ b/src/map/packet_system.cpp @@ -885,6 +885,11 @@ void SmallPacket0x01A(map_session_data_t* const PSession, CCharEntity* const PCh return; } + if (PChar->m_PMonstrosity != nullptr) + { + return; + } + CBaseEntity* PNpc = nullptr; PNpc = PChar->GetEntity(TargID, TYPE_NPC | TYPE_MOB); @@ -7249,8 +7254,12 @@ void SmallPacket0x100(map_session_data_t* const PSession, CCharEntity* const PCh PChar->StatusEffectContainer->DelStatusEffectsByFlag(EFFECTFLAG_DISPELABLE | EFFECTFLAG_ROLL | EFFECTFLAG_ON_JOBCHANGE); + // clang-format off PChar->ForParty([](CBattleEntity* PMember) - { ((CCharEntity*)PMember)->PLatentEffectContainer->CheckLatentsPartyJobs(); }); + { + ((CCharEntity*)PMember)->PLatentEffectContainer->CheckLatentsPartyJobs(); + }); + // clang-format on PChar->UpdateHealth(); From 97c32fb11ebe53f46382f2a89a75658c39430ba7 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Sat, 7 Oct 2023 15:46:37 +0100 Subject: [PATCH 056/103] Promote MON to be a real job, add Relinquish ability --- scripts/actions/abilities/relinquish.lua | 15 +++++++++++ scripts/enum/job.lua | 3 ++- sql/abilities.sql | 2 +- src/map/entities/battleentity.h | 7 ++--- src/map/monstrosity.cpp | 1 + src/map/utils/battleutils.cpp | 7 ++--- src/map/utils/charutils.cpp | 33 +++++++++++++++++------- 7 files changed, 49 insertions(+), 19 deletions(-) create mode 100644 scripts/actions/abilities/relinquish.lua diff --git a/scripts/actions/abilities/relinquish.lua b/scripts/actions/abilities/relinquish.lua new file mode 100644 index 00000000000..025887012ac --- /dev/null +++ b/scripts/actions/abilities/relinquish.lua @@ -0,0 +1,15 @@ +----------------------------------- +-- Ability: Relinquish +----------------------------------- +local abilityObject = {} + +abilityObject.onAbilityCheck = function(player, target, ability) + -- TODO: Block if being attacked + return 0, 0 +end + +abilityObject.onUseAbility = function(player, target, ability) + player:setPos(0, 0, 0, 0, xi.zone.FERETORY) +end + +return abilityObject diff --git a/scripts/enum/job.lua b/scripts/enum/job.lua index 6d38033cabd..b68e4f22738 100644 --- a/scripts/enum/job.lua +++ b/scripts/enum/job.lua @@ -25,6 +25,7 @@ xi.job = SCH = 20, GEO = 21, RUN = 22, + MON = 23, } -xi.MAX_JOB_TYPE = 23 +xi.MAX_JOB_TYPE = 24 diff --git a/sql/abilities.sql b/sql/abilities.sql index d7e07bce7cd..72563987e4e 100644 --- a/sql/abilities.sql +++ b/sql/abilities.sql @@ -386,7 +386,7 @@ INSERT INTO `abilities` VALUES (378,'odyllic_subterfuge',22,96,4,3600,131,0,0,27 INSERT INTO `abilities` VALUES (379,'ward',22,1,1,0,142,0,0,0,2000,0,6,0.0,0,0,0,0,0,'SOA'); INSERT INTO `abilities` VALUES (380,'effusion',22,1,1,0,143,0,0,0,2000,0,6,0.0,0,0,0,0,0,'SOA'); INSERT INTO `abilities` VALUES (381,'chocobo_jig_ii',19,70,1,60,218,126,0,13,2000,0,14,10.0,1,1,300,0,0,'SOA'); --- INSERT INTO `abilities` VALUES (382,'relinquish',23,1,1,60,253,0,0,0,0,0,6,0.0,0,0,0,0,0,NULL); +INSERT INTO `abilities` VALUES (382,'relinquish',23,1,1,60,253,0,0,0,0,0,6,0.0,0,0,0,0,0,NULL); INSERT INTO `abilities` VALUES (383,'vivacious_pulse',22,65,1,60,242,102,0,327,2000,0,6,0.0,0,0,0,0,0,'SOA'); INSERT INTO `abilities` VALUES (384,'contradance',19,50,1,300,229,0,0,329,2000,0,6,0.0,0,0,0,0,0,NULL); -- check animation INSERT INTO `abilities` VALUES (385,'apogee',15,70,1,180,108,100,0,94,2000,0,6,0.0,0,1,80,0,0,'SOA'); diff --git a/src/map/entities/battleentity.h b/src/map/entities/battleentity.h index e044956a0e3..e673c9e1a75 100644 --- a/src/map/entities/battleentity.h +++ b/src/map/entities/battleentity.h @@ -93,13 +93,10 @@ enum JOBTYPE JOB_SCH = 20, JOB_GEO = 21, JOB_RUN = 22, - JOB_MON = 23, // See note below (in battleentity.h) + JOB_MON = 23, // NOTE: MON is not a full job }; -// NOTE: Comparisons to this are specified with operator< (less than), so that JOB_MON is never reached. -// JOB_MON is not a full job, but it is used in a couple of places while Monstrosity is in use. -// It doesn't have its own (direct) stat tiers, merits, traits, etc. so we don't want to iterate to it. -#define MAX_JOBTYPE 23 +#define MAX_JOBTYPE 24 enum SKILLTYPE { diff --git a/src/map/monstrosity.cpp b/src/map/monstrosity.cpp index 3c9bb0d23c0..a69d38a0e1a 100644 --- a/src/map/monstrosity.cpp +++ b/src/map/monstrosity.cpp @@ -26,6 +26,7 @@ #include "entities/charentity.h" +#include "packets/char_abilities.h" #include "packets/char_appearance.h" #include "packets/char_job_extra.h" #include "packets/char_jobs.h" diff --git a/src/map/utils/battleutils.cpp b/src/map/utils/battleutils.cpp index d16c7313506..55a3d39d4ed 100644 --- a/src/map/utils/battleutils.cpp +++ b/src/map/utils/battleutils.cpp @@ -125,13 +125,14 @@ namespace battleutils ret = sql->Query(fmtQuery); + // NOTE: Skip over Monstrosity, they re-use other jobs ranks if (ret != SQL_ERROR && sql->NumRows() != 0) { - for (uint32 x = 0; x < MAX_SKILLTYPE && sql->NextRow() == SQL_SUCCESS; ++x) + for (uint32 x = 0; x < JOB_MON && sql->NextRow() == SQL_SUCCESS; ++x) { - auto SkillID = std::clamp(sql->GetIntData(0), 0, MAX_SKILLTYPE - 1); + auto SkillID = std::clamp(sql->GetIntData(0), 0, JOB_MON - 1); - for (uint32 y = 1; y < MAX_JOBTYPE; ++y) + for (uint32 y = 1; y < JOB_MON; ++y) { g_SkillRanks[SkillID][y] = std::clamp(sql->GetIntData(y), 0, 11); } diff --git a/src/map/utils/charutils.cpp b/src/map/utils/charutils.cpp index d4d2e14ad2b..adb5b8e4d98 100644 --- a/src/map/utils/charutils.cpp +++ b/src/map/utils/charutils.cpp @@ -146,6 +146,15 @@ namespace charutils JOBTYPE sjob = PChar->GetSJob(); MERIT_TYPE statMerit[] = { MERIT_STR, MERIT_DEX, MERIT_VIT, MERIT_AGI, MERIT_INT, MERIT_MND, MERIT_CHR }; + // NOTE: Monstrosity (MON) is treated as its own job, but each species is it's own + // : combination of main/sub job for stats, traits and abilities. + if (mjob == JOB_MON || sjob == JOB_MON) + { + // TODO: Look up species jobs, hardcoded as WAR for now + mjob = JOB_WAR; + sjob = JOB_WAR; + } + uint8 race = 0; // Hume switch (PChar->look.race) @@ -2926,19 +2935,15 @@ namespace charutils void BuildingCharAbilityTable(CCharEntity* PChar) { - std::vector AbilitiesList; - if (PChar == nullptr) { ShowWarning("charutils::BuildingCharAbilityTable() - PChar was null."); return; } - memset(&PChar->m_Abilities, 0, sizeof(PChar->m_Abilities)); + std::memset(&PChar->m_Abilities, 0, sizeof(PChar->m_Abilities)); - AbilitiesList = ability::GetAbilities(PChar->GetMJob()); - - for (auto PAbility : AbilitiesList) + for (auto PAbility : ability::GetAbilities(PChar->GetMJob())) { if (PAbility == nullptr) { @@ -2976,9 +2981,7 @@ namespace charutils return; } - AbilitiesList = ability::GetAbilities(PChar->GetSJob()); - - for (auto PAbility : AbilitiesList) + for (auto PAbility : ability::GetAbilities(PChar->GetSJob())) { if (PChar->GetSLevel() >= PAbility->getLevel()) { @@ -5423,6 +5426,12 @@ namespace charutils return; } + // Monstrosity job and level data is handled elsewhere, bail out now + if (job == JOB_MON) + { + return; + } + const char* fmtQuery = ""; switch (job) @@ -5510,6 +5519,12 @@ namespace charutils return; } + // Monstrosity exp data is handled elsewhere, bail out now + if (job == JOB_MON) + { + return; + } + const char* Query = ""; switch (job) From 902ed53782686c27a483b61f3bc91aeec9b5311e Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Sat, 7 Oct 2023 15:57:05 +0100 Subject: [PATCH 057/103] Hide level of MON players from searches --- src/search/data_loader.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/search/data_loader.cpp b/src/search/data_loader.cpp index 043e0dae870..a2bbf9dcd95 100644 --- a/src/search/data_loader.cpp +++ b/src/search/data_loader.cpp @@ -30,6 +30,11 @@ along with this program. If not, see http://www.gnu.org/licenses/ #include "data_loader.h" #include "search.h" +namespace +{ + uint8 JOB_MON = 23; +} // namespace + CDataLoader::CDataLoader() : sql(std::make_unique()) { @@ -287,30 +292,37 @@ std::list CDataLoader::GetPlayersList(search_req sr, int* count) { PPlayer->flags1 |= 0x0001; } + if (partyid == PPlayer->id) { PPlayer->flags1 |= 0x0008; } + if (PPlayer->seacom_type) { PPlayer->flags1 |= 0x0010; } + if (nameflag & FLAG_AWAY) { PPlayer->flags1 |= 0x0100; } + if (nameflag & FLAG_DC) { PPlayer->flags1 |= 0x0800; } + if (partyid != 0) { PPlayer->flags1 |= 0x2000; } + if (nameflag & FLAG_ANON) { PPlayer->flags1 |= 0x4000; } + if (nameflag & FLAG_INVITE) { PPlayer->flags1 |= 0x8000; @@ -318,6 +330,12 @@ std::list CDataLoader::GetPlayersList(search_req sr, int* count) PPlayer->flags2 = PPlayer->flags1; + if (PPlayer->mjob == JOB_MON || PPlayer->sjob == JOB_MON) + { + PPlayer->mjob = 0; + PPlayer->sjob = 0; + } + // filter by job if (sr.jobid > 0 && sr.jobid != PPlayer->mjob) { From 92dc9acb3dfa668a7d71d0940f1be7b5bc1ff265 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Sat, 7 Oct 2023 17:41:38 +0100 Subject: [PATCH 058/103] Add instinct cost validation --- src/map/monstrosity.cpp | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/src/map/monstrosity.cpp b/src/map/monstrosity.cpp index a69d38a0e1a..964621c8b9c 100644 --- a/src/map/monstrosity.cpp +++ b/src/map/monstrosity.cpp @@ -337,12 +337,24 @@ void monstrosity::HandleEquipChangePacket(CCharEntity* PChar, CBasicPacket& data } else if (flag == 0x04) // Instinct Change { + // clang-format off + auto getTotalCost = [&](std::array input) -> uint8 + { + uint8 total = 0; + + for (auto const& idx : input) + { + total += gMonstrosityInstinctMap[idx].cost; + } + + return total; + }; + // clang-format on + auto previousEquipped = PChar->m_PMonstrosity->EquippedInstincts; - // TODO: // NOTE: This is set by the client auto maxPoints = PChar->m_PMonstrosity->levels[PChar->m_PMonstrosity->MonstrosityId] + 10; - std::ignore = maxPoints; // Remove All if (data.ref(0x16) == 0xFFFF) @@ -380,14 +392,20 @@ void monstrosity::HandleEquipChangePacket(CCharEntity* PChar, CBasicPacket& data { auto instinct = (*maybeInstinct).second; - // TODO: Check: - // instinct.cost - PChar->m_PMonstrosity->EquippedInstincts[idx] = value; - for (auto const& mod : instinct.mods) + // Validate: + if (getTotalCost(PChar->m_PMonstrosity->EquippedInstincts) > maxPoints) + { + // Reset to what it was before and don't handle mods + PChar->m_PMonstrosity->EquippedInstincts = previousEquipped; + } + else { - PChar->addModifier(mod.getModID(), mod.getModAmount()); + for (auto const& mod : instinct.mods) + { + PChar->addModifier(mod.getModID(), mod.getModAmount()); + } } } } From 4a3e0b2aeae330d75ae096763f96e5a091c5a50e Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Sat, 7 Oct 2023 17:44:30 +0100 Subject: [PATCH 059/103] Add species validation --- src/map/monstrosity.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/map/monstrosity.cpp b/src/map/monstrosity.cpp index 964621c8b9c..4353dcc1d42 100644 --- a/src/map/monstrosity.cpp +++ b/src/map/monstrosity.cpp @@ -320,8 +320,20 @@ void monstrosity::HandleEquipChangePacket(CCharEntity* PChar, CBasicPacket& data auto newSpecies = data.ref(0x0C); + // Invalid species + if (gMonstrositySpeciesMap.find(newSpecies) == gMonstrositySpeciesMap.end()) + { + return; + } + auto data = gMonstrositySpeciesMap[newSpecies]; + // Not unlocked + if (PChar->m_PMonstrosity->levels[data.monstrosityId] == 0) + { + return; + } + PChar->m_PMonstrosity->Species = newSpecies; PChar->m_PMonstrosity->MonstrosityId = data.monstrosityId; From 5cda958e2afbc6ec9014532a236215531a76db91 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Sat, 7 Oct 2023 18:14:58 +0100 Subject: [PATCH 060/103] Track MON state using JOB_MON not charvars --- scripts/commands/monstrosity.lua | 6 ++---- src/map/monstrosity.cpp | 7 ++++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/scripts/commands/monstrosity.lua b/scripts/commands/monstrosity.lua index ea1fc728989..9727e72e855 100644 --- a/scripts/commands/monstrosity.lua +++ b/scripts/commands/monstrosity.lua @@ -7,10 +7,8 @@ commandObj.cmdprops = } commandObj.onTrigger = function(player) - if player:getCharVar('MONSTROSITY_START') == 1 then - player:setCharVar('MONSTROSITY_START', 0) - else - player:setCharVar('MONSTROSITY_START', 1) + if not player:getMainJob() ~= xi.job.MON then + player:changeJob(xi.job.MON) end player:setPos(player:getXPos(), player:getYPos(), player:getZPos(), player:getRotPos(), player:getZoneID()) diff --git a/src/map/monstrosity.cpp b/src/map/monstrosity.cpp index 4353dcc1d42..c46c6b5a580 100644 --- a/src/map/monstrosity.cpp +++ b/src/map/monstrosity.cpp @@ -202,8 +202,7 @@ void monstrosity::WriteMonstrosityData(CCharEntity* PChar) void monstrosity::HandleZoneIn(CCharEntity* PChar) { - // TODO: Check we're about to enter monstrosity, charvar, flag, etc. - if (charutils::GetCharVar(PChar, "MONSTROSITY_START") == 1) + if (PChar->GetMJob() == JOB_MON) { // Populates PChar->m_PMonstrosity ReadMonstrosityData(PChar); @@ -404,9 +403,11 @@ void monstrosity::HandleEquipChangePacket(CCharEntity* PChar, CBasicPacket& data { auto instinct = (*maybeInstinct).second; + // TODO: Validate whether or not this instinct is unlocked + PChar->m_PMonstrosity->EquippedInstincts[idx] = value; - // Validate: + // Validate cost if (getTotalCost(PChar->m_PMonstrosity->EquippedInstincts) > maxPoints) { // Reset to what it was before and don't handle mods From 649989d3d6fd6cc8bb00009d54decfcfb6a585c6 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Sat, 7 Oct 2023 21:12:36 +0100 Subject: [PATCH 061/103] Stop MONs using zonelines --- src/map/packet_system.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/map/packet_system.cpp b/src/map/packet_system.cpp index cbc6ae14f4e..fadc4c0cc05 100644 --- a/src/map/packet_system.cpp +++ b/src/map/packet_system.cpp @@ -3976,6 +3976,16 @@ void SmallPacket0x05E(map_session_data_t* const PSession, CCharEntity* const PCh PChar->status = STATUS_TYPE::NORMAL; return; } + else if (PChar->m_PMonstrosity) // Not allowed to use zonelines while MON + { + PChar->loc.p.rotation += 128; + + PChar->pushPacket(new CMessageSystemPacket(0, 0, 2)); // You could not enter the next area. + PChar->pushPacket(new CCSPositionPacket(PChar)); + + PChar->status = STATUS_TYPE::NORMAL; + return; + } else { // Ensure the destination exists From f89bc66b08b5f7e9dc556a94f014be9c864f6473 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Sat, 7 Oct 2023 21:12:50 +0100 Subject: [PATCH 062/103] Start on assigning MONs jobs in db --- documentation/model_ids.txt | 2 +- scripts/globals/monstrosity.lua | 2 +- sql/monstrosity_species.sql | 329 ++++++++++++++++---------------- src/map/monstrosity.cpp | 8 +- 4 files changed, 177 insertions(+), 164 deletions(-) diff --git a/documentation/model_ids.txt b/documentation/model_ids.txt index be74336b34d..4b54a9467f2 100644 --- a/documentation/model_ids.txt +++ b/documentation/model_ids.txt @@ -404,7 +404,7 @@ ID Number Costume Name 402 Manticore 403 Kirin 404 Behemoth -405 Elsamoth(Abys) +405 Elasamoth(Abys) 406 Crawler 3(Eruca) 407 Genbu 408 Beetle 1(green) diff --git a/scripts/globals/monstrosity.lua b/scripts/globals/monstrosity.lua index 64f81935269..6e8715285a5 100644 --- a/scripts/globals/monstrosity.lua +++ b/scripts/globals/monstrosity.lua @@ -94,7 +94,7 @@ xi.monstrosity.variants = LAPINION = 2, -- Behemoth - ELSAMOTH = 3, + ELASMOTH = 3, -- Tiger LEGENDARY_TIGER = 5, diff --git a/sql/monstrosity_species.sql b/sql/monstrosity_species.sql index 2db4f9c74b6..336f61ad03b 100644 --- a/sql/monstrosity_species.sql +++ b/sql/monstrosity_species.sql @@ -3,230 +3,239 @@ CREATE TABLE `monstrosity_species` ( `monstrosity_id` smallint(30) unsigned NOT NULL, `monstrosity_species_code` smallint(30) unsigned NOT NULL, `name` varchar(60) DEFAULT NULL, + `mjob` tinyint(3) unsigned NOT NULL, + `sjob` tinyint(3) unsigned NOT NULL, `look` smallint(3) unsigned NOT NULL, PRIMARY KEY (`monstrosity_id`, `monstrosity_species_code`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; -INSERT INTO `monstrosity_species` VALUES (1, 1, "Rabbit", 0x010C); -INSERT INTO `monstrosity_species` VALUES (1, 256, "Onyx Rabbit", 0x010D); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (1, 257, "Alabaster Rabbit", 0x010E); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (1, 258, "Lapinion (Rabbit)", 0x0791); -- TODO: Look guessed not capped +-- TODO: Double check the mjob/sjob values for everything using both resources: +-- https://ffxiclopedia.fandom.com/wiki/Category:Monipulators +-- https://www.bg-wiki.com/ffxi/Category:Monstrosity/Species -INSERT INTO `monstrosity_species` VALUES (2, 2, "Behemoth", 0x0194); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (2, 259, "Elasamoth (Behemoth)", 0x0195); -- TODO: Look guessed not capped +-- NOTE: The mjob/sjob of MONs rise at the same level, so the only difference between which +-- : order you specify them is is which 2H ability you get. -INSERT INTO `monstrosity_species` VALUES (3, 3, "Tiger", 0x0134); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (3, 261, "Legendary Tiger", 0x0134); -- TODO: Look guessed not capped, this is the wrong id -INSERT INTO `monstrosity_species` VALUES (3, 262, "Smilodon (Tiger)", 0x0134); -- TODO: Look guessed not capped, this is the wrong id +INSERT INTO `monstrosity_species` VALUES (1, 1, "Rabbit", 1, 1, 0x010C); +INSERT INTO `monstrosity_species` VALUES (1, 256, "Onyx Rabbit", 1, 4, 0x010D); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (1, 257, "Alabaster Rabbit", 1, 3, 0x010E); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (1, 258, "Lapinion (Rabbit)", 3, 4, 0x0791); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (4, 4, "Sheep", 0x0154); -- TODO: Look guessed not capped, this is the wrong id -INSERT INTO `monstrosity_species` VALUES (4, 263, "Karakul (Sheep)", 0x0154); -- TODO: Look guessed not capped, this is the wrong id +INSERT INTO `monstrosity_species` VALUES (2, 2, "Behemoth", 1, 6, 0x0194); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (2, 259, "Elasamoth (Behemoth)", 8, 6, 0x0195); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (5, 5, "Ram (Sheep)", 0x0000); +INSERT INTO `monstrosity_species` VALUES (3, 3, "Tiger", 1, 1, 0x0134); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (3, 261, "Legendary Tiger", 1, 6, 0x0134); -- TODO: Look guessed not capped, this is the wrong id +INSERT INTO `monstrosity_species` VALUES (3, 262, "Smilodon (Tiger)", 1, 1, 0x0134); -- TODO: Look guessed not capped, this is the wrong id -INSERT INTO `monstrosity_species` VALUES (6, 6, "Dhalmel", 0x0000); +INSERT INTO `monstrosity_species` VALUES (4, 4, "Sheep", 1, 1, 0x0154); -- TODO: Look guessed not capped, this is the wrong id +INSERT INTO `monstrosity_species` VALUES (4, 263, "Karakul (Sheep)", 1, 1, 0x0154); -- TODO: Look guessed not capped, this is the wrong id -INSERT INTO `monstrosity_species` VALUES (7, 7, "Coeurl", 0x0000); -INSERT INTO `monstrosity_species` VALUES (7, 266, "Lynx (Coeurl)", 0x0000); -INSERT INTO `monstrosity_species` VALUES (7, 267, "Collared Lynx (Coeurl)", 0x0000); +INSERT INTO `monstrosity_species` VALUES (5, 5, "Ram (Sheep)", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (8, 8, "Opo-opo", 0x0000); +INSERT INTO `monstrosity_species` VALUES (6, 6, "Dhalmel", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (9, 9, "Manticore", 0x0000); -INSERT INTO `monstrosity_species` VALUES (9, 268, "Legendary Manticore", 0x0000); +INSERT INTO `monstrosity_species` VALUES (7, 7, "Coeurl", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (7, 266, "Lynx (Coeurl)", 1, 4, 0x0000); +INSERT INTO `monstrosity_species` VALUES (7, 267, "Collared Lynx (Coeurl)", 5, 4, 0x0000); -INSERT INTO `monstrosity_species` VALUES (10, 10, "Buffalo", 0x0000); +INSERT INTO `monstrosity_species` VALUES (8, 8, "Opo-opo", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (11, 11, "Marid", 0x0000); +INSERT INTO `monstrosity_species` VALUES (9, 9, "Manticore", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (9, 268, "Legendary Manticore", 5, 4, 0x0000); -INSERT INTO `monstrosity_species` VALUES (12, 12, "Cerberus", 0x0000); -INSERT INTO `monstrosity_species` VALUES (12, 269, "Orthrus (Cerberus)", 0x0000); +INSERT INTO `monstrosity_species` VALUES (10, 10, "Buffalo", 7, 7, 0x0000); -INSERT INTO `monstrosity_species` VALUES (13, 13, "Gnole", 0x0000); -INSERT INTO `monstrosity_species` VALUES (13, 270, "Bipedal Gnole", 0x0000); +INSERT INTO `monstrosity_species` VALUES (11, 11, "Marid", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (15, 15, "Funguar", 0x0000); -INSERT INTO `monstrosity_species` VALUES (15, 271, "Coppercap (Funguar)", 0x0000); +INSERT INTO `monstrosity_species` VALUES (12, 12, "Cerberus", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (12, 269, "Orthrus (Cerberus)", 1, 4, 0x0000); -INSERT INTO `monstrosity_species` VALUES (16, 16, "Treant Sapling", 0x0000); -INSERT INTO `monstrosity_species` VALUES (16, 272, "Treant", 0x0000); -INSERT INTO `monstrosity_species` VALUES (16, 273, "Flowering Treant", 0x0000); -INSERT INTO `monstrosity_species` VALUES (16, 274, "Scarlet-tinged Treant", 0x0000); -INSERT INTO `monstrosity_species` VALUES (16, 275, "Barren Treant", 0x0000); -INSERT INTO `monstrosity_species` VALUES (16, 276, "Necklaced Treant", 0x0000); +INSERT INTO `monstrosity_species` VALUES (13, 13, "Gnole", 2, 2, 0x0000); +INSERT INTO `monstrosity_species` VALUES (13, 270, "Bipedal Gnole", 2, 2, 0x0000); -INSERT INTO `monstrosity_species` VALUES (17, 17, "Morbol", 0x0000); -INSERT INTO `monstrosity_species` VALUES (17, 277, "Pygmy Morbol", 0x0000); -INSERT INTO `monstrosity_species` VALUES (17, 278, "Scarce Morbol", 0x0000); -INSERT INTO `monstrosity_species` VALUES (17, 279, "Ameretat (Morbol)", 0x0000); -INSERT INTO `monstrosity_species` VALUES (17, 280, "Purbol (Morbol)", 0x0000); +INSERT INTO `monstrosity_species` VALUES (15, 15, "Funguar", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (15, 271, "Coppercap (Funguar)", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (18, 18, "Mandragora", 0x012C); -INSERT INTO `monstrosity_species` VALUES (18, 281, "Korrigan (Mandragora)", 0x012C); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (18, 282, "Lycopodium (Mandragora)", 0x012C); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (18, 283, "Pygmy Mandragora", 0x012C); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (18, 284, "Adenium (Mandragora)", 0x012C); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (18, 285, "Pachypodium (Mandragora)", 0x012C); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (18, 286, "Enlightened Mandragora", 0x012C); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (18, 287, "New Year Mandragora", 0x0B48); +INSERT INTO `monstrosity_species` VALUES (16, 16, "Treant Sapling", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (16, 272, "Treant", 1, 4, 0x0000); +INSERT INTO `monstrosity_species` VALUES (16, 273, "Flowering Treant", 1, 4, 0x0000); +INSERT INTO `monstrosity_species` VALUES (16, 274, "Scarlet-tinged Treant", 1, 4, 0x0000); +INSERT INTO `monstrosity_species` VALUES (16, 275, "Barren Treant", 1, 4, 0x0000); +INSERT INTO `monstrosity_species` VALUES (16, 276, "Necklaced Treant", 1, 4, 0x0000); -INSERT INTO `monstrosity_species` VALUES (19, 19, "Sabotender", 0x0000); -INSERT INTO `monstrosity_species` VALUES (19, 288, "Sabotender Florido", 0x0000); +INSERT INTO `monstrosity_species` VALUES (17, 17, "Morbol", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (17, 277, "Pygmy Morbol", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (17, 278, "Scarce Morbol", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (17, 279, "Ameretat (Morbol)", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (17, 280, "Purbol (Morbol)", 1, 4, 0x0000); -INSERT INTO `monstrosity_species` VALUES (20, 20, "Flytrap", 0x0000); +INSERT INTO `monstrosity_species` VALUES (18, 18, "Mandragora", 2, 2, 0x012C); +INSERT INTO `monstrosity_species` VALUES (18, 281, "Korrigan (Mandragora)", 2, 4, 0x012C); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (18, 282, "Lycopodium (Mandragora)", 2, 3, 0x012C); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (18, 283, "Pygmy Mandragora", 2, 2, 0x012C); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (18, 284, "Adenium (Mandragora)", 2, 5, 0x012C); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (18, 285, "Pachypodium (Mandragora)", 2, 2, 0x012C); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (18, 286, "Enlightened Mandragora", 2, 2, 0x012C); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (18, 287, "New Year Mandragora", 2, 13, 0x0B48); -INSERT INTO `monstrosity_species` VALUES (21, 21, "Goobbue", 0x0000); +INSERT INTO `monstrosity_species` VALUES (19, 19, "Sabotender", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (19, 288, "Sabotender Florido", 1, 5, 0x0000); -INSERT INTO `monstrosity_species` VALUES (22, 22, "Rafflesia", 0x0000); -INSERT INTO `monstrosity_species` VALUES (22, 289, "Mitrastema (Rafflesia)", 0x0000); +INSERT INTO `monstrosity_species` VALUES (20, 20, "Flytrap", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (23, 23, "Panopt", 0x0000); +INSERT INTO `monstrosity_species` VALUES (21, 21, "Goobbue", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (27, 27, "Bee", 0x0000); -INSERT INTO `monstrosity_species` VALUES (27, 290, "Vermillion and Onyx Bee", 0x0000); -INSERT INTO `monstrosity_species` VALUES (27, 291, "Zaffre Bee", 0x0000); +INSERT INTO `monstrosity_species` VALUES (22, 22, "Rafflesia", 8, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (22, 289, "Mitrastema (Rafflesia)", 8, 4, 0x0000); -INSERT INTO `monstrosity_species` VALUES (28, 28, "Beetle", 0x0000); -INSERT INTO `monstrosity_species` VALUES (28, 292, "Onyx Beetle", 0x0000); -INSERT INTO `monstrosity_species` VALUES (28, 293, "Gamboge Beetle", 0x0000); +INSERT INTO `monstrosity_species` VALUES (23, 23, "Panopt", 4, 4, 0x0000); -INSERT INTO `monstrosity_species` VALUES (29, 29, "Crawler", 0x0000); -INSERT INTO `monstrosity_species` VALUES (29, 294, "Eruca (Crawler)", 0x0000); -INSERT INTO `monstrosity_species` VALUES (29, 295, "Emerald Crawler", 0x0000); -INSERT INTO `monstrosity_species` VALUES (29, 296, "Pygmy Emerald Crawler", 0x0000); +INSERT INTO `monstrosity_species` VALUES (27, 27, "Bee", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (27, 290, "Vermillion and Onyx Bee", 1, 5, 0x0000); +INSERT INTO `monstrosity_species` VALUES (27, 291, "Zaffre Bee", 1, 4, 0x0000); -INSERT INTO `monstrosity_species` VALUES (30, 30, "Fly", 0x0000); -INSERT INTO `monstrosity_species` VALUES (30, 297, "Vermillion Fly", 0x0000); +INSERT INTO `monstrosity_species` VALUES (28, 28, "Beetle", 7, 7, 0x0000); +INSERT INTO `monstrosity_species` VALUES (28, 292, "Onyx Beetle", 7, 4, 0x0000); +INSERT INTO `monstrosity_species` VALUES (28, 293, "Gamboge Beetle", 7, 5, 0x0000); -INSERT INTO `monstrosity_species` VALUES (31, 31, "Scorpion", 0x0000); -INSERT INTO `monstrosity_species` VALUES (31, 298, "Scolopendrid (Scorpion)", 0x0000); -INSERT INTO `monstrosity_species` VALUES (31, 299, "Unusual Scolopendrid (Scorpion)", 0x0000); +INSERT INTO `monstrosity_species` VALUES (29, 29, "Crawler", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (29, 294, "Eruca (Crawler)", 1, 5, 0x0000); +INSERT INTO `monstrosity_species` VALUES (29, 295, "Emerald Crawler", 3, 4, 0x0000); +INSERT INTO `monstrosity_species` VALUES (29, 296, "Pygmy Emerald Crawler", 3, 4, 0x0000); -INSERT INTO `monstrosity_species` VALUES (32, 32, "Spider", 0x0000); -INSERT INTO `monstrosity_species` VALUES (32, 300, "Reticulated Spider", 0x0000); -INSERT INTO `monstrosity_species` VALUES (32, 301, "Vermillion and Onyx Spider", 0x0000); +INSERT INTO `monstrosity_species` VALUES (30, 30, "Fly", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (30, 297, "Vermillion Fly", 1, 5, 0x0000); -INSERT INTO `monstrosity_species` VALUES (33, 33, "Antlion", 0x0000); -INSERT INTO `monstrosity_species` VALUES (33, 302, "Onyx Antlion", 0x0000); -INSERT INTO `monstrosity_species` VALUES (33, 303, "Formiceros (Antlion)", 0x0000); +INSERT INTO `monstrosity_species` VALUES (31, 31, "Scorpion", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (31, 298, "Scolopendrid (Scorpion)", 1, 4, 0x0000); +INSERT INTO `monstrosity_species` VALUES (31, 299, "Unusual Scolopendrid (Scorpion)", 8, 4, 0x0000); -INSERT INTO `monstrosity_species` VALUES (34, 34, "Diremite", 0x0000); -INSERT INTO `monstrosity_species` VALUES (34, 304, "Arundemite (Diremite)", 0x0000); +INSERT INTO `monstrosity_species` VALUES (32, 32, "Spider", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (32, 300, "Reticulated Spider", 1, 8, 0x0000); +INSERT INTO `monstrosity_species` VALUES (32, 301, "Vermillion and Onyx Spider", 1, 5, 0x0000); -INSERT INTO `monstrosity_species` VALUES (35, 35, "Chigoe", 0x0000); -INSERT INTO `monstrosity_species` VALUES (35, 305, "Azure Chigoe", 0x0000); +INSERT INTO `monstrosity_species` VALUES (33, 33, "Antlion", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (33, 302, "Onyx Antlion", 1, 4, 0x0000); +INSERT INTO `monstrosity_species` VALUES (33, 303, "Formiceros (Antlion)", 8, 4, 0x0000); -INSERT INTO `monstrosity_species` VALUES (36, 36, "Wamouracampa (Wamoura larva)", 0x0000); -INSERT INTO `monstrosity_species` VALUES (36, 306, "Coiled Wamouracampa (Wamoura larva)", 0x0000); -INSERT INTO `monstrosity_species` VALUES (36, 307, "Wamoura", 0x0000); -INSERT INTO `monstrosity_species` VALUES (36, 308, "Coral Wamoura", 0x0000); +INSERT INTO `monstrosity_species` VALUES (34, 34, "Diremite", 8, 8, 0x0000); +INSERT INTO `monstrosity_species` VALUES (34, 304, "Arundemite (Diremite)", 8, 8, 0x0000); -INSERT INTO `monstrosity_species` VALUES (37, 37, "Ladybug", 0x0000); -INSERT INTO `monstrosity_species` VALUES (37, 309, "Gold Ladybug", 0x0000); +INSERT INTO `monstrosity_species` VALUES (35, 35, "Chigoe", 6, 6, 0x0000); +INSERT INTO `monstrosity_species` VALUES (35, 305, "Azure Chigoe", 6, 4, 0x0000); -INSERT INTO `monstrosity_species` VALUES (38, 38, "Gnat", 0x0000); -INSERT INTO `monstrosity_species` VALUES (38, 310, "Midge (Gnat)", 0x0000); +INSERT INTO `monstrosity_species` VALUES (36, 36, "Wamouracampa (Wamoura larva)", 7, 7, 0x0000); +INSERT INTO `monstrosity_species` VALUES (36, 306, "Coiled Wamouracampa (Wamoura larva)", 7, 7, 0x0000); +INSERT INTO `monstrosity_species` VALUES (36, 307, "Wamoura", 1, 7, 0x0000); +INSERT INTO `monstrosity_species` VALUES (36, 308, "Coral Wamoura", 4, 7, 0x0000); -INSERT INTO `monstrosity_species` VALUES (43, 43, "Lizard", 0x0148); -INSERT INTO `monstrosity_species` VALUES (43, 315, "Ashen Lizard", 0x0148); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (37, 37, "Ladybug", 6, 6, 0x0000); +INSERT INTO `monstrosity_species` VALUES (37, 309, "Gold Ladybug", 6, 5, 0x0000); -INSERT INTO `monstrosity_species` VALUES (44, 44, "Raptor", 0x0000); -INSERT INTO `monstrosity_species` VALUES (44, 316, "Emerald Raptor", 0x0000); -INSERT INTO `monstrosity_species` VALUES (44, 317, "Vermillion Raptor", 0x0000); +INSERT INTO `monstrosity_species` VALUES (38, 38, "Gnat", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (38, 310, "Midge (Gnat)", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (45, 45, "Adamantoise", 0x0000); -INSERT INTO `monstrosity_species` VALUES (45, 318, "Pygmy Adamantoise", 0x0000); -INSERT INTO `monstrosity_species` VALUES (45, 319, "Legendary Adamantoise", 0x0000); -INSERT INTO `monstrosity_species` VALUES (45, 320, "Ferromantoise (Adamantoise)", 0x0000); +INSERT INTO `monstrosity_species` VALUES (43, 43, "Lizard", 1, 1, 0x0148); +INSERT INTO `monstrosity_species` VALUES (43, 315, "Ashen Lizard", 1, 4, 0x0148); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (46, 46, "Bugard", 0x0000); -INSERT INTO `monstrosity_species` VALUES (46, 321, "Abyssobugard (Bugard)", 0x0000); +INSERT INTO `monstrosity_species` VALUES (44, 44, "Raptor", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (44, 316, "Emerald Raptor", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (44, 317, "Vermillion Raptor", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (47, 47, "Eft", 0x0000); -INSERT INTO `monstrosity_species` VALUES (47, 322, "Tarichuk (Eft)", 0x0000); +INSERT INTO `monstrosity_species` VALUES (45, 45, "Adamantoise", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (45, 318, "Pygmy Adamantoise", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (45, 319, "Legendary Adamantoise", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (45, 320, "Ferromantoise (Adamantoise)", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (48, 48, "Wivre", 0x0000); -INSERT INTO `monstrosity_species` VALUES (48, 323, "Unusual Wivre", 0x0000); +INSERT INTO `monstrosity_species` VALUES (46, 46, "Bugard", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (46, 321, "Abyssobugard (Bugard)", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (49, 49, "Peiste", 0x0000); -INSERT INTO `monstrosity_species` VALUES (49, 324, "Sibilus (Peiste)", 0x0000); +INSERT INTO `monstrosity_species` VALUES (47, 47, "Eft", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (47, 322, "Tarichuk (Eft)", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (52, 52, "Slime", 0x0000); -INSERT INTO `monstrosity_species` VALUES (52, 329, "Clot (Slime)", 0x0000); -INSERT INTO `monstrosity_species` VALUES (52, 330, "Gold Slime", 0x0000); -INSERT INTO `monstrosity_species` VALUES (52, 331, "Boil (Slime)", 0x0000); +INSERT INTO `monstrosity_species` VALUES (48, 48, "Wivre", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (48, 323, "Unusual Wivre", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (53, 53, "Hecteyes", 0x0000); +INSERT INTO `monstrosity_species` VALUES (49, 49, "Peiste", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (49, 324, "Sibilus (Peiste)", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (54, 54, "Flan", 0x0000); -INSERT INTO `monstrosity_species` VALUES (54, 332, "Gold Flan", 0x0000); -INSERT INTO `monstrosity_species` VALUES (54, 333, "Blancmange (Flan)", 0x0000); +INSERT INTO `monstrosity_species` VALUES (52, 52, "Slime", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (52, 329, "Clot (Slime)", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (52, 330, "Gold Slime", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (52, 331, "Boil (Slime)", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (56, 56, "Slug", 0x0000); +INSERT INTO `monstrosity_species` VALUES (53, 53, "Hecteyes", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (57, 57, "Sandworm", 0x0000); -INSERT INTO `monstrosity_species` VALUES (57, 334, "Pygmy Sandworm", 0x0000); -INSERT INTO `monstrosity_species` VALUES (57, 335, "Gigaworm (Sandworm)", 0x0000); +INSERT INTO `monstrosity_species` VALUES (54, 54, "Flan", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (54, 332, "Gold Flan", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (54, 333, "Blancmange (Flan)", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (58, 58, "Leech", 0x0000); -INSERT INTO `monstrosity_species` VALUES (58, 336, "Azure Leech", 0x0000); -INSERT INTO `monstrosity_species` VALUES (58, 337, "Obdella (Leech)", 0x0000); +INSERT INTO `monstrosity_species` VALUES (56, 56, "Slug", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (60, 60, "Crab", 0x0000); -INSERT INTO `monstrosity_species` VALUES (60, 340, "Vermillion Crab", 0x0000); -INSERT INTO `monstrosity_species` VALUES (60, 341, "Basket-burdened Crab", 0x0000); -INSERT INTO `monstrosity_species` VALUES (60, 342, "Vermillion Basket-burdened Crab", 0x0000); -INSERT INTO `monstrosity_species` VALUES (60, 343, "Porter Crab (Crab)", 0x0000); +INSERT INTO `monstrosity_species` VALUES (57, 57, "Sandworm", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (57, 334, "Pygmy Sandworm", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (57, 335, "Gigaworm (Sandworm)", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (61, 61, "Pugil", 0x0000); -INSERT INTO `monstrosity_species` VALUES (61, 344, "Jagil (Pugil)", 0x0000); +INSERT INTO `monstrosity_species` VALUES (58, 58, "Leech", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (58, 336, "Azure Leech", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (58, 337, "Obdella (Leech)", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (62, 62, "Sea Monk", 0x0000); -INSERT INTO `monstrosity_species` VALUES (62, 345, "Azure Sea Monk", 0x0000); +INSERT INTO `monstrosity_species` VALUES (60, 60, "Crab", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (60, 340, "Vermillion Crab", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (60, 341, "Basket-burdened Crab", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (60, 342, "Vermillion Basket-burdened Crab", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (60, 343, "Porter Crab (Crab)", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (63, 63, "Uragnite", 0x0000); -INSERT INTO `monstrosity_species` VALUES (63, 346, "Limascabra (Uragnite)", 0x0000); +INSERT INTO `monstrosity_species` VALUES (61, 61, "Pugil", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (61, 344, "Jagil (Pugil)", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (64, 64, "Orobon", 0x0000); -INSERT INTO `monstrosity_species` VALUES (64, 347, "Pygmy Orobon", 0x0000); -INSERT INTO `monstrosity_species` VALUES (64, 348, "Ogrebon (Orobon)", 0x0000); +INSERT INTO `monstrosity_species` VALUES (62, 62, "Sea Monk", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (62, 345, "Azure Sea Monk", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (65, 65, "Ruszor", 0x0000); +INSERT INTO `monstrosity_species` VALUES (63, 63, "Uragnite", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (63, 346, "Limascabra (Uragnite)", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (66, 66, "Toad", 0x0000); -INSERT INTO `monstrosity_species` VALUES (66, 349, "Azure Toad", 0x0000); -INSERT INTO `monstrosity_species` VALUES (66, 350, "Vermillion Toad", 0x0000); +INSERT INTO `monstrosity_species` VALUES (64, 64, "Orobon", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (64, 347, "Pygmy Orobon", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (64, 348, "Ogrebon (Orobon)", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (69, 69, "Bird", 0x0000); -INSERT INTO `monstrosity_species` VALUES (69, 351, "Onyx Bird", 0x0000); +INSERT INTO `monstrosity_species` VALUES (65, 65, "Ruszor", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (70, 70, "Cockatrice", 0x0000); -INSERT INTO `monstrosity_species` VALUES (70, 352, "Ziz (Cockatrice)", 0x0000); +INSERT INTO `monstrosity_species` VALUES (66, 66, "Toad", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (66, 349, "Azure Toad", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (66, 350, "Vermillion Toad", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (71, 71, "Roc", 0x0000); -INSERT INTO `monstrosity_species` VALUES (71, 353, "Legendary Roc", 0x0000); -INSERT INTO `monstrosity_species` VALUES (71, 354, "Gagana (Roc)", 0x0000); +INSERT INTO `monstrosity_species` VALUES (69, 69, "Bird", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (69, 351, "Onyx Bird", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (72, 72, "Bat", 0x0000); -INSERT INTO `monstrosity_species` VALUES (72, 355, "Bats", 0x0000); -INSERT INTO `monstrosity_species` VALUES (72, 356, "Vermillion Bat", 0x0000); -INSERT INTO `monstrosity_species` VALUES (72, 357, "Vermillion Bats", 0x0000); +INSERT INTO `monstrosity_species` VALUES (70, 70, "Cockatrice", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (70, 352, "Ziz (Cockatrice)", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (73, 73, "Hippogryph", 0x0000); +INSERT INTO `monstrosity_species` VALUES (71, 71, "Roc", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (71, 353, "Legendary Roc", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (71, 354, "Gagana (Roc)", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (74, 74, "Apkallu", 0x0000); -INSERT INTO `monstrosity_species` VALUES (74, 358, "Inguza (Apkallu)", 0x0000); +INSERT INTO `monstrosity_species` VALUES (72, 72, "Bat", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (72, 355, "Bats", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (72, 356, "Vermillion Bat", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (72, 357, "Vermillion Bats", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (75, 75, "Colibri", 0x0000); -INSERT INTO `monstrosity_species` VALUES (75, 359, "Toucalibri (Colibri)", 0x0000); +INSERT INTO `monstrosity_species` VALUES (73, 73, "Hippogryph", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (76, 76, "Amphiptere", 0x0000); -INSERT INTO `monstrosity_species` VALUES (76, 360, "Sanguiptere (Amphiptere)", 0x0000); +INSERT INTO `monstrosity_species` VALUES (74, 74, "Apkallu", 2, 2, 0x0000); +INSERT INTO `monstrosity_species` VALUES (74, 358, "Inguza (Apkallu)", 13, 2, 0x0000); -INSERT INTO `monstrosity_species` VALUES (126, 254, "Astoltian Slime", 0x0B41); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (126, 508, "Astoltian She-Slime", 0x0B5B); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (126, 509, "Astoltian Metal Slime", 0x0B5C); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (75, 75, "Colibri", 5, 5, 0x0000); +INSERT INTO `monstrosity_species` VALUES (75, 359, "Toucalibri (Colibri)", 10, 5, 0x0000); -INSERT INTO `monstrosity_species` VALUES (127, 255, "Eorzean Spriggan", 0x0B42); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (127, 510, "Eorzean Spriggan.C", 0x0B5D); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (127, 511, "Eorzean Spriggan.G", 0x0B5E); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (76, 76, "Amphiptere", 1, 4, 0x0000); +INSERT INTO `monstrosity_species` VALUES (76, 360, "Sanguiptere (Amphiptere)", 4, 1, 0x0000); + +INSERT INTO `monstrosity_species` VALUES (126, 254, "Astoltian Slime", 1, 1, 0x0B41); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (126, 508, "Astoltian She-Slime", 1, 1, 0x0B5B); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (126, 509, "Astoltian Metal Slime", 4, 1, 0x0B5C); -- TODO: Look guessed not capped + +INSERT INTO `monstrosity_species` VALUES (127, 255, "Eorzean Spriggan", 1, 4, 0x0B42); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (127, 510, "Eorzean Spriggan.C", 1, 4, 0x0B5D); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (127, 511, "Eorzean Spriggan.G", 4, 1, 0x0B5E); -- TODO: Look guessed not capped diff --git a/src/map/monstrosity.cpp b/src/map/monstrosity.cpp index c46c6b5a580..244684ca52f 100644 --- a/src/map/monstrosity.cpp +++ b/src/map/monstrosity.cpp @@ -47,6 +47,8 @@ struct MonstrositySpeciesRow uint8 monstrosityId; uint16 monstrositySpeciesCode; std::string name; + JOBTYPE mjob; + JOBTYPE sjob; uint16 look; }; @@ -84,7 +86,7 @@ void monstrosity::LoadStaticData() { ShowInfo("Loading Monstrosity data"); - int32 ret = sql->Query("SELECT monstrosity_id, monstrosity_species_code, name, look FROM monstrosity_species;"); + int32 ret = sql->Query("SELECT monstrosity_id, monstrosity_species_code, name, mjob, sjob, look FROM monstrosity_species;"); if (ret != SQL_ERROR && sql->NumRows() != 0) { while (sql->NextRow() == SQL_SUCCESS) @@ -94,7 +96,9 @@ void monstrosity::LoadStaticData() row.monstrosityId = static_cast(sql->GetUIntData(0)); row.monstrositySpeciesCode = static_cast(sql->GetUIntData(1)); row.name = sql->GetStringData(2); - row.look = static_cast(sql->GetUIntData(3)); + row.mjob = static_cast(sql->GetUIntData(3)); + row.sjob = static_cast(sql->GetUIntData(4)); + row.look = static_cast(sql->GetUIntData(5)); gMonstrositySpeciesMap[row.monstrositySpeciesCode] = row; } From 4498947f23b404f0feb885e61ee4eb3c6849ccbb Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Mon, 9 Oct 2023 11:09:50 +0100 Subject: [PATCH 063/103] Assign player traits to MONs based on species jobs --- scripts/commands/monstrosity.lua | 2 +- src/map/monstrosity.cpp | 20 +++++++++++++++++- src/map/monstrosity.h | 7 +++++- src/map/packet_system.cpp | 30 +++++++++++++------------- src/map/packets/char_job_extra.cpp | 2 +- src/map/packets/char_jobs.cpp | 2 +- src/map/packets/zone_in.cpp | 2 +- src/map/utils/charutils.cpp | 34 +++++++++++++++++++++++------- src/map/zone.cpp | 5 ++--- 9 files changed, 72 insertions(+), 32 deletions(-) diff --git a/scripts/commands/monstrosity.lua b/scripts/commands/monstrosity.lua index 9727e72e855..32b0f611176 100644 --- a/scripts/commands/monstrosity.lua +++ b/scripts/commands/monstrosity.lua @@ -7,7 +7,7 @@ commandObj.cmdprops = } commandObj.onTrigger = function(player) - if not player:getMainJob() ~= xi.job.MON then + if player:getMainJob() ~= xi.job.MON then player:changeJob(xi.job.MON) end diff --git a/src/map/monstrosity.cpp b/src/map/monstrosity.cpp index 244684ca52f..68ab1417ce1 100644 --- a/src/map/monstrosity.cpp +++ b/src/map/monstrosity.cpp @@ -73,6 +73,8 @@ monstrosity::MonstrosityData_t::MonstrosityData_t() , Look(0x010C) // Rabbit , NamePrefix1(0x00) // Nothing , NamePrefix2(0x00) // Nothing +, MainJob(JOB_WAR) // +, SubJob(JOB_WAR) // , CurrentExp(0) // No exp { levels[1] = 1; // Rabbit @@ -158,6 +160,10 @@ void monstrosity::ReadMonstrosityData(CCharEntity* PChar) sql->GetBlobData(8, &data->instincts); sql->GetBlobData(9, &data->variants); + // Build additional data from lookups + data->MainJob = gMonstrositySpeciesMap[data->Species].mjob; + data->SubJob = gMonstrositySpeciesMap[data->Species].sjob; + // TODO: auto level = data->levels[data->MonstrosityId]; std::ignore = level; @@ -204,7 +210,7 @@ void monstrosity::WriteMonstrosityData(CCharEntity* PChar) variantsEscaped.c_str()); } -void monstrosity::HandleZoneIn(CCharEntity* PChar) +void monstrosity::TryPopulateMonstrosityData(CCharEntity* PChar) { if (PChar->GetMJob() == JOB_MON) { @@ -213,7 +219,13 @@ void monstrosity::HandleZoneIn(CCharEntity* PChar) // This handles !monstrosity GM command, is this needed? WriteMonstrosityData(PChar); + } +} +void monstrosity::HandleZoneIn(CCharEntity* PChar) +{ + if (PChar->m_PMonstrosity != nullptr) + { // Add stats from equipped instincts for (auto instinctId : PChar->m_PMonstrosity->EquippedInstincts) { @@ -287,6 +299,8 @@ void monstrosity::SendFullMonstrosityUpdate(CCharEntity* PChar) // TODO: Only send model change packets when the model actually changes - otherwise it disappears! + charutils::BuildingCharTraitsTable(PChar); + PChar->pushPacket(new CMonipulatorPacket1(PChar)); PChar->pushPacket(new CMonipulatorPacket2(PChar)); PChar->pushPacket(new CCharJobsPacket(PChar)); @@ -294,6 +308,8 @@ void monstrosity::SendFullMonstrosityUpdate(CCharEntity* PChar) PChar->pushPacket(new CCharJobExtraPacket(PChar, false)); PChar->pushPacket(new CCharAppearancePacket(PChar)); PChar->pushPacket(new CCharStatsPacket(PChar)); + PChar->pushPacket(new CCharAbilitiesPacket(PChar)); + PChar->updatemask |= UPDATE_LOOK; } @@ -340,6 +356,8 @@ void monstrosity::HandleEquipChangePacket(CCharEntity* PChar, CBasicPacket& data PChar->m_PMonstrosity->Species = newSpecies; PChar->m_PMonstrosity->MonstrosityId = data.monstrosityId; + PChar->m_PMonstrosity->MainJob = data.mjob; + PChar->m_PMonstrosity->SubJob = data.sjob; PChar->m_PMonstrosity->Look = data.look; if (PChar->m_PMonstrosity->MonstrosityId != previousId) diff --git a/src/map/monstrosity.h b/src/map/monstrosity.h index f648895eec1..3cbf049ce8a 100644 --- a/src/map/monstrosity.h +++ b/src/map/monstrosity.h @@ -25,6 +25,8 @@ #include "packets/basic.h" +#include "entities/battleentity.h" + #include class CCharEntity; @@ -44,7 +46,9 @@ namespace monstrosity uint8 NamePrefix1; uint8 NamePrefix2; - uint32 CurrentExp; + JOBTYPE MainJob; + JOBTYPE SubJob; + uint32 CurrentExp; std::array EquippedInstincts{ 0 }; std::array levels{ 0 }; @@ -57,6 +61,7 @@ namespace monstrosity void ReadMonstrosityData(CCharEntity* PChar); void WriteMonstrosityData(CCharEntity* PChar); + void TryPopulateMonstrosityData(CCharEntity* PChar); void HandleZoneIn(CCharEntity* PChar); uint32 GetPackedMonstrosityName(CCharEntity* PChar); void SendFullMonstrosityUpdate(CCharEntity* PChar); diff --git a/src/map/packet_system.cpp b/src/map/packet_system.cpp index fadc4c0cc05..a5f0da944b3 100644 --- a/src/map/packet_system.cpp +++ b/src/map/packet_system.cpp @@ -793,8 +793,10 @@ void SmallPacket0x01A(map_session_data_t* const PSession, CCharEntity* const PCh { TracyZoneScoped; - uint16 TargID = data.ref(0x08); - uint8 action = data.ref(0x0A); + uint16 TargID = data.ref(0x08); + uint8 action = data.ref(0x0A); + + // clang-format off position_t actionOffset = { data.ref(0x10), @@ -803,12 +805,7 @@ void SmallPacket0x01A(map_session_data_t* const PSession, CCharEntity* const PCh 0, // moving (packet only contains x/y/z) 0, // rotation (packet only contains x/y/z) }; - - // Monstrosity: Can't really do anything while under Gestation until you click it off - if (PChar->StatusEffectContainer->HasStatusEffect(EFFECT_GESTATION)) - { - return; - } + // clang-format on constexpr auto actionToStr = [](uint8 actionIn) { @@ -865,6 +862,14 @@ void SmallPacket0x01A(map_session_data_t* const PSession, CCharEntity* const PCh } }; + // Monstrosity: Can't really do anything while under Gestation until you click it off. + // : MONs can trigger doors, so we'll handle that later. + // TODO! + if (PChar->StatusEffectContainer->HasStatusEffect(EFFECT_GESTATION) && action != 0x00 /* trigger */) + { + return; + } + auto actionStr = fmt::format("Player Action: {}: {} (0x{:02X}) -> targid: {}", PChar->GetName(), actionToStr(action), action, TargID); TracyZoneString(actionStr); ShowTrace(actionStr); @@ -885,11 +890,6 @@ void SmallPacket0x01A(map_session_data_t* const PSession, CCharEntity* const PCh return; } - if (PChar->m_PMonstrosity != nullptr) - { - return; - } - CBaseEntity* PNpc = nullptr; PNpc = PChar->GetEntity(TargID, TYPE_NPC | TYPE_MOB); @@ -1022,7 +1022,7 @@ void SmallPacket0x01A(map_session_data_t* const PSession, CCharEntity* const PCh return; } - if (PChar->m_PMonstrosity) + if (PChar->m_PMonstrosity != nullptr) { auto type = data.ref(0x0C); monstrosity::HandleDeathMenu(PChar, type); @@ -3976,7 +3976,7 @@ void SmallPacket0x05E(map_session_data_t* const PSession, CCharEntity* const PCh PChar->status = STATUS_TYPE::NORMAL; return; } - else if (PChar->m_PMonstrosity) // Not allowed to use zonelines while MON + else if (PChar->m_PMonstrosity != nullptr) // Not allowed to use zonelines while MON { PChar->loc.p.rotation += 128; diff --git a/src/map/packets/char_job_extra.cpp b/src/map/packets/char_job_extra.cpp index ff13386b814..f6f128e1888 100644 --- a/src/map/packets/char_job_extra.cpp +++ b/src/map/packets/char_job_extra.cpp @@ -133,7 +133,7 @@ CCharJobExtraPacket::CCharJobExtraPacket(CCharEntity* PChar, bool mjob) ref(0x9C) = PChar->getMod(Mod::AUTO_ELEM_CAPACITY); } - else if (job == JOB_MON && PChar->m_PMonstrosity != nullptr) + else if (PChar->m_PMonstrosity != nullptr) { ref(0x08) = PChar->m_PMonstrosity->Species; diff --git a/src/map/packets/char_jobs.cpp b/src/map/packets/char_jobs.cpp index bbbbb045181..5e715a0ebef 100644 --- a/src/map/packets/char_jobs.cpp +++ b/src/map/packets/char_jobs.cpp @@ -58,7 +58,7 @@ CCharJobsPacket::CCharJobsPacket(CCharEntity* PChar) ref(0x68) = 0; // Is job mastered, and has Master Breaker KI ref(0x6D) = 0; // Master Level - if (PChar->m_PMonstrosity) + if (PChar->m_PMonstrosity != nullptr) { ref(0x08) = static_cast(JOB_MON); ref(0x0B) = static_cast(JOB_MON); diff --git a/src/map/packets/zone_in.cpp b/src/map/packets/zone_in.cpp index cd36fb81d75..320997ac40c 100644 --- a/src/map/packets/zone_in.cpp +++ b/src/map/packets/zone_in.cpp @@ -264,7 +264,7 @@ CZoneInPacket::CZoneInPacket(CCharEntity* PChar, const EventInfo* currentEvent) ref(0x54) = 0xFFFF; // Enable Monstrosity menu options - ref(0x7E) = 0x1F; + ref(0x7E) = 0x1F; ref(0x98) = monstrosity::GetPackedMonstrosityName(PChar); } diff --git a/src/map/utils/charutils.cpp b/src/map/utils/charutils.cpp index adb5b8e4d98..8ecd52309e9 100644 --- a/src/map/utils/charutils.cpp +++ b/src/map/utils/charutils.cpp @@ -148,11 +148,12 @@ namespace charutils // NOTE: Monstrosity (MON) is treated as its own job, but each species is it's own // : combination of main/sub job for stats, traits and abilities. - if (mjob == JOB_MON || sjob == JOB_MON) + if (PChar->m_PMonstrosity != nullptr) { - // TODO: Look up species jobs, hardcoded as WAR for now - mjob = JOB_WAR; - sjob = JOB_WAR; + mjob = PChar->m_PMonstrosity->MainJob; + sjob = PChar->m_PMonstrosity->SubJob; + mlvl = PChar->m_PMonstrosity->levels[PChar->m_PMonstrosity->MonstrosityId]; + slvl = mlvl; } uint8 race = 0; // Hume @@ -865,6 +866,8 @@ namespace charutils PChar->m_FieldChocobo = sql->GetUIntData(0); } + monstrosity::TryPopulateMonstrosityData(PChar); + charutils::LoadInventory(PChar); CalculateStats(PChar); @@ -3217,17 +3220,32 @@ namespace charutils PChar->TraitList.clear(); memset(&PChar->m_TraitList, 0, sizeof(PChar->m_TraitList)); - battleutils::AddTraits(PChar, traits::GetTraits(PChar->GetMJob()), PChar->GetMLevel()); - battleutils::AddTraits(PChar, traits::GetTraits(PChar->GetSJob()), PChar->GetSLevel()); + auto mjob = PChar->GetMJob(); + auto sjob = PChar->GetSJob(); + auto mlvl = PChar->GetMLevel(); + auto slvl = PChar->GetSLevel(); + + // NOTE: Monstrosity (MON) is treated as its own job, but each species is it's own + // : combination of main/sub job for stats, traits and abilities. + if (PChar->m_PMonstrosity != nullptr) + { + mjob = PChar->m_PMonstrosity->MainJob; + sjob = PChar->m_PMonstrosity->SubJob; + mlvl = PChar->m_PMonstrosity->levels[PChar->m_PMonstrosity->MonstrosityId]; + slvl = mlvl; + } + + battleutils::AddTraits(PChar, traits::GetTraits(mjob), mlvl); + battleutils::AddTraits(PChar, traits::GetTraits(sjob), slvl); - if (PChar->GetMJob() == JOB_BLU || PChar->GetSJob() == JOB_BLU) + if (mjob == JOB_BLU || sjob == JOB_BLU) { blueutils::CalculateTraits(PChar); } PChar->delModifier(Mod::MEVA, PChar->m_magicEvasion); - PChar->m_magicEvasion = battleutils::GetMaxSkill(12, PChar->GetMLevel()); // Player MEVA is Rank G + PChar->m_magicEvasion = battleutils::GetMaxSkill(12, mlvl); // Player MEVA is Rank G PChar->addModifier(Mod::MEVA, PChar->m_magicEvasion); } diff --git a/src/map/zone.cpp b/src/map/zone.cpp index 2de4ca596a9..ac9be93dae3 100644 --- a/src/map/zone.cpp +++ b/src/map/zone.cpp @@ -1011,7 +1011,6 @@ void CZone::createZoneTimers() void CZone::CharZoneIn(CCharEntity* PChar) { TracyZoneScoped; - // ищем свободный targid для входящего в зону персонажа PChar->loc.zone = this; PChar->loc.zoning = false; @@ -1094,10 +1093,10 @@ void CZone::CharZoneIn(CCharEntity* PChar) } } - PChar->PLatentEffectContainer->CheckLatentsZone(); - monstrosity::HandleZoneIn(PChar); + PChar->PLatentEffectContainer->CheckLatentsZone(); + charutils::ReadHistory(PChar); moduleutils::OnCharZoneIn(PChar); From c9fcf54b15d4b49783f10fb75a95a3f36325402c Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Mon, 9 Oct 2023 11:36:19 +0100 Subject: [PATCH 064/103] Add getMonstrosity binding --- scripts/globals/monstrosity.lua | 10 ++++-- src/map/lua/lua_baseentity.cpp | 57 +++++++++++++++++++++++++++++++++ src/map/lua/lua_baseentity.h | 1 + 3 files changed, 65 insertions(+), 3 deletions(-) diff --git a/scripts/globals/monstrosity.lua b/scripts/globals/monstrosity.lua index 6e8715285a5..d5379186d53 100644 --- a/scripts/globals/monstrosity.lua +++ b/scripts/globals/monstrosity.lua @@ -397,7 +397,7 @@ xi.monstrosity.unlockAll = function(player) if byteOffset < 64 then data.instincts[byteOffset] = bit.bor(data.instincts[byteOffset] or 0, bit.lshift(unlockAmount, shiftAmount)) else - print("byteOffset out of range") + print('byteOffset out of range') end end @@ -409,7 +409,7 @@ xi.monstrosity.unlockAll = function(player) if byteOffset >= 20 and byteOffset < 24 then data.instincts[byteOffset] = bit.bor(data.instincts[byteOffset] or 0, bit.lshift(0x01, shiftAmount)) else - print("byteOffset out of range") + print('byteOffset out of range') end end @@ -423,7 +423,7 @@ xi.monstrosity.unlockAll = function(player) if byteOffset < 32 then data.variants[byteOffset] = bit.bor(data.variants[byteOffset] or 0, bit.lshift(0x01, shiftAmount)) else - print("byteOffset out of range") + print('byteOffset out of range') end end @@ -471,6 +471,10 @@ xi.monstrosity.feretoryOnZoneIn = function(player, prevZone) player:setPos(-358.000, -3.400, -440.00, 63) + if player:getMainJob() ~= xi.job.MON then + player:changeJob(xi.job.MON) + end + return cs end diff --git a/src/map/lua/lua_baseentity.cpp b/src/map/lua/lua_baseentity.cpp index a5cab9b37f1..933cb7835c1 100644 --- a/src/map/lua/lua_baseentity.cpp +++ b/src/map/lua/lua_baseentity.cpp @@ -6306,6 +6306,63 @@ void CLuaBaseEntity::addJobTraits(uint8 jobID, uint8 level) } } +sol::table CLuaBaseEntity::getMonstrosity() +{ + auto* PChar = dynamic_cast(m_PBaseEntity); + if (PChar == nullptr) + { + return sol::lua_nil; + } + + bool startsWithMonstrosityData = PChar->m_PMonstrosity != nullptr; + if (!startsWithMonstrosityData) + { + monstrosity::ReadMonstrosityData(PChar); + } + + auto table = lua.create_table(); + + table["monstrosityId"] = PChar->m_PMonstrosity->MonstrosityId; + table["species"] = PChar->m_PMonstrosity->Species; + table["flags"] = PChar->m_PMonstrosity->Flags; + + { + std::size_t idx = 0; + table["levels"] = lua.create_table(); + for (auto entry : PChar->m_PMonstrosity->levels) + { + table["levels"][idx++] = entry; + } + } + + { + std::size_t idx = 0; + table["instincts"] = lua.create_table(); + for (auto entry : PChar->m_PMonstrosity->instincts) + { + table["instincts"][idx++] = entry; + } + } + + { + std::size_t idx = 0; + table["variants"] = lua.create_table(); + for (auto entry : PChar->m_PMonstrosity->variants) + { + table["variants"][idx++] = entry; + } + } + + // If we didn't start with Monstrosity data, we should wipe it out now so we + // don't change modes + if (!startsWithMonstrosityData) + { + PChar->m_PMonstrosity = nullptr; + } + + return table; +} + void CLuaBaseEntity::setMonstrosity(sol::table table) { auto* PChar = dynamic_cast(m_PBaseEntity); diff --git a/src/map/lua/lua_baseentity.h b/src/map/lua/lua_baseentity.h index 318dcb1076e..084e0fe0712 100644 --- a/src/map/lua/lua_baseentity.h +++ b/src/map/lua/lua_baseentity.h @@ -330,6 +330,7 @@ class CLuaBaseEntity uint8 levelRestriction(sol::object const& level); // Establish/return current level restriction void addJobTraits(uint8 jobID, uint8 level); + auto getMonstrosity() -> sol::table; void setMonstrosity(sol::table table); // Player Titles and Fame From 1cea057b2094e4fc5cabeebea18da1bd5a7e8f77 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Mon, 9 Oct 2023 11:39:34 +0100 Subject: [PATCH 065/103] Add helpers for getSpeciesLevel and hasUnlockedSpecies --- scripts/globals/monstrosity.lua | 11 ++++++++++- src/map/lua/lua_baseentity.cpp | 5 +++-- src/map/packet_system.cpp | 4 ++-- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/scripts/globals/monstrosity.lua b/scripts/globals/monstrosity.lua index d5379186d53..10b32a64192 100644 --- a/scripts/globals/monstrosity.lua +++ b/scripts/globals/monstrosity.lua @@ -344,6 +344,15 @@ xi.monstrosity.unlockStartingMONs = function(player, choice) player:setMonstrosity(data) end +xi.monstrosity.getSpeciesLevel = function(player, species) + local data = player:getMonstrosity() + return data['levels'][species] +end + +xi.monstrosity.hasUnlockedSpecies = function(player, species) + return xi.monstrosity.getSpeciesLevel(player, species) > 0 +end + ----------------------------------- -- Debug ----------------------------------- @@ -428,7 +437,7 @@ xi.monstrosity.unlockAll = function(player) end -- Set data - player:setMonstrosity(data); + player:setMonstrosity(data) end ----------------------------------- diff --git a/src/map/lua/lua_baseentity.cpp b/src/map/lua/lua_baseentity.cpp index 933cb7835c1..85cd9ef6ee8 100644 --- a/src/map/lua/lua_baseentity.cpp +++ b/src/map/lua/lua_baseentity.cpp @@ -6336,7 +6336,7 @@ sol::table CLuaBaseEntity::getMonstrosity() } { - std::size_t idx = 0; + std::size_t idx = 0; table["instincts"] = lua.create_table(); for (auto entry : PChar->m_PMonstrosity->instincts) { @@ -6345,7 +6345,7 @@ sol::table CLuaBaseEntity::getMonstrosity() } { - std::size_t idx = 0; + std::size_t idx = 0; table["variants"] = lua.create_table(); for (auto entry : PChar->m_PMonstrosity->variants) { @@ -17238,6 +17238,7 @@ void CLuaBaseEntity::Register() SOL_REGISTER("levelRestriction", CLuaBaseEntity::levelRestriction); SOL_REGISTER("addJobTraits", CLuaBaseEntity::addJobTraits); + SOL_REGISTER("getMonstrosity", CLuaBaseEntity::getMonstrosity); SOL_REGISTER("setMonstrosity", CLuaBaseEntity::setMonstrosity); // Player Titles and Fame diff --git a/src/map/packet_system.cpp b/src/map/packet_system.cpp index a5f0da944b3..51aa79f745b 100644 --- a/src/map/packet_system.cpp +++ b/src/map/packet_system.cpp @@ -793,8 +793,8 @@ void SmallPacket0x01A(map_session_data_t* const PSession, CCharEntity* const PCh { TracyZoneScoped; - uint16 TargID = data.ref(0x08); - uint8 action = data.ref(0x0A); + uint16 TargID = data.ref(0x08); + uint8 action = data.ref(0x0A); // clang-format off position_t actionOffset = From 1f9844aa4ccde5f3e0fdb4f3e647a6bdf40ed104 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Mon, 9 Oct 2023 12:55:14 +0100 Subject: [PATCH 066/103] Reinstate Costum2 and bindings --- src/map/entities/charentity.cpp | 1 + src/map/entities/charentity.h | 1 + src/map/lua/lua_baseentity.cpp | 52 ++++++++++++++++++++++++++--- src/map/lua/lua_baseentity.h | 21 ++++++------ src/map/packets/char.cpp | 6 ++++ src/map/packets/char_appearance.cpp | 6 ++++ 6 files changed, 73 insertions(+), 14 deletions(-) diff --git a/src/map/entities/charentity.cpp b/src/map/entities/charentity.cpp index 3bcd3b7eba4..94304811414 100644 --- a/src/map/entities/charentity.cpp +++ b/src/map/entities/charentity.cpp @@ -176,6 +176,7 @@ CCharEntity::CCharEntity() m_PMonstrosity = nullptr; m_Costume = 0; + m_Costume2 = 0; m_hasTractor = 0; m_hasRaise = 0; m_weaknessLvl = 0; diff --git a/src/map/entities/charentity.h b/src/map/entities/charentity.h index 90f671cbbe0..f9a43856189 100644 --- a/src/map/entities/charentity.h +++ b/src/map/entities/charentity.h @@ -453,6 +453,7 @@ class CCharEntity : public CBattleEntity uint32 m_InsideTriggerAreaID; // The ID of the trigger area the character is inside uint8 m_LevelRestriction; // Character level limit uint16 m_Costume; + uint16 m_Costume2; uint32 m_AHHistoryTimestamp; uint32 m_DeathTimestamp; time_point m_deathSyncTime; // Timer used for sending an update packet at a regular interval while the character is dead diff --git a/src/map/lua/lua_baseentity.cpp b/src/map/lua/lua_baseentity.cpp index 85cd9ef6ee8..2e0da3a0b00 100644 --- a/src/map/lua/lua_baseentity.cpp +++ b/src/map/lua/lua_baseentity.cpp @@ -5148,6 +5148,25 @@ void CLuaBaseEntity::setModelId(uint16 modelId, sol::object const& slotObj) m_PBaseEntity->updatemask |= UPDATE_LOOK; } +/************************************************************************ + * Function: getCostume() + * Purpose : Returns the PC's appearance + * Example : player:getCostume() + ************************************************************************/ + +uint16 CLuaBaseEntity::getCostume() +{ + if (m_PBaseEntity->objtype != TYPE_PC) + { + ShowWarning("Invalid entity type calling function (%s).", m_PBaseEntity->GetName()); + return 0; + } + + auto* PChar = static_cast(m_PBaseEntity); + + return PChar->m_Costume; +} + /************************************************************************ * Function: setCostume() * Purpose : Updates the PC's appearance @@ -5173,12 +5192,12 @@ void CLuaBaseEntity::setCostume(uint16 costume) } /************************************************************************ - * Function: getCostume() + * Function: getCostume2() * Purpose : Returns the PC's appearance * Example : player:getCostume() ************************************************************************/ -uint16 CLuaBaseEntity::getCostume() +uint16 CLuaBaseEntity::getCostume2() { if (m_PBaseEntity->objtype != TYPE_PC) { @@ -5188,9 +5207,32 @@ uint16 CLuaBaseEntity::getCostume() auto* PChar = static_cast(m_PBaseEntity); - return PChar->m_Costume; + return PChar->m_Costume2; } +/************************************************************************ + * Function: setCostume2() + * Purpose : Updates the PC's appearance + * Example : player:setCostume2( costumeId ) + ************************************************************************/ + +void CLuaBaseEntity::setCostume2(uint16 costume) +{ + if (m_PBaseEntity->objtype != TYPE_PC) + { + ShowWarning("Invalid entity type calling function (%s).", m_PBaseEntity->GetName()); + return; + } + + auto* PChar = static_cast(m_PBaseEntity); + + if (PChar->m_Costume2 != costume && PChar->status != STATUS_TYPE::SHUTDOWN && PChar->status != STATUS_TYPE::DISAPPEAR) + { + PChar->m_Costume2 = costume; + PChar->updatemask |= UPDATE_LOOK; + PChar->pushPacket(new CCharAppearancePacket(PChar)); + } +} /************************************************************************ * Function: getAnimation() * Purpose : Returns the assigned default animation of an entity @@ -17179,8 +17221,10 @@ void CLuaBaseEntity::Register() SOL_REGISTER("checkNameFlags", CLuaBaseEntity::checkNameFlags); SOL_REGISTER("getModelId", CLuaBaseEntity::getModelId); SOL_REGISTER("setModelId", CLuaBaseEntity::setModelId); - SOL_REGISTER("setCostume", CLuaBaseEntity::setCostume); SOL_REGISTER("getCostume", CLuaBaseEntity::getCostume); + SOL_REGISTER("setCostume", CLuaBaseEntity::setCostume); + SOL_REGISTER("getCostume2", CLuaBaseEntity::getCostume2); + SOL_REGISTER("setCostume2", CLuaBaseEntity::setCostume2); SOL_REGISTER("getAnimation", CLuaBaseEntity::getAnimation); SOL_REGISTER("setAnimation", CLuaBaseEntity::setAnimation); SOL_REGISTER("getAnimationSub", CLuaBaseEntity::getAnimationSub); diff --git a/src/map/lua/lua_baseentity.h b/src/map/lua/lua_baseentity.h index 084e0fe0712..dedd2214d03 100644 --- a/src/map/lua/lua_baseentity.h +++ b/src/map/lua/lua_baseentity.h @@ -269,17 +269,18 @@ class CLuaBaseEntity bool checkNameFlags(uint32 flags); // this is check and not get because it tests for a flag, it doesn't return all flags uint16 getModelId(); void setModelId(uint16 modelId, sol::object const& slotObj); - void setCostume(uint16 costume); uint16 getCostume(); - - uint8 getAnimation(); - void setAnimation(uint8 animation); - uint8 getAnimationSub(); - void setAnimationSub(uint8 animationsub); - bool getCallForHelpFlag() const; - void setCallForHelpFlag(bool cfh); - bool getCallForHelpBlocked() const; - void setCallForHelpBlocked(bool blocked); + void setCostume(uint16 costume); + uint16 getCostume2(); + void setCostume2(uint16 costume); + uint8 getAnimation(); + void setAnimation(uint8 animation); + uint8 getAnimationSub(); + void setAnimationSub(uint8 animationsub); + bool getCallForHelpFlag() const; + void setCallForHelpFlag(bool cfh); + bool getCallForHelpBlocked() const; + void setCallForHelpBlocked(bool blocked); // Player Status uint8 getNation(); diff --git a/src/map/packets/char.cpp b/src/map/packets/char.cpp index d60069bc2af..5109a4d7a36 100644 --- a/src/map/packets/char.cpp +++ b/src/map/packets/char.cpp @@ -178,6 +178,12 @@ void CCharPacket::updateWith(CCharEntity* PChar, ENTITYUPDATE type, uint8 update ref(0x54) = look->main + 0x6000; ref(0x56) = look->sub + 0x7000; ref(0x58) = look->ranged + 0x8000; + + if (PChar->m_Costume2 != 0) + { + ref(0x48) = PChar->m_Costume2; + ref(0x58) = 0xFFFF; + } } if (updatemask & UPDATE_NAME) diff --git a/src/map/packets/char_appearance.cpp b/src/map/packets/char_appearance.cpp index 47435f9b28b..b0f9da09241 100644 --- a/src/map/packets/char_appearance.cpp +++ b/src/map/packets/char_appearance.cpp @@ -41,6 +41,12 @@ CCharAppearancePacket::CCharAppearancePacket(CCharEntity* PChar) ref(0x12) = look->sub + 0x7000; ref(0x14) = look->ranged + 0x8000; + if (PChar->m_Costume2 != 0) + { + ref(0x04) = PChar->m_Costume2; + ref(0x14) = 0xFFFF; + } + if (PChar->m_PMonstrosity != nullptr) { ref(0x04) = PChar->m_PMonstrosity->Look; From f3de97ba64fea1c9391f068fff131fc8c360d451 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Mon, 9 Oct 2023 15:31:45 +0100 Subject: [PATCH 067/103] Change bindings to MonstrosityData() --- scripts/globals/monstrosity.lua | 60 ++++++++++++++++++---------- src/map/lua/lua_baseentity.cpp | 9 +++-- src/map/lua/lua_baseentity.h | 5 ++- src/map/lua/luautils.cpp | 20 ++++++++++ src/map/lua/luautils.h | 2 + src/map/monstrosity.cpp | 69 +++++++++++++++++++-------------- 6 files changed, 108 insertions(+), 57 deletions(-) diff --git a/scripts/globals/monstrosity.lua b/scripts/globals/monstrosity.lua index 10b32a64192..628b0cccfaf 100644 --- a/scripts/globals/monstrosity.lua +++ b/scripts/globals/monstrosity.lua @@ -341,11 +341,11 @@ xi.monstrosity.unlockStartingMONs = function(player, choice) species = choice, } - player:setMonstrosity(data) + player:setMonstrosityData(data) end xi.monstrosity.getSpeciesLevel = function(player, species) - local data = player:getMonstrosity() + local data = player:getMonstrosityData() return data['levels'][species] end @@ -353,6 +353,41 @@ xi.monstrosity.hasUnlockedSpecies = function(player, species) return xi.monstrosity.getSpeciesLevel(player, species) > 0 end +----------------------------------- +-- Bound by C++ (DO NOT CHANGE SIGNATURE) +----------------------------------- + +xi.monstrosity.onMonstrosityUpdate = function(player) + local data = player:getMonstrosityData() + + -- Tap level-based unlocks + + -- Instincts by MON level + -- NOTE: Since this is a bitfield, it's zero-indexed! + for _, val in pairs(xi.monstrosity.species) do + local speciesKey = val + local speciesLevel = data.levels[val] + local byteOffset = math.floor(speciesKey / 4) + local unlockAmount = math.floor(speciesLevel / 30) + local shiftAmount = (speciesKey * 2) % 8 + + -- Special case for writing Slime & Spriggan data at the end of the 64-byte array + if byteOffset == 31 then + byteOffset = 63 + end + + if byteOffset < 64 then + data.instincts[byteOffset] = bit.bor(data.instincts[byteOffset] or 0, bit.lshift(unlockAmount, shiftAmount)) + else + print('byteOffset out of range') + end + end + + -- TODO: Handle level-based variants here + + player:setMonstrosityData(data) +end + ----------------------------------- -- Debug ----------------------------------- @@ -365,24 +400,7 @@ xi.monstrosity.unlockAll = function(player) -- Add Monstrosity key item player:addKeyItem(xi.keyItem.RING_OF_SUPERNAL_DISJUNCTION) - local data = - { - -- 1 byte per entry, mapped out to species table - -- (0 - 127) - levels = - { - }, - - -- Bitfield (0 - 63) - instincts = - { - }, - - -- Bitfield (0 - 31) - variants = - { - }, - } + local data = player:getMonstrosityData() -- Set all levels to 99 for _, val in pairs(xi.monstrosity.species) do @@ -437,7 +455,7 @@ xi.monstrosity.unlockAll = function(player) end -- Set data - player:setMonstrosity(data) + player:setMonstrosityData(data) end ----------------------------------- diff --git a/src/map/lua/lua_baseentity.cpp b/src/map/lua/lua_baseentity.cpp index 2e0da3a0b00..e84f4cf03b0 100644 --- a/src/map/lua/lua_baseentity.cpp +++ b/src/map/lua/lua_baseentity.cpp @@ -6348,7 +6348,7 @@ void CLuaBaseEntity::addJobTraits(uint8 jobID, uint8 level) } } -sol::table CLuaBaseEntity::getMonstrosity() +sol::table CLuaBaseEntity::getMonstrosityData() { auto* PChar = dynamic_cast(m_PBaseEntity); if (PChar == nullptr) @@ -6405,7 +6405,7 @@ sol::table CLuaBaseEntity::getMonstrosity() return table; } -void CLuaBaseEntity::setMonstrosity(sol::table table) +void CLuaBaseEntity::setMonstrosityData(sol::table table) { auto* PChar = dynamic_cast(m_PBaseEntity); if (PChar == nullptr) @@ -17282,8 +17282,9 @@ void CLuaBaseEntity::Register() SOL_REGISTER("levelRestriction", CLuaBaseEntity::levelRestriction); SOL_REGISTER("addJobTraits", CLuaBaseEntity::addJobTraits); - SOL_REGISTER("getMonstrosity", CLuaBaseEntity::getMonstrosity); - SOL_REGISTER("setMonstrosity", CLuaBaseEntity::setMonstrosity); + // Monstrosity + SOL_REGISTER("getMonstrosityData", CLuaBaseEntity::getMonstrosityData); + SOL_REGISTER("setMonstrosityData", CLuaBaseEntity::setMonstrosityData); // Player Titles and Fame SOL_REGISTER("getTitle", CLuaBaseEntity::getTitle); diff --git a/src/map/lua/lua_baseentity.h b/src/map/lua/lua_baseentity.h index dedd2214d03..6cbee316b1c 100644 --- a/src/map/lua/lua_baseentity.h +++ b/src/map/lua/lua_baseentity.h @@ -331,8 +331,9 @@ class CLuaBaseEntity uint8 levelRestriction(sol::object const& level); // Establish/return current level restriction void addJobTraits(uint8 jobID, uint8 level); - auto getMonstrosity() -> sol::table; - void setMonstrosity(sol::table table); + // Monstrosity + auto getMonstrosityData() -> sol::table; + void setMonstrosityData(sol::table table); // Player Titles and Fame uint16 getTitle(); diff --git a/src/map/lua/luautils.cpp b/src/map/lua/luautils.cpp index 3ae49df0c07..b323f839fa3 100644 --- a/src/map/lua/luautils.cpp +++ b/src/map/lua/luautils.cpp @@ -3888,6 +3888,26 @@ namespace luautils return result.get_type(0) == sol::type::number ? result.get(0) : 0; } + void OnMonstrosityUpdate(CBaseEntity* PChar) + { + TracyZoneScoped; + + sol::function onMonstrosityUpdate = lua["xi"]["monstrosity"]["onMonstrosityUpdate"]; + if (!onMonstrosityUpdate.valid()) + { + ShowError("luautils::OnMonstrosityUpdate"); + return; + } + + auto result = onMonstrosityUpdate(CLuaBaseEntity(PChar)); + if (!result.valid()) + { + sol::error err = result; + ShowError("luautils::OnMonstrosityUpdate: %s", err.what()); + return; + } + } + int32 OnMagicCastingCheck(CBaseEntity* PChar, CBaseEntity* PTarget, CSpell* PSpell) { TracyZoneScoped; diff --git a/src/map/lua/luautils.h b/src/map/lua/luautils.h index 3f2fdaf70c7..d89ddb52fc2 100644 --- a/src/map/lua/luautils.h +++ b/src/map/lua/luautils.h @@ -285,6 +285,8 @@ namespace luautils int32 OnAutomatonAbilityCheck(CBaseEntity* PChar, CAutomatonEntity* PAutomaton, CMobSkill* PMobSkill); int32 OnAutomatonAbility(CBaseEntity* PTarget, CBaseEntity* PMob, CMobSkill* PMobSkill, CBaseEntity* PMobMaster, action_t* action); + void OnMonstrosityUpdate(CBaseEntity* PChar); + int32 OnAbilityCheck(CBaseEntity* PChar, CBaseEntity* PTarget, CAbility* PAbility, CBaseEntity** PMsgTarget); int32 OnPetAbility(CBaseEntity* PTarget, CBaseEntity* PMob, CMobSkill* PMobSkill, CBaseEntity* PPetMaster, action_t* action); int32 OnPetAbility(CBaseEntity* PTarget, CPetEntity* PPet, CPetSkill* PMobSkill, CBaseEntity* PPetMaster, action_t* action); // triggers when pet uses an ability, specialized for pets diff --git a/src/map/monstrosity.cpp b/src/map/monstrosity.cpp index 68ab1417ce1..30833435fb3 100644 --- a/src/map/monstrosity.cpp +++ b/src/map/monstrosity.cpp @@ -26,6 +26,8 @@ #include "entities/charentity.h" +#include "lua/luautils.h" + #include "packets/char_abilities.h" #include "packets/char_appearance.h" #include "packets/char_job_extra.h" @@ -224,48 +226,53 @@ void monstrosity::TryPopulateMonstrosityData(CCharEntity* PChar) void monstrosity::HandleZoneIn(CCharEntity* PChar) { - if (PChar->m_PMonstrosity != nullptr) + if (PChar->m_PMonstrosity == nullptr) { - // Add stats from equipped instincts - for (auto instinctId : PChar->m_PMonstrosity->EquippedInstincts) + return; + } + + // Add stats from equipped instincts + for (auto instinctId : PChar->m_PMonstrosity->EquippedInstincts) + { + auto maybeInstinct = gMonstrosityInstinctMap.find(instinctId); + if (maybeInstinct != gMonstrosityInstinctMap.end()) { - auto maybeInstinct = gMonstrosityInstinctMap.find(instinctId); - if (maybeInstinct != gMonstrosityInstinctMap.end()) + auto instinct = (*maybeInstinct).second; + for (auto const& mod : instinct.mods) { - auto instinct = (*maybeInstinct).second; - for (auto const& mod : instinct.mods) - { - PChar->addModifier(mod.getModID(), mod.getModAmount()); - } + PChar->addModifier(mod.getModID(), mod.getModAmount()); } } + } - if (PChar->loc.zone->GetID() != ZONE_FERETORY) - { - // TODO: If belligerency, timer is 60s - CStatusEffect* PEffect = new CStatusEffect(EFFECT::EFFECT_GESTATION, EFFECT::EFFECT_GESTATION, 0, 0, 64800); // 18 hours - - // TODO: You cannot attack while you're in Gest., you have to click it off + // TODO: There are more conditions to handle here + if (PChar->loc.zone->GetID() != ZONE_FERETORY) + { + // TODO: If belligerency, timer is 60s + CStatusEffect* PEffect = new CStatusEffect(EFFECT::EFFECT_GESTATION, EFFECT::EFFECT_GESTATION, 0, 0, 64800); // 18 hours - // TODO: Move these into the db - PEffect->AddEffectFlag(EFFECTFLAG_INVISIBLE); - PEffect->AddEffectFlag(EFFECTFLAG_DEATH); - PEffect->AddEffectFlag(EFFECTFLAG_ATTACK); - PEffect->AddEffectFlag(EFFECTFLAG_MAGIC_BEGIN); - PEffect->AddEffectFlag(EFFECTFLAG_DETECTABLE); - PEffect->AddEffectFlag(EFFECTFLAG_ON_ZONE); + // TODO: You cannot attack while you're in Gest., you have to click it off - // TODO: Does the timer survive logout, does it tick while offline, does it reset? - // PEffect->AddEffectFlag(EFFECTFLAG_LOGOUT); + // TODO: Move these into the db + PEffect->AddEffectFlag(EFFECTFLAG_INVISIBLE); + PEffect->AddEffectFlag(EFFECTFLAG_DEATH); + PEffect->AddEffectFlag(EFFECTFLAG_ATTACK); + PEffect->AddEffectFlag(EFFECTFLAG_MAGIC_BEGIN); + PEffect->AddEffectFlag(EFFECTFLAG_DETECTABLE); + PEffect->AddEffectFlag(EFFECTFLAG_ON_ZONE); - // NOTE: It DOES say the effect wears off - // PEffect->AddEffectFlag(EFFECTFLAG_NO_LOSS_MESSAGE); + // TODO: Does the timer survive logout, does it tick while offline, does it reset? + // PEffect->AddEffectFlag(EFFECTFLAG_LOGOUT); - PChar->StatusEffectContainer->AddStatusEffect(PEffect, true); - } + // NOTE: It DOES say the effect wears off + // PEffect->AddEffectFlag(EFFECTFLAG_NO_LOSS_MESSAGE); - PChar->updatemask |= UPDATE_LOOK; + PChar->StatusEffectContainer->AddStatusEffect(PEffect, true); } + + SendFullMonstrosityUpdate(PChar); + + PChar->updatemask |= UPDATE_LOOK; } uint32 monstrosity::GetPackedMonstrosityName(CCharEntity* PChar) @@ -301,6 +308,8 @@ void monstrosity::SendFullMonstrosityUpdate(CCharEntity* PChar) charutils::BuildingCharTraitsTable(PChar); + luautils::OnMonstrosityUpdate(PChar); + PChar->pushPacket(new CMonipulatorPacket1(PChar)); PChar->pushPacket(new CMonipulatorPacket2(PChar)); PChar->pushPacket(new CCharJobsPacket(PChar)); From 240f1ee6bb29ef15ab29d5cf2a69414cee23ccdb Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Mon, 9 Oct 2023 18:12:41 +0100 Subject: [PATCH 068/103] Add MON technical blurb --- scripts/commands/monstrosity.lua | 4 ++++ scripts/globals/monstrosity.lua | 26 +++++++++++++++++++++++++- src/map/monstrosity.cpp | 4 ++++ src/map/monstrosity.h | 3 +++ 4 files changed, 36 insertions(+), 1 deletion(-) diff --git a/scripts/commands/monstrosity.lua b/scripts/commands/monstrosity.lua index 32b0f611176..5e38c246043 100644 --- a/scripts/commands/monstrosity.lua +++ b/scripts/commands/monstrosity.lua @@ -1,3 +1,7 @@ +----------------------------------- +-- func: monstrosity +-- desc: scripts/globals/monstrosity.lua for a general overview of how Monstrosity works and is designed. +----------------------------------- local commandObj = {} commandObj.cmdprops = diff --git a/scripts/globals/monstrosity.lua b/scripts/globals/monstrosity.lua index 628b0cccfaf..7324882ed30 100644 --- a/scripts/globals/monstrosity.lua +++ b/scripts/globals/monstrosity.lua @@ -1,5 +1,29 @@ ----------------------------------- --- Monstrosity +-- Monstrosity (MON) +-- +-- === How does it work? === +-- +-- Monstrosity is enabled through two mechanisms: setting your job to JOB_MON (23) and zoning. +-- Currently, there are some details that seemingly can only be populated at zone-time, so switching in/out +-- of MON mode is reliant on zoning. +-- +-- When you zone your job will be checked, and if it is JOB_MON, then PChar->m_PMonstrosity will get +-- populated with your relevant Monstrosity data from table defined in char_monstrosity.sql. If you don't have +-- this information yet, it'll be created and saved for you with the defaults (the starting 3 MONs and the basic instincts). +-- +-- Most other logic for determining stats, exp, exp ranges, traits, etc. will check you are either JOB_MON +-- or have m_PMonstrosity populated, and then look up what main/sub job your current species is, and then +-- forward that information into the relevant code for working out stats, etc. +-- +-- IT IS VITAL that m_PMonstrosity is managed correctly, or that it's existance is constantly checked. +-- +-- There is _a lot_ of client-side validation for MON, but we have all the information available server-side, +-- so we make sure to validate everything that comes through the zone_in and MON equip packets. It's also important +-- to validate all things for MON, because if they're invalid the client will get stuck in a state where they can't change +-- jobs, species, instincts, or names without GM intervention. +-- +-- MONs main and subjob are in lock-step, so if you are a MNK15/NIN, the NIN will also be Lv15, and you'll get all the abilities, +-- traits, and stat contributions (TODO?) from both - except for the 2H which comes from the main job. ----------------------------------- require('scripts/globals/npc_util') require('scripts/globals/quests') diff --git a/src/map/monstrosity.cpp b/src/map/monstrosity.cpp index 30833435fb3..0d6e30a1f20 100644 --- a/src/map/monstrosity.cpp +++ b/src/map/monstrosity.cpp @@ -19,6 +19,10 @@ =========================================================================== */ +// === +// See scripts/globals/monstrosity.lua for a general overview of how Monstrosity works and is designed. +// === + #include "monstrosity.h" #include "common/logging.h" diff --git a/src/map/monstrosity.h b/src/map/monstrosity.h index 3cbf049ce8a..32b90ecc72c 100644 --- a/src/map/monstrosity.h +++ b/src/map/monstrosity.h @@ -31,6 +31,9 @@ class CCharEntity; +// === +// See scripts/globals/monstrosity.lua for a general overview of how Monstrosity works and is designed. +// === namespace monstrosity { struct MonstrosityData_t From fb7c7a264267257cd519c8953fca693e8db606cd Mon Sep 17 00:00:00 2001 From: claywar Date: Mon, 9 Oct 2023 15:41:12 -0400 Subject: [PATCH 069/103] Interaction - Hidden Quest: Monstrosity Bee Unlock --- scripts/globals/monstrosity.lua | 29 ------- .../quests/hiddenQuests/Monstrosity_Bee.lua | 82 +++++++++++++++++++ scripts/zones/Feretory/DefaultActions.lua | 5 ++ scripts/zones/Feretory/IDs.lua | 1 + scripts/zones/Feretory/npcs/Suibhne.lua | 10 +-- 5 files changed, 90 insertions(+), 37 deletions(-) create mode 100644 scripts/quests/hiddenQuests/Monstrosity_Bee.lua create mode 100644 scripts/zones/Feretory/DefaultActions.lua diff --git a/scripts/globals/monstrosity.lua b/scripts/globals/monstrosity.lua index 7324882ed30..3785d1a9b94 100644 --- a/scripts/globals/monstrosity.lua +++ b/scripts/globals/monstrosity.lua @@ -607,32 +607,3 @@ end xi.monstrosity.maccusOnEventFinish = function(player, csid, option, npc) -- print('finish', csid, option) end - ------------------------------------ --- Suibhne (Feretory NPC) ------------------------------------ - -xi.monstrosity.suibhneOnTrade = function(player, npc, trade) -end - -xi.monstrosity.suibhneOnTrigger = function(player, npc) - player:startEvent(11, 1, 1, 0, 0, 0, 0, 0, 0) -end - -xi.monstrosity.suibhneOnEventUpdate = function(player, csid, option, npc) - -- print('update', csid, option) -end - -xi.monstrosity.suibhneOnEventFinish = function(player, csid, option, npc) - -- print('finish', csid, option) - -- Answers: - -- 1) 4. Teyrnon - -- 2) 3. Suibhne - -- 3) 1. Aengus - if csid == 11 and option == 2 then - -- Quiz failed - elseif csid == 11 and option == 6029313 then - -- Quiz succeeded - -- TODO: Unlock Bee (MON) - end -end diff --git a/scripts/quests/hiddenQuests/Monstrosity_Bee.lua b/scripts/quests/hiddenQuests/Monstrosity_Bee.lua new file mode 100644 index 00000000000..7e2ddd1ac9a --- /dev/null +++ b/scripts/quests/hiddenQuests/Monstrosity_Bee.lua @@ -0,0 +1,82 @@ +----------------------------------- +-- Monstrosity: Suibhne Bee Guessing Game +----------------------------------- +-- Suibhne : !pos -366 -3.612 -466 285 +----------------------------------- +local feretoryID = zones[xi.zone.FERETORY] +----------------------------------- + +local quest = HiddenQuest:new('monstrosityBee') + +quest.reward = {} + +local choices = +{ + AENGUS = 1, + MACCUS = 2, + SUIBHNE = 3, + TERYNON = 4, +} + +local answerKey = +{ + [1] = { [0] = choices.TERYNON, [1] = choices.AENGUS, [2] = choices.SUIBHNE }, + [2] = { [0] = choices.TERYNON, [1] = choices.SUIBHNE, [2] = choices.AENGUS }, + [3] = { [0] = choices.AENGUS, [1] = choices.TERYNON, [2] = choices.SUIBHNE }, + [4] = { [0] = choices.AENGUS, [1] = choices.SUIBHNE, [2] = choices.TERYNON }, + [5] = { [0] = choices.SUIBHNE, [1] = choices.AENGUS, [2] = choices.TERYNON }, + [6] = { [0] = choices.SUIBHNE, [1] = choices.TERYNON, [2] = choices.AENGUS }, +} + +local function getCheckValue(choiceNum) + local checkValue = 0 + + for fieldPos, correctChoice in pairs(answerKey[choiceNum]) do + checkValue = checkValue + bit.lshift(correctChoice, 16 + 3 * fieldPos) + end + + return checkValue +end + +quest.sections = +{ + { + check = function(player, questVars, vars) + return quest:getVar(player, 'Timer') <= VanadielUniqueDay() and + not xi.monstrosity.hasUnlockedSpecies(xi.monstrosity.species.BEE) + end, + + [xi.zone.FERETORY] = + { + ['Suibhne'] = + { + onTrigger = function(player, npc) + local quizInfo = quest:getVar(player, 'Option') + + if quizInfo == 0 then + quizInfo = math.random(1, 6) + quest:setVar(player, 'Option', quizInfo) + end + + return quest:progressEvent(11, 1, quizInfo - 1) + end, + }, + + onEventFinish = + { + [11] = function(player, csid, option, npc) + if option == 1 + getCheckValue(quest:getVar(player, 'Option')) then + if quest:complete(player) then + -- TODO: Unlock Bee Species! + player:messageSpecial(feretoryID.text.MAY_POSSESS_BEES) + end + elseif option == 2 then + quest:setVar(player, 'Timer', VanadielUniqueDay() + 1) + end + end, + }, + }, + }, +} + +return quest diff --git a/scripts/zones/Feretory/DefaultActions.lua b/scripts/zones/Feretory/DefaultActions.lua new file mode 100644 index 00000000000..0de848f990a --- /dev/null +++ b/scripts/zones/Feretory/DefaultActions.lua @@ -0,0 +1,5 @@ +-- local ID = zones[xi.zone.FERETORY] + +return { + ['Suibhne'] = { event = 11 }, +} diff --git a/scripts/zones/Feretory/IDs.lua b/scripts/zones/Feretory/IDs.lua index 438d1708c66..508b3c15941 100644 --- a/scripts/zones/Feretory/IDs.lua +++ b/scripts/zones/Feretory/IDs.lua @@ -15,6 +15,7 @@ zones[xi.zone.FERETORY] = LOGIN_CAMPAIGN_UNDERWAY = 7002, -- The [/January/February/March/April/May/June/July/August/September/October/November/December] Login Campaign is currently underway! LOGIN_NUMBER = 7003, -- In celebration of your most recent login (login no. ), we have provided you with points! You currently have a total of points. MEMBERS_LEVELS_ARE_RESTRICTED = 7023, -- Your party is unable to participate because certain members' levels are restricted. + MAY_POSSESS_BEES = 7382, -- You may now possess bees! }, mob = { diff --git a/scripts/zones/Feretory/npcs/Suibhne.lua b/scripts/zones/Feretory/npcs/Suibhne.lua index 0588fc943ff..926eeb6e09e 100644 --- a/scripts/zones/Feretory/npcs/Suibhne.lua +++ b/scripts/zones/Feretory/npcs/Suibhne.lua @@ -1,26 +1,20 @@ ----------------------------------- -- Area: Feretory --- NPC: Suibhne --- !pos TODO ------------------------------------ -require('scripts/globals/monstrosity') +-- NPC: Suibhne +-- !pos -366 -3.612 -466 285 ----------------------------------- local entity = {} entity.onTrade = function(player, npc, trade) - xi.monstrosity.suibhneOnTrade(player, npc, trade) end entity.onTrigger = function(player, npc) - xi.monstrosity.suibhneOnTrigger(player, npc) end entity.onEventUpdate = function(player, csid, option, npc) - xi.monstrosity.suibhneOnEventUpdate(player, csid, option, npc) end entity.onEventFinish = function(player, csid, option, npc) - xi.monstrosity.suibhneOnEventFinish(player, csid, option, npc) end return entity From adce6a0c8f99d437e6f53ab93816eccc4795f7cf Mon Sep 17 00:00:00 2001 From: claywar Date: Mon, 9 Oct 2023 16:19:41 -0400 Subject: [PATCH 070/103] Add helpers for species level and unlock Co-authored-by: zach2good --- scripts/globals/monstrosity.lua | 44 +++++++++++++++++++ .../quests/hiddenQuests/Monstrosity_Bee.lua | 2 +- 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/scripts/globals/monstrosity.lua b/scripts/globals/monstrosity.lua index 3785d1a9b94..53c2c0cb43c 100644 --- a/scripts/globals/monstrosity.lua +++ b/scripts/globals/monstrosity.lua @@ -377,6 +377,50 @@ xi.monstrosity.hasUnlockedSpecies = function(player, species) return xi.monstrosity.getSpeciesLevel(player, species) > 0 end +xi.monstrosity.setSpeciesLevel = function(player, species, level) + local data = player:getMonstrosityData() + data.levels[species] = level + player:setMonstrosityData(data) +end + +xi.monstrosity.unlockSpecies = function(player, species) + if not xi.monstrosity.hasUnlockedSpecies(player, species) then + xi.monstrosity.setSpeciesLevel(player, species, 1) + end +end + +-- Use xi.monstrosity.variants +xi.monstrosity.hasUnlockedVariant = function(player, variant) + local data = player:getMonstrosityData() + + local byteOffset = math.floor(variant / 8) + local shiftAmount = variant % 8 + + if byteOffset < 32 then + return bit.band(data.variants[byteOffset] or 0, bit.lshift(0x01, shiftAmount)) > 0 + end + + return false +end + +-- Use xi.monstrosity.variants +xi.monstrosity.unlockVariant = function(player, variant) + if not xi.monstrosity.hasUnlockedVariant(player, variant) then + local data = player:getMonstrosityData() + + local byteOffset = math.floor(variant / 8) + local shiftAmount = variant % 8 + + if byteOffset < 32 then + data.variants[byteOffset] = bit.bor(data.variants[byteOffset] or 0, bit.lshift(0x01, shiftAmount)) + else + print('byteOffset out of range') + end + + player:setMonstrosityData(data) + end +end + ----------------------------------- -- Bound by C++ (DO NOT CHANGE SIGNATURE) ----------------------------------- diff --git a/scripts/quests/hiddenQuests/Monstrosity_Bee.lua b/scripts/quests/hiddenQuests/Monstrosity_Bee.lua index 7e2ddd1ac9a..c85ecd35790 100644 --- a/scripts/quests/hiddenQuests/Monstrosity_Bee.lua +++ b/scripts/quests/hiddenQuests/Monstrosity_Bee.lua @@ -67,7 +67,7 @@ quest.sections = [11] = function(player, csid, option, npc) if option == 1 + getCheckValue(quest:getVar(player, 'Option')) then if quest:complete(player) then - -- TODO: Unlock Bee Species! + xi.monstrosity.unlockSpecies(xi.monstrosity.species.BEE) player:messageSpecial(feretoryID.text.MAY_POSSESS_BEES) end elseif option == 2 then From 83e49207e03904c68382ba18b1eacc86ea8541ff Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Mon, 9 Oct 2023 22:28:52 +0100 Subject: [PATCH 071/103] Stop MONs using onTrigger, for now --- scripts/globals/monstrosity.lua | 6 +++++- src/map/packet_system.cpp | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/scripts/globals/monstrosity.lua b/scripts/globals/monstrosity.lua index 53c2c0cb43c..0c377da0888 100644 --- a/scripts/globals/monstrosity.lua +++ b/scripts/globals/monstrosity.lua @@ -357,7 +357,7 @@ xi.monstrosity.purchasableInstincts = ----------------------------------- -- Helpers ----------------------------------- - +-- Use xi.monstrosity.species xi.monstrosity.unlockStartingMONs = function(player, choice) local data = { @@ -368,21 +368,25 @@ xi.monstrosity.unlockStartingMONs = function(player, choice) player:setMonstrosityData(data) end +-- Use xi.monstrosity.species xi.monstrosity.getSpeciesLevel = function(player, species) local data = player:getMonstrosityData() return data['levels'][species] end +-- Use xi.monstrosity.species xi.monstrosity.hasUnlockedSpecies = function(player, species) return xi.monstrosity.getSpeciesLevel(player, species) > 0 end +-- Use xi.monstrosity.species xi.monstrosity.setSpeciesLevel = function(player, species, level) local data = player:getMonstrosityData() data.levels[species] = level player:setMonstrosityData(data) end +-- Use xi.monstrosity.species xi.monstrosity.unlockSpecies = function(player, species) if not xi.monstrosity.hasUnlockedSpecies(player, species) then xi.monstrosity.setSpeciesLevel(player, species, 1) diff --git a/src/map/packet_system.cpp b/src/map/packet_system.cpp index 51aa79f745b..c0724c34ece 100644 --- a/src/map/packet_system.cpp +++ b/src/map/packet_system.cpp @@ -865,7 +865,7 @@ void SmallPacket0x01A(map_session_data_t* const PSession, CCharEntity* const PCh // Monstrosity: Can't really do anything while under Gestation until you click it off. // : MONs can trigger doors, so we'll handle that later. // TODO! - if (PChar->StatusEffectContainer->HasStatusEffect(EFFECT_GESTATION) && action != 0x00 /* trigger */) + if (PChar->StatusEffectContainer->HasStatusEffect(EFFECT_GESTATION)) { return; } From 06e5b3ac42ae2a9c9ef3ad6ee1f2ca079629173e Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Tue, 10 Oct 2023 09:05:38 +0100 Subject: [PATCH 072/103] Fix infinite MON data loop --- scripts/globals/monstrosity.lua | 6 +-- src/map/lua/lua_baseentity.cpp | 78 +-------------------------- src/map/lua/luautils.cpp | 96 ++++++++++++++++++++++++++++++++- src/map/lua/luautils.h | 4 +- 4 files changed, 100 insertions(+), 84 deletions(-) diff --git a/scripts/globals/monstrosity.lua b/scripts/globals/monstrosity.lua index 0c377da0888..640172a80da 100644 --- a/scripts/globals/monstrosity.lua +++ b/scripts/globals/monstrosity.lua @@ -429,9 +429,7 @@ end -- Bound by C++ (DO NOT CHANGE SIGNATURE) ----------------------------------- -xi.monstrosity.onMonstrosityUpdate = function(player) - local data = player:getMonstrosityData() - +xi.monstrosity.onMonstrosityUpdate = function(player, data) -- Tap level-based unlocks -- Instincts by MON level @@ -456,8 +454,6 @@ xi.monstrosity.onMonstrosityUpdate = function(player) end -- TODO: Handle level-based variants here - - player:setMonstrosityData(data) end ----------------------------------- diff --git a/src/map/lua/lua_baseentity.cpp b/src/map/lua/lua_baseentity.cpp index e84f4cf03b0..93e9ba4a77f 100644 --- a/src/map/lua/lua_baseentity.cpp +++ b/src/map/lua/lua_baseentity.cpp @@ -6362,38 +6362,7 @@ sol::table CLuaBaseEntity::getMonstrosityData() monstrosity::ReadMonstrosityData(PChar); } - auto table = lua.create_table(); - - table["monstrosityId"] = PChar->m_PMonstrosity->MonstrosityId; - table["species"] = PChar->m_PMonstrosity->Species; - table["flags"] = PChar->m_PMonstrosity->Flags; - - { - std::size_t idx = 0; - table["levels"] = lua.create_table(); - for (auto entry : PChar->m_PMonstrosity->levels) - { - table["levels"][idx++] = entry; - } - } - - { - std::size_t idx = 0; - table["instincts"] = lua.create_table(); - for (auto entry : PChar->m_PMonstrosity->instincts) - { - table["instincts"][idx++] = entry; - } - } - - { - std::size_t idx = 0; - table["variants"] = lua.create_table(); - for (auto entry : PChar->m_PMonstrosity->variants) - { - table["variants"][idx++] = entry; - } - } + auto table = luautils::GetMonstrosityLuaTable(PChar); // If we didn't start with Monstrosity data, we should wipe it out now so we // don't change modes @@ -6418,50 +6387,7 @@ void CLuaBaseEntity::setMonstrosityData(sol::table table) // NOTE: This will populate m_PMonstrosity if it doesn't exist monstrosity::ReadMonstrosityData(PChar); - if (table["monstrosityId"].valid()) - { - PChar->m_PMonstrosity->MonstrosityId = table.get("monstrosityId"); - } - - if (table["species"].valid()) - { - PChar->m_PMonstrosity->Species = table.get("species"); - } - - if (table["flags"].valid()) - { - PChar->m_PMonstrosity->Flags = table.get("flags"); - } - - if (table["levels"].valid()) - { - for (auto const& [keyObj, valObj] : table.get("levels")) - { - uint8 key = keyObj.as(); - uint8 val = valObj.as(); - PChar->m_PMonstrosity->levels[key] |= val; - } - } - - if (table["instincts"].valid()) - { - for (auto const& [keyObj, valObj] : table.get("instincts")) - { - uint8 key = keyObj.as(); - uint8 val = valObj.as(); - PChar->m_PMonstrosity->instincts[key] |= val; - } - } - - if (table["variants"].valid()) - { - for (auto const& [keyObj, valObj] : table.get("variants")) - { - uint8 key = keyObj.as(); - uint8 val = valObj.as(); - PChar->m_PMonstrosity->variants[key] |= val; - } - } + luautils::SetMonstrosityLuaTable(PChar, table); monstrosity::WriteMonstrosityData(PChar); diff --git a/src/map/lua/luautils.cpp b/src/map/lua/luautils.cpp index b323f839fa3..64cc2c2da02 100644 --- a/src/map/lua/luautils.cpp +++ b/src/map/lua/luautils.cpp @@ -75,6 +75,7 @@ #include "map.h" #include "message.h" #include "mobskill.h" +#include "monstrosity.h" #include "packets/action.h" #include "packets/char_emotion.h" #include "packets/char_update.h" @@ -3888,7 +3889,98 @@ namespace luautils return result.get_type(0) == sol::type::number ? result.get(0) : 0; } - void OnMonstrosityUpdate(CBaseEntity* PChar) + auto GetMonstrosityLuaTable(CCharEntity* PChar) -> sol::table + { + TracyZoneScoped; + + // TODO: lua_monstrosity.cpp? + auto table = lua.create_table(); + + table["monstrosityId"] = PChar->m_PMonstrosity->MonstrosityId; + table["species"] = PChar->m_PMonstrosity->Species; + table["flags"] = PChar->m_PMonstrosity->Flags; + + { + std::size_t idx = 0; + table["levels"] = lua.create_table(); + for (auto entry : PChar->m_PMonstrosity->levels) + { + table["levels"][idx++] = entry; + } + } + + { + std::size_t idx = 0; + table["instincts"] = lua.create_table(); + for (auto entry : PChar->m_PMonstrosity->instincts) + { + table["instincts"][idx++] = entry; + } + } + + { + std::size_t idx = 0; + table["variants"] = lua.create_table(); + for (auto entry : PChar->m_PMonstrosity->variants) + { + table["variants"][idx++] = entry; + } + } + + return table; + } + + void SetMonstrosityLuaTable(CCharEntity* PChar, sol::table table) + { + TracyZoneScoped; + + if (table["monstrosityId"].valid()) + { + PChar->m_PMonstrosity->MonstrosityId = table.get("monstrosityId"); + } + + if (table["species"].valid()) + { + PChar->m_PMonstrosity->Species = table.get("species"); + } + + if (table["flags"].valid()) + { + PChar->m_PMonstrosity->Flags = table.get("flags"); + } + + if (table["levels"].valid()) + { + for (auto const& [keyObj, valObj] : table.get("levels")) + { + uint8 key = keyObj.as(); + uint8 val = valObj.as(); + PChar->m_PMonstrosity->levels[key] |= val; + } + } + + if (table["instincts"].valid()) + { + for (auto const& [keyObj, valObj] : table.get("instincts")) + { + uint8 key = keyObj.as(); + uint8 val = valObj.as(); + PChar->m_PMonstrosity->instincts[key] |= val; + } + } + + if (table["variants"].valid()) + { + for (auto const& [keyObj, valObj] : table.get("variants")) + { + uint8 key = keyObj.as(); + uint8 val = valObj.as(); + PChar->m_PMonstrosity->variants[key] |= val; + } + } + } + + void OnMonstrosityUpdate(CCharEntity* PChar) { TracyZoneScoped; @@ -3899,7 +3991,7 @@ namespace luautils return; } - auto result = onMonstrosityUpdate(CLuaBaseEntity(PChar)); + auto result = onMonstrosityUpdate(CLuaBaseEntity(PChar), GetMonstrosityLuaTable(PChar)); if (!result.valid()) { sol::error err = result; diff --git a/src/map/lua/luautils.h b/src/map/lua/luautils.h index d89ddb52fc2..1d782ac50db 100644 --- a/src/map/lua/luautils.h +++ b/src/map/lua/luautils.h @@ -285,7 +285,9 @@ namespace luautils int32 OnAutomatonAbilityCheck(CBaseEntity* PChar, CAutomatonEntity* PAutomaton, CMobSkill* PMobSkill); int32 OnAutomatonAbility(CBaseEntity* PTarget, CBaseEntity* PMob, CMobSkill* PMobSkill, CBaseEntity* PMobMaster, action_t* action); - void OnMonstrosityUpdate(CBaseEntity* PChar); + auto GetMonstrosityLuaTable(CCharEntity* PChar) -> sol::table; + void SetMonstrosityLuaTable(CCharEntity* PChar, sol::table data); + void OnMonstrosityUpdate(CCharEntity* PChar); int32 OnAbilityCheck(CBaseEntity* PChar, CBaseEntity* PTarget, CAbility* PAbility, CBaseEntity** PMsgTarget); int32 OnPetAbility(CBaseEntity* PTarget, CBaseEntity* PMob, CMobSkill* PMobSkill, CBaseEntity* PPetMaster, action_t* action); From ace2f0c85e6cee68aeafe4b983116bc4e4f5cfea Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Tue, 10 Oct 2023 09:14:39 +0100 Subject: [PATCH 073/103] MONs can use doors, but not NPCs --- src/map/packet_system.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/map/packet_system.cpp b/src/map/packet_system.cpp index c0724c34ece..b4a9b1fe270 100644 --- a/src/map/packet_system.cpp +++ b/src/map/packet_system.cpp @@ -1,4 +1,4 @@ -/* +/* =========================================================================== Copyright (c) 2010-2015 Darkstar Dev Teams @@ -864,8 +864,7 @@ void SmallPacket0x01A(map_session_data_t* const PSession, CCharEntity* const PCh // Monstrosity: Can't really do anything while under Gestation until you click it off. // : MONs can trigger doors, so we'll handle that later. - // TODO! - if (PChar->StatusEffectContainer->HasStatusEffect(EFFECT_GESTATION)) + if (PChar->StatusEffectContainer->HasStatusEffect(EFFECT_GESTATION) && action == 0x00) { return; } @@ -893,6 +892,13 @@ void SmallPacket0x01A(map_session_data_t* const PSession, CCharEntity* const PCh CBaseEntity* PNpc = nullptr; PNpc = PChar->GetEntity(TargID, TYPE_NPC | TYPE_MOB); + // MONs are allowed to use doors, but nothing else + if (PChar->m_PMonstrosity != nullptr && PNpc->look.size != 0x02) + { + PChar->pushPacket(new CReleasePacket(PChar, RELEASE_TYPE::STANDARD)); + return; + } + // NOTE: Moogles inside of mog houses are the exception for not requiring Spawned or Status checks. if (PNpc != nullptr && distance(PNpc->loc.p, PChar->loc.p) <= 10 && ((PNpc->PAI->IsSpawned() && PNpc->status == STATUS_TYPE::NORMAL) || PChar->m_moghouseID != 0)) { From 65b50c9400d5cfe2b98d0c5e298ccccdba6aed21 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Tue, 10 Oct 2023 09:14:48 +0100 Subject: [PATCH 074/103] MONs can't trade items --- src/map/packet_system.cpp | 35 ++++++++++++++++++++++++++++++++++- src/map/utils/charutils.cpp | 7 +++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/map/packet_system.cpp b/src/map/packet_system.cpp index b4a9b1fe270..866cb350735 100644 --- a/src/map/packet_system.cpp +++ b/src/map/packet_system.cpp @@ -1,4 +1,4 @@ -/* +/* =========================================================================== Copyright (c) 2010-2015 Darkstar Dev Teams @@ -1532,6 +1532,13 @@ void SmallPacket0x029(map_session_data_t* const PSession, CCharEntity* const PCh void SmallPacket0x032(map_session_data_t* const PSession, CCharEntity* const PChar, CBasicPacket& data) { TracyZoneScoped; + + // MONs can't trade + if (PChar->m_PMonstrosity != nullptr) + { + return; + } + uint32 charid = data.ref(0x04); uint16 targid = data.ref(0x08); @@ -1635,6 +1642,13 @@ void SmallPacket0x032(map_session_data_t* const PSession, CCharEntity* const PCh void SmallPacket0x033(map_session_data_t* const PSession, CCharEntity* const PChar, CBasicPacket& data) { TracyZoneScoped; + + // MONs can't trade + if (PChar->m_PMonstrosity != nullptr) + { + return; + } + CCharEntity* PTarget = (CCharEntity*)PChar->GetEntity(PChar->TradePending.targid, TYPE_PC); if (PTarget != nullptr && PChar->TradePending.id == PTarget->id) @@ -1736,6 +1750,13 @@ void SmallPacket0x033(map_session_data_t* const PSession, CCharEntity* const PCh void SmallPacket0x034(map_session_data_t* const PSession, CCharEntity* const PChar, CBasicPacket& data) { TracyZoneScoped; + + // MONs can't trade + if (PChar->m_PMonstrosity != nullptr) + { + return; + } + uint32 quantity = data.ref(0x04); uint16 itemID = data.ref(0x08); uint8 invSlotID = data.ref(0x0A); @@ -1840,6 +1861,12 @@ void SmallPacket0x036(map_session_data_t* const PSession, CCharEntity* const PCh return; } + // MONs can't trade + if (PChar->m_PMonstrosity != nullptr) + { + return; + } + uint32 npcid = data.ref(0x04); uint16 targid = data.ref(0x3A); @@ -1889,6 +1916,12 @@ void SmallPacket0x037(map_session_data_t* const PSession, CCharEntity* const PCh { TracyZoneScoped; + // MONs can't use usable items + if (PChar->m_PMonstrosity != nullptr) + { + return; + } + uint16 TargetID = data.ref(0x0C); uint8 SlotID = data.ref(0x0E); uint8 StorageID = data.ref(0x10); diff --git a/src/map/utils/charutils.cpp b/src/map/utils/charutils.cpp index 8ecd52309e9..90d78fa8f72 100644 --- a/src/map/utils/charutils.cpp +++ b/src/map/utils/charutils.cpp @@ -1623,11 +1623,17 @@ namespace charutils bool CanTrade(CCharEntity* PChar, CCharEntity* PTarget) { + if (PChar->m_PMonstrosity != nullptr || PTarget->m_PMonstrosity != nullptr) + { + return false; + } + if (PTarget->getStorage(LOC_INVENTORY)->GetFreeSlotsCount() < PChar->UContainer->GetItemsCount()) { ShowDebug("Unable to trade, %s doesn't have enough inventory space", PTarget->GetName()); return false; } + for (uint8 slotid = 0; slotid <= 8; ++slotid) { CItem* PItem = PChar->UContainer->GetItem(slotid); @@ -1641,6 +1647,7 @@ namespace charutils } } } + return true; } From 55a9b014885374eb50c4a610a5a63c04cda27fd2 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Tue, 10 Oct 2023 09:18:23 +0100 Subject: [PATCH 075/103] MONs don't lose exp on death --- src/map/utils/charutils.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/map/utils/charutils.cpp b/src/map/utils/charutils.cpp index 90d78fa8f72..97868123c36 100644 --- a/src/map/utils/charutils.cpp +++ b/src/map/utils/charutils.cpp @@ -4649,6 +4649,12 @@ namespace charutils return; } + // MONs don't lose exp on death + if (PChar->m_PMonstrosity != nullptr) + { + return; + } + uint8 mLevel = (PChar->m_LevelRestriction != 0 && PChar->m_LevelRestriction < PChar->GetMLevel()) ? PChar->m_LevelRestriction : PChar->GetMLevel(); uint16 exploss = mLevel <= 67 ? (GetExpNEXTLevel(mLevel) * 8) / 100 : 2400; From 270109c2a8459c7948013ae0de8b8cfdf3f58766 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Tue, 10 Oct 2023 09:41:37 +0100 Subject: [PATCH 076/103] Validate variant unlocks, instinct unlocks, and instinct dupes --- src/map/monstrosity.cpp | 111 +++++++++++++++++++++++++++++++--------- src/map/monstrosity.h | 3 ++ 2 files changed, 91 insertions(+), 23 deletions(-) diff --git a/src/map/monstrosity.cpp b/src/map/monstrosity.cpp index 0d6e30a1f20..5a78787607a 100644 --- a/src/map/monstrosity.cpp +++ b/src/map/monstrosity.cpp @@ -255,8 +255,6 @@ void monstrosity::HandleZoneIn(CCharEntity* PChar) // TODO: If belligerency, timer is 60s CStatusEffect* PEffect = new CStatusEffect(EFFECT::EFFECT_GESTATION, EFFECT::EFFECT_GESTATION, 0, 0, 64800); // 18 hours - // TODO: You cannot attack while you're in Gest., you have to click it off - // TODO: Move these into the db PEffect->AddEffectFlag(EFFECTFLAG_INVISIBLE); PEffect->AddEffectFlag(EFFECTFLAG_DEATH); @@ -340,11 +338,36 @@ void monstrosity::HandleEquipChangePacket(CCharEntity* PChar, CBasicPacket& data return; } - // TODO: Validate that we have the species/instinct that we're trying to equip - - // TODO: Validate that we'll have enough points to hold our instincts when we equip // NOTE: The amount of pointer per level is level + 10, this is set in the client + // clang-format off + auto getTotalInstinctsCost = [&](std::array input) -> uint8 + { + uint8 total = 0; + + for (auto const& idx : input) + { + total += gMonstrosityInstinctMap[idx].cost; + } + + return total; + }; + + auto instinctsContainDuplicates = [&](std::array input) -> bool + { + std::unordered_set set; + for (auto const& idx : input) + { + if (set.find(idx) != set.end()) + { + // Found dupe + return true; + } + } + return false; + }; + // clang-format on + uint8 flag = data.ref(0x0A); if (flag == 0x01) // Species Change { @@ -366,6 +389,12 @@ void monstrosity::HandleEquipChangePacket(CCharEntity* PChar, CBasicPacket& data return; } + // If is a variant, and isn't unlocked, bail + if (newSpecies >= 256 && !IsVariantUnlocked(PChar, newSpecies - 256)) + { + return; + } + PChar->m_PMonstrosity->Species = newSpecies; PChar->m_PMonstrosity->MonstrosityId = data.monstrosityId; @@ -383,20 +412,6 @@ void monstrosity::HandleEquipChangePacket(CCharEntity* PChar, CBasicPacket& data } else if (flag == 0x04) // Instinct Change { - // clang-format off - auto getTotalCost = [&](std::array input) -> uint8 - { - uint8 total = 0; - - for (auto const& idx : input) - { - total += gMonstrosityInstinctMap[idx].cost; - } - - return total; - }; - // clang-format on - auto previousEquipped = PChar->m_PMonstrosity->EquippedInstincts; // NOTE: This is set by the client @@ -436,20 +451,23 @@ void monstrosity::HandleEquipChangePacket(CCharEntity* PChar, CBasicPacket& data auto maybeInstinct = gMonstrosityInstinctMap.find(value); if (maybeInstinct != gMonstrosityInstinctMap.end()) { - auto instinct = (*maybeInstinct).second; - - // TODO: Validate whether or not this instinct is unlocked + if (!IsInstinctUnlocked(PChar, value)) + { + return; + } PChar->m_PMonstrosity->EquippedInstincts[idx] = value; // Validate cost - if (getTotalCost(PChar->m_PMonstrosity->EquippedInstincts) > maxPoints) + if (getTotalInstinctsCost(PChar->m_PMonstrosity->EquippedInstincts) > maxPoints || + instinctsContainDuplicates(PChar->m_PMonstrosity->EquippedInstincts)) { // Reset to what it was before and don't handle mods PChar->m_PMonstrosity->EquippedInstincts = previousEquipped; } else { + auto instinct = (*maybeInstinct).second; for (auto const& mod : instinct.mods) { PChar->addModifier(mod.getModID(), mod.getModAmount()); @@ -527,6 +545,53 @@ void monstrosity::HandleDeathMenu(CCharEntity* PChar, uint8 type) charutils::SendToZone(PChar, 2, zoneutils::GetZoneIPP(PChar->loc.destination)); } +bool monstrosity::IsInstinctUnlocked(CCharEntity* PChar, uint16 instinct) +{ + if (PChar->m_PMonstrosity == nullptr) + { + return false; + } + + // Purchasable instincts are 768 + 5 onwards + if (instinct >= 773) + { + auto idx = instinct - 765; + uint8 byteOffset = 20 + (idx / 8); + uint8 shiftAmount = idx % 8; + + // There is a gap in the instincts bitpack, so we put the purchase information + // for these instincts in there. Sneaky sneaky. + if (byteOffset >= 20 && byteOffset < 24) + { + return PChar->m_PMonstrosity->instincts[byteOffset] & (0x01 << shiftAmount); + } + } + else + { + // TODO: Level-based instincts + } + + return false; +} + +bool monstrosity::IsVariantUnlocked(CCharEntity* PChar, uint8 variant) +{ + if (PChar->m_PMonstrosity == nullptr) + { + return false; + } + + uint8 byteOffset = static_cast(variant) / 8; + uint8 shiftAmount = static_cast(variant) % 8; + + if (byteOffset < 32) + { + return PChar->m_PMonstrosity->variants[byteOffset] & (0x01 << shiftAmount); + } + + return false; +} + void monstrosity::MaxAllLevels(CCharEntity* PChar) { if (PChar->m_PMonstrosity == nullptr) diff --git a/src/map/monstrosity.h b/src/map/monstrosity.h index 32b90ecc72c..0b7979a6c42 100644 --- a/src/map/monstrosity.h +++ b/src/map/monstrosity.h @@ -76,6 +76,9 @@ namespace monstrosity void HandleDeathMenu(CCharEntity* PChar, uint8 type); + bool IsInstinctUnlocked(CCharEntity* PChar, uint16 instinct); + bool IsVariantUnlocked(CCharEntity* PChar, uint8 variant); + // Debug void MaxAllLevels(CCharEntity* PChar); void UnlockAllInstincts(CCharEntity* PChar); From b94e742c0b62213b40006ec48de52654bf88773f Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Tue, 10 Oct 2023 09:57:40 +0100 Subject: [PATCH 077/103] Stat generation safety --- src/map/utils/charutils.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/map/utils/charutils.cpp b/src/map/utils/charutils.cpp index 97868123c36..5f51e8c52d3 100644 --- a/src/map/utils/charutils.cpp +++ b/src/map/utils/charutils.cpp @@ -146,6 +146,13 @@ namespace charutils JOBTYPE sjob = PChar->GetSJob(); MERIT_TYPE statMerit[] = { MERIT_STR, MERIT_DEX, MERIT_VIT, MERIT_AGI, MERIT_INT, MERIT_MND, MERIT_CHR }; + // We have to make sure we don't leave the job as JOB_MON - we CANNOT generate stats for it. + if (mjob == JOB_MON || sjob == JOB_MON) + { + mjob = JOB_WAR; + sjob = JOB_WAR; + } + // NOTE: Monstrosity (MON) is treated as its own job, but each species is it's own // : combination of main/sub job for stats, traits and abilities. if (PChar->m_PMonstrosity != nullptr) From 0dd452d801ffe01c8b222d563f34aac7bef83439 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Tue, 10 Oct 2023 13:25:18 +0100 Subject: [PATCH 078/103] Fix offset issue with IsInstinctUnlocked --- src/map/monstrosity.cpp | 6 +++--- src/map/packets/zone_in.cpp | 5 +++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/map/monstrosity.cpp b/src/map/monstrosity.cpp index 5a78787607a..11815c9a683 100644 --- a/src/map/monstrosity.cpp +++ b/src/map/monstrosity.cpp @@ -552,10 +552,10 @@ bool monstrosity::IsInstinctUnlocked(CCharEntity* PChar, uint16 instinct) return false; } - // Purchasable instincts are 768 + 5 onwards - if (instinct >= 773) + // Purchasable instincts are 768 onwards + if (instinct >= 768) { - auto idx = instinct - 765; + auto idx = instinct - 768; uint8 byteOffset = 20 + (idx / 8); uint8 shiftAmount = idx % 8; diff --git a/src/map/packets/zone_in.cpp b/src/map/packets/zone_in.cpp index 320997ac40c..7c6c3bc62d6 100644 --- a/src/map/packets/zone_in.cpp +++ b/src/map/packets/zone_in.cpp @@ -257,6 +257,11 @@ CZoneInPacket::CZoneInPacket(CCharEntity* PChar, const EventInfo* currentEvent) ref(0x100) = 0x01; // observed: RoZ = 3, CoP = 5, ToAU = 9, WoTG = 11, SoA/original areas = 1 + if (PChar->GetMJob() == JOB_MON) + { + monstrosity::ReadMonstrosityData(PChar); + } + if (PChar->m_PMonstrosity != nullptr) { // look_t data from above From 46efb7daf90cdc4eb27627ca4288fc9fba7105b4 Mon Sep 17 00:00:00 2001 From: claywar Date: Tue, 10 Oct 2023 20:19:40 -0400 Subject: [PATCH 079/103] Add Feretory Odyssean Passage support --- scripts/globals/monstrosity.lua | 69 +++++++++++++++---- .../zones/Feretory/npcs/Odyssean_Passage.lua | 2 - 2 files changed, 56 insertions(+), 15 deletions(-) diff --git a/scripts/globals/monstrosity.lua b/scripts/globals/monstrosity.lua index 640172a80da..c97f95bedaf 100644 --- a/scripts/globals/monstrosity.lua +++ b/scripts/globals/monstrosity.lua @@ -354,6 +354,31 @@ xi.monstrosity.purchasableInstincts = RUN = 31, } +xi.monstrosity.teleports = +{ + [xi.zone.EAST_RONFAURE] = + { + { 120, 0.5, -530, 192 }, + { 115, -59.684, 247, 16 }, + }, + + [xi.zone.QUFIM_ISLAND] = + { + { -2, -20.001, 324, 64 }, + { 161, -20, 37, 192 }, + }, + + [xi.zone.VALKURM_DUNES] = + { + { 838, 0, -162, 64 }, + }, + + [xi.zone.WESTERN_ALTEPA_DESERT] = + { + { 685.548, -1.744, -50.395, 128 }, + }, +} + ----------------------------------- -- Helpers ----------------------------------- @@ -527,34 +552,52 @@ xi.monstrosity.unlockAll = function(player) end ----------------------------------- --- Odyssean Passage +-- Odyssean Passage (Feretory Only) ----------------------------------- xi.monstrosity.odysseanPassageOnTrade = function(player, npc, trade) end xi.monstrosity.odysseanPassageOnTrigger = function(player, npc) - -- TODO: Handle xi.settings.main.ENABLE_MONSTROSITY - - -- TODO: If passage in the overworld, do logic X - local isMonstrosityUnlocked = player:hasKeyItem(xi.keyItem.RING_OF_SUPERNAL_DISJUNCTION) - if isMonstrosityUnlocked then - player:startEvent(883) + if xi.settings.main.ENABLE_MONSTROSITY ~= 1 then + return end - -- TODO: If passage in Feretory, do logic Y - -- In Feretory: Event 5 (0, 0, 0, 0, 0, 0, 0, 0) + local monSize = 1 -- 0-index, Small, Medium, Large + + -- NOTE: Param5 is not consistent, Bee has seen 0, 1, and 2 so far + -- player:startEvent(5, 0, 0, 0, 0, 2, 0, 0, 0) -- Bee + player:startEvent(5, 0, monSize, 0, 0, 0, 0, 0, 0) -- Tiger end xi.monstrosity.odysseanPassageOnEventUpdate = function(player, csid, option, npc) - -- print('update', csid, option) player:updateEvent(0, 0, 0, 0, 1, 0, 0, 0) end xi.monstrosity.odysseanPassageOnEventFinish = function(player, csid, option, npc) - -- print('finish', csid, option) - -- Option 1: Leave & Teleport to last city zone - -- Option 529: Teleport to Al'Taieu + local eventOption = bit.band(option, 0xF) + local zoneSelected = bit.rshift(option, 4) + + if eventOption == 1 then + if zoneSelected == 0 then + -- TODO: Return to Zone the Player entered Feretory from + utils.unused() + else + if xi.monstrosity.teleports[zoneSelected] then + local teleportPos = xi.monstrosity.teleports[zoneSelected][math.random(1, #xi.monstrosity.teleports[zoneSelected])] + + player:setPos(teleportPos[1], + teleportPos[2], + teleportPos[3], + teleportPos[4], + zoneSelected + ) + else + print('Monstrosity Teleport - No Valid Entries for Zone' .. zoneSelected ..'. Setting pos to (0, 0, 0)!') + player:setPos(0, 0, 0, 0, zoneSelected) + end + end + end end ----------------------------------- diff --git a/scripts/zones/Feretory/npcs/Odyssean_Passage.lua b/scripts/zones/Feretory/npcs/Odyssean_Passage.lua index 737ebe8be22..59e8cfa4462 100644 --- a/scripts/zones/Feretory/npcs/Odyssean_Passage.lua +++ b/scripts/zones/Feretory/npcs/Odyssean_Passage.lua @@ -3,8 +3,6 @@ -- NPC: Odyssean Passage -- !pos TODO ----------------------------------- -require('scripts/globals/monstrosity') ------------------------------------ local entity = {} entity.onTrade = function(player, npc, trade) From 2bb768564c12d03715d0acf3ee6a44d77b830c64 Mon Sep 17 00:00:00 2001 From: claywar Date: Fri, 13 Oct 2023 11:48:48 -0400 Subject: [PATCH 080/103] Add Terynon-related functions for purchasing instincts Allow onTrigger for MONs in Feretory --- scripts/globals/monstrosity.lua | 174 ++++++++++++++++++++++++++++---- scripts/zones/Feretory/IDs.lua | 1 + src/map/packet_system.cpp | 2 +- 3 files changed, 155 insertions(+), 22 deletions(-) diff --git a/scripts/globals/monstrosity.lua b/scripts/globals/monstrosity.lua index c97f95bedaf..52b258d04bf 100644 --- a/scripts/globals/monstrosity.lua +++ b/scripts/globals/monstrosity.lua @@ -354,6 +354,22 @@ xi.monstrosity.purchasableInstincts = RUN = 31, } +local limitBreakQuests = +{ + [xi.job.BLU] = { xi.quest.log_id.AHT_URHGAN, xi.quest.id.ahtUrhgan.THE_BEAST_WITHIN }, + [xi.job.COR] = { xi.quest.log_id.AHT_URHGAN, xi.quest.id.ahtUrhgan.BREAKING_THE_BONDS_OF_FATE }, + [xi.job.PUP] = { xi.quest.log_id.BASTOK, xi.quest.id.bastok.ACHIEVING_TRUE_POWER }, + [xi.job.DNC] = { xi.quest.log_id.JEUNO, xi.quest.id.jeuno.A_FURIOUS_FINALE }, + [xi.job.SCH] = { xi.quest.log_id.OTHER_AREAS, xi.quest.id.otherAreas.SURVIVAL_OF_THE_WISEST }, + [xi.job.GEO] = { xi.quest.log_id.ADOULIN, xi.quest.id.adoulin.ELEMENTARY_MY_DEAR_SYLVIE }, + [xi.job.RUN] = { xi.quest.log_id.ADOULIN, xi.quest.id.adoulin.ENDEAVORING_TO_AWAKEN }, +} + +local terynonMonData = +{ + +} + xi.monstrosity.teleports = { [xi.zone.EAST_RONFAURE] = @@ -422,8 +438,8 @@ end xi.monstrosity.hasUnlockedVariant = function(player, variant) local data = player:getMonstrosityData() - local byteOffset = math.floor(variant / 8) - local shiftAmount = variant % 8 + local byteOffset = math.floor(variant / 8) + local shiftAmount = variant % 8 if byteOffset < 32 then return bit.band(data.variants[byteOffset] or 0, bit.lshift(0x01, shiftAmount)) > 0 @@ -450,6 +466,72 @@ xi.monstrosity.unlockVariant = function(player, variant) end end +local function hasPurchasedInstinct(player, purchasableInstinctId) + local data = player:getMonstrosityData() + local byteOffset = 20 + math.floor(purchasableInstinctId / 8) + local shiftAmount = purchasableInstinctId % 8 + + if byteOffset >= 20 and byteOffset < 24 then + return bit.band(data.instincts[byteOffset], bit.lshift(1, shiftAmount)) > 0 + else + print('byteOffset out of range') + end +end + +local function getPurchasedInstinctsMask(player) + local instinctMask = 0 + + for _, purchasableInstinctId in pairs(xi.monstrosity.purchasableInstincts) do + if + purchasableInstinctId >= xi.monstrosity.purchasableInstincts.HUME_II and + hasPurchasedInstinct(player, purchasableInstinctId) + then + instinctMask = utils.mask.setBit(instinctMask, purchasableInstinctId - xi.monstrosity.purchasableInstincts.HUME_II, true) + end + end + + return instinctMask +end + +local function addPurchasedInstinct(player, purchasableInstinctId) + local data = player:getMonstrosityData() + local byteOffset = 20 + math.floor(purchasableInstinctId / 8) + local shiftAmount = purchasableInstinctId % 8 + + if byteOffset >= 20 and byteOffset < 24 then + data.instincts[byteOffset] = bit.bor(data.instincts[byteOffset] or 0, bit.lshift(0x01, shiftAmount)) + else + print('byteOffset out of range') + end + + player:setMonstrosityData(data) +end + +-- When generating Terynon's mask for discounts, we need a bitmask for +-- specific jobs. Since only one quest exists for pre-ToAU jobs, use +-- Maat's Cap tracking for those. +local function hasCompletedLimitBreak(player, jobId) + if jobId <= xi.job.SMN then + local maatsCap = player:getCharVar('maatsCap') + + return utils.mask.getBit(maatsCap, jobId - 1) + else + return player:hasCompletedQuest(unpack(limitBreakQuests[jobId])) + end +end + +local function getLimitBreakMask(player) + local limitMask = 0 + + for jobId = xi.job.WAR, xi.job.RUN do + if hasCompletedLimitBreak(player, jobId) then + limitMask = utils.mask.setBit(limitMask, jobId - 1, true) + end + end + + return limitMask +end + ----------------------------------- -- Bound by C++ (DO NOT CHANGE SIGNATURE) ----------------------------------- @@ -563,11 +645,12 @@ xi.monstrosity.odysseanPassageOnTrigger = function(player, npc) return end - local monSize = 1 -- 0-index, Small, Medium, Large + local monSize = 1 -- 0-index, Small, Medium, Large + local hasBelligerency = player:hasKeyItem(xi.ki.GLADIATORIAL_WRIT_OF_SUMMONS) and 1 or 0 -- NOTE: Param5 is not consistent, Bee has seen 0, 1, and 2 so far -- player:startEvent(5, 0, 0, 0, 0, 2, 0, 0, 0) -- Bee - player:startEvent(5, 0, monSize, 0, 0, 0, 0, 0, 0) -- Tiger + player:startEvent(5, 0, monSize, hasBelligerency, 0, 0, 0, 0, 0) -- Tiger end xi.monstrosity.odysseanPassageOnEventUpdate = function(player, csid, option, npc) @@ -593,7 +676,7 @@ xi.monstrosity.odysseanPassageOnEventFinish = function(player, csid, option, npc zoneSelected ) else - print('Monstrosity Teleport - No Valid Entries for Zone' .. zoneSelected ..'. Setting pos to (0, 0, 0)!') + print('Monstrosity Teleport - No Valid Entries for Zone' .. zoneSelected .. '. Setting pos to (0, 0, 0)!') player:setPos(0, 0, 0, 0, zoneSelected) end end @@ -630,7 +713,7 @@ xi.monstrosity.aengusOnTrade = function(player, npc, trade) end xi.monstrosity.aengusOnTrigger = function(player, npc) - player:startEvent(13, 0, 0, 2, 0, 2, 90, 0, 0) + player:startEvent(13, 0, player:getCurrency('infamy'), 0, 0, 0, 0, 0, 0) end xi.monstrosity.aengusOnEventUpdate = function(player, csid, option, npc) @@ -652,28 +735,77 @@ xi.monstrosity.teyrnonOnTrade = function(player, npc, trade) end xi.monstrosity.teyrnonOnTrigger = function(player, npc) - player:startEvent(7, 0, 0, 0, 0, 0, 0, 0, 0) + player:startEvent(7, player:getCurrency('infamy'), 0, 0, 0, 0, 0, 0, 0) end xi.monstrosity.teyrnonOnEventUpdate = function(player, csid, option, npc) - -- print('update', csid, option) - if csid == 7 and option == 0 then -- Monsters Menu - player:updateEvent(0, 0, 0, 0, 0, 0, 0, 0) - elseif csid == 7 and option == 1 then -- Instinct menu - player:updateEvent(0, 0, 0, 0, 0, 0, 0, 0) + if csid == 7 then + if option == 0 then + -- Monsters + utils.unused(terynonMonData) + + player:updateEvent(2, 0, 0, 0, 0, 0, 0, 0) + elseif option == 1 then + -- Instincts + + local purchasedInstincts = getPurchasedInstinctsMask(player) + local completedLimits = getLimitBreakMask(player) + + player:updateEvent(purchasedInstincts, completedLimits, 0, 0, 0, 0, 0, 0) + end end end xi.monstrosity.teyrnonOnEventFinish = function(player, csid, option, npc) - -- print('finish', csid, option) - -- Support Menu: - -- option 3: Dedication 1 - -- option 259: Dedication 2 - -- option 515: Regen - -- option 771: Refresh - -- option 1027: Protect - -- option 1283: Shell - -- option 1539: Haste + local optionType = bit.band(option, 0xFF) + + if optionType == 1 then + + elseif optionType == 2 then + -- Instincts: Costs are hardcoded, and adjusted based on having completed certain + -- prerequisites. This data is not tabled with Terynon, as it cannot be controlled. + + local selectedInstinct = bit.band(bit.rshift(option, 8), 0xFF) + local instinctPrice = selectedInstinct > xi.monstrosity.purchasableInstincts.GALKA_II and 10000 or 500 + local checkValue = bit.rshift(option, 16) + + if checkValue ~= 119 then + print(string.format('Invalid Event Finish Option received by Terynon! (%s:%d)', player:getName(), option)) + return + end + + if + selectedInstinct > xi.monstrosity.purchasableInstincts.GALKA_II and + hasCompletedLimitBreak(player, selectedInstinct - xi.monstrosity.purchasableInstincts.GALKA_II) + then + instinctPrice = instinctPrice / 2 + end + + if player:getCurrency('infamy') >= instinctPrice then + addPurchasedInstinct(player, selectedInstinct) + + -- NOTE: The offset below is the beginning parameter for purchased instincts used by this message, and + -- lower values will result in an item being placed in the message. Base offset for all instincts + -- is 29696 (29696 + 3 -> Rabbit Instinct I) + player:messageSpecial(zones[xi.zone.FERETORY].text.YOU_LEARNED_INSTINCT, 30464 + selectedInstinct) + end + + elseif optionType == 3 then + local selectedEffect = bit.rshift(option, 8) + + utils.unused(selectedEffect) + -- TODO: Use Abyssea buff method with tabled data here. Do we need a check to remove + -- these statuses when MON is removed? + + -- selectedEffect: + -- 0: Dedication 1 + -- 1: Dedication 2 + -- 2: Regen + -- 3: Refresh + -- 4: Protect + -- 5: Shell + -- 6: Haste + end end ----------------------------------- diff --git a/scripts/zones/Feretory/IDs.lua b/scripts/zones/Feretory/IDs.lua index 508b3c15941..32d7c3fce3d 100644 --- a/scripts/zones/Feretory/IDs.lua +++ b/scripts/zones/Feretory/IDs.lua @@ -15,6 +15,7 @@ zones[xi.zone.FERETORY] = LOGIN_CAMPAIGN_UNDERWAY = 7002, -- The [/January/February/March/April/May/June/July/August/September/October/November/December] Login Campaign is currently underway! LOGIN_NUMBER = 7003, -- In celebration of your most recent login (login no. ), we have provided you with points! You currently have a total of points. MEMBERS_LEVELS_ARE_RESTRICTED = 7023, -- Your party is unable to participate because certain members' levels are restricted. + YOU_LEARNED_INSTINCT = 7354, -- You learned ! MAY_POSSESS_BEES = 7382, -- You may now possess bees! }, mob = diff --git a/src/map/packet_system.cpp b/src/map/packet_system.cpp index 866cb350735..dbb73ea3c1c 100644 --- a/src/map/packet_system.cpp +++ b/src/map/packet_system.cpp @@ -893,7 +893,7 @@ void SmallPacket0x01A(map_session_data_t* const PSession, CCharEntity* const PCh PNpc = PChar->GetEntity(TargID, TYPE_NPC | TYPE_MOB); // MONs are allowed to use doors, but nothing else - if (PChar->m_PMonstrosity != nullptr && PNpc->look.size != 0x02) + if (PChar->m_PMonstrosity != nullptr && PNpc->look.size != 0x02 && PChar->getZone() != ZONEID::ZONE_FERETORY) { PChar->pushPacket(new CReleasePacket(PChar, RELEASE_TYPE::STANDARD)); return; From b6f1e5efe218638eb08d9b341b3b7b80057982ed Mon Sep 17 00:00:00 2001 From: claywar Date: Sun, 15 Oct 2023 08:57:25 -0400 Subject: [PATCH 081/103] Add Terynon-related functions for purchasing MONs Generate bitmask based on monster category in menu Handle variants vs species Populate Vermin data Add Lizards Correct hasPurchased condition Gogo Amorphs and Aquans Block out birds sans requirements Add bird requirements Add event finish handling, send message when infamy not met Unlock in onEventFinish Add purchase message Actually remove currency --- scripts/globals/monstrosity.lua | 540 +++++++++++++++++++++++++++++++- scripts/zones/Feretory/IDs.lua | 2 + 2 files changed, 537 insertions(+), 5 deletions(-) diff --git a/scripts/globals/monstrosity.lua b/scripts/globals/monstrosity.lua index 52b258d04bf..3a777eb588f 100644 --- a/scripts/globals/monstrosity.lua +++ b/scripts/globals/monstrosity.lua @@ -365,9 +365,465 @@ local limitBreakQuests = [xi.job.RUN] = { xi.quest.log_id.ADOULIN, xi.quest.id.adoulin.ENDEAVORING_TO_AWAKEN }, } +-- NOTE: Cost and granted species/variant are hardcoded into Terynon's event; however, the requirements +-- to get each of these purchasable MONs is not displayed, and can be modified to a different set or +-- level. The requirements are limited to species! local terynonMonData = { + [0] = -- Beasts + { + [0] = + { + monVariant = xi.monstrosity.variants.LAPINION, + infamyCost = 7500, + requirements = + { + { xi.monstrosity.species.RABBIT, 90 }, + }, + }, + + [1] = + { + monSpecies = xi.monstrosity.species.SHEEP, + infamyCost = 3000, + }, + + [2] = + { + monSpecies = xi.monstrosity.species.BEHEMOTH, + infamyCost = 10000, + requirements = + { + { xi.monstrosity.species.RABBIT, 75 }, + { xi.monstrosity.species.OPO_OPO, 75 }, + { xi.monstrosity.species.GNOLE, 75 }, + }, + }, + + [3] = + { + monVariant = xi.monstrosity.variants.ELASMOTH, + infamyCost = 25000, + requirements = + { + { xi.monstrosity.species.BEHEMOTH, 50 }, + }, + }, + + [4] = + { + monSpecies = xi.monstrosity.species.CERBERUS, + infamyCost = 10000, + requirements = + { + { xi.monstrosity.species.BUFFALO, 60 }, + { xi.monstrosity.species.MANTICORE, 60 }, + { xi.monstrosity.species.MARID, 60 }, + { xi.monstrosity.species.SHEEP, 60 }, + { xi.monstrosity.species.DHALMEL, 60 }, + }, + }, + + [5] = + { + monVariant = xi.monstrosity.variants.ORTHRUS, + infamyCost = 25000, + requirements = + { + { xi.monstrosity.species.CERBERUS, 50 }, + }, + }, + }, + [1] = -- Plantoids + { + [0] = + { + monVariant = xi.monstrosity.variants.PYGMY_MANDRAGORA, + infamyCost = 7500, + requirements = + { + { xi.monstrosity.species.MANDRAGORA, 45 }, + }, + }, + + [1] = + { + monSpecies = xi.monstrosity.species.TREANT, + infamyCost = 3000, + }, + + [2] = + { + monVariant = xi.monstrosity.variants.PYGMY_MORBOL, + infamyCost = 7500, + requirements = + { + { xi.monstrosity.species.MORBOL, 1 }, + }, + }, + + [3] = + { + monVariant = xi.monstrosity.variants.PURBOL, + infamyCost = 15000, + requirements = + { + { xi.monstrosity.species.MORBOL, 75 }, + }, + }, + }, + + [2] = -- Vermin + { + [0] = + { + monVariant = xi.monstrosity.variants.GOLD_LADYBUG, + infamyCost = 7500, + requirements = + { + { xi.monstrosity.species.LADYBUG, 50 }, + }, + }, + + [1] = + { + monSpecies = xi.monstrosity.species.BEETLE, + infamyCost = 3000, + }, + + [2] = + { + monVariant = xi.monstrosity.variants.UNUSUAL_SCOLOPENDRID, + infamyCost = 10000, + requirements = + { + { xi.monstrosity.species.SCORPION, 60 }, + }, + }, + + [3] = + { + monSpecies = xi.monstrosity.species.ANTLION, + infamyCost = 7500, + requirements = + { + { xi.monstrosity.species.SCORPION, 60 }, + }, + }, + + [4] = + { + monVariant = xi.monstrosity.variants.FORMICEROS, + infamyCost = 15000, + requirements = + { + { xi.monstrosity.species.ANTLION, 60 }, + }, + }, + + [5] = + { + monVariant = xi.monstrosity.variants.PYGMY_EMERALD_CRAWLER, + infamyCost = 6000, + requirements = + { + { xi.monstrosity.species.CRAWLER, 60 }, + }, + }, + + [6] = + { + monVariant = xi.monstrosity.variants.CORAL_WAMOURA, + infamyCost = 15000, + requirements = + { + { xi.monstrosity.species.WAMOURACAMPA, 60 }, + }, + }, + + [7] = + { + monSpecies = xi.monstrosity.species.GNAT, + infamyCost = 5000, + requirements = + { + { xi.monstrosity.species.LADYBUG, 50 }, + { xi.monstrosity.species.WAMOURACAMPA, 50 }, + }, + }, + }, + + [3] = -- Lizards + { + [0] = + { + monVariant = xi.monstrosity.species.UNUSUAL_WIVRE, + infamyCost = 7500, + requirements = + { + { xi.monstrosity.species.WIVRE, 60 }, + }, + }, + + [1] = + { + monSpecies = xi.monstrosity.species.ADAMANTOISE, + infamyCost = 10000, + requirements = + { + { xi.monstrosity.species.BUGARD, 60 }, + { xi.monstrosity.species.LIZARD, 60 }, + { xi.monstrosity.species.WIVRE, 60 }, + }, + }, + + [2] = + { + monVariant = xi.monstrosity.variants.FERROMANTOISE, + infamyCost = 20000, + requirements = + { + { xi.monstrosity.species.ADAMANTOISE, 70 }, + }, + }, + + [3] = + { + monSpecies = xi.monstrosity.species.RAPTOR, + infamyCost = 3000, + }, + + [4] = + { + monSpecies = xi.monstrosity.species.PEISTE, + infamyCost = 8000, + requirements = + { + { xi.monstrosity.species.EFT, 50 }, + { xi.monstrosity.species.RAPTOR, 50 }, + }, + }, + + [5] = + { + monVariant = xi.monstrosity.variants.SIBILUS, + infamyCost = 15000, + requirements = + { + { xi.monstrosity.species.PEISTE, 50 }, + }, + }, + }, + + [4] = -- Amorphs + { + [0] = + { + monSpecies = xi.monstrosity.species.SLIME, + infamyCost = 3000, + }, + + [1] = + { + monVariant = xi.monstrosity.variants.BOIL, + infamyCost = 25000, + requirements = + { + { xi.monstrosity.species.SLIME, 50 }, + }, + }, + + [2] = + { + monVariant = xi.monstrosity.variants.PYGMY_SANDWORM, + infamyCost = 10000, + requirements = + { + { xi.monstrosity.species.SANDWORM, 1 }, + }, + }, + + [3] = + { + monVariant = xi.monstrosity.variants.GIGAWORM, + infamyCost = 25000, + requirements = + { + { xi.monstrosity.species.SANDWORM, 60 }, + }, + }, + + [4] = + { + monSpecies = xi.monstrosity.species.LEECH, + infamyCost = 2000, + }, + }, + + [5] = -- Aquans + { + [0] = + { + monSpecies = xi.monstrosity.species.CRAB, + infamyCost = 2000, + }, + + [1] = + { + monVariant = xi.monstrosity.variants.BASKET_BURDENED_CRAB, + infamyCost = 20000, + requirements = + { + { xi.monstrosity.species.CRAB, 1 }, + }, + }, + + [2] = + { + monVariant = xi.monstrosity.variants.VERMILLION_BASKET_BURDENED_CRAB, + infamyCost = 20000, + requirements = + { + { xi.monstrosity.species.CRAB, 15 }, + }, + }, + + [3] = + { + monVariant = xi.monstrosity.variants.PORTER_CRAB, + infamyCost = 15000, + requirements = + { + { xi.monstrosity.species.CRAB, 60 }, + }, + }, + + [4] = + { + monSpecies = xi.monstrosity.species.PUGIL, + infamyCost = 3000, + }, + + [5] = + { + monVariant = xi.monstrosity.variants.LIMASCABRA, + infamyCost = 15000, + requirements = + { + { xi.monstrosity.species.URAGNITE, 50 }, + }, + }, + + [6] = + { + monVariant = xi.monstrosity.variants.PYGMY_OROBON, + infamyCost = 10000, + requirements = + { + { xi.monstrosity.species.OROBON, 1 }, + }, + }, + + [7] = + { + monVariant = xi.monstrosity.variants.OGREBON, + infamyCost = 18000, + requirements = + { + { xi.monstrosity.species.OROBON, 50 }, + }, + }, + + [8] = + { + monSpecies = xi.monstrosity.species.RUSZOR, + infamyCost = 10000, + requirements = + { + { xi.monstrosity.species.OROBON, 75 }, + { xi.monstrosity.species.URAGNITE, 75 }, + }, + }, + }, + + [6] = -- Birds + { + [0] = + { + monSpecies = xi.monstrosity.species.COCKATRICE, + infamyCost = 3000, + }, + + [1] = + { + monVariant = xi.monstrosity.variants.GAGANA, + infamyCost = 15000, + requirements = + { + { xi.monstrosity.species.ROC, 75 }, + }, + }, + + [2] = + { + monSpecies = xi.monstrosity.species.BAT, + infamyCost = 2000, + }, + + [3] = + { + monVariant = xi.monstrosity.variants.INGUZA, + infamyCost = 15000, + requirements = + { + { xi.monstrosity.species.APKALLU, 50 }, + }, + }, + + [4] = + { + monSpecies = xi.monstrosity.species.COLIBRI, + infamyCost = 5000, + requirements = + { + { xi.monstrosity.species.BAT, 50 }, + { xi.monstrosity.species.BIRD, 45 }, + }, + }, + + [5] = + { + monVariant = xi.monstrosity.variants.TOUCALIBRI, + infamyCost = 15000, + requirements = + { + { xi.monstrosity.species.COLIBRI, 50 }, + }, + }, + + [6] = + { + monSpecies = xi.monstrosity.species.AMPHIPTERE, + infamyCost = 10000, + requirements = + { + { xi.monstrosity.species.COCKATRICE, 75 }, + { xi.monstrosity.species.ROC, 75 }, + { xi.monstrosity.species.HIPPOGRYPH, 75 }, + }, + }, + + [7] = + { + monVariant = xi.monstrosity.variants.SANGUIPTERE, + infamyCost = 20000, + requirements = + { + { xi.monstrosity.species.AMPHIPTERE, 50 }, + }, + }, + }, } xi.monstrosity.teleports = @@ -532,6 +988,45 @@ local function getLimitBreakMask(player) return limitMask end +local function hasPurchaseRequirements(player, monCategory, selectedMon) + local selectedMonData = terynonMonData[monCategory][selectedMon] + local eligibleSpecies = selectedMonData.monSpecies and xi.monstrosity.getSpeciesLevel(player, selectedMonData.monSpecies) == 0 + local eligibleVariant = selectedMonData.monVariant and not xi.monstrosity.hasUnlockedVariant(player, selectedMonData.monVariant) + + if + eligibleSpecies or + eligibleVariant + then + if selectedMonData.requirements then + for _, reqTable in ipairs(selectedMonData.requirements) do + if xi.monstrosity.getSpeciesLevel(player, reqTable[1]) < reqTable[2] then + return false + end + end + end + + return true + end + + return false +end + +local function getMonPageMask(player, monCategory) + local pageMask = 0 + + if terynonMonData[monCategory] then + local categoryTable = terynonMonData[monCategory] + + for bitPos, _ in pairs(categoryTable) do + if hasPurchaseRequirements(player, monCategory, bitPos) then + pageMask = utils.mask.setBit(pageMask, bitPos, true) + end + end + end + + return pageMask +end + ----------------------------------- -- Bound by C++ (DO NOT CHANGE SIGNATURE) ----------------------------------- @@ -690,7 +1185,13 @@ end xi.monstrosity.feretoryOnZoneIn = function(player, prevZone) local cs = -1 - player:setPos(-358.000, -3.400, -440.00, 63) + if + player:getXPos() == 0 and + player:getYPos() == 0 and + player:getZPos() == 0 + then + player:setPos(-358.000, -3.400, -440.00, 63) + end if player:getMainJob() ~= xi.job.MON then player:changeJob(xi.job.MON) @@ -740,12 +1241,16 @@ end xi.monstrosity.teyrnonOnEventUpdate = function(player, csid, option, npc) if csid == 7 then - if option == 0 then + local optionType = bit.band(option, 0xFF) + + if optionType == 0 then -- Monsters - utils.unused(terynonMonData) - player:updateEvent(2, 0, 0, 0, 0, 0, 0, 0) - elseif option == 1 then + local monPage = bit.rshift(option, 16) + local availableMons = getMonPageMask(player, monPage) + + player:updateEvent(availableMons, 0, 0, 0, 0, 0, 0, 0) + elseif optionType == 1 then -- Instincts local purchasedInstincts = getPurchasedInstinctsMask(player) @@ -760,6 +1265,28 @@ xi.monstrosity.teyrnonOnEventFinish = function(player, csid, option, npc) local optionType = bit.band(option, 0xFF) if optionType == 1 then + local selectedCategory = bit.band(bit.rshift(option, 8), 0xF) - 1 + local selectedMon = bit.rshift(option, 16) + local monData = terynonMonData[selectedCategory][selectedMon] + + if not monData then + print(string.format('Invalid Event Finish Option received by Terynon! (%s:%d)', player:getName(), option)) + return + end + + if player:getCurrency('infamy') >= monData.infamyCost then + player:delCurrency('infamy', monData.infamyCost) + + if monData.monSpecies then + xi.monstrosity.unlockSpecies(player, monData.monSpecies) + elseif monData.monVariant then + xi.monstrosity.unlockVariant(player, monData.monVariant) + end + + player:messageSpecial(zones[xi.zone.FERETORY].text.MAY_POSSESS_BEASTS + 3 * selectedCategory, 0, selectedMon) + else + player:messageSpecial(zones[xi.zone.FERETORY].text.THY_BRAZEN_DISREGARD) + end elseif optionType == 2 then -- Instincts: Costs are hardcoded, and adjusted based on having completed certain @@ -782,12 +1309,15 @@ xi.monstrosity.teyrnonOnEventFinish = function(player, csid, option, npc) end if player:getCurrency('infamy') >= instinctPrice then + player:delCurrency('infamy', instinctPrice) addPurchasedInstinct(player, selectedInstinct) -- NOTE: The offset below is the beginning parameter for purchased instincts used by this message, and -- lower values will result in an item being placed in the message. Base offset for all instincts -- is 29696 (29696 + 3 -> Rabbit Instinct I) player:messageSpecial(zones[xi.zone.FERETORY].text.YOU_LEARNED_INSTINCT, 30464 + selectedInstinct) + else + player:messageSpecial(zones[xi.zone.FERETORY].text.THY_BRAZEN_DISREGARD) end elseif optionType == 3 then diff --git a/scripts/zones/Feretory/IDs.lua b/scripts/zones/Feretory/IDs.lua index 32d7c3fce3d..7f9c02c4806 100644 --- a/scripts/zones/Feretory/IDs.lua +++ b/scripts/zones/Feretory/IDs.lua @@ -15,6 +15,8 @@ zones[xi.zone.FERETORY] = LOGIN_CAMPAIGN_UNDERWAY = 7002, -- The [/January/February/March/April/May/June/July/August/September/October/November/December] Login Campaign is currently underway! LOGIN_NUMBER = 7003, -- In celebration of your most recent login (login no. ), we have provided you with points! You currently have a total of points. MEMBERS_LEVELS_ARE_RESTRICTED = 7023, -- Your party is unable to participate because certain members' levels are restricted. + MAY_POSSESS_BEASTS = 7330, -- You may now possess [lapinions/sheep/behemoths/elasmoths/cerebruses/orthruses]! + THY_BRAZEN_DISREGARD = 7349, -- Thy brazen disregard to count correctly is an affront to monipulators everywhere. Return whenas thou hast the meet amount of infamy. YOU_LEARNED_INSTINCT = 7354, -- You learned ! MAY_POSSESS_BEES = 7382, -- You may now possess bees! }, From 156579a428cda9deecb4406409bc050d1e19dca7 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Sun, 15 Oct 2023 19:30:10 +0100 Subject: [PATCH 082/103] Add information for Belligerency icon & other tidbits --- src/map/monstrosity.cpp | 2 ++ src/map/packets/char_sync.cpp | 4 ++-- src/map/packets/char_update.cpp | 10 ++++++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/map/monstrosity.cpp b/src/map/monstrosity.cpp index 11815c9a683..a3d2448aa26 100644 --- a/src/map/monstrosity.cpp +++ b/src/map/monstrosity.cpp @@ -284,6 +284,8 @@ uint32 monstrosity::GetPackedMonstrosityName(CCharEntity* PChar) return 0x00000000; } + // NOTE: Changing this 0x8000 to 0xC000 will hide the species name. + // : This looks to be a quirk of the client and not intended. uint16 a = 0x8000 | PChar->m_PMonstrosity->Species; uint8 b = PChar->m_PMonstrosity->NamePrefix1; uint8 c = PChar->m_PMonstrosity->NamePrefix2; diff --git a/src/map/packets/char_sync.cpp b/src/map/packets/char_sync.cpp index f74063d3d4e..db4db991bc1 100644 --- a/src/map/packets/char_sync.cpp +++ b/src/map/packets/char_sync.cpp @@ -37,14 +37,14 @@ CCharSyncPacket::CCharSyncPacket(CCharEntity* PChar) ref(0x08) = PChar->id; // ref(0x0C) = PChar->PFellow ? PChar->PFellow->targid : 0 - ref(0x10) = PChar->StatusEffectContainer->HasStatusEffect(EFFECT_ALLIED_TAGS) ? 2 : 0; // 0x02 - Campaign Battle, 0x04 - Level Sync + ref(0x10) = PChar->StatusEffectContainer->HasStatusEffect(EFFECT_ALLIED_TAGS) ? 0x02 : 0x00; // 0x02 - Campaign Battle, 0x04 - Level Sync if (PChar->m_LevelRestriction && PChar->StatusEffectContainer->HasStatusEffect(EFFECT_LEVEL_SYNC)) { if (PChar->PBattlefield == nullptr) { // Only display the level sync icon outside of BCNMs - ref(0x10) |= 4; + ref(0x10) |= 0x04; } ref(0x26) = PChar->m_LevelRestriction; diff --git a/src/map/packets/char_update.cpp b/src/map/packets/char_update.cpp index 9f23dbce60f..3a4ecf1de54 100644 --- a/src/map/packets/char_update.cpp +++ b/src/map/packets/char_update.cpp @@ -100,6 +100,9 @@ CCharUpdatePacket::CCharUpdatePacket(CCharEntity* PChar) ref(0x36) = flag; + // Sword & Shield Icon + // ref(0x37) = 0x01; + uint32 timeRemainingToForcedHomepoint = PChar->GetTimeRemainingUntilDeathHomepoint(); ref(0x3C) = timeRemainingToForcedHomepoint; @@ -134,5 +137,12 @@ CCharUpdatePacket::CCharUpdatePacket(CCharEntity* PChar) if (PChar->m_PMonstrosity != nullptr) { ref(0x54) = monstrosity::GetPackedMonstrosityName(PChar); + + // TODO: + bool belligerencyEnabled = false; + if (belligerencyEnabled) + { + ref(0x37) |= 0x01; + } } } From 61abad35fc2a99a5545d4cf2d045f3b23452d14c Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Sun, 15 Oct 2023 20:13:22 +0100 Subject: [PATCH 083/103] Add bindings for Belligerency status --- scripts/globals/monstrosity.lua | 8 ++++---- sql/char_monstrosity.sql | 1 + src/map/lua/lua_baseentity.cpp | 29 +++++++++++++++++++++++++++++ src/map/lua/lua_baseentity.h | 2 ++ src/map/monstrosity.cpp | 33 ++++++++++++++++++++++++++------- src/map/monstrosity.h | 4 ++++ src/map/packets/char_update.cpp | 7 +++---- 7 files changed, 69 insertions(+), 15 deletions(-) diff --git a/scripts/globals/monstrosity.lua b/scripts/globals/monstrosity.lua index 3a777eb588f..3ff96b592b9 100644 --- a/scripts/globals/monstrosity.lua +++ b/scripts/globals/monstrosity.lua @@ -1214,17 +1214,17 @@ xi.monstrosity.aengusOnTrade = function(player, npc, trade) end xi.monstrosity.aengusOnTrigger = function(player, npc) - player:startEvent(13, 0, player:getCurrency('infamy'), 0, 0, 0, 0, 0, 0) + local inBelligerency = player:getBelligerencyFlag() and 1 or 0 + player:startEvent(13, inBelligerency, player:getCurrency('infamy'), 0, 0, 0, 0, 0, 0) end xi.monstrosity.aengusOnEventUpdate = function(player, csid, option, npc) - -- print('update', csid, option) end xi.monstrosity.aengusOnEventFinish = function(player, csid, option, npc) - -- print('finish', csid, option) if csid == 13 and option == 1 then - -- Selected: Enter Belligerency + -- Toggle + player:setBelligerencyFlag(not player:getBelligerencyFlag()) end end diff --git a/sql/char_monstrosity.sql b/sql/char_monstrosity.sql index e59c2e3fe2e..aca0ad38de9 100644 --- a/sql/char_monstrosity.sql +++ b/sql/char_monstrosity.sql @@ -10,5 +10,6 @@ CREATE TABLE `char_monstrosity` ( `levels` blob DEFAULT NULL, `instincts` blob DEFAULT NULL, `variants` blob DEFAULT NULL, + `belligerency` tinyint(3) unsigned NOT NULL DEFAULT 0, PRIMARY KEY (`charid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; diff --git a/src/map/lua/lua_baseentity.cpp b/src/map/lua/lua_baseentity.cpp index 93e9ba4a77f..0249f6fa38d 100644 --- a/src/map/lua/lua_baseentity.cpp +++ b/src/map/lua/lua_baseentity.cpp @@ -6403,6 +6403,33 @@ void CLuaBaseEntity::setMonstrosityData(sol::table table) } } +bool CLuaBaseEntity::getBelligerencyFlag() +{ + auto* PChar = dynamic_cast(m_PBaseEntity); + if (PChar == nullptr) + { + return false; + } + + if (PChar->m_PMonstrosity == nullptr) + { + return false; + } + + return PChar->m_PMonstrosity->Belligerency; +} + +void CLuaBaseEntity::setBelligerencyFlag(bool flag) +{ + auto* PChar = dynamic_cast(m_PBaseEntity); + if (PChar == nullptr) + { + return; + } + + monstrosity::SetBelligerencyFlag(PChar, flag); +} + /************************************************************************ * Function: getTitle() * Purpose : Returns the integer value of the player's current title @@ -17211,6 +17238,8 @@ void CLuaBaseEntity::Register() // Monstrosity SOL_REGISTER("getMonstrosityData", CLuaBaseEntity::getMonstrosityData); SOL_REGISTER("setMonstrosityData", CLuaBaseEntity::setMonstrosityData); + SOL_REGISTER("getBelligerencyFlag", CLuaBaseEntity::getBelligerencyFlag); + SOL_REGISTER("setBelligerencyFlag", CLuaBaseEntity::setBelligerencyFlag); // Player Titles and Fame SOL_REGISTER("getTitle", CLuaBaseEntity::getTitle); diff --git a/src/map/lua/lua_baseentity.h b/src/map/lua/lua_baseentity.h index 6cbee316b1c..09339db60d3 100644 --- a/src/map/lua/lua_baseentity.h +++ b/src/map/lua/lua_baseentity.h @@ -334,6 +334,8 @@ class CLuaBaseEntity // Monstrosity auto getMonstrosityData() -> sol::table; void setMonstrosityData(sol::table table); + bool getBelligerencyFlag(); + void setBelligerencyFlag(bool flag); // Player Titles and Fame uint16 getTitle(); diff --git a/src/map/monstrosity.cpp b/src/map/monstrosity.cpp index a3d2448aa26..0df69ea264b 100644 --- a/src/map/monstrosity.cpp +++ b/src/map/monstrosity.cpp @@ -82,6 +82,7 @@ monstrosity::MonstrosityData_t::MonstrosityData_t() , MainJob(JOB_WAR) // , SubJob(JOB_WAR) // , CurrentExp(0) // No exp +, Belligerency(false) // { levels[1] = 1; // Rabbit levels[18] = 1; // Mandragora @@ -147,7 +148,7 @@ void monstrosity::ReadMonstrosityData(CCharEntity* PChar) { auto data = std::make_unique(); - auto ret = sql->Query("SELECT charid, current_monstrosity_id, current_monstrosity_species, current_monstrosity_name_prefix_1, current_monstrosity_name_prefix_2, current_exp, equip, levels, instincts, variants FROM char_monstrosity WHERE charid = %d LIMIT 1;", PChar->id); + auto ret = sql->Query("SELECT charid, current_monstrosity_id, current_monstrosity_species, current_monstrosity_name_prefix_1, current_monstrosity_name_prefix_2, current_exp, equip, levels, instincts, variants, belligerency FROM char_monstrosity WHERE charid = %d LIMIT 1;", PChar->id); if (ret != SQL_ERROR && sql->NumRows() != 0) { while (sql->NextRow() == SQL_SUCCESS) @@ -166,6 +167,8 @@ void monstrosity::ReadMonstrosityData(CCharEntity* PChar) sql->GetBlobData(8, &data->instincts); sql->GetBlobData(9, &data->variants); + data->Belligerency = static_cast(sql->GetUIntData(10)); + // Build additional data from lookups data->MainJob = gMonstrositySpeciesMap[data->Species].mjob; data->SubJob = gMonstrositySpeciesMap[data->Species].sjob; @@ -196,7 +199,8 @@ void monstrosity::WriteMonstrosityData(CCharEntity* PChar) "equip = '%s', " "levels = '%s', " "instincts = '%s', " - "variants = '%s';"; + "variants = '%s', " + "belligerency = '%d';"; auto equipEscaped = sql->ObjectToBlobString(&PChar->m_PMonstrosity->EquippedInstincts); auto levelsEscaped = sql->ObjectToBlobString(&PChar->m_PMonstrosity->levels); @@ -213,7 +217,8 @@ void monstrosity::WriteMonstrosityData(CCharEntity* PChar) equipEscaped.c_str(), levelsEscaped.c_str(), instinctsEscaped.c_str(), - variantsEscaped.c_str()); + variantsEscaped.c_str(), + static_cast(PChar->m_PMonstrosity->Belligerency)); } void monstrosity::TryPopulateMonstrosityData(CCharEntity* PChar) @@ -249,11 +254,14 @@ void monstrosity::HandleZoneIn(CCharEntity* PChar) } } - // TODO: There are more conditions to handle here + // NOTE: Whenever you log in as a MON, you'll have Gestation - even if you've previously clicked it off. + // TODO: Check this is true in Belligerency. + // TODO: There are more conditions to handle here? if (PChar->loc.zone->GetID() != ZONE_FERETORY) { - // TODO: If belligerency, timer is 60s - CStatusEffect* PEffect = new CStatusEffect(EFFECT::EFFECT_GESTATION, EFFECT::EFFECT_GESTATION, 0, 0, 64800); // 18 hours + uint32 duration = PChar->m_PMonstrosity->Belligerency ? 60 : 64800 /* 18 hours */; + + CStatusEffect* PEffect = new CStatusEffect(EFFECT::EFFECT_GESTATION, EFFECT::EFFECT_GESTATION, 0, 0, duration); // TODO: Move these into the db PEffect->AddEffectFlag(EFFECTFLAG_INVISIBLE); @@ -263,7 +271,6 @@ void monstrosity::HandleZoneIn(CCharEntity* PChar) PEffect->AddEffectFlag(EFFECTFLAG_DETECTABLE); PEffect->AddEffectFlag(EFFECTFLAG_ON_ZONE); - // TODO: Does the timer survive logout, does it tick while offline, does it reset? // PEffect->AddEffectFlag(EFFECTFLAG_LOGOUT); // NOTE: It DOES say the effect wears off @@ -594,6 +601,18 @@ bool monstrosity::IsVariantUnlocked(CCharEntity* PChar, uint8 variant) return false; } +void monstrosity::SetBelligerencyFlag(CCharEntity* PChar, bool flag) +{ + if (PChar->m_PMonstrosity == nullptr) + { + return; + } + + PChar->m_PMonstrosity->Belligerency = flag; + + WriteMonstrosityData(PChar); +} + void monstrosity::MaxAllLevels(CCharEntity* PChar) { if (PChar->m_PMonstrosity == nullptr) diff --git a/src/map/monstrosity.h b/src/map/monstrosity.h index 0b7979a6c42..b4994a51630 100644 --- a/src/map/monstrosity.h +++ b/src/map/monstrosity.h @@ -57,6 +57,8 @@ namespace monstrosity std::array levels{ 0 }; std::array instincts{ 0 }; std::array variants{ 0 }; + + bool Belligerency; }; void LoadStaticData(); @@ -79,6 +81,8 @@ namespace monstrosity bool IsInstinctUnlocked(CCharEntity* PChar, uint16 instinct); bool IsVariantUnlocked(CCharEntity* PChar, uint8 variant); + void SetBelligerencyFlag(CCharEntity* PChar, bool flag); + // Debug void MaxAllLevels(CCharEntity* PChar); void UnlockAllInstincts(CCharEntity* PChar); diff --git a/src/map/packets/char_update.cpp b/src/map/packets/char_update.cpp index 3a4ecf1de54..3d50146f5a6 100644 --- a/src/map/packets/char_update.cpp +++ b/src/map/packets/char_update.cpp @@ -100,7 +100,7 @@ CCharUpdatePacket::CCharUpdatePacket(CCharEntity* PChar) ref(0x36) = flag; - // Sword & Shield Icon + // Sword & Shield Icon (Campaign battles, etc?) // ref(0x37) = 0x01; uint32 timeRemainingToForcedHomepoint = PChar->GetTimeRemainingUntilDeathHomepoint(); @@ -138,9 +138,8 @@ CCharUpdatePacket::CCharUpdatePacket(CCharEntity* PChar) { ref(0x54) = monstrosity::GetPackedMonstrosityName(PChar); - // TODO: - bool belligerencyEnabled = false; - if (belligerencyEnabled) + // Sword & Shield icon only shows outside of the Feretory + if (PChar->m_PMonstrosity->Belligerency && PChar->loc.zone->GetID() != ZONE_FERETORY) { ref(0x37) |= 0x01; } From 21d8dfb0a3684f66c4c23ff276c5af0bc663adc8 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Sun, 15 Oct 2023 20:29:40 +0100 Subject: [PATCH 084/103] Add belligerency flag to odysseanPassageOnEventUpdate --- scripts/globals/monstrosity.lua | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/scripts/globals/monstrosity.lua b/scripts/globals/monstrosity.lua index 3ff96b592b9..28ec3a69c48 100644 --- a/scripts/globals/monstrosity.lua +++ b/scripts/globals/monstrosity.lua @@ -851,6 +851,14 @@ xi.monstrosity.teleports = }, } +-- NOTE: The zones in this list are not customisable, but the level caps are! +xi.monstrosity.belligerencyCaps = +{ + [xi.zone.BUBURIMU_PENINSULA] = 30, + [xi.zone.XARCABARD] = 60, + [xi.zone.ULEGUERAND_RANGE] = 90, +} + ----------------------------------- -- Helpers ----------------------------------- @@ -1141,15 +1149,19 @@ xi.monstrosity.odysseanPassageOnTrigger = function(player, npc) end local monSize = 1 -- 0-index, Small, Medium, Large - local hasBelligerency = player:hasKeyItem(xi.ki.GLADIATORIAL_WRIT_OF_SUMMONS) and 1 or 0 + local hasBelligerency = player:getBelligerencyFlag() and 1 or 0 + -- NOTE: The list of available zones is built from the char's list of + -- visited zones. If you haven't visited any zones in a category it'll back + -- out immediately. -- NOTE: Param5 is not consistent, Bee has seen 0, 1, and 2 so far -- player:startEvent(5, 0, 0, 0, 0, 2, 0, 0, 0) -- Bee player:startEvent(5, 0, monSize, hasBelligerency, 0, 0, 0, 0, 0) -- Tiger end xi.monstrosity.odysseanPassageOnEventUpdate = function(player, csid, option, npc) - player:updateEvent(0, 0, 0, 0, 1, 0, 0, 0) + local zoneSelected = bit.rshift(option, 4) + player:updateEvent(xi.monstrosity.belligerencyCaps[zoneSelected], 0, 0, 0, 1, 0, 0, 0) end xi.monstrosity.odysseanPassageOnEventFinish = function(player, csid, option, npc) From 996ffbce91801ff4b95bd2671b9e3263d00ce66a Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Sun, 15 Oct 2023 21:14:38 +0100 Subject: [PATCH 085/103] Disable abilities in Feretory --- src/map/packets/zone_in.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/map/packets/zone_in.cpp b/src/map/packets/zone_in.cpp index 7c6c3bc62d6..7ffe0a10327 100644 --- a/src/map/packets/zone_in.cpp +++ b/src/map/packets/zone_in.cpp @@ -204,7 +204,7 @@ CZoneInPacket::CZoneInPacket(CCharEntity* PChar, const EventInfo* currentEvent) if (PChar->m_moghouseID != 0) { - ref(0x80) = 1; + ref(0x80) = 0x01; if (PChar->profile.mhflag & 0x0040) // On MH2F { @@ -216,7 +216,7 @@ CZoneInPacket::CZoneInPacket(CCharEntity* PChar, const EventInfo* currentEvent) } else { - ref(0x80) = 2; + ref(0x80) = 0x02; ref(0xAA) = 0x01FF; // TODO: This has also been seen as 0x04 and 0x07 @@ -262,6 +262,15 @@ CZoneInPacket::CZoneInPacket(CCharEntity* PChar, const EventInfo* currentEvent) monstrosity::ReadMonstrosityData(PChar); } + if (PChar->loc.zone->GetID() == ZONE_FERETORY) + { + // This disables the zone model, but also disables abilities etc. + ref(0x80) = 0x01; + + // Zone Model + ref(0xAA) = 0x02D9; // 729 + } + if (PChar->m_PMonstrosity != nullptr) { // look_t data from above From 32fb3a9173eadaf231340a5443282e865e37ccc8 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Sun, 15 Oct 2023 21:49:53 +0100 Subject: [PATCH 086/103] Add countdown to Relinquish --- scripts/actions/abilities/relinquish.lua | 4 ++- scripts/enum/msg.lua | 3 ++ scripts/globals/monstrosity.lua | 37 ++++++++++++++++++++++++ sql/abilities.sql | 2 +- src/map/packets/message_basic.h | 14 +++++---- 5 files changed, 52 insertions(+), 8 deletions(-) diff --git a/scripts/actions/abilities/relinquish.lua b/scripts/actions/abilities/relinquish.lua index 025887012ac..8de342cf029 100644 --- a/scripts/actions/abilities/relinquish.lua +++ b/scripts/actions/abilities/relinquish.lua @@ -1,6 +1,8 @@ ----------------------------------- -- Ability: Relinquish ----------------------------------- +require('scripts/globals/monstrosity') +----------------------------------- local abilityObject = {} abilityObject.onAbilityCheck = function(player, target, ability) @@ -9,7 +11,7 @@ abilityObject.onAbilityCheck = function(player, target, ability) end abilityObject.onUseAbility = function(player, target, ability) - player:setPos(0, 0, 0, 0, xi.zone.FERETORY) + xi.monstrosity.relinquishOnAbility(player, target, ability) end return abilityObject diff --git a/scripts/enum/msg.lua b/scripts/enum/msg.lua index fcbd8850d94..1be86316b57 100644 --- a/scripts/enum/msg.lua +++ b/scripts/enum/msg.lua @@ -375,6 +375,9 @@ xi.msg.basic = -- TRUST & ALTER EGO TRUST_NO_CAST_TRUST = 700, -- You are unable to use Trust magic at this time. TRUST_NO_CALL_AE = 717, -- You cannot call forth alter egos here. + + -- Monstrosity + FERETORY_COUNTDOWN = 679, -- will return to the Feretory in } -- Used to modify certain basic messages. diff --git a/scripts/globals/monstrosity.lua b/scripts/globals/monstrosity.lua index 28ec3a69c48..bba0eb7bf33 100644 --- a/scripts/globals/monstrosity.lua +++ b/scripts/globals/monstrosity.lua @@ -1066,6 +1066,43 @@ xi.monstrosity.onMonstrosityUpdate = function(player, data) -- TODO: Handle level-based variants here end +----------------------------------- +-- Relinquish +----------------------------------- + +xi.monstrosity.relinquishSteps = +{ + [0] = function(player) + player:messageBasic(xi.msg.basic.FERETORY_COUNTDOWN, 0, 4) + end, + [1] = function(player) + player:messageBasic(xi.msg.basic.FERETORY_COUNTDOWN, 0, 3) + end, + [2] = function(player) + player:messageBasic(xi.msg.basic.FERETORY_COUNTDOWN, 0, 2) + end, + [3] = function(player) + player:messageBasic(xi.msg.basic.FERETORY_COUNTDOWN, 0, 1) + end, + [4] = function(player) + player:setPos(0, 0, 0, 0, xi.zone.FERETORY) + end, +} + +xi.monstrosity.relinquishFuncBody = function(player) + -- TODO: Make this countdown interruptable + player:timer(1000, function(playerArg) + local step = utils.clamp(playerArg:getLocalVar('RELINQUISH_COUNTDOWN'), 0, 4) + xi.monstrosity.relinquishSteps[step](playerArg) + playerArg:setLocalVar('RELINQUISH_COUNTDOWN', step + 1) + xi.monstrosity.relinquishFuncBody(playerArg) + end) +end + +xi.monstrosity.relinquishOnAbility = function(player, target, ability) + xi.monstrosity.relinquishFuncBody(player) +end + ----------------------------------- -- Debug ----------------------------------- diff --git a/sql/abilities.sql b/sql/abilities.sql index 72563987e4e..9f7bc9c13cb 100644 --- a/sql/abilities.sql +++ b/sql/abilities.sql @@ -386,7 +386,7 @@ INSERT INTO `abilities` VALUES (378,'odyllic_subterfuge',22,96,4,3600,131,0,0,27 INSERT INTO `abilities` VALUES (379,'ward',22,1,1,0,142,0,0,0,2000,0,6,0.0,0,0,0,0,0,'SOA'); INSERT INTO `abilities` VALUES (380,'effusion',22,1,1,0,143,0,0,0,2000,0,6,0.0,0,0,0,0,0,'SOA'); INSERT INTO `abilities` VALUES (381,'chocobo_jig_ii',19,70,1,60,218,126,0,13,2000,0,14,10.0,1,1,300,0,0,'SOA'); -INSERT INTO `abilities` VALUES (382,'relinquish',23,1,1,60,253,0,0,0,0,0,6,0.0,0,0,0,0,0,NULL); +INSERT INTO `abilities` VALUES (382,'relinquish',23,1,1,60,253,0,0,312,0,0,6,0.0,0,0,0,0,0,NULL); INSERT INTO `abilities` VALUES (383,'vivacious_pulse',22,65,1,60,242,102,0,327,2000,0,6,0.0,0,0,0,0,0,'SOA'); INSERT INTO `abilities` VALUES (384,'contradance',19,50,1,300,229,0,0,329,2000,0,6,0.0,0,0,0,0,0,NULL); -- check animation INSERT INTO `abilities` VALUES (385,'apogee',15,70,1,180,108,100,0,94,2000,0,6,0.0,0,1,80,0,0,'SOA'); diff --git a/src/map/packets/message_basic.h b/src/map/packets/message_basic.h index 227e6253b4c..e747e2e8f30 100644 --- a/src/map/packets/message_basic.h +++ b/src/map/packets/message_basic.h @@ -171,12 +171,14 @@ enum MSGBASIC_ID : uint16 MSGBASIC_MERIT_INCREASE = 380, // Your modification has risen to level MSGBASIC_MERIT_DECREASE = 381, // Your modification has dropped to level /* DEBUG MESSAGES */ - MSGBASIC_DEBUG_RESISTED_SPELL = 66, /* Debug: Resisted spell! */ - MSGBASIC_DEBUG_RECEIVED_STATUS = 73, /* Debug: 's status is now .. */ - MSGBASIC_DEBUG_RECOVERED_STATUS = 74, /* Debug: recovers from .. */ - MSGBASIC_DEBUG_DBLATK_PROC = 79, /* Debug: uses Double Attack (..%) */ - MSGBASIC_DEBUG_TRPATK_PROC = 80, /* Debug: uses Triple Attack (..%) */ - MSGBASIC_DEBUG_SUCCESS_CHANCE = 255 /* DEBUG: ..% chance of success */ + MSGBASIC_DEBUG_RESISTED_SPELL = 66, /* Debug: Resisted spell! */ + MSGBASIC_DEBUG_RECEIVED_STATUS = 73, /* Debug: 's status is now .. */ + MSGBASIC_DEBUG_RECOVERED_STATUS = 74, /* Debug: recovers from .. */ + MSGBASIC_DEBUG_DBLATK_PROC = 79, /* Debug: uses Double Attack (..%) */ + MSGBASIC_DEBUG_TRPATK_PROC = 80, /* Debug: uses Triple Attack (..%) */ + MSGBASIC_DEBUG_SUCCESS_CHANCE = 255, /* DEBUG: ..% chance of success */ + /* Monstrosity */ + MSGBASIC_FERETORY_COUNTDOWN = 679, // will return to the Feretory in }; class CBaseEntity; From 0dd7c1747920ff325c0076728ce1244d0d71f9e9 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Sun, 15 Oct 2023 22:23:58 +0100 Subject: [PATCH 087/103] Hook up species size --- scripts/globals/monstrosity.lua | 6 +- sql/monstrosity_species.sql | 321 ++++++++++++++++---------------- src/map/lua/lua_baseentity.cpp | 17 ++ src/map/lua/lua_baseentity.h | 1 + src/map/monstrosity.cpp | 9 +- src/map/monstrosity.h | 1 + 6 files changed, 192 insertions(+), 163 deletions(-) diff --git a/scripts/globals/monstrosity.lua b/scripts/globals/monstrosity.lua index bba0eb7bf33..94073041b14 100644 --- a/scripts/globals/monstrosity.lua +++ b/scripts/globals/monstrosity.lua @@ -1075,15 +1075,19 @@ xi.monstrosity.relinquishSteps = [0] = function(player) player:messageBasic(xi.msg.basic.FERETORY_COUNTDOWN, 0, 4) end, + [1] = function(player) player:messageBasic(xi.msg.basic.FERETORY_COUNTDOWN, 0, 3) end, + [2] = function(player) player:messageBasic(xi.msg.basic.FERETORY_COUNTDOWN, 0, 2) end, + [3] = function(player) player:messageBasic(xi.msg.basic.FERETORY_COUNTDOWN, 0, 1) end, + [4] = function(player) player:setPos(0, 0, 0, 0, xi.zone.FERETORY) end, @@ -1185,7 +1189,7 @@ xi.monstrosity.odysseanPassageOnTrigger = function(player, npc) return end - local monSize = 1 -- 0-index, Small, Medium, Large + local monSize = player:getMonstrositySize() local hasBelligerency = player:getBelligerencyFlag() and 1 or 0 -- NOTE: The list of available zones is built from the char's list of diff --git a/sql/monstrosity_species.sql b/sql/monstrosity_species.sql index 336f61ad03b..224a008a5f0 100644 --- a/sql/monstrosity_species.sql +++ b/sql/monstrosity_species.sql @@ -5,6 +5,7 @@ CREATE TABLE `monstrosity_species` ( `name` varchar(60) DEFAULT NULL, `mjob` tinyint(3) unsigned NOT NULL, `sjob` tinyint(3) unsigned NOT NULL, + `size` tinyint(3) unsigned NOT NULL, -- 0: small, 1: medium, 2: large `look` smallint(3) unsigned NOT NULL, PRIMARY KEY (`monstrosity_id`, `monstrosity_species_code`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4; @@ -16,226 +17,226 @@ CREATE TABLE `monstrosity_species` ( -- NOTE: The mjob/sjob of MONs rise at the same level, so the only difference between which -- : order you specify them is is which 2H ability you get. -INSERT INTO `monstrosity_species` VALUES (1, 1, "Rabbit", 1, 1, 0x010C); -INSERT INTO `monstrosity_species` VALUES (1, 256, "Onyx Rabbit", 1, 4, 0x010D); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (1, 257, "Alabaster Rabbit", 1, 3, 0x010E); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (1, 258, "Lapinion (Rabbit)", 3, 4, 0x0791); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (1, 1, "Rabbit", 1, 1, 0, 0x010C); +INSERT INTO `monstrosity_species` VALUES (1, 256, "Onyx Rabbit", 1, 4, 0, 0x010D); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (1, 257, "Alabaster Rabbit", 1, 3, 0, 0x010E); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (1, 258, "Lapinion (Rabbit)", 3, 4, 0, 0x0791); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (2, 2, "Behemoth", 1, 6, 0x0194); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (2, 259, "Elasamoth (Behemoth)", 8, 6, 0x0195); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (2, 2, "Behemoth", 1, 6, 2, 0x0194); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (2, 259, "Elasamoth (Behemoth)", 8, 6, 2, 0x0195); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (3, 3, "Tiger", 1, 1, 0x0134); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (3, 261, "Legendary Tiger", 1, 6, 0x0134); -- TODO: Look guessed not capped, this is the wrong id -INSERT INTO `monstrosity_species` VALUES (3, 262, "Smilodon (Tiger)", 1, 1, 0x0134); -- TODO: Look guessed not capped, this is the wrong id +INSERT INTO `monstrosity_species` VALUES (3, 3, "Tiger", 1, 1, 1, 0x0134); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (3, 261, "Legendary Tiger", 1, 6, 1, 0x0134); -- TODO: Look guessed not capped, this is the wrong id +INSERT INTO `monstrosity_species` VALUES (3, 262, "Smilodon (Tiger)", 1, 1, 1, 0x0134); -- TODO: Look guessed not capped, this is the wrong id -INSERT INTO `monstrosity_species` VALUES (4, 4, "Sheep", 1, 1, 0x0154); -- TODO: Look guessed not capped, this is the wrong id -INSERT INTO `monstrosity_species` VALUES (4, 263, "Karakul (Sheep)", 1, 1, 0x0154); -- TODO: Look guessed not capped, this is the wrong id +INSERT INTO `monstrosity_species` VALUES (4, 4, "Sheep", 1, 1, 1, 0x0154); -- TODO: Look guessed not capped, this is the wrong id +INSERT INTO `monstrosity_species` VALUES (4, 263, "Karakul (Sheep)", 1, 1, 1, 0x0154); -- TODO: Look guessed not capped, this is the wrong id -INSERT INTO `monstrosity_species` VALUES (5, 5, "Ram (Sheep)", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (5, 5, "Ram (Sheep)", 1, 1, 2, 0x0000); -INSERT INTO `monstrosity_species` VALUES (6, 6, "Dhalmel", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (6, 6, "Dhalmel", 1, 1, 2, 0x0000); -INSERT INTO `monstrosity_species` VALUES (7, 7, "Coeurl", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (7, 266, "Lynx (Coeurl)", 1, 4, 0x0000); -INSERT INTO `monstrosity_species` VALUES (7, 267, "Collared Lynx (Coeurl)", 5, 4, 0x0000); +INSERT INTO `monstrosity_species` VALUES (7, 7, "Coeurl", 1, 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (7, 266, "Lynx (Coeurl)", 1, 4, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (7, 267, "Collared Lynx (Coeurl)", 5, 4, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (8, 8, "Opo-opo", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (8, 8, "Opo-opo", 1, 1, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (9, 9, "Manticore", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (9, 268, "Legendary Manticore", 5, 4, 0x0000); +INSERT INTO `monstrosity_species` VALUES (9, 9, "Manticore", 1, 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (9, 268, "Legendary Manticore", 5, 4, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (10, 10, "Buffalo", 7, 7, 0x0000); +INSERT INTO `monstrosity_species` VALUES (10, 10, "Buffalo", 7, 7, 2, 0x0000); -INSERT INTO `monstrosity_species` VALUES (11, 11, "Marid", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (11, 11, "Marid", 1, 1, 2, 0x0000); -INSERT INTO `monstrosity_species` VALUES (12, 12, "Cerberus", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (12, 269, "Orthrus (Cerberus)", 1, 4, 0x0000); +INSERT INTO `monstrosity_species` VALUES (12, 12, "Cerberus", 1, 1, 2, 0x0000); +INSERT INTO `monstrosity_species` VALUES (12, 269, "Orthrus (Cerberus)", 1, 4, 2, 0x0000); -INSERT INTO `monstrosity_species` VALUES (13, 13, "Gnole", 2, 2, 0x0000); -INSERT INTO `monstrosity_species` VALUES (13, 270, "Bipedal Gnole", 2, 2, 0x0000); +INSERT INTO `monstrosity_species` VALUES (13, 13, "Gnole", 2, 2, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (13, 270, "Bipedal Gnole", 2, 2, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (15, 15, "Funguar", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (15, 271, "Coppercap (Funguar)", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (15, 15, "Funguar", 1, 1, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (15, 271, "Coppercap (Funguar)", 1, 1, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (16, 16, "Treant Sapling", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (16, 272, "Treant", 1, 4, 0x0000); -INSERT INTO `monstrosity_species` VALUES (16, 273, "Flowering Treant", 1, 4, 0x0000); -INSERT INTO `monstrosity_species` VALUES (16, 274, "Scarlet-tinged Treant", 1, 4, 0x0000); -INSERT INTO `monstrosity_species` VALUES (16, 275, "Barren Treant", 1, 4, 0x0000); -INSERT INTO `monstrosity_species` VALUES (16, 276, "Necklaced Treant", 1, 4, 0x0000); +INSERT INTO `monstrosity_species` VALUES (16, 16, "Treant Sapling", 1, 1, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (16, 272, "Treant", 1, 4, 2, 0x0000); +INSERT INTO `monstrosity_species` VALUES (16, 273, "Flowering Treant", 1, 4, 2, 0x0000); +INSERT INTO `monstrosity_species` VALUES (16, 274, "Scarlet-tinged Treant", 1, 4, 2, 0x0000); +INSERT INTO `monstrosity_species` VALUES (16, 275, "Barren Treant", 1, 4, 2, 0x0000); +INSERT INTO `monstrosity_species` VALUES (16, 276, "Necklaced Treant", 1, 4, 2, 0x0000); -INSERT INTO `monstrosity_species` VALUES (17, 17, "Morbol", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (17, 277, "Pygmy Morbol", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (17, 278, "Scarce Morbol", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (17, 279, "Ameretat (Morbol)", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (17, 280, "Purbol (Morbol)", 1, 4, 0x0000); +INSERT INTO `monstrosity_species` VALUES (17, 17, "Morbol", 1, 1, 2, 0x0000); +INSERT INTO `monstrosity_species` VALUES (17, 277, "Pygmy Morbol", 1, 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (17, 278, "Scarce Morbol", 1, 1, 2, 0x0000); +INSERT INTO `monstrosity_species` VALUES (17, 279, "Ameretat (Morbol)", 1, 1, 2, 0x0000); +INSERT INTO `monstrosity_species` VALUES (17, 280, "Purbol (Morbol)", 1, 4, 2, 0x0000); -INSERT INTO `monstrosity_species` VALUES (18, 18, "Mandragora", 2, 2, 0x012C); -INSERT INTO `monstrosity_species` VALUES (18, 281, "Korrigan (Mandragora)", 2, 4, 0x012C); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (18, 282, "Lycopodium (Mandragora)", 2, 3, 0x012C); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (18, 283, "Pygmy Mandragora", 2, 2, 0x012C); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (18, 284, "Adenium (Mandragora)", 2, 5, 0x012C); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (18, 285, "Pachypodium (Mandragora)", 2, 2, 0x012C); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (18, 286, "Enlightened Mandragora", 2, 2, 0x012C); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (18, 287, "New Year Mandragora", 2, 13, 0x0B48); +INSERT INTO `monstrosity_species` VALUES (18, 18, "Mandragora", 2, 2, 0, 0x012C); +INSERT INTO `monstrosity_species` VALUES (18, 281, "Korrigan (Mandragora)", 2, 4, 0, 0x012C); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (18, 282, "Lycopodium (Mandragora)", 2, 3, 0, 0x012C); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (18, 283, "Pygmy Mandragora", 2, 2, 0, 0x012C); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (18, 284, "Adenium (Mandragora)", 2, 5, 0, 0x012C); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (18, 285, "Pachypodium (Mandragora)", 2, 2, 0, 0x012C); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (18, 286, "Enlightened Mandragora", 2, 2, 0, 0x012C); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (18, 287, "New Year Mandragora", 2, 13, 0, 0x0B48); -INSERT INTO `monstrosity_species` VALUES (19, 19, "Sabotender", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (19, 288, "Sabotender Florido", 1, 5, 0x0000); +INSERT INTO `monstrosity_species` VALUES (19, 19, "Sabotender", 1, 1, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (19, 288, "Sabotender Florido", 1, 5, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (20, 20, "Flytrap", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (20, 20, "Flytrap", 1, 1, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (21, 21, "Goobbue", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (21, 21, "Goobbue", 1, 1, 2, 0x0000); -INSERT INTO `monstrosity_species` VALUES (22, 22, "Rafflesia", 8, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (22, 289, "Mitrastema (Rafflesia)", 8, 4, 0x0000); +INSERT INTO `monstrosity_species` VALUES (22, 22, "Rafflesia", 8, 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (22, 289, "Mitrastema (Rafflesia)", 8, 4, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (23, 23, "Panopt", 4, 4, 0x0000); +INSERT INTO `monstrosity_species` VALUES (23, 23, "Panopt", 4, 4, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (27, 27, "Bee", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (27, 290, "Vermillion and Onyx Bee", 1, 5, 0x0000); -INSERT INTO `monstrosity_species` VALUES (27, 291, "Zaffre Bee", 1, 4, 0x0000); +INSERT INTO `monstrosity_species` VALUES (27, 27, "Bee", 1, 1, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (27, 290, "Vermillion and Onyx Bee", 1, 5, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (27, 291, "Zaffre Bee", 1, 4, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (28, 28, "Beetle", 7, 7, 0x0000); -INSERT INTO `monstrosity_species` VALUES (28, 292, "Onyx Beetle", 7, 4, 0x0000); -INSERT INTO `monstrosity_species` VALUES (28, 293, "Gamboge Beetle", 7, 5, 0x0000); +INSERT INTO `monstrosity_species` VALUES (28, 28, "Beetle", 7, 7, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (28, 292, "Onyx Beetle", 7, 4, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (28, 293, "Gamboge Beetle", 7, 5, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (29, 29, "Crawler", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (29, 294, "Eruca (Crawler)", 1, 5, 0x0000); -INSERT INTO `monstrosity_species` VALUES (29, 295, "Emerald Crawler", 3, 4, 0x0000); -INSERT INTO `monstrosity_species` VALUES (29, 296, "Pygmy Emerald Crawler", 3, 4, 0x0000); +INSERT INTO `monstrosity_species` VALUES (29, 29, "Crawler", 1, 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (29, 294, "Eruca (Crawler)", 1, 5, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (29, 295, "Emerald Crawler", 3, 4, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (29, 296, "Pygmy Emerald Crawler", 3, 4, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (30, 30, "Fly", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (30, 297, "Vermillion Fly", 1, 5, 0x0000); +INSERT INTO `monstrosity_species` VALUES (30, 30, "Fly", 1, 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (30, 297, "Vermillion Fly", 1, 5, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (31, 31, "Scorpion", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (31, 298, "Scolopendrid (Scorpion)", 1, 4, 0x0000); -INSERT INTO `monstrosity_species` VALUES (31, 299, "Unusual Scolopendrid (Scorpion)", 8, 4, 0x0000); +INSERT INTO `monstrosity_species` VALUES (31, 31, "Scorpion", 1, 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (31, 298, "Scolopendrid (Scorpion)", 1, 4, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (31, 299, "Unusual Scolopendrid (Scorpion)", 8, 4, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (32, 32, "Spider", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (32, 300, "Reticulated Spider", 1, 8, 0x0000); -INSERT INTO `monstrosity_species` VALUES (32, 301, "Vermillion and Onyx Spider", 1, 5, 0x0000); +INSERT INTO `monstrosity_species` VALUES (32, 32, "Spider", 1, 1, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (32, 300, "Reticulated Spider", 1, 8, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (32, 301, "Vermillion and Onyx Spider", 1, 5, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (33, 33, "Antlion", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (33, 302, "Onyx Antlion", 1, 4, 0x0000); -INSERT INTO `monstrosity_species` VALUES (33, 303, "Formiceros (Antlion)", 8, 4, 0x0000); +INSERT INTO `monstrosity_species` VALUES (33, 33, "Antlion", 1, 1, 2, 0x0000); +INSERT INTO `monstrosity_species` VALUES (33, 302, "Onyx Antlion", 1, 4, 2, 0x0000); +INSERT INTO `monstrosity_species` VALUES (33, 303, "Formiceros (Antlion)", 8, 4, 2, 0x0000); -INSERT INTO `monstrosity_species` VALUES (34, 34, "Diremite", 8, 8, 0x0000); -INSERT INTO `monstrosity_species` VALUES (34, 304, "Arundemite (Diremite)", 8, 8, 0x0000); +INSERT INTO `monstrosity_species` VALUES (34, 34, "Diremite", 8, 8, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (34, 304, "Arundemite (Diremite)", 8, 8, 2, 0x0000); -INSERT INTO `monstrosity_species` VALUES (35, 35, "Chigoe", 6, 6, 0x0000); -INSERT INTO `monstrosity_species` VALUES (35, 305, "Azure Chigoe", 6, 4, 0x0000); +INSERT INTO `monstrosity_species` VALUES (35, 35, "Chigoe", 6, 6, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (35, 305, "Azure Chigoe", 6, 4, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (36, 36, "Wamouracampa (Wamoura larva)", 7, 7, 0x0000); -INSERT INTO `monstrosity_species` VALUES (36, 306, "Coiled Wamouracampa (Wamoura larva)", 7, 7, 0x0000); -INSERT INTO `monstrosity_species` VALUES (36, 307, "Wamoura", 1, 7, 0x0000); -INSERT INTO `monstrosity_species` VALUES (36, 308, "Coral Wamoura", 4, 7, 0x0000); +INSERT INTO `monstrosity_species` VALUES (36, 36, "Wamouracampa (Wamoura larva)", 7, 7, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (36, 306, "Coiled Wamouracampa (Wamoura larva)", 7, 7, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (36, 307, "Wamoura", 1, 7, 2, 0x0000); +INSERT INTO `monstrosity_species` VALUES (36, 308, "Coral Wamoura", 4, 7, 2, 0x0000); -INSERT INTO `monstrosity_species` VALUES (37, 37, "Ladybug", 6, 6, 0x0000); -INSERT INTO `monstrosity_species` VALUES (37, 309, "Gold Ladybug", 6, 5, 0x0000); +INSERT INTO `monstrosity_species` VALUES (37, 37, "Ladybug", 6, 6, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (37, 309, "Gold Ladybug", 6, 5, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (38, 38, "Gnat", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (38, 310, "Midge (Gnat)", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (38, 38, "Gnat", 1, 1, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (38, 310, "Midge (Gnat)", 1, 1, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (43, 43, "Lizard", 1, 1, 0x0148); -INSERT INTO `monstrosity_species` VALUES (43, 315, "Ashen Lizard", 1, 4, 0x0148); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (43, 43, "Lizard", 1, 1, 0, 0x0148); +INSERT INTO `monstrosity_species` VALUES (43, 315, "Ashen Lizard", 1, 4, 0, 0x0148); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (44, 44, "Raptor", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (44, 316, "Emerald Raptor", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (44, 317, "Vermillion Raptor", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (44, 44, "Raptor", 1, 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (44, 316, "Emerald Raptor", 1, 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (44, 317, "Vermillion Raptor", 1, 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (45, 45, "Adamantoise", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (45, 318, "Pygmy Adamantoise", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (45, 319, "Legendary Adamantoise", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (45, 320, "Ferromantoise (Adamantoise)", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (45, 45, "Adamantoise", 1, 1, 2, 0x0000); +INSERT INTO `monstrosity_species` VALUES (45, 318, "Pygmy Adamantoise", 1, 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (45, 319, "Legendary Adamantoise", 1, 1, 2, 0x0000); +INSERT INTO `monstrosity_species` VALUES (45, 320, "Ferromantoise (Adamantoise)", 1, 1, 2, 0x0000); -INSERT INTO `monstrosity_species` VALUES (46, 46, "Bugard", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (46, 321, "Abyssobugard (Bugard)", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (46, 46, "Bugard", 1, 1, 2, 0x0000); +INSERT INTO `monstrosity_species` VALUES (46, 321, "Abyssobugard (Bugard)", 1, 1, 2, 0x0000); -INSERT INTO `monstrosity_species` VALUES (47, 47, "Eft", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (47, 322, "Tarichuk (Eft)", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (47, 47, "Eft", 1, 1, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (47, 322, "Tarichuk (Eft)", 1, 1, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (48, 48, "Wivre", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (48, 323, "Unusual Wivre", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (48, 48, "Wivre", 1, 1, 2, 0x0000); +INSERT INTO `monstrosity_species` VALUES (48, 323, "Unusual Wivre", 1, 1, 2, 0x0000); -INSERT INTO `monstrosity_species` VALUES (49, 49, "Peiste", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (49, 324, "Sibilus (Peiste)", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (49, 49, "Peiste", 1, 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (49, 324, "Sibilus (Peiste)", 1, 1, 2, 0x0000); -INSERT INTO `monstrosity_species` VALUES (52, 52, "Slime", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (52, 329, "Clot (Slime)", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (52, 330, "Gold Slime", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (52, 331, "Boil (Slime)", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (52, 52, "Slime", 1, 1, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (52, 329, "Clot (Slime)", 1, 1, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (52, 330, "Gold Slime", 1, 1, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (52, 331, "Boil (Slime)", 1, 1, 2, 0x0000); -INSERT INTO `monstrosity_species` VALUES (53, 53, "Hecteyes", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (53, 53, "Hecteyes", 1, 1, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (54, 54, "Flan", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (54, 332, "Gold Flan", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (54, 333, "Blancmange (Flan)", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (54, 54, "Flan", 1, 1, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (54, 332, "Gold Flan", 1, 1, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (54, 333, "Blancmange (Flan)", 1, 1, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (56, 56, "Slug", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (56, 56, "Slug", 1, 1, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (57, 57, "Sandworm", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (57, 334, "Pygmy Sandworm", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (57, 335, "Gigaworm (Sandworm)", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (57, 57, "Sandworm", 1, 1, 2, 0x0000); +INSERT INTO `monstrosity_species` VALUES (57, 334, "Pygmy Sandworm", 1, 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (57, 335, "Gigaworm (Sandworm)", 1, 1, 2, 0x0000); -INSERT INTO `monstrosity_species` VALUES (58, 58, "Leech", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (58, 336, "Azure Leech", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (58, 337, "Obdella (Leech)", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (58, 58, "Leech", 1, 1, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (58, 336, "Azure Leech", 1, 1, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (58, 337, "Obdella (Leech)", 1, 1, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (60, 60, "Crab", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (60, 340, "Vermillion Crab", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (60, 341, "Basket-burdened Crab", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (60, 342, "Vermillion Basket-burdened Crab", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (60, 343, "Porter Crab (Crab)", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (60, 60, "Crab", 1, 1, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (60, 340, "Vermillion Crab", 1, 1, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (60, 341, "Basket-burdened Crab", 1, 1, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (60, 342, "Vermillion Basket-burdened Crab", 1, 1, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (60, 343, "Porter Crab (Crab)", 1, 1, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (61, 61, "Pugil", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (61, 344, "Jagil (Pugil)", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (61, 61, "Pugil", 1, 1, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (61, 344, "Jagil (Pugil)", 1, 1, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (62, 62, "Sea Monk", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (62, 345, "Azure Sea Monk", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (62, 62, "Sea Monk", 1, 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (62, 345, "Azure Sea Monk", 1, 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (63, 63, "Uragnite", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (63, 346, "Limascabra (Uragnite)", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (63, 63, "Uragnite", 1, 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (63, 346, "Limascabra (Uragnite)", 1, 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (64, 64, "Orobon", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (64, 347, "Pygmy Orobon", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (64, 348, "Ogrebon (Orobon)", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (64, 64, "Orobon", 1, 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (64, 347, "Pygmy Orobon", 1, 1, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (64, 348, "Ogrebon (Orobon)", 1, 1, 2, 0x0000); -INSERT INTO `monstrosity_species` VALUES (65, 65, "Ruszor", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (65, 65, "Ruszor", 1, 1, 2, 0x0000); -INSERT INTO `monstrosity_species` VALUES (66, 66, "Toad", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (66, 349, "Azure Toad", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (66, 350, "Vermillion Toad", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (66, 66, "Toad", 1, 1, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (66, 349, "Azure Toad", 1, 1, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (66, 350, "Vermillion Toad", 1, 1, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (69, 69, "Bird", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (69, 351, "Onyx Bird", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (69, 69, "Bird", 1, 1, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (69, 351, "Onyx Bird", 1, 1, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (70, 70, "Cockatrice", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (70, 352, "Ziz (Cockatrice)", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (70, 70, "Cockatrice", 1, 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (70, 352, "Ziz (Cockatrice)", 1, 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (71, 71, "Roc", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (71, 353, "Legendary Roc", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (71, 354, "Gagana (Roc)", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (71, 71, "Roc", 1, 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (71, 353, "Legendary Roc", 1, 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (71, 354, "Gagana (Roc)", 1, 1, 2, 0x0000); -INSERT INTO `monstrosity_species` VALUES (72, 72, "Bat", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (72, 355, "Bats", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (72, 356, "Vermillion Bat", 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (72, 357, "Vermillion Bats", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (72, 72, "Bat", 1, 1, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (72, 355, "Bats", 1, 1, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (72, 356, "Vermillion Bat", 1, 1, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (72, 357, "Vermillion Bats", 1, 1, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (73, 73, "Hippogryph", 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (73, 73, "Hippogryph", 1, 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (74, 74, "Apkallu", 2, 2, 0x0000); -INSERT INTO `monstrosity_species` VALUES (74, 358, "Inguza (Apkallu)", 13, 2, 0x0000); +INSERT INTO `monstrosity_species` VALUES (74, 74, "Apkallu", 2, 2, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (74, 358, "Inguza (Apkallu)", 13, 2, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (75, 75, "Colibri", 5, 5, 0x0000); -INSERT INTO `monstrosity_species` VALUES (75, 359, "Toucalibri (Colibri)", 10, 5, 0x0000); +INSERT INTO `monstrosity_species` VALUES (75, 75, "Colibri", 5, 5, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (75, 359, "Toucalibri (Colibri)", 10, 5, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (76, 76, "Amphiptere", 1, 4, 0x0000); -INSERT INTO `monstrosity_species` VALUES (76, 360, "Sanguiptere (Amphiptere)", 4, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (76, 76, "Amphiptere", 1, 4, 2, 0x0000); +INSERT INTO `monstrosity_species` VALUES (76, 360, "Sanguiptere (Amphiptere)", 4, 1, 2, 0x0000); -INSERT INTO `monstrosity_species` VALUES (126, 254, "Astoltian Slime", 1, 1, 0x0B41); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (126, 508, "Astoltian She-Slime", 1, 1, 0x0B5B); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (126, 509, "Astoltian Metal Slime", 4, 1, 0x0B5C); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (126, 254, "Astoltian Slime", 1, 1, 0, 0x0B41); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (126, 508, "Astoltian She-Slime", 1, 1, 0, 0x0B5B); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (126, 509, "Astoltian Metal Slime", 4, 1, 0, 0x0B5C); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (127, 255, "Eorzean Spriggan", 1, 4, 0x0B42); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (127, 510, "Eorzean Spriggan.C", 1, 4, 0x0B5D); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (127, 511, "Eorzean Spriggan.G", 4, 1, 0x0B5E); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (127, 255, "Eorzean Spriggan", 1, 4, 0, 0x0B42); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (127, 510, "Eorzean Spriggan.C", 1, 4, 0, 0x0B5D); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (127, 511, "Eorzean Spriggan.G", 4, 1, 0, 0x0B5E); -- TODO: Look guessed not capped diff --git a/src/map/lua/lua_baseentity.cpp b/src/map/lua/lua_baseentity.cpp index 0249f6fa38d..fcdf2280a2a 100644 --- a/src/map/lua/lua_baseentity.cpp +++ b/src/map/lua/lua_baseentity.cpp @@ -6430,6 +6430,22 @@ void CLuaBaseEntity::setBelligerencyFlag(bool flag) monstrosity::SetBelligerencyFlag(PChar, flag); } +auto CLuaBaseEntity::getMonstrositySize() -> uint8 +{ + auto* PChar = dynamic_cast(m_PBaseEntity); + if (PChar == nullptr) + { + return 0; + } + + if (PChar->m_PMonstrosity == nullptr) + { + return 0; + } + + return PChar->m_PMonstrosity->Size; +} + /************************************************************************ * Function: getTitle() * Purpose : Returns the integer value of the player's current title @@ -17240,6 +17256,7 @@ void CLuaBaseEntity::Register() SOL_REGISTER("setMonstrosityData", CLuaBaseEntity::setMonstrosityData); SOL_REGISTER("getBelligerencyFlag", CLuaBaseEntity::getBelligerencyFlag); SOL_REGISTER("setBelligerencyFlag", CLuaBaseEntity::setBelligerencyFlag); + SOL_REGISTER("getMonstrositySize", CLuaBaseEntity::getMonstrositySize); // Player Titles and Fame SOL_REGISTER("getTitle", CLuaBaseEntity::getTitle); diff --git a/src/map/lua/lua_baseentity.h b/src/map/lua/lua_baseentity.h index 09339db60d3..a0bb0f47a16 100644 --- a/src/map/lua/lua_baseentity.h +++ b/src/map/lua/lua_baseentity.h @@ -336,6 +336,7 @@ class CLuaBaseEntity void setMonstrosityData(sol::table table); bool getBelligerencyFlag(); void setBelligerencyFlag(bool flag); + auto getMonstrositySize() -> uint8; // Player Titles and Fame uint16 getTitle(); diff --git a/src/map/monstrosity.cpp b/src/map/monstrosity.cpp index 0df69ea264b..0680a04f7b1 100644 --- a/src/map/monstrosity.cpp +++ b/src/map/monstrosity.cpp @@ -55,6 +55,7 @@ struct MonstrositySpeciesRow std::string name; JOBTYPE mjob; JOBTYPE sjob; + uint8 size; uint16 look; }; @@ -77,6 +78,7 @@ monstrosity::MonstrosityData_t::MonstrosityData_t() , Species(0x0001) // Rabbit , Flags(0x0B44) // ? , Look(0x010C) // Rabbit +, Size(0x00) // Size (0: Small, 1: Medium, 2: Large) , NamePrefix1(0x00) // Nothing , NamePrefix2(0x00) // Nothing , MainJob(JOB_WAR) // @@ -95,7 +97,7 @@ void monstrosity::LoadStaticData() { ShowInfo("Loading Monstrosity data"); - int32 ret = sql->Query("SELECT monstrosity_id, monstrosity_species_code, name, mjob, sjob, look FROM monstrosity_species;"); + int32 ret = sql->Query("SELECT monstrosity_id, monstrosity_species_code, name, mjob, sjob, size, look FROM monstrosity_species;"); if (ret != SQL_ERROR && sql->NumRows() != 0) { while (sql->NextRow() == SQL_SUCCESS) @@ -107,7 +109,8 @@ void monstrosity::LoadStaticData() row.name = sql->GetStringData(2); row.mjob = static_cast(sql->GetUIntData(3)); row.sjob = static_cast(sql->GetUIntData(4)); - row.look = static_cast(sql->GetUIntData(5)); + row.size = static_cast(sql->GetUIntData(5)); + row.look = static_cast(sql->GetUIntData(6)); gMonstrositySpeciesMap[row.monstrositySpeciesCode] = row; } @@ -172,6 +175,7 @@ void monstrosity::ReadMonstrosityData(CCharEntity* PChar) // Build additional data from lookups data->MainJob = gMonstrositySpeciesMap[data->Species].mjob; data->SubJob = gMonstrositySpeciesMap[data->Species].sjob; + data->Size = gMonstrositySpeciesMap[data->Species].size; // TODO: auto level = data->levels[data->MonstrosityId]; @@ -409,6 +413,7 @@ void monstrosity::HandleEquipChangePacket(CCharEntity* PChar, CBasicPacket& data PChar->m_PMonstrosity->MonstrosityId = data.monstrosityId; PChar->m_PMonstrosity->MainJob = data.mjob; PChar->m_PMonstrosity->SubJob = data.sjob; + PChar->m_PMonstrosity->Size = data.size; PChar->m_PMonstrosity->Look = data.look; if (PChar->m_PMonstrosity->MonstrosityId != previousId) diff --git a/src/map/monstrosity.h b/src/map/monstrosity.h index b4994a51630..cd02955032e 100644 --- a/src/map/monstrosity.h +++ b/src/map/monstrosity.h @@ -45,6 +45,7 @@ namespace monstrosity uint16 Species; uint16 Flags; uint16 Look; + uint8 Size; uint8 NamePrefix1; uint8 NamePrefix2; From 91b3a852ce2f9a8bc9c7262cb590ad8ef4d77c7c Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Mon, 16 Oct 2023 00:20:15 +0100 Subject: [PATCH 088/103] Move OnMobSkillFinished down to CBattleEntity --- src/map/ai/ai_container.cpp | 14 +- src/map/ai/states/mobskill_state.cpp | 3 +- src/map/ai/states/mobskill_state.h | 6 +- src/map/entities/battleentity.cpp | 249 +++++++++++++++++++++++++++ src/map/entities/battleentity.h | 2 + src/map/entities/mobentity.cpp | 241 +------------------------- src/map/entities/mobentity.h | 2 +- src/map/monstrosity.cpp | 22 ++- 8 files changed, 285 insertions(+), 254 deletions(-) diff --git a/src/map/ai/ai_container.cpp b/src/map/ai/ai_container.cpp index e8caed61783..1a8457382bf 100644 --- a/src/map/ai/ai_container.cpp +++ b/src/map/ai/ai_container.cpp @@ -226,7 +226,7 @@ bool CAIContainer::Internal_Cast(uint16 targetid, SpellID spellid) auto* entity = dynamic_cast(PEntity); if (entity) { - if (auto target = entity->GetEntity(targetid); target && target->PAI->IsUntargetable()) + if (auto* target = entity->GetEntity(targetid); target && target->PAI->IsUntargetable()) { return false; } @@ -269,7 +269,7 @@ bool CAIContainer::Internal_WeaponSkill(uint16 targid, uint16 wsid) auto* entity = dynamic_cast(PEntity); if (entity) { - if (auto target = entity->GetEntity(targid); target && target->PAI->IsUntargetable()) + if (auto* target = entity->GetEntity(targid); target && target->PAI->IsUntargetable()) { return false; } @@ -280,10 +280,10 @@ bool CAIContainer::Internal_WeaponSkill(uint16 targid, uint16 wsid) bool CAIContainer::Internal_MobSkill(uint16 targid, uint16 wsid) { - auto* entity = dynamic_cast(PEntity); + auto* entity = dynamic_cast(PEntity); if (entity) { - if (auto target = entity->GetEntity(targid); target && target->PAI->IsUntargetable()) + if (auto* target = entity->GetEntity(targid); target && target->PAI->IsUntargetable()) { return false; } @@ -297,7 +297,7 @@ bool CAIContainer::Internal_PetSkill(uint16 targid, uint16 abilityid) auto* entity = dynamic_cast(PEntity); if (entity) { - if (auto target = entity->GetEntity(targid); target && target->PAI->IsUntargetable()) + if (auto* target = entity->GetEntity(targid); target && target->PAI->IsUntargetable()) { return false; } @@ -311,7 +311,7 @@ bool CAIContainer::Internal_Ability(uint16 targetid, uint16 abilityid) auto* entity = dynamic_cast(PEntity); if (entity) { - if (auto target = entity->GetEntity(targetid); target && target->PAI->IsUntargetable()) + if (auto* target = entity->GetEntity(targetid); target && target->PAI->IsUntargetable()) { return false; } @@ -325,7 +325,7 @@ bool CAIContainer::Internal_RangedAttack(uint16 targetid) auto* entity = dynamic_cast(PEntity); if (entity) { - if (auto target = entity->GetEntity(targetid); target && target->PAI->IsUntargetable()) + if (auto* target = entity->GetEntity(targetid); target && target->PAI->IsUntargetable()) { return false; } diff --git a/src/map/ai/states/mobskill_state.cpp b/src/map/ai/states/mobskill_state.cpp index 4a2c85a223d..9fb0fbf3fee 100644 --- a/src/map/ai/states/mobskill_state.cpp +++ b/src/map/ai/states/mobskill_state.cpp @@ -22,13 +22,14 @@ along with this program. If not, see http://www.gnu.org/licenses/ #include "mobskill_state.h" #include "ai/ai_container.h" #include "enmity_container.h" +#include "entities/battleentity.h" #include "entities/mobentity.h" #include "mobskill.h" #include "packets/action.h" #include "status_effect_container.h" #include "utils/battleutils.h" -CMobSkillState::CMobSkillState(CMobEntity* PEntity, uint16 targid, uint16 wsid) +CMobSkillState::CMobSkillState(CBattleEntity* PEntity, uint16 targid, uint16 wsid) : CState(PEntity, targid) , m_PEntity(PEntity) , m_spentTP(0) diff --git a/src/map/ai/states/mobskill_state.h b/src/map/ai/states/mobskill_state.h index 0b640ab8203..25232eee384 100644 --- a/src/map/ai/states/mobskill_state.h +++ b/src/map/ai/states/mobskill_state.h @@ -25,12 +25,12 @@ along with this program. If not, see http://www.gnu.org/licenses/ #include "mobskill.h" #include "state.h" -class CMobEntity; +class CBattleEntity; class CMobSkillState : public CState { public: - CMobSkillState(CMobEntity* PEntity, uint16 targid, uint16 wsid); + CMobSkillState(CBattleEntity* PEntity, uint16 targid, uint16 wsid); CMobSkill* GetSkill(); @@ -57,7 +57,7 @@ class CMobSkillState : public CState void SpendCost(); private: - CMobEntity* const m_PEntity; + CBattleEntity* const m_PEntity; std::unique_ptr m_PSkill; time_point m_finishTime; duration m_castTime{}; diff --git a/src/map/entities/battleentity.cpp b/src/map/entities/battleentity.cpp index 3477233ffc0..adc2ee477a4 100644 --- a/src/map/entities/battleentity.cpp +++ b/src/map/entities/battleentity.cpp @@ -30,6 +30,7 @@ #include "ai/states/despawn_state.h" #include "ai/states/inactive_state.h" #include "ai/states/magic_state.h" +#include "ai/states/mobskill_state.h" #include "ai/states/raise_state.h" #include "ai/states/weaponskill_state.h" #include "attack.h" @@ -1644,6 +1645,254 @@ void CBattleEntity::OnWeaponSkillFinished(CWeaponSkillState& state, action_t& ac action.actionid = PWeaponskill->getID(); } +void CBattleEntity::OnMobSkillFinished(CMobSkillState& state, action_t& action) +{ + auto* PSkill = state.GetSkill(); + auto* PTarget = dynamic_cast(state.GetTarget()); + + if (PTarget == nullptr) + { + ShowWarning("CMobEntity::OnMobSkillFinished: PTarget is null"); + return; + } + + if (auto* PMob = dynamic_cast(this)) + { + // store the skill used + PMob->m_UsedSkillIds[PSkill->getID()] = GetMLevel(); + } + + PAI->TargetFind->reset(); + + float distance = PSkill->getDistance(); + uint8 findFlags = 0; + if (PSkill->getFlag() & SKILLFLAG_HIT_ALL) + { + findFlags |= FINDFLAGS_HIT_ALL; + } + + // Mob buff abilities also hit monster's pets + if (PSkill->getValidTargets() == TARGET_SELF) + { + findFlags |= FINDFLAGS_PET; + } + + if ((PSkill->getValidTargets() & TARGET_IGNORE_BATTLEID) == TARGET_IGNORE_BATTLEID) + { + findFlags |= FINDFLAGS_IGNORE_BATTLEID; + } + + action.id = id; + if (objtype == TYPE_PET && static_cast(this)->getPetType() == PET_TYPE::AVATAR) + { + action.actiontype = ACTION_PET_MOBABILITY_FINISH; + } + else if (PSkill->getID() < 256) + { + action.actiontype = ACTION_WEAPONSKILL_FINISH; + } + else + { + action.actiontype = ACTION_MOBABILITY_FINISH; + } + action.actionid = PSkill->getID(); + + if (PAI->TargetFind->isWithinRange(&PTarget->loc.p, distance)) + { + if (PSkill->isAoE()) + { + PAI->TargetFind->findWithinArea(PTarget, static_cast(PSkill->getAoe()), PSkill->getRadius(), findFlags); + } + else if (PSkill->isConal()) + { + float angle = 45.0f; + PAI->TargetFind->findWithinCone(PTarget, distance, angle, findFlags); + } + else + { + if (this->objtype == TYPE_MOB && PTarget->objtype == TYPE_PC) + { + CBattleEntity* PCoverAbilityUser = battleutils::GetCoverAbilityUser(PTarget, this); + if (PCoverAbilityUser != nullptr) + { + PTarget = PCoverAbilityUser; + } + } + + PAI->TargetFind->findSingleTarget(PTarget, findFlags); + } + } + else // Out of range + { + action.actiontype = ACTION_MOBABILITY_INTERRUPT; + action.actionid = 0; + actionList_t& actionList = action.getNewActionList(); + actionList.ActionTargetID = PTarget->id; + + actionTarget_t& actionTarget = actionList.getNewActionTarget(); + actionTarget.animation = 0x1FC; // Hardcoded magic sent from the server + actionTarget.messageID = MSGBASIC_TOO_FAR_AWAY; + actionTarget.speceffect = SPECEFFECT::BLOOD; + return; + } + + uint16 targets = static_cast(PAI->TargetFind->m_targets.size()); + + // No targets, perhaps something like Super Jump or otherwise untargetable + if (targets == 0) + { + action.actiontype = ACTION_MOBABILITY_INTERRUPT; + action.actionid = 28787; // Some hardcoded magic for interrupts + actionList_t& actionList = action.getNewActionList(); + actionList.ActionTargetID = id; + + actionTarget_t& actionTarget = actionList.getNewActionTarget(); + actionTarget.animation = 0x1FC; // Hardcoded magic sent from the server + actionTarget.messageID = 0; + actionTarget.reaction = REACTION::ABILITY | REACTION::HIT; + + return; + } + + PSkill->setTotalTargets(targets); + PSkill->setTP(state.GetSpentTP()); + PSkill->setHPP(GetHPP()); + + uint16 msg = 0; + uint16 defaultMessage = PSkill->getMsg(); + + bool first{ true }; + for (auto&& PTargetFound : PAI->TargetFind->m_targets) + { + actionList_t& list = action.getNewActionList(); + + list.ActionTargetID = PTargetFound->id; + + actionTarget_t& target = list.getNewActionTarget(); + + list.ActionTargetID = PTargetFound->id; + target.reaction = REACTION::HIT; + target.speceffect = SPECEFFECT::HIT; + target.animation = PSkill->getAnimationID(); + target.messageID = PSkill->getMsg(); + + // reset the skill's message back to default + PSkill->setMsg(defaultMessage); + int32 damage = 0; + if (objtype == TYPE_PET && static_cast(this)->getPetType() != PET_TYPE::JUG_PET) + { + PET_TYPE petType = static_cast(this)->getPetType(); + + if (static_cast(this)->getPetType() == PET_TYPE::AVATAR || static_cast(this)->getPetType() == PET_TYPE::WYVERN) + { + target.animation = PSkill->getPetAnimationID(); + } + + if (petType == PET_TYPE::AUTOMATON) + { + damage = luautils::OnAutomatonAbility(PTargetFound, this, PSkill, PMaster, &action); + } + else + { + damage = luautils::OnPetAbility(PTargetFound, this, PSkill, PMaster, &action); + } + } + else + { + damage = luautils::OnMobWeaponSkill(PTargetFound, this, PSkill, &action); + this->PAI->EventHandler.triggerListener("WEAPONSKILL_USE", CLuaBaseEntity(this), CLuaBaseEntity(PTargetFound), PSkill->getID(), state.GetSpentTP(), CLuaAction(&action), damage); + PTarget->PAI->EventHandler.triggerListener("WEAPONSKILL_TAKE", CLuaBaseEntity(PTargetFound), CLuaBaseEntity(this), PSkill->getID(), state.GetSpentTP(), CLuaAction(&action)); + } + + if (msg == 0) + { + msg = PSkill->getMsg(); + } + else + { + msg = PSkill->getAoEMsg(); + } + + if (damage < 0) + { + msg = MSGBASIC_SKILL_RECOVERS_HP; // TODO: verify this message does/does not vary depending on mob/avatar/automaton use + target.param = std::clamp(-damage, 0, PTargetFound->GetMaxHP() - PTargetFound->health.hp); + } + else + { + target.param = damage; + } + + target.messageID = msg; + + if (PSkill->hasMissMsg()) + { + target.reaction = REACTION::MISS; + target.speceffect = SPECEFFECT::NONE; + if (msg == PSkill->getAoEMsg()) + { + msg = 282; + } + } + else + { + target.reaction = REACTION::HIT; + target.speceffect = SPECEFFECT::HIT; + } + + // TODO: Should this be reaction and not speceffect? + if (target.speceffect == SPECEFFECT::HIT) // Formerly bitwise and, though nothing in this function adds additional bits to the field + { + target.speceffect = SPECEFFECT::RECOIL; + target.knockback = PSkill->getKnockback(); + if (first && (PSkill->getPrimarySkillchain() != 0)) + { + SUBEFFECT effect = battleutils::GetSkillChainEffect(PTargetFound, PSkill->getPrimarySkillchain(), PSkill->getSecondarySkillchain(), + PSkill->getTertiarySkillchain()); + if (effect != SUBEFFECT_NONE) + { + int32 skillChainDamage = battleutils::TakeSkillchainDamage(this, PTargetFound, target.param, nullptr); + if (skillChainDamage < 0) + { + target.addEffectParam = -skillChainDamage; + target.addEffectMessage = 384 + effect; + } + else + { + target.addEffectParam = skillChainDamage; + target.addEffectMessage = 287 + effect; + } + target.additionalEffect = effect; + } + + first = false; + } + } + + if (PSkill->getValidTargets() & TARGET_ENEMY) + { + PTargetFound->StatusEffectContainer->DelStatusEffectsByFlag(EFFECTFLAG_DETECTABLE); + } + + if (PTargetFound->isDead()) + { + battleutils::ClaimMob(PTargetFound, this); + } + battleutils::DirtyExp(PTargetFound, this); + } + + PTarget = dynamic_cast(state.GetTarget()); // TODO: why is this recast here? can state change between now and the original cast? + + if (PTarget) + { + if (PTarget->objtype == TYPE_MOB && (PTarget->isDead() || (objtype == TYPE_PET && static_cast(this)->getPetType() == PET_TYPE::AVATAR))) + { + battleutils::ClaimMob(PTarget, this); + } + battleutils::DirtyExp(PTarget, this); + } +} + bool CBattleEntity::CanAttack(CBattleEntity* PTarget, std::unique_ptr& errMsg) { TracyZoneScoped; diff --git a/src/map/entities/battleentity.h b/src/map/entities/battleentity.h index e673c9e1a75..eb6166640b4 100644 --- a/src/map/entities/battleentity.h +++ b/src/map/entities/battleentity.h @@ -521,6 +521,7 @@ class CSpell; class CItemEquipment; class CAbilityState; class CAttackState; +class CMobSkillState; class CWeaponSkillState; class CMagicState; class CDespawnState; @@ -701,6 +702,7 @@ class CBattleEntity : public CBaseEntity virtual void OnCastInterrupted(CMagicState&, action_t&, MSGBASIC_ID msg, bool blockedCast); /* Weaponskill */ virtual void OnWeaponSkillFinished(CWeaponSkillState& state, action_t& action); + virtual void OnMobSkillFinished(CMobSkillState& state, action_t& action); virtual void OnChangeTarget(CBattleEntity* PTarget); // Used to set an action to an "interrupted" state diff --git a/src/map/entities/mobentity.cpp b/src/map/entities/mobentity.cpp index 949ecfda538..b08eec7cb3e 100644 --- a/src/map/entities/mobentity.cpp +++ b/src/map/entities/mobentity.cpp @@ -591,249 +591,10 @@ void CMobEntity::OnWeaponSkillFinished(CWeaponSkillState& state, action_t& actio void CMobEntity::OnMobSkillFinished(CMobSkillState& state, action_t& action) { TracyZoneScoped; - auto* PSkill = state.GetSkill(); - auto* PTarget = dynamic_cast(state.GetTarget()); - if (PTarget == nullptr) - { - ShowWarning("CMobEntity::OnMobSkillFinished: PTarget is null"); - return; - } + CBattleEntity::OnMobSkillFinished(state, action); TapDeaggroTime(); - - // store the skill used - m_UsedSkillIds[PSkill->getID()] = GetMLevel(); - - PAI->TargetFind->reset(); - - float distance = PSkill->getDistance(); - uint8 findFlags = 0; - if (PSkill->getFlag() & SKILLFLAG_HIT_ALL) - { - findFlags |= FINDFLAGS_HIT_ALL; - } - - // Mob buff abilities also hit monster's pets - if (PSkill->getValidTargets() == TARGET_SELF) - { - findFlags |= FINDFLAGS_PET; - } - - if ((PSkill->getValidTargets() & TARGET_IGNORE_BATTLEID) == TARGET_IGNORE_BATTLEID) - { - findFlags |= FINDFLAGS_IGNORE_BATTLEID; - } - - action.id = id; - if (objtype == TYPE_PET && static_cast(this)->getPetType() == PET_TYPE::AVATAR) - { - action.actiontype = ACTION_PET_MOBABILITY_FINISH; - } - else if (PSkill->getID() < 256) - { - action.actiontype = ACTION_WEAPONSKILL_FINISH; - } - else - { - action.actiontype = ACTION_MOBABILITY_FINISH; - } - action.actionid = PSkill->getID(); - - if (PAI->TargetFind->isWithinRange(&PTarget->loc.p, distance)) - { - if (PSkill->isAoE()) - { - PAI->TargetFind->findWithinArea(PTarget, static_cast(PSkill->getAoe()), PSkill->getRadius(), findFlags); - } - else if (PSkill->isConal()) - { - float angle = 45.0f; - PAI->TargetFind->findWithinCone(PTarget, distance, angle, findFlags); - } - else - { - if (this->objtype == TYPE_MOB && PTarget->objtype == TYPE_PC) - { - CBattleEntity* PCoverAbilityUser = battleutils::GetCoverAbilityUser(PTarget, this); - if (PCoverAbilityUser != nullptr) - { - PTarget = PCoverAbilityUser; - } - } - - PAI->TargetFind->findSingleTarget(PTarget, findFlags); - } - } - else // Out of range - { - action.actiontype = ACTION_MOBABILITY_INTERRUPT; - action.actionid = 0; - actionList_t& actionList = action.getNewActionList(); - actionList.ActionTargetID = PTarget->id; - - actionTarget_t& actionTarget = actionList.getNewActionTarget(); - actionTarget.animation = 0x1FC; // Hardcoded magic sent from the server - actionTarget.messageID = MSGBASIC_TOO_FAR_AWAY; - actionTarget.speceffect = SPECEFFECT::BLOOD; - return; - } - - uint16 targets = static_cast(PAI->TargetFind->m_targets.size()); - - // No targets, perhaps something like Super Jump or otherwise untargetable - if (targets == 0) - { - action.actiontype = ACTION_MOBABILITY_INTERRUPT; - action.actionid = 28787; // Some hardcoded magic for interrupts - actionList_t& actionList = action.getNewActionList(); - actionList.ActionTargetID = id; - - actionTarget_t& actionTarget = actionList.getNewActionTarget(); - actionTarget.animation = 0x1FC; // Hardcoded magic sent from the server - actionTarget.messageID = 0; - actionTarget.reaction = REACTION::ABILITY | REACTION::HIT; - - return; - } - - PSkill->setTotalTargets(targets); - PSkill->setTP(state.GetSpentTP()); - PSkill->setHPP(GetHPP()); - - uint16 msg = 0; - uint16 defaultMessage = PSkill->getMsg(); - - bool first{ true }; - for (auto&& PTargetFound : PAI->TargetFind->m_targets) - { - actionList_t& list = action.getNewActionList(); - - list.ActionTargetID = PTargetFound->id; - - actionTarget_t& target = list.getNewActionTarget(); - - list.ActionTargetID = PTargetFound->id; - target.reaction = REACTION::HIT; - target.speceffect = SPECEFFECT::HIT; - target.animation = PSkill->getAnimationID(); - target.messageID = PSkill->getMsg(); - - // reset the skill's message back to default - PSkill->setMsg(defaultMessage); - int32 damage = 0; - if (objtype == TYPE_PET && static_cast(this)->getPetType() != PET_TYPE::JUG_PET) - { - PET_TYPE petType = static_cast(this)->getPetType(); - - if (static_cast(this)->getPetType() == PET_TYPE::AVATAR || static_cast(this)->getPetType() == PET_TYPE::WYVERN) - { - target.animation = PSkill->getPetAnimationID(); - } - - if (petType == PET_TYPE::AUTOMATON) - { - damage = luautils::OnAutomatonAbility(PTargetFound, this, PSkill, PMaster, &action); - } - else - { - damage = luautils::OnPetAbility(PTargetFound, this, PSkill, PMaster, &action); - } - } - else - { - damage = luautils::OnMobWeaponSkill(PTargetFound, this, PSkill, &action); - this->PAI->EventHandler.triggerListener("WEAPONSKILL_USE", CLuaBaseEntity(this), CLuaBaseEntity(PTargetFound), PSkill->getID(), state.GetSpentTP(), CLuaAction(&action), damage); - PTarget->PAI->EventHandler.triggerListener("WEAPONSKILL_TAKE", CLuaBaseEntity(PTargetFound), CLuaBaseEntity(this), PSkill->getID(), state.GetSpentTP(), CLuaAction(&action)); - } - - if (msg == 0) - { - msg = PSkill->getMsg(); - } - else - { - msg = PSkill->getAoEMsg(); - } - - if (damage < 0) - { - msg = MSGBASIC_SKILL_RECOVERS_HP; // TODO: verify this message does/does not vary depending on mob/avatar/automaton use - target.param = std::clamp(-damage, 0, PTargetFound->GetMaxHP() - PTargetFound->health.hp); - } - else - { - target.param = damage; - } - - target.messageID = msg; - - if (PSkill->hasMissMsg()) - { - target.reaction = REACTION::MISS; - target.speceffect = SPECEFFECT::NONE; - if (msg == PSkill->getAoEMsg()) - { - msg = 282; - } - } - else - { - target.reaction = REACTION::HIT; - target.speceffect = SPECEFFECT::HIT; - } - - // TODO: Should this be reaction and not speceffect? - if (target.speceffect == SPECEFFECT::HIT) // Formerly bitwise and, though nothing in this function adds additional bits to the field - { - target.speceffect = SPECEFFECT::RECOIL; - target.knockback = PSkill->getKnockback(); - if (first && (PSkill->getPrimarySkillchain() != 0)) - { - SUBEFFECT effect = battleutils::GetSkillChainEffect(PTargetFound, PSkill->getPrimarySkillchain(), PSkill->getSecondarySkillchain(), - PSkill->getTertiarySkillchain()); - if (effect != SUBEFFECT_NONE) - { - int32 skillChainDamage = battleutils::TakeSkillchainDamage(this, PTargetFound, target.param, nullptr); - if (skillChainDamage < 0) - { - target.addEffectParam = -skillChainDamage; - target.addEffectMessage = 384 + effect; - } - else - { - target.addEffectParam = skillChainDamage; - target.addEffectMessage = 287 + effect; - } - target.additionalEffect = effect; - } - - first = false; - } - } - - if (PSkill->getValidTargets() & TARGET_ENEMY) - { - PTargetFound->StatusEffectContainer->DelStatusEffectsByFlag(EFFECTFLAG_DETECTABLE); - } - - if (PTargetFound->isDead()) - { - battleutils::ClaimMob(PTargetFound, this); - } - battleutils::DirtyExp(PTargetFound, this); - } - - PTarget = dynamic_cast(state.GetTarget()); // TODO: why is this recast here? can state change between now and the original cast? - - if (PTarget) - { - if (PTarget->objtype == TYPE_MOB && (PTarget->isDead() || (objtype == TYPE_PET && static_cast(this)->getPetType() == PET_TYPE::AVATAR))) - { - battleutils::ClaimMob(PTarget, this); - } - battleutils::DirtyExp(PTarget, this); - } } void CMobEntity::DistributeRewards() diff --git a/src/map/entities/mobentity.h b/src/map/entities/mobentity.h index 6bab4b17453..c01edc04e2f 100644 --- a/src/map/entities/mobentity.h +++ b/src/map/entities/mobentity.h @@ -158,7 +158,7 @@ class CMobEntity : public CBattleEntity virtual void Die() override; virtual void OnWeaponSkillFinished(CWeaponSkillState&, action_t&) override; - virtual void OnMobSkillFinished(CMobSkillState&, action_t&); + virtual void OnMobSkillFinished(CMobSkillState&, action_t&) override; virtual void OnEngage(CAttackState&) override; virtual bool OnAttack(CAttackState&, action_t&) override; diff --git a/src/map/monstrosity.cpp b/src/map/monstrosity.cpp index 0680a04f7b1..3a567ff21d7 100644 --- a/src/map/monstrosity.cpp +++ b/src/map/monstrosity.cpp @@ -25,6 +25,8 @@ #include "monstrosity.h" +#include "ai/ai_container.h" + #include "common/logging.h" #include "common/sql.h" @@ -339,9 +341,25 @@ void monstrosity::SendFullMonstrosityUpdate(CCharEntity* PChar) void monstrosity::HandleMonsterSkillActionPacket(CCharEntity* PChar, CBasicPacket& data) { - // TODO + if (PChar->GetMJob() != JOB_MON) + { + return; + } + + if (PChar->m_PMonstrosity == nullptr) + { + return; + } + + // uint16 other = data.ref(0x0A); // Always 25? + + uint16 targId = data.ref(0x08); + uint16 skillId = data.ref(0x0C); + + // TODO: Validate that this move is available at this level, for this species, and that + // we're capable of using it (state, TP, etc.). - // TODO: Lua binding + PChar->PAI->Internal_MobSkill(targId, skillId); } void monstrosity::HandleEquipChangePacket(CCharEntity* PChar, CBasicPacket& data) From d026eeb4924fc0547da12d0d2ee82935afdc2f71 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Mon, 16 Oct 2023 01:11:42 +0100 Subject: [PATCH 089/103] Remove unused database changes --- sql/char_unlocks.sql | 3 --- sql/monstrosity_skills.sql | 0 sql/monstrosity_traits.sql | 0 3 files changed, 3 deletions(-) delete mode 100644 sql/monstrosity_skills.sql delete mode 100644 sql/monstrosity_traits.sql diff --git a/sql/char_unlocks.sql b/sql/char_unlocks.sql index b4b0b53ce51..3c6e7c8aa98 100644 --- a/sql/char_unlocks.sql +++ b/sql/char_unlocks.sql @@ -20,8 +20,5 @@ CREATE TABLE `char_unlocks` ( `eschan_portals` blob DEFAULT NULL, `claimed_deeds` blob DEFAULT NULL, `unique_event` blob DEFAULT NULL, - `monstrosity_levels` blob DEFAULT NULL, - `monstrosity_instincts` blob DEFAULT NULL, - `monstrosity_variants` blob DEFAULT NULL, PRIMARY KEY (`charid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; diff --git a/sql/monstrosity_skills.sql b/sql/monstrosity_skills.sql deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/sql/monstrosity_traits.sql b/sql/monstrosity_traits.sql deleted file mode 100644 index e69de29bb2d..00000000000 From 7fd74463d4deb1663c0584b6883fea4c9b974d97 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Mon, 16 Oct 2023 01:26:39 +0100 Subject: [PATCH 090/103] Block out some settings for Monstrosity --- settings/default/main.lua | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/settings/default/main.lua b/settings/default/main.lua index da6767fec54..d710e53e952 100644 --- a/settings/default/main.lua +++ b/settings/default/main.lua @@ -64,13 +64,25 @@ xi.settings.main = CAP_CURRENCY_VALOR = 50000, -- Magian Trials - ENABLE_MAGIAN_TRIALS = 1, + ENABLE_MAGIAN_TRIALS = 1, -- VoidWalker ENABLE_VOIDWALKER = 1, -- Monstrosity - ENABLE_MONSTROSITY = 1, + ENABLE_MONSTROSITY = 1, + MONSTROSITY_INFAMY_RATIO = 0.1, -- (float) The ratio of exp gained to infamy gained on defeating a mob. + MONSTROSITY_INFAMY_MESSAGING = 0, -- Show a message when you gain infamy. + MONSTROSITY_TELEPORT_TO_FERETORY = 0, -- Return to Feretory instead of the zone where you entered Feretory when Relinquishing or after death. + MONSTROSITY_TRIGGER_NPCS = 0, -- Allow Monipulators to trigger NPCs outside of the Feretory. + MONSTROSITY_DONT_WIPE_BUFFS = 0, -- If set, buffs won't be wiped when changing species in the Feretory. + + -- Monstrosity PVP Mode + -- 0: Retail (fully restricted): Monipulators and Players must both be flagged for Beligerency before they can fight + -- 1: (partially restricted): Players do not need to be flagged to fight, but Monipulators do. + -- 2: (open): Belligerency is not needed for Players and Monipulators to fight. + MONSTROSITY_PVP_MODE = 0, + MONSTROSITY_PVP_ZONE_BYPASS = 0, -- Show the full zone teleport menu from Feretory while Belligerency is flagged. -- TREASURE CASKETS -- Retail droprate = 0.1 (10%) with no other effects active From 85ba35735419b63cd5a42dfd7cb4ec3e0332dc01 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Mon, 16 Oct 2023 09:59:53 +0100 Subject: [PATCH 091/103] Add (missing?) costume2 command --- scripts/commands/costume2.lua | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 scripts/commands/costume2.lua diff --git a/scripts/commands/costume2.lua b/scripts/commands/costume2.lua new file mode 100644 index 00000000000..788a7cd8928 --- /dev/null +++ b/scripts/commands/costume2.lua @@ -0,0 +1,29 @@ +----------------------------------- +-- func: costume2 +-- desc: Sets the players current costume2. +----------------------------------- +local commandObj = {} + +commandObj.cmdprops = +{ + permission = 1, + parameters = 'i' +} + +local function error(player, msg) + player:PrintToPlayer(msg) + player:PrintToPlayer('!costume2 ') +end + +commandObj.onTrigger = function(player, costumeId) + -- validate costumeId + if costumeId == nil or costumeId < 0 then + error(player, 'Invalid costumeID.') + return + end + + -- put on costume + player:setCostume2(costumeId) +end + +return commandObj From 0ae64a2c2f3ef42c98ddf1c1bf6bb20f717e1d7f Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Mon, 16 Oct 2023 10:24:37 +0100 Subject: [PATCH 092/103] Fill in some species modelIds and leave some TODOs --- sql/monstrosity_species.sql | 298 ++++++++++++++++++------------------ 1 file changed, 151 insertions(+), 147 deletions(-) diff --git a/sql/monstrosity_species.sql b/sql/monstrosity_species.sql index 224a008a5f0..10f26b6abdf 100644 --- a/sql/monstrosity_species.sql +++ b/sql/monstrosity_species.sql @@ -17,6 +17,10 @@ CREATE TABLE `monstrosity_species` ( -- NOTE: The mjob/sjob of MONs rise at the same level, so the only difference between which -- : order you specify them is is which 2H ability you get. +-- NOTE: Since there are so many variants of model that SEEM THE SAME BUT ACT DIFFERENTLY, +-- : guessing the model IDs isn't always good enough - they all need to eventually be +-- : properly captured. + INSERT INTO `monstrosity_species` VALUES (1, 1, "Rabbit", 1, 1, 0, 0x010C); INSERT INTO `monstrosity_species` VALUES (1, 256, "Onyx Rabbit", 1, 4, 0, 0x010D); -- TODO: Look guessed not capped INSERT INTO `monstrosity_species` VALUES (1, 257, "Alabaster Rabbit", 1, 3, 0, 0x010E); -- TODO: Look guessed not capped @@ -26,216 +30,216 @@ INSERT INTO `monstrosity_species` VALUES (2, 2, "Behemoth", 1, 6, 2, 0x0194); -- INSERT INTO `monstrosity_species` VALUES (2, 259, "Elasamoth (Behemoth)", 8, 6, 2, 0x0195); -- TODO: Look guessed not capped INSERT INTO `monstrosity_species` VALUES (3, 3, "Tiger", 1, 1, 1, 0x0134); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (3, 261, "Legendary Tiger", 1, 6, 1, 0x0134); -- TODO: Look guessed not capped, this is the wrong id -INSERT INTO `monstrosity_species` VALUES (3, 262, "Smilodon (Tiger)", 1, 1, 1, 0x0134); -- TODO: Look guessed not capped, this is the wrong id +INSERT INTO `monstrosity_species` VALUES (3, 261, "Legendary Tiger", 1, 6, 1, 0x0135); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (3, 262, "Smilodon (Tiger)", 1, 1, 1, 0x08C8); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (4, 4, "Sheep", 1, 1, 1, 0x0154); -- TODO: Look guessed not capped, this is the wrong id -INSERT INTO `monstrosity_species` VALUES (4, 263, "Karakul (Sheep)", 1, 1, 1, 0x0154); -- TODO: Look guessed not capped, this is the wrong id +INSERT INTO `monstrosity_species` VALUES (4, 4, "Sheep", 1, 1, 1, 0x0154); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (4, 263, "Karakul (Sheep)", 1, 1, 1, 0x0951); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (5, 5, "Ram (Sheep)", 1, 1, 2, 0x0000); +INSERT INTO `monstrosity_species` VALUES (5, 5, "Ram (Sheep)", 1, 1, 2, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (6, 6, "Dhalmel", 1, 1, 2, 0x0000); +INSERT INTO `monstrosity_species` VALUES (6, 6, "Dhalmel", 1, 1, 2, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (7, 7, "Coeurl", 1, 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (7, 266, "Lynx (Coeurl)", 1, 4, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (7, 267, "Collared Lynx (Coeurl)", 5, 4, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (7, 7, "Coeurl", 1, 1, 1, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (7, 266, "Lynx (Coeurl)", 1, 4, 1, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (7, 267, "Collared Lynx (Coeurl)", 5, 4, 1, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (8, 8, "Opo-opo", 1, 1, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (8, 8, "Opo-opo", 1, 1, 0, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (9, 9, "Manticore", 1, 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (9, 268, "Legendary Manticore", 5, 4, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (9, 9, "Manticore", 1, 1, 1, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (9, 268, "Legendary Manticore", 5, 4, 1, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (10, 10, "Buffalo", 7, 7, 2, 0x0000); +INSERT INTO `monstrosity_species` VALUES (10, 10, "Buffalo", 7, 7, 2, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (11, 11, "Marid", 1, 1, 2, 0x0000); +INSERT INTO `monstrosity_species` VALUES (11, 11, "Marid", 1, 1, 2, 0x06CA); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (12, 12, "Cerberus", 1, 1, 2, 0x0000); -INSERT INTO `monstrosity_species` VALUES (12, 269, "Orthrus (Cerberus)", 1, 4, 2, 0x0000); +INSERT INTO `monstrosity_species` VALUES (12, 12, "Cerberus", 1, 1, 2, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (12, 269, "Orthrus (Cerberus)", 1, 4, 2, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (13, 13, "Gnole", 2, 2, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (13, 270, "Bipedal Gnole", 2, 2, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (13, 13, "Gnole", 2, 2, 1, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (13, 270, "Bipedal Gnole", 2, 2, 1, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (15, 15, "Funguar", 1, 1, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (15, 271, "Coppercap (Funguar)", 1, 1, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (15, 15, "Funguar", 1, 1, 0, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (15, 271, "Coppercap (Funguar)", 1, 1, 0, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (16, 16, "Treant Sapling", 1, 1, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (16, 272, "Treant", 1, 4, 2, 0x0000); -INSERT INTO `monstrosity_species` VALUES (16, 273, "Flowering Treant", 1, 4, 2, 0x0000); -INSERT INTO `monstrosity_species` VALUES (16, 274, "Scarlet-tinged Treant", 1, 4, 2, 0x0000); -INSERT INTO `monstrosity_species` VALUES (16, 275, "Barren Treant", 1, 4, 2, 0x0000); -INSERT INTO `monstrosity_species` VALUES (16, 276, "Necklaced Treant", 1, 4, 2, 0x0000); +INSERT INTO `monstrosity_species` VALUES (16, 16, "Treant Sapling", 1, 1, 0, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (16, 272, "Treant", 1, 4, 2, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (16, 273, "Flowering Treant", 1, 4, 2, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (16, 274, "Scarlet-tinged Treant", 1, 4, 2, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (16, 275, "Barren Treant", 1, 4, 2, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (16, 276, "Necklaced Treant", 1, 4, 2, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (17, 17, "Morbol", 1, 1, 2, 0x0000); -INSERT INTO `monstrosity_species` VALUES (17, 277, "Pygmy Morbol", 1, 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (17, 278, "Scarce Morbol", 1, 1, 2, 0x0000); -INSERT INTO `monstrosity_species` VALUES (17, 279, "Ameretat (Morbol)", 1, 1, 2, 0x0000); -INSERT INTO `monstrosity_species` VALUES (17, 280, "Purbol (Morbol)", 1, 4, 2, 0x0000); +INSERT INTO `monstrosity_species` VALUES (17, 17, "Morbol", 1, 1, 2, 0x017C); +INSERT INTO `monstrosity_species` VALUES (17, 277, "Pygmy Morbol", 1, 1, 1, 0x0950); +INSERT INTO `monstrosity_species` VALUES (17, 278, "Scarce Morbol", 1, 1, 2, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (17, 279, "Ameretat (Morbol)", 1, 1, 2, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (17, 280, "Purbol (Morbol)", 1, 4, 2, 0x0000); -- TODO INSERT INTO `monstrosity_species` VALUES (18, 18, "Mandragora", 2, 2, 0, 0x012C); -INSERT INTO `monstrosity_species` VALUES (18, 281, "Korrigan (Mandragora)", 2, 4, 0, 0x012C); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (18, 282, "Lycopodium (Mandragora)", 2, 3, 0, 0x012C); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (18, 283, "Pygmy Mandragora", 2, 2, 0, 0x012C); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (18, 284, "Adenium (Mandragora)", 2, 5, 0, 0x012C); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (18, 285, "Pachypodium (Mandragora)", 2, 2, 0, 0x012C); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (18, 286, "Enlightened Mandragora", 2, 2, 0, 0x012C); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (18, 281, "Korrigan (Mandragora)", 2, 4, 0, 0x012D); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (18, 282, "Lycopodium (Mandragora)", 2, 3, 0, 0x08C7); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (18, 283, "Pygmy Mandragora", 2, 2, 0, 0x012C); -- TODO: Look guessed not capped, this is the wrong id +INSERT INTO `monstrosity_species` VALUES (18, 284, "Adenium (Mandragora)", 2, 5, 0, 0x012C); -- TODO: Look guessed not capped, this is the wrong id +INSERT INTO `monstrosity_species` VALUES (18, 285, "Pachypodium (Mandragora)", 2, 2, 0, 0x012C); -- TODO: Look guessed not capped, this is the wrong id +INSERT INTO `monstrosity_species` VALUES (18, 286, "Enlightened Mandragora", 2, 2, 0, 0x012C); -- TODO: Look guessed not capped, this is the wrong id INSERT INTO `monstrosity_species` VALUES (18, 287, "New Year Mandragora", 2, 13, 0, 0x0B48); -INSERT INTO `monstrosity_species` VALUES (19, 19, "Sabotender", 1, 1, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (19, 288, "Sabotender Florido", 1, 5, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (19, 19, "Sabotender", 1, 1, 0, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (19, 288, "Sabotender Florido", 1, 5, 0, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (20, 20, "Flytrap", 1, 1, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (20, 20, "Flytrap", 1, 1, 0, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (21, 21, "Goobbue", 1, 1, 2, 0x0000); +INSERT INTO `monstrosity_species` VALUES (21, 21, "Goobbue", 1, 1, 2, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (22, 22, "Rafflesia", 8, 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (22, 289, "Mitrastema (Rafflesia)", 8, 4, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (22, 22, "Rafflesia", 8, 1, 1, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (22, 289, "Mitrastema (Rafflesia)", 8, 4, 1, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (23, 23, "Panopt", 4, 4, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (23, 23, "Panopt", 4, 4, 0, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (27, 27, "Bee", 1, 1, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (27, 290, "Vermillion and Onyx Bee", 1, 5, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (27, 291, "Zaffre Bee", 1, 4, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (27, 27, "Bee", 1, 1, 0, 0x0110); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (27, 290, "Vermillion and Onyx Bee", 1, 5, 0, 0x0111); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (27, 291, "Zaffre Bee", 1, 4, 0, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (28, 28, "Beetle", 7, 7, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (28, 292, "Onyx Beetle", 7, 4, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (28, 293, "Gamboge Beetle", 7, 5, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (28, 28, "Beetle", 7, 7, 0, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (28, 292, "Onyx Beetle", 7, 4, 0, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (28, 293, "Gamboge Beetle", 7, 5, 0, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (29, 29, "Crawler", 1, 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (29, 294, "Eruca (Crawler)", 1, 5, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (29, 295, "Emerald Crawler", 3, 4, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (29, 296, "Pygmy Emerald Crawler", 3, 4, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (29, 29, "Crawler", 1, 1, 1, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (29, 294, "Eruca (Crawler)", 1, 5, 1, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (29, 295, "Emerald Crawler", 3, 4, 1, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (29, 296, "Pygmy Emerald Crawler", 3, 4, 0, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (30, 30, "Fly", 1, 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (30, 297, "Vermillion Fly", 1, 5, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (30, 30, "Fly", 1, 1, 1, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (30, 297, "Vermillion Fly", 1, 5, 1, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (31, 31, "Scorpion", 1, 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (31, 298, "Scolopendrid (Scorpion)", 1, 4, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (31, 299, "Unusual Scolopendrid (Scorpion)", 8, 4, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (31, 31, "Scorpion", 1, 1, 1, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (31, 298, "Scolopendrid (Scorpion)", 1, 4, 1, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (31, 299, "Unusual Scolopendrid (Scorpion)", 8, 4, 1, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (32, 32, "Spider", 1, 1, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (32, 300, "Reticulated Spider", 1, 8, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (32, 301, "Vermillion and Onyx Spider", 1, 5, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (32, 32, "Spider", 1, 1, 0, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (32, 300, "Reticulated Spider", 1, 8, 0, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (32, 301, "Vermillion and Onyx Spider", 1, 5, 0, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (33, 33, "Antlion", 1, 1, 2, 0x0000); -INSERT INTO `monstrosity_species` VALUES (33, 302, "Onyx Antlion", 1, 4, 2, 0x0000); -INSERT INTO `monstrosity_species` VALUES (33, 303, "Formiceros (Antlion)", 8, 4, 2, 0x0000); +INSERT INTO `monstrosity_species` VALUES (33, 33, "Antlion", 1, 1, 2, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (33, 302, "Onyx Antlion", 1, 4, 2, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (33, 303, "Formiceros (Antlion)", 8, 4, 2, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (34, 34, "Diremite", 8, 8, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (34, 304, "Arundemite (Diremite)", 8, 8, 2, 0x0000); +INSERT INTO `monstrosity_species` VALUES (34, 34, "Diremite", 8, 8, 1, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (34, 304, "Arundemite (Diremite)", 8, 8, 2, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (35, 35, "Chigoe", 6, 6, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (35, 305, "Azure Chigoe", 6, 4, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (35, 35, "Chigoe", 6, 6, 0, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (35, 305, "Azure Chigoe", 6, 4, 0, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (36, 36, "Wamouracampa (Wamoura larva)", 7, 7, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (36, 306, "Coiled Wamouracampa (Wamoura larva)", 7, 7, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (36, 307, "Wamoura", 1, 7, 2, 0x0000); -INSERT INTO `monstrosity_species` VALUES (36, 308, "Coral Wamoura", 4, 7, 2, 0x0000); +INSERT INTO `monstrosity_species` VALUES (36, 36, "Wamouracampa (Wamoura larva)", 7, 7, 1, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (36, 306, "Coiled Wamouracampa (Wamoura larva)", 7, 7, 1, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (36, 307, "Wamoura", 1, 7, 2, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (36, 308, "Coral Wamoura", 4, 7, 2, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (37, 37, "Ladybug", 6, 6, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (37, 309, "Gold Ladybug", 6, 5, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (37, 37, "Ladybug", 6, 6, 0, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (37, 309, "Gold Ladybug", 6, 5, 0, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (38, 38, "Gnat", 1, 1, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (38, 310, "Midge (Gnat)", 1, 1, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (38, 38, "Gnat", 1, 1, 0, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (38, 310, "Midge (Gnat)", 1, 1, 0, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (43, 43, "Lizard", 1, 1, 0, 0x0148); -INSERT INTO `monstrosity_species` VALUES (43, 315, "Ashen Lizard", 1, 4, 0, 0x0148); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (43, 43, "Lizard", 1, 1, 0, 0x0148); -- TODO +INSERT INTO `monstrosity_species` VALUES (43, 315, "Ashen Lizard", 1, 4, 0, 0x0149); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (44, 44, "Raptor", 1, 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (44, 316, "Emerald Raptor", 1, 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (44, 317, "Vermillion Raptor", 1, 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (44, 44, "Raptor", 1, 1, 1, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (44, 316, "Emerald Raptor", 1, 1, 1, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (44, 317, "Vermillion Raptor", 1, 1, 1, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (45, 45, "Adamantoise", 1, 1, 2, 0x0000); -INSERT INTO `monstrosity_species` VALUES (45, 318, "Pygmy Adamantoise", 1, 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (45, 319, "Legendary Adamantoise", 1, 1, 2, 0x0000); -INSERT INTO `monstrosity_species` VALUES (45, 320, "Ferromantoise (Adamantoise)", 1, 1, 2, 0x0000); +INSERT INTO `monstrosity_species` VALUES (45, 45, "Adamantoise", 1, 1, 2, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (45, 318, "Pygmy Adamantoise", 1, 1, 1, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (45, 319, "Legendary Adamantoise", 1, 1, 2, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (45, 320, "Ferromantoise (Adamantoise)", 1, 1, 2, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (46, 46, "Bugard", 1, 1, 2, 0x0000); -INSERT INTO `monstrosity_species` VALUES (46, 321, "Abyssobugard (Bugard)", 1, 1, 2, 0x0000); +INSERT INTO `monstrosity_species` VALUES (46, 46, "Bugard", 1, 1, 2, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (46, 321, "Abyssobugard (Bugard)", 1, 1, 2, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (47, 47, "Eft", 1, 1, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (47, 322, "Tarichuk (Eft)", 1, 1, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (47, 47, "Eft", 1, 1, 0, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (47, 322, "Tarichuk (Eft)", 1, 1, 0, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (48, 48, "Wivre", 1, 1, 2, 0x0000); -INSERT INTO `monstrosity_species` VALUES (48, 323, "Unusual Wivre", 1, 1, 2, 0x0000); +INSERT INTO `monstrosity_species` VALUES (48, 48, "Wivre", 1, 1, 2, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (48, 323, "Unusual Wivre", 1, 1, 2, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (49, 49, "Peiste", 1, 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (49, 324, "Sibilus (Peiste)", 1, 1, 2, 0x0000); +INSERT INTO `monstrosity_species` VALUES (49, 49, "Peiste", 1, 1, 1, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (49, 324, "Sibilus (Peiste)", 1, 1, 2, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (52, 52, "Slime", 1, 1, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (52, 329, "Clot (Slime)", 1, 1, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (52, 330, "Gold Slime", 1, 1, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (52, 331, "Boil (Slime)", 1, 1, 2, 0x0000); +INSERT INTO `monstrosity_species` VALUES (52, 52, "Slime", 1, 1, 0, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (52, 329, "Clot (Slime)", 1, 1, 0, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (52, 330, "Gold Slime", 1, 1, 0, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (52, 331, "Boil (Slime)", 1, 1, 2, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (53, 53, "Hecteyes", 1, 1, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (53, 53, "Hecteyes", 1, 1, 0, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (54, 54, "Flan", 1, 1, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (54, 332, "Gold Flan", 1, 1, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (54, 333, "Blancmange (Flan)", 1, 1, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (54, 54, "Flan", 1, 1, 0, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (54, 332, "Gold Flan", 1, 1, 0, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (54, 333, "Blancmange (Flan)", 1, 1, 0, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (56, 56, "Slug", 1, 1, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (56, 56, "Slug", 1, 1, 0, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (57, 57, "Sandworm", 1, 1, 2, 0x0000); -INSERT INTO `monstrosity_species` VALUES (57, 334, "Pygmy Sandworm", 1, 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (57, 335, "Gigaworm (Sandworm)", 1, 1, 2, 0x0000); +INSERT INTO `monstrosity_species` VALUES (57, 57, "Sandworm", 1, 1, 2, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (57, 334, "Pygmy Sandworm", 1, 1, 1, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (57, 335, "Gigaworm (Sandworm)", 1, 1, 2, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (58, 58, "Leech", 1, 1, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (58, 336, "Azure Leech", 1, 1, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (58, 337, "Obdella (Leech)", 1, 1, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (58, 58, "Leech", 1, 1, 0, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (58, 336, "Azure Leech", 1, 1, 0, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (58, 337, "Obdella (Leech)", 1, 1, 0, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (60, 60, "Crab", 1, 1, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (60, 340, "Vermillion Crab", 1, 1, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (60, 341, "Basket-burdened Crab", 1, 1, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (60, 342, "Vermillion Basket-burdened Crab", 1, 1, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (60, 343, "Porter Crab (Crab)", 1, 1, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (60, 60, "Crab", 1, 1, 0, 0x0164); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (60, 340, "Vermillion Crab", 1, 1, 0, 0x0165); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (60, 341, "Basket-burdened Crab", 1, 1, 0, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (60, 342, "Vermillion Basket-burdened Crab", 1, 1, 0, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (60, 343, "Porter Crab (Crab)", 1, 1, 0, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (61, 61, "Pugil", 1, 1, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (61, 344, "Jagil (Pugil)", 1, 1, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (61, 61, "Pugil", 1, 1, 0, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (61, 344, "Jagil (Pugil)", 1, 1, 0, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (62, 62, "Sea Monk", 1, 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (62, 345, "Azure Sea Monk", 1, 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (62, 62, "Sea Monk", 1, 1, 1, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (62, 345, "Azure Sea Monk", 1, 1, 1, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (63, 63, "Uragnite", 1, 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (63, 346, "Limascabra (Uragnite)", 1, 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (63, 63, "Uragnite", 1, 1, 1, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (63, 346, "Limascabra (Uragnite)", 1, 1, 1, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (64, 64, "Orobon", 1, 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (64, 347, "Pygmy Orobon", 1, 1, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (64, 348, "Ogrebon (Orobon)", 1, 1, 2, 0x0000); +INSERT INTO `monstrosity_species` VALUES (64, 64, "Orobon", 1, 1, 1, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (64, 347, "Pygmy Orobon", 1, 1, 0, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (64, 348, "Ogrebon (Orobon)", 1, 1, 2, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (65, 65, "Ruszor", 1, 1, 2, 0x0000); +INSERT INTO `monstrosity_species` VALUES (65, 65, "Ruszor", 1, 1, 2, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (66, 66, "Toad", 1, 1, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (66, 349, "Azure Toad", 1, 1, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (66, 350, "Vermillion Toad", 1, 1, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (66, 66, "Toad", 1, 1, 0, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (66, 349, "Azure Toad", 1, 1, 0, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (66, 350, "Vermillion Toad", 1, 1, 0, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (69, 69, "Bird", 1, 1, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (69, 351, "Onyx Bird", 1, 1, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (69, 69, "Bird", 1, 1, 0, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (69, 351, "Onyx Bird", 1, 1, 0, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (70, 70, "Cockatrice", 1, 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (70, 352, "Ziz (Cockatrice)", 1, 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (70, 70, "Cockatrice", 1, 1, 1, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (70, 352, "Ziz (Cockatrice)", 1, 1, 1, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (71, 71, "Roc", 1, 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (71, 353, "Legendary Roc", 1, 1, 1, 0x0000); -INSERT INTO `monstrosity_species` VALUES (71, 354, "Gagana (Roc)", 1, 1, 2, 0x0000); +INSERT INTO `monstrosity_species` VALUES (71, 71, "Roc", 1, 1, 1, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (71, 353, "Legendary Roc", 1, 1, 1, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (71, 354, "Gagana (Roc)", 1, 1, 2, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (72, 72, "Bat", 1, 1, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (72, 355, "Bats", 1, 1, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (72, 356, "Vermillion Bat", 1, 1, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (72, 357, "Vermillion Bats", 1, 1, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (72, 72, "Bat", 1, 1, 0, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (72, 355, "Bats", 1, 1, 0, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (72, 356, "Vermillion Bat", 1, 1, 0, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (72, 357, "Vermillion Bats", 1, 1, 0, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (73, 73, "Hippogryph", 1, 1, 1, 0x0000); +INSERT INTO `monstrosity_species` VALUES (73, 73, "Hippogryph", 1, 1, 1, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (74, 74, "Apkallu", 2, 2, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (74, 358, "Inguza (Apkallu)", 13, 2, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (74, 74, "Apkallu", 2, 2, 0, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (74, 358, "Inguza (Apkallu)", 13, 2, 0, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (75, 75, "Colibri", 5, 5, 0, 0x0000); -INSERT INTO `monstrosity_species` VALUES (75, 359, "Toucalibri (Colibri)", 10, 5, 0, 0x0000); +INSERT INTO `monstrosity_species` VALUES (75, 75, "Colibri", 5, 5, 0, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (75, 359, "Toucalibri (Colibri)", 10, 5, 0, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (76, 76, "Amphiptere", 1, 4, 2, 0x0000); -INSERT INTO `monstrosity_species` VALUES (76, 360, "Sanguiptere (Amphiptere)", 4, 1, 2, 0x0000); +INSERT INTO `monstrosity_species` VALUES (76, 76, "Amphiptere", 1, 4, 2, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (76, 360, "Sanguiptere (Amphiptere)", 4, 1, 2, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (126, 254, "Astoltian Slime", 1, 1, 0, 0x0B41); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (126, 254, "Astoltian Slime", 1, 1, 0, 0x0B41); INSERT INTO `monstrosity_species` VALUES (126, 508, "Astoltian She-Slime", 1, 1, 0, 0x0B5B); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (126, 509, "Astoltian Metal Slime", 4, 1, 0, 0x0B5C); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (126, 509, "Astoltian Metal Slime", 4, 1, 0, 0x0B5C); INSERT INTO `monstrosity_species` VALUES (127, 255, "Eorzean Spriggan", 1, 4, 0, 0x0B42); -- TODO: Look guessed not capped INSERT INTO `monstrosity_species` VALUES (127, 510, "Eorzean Spriggan.C", 1, 4, 0, 0x0B5D); -- TODO: Look guessed not capped From f6a6497e5aff90d0f0214a6042fe3b06403a3540 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Mon, 16 Oct 2023 10:39:08 +0100 Subject: [PATCH 093/103] Add Gestation move speed mod --- scripts/effects/gestation.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/effects/gestation.lua b/scripts/effects/gestation.lua index ebc7ca6aab4..17dfacb2808 100644 --- a/scripts/effects/gestation.lua +++ b/scripts/effects/gestation.lua @@ -18,13 +18,17 @@ ----------------------------------- local effectObject = {} +local boostAmount = 50 -- +50% movement speed + effectObject.onEffectGain = function(target, effect) + target:addMod(xi.mod.MOVE, boostAmount) end effectObject.onEffectTick = function(target, effect) end effectObject.onEffectLose = function(target, effect) + target:delMod(xi.mod.MOVE, boostAmount) end return effectObject From 54599098fe1af7c223fc669a2d4fb447b63d501c Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Mon, 16 Oct 2023 12:05:04 +0100 Subject: [PATCH 094/103] Remember and return to entrance location from Feretory --- scripts/commands/monstrosity.lua | 6 +++ scripts/globals/monstrosity.lua | 32 ++++++++++-- scripts/quests/otherAreas/Monstrosity.lua | 2 + sql/char_monstrosity.sql | 7 +++ src/map/lua/lua_baseentity.cpp | 33 ++++++++++++ src/map/lua/lua_baseentity.h | 1 + src/map/lua/luautils.cpp | 62 +++++++++++++++++++++++ src/map/lua/luautils.h | 1 + src/map/monstrosity.cpp | 61 +++++++++++++++++++--- src/map/monstrosity.h | 5 ++ 10 files changed, 199 insertions(+), 11 deletions(-) diff --git a/scripts/commands/monstrosity.lua b/scripts/commands/monstrosity.lua index 5e38c246043..e1a5a49ef55 100644 --- a/scripts/commands/monstrosity.lua +++ b/scripts/commands/monstrosity.lua @@ -12,7 +12,13 @@ commandObj.cmdprops = commandObj.onTrigger = function(player) if player:getMainJob() ~= xi.job.MON then + local pos = player:getPos() + player:setMonstrosityEntryData(pos.x, pos.y, pos.z, pos.rot, player:getZoneID(), player:getMainJob(), player:getSubJob()) player:changeJob(xi.job.MON) + else + local data = player:getMonstrosityData() + player:changeJob(data.entry_mjob) + player:changesJob(data.entry_sjob) end player:setPos(player:getXPos(), player:getYPos(), player:getZPos(), player:getRotPos(), player:getZoneID()) diff --git a/scripts/globals/monstrosity.lua b/scripts/globals/monstrosity.lua index 94073041b14..57fba69707a 100644 --- a/scripts/globals/monstrosity.lua +++ b/scripts/globals/monstrosity.lua @@ -1066,6 +1066,33 @@ xi.monstrosity.onMonstrosityUpdate = function(player, data) -- TODO: Handle level-based variants here end +xi.monstrosity.onMonstrosityReturnToEntrance = function(player) + local data = player:getMonstrosityData() + local x = data.entry_x; + local y = data.entry_y; + local z = data.entry_z; + local rot = data.entry_rot; + local zoneId = data.entry_zone_id; + local mjob = data.entry_mjob; + local sjob = data.entry_sjob; + + -- TODO: Sanity check + + -- TODO: Check settings to see if we should go to Feretory or not + if xi.settings.main.MONSTROSITY_TELEPORT_TO_FERETORY == 1 then + if player:getZoneID() ~= xi.zone.FERETORY then + player:setPos(-358, -3.4, -440, 64, xi.zone.FERETORY) + return + end + + -- Otherwise fallthrough and exit as normal + end + + player:changeJob(mjob) + player:changesJob(sjob) + player:setPos(x, y, z, rot, zoneId) +end + ----------------------------------- -- Relinquish ----------------------------------- @@ -1089,7 +1116,7 @@ xi.monstrosity.relinquishSteps = end, [4] = function(player) - player:setPos(0, 0, 0, 0, xi.zone.FERETORY) + xi.monstrosity.onMonstrosityReturnToEntrance(player) end, } @@ -1211,8 +1238,7 @@ xi.monstrosity.odysseanPassageOnEventFinish = function(player, csid, option, npc if eventOption == 1 then if zoneSelected == 0 then - -- TODO: Return to Zone the Player entered Feretory from - utils.unused() + xi.monstrosity.onMonstrosityReturnToEntrance(player) else if xi.monstrosity.teleports[zoneSelected] then local teleportPos = xi.monstrosity.teleports[zoneSelected][math.random(1, #xi.monstrosity.teleports[zoneSelected])] diff --git a/scripts/quests/otherAreas/Monstrosity.lua b/scripts/quests/otherAreas/Monstrosity.lua index b30d599ce72..1d6b060bfc2 100644 --- a/scripts/quests/otherAreas/Monstrosity.lua +++ b/scripts/quests/otherAreas/Monstrosity.lua @@ -70,6 +70,8 @@ local odysseanPassageNpc = local odysseanPassageOnEventFinish = function(player, csid, option, npc) if option == 1 then + local pos = player:getPos() + player:setMonstrosityEntryData(pos.x, pos.y, pos.z, pos.rot, player:getZoneID(), player:getMainJob(), player:getSubJob()) player:setPos(-358, -3.4, -440, 64, xi.zone.FERETORY) end end diff --git a/sql/char_monstrosity.sql b/sql/char_monstrosity.sql index aca0ad38de9..ecadc8f99d4 100644 --- a/sql/char_monstrosity.sql +++ b/sql/char_monstrosity.sql @@ -11,5 +11,12 @@ CREATE TABLE `char_monstrosity` ( `instincts` blob DEFAULT NULL, `variants` blob DEFAULT NULL, `belligerency` tinyint(3) unsigned NOT NULL DEFAULT 0, + `entry_x` float(7,3) NOT NULL DEFAULT '0.000', + `entry_y` float(7,3) NOT NULL DEFAULT '0.000', + `entry_z` float(7,3) NOT NULL DEFAULT '0.000', + `entry_rot` tinyint(3) unsigned NOT NULL DEFAULT '0', + `entry_zone_id` smallint(3) unsigned NOT NULL DEFAULT '0', + `entry_mjob` tinyint(2) unsigned NOT NULL DEFAULT '0', + `entry_sjob` tinyint(2) unsigned NOT NULL DEFAULT '0', PRIMARY KEY (`charid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; diff --git a/src/map/lua/lua_baseentity.cpp b/src/map/lua/lua_baseentity.cpp index fcdf2280a2a..1f1fe1a4c67 100644 --- a/src/map/lua/lua_baseentity.cpp +++ b/src/map/lua/lua_baseentity.cpp @@ -6446,6 +6446,38 @@ auto CLuaBaseEntity::getMonstrositySize() -> uint8 return PChar->m_PMonstrosity->Size; } +void CLuaBaseEntity::setMonstrosityEntryData(float x, float y, float z, uint8 rot, uint16 zoneId, uint8 mjob, uint8 sjob) +{ + auto* PChar = dynamic_cast(m_PBaseEntity); + if (PChar == nullptr) + { + return; + } + + bool startsWithMonstrosityData = PChar->m_PMonstrosity != nullptr; + if (!startsWithMonstrosityData) + { + monstrosity::ReadMonstrosityData(PChar); + } + + PChar->m_PMonstrosity->EntryPos.x = x; + PChar->m_PMonstrosity->EntryPos.y = y; + PChar->m_PMonstrosity->EntryPos.z = z; + PChar->m_PMonstrosity->EntryPos.rotation = rot; + PChar->m_PMonstrosity->EntryZoneId = zoneId; + PChar->m_PMonstrosity->EntryMainJob = mjob; + PChar->m_PMonstrosity->EntrySubJob = sjob; + + monstrosity::WriteMonstrosityData(PChar); + + // If we didn't start with Monstrosity data, we should wipe it out now so we + // don't change modes + if (!startsWithMonstrosityData) + { + PChar->m_PMonstrosity = nullptr; + } +} + /************************************************************************ * Function: getTitle() * Purpose : Returns the integer value of the player's current title @@ -17257,6 +17289,7 @@ void CLuaBaseEntity::Register() SOL_REGISTER("getBelligerencyFlag", CLuaBaseEntity::getBelligerencyFlag); SOL_REGISTER("setBelligerencyFlag", CLuaBaseEntity::setBelligerencyFlag); SOL_REGISTER("getMonstrositySize", CLuaBaseEntity::getMonstrositySize); + SOL_REGISTER("setMonstrosityEntryData", CLuaBaseEntity::setMonstrosityEntryData); // Player Titles and Fame SOL_REGISTER("getTitle", CLuaBaseEntity::getTitle); diff --git a/src/map/lua/lua_baseentity.h b/src/map/lua/lua_baseentity.h index a0bb0f47a16..fd39de465e5 100644 --- a/src/map/lua/lua_baseentity.h +++ b/src/map/lua/lua_baseentity.h @@ -337,6 +337,7 @@ class CLuaBaseEntity bool getBelligerencyFlag(); void setBelligerencyFlag(bool flag); auto getMonstrositySize() -> uint8; + void setMonstrosityEntryData(float x, float y, float z, uint8 rot, uint16 zoneId, uint8 mjob, uint8 sjob); // Player Titles and Fame uint16 getTitle(); diff --git a/src/map/lua/luautils.cpp b/src/map/lua/luautils.cpp index 64cc2c2da02..de5f74336bc 100644 --- a/src/map/lua/luautils.cpp +++ b/src/map/lua/luautils.cpp @@ -3899,6 +3899,13 @@ namespace luautils table["monstrosityId"] = PChar->m_PMonstrosity->MonstrosityId; table["species"] = PChar->m_PMonstrosity->Species; table["flags"] = PChar->m_PMonstrosity->Flags; + table["entry_x"] = PChar->m_PMonstrosity->EntryPos.x; + table["entry_y"] = PChar->m_PMonstrosity->EntryPos.y; + table["entry_z"] = PChar->m_PMonstrosity->EntryPos.z; + table["entry_rot"] = PChar->m_PMonstrosity->EntryPos.rotation; + table["entry_zone_id"] = PChar->m_PMonstrosity->EntryZoneId; + table["entry_mjob"] = PChar->m_PMonstrosity->EntryMainJob; + table["entry_sjob"] = PChar->m_PMonstrosity->EntrySubJob; { std::size_t idx = 0; @@ -3949,6 +3956,41 @@ namespace luautils PChar->m_PMonstrosity->Flags = table.get("flags"); } + if (table["entry_x"].valid()) + { + PChar->m_PMonstrosity->EntryPos.x = table.get("entry_x"); + } + + if (table["entry_y"].valid()) + { + PChar->m_PMonstrosity->EntryPos.y = table.get("entry_y"); + } + + if (table["entry_z"].valid()) + { + PChar->m_PMonstrosity->EntryPos.z = table.get("entry_z"); + } + + if (table["entry_rot"].valid()) + { + PChar->m_PMonstrosity->EntryPos.rotation = table.get("entry_rot"); + } + + if (table["entry_zone_id"].valid()) + { + PChar->m_PMonstrosity->EntryZoneId = table.get("entry_zone_id"); + } + + if (table["entry_mjob"].valid()) + { + PChar->m_PMonstrosity->EntryMainJob = table.get("entry_mjob"); + } + + if (table["entry_sjob"].valid()) + { + PChar->m_PMonstrosity->EntrySubJob = table.get("entry_sjob"); + } + if (table["levels"].valid()) { for (auto const& [keyObj, valObj] : table.get("levels")) @@ -4000,6 +4042,26 @@ namespace luautils } } + void OnMonstrosityReturnToEntrance(CCharEntity* PChar) + { + TracyZoneScoped; + + sol::function onMonstrosityReturnToEntrance = lua["xi"]["monstrosity"]["onMonstrosityReturnToEntrance"]; + if (!onMonstrosityReturnToEntrance.valid()) + { + ShowError("luautils::retuonMonstrosityReturnToEntrancernToEntrance"); + return; + } + + auto result = onMonstrosityReturnToEntrance(CLuaBaseEntity(PChar)); + if (!result.valid()) + { + sol::error err = result; + ShowError("luautils::onMonstrosityReturnToEntrance: %s", err.what()); + return; + } + } + int32 OnMagicCastingCheck(CBaseEntity* PChar, CBaseEntity* PTarget, CSpell* PSpell) { TracyZoneScoped; diff --git a/src/map/lua/luautils.h b/src/map/lua/luautils.h index 1d782ac50db..b9dcdc9a90b 100644 --- a/src/map/lua/luautils.h +++ b/src/map/lua/luautils.h @@ -288,6 +288,7 @@ namespace luautils auto GetMonstrosityLuaTable(CCharEntity* PChar) -> sol::table; void SetMonstrosityLuaTable(CCharEntity* PChar, sol::table data); void OnMonstrosityUpdate(CCharEntity* PChar); + void OnMonstrosityReturnToEntrance(CCharEntity* PChar); int32 OnAbilityCheck(CBaseEntity* PChar, CBaseEntity* PTarget, CAbility* PAbility, CBaseEntity** PMsgTarget); int32 OnPetAbility(CBaseEntity* PTarget, CBaseEntity* PMob, CMobSkill* PMobSkill, CBaseEntity* PPetMaster, action_t* action); diff --git a/src/map/monstrosity.cpp b/src/map/monstrosity.cpp index 3a567ff21d7..9bb314dd624 100644 --- a/src/map/monstrosity.cpp +++ b/src/map/monstrosity.cpp @@ -87,6 +87,9 @@ monstrosity::MonstrosityData_t::MonstrosityData_t() , SubJob(JOB_WAR) // , CurrentExp(0) // No exp , Belligerency(false) // +, EntryZoneId(0) // +, EntryMainJob(0) // +, EntrySubJob(0) // { levels[1] = 1; // Rabbit levels[18] = 1; // Mandragora @@ -153,7 +156,30 @@ void monstrosity::ReadMonstrosityData(CCharEntity* PChar) { auto data = std::make_unique(); - auto ret = sql->Query("SELECT charid, current_monstrosity_id, current_monstrosity_species, current_monstrosity_name_prefix_1, current_monstrosity_name_prefix_2, current_exp, equip, levels, instincts, variants, belligerency FROM char_monstrosity WHERE charid = %d LIMIT 1;", PChar->id); + // clang-format off + auto ret = sql->Query("SELECT " + "charid, " + "current_monstrosity_id, " + "current_monstrosity_species, " + "current_monstrosity_name_prefix_1, " + "current_monstrosity_name_prefix_2, " + "current_exp, " + "equip, " + "levels, " + "instincts, " + "variants, " + "belligerency, " + "entry_x, " + "entry_y, " + "entry_z, " + "entry_rot, " + "entry_zone_id, " + "entry_mjob, " + "entry_sjob " + "FROM char_monstrosity WHERE charid = %d LIMIT 1;", + PChar->id); + // clang-format on + if (ret != SQL_ERROR && sql->NumRows() != 0) { while (sql->NextRow() == SQL_SUCCESS) @@ -174,6 +200,14 @@ void monstrosity::ReadMonstrosityData(CCharEntity* PChar) data->Belligerency = static_cast(sql->GetUIntData(10)); + data->EntryPos.x = sql->GetFloatData(11); + data->EntryPos.y = sql->GetFloatData(12); + data->EntryPos.z = sql->GetFloatData(13); + data->EntryPos.rotation = static_cast(sql->GetUIntData(14)); + data->EntryZoneId = static_cast(sql->GetUIntData(15)); + data->EntryMainJob = static_cast(sql->GetUIntData(16)); + data->EntrySubJob = static_cast(sql->GetUIntData(17)); + // Build additional data from lookups data->MainJob = gMonstrositySpeciesMap[data->Species].mjob; data->SubJob = gMonstrositySpeciesMap[data->Species].sjob; @@ -206,7 +240,14 @@ void monstrosity::WriteMonstrosityData(CCharEntity* PChar) "levels = '%s', " "instincts = '%s', " "variants = '%s', " - "belligerency = '%d';"; + "belligerency = '%d', " + "entry_x = '%.3f', " + "entry_y = '%.3f', " + "entry_z = '%.3f', " + "entry_rot = '%u', " + "entry_zone_id = '%d', " + "entry_mjob = '%d', " + "entry_sjob = '%d';"; auto equipEscaped = sql->ObjectToBlobString(&PChar->m_PMonstrosity->EquippedInstincts); auto levelsEscaped = sql->ObjectToBlobString(&PChar->m_PMonstrosity->levels); @@ -224,7 +265,14 @@ void monstrosity::WriteMonstrosityData(CCharEntity* PChar) levelsEscaped.c_str(), instinctsEscaped.c_str(), variantsEscaped.c_str(), - static_cast(PChar->m_PMonstrosity->Belligerency)); + static_cast(PChar->m_PMonstrosity->Belligerency), + PChar->m_PMonstrosity->EntryPos.x, + PChar->m_PMonstrosity->EntryPos.y, + PChar->m_PMonstrosity->EntryPos.z, + PChar->m_PMonstrosity->EntryPos.rotation, + PChar->m_PMonstrosity->EntryZoneId, + PChar->m_PMonstrosity->EntryMainJob, + PChar->m_PMonstrosity->EntrySubJob); } void monstrosity::TryPopulateMonstrosityData(CCharEntity* PChar) @@ -564,17 +612,14 @@ void monstrosity::HandleDeathMenu(CCharEntity* PChar, uint8 type) // 1: Cancel if (type == 1) { - // Return to Feretory - // TODO: This should return you to the Odyssean Passage you entered with, not Feretory! - PChar->loc.destination = ZONE_FERETORY; + luautils::OnMonstrosityReturnToEntrance(PChar); } else if (type == 2) { // Restart this zone with Gestation effect PChar->loc.destination = PChar->loc.zone->GetID(); + charutils::SendToZone(PChar, 2, zoneutils::GetZoneIPP(PChar->loc.destination)); } - - charutils::SendToZone(PChar, 2, zoneutils::GetZoneIPP(PChar->loc.destination)); } bool monstrosity::IsInstinctUnlocked(CCharEntity* PChar, uint16 instinct) diff --git a/src/map/monstrosity.h b/src/map/monstrosity.h index cd02955032e..33ffa9a2d6a 100644 --- a/src/map/monstrosity.h +++ b/src/map/monstrosity.h @@ -60,6 +60,11 @@ namespace monstrosity std::array variants{ 0 }; bool Belligerency; + + position_t EntryPos{}; + uint16 EntryZoneId; + uint8 EntryMainJob; + uint8 EntrySubJob; }; void LoadStaticData(); From a149932955bf93f666f0195270ebd78e31993204 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Mon, 16 Oct 2023 12:15:55 +0100 Subject: [PATCH 095/103] Hook up Monstrosity settings --- scripts/globals/monstrosity.lua | 24 +++++++++++++++--------- src/map/monstrosity.cpp | 7 +++++++ src/map/packet_system.cpp | 4 +++- 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/scripts/globals/monstrosity.lua b/scripts/globals/monstrosity.lua index 57fba69707a..c4de404c60f 100644 --- a/scripts/globals/monstrosity.lua +++ b/scripts/globals/monstrosity.lua @@ -1067,14 +1067,15 @@ xi.monstrosity.onMonstrosityUpdate = function(player, data) end xi.monstrosity.onMonstrosityReturnToEntrance = function(player) - local data = player:getMonstrosityData() - local x = data.entry_x; - local y = data.entry_y; - local z = data.entry_z; - local rot = data.entry_rot; - local zoneId = data.entry_zone_id; - local mjob = data.entry_mjob; - local sjob = data.entry_sjob; + local data = player:getMonstrosityData() + + local x = data.entry_x + local y = data.entry_y + local z = data.entry_z + local rot = data.entry_rot + local zoneId = data.entry_zone_id + local mjob = data.entry_mjob + local sjob = data.entry_sjob -- TODO: Sanity check @@ -1219,12 +1220,17 @@ xi.monstrosity.odysseanPassageOnTrigger = function(player, npc) local monSize = player:getMonstrositySize() local hasBelligerency = player:getBelligerencyFlag() and 1 or 0 + -- Show the full menu, not the restricted one + if xi.settings.main.MONSTROSITY_PVP_ZONE_BYPASS == 1 then + hasBelligerency = 0 + end + -- NOTE: The list of available zones is built from the char's list of -- visited zones. If you haven't visited any zones in a category it'll back -- out immediately. -- NOTE: Param5 is not consistent, Bee has seen 0, 1, and 2 so far -- player:startEvent(5, 0, 0, 0, 0, 2, 0, 0, 0) -- Bee - player:startEvent(5, 0, monSize, hasBelligerency, 0, 0, 0, 0, 0) -- Tiger + player:startEvent(5, 0, monSize, hasBelligerency, 0, 0, 0, 0, 0) end xi.monstrosity.odysseanPassageOnEventUpdate = function(player, csid, option, npc) diff --git a/src/map/monstrosity.cpp b/src/map/monstrosity.cpp index 9bb314dd624..90fb146a1c9 100644 --- a/src/map/monstrosity.cpp +++ b/src/map/monstrosity.cpp @@ -482,12 +482,19 @@ void monstrosity::HandleEquipChangePacket(CCharEntity* PChar, CBasicPacket& data PChar->m_PMonstrosity->Size = data.size; PChar->m_PMonstrosity->Look = data.look; + // If changing "family" of species if (PChar->m_PMonstrosity->MonstrosityId != previousId) { + // Unequip all instincts for (std::size_t idx = 0; idx < 12; ++idx) { PChar->m_PMonstrosity->EquippedInstincts[idx] = 0x0000; } + + if (!settings::get("main.MONSTROSITY_DONT_WIPE_BUFFS")) + { + PChar->StatusEffectContainer->EraseAllStatusEffect(); + } } } else if (flag == 0x04) // Instinct Change diff --git a/src/map/packet_system.cpp b/src/map/packet_system.cpp index dbb73ea3c1c..2818b8119f8 100644 --- a/src/map/packet_system.cpp +++ b/src/map/packet_system.cpp @@ -893,7 +893,9 @@ void SmallPacket0x01A(map_session_data_t* const PSession, CCharEntity* const PCh PNpc = PChar->GetEntity(TargID, TYPE_NPC | TYPE_MOB); // MONs are allowed to use doors, but nothing else - if (PChar->m_PMonstrosity != nullptr && PNpc->look.size != 0x02 && PChar->getZone() != ZONEID::ZONE_FERETORY) + if (PChar->m_PMonstrosity != nullptr && + PNpc->look.size != 0x02 && + (PChar->getZone() != ZONEID::ZONE_FERETORY || !settings::get("main.MONSTROSITY_TRIGGER_NPCS"))) { PChar->pushPacket(new CReleasePacket(PChar, RELEASE_TYPE::STANDARD)); return; From 8fd35c9fa946a5180dea3118199b2aa283e4d6a8 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Mon, 16 Oct 2023 15:42:56 +0100 Subject: [PATCH 096/103] Monstrosity playtesting fixes Death menu conditions shuffled around Added comments Fixed NPC trigger conditions Fix Bee quest --- scripts/globals/monstrosity.lua | 2 +- .../quests/hiddenQuests/Monstrosity_Bee.lua | 2 +- scripts/quests/otherAreas/Monstrosity.lua | 10 ++++++-- src/map/monstrosity.cpp | 23 +++++++++++-------- src/map/packet_system.cpp | 3 ++- 5 files changed, 25 insertions(+), 15 deletions(-) diff --git a/scripts/globals/monstrosity.lua b/scripts/globals/monstrosity.lua index c4de404c60f..7496b4c09f0 100644 --- a/scripts/globals/monstrosity.lua +++ b/scripts/globals/monstrosity.lua @@ -1256,7 +1256,7 @@ xi.monstrosity.odysseanPassageOnEventFinish = function(player, csid, option, npc zoneSelected ) else - print('Monstrosity Teleport - No Valid Entries for Zone' .. zoneSelected .. '. Setting pos to (0, 0, 0)!') + print('Monstrosity Teleport - No Valid Entries for Zone ' .. zoneSelected .. '. Setting pos to (0, 0, 0)!') player:setPos(0, 0, 0, 0, zoneSelected) end end diff --git a/scripts/quests/hiddenQuests/Monstrosity_Bee.lua b/scripts/quests/hiddenQuests/Monstrosity_Bee.lua index c85ecd35790..fbfdd6a836a 100644 --- a/scripts/quests/hiddenQuests/Monstrosity_Bee.lua +++ b/scripts/quests/hiddenQuests/Monstrosity_Bee.lua @@ -43,7 +43,7 @@ quest.sections = { check = function(player, questVars, vars) return quest:getVar(player, 'Timer') <= VanadielUniqueDay() and - not xi.monstrosity.hasUnlockedSpecies(xi.monstrosity.species.BEE) + not xi.monstrosity.hasUnlockedSpecies(player, xi.monstrosity.species.BEE) end, [xi.zone.FERETORY] = diff --git a/scripts/quests/otherAreas/Monstrosity.lua b/scripts/quests/otherAreas/Monstrosity.lua index 1d6b060bfc2..38f808fbc30 100644 --- a/scripts/quests/otherAreas/Monstrosity.lua +++ b/scripts/quests/otherAreas/Monstrosity.lua @@ -2,7 +2,13 @@ -- Monstrosity ----------------------------------- -- Log ID: 4, Quest ID: 34 --- Suspicious Hume : !pos -491.817 24.988 -618.552 109 +-- Suspicious Hume : !pos -491.817 24.988 -618.552 109 +-- Suspicious Elvaan : !pos 82.217 -0.199 42.682 231 +-- Suspicious Galka : !pos 81.610 8.499 -229.065 236 +-- Suspicious Tarutaru : !pos 221.286 -12.000 225.311 241 +-- Rabbit Hide : !additem 856 +-- Lizard Tail : !additem 926 +-- Two-leaf Mandy Bud : !additem 4368 ----------------------------------- local quest = Quest:new(xi.quest.log_id.OTHER_AREAS, xi.quest.id.otherAreas.MONSTROSITY) @@ -170,7 +176,7 @@ quest.sections = { [2] = function(player, csid, option, npc) if option == 1 then - -- TODO: Give monstrosity things here or earlier, and wire up this update + -- TODO: Character appearance has to be encoded here to make the CS show the right character player:updateEvent(7, 10, 2, 1024, 2, 0, 0, 0) end end, diff --git a/src/map/monstrosity.cpp b/src/map/monstrosity.cpp index 90fb146a1c9..26a469f8fb5 100644 --- a/src/map/monstrosity.cpp +++ b/src/map/monstrosity.cpp @@ -599,20 +599,11 @@ void monstrosity::HandleDeathMenu(CCharEntity* PChar, uint8 type) return; } - // remove weakness on homepoint - // PChar->StatusEffectContainer->DelStatusEffectSilent(EFFECT_WEAKNESS); - // PChar->StatusEffectContainer->DelStatusEffectSilent(EFFECT_LEVEL_SYNC); - - PChar->SetDeathTimestamp(0); - PChar->health.hp = PChar->GetMaxHP(); PChar->health.mp = PChar->GetMaxMP(); - - PChar->status = STATUS_TYPE::DISAPPEAR; PChar->animation = ANIMATION_NONE; - PChar->updatemask |= UPDATE_HP; - PChar->clearPacketList(); + PChar->updatemask |= UPDATE_HP; // Monstrosity death menu: // 2: Retry @@ -623,6 +614,18 @@ void monstrosity::HandleDeathMenu(CCharEntity* PChar, uint8 type) } else if (type == 2) { + // TODO: Pick a location from the starting points list + + PChar->loc.p.x = 0.0f; + PChar->loc.p.y = 0.0f; + PChar->loc.p.z = 0.0f; + + PChar->SetDeathTimestamp(0); + + PChar->status = STATUS_TYPE::DISAPPEAR; + + PChar->clearPacketList(); + // Restart this zone with Gestation effect PChar->loc.destination = PChar->loc.zone->GetID(); charutils::SendToZone(PChar, 2, zoneutils::GetZoneIPP(PChar->loc.destination)); diff --git a/src/map/packet_system.cpp b/src/map/packet_system.cpp index 2818b8119f8..2879d28228f 100644 --- a/src/map/packet_system.cpp +++ b/src/map/packet_system.cpp @@ -895,7 +895,8 @@ void SmallPacket0x01A(map_session_data_t* const PSession, CCharEntity* const PCh // MONs are allowed to use doors, but nothing else if (PChar->m_PMonstrosity != nullptr && PNpc->look.size != 0x02 && - (PChar->getZone() != ZONEID::ZONE_FERETORY || !settings::get("main.MONSTROSITY_TRIGGER_NPCS"))) + PChar->getZone() != ZONEID::ZONE_FERETORY && + !settings::get("main.MONSTROSITY_TRIGGER_NPCS")) { PChar->pushPacket(new CReleasePacket(PChar, RELEASE_TYPE::STANDARD)); return; From 0d316aaaa6e74c4030f0ea4bbc688ed83246144e Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Mon, 16 Oct 2023 15:45:05 +0100 Subject: [PATCH 097/103] Add warning for ENABLE_MONSTROSITY --- settings/default/main.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/settings/default/main.lua b/settings/default/main.lua index d710e53e952..45a57d316e0 100644 --- a/settings/default/main.lua +++ b/settings/default/main.lua @@ -69,8 +69,8 @@ xi.settings.main = -- VoidWalker ENABLE_VOIDWALKER = 1, - -- Monstrosity - ENABLE_MONSTROSITY = 1, + -- Monstrosity (Heavily in development, use at your own risk!) + ENABLE_MONSTROSITY = 0, MONSTROSITY_INFAMY_RATIO = 0.1, -- (float) The ratio of exp gained to infamy gained on defeating a mob. MONSTROSITY_INFAMY_MESSAGING = 0, -- Show a message when you gain infamy. MONSTROSITY_TELEPORT_TO_FERETORY = 0, -- Return to Feretory instead of the zone where you entered Feretory when Relinquishing or after death. From 5cbb6b9d133095115fec92b9920b0f0ef48b81e2 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Mon, 16 Oct 2023 15:52:44 +0100 Subject: [PATCH 098/103] Add rough math for Monstrosity Rank --- src/map/packets/monipulator1.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/map/packets/monipulator1.cpp b/src/map/packets/monipulator1.cpp index 51199a3f103..4d0bcc32bc1 100644 --- a/src/map/packets/monipulator1.cpp +++ b/src/map/packets/monipulator1.cpp @@ -42,13 +42,22 @@ CMonipulatorPacket1::CMonipulatorPacket1(CCharEntity* PChar) ref(0x08) = PChar->m_PMonstrosity->Species; ref(0x0A) = PChar->m_PMonstrosity->Flags; - ref(0x0C) = 0; // Monstrosity Rank (0 = Mon, 1 = NM, 2 = HNM) + int32 infamy = charutils::GetPoints(PChar, "infamy"); + + // Monstrosity Rank (0 = Mon, 1 = NM, 2 = HNM) + // TODO: The ranks are listed as: + // 0~10,000 Mon. (Monster) + // 10,001~20,000 NM (Notorious Monster) + // 20,001+ HNM (Highly Notorious Monster) + // But this integer division gives the next rank on 10'000 etc. + // FIXME + ref(0x0C) = static_cast(infamy / 10000); // Unknown ref(0x10) = 0xEC; ref(0x11) = 0x00; - ref(0x12) = charutils::GetPoints(PChar, "infamy"); + ref(0x12) = infamy; // Unknown ref(0x14) = 0x2C; From d1fc64f1d5721ab09a0c66c5aff172cfbcec9db0 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Mon, 16 Oct 2023 17:57:16 +0100 Subject: [PATCH 099/103] Add DefaultAction for Odyssean_Passage in Pashhow Marshlands --- scripts/zones/Pashhow_Marshlands/DefaultActions.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/zones/Pashhow_Marshlands/DefaultActions.lua b/scripts/zones/Pashhow_Marshlands/DefaultActions.lua index 34c7498830f..5be95baa3c8 100644 --- a/scripts/zones/Pashhow_Marshlands/DefaultActions.lua +++ b/scripts/zones/Pashhow_Marshlands/DefaultActions.lua @@ -1,5 +1,6 @@ local ID = zones[xi.zone.PASHHOW_MARSHLANDS] return { - ['Outpost_Gate'] = { messageSpecial = ID.text.GATE_IS_LOCKED }, + ['Odyssean_Passage'] = { messageSpecial = ID.text.NOTHING_HAPPENS }, + ['Outpost_Gate'] = { messageSpecial = ID.text.GATE_IS_LOCKED }, } From 15b9e998d29b96e1ddc859c1921d8414e982e885 Mon Sep 17 00:00:00 2001 From: TeoTwawki Date: Mon, 16 Oct 2023 13:33:08 -0400 Subject: [PATCH 100/103] Update/confirm several model ID for monstrosity --- sql/monstrosity_species.sql | 50 ++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/sql/monstrosity_species.sql b/sql/monstrosity_species.sql index 10f26b6abdf..29409d8ce33 100644 --- a/sql/monstrosity_species.sql +++ b/sql/monstrosity_species.sql @@ -33,7 +33,7 @@ INSERT INTO `monstrosity_species` VALUES (3, 3, "Tiger", 1, 1, 1, 0x0134); -- TO INSERT INTO `monstrosity_species` VALUES (3, 261, "Legendary Tiger", 1, 6, 1, 0x0135); -- TODO: Look guessed not capped INSERT INTO `monstrosity_species` VALUES (3, 262, "Smilodon (Tiger)", 1, 1, 1, 0x08C8); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (4, 4, "Sheep", 1, 1, 1, 0x0154); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (4, 4, "Sheep", 1, 1, 1, 0x0154); INSERT INTO `monstrosity_species` VALUES (4, 263, "Karakul (Sheep)", 1, 1, 1, 0x0951); -- TODO: Look guessed not capped INSERT INTO `monstrosity_species` VALUES (5, 5, "Ram (Sheep)", 1, 1, 2, 0x0000); -- TODO @@ -59,11 +59,11 @@ INSERT INTO `monstrosity_species` VALUES (12, 269, "Orthrus (Cerberus)", 1, 4, 2 INSERT INTO `monstrosity_species` VALUES (13, 13, "Gnole", 2, 2, 1, 0x0000); -- TODO INSERT INTO `monstrosity_species` VALUES (13, 270, "Bipedal Gnole", 2, 2, 1, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (15, 15, "Funguar", 1, 1, 0, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (15, 15, "Funguar", 1, 1, 0, 0x0178); INSERT INTO `monstrosity_species` VALUES (15, 271, "Coppercap (Funguar)", 1, 1, 0, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (16, 16, "Treant Sapling", 1, 1, 0, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (16, 272, "Treant", 1, 4, 2, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (16, 16, "Treant Sapling", 1, 1, 0, 0x0188); +INSERT INTO `monstrosity_species` VALUES (16, 272, "Treant", 1, 4, 2, 0x0B3C); INSERT INTO `monstrosity_species` VALUES (16, 273, "Flowering Treant", 1, 4, 2, 0x0000); -- TODO INSERT INTO `monstrosity_species` VALUES (16, 274, "Scarlet-tinged Treant", 1, 4, 2, 0x0000); -- TODO INSERT INTO `monstrosity_species` VALUES (16, 275, "Barren Treant", 1, 4, 2, 0x0000); -- TODO @@ -71,7 +71,7 @@ INSERT INTO `monstrosity_species` VALUES (16, 276, "Necklaced Treant", 1, 4, 2, INSERT INTO `monstrosity_species` VALUES (17, 17, "Morbol", 1, 1, 2, 0x017C); INSERT INTO `monstrosity_species` VALUES (17, 277, "Pygmy Morbol", 1, 1, 1, 0x0950); -INSERT INTO `monstrosity_species` VALUES (17, 278, "Scarce Morbol", 1, 1, 2, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (17, 278, "Scarce Morbol", 1, 1, 2, 0x00017D); INSERT INTO `monstrosity_species` VALUES (17, 279, "Ameretat (Morbol)", 1, 1, 2, 0x0000); -- TODO INSERT INTO `monstrosity_species` VALUES (17, 280, "Purbol (Morbol)", 1, 4, 2, 0x0000); -- TODO @@ -94,13 +94,13 @@ INSERT INTO `monstrosity_species` VALUES (21, 21, "Goobbue", 1, 1, 2, 0x0000); - INSERT INTO `monstrosity_species` VALUES (22, 22, "Rafflesia", 8, 1, 1, 0x0000); -- TODO INSERT INTO `monstrosity_species` VALUES (22, 289, "Mitrastema (Rafflesia)", 8, 4, 1, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (23, 23, "Panopt", 4, 4, 0, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (23, 23, "Panopt", 4, 4, 0, 0x0205); -INSERT INTO `monstrosity_species` VALUES (27, 27, "Bee", 1, 1, 0, 0x0110); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (27, 290, "Vermillion and Onyx Bee", 1, 5, 0, 0x0111); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (27, 291, "Zaffre Bee", 1, 4, 0, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (27, 27, "Bee", 1, 1, 0, 0x0110); +INSERT INTO `monstrosity_species` VALUES (27, 290, "Vermillion and Onyx Bee", 1, 5, 0, 0x0111); +INSERT INTO `monstrosity_species` VALUES (27, 291, "Zaffre Bee", 1, 4, 0, 0x0790); -INSERT INTO `monstrosity_species` VALUES (28, 28, "Beetle", 7, 7, 0, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (28, 28, "Beetle", 7, 7, 0, 0x0198); INSERT INTO `monstrosity_species` VALUES (28, 292, "Onyx Beetle", 7, 4, 0, 0x0000); -- TODO INSERT INTO `monstrosity_species` VALUES (28, 293, "Gamboge Beetle", 7, 5, 0, 0x0000); -- TODO @@ -109,7 +109,7 @@ INSERT INTO `monstrosity_species` VALUES (29, 294, "Eruca (Crawler)", 1, 5, 1, 0 INSERT INTO `monstrosity_species` VALUES (29, 295, "Emerald Crawler", 3, 4, 1, 0x0000); -- TODO INSERT INTO `monstrosity_species` VALUES (29, 296, "Pygmy Emerald Crawler", 3, 4, 0, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (30, 30, "Fly", 1, 1, 1, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (30, 30, "Fly", 1, 1, 1, 0x01C0); INSERT INTO `monstrosity_species` VALUES (30, 297, "Vermillion Fly", 1, 5, 1, 0x0000); -- TODO INSERT INTO `monstrosity_species` VALUES (31, 31, "Scorpion", 1, 1, 1, 0x0000); -- TODO @@ -141,10 +141,10 @@ INSERT INTO `monstrosity_species` VALUES (37, 309, "Gold Ladybug", 6, 5, 0, 0x00 INSERT INTO `monstrosity_species` VALUES (38, 38, "Gnat", 1, 1, 0, 0x0000); -- TODO INSERT INTO `monstrosity_species` VALUES (38, 310, "Midge (Gnat)", 1, 1, 0, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (43, 43, "Lizard", 1, 1, 0, 0x0148); -- TODO -INSERT INTO `monstrosity_species` VALUES (43, 315, "Ashen Lizard", 1, 4, 0, 0x0149); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (43, 43, "Lizard", 1, 1, 0, 0x0148); +INSERT INTO `monstrosity_species` VALUES (43, 315, "Ashen Lizard", 1, 4, 0, 0x0149); -INSERT INTO `monstrosity_species` VALUES (44, 44, "Raptor", 1, 1, 1, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (44, 44, "Raptor", 1, 1, 1, 0x013C); INSERT INTO `monstrosity_species` VALUES (44, 316, "Emerald Raptor", 1, 1, 1, 0x0000); -- TODO INSERT INTO `monstrosity_species` VALUES (44, 317, "Vermillion Raptor", 1, 1, 1, 0x0000); -- TODO @@ -153,13 +153,13 @@ INSERT INTO `monstrosity_species` VALUES (45, 318, "Pygmy Adamantoise", 1, 1, 1, INSERT INTO `monstrosity_species` VALUES (45, 319, "Legendary Adamantoise", 1, 1, 2, 0x0000); -- TODO INSERT INTO `monstrosity_species` VALUES (45, 320, "Ferromantoise (Adamantoise)", 1, 1, 2, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (46, 46, "Bugard", 1, 1, 2, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (46, 321, "Abyssobugard (Bugard)", 1, 1, 2, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (46, 46, "Bugard", 1, 1, 2, 0x0547); +INSERT INTO `monstrosity_species` VALUES (46, 321, "Abyssobugard (Bugard)", 1, 1, 2, 0x0548); INSERT INTO `monstrosity_species` VALUES (47, 47, "Eft", 1, 1, 0, 0x0000); -- TODO INSERT INTO `monstrosity_species` VALUES (47, 322, "Tarichuk (Eft)", 1, 1, 0, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (48, 48, "Wivre", 1, 1, 2, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (48, 48, "Wivre", 1, 1, 2, 0x08B9); INSERT INTO `monstrosity_species` VALUES (48, 323, "Unusual Wivre", 1, 1, 2, 0x0000); -- TODO INSERT INTO `monstrosity_species` VALUES (49, 49, "Peiste", 1, 1, 1, 0x0000); -- TODO @@ -182,17 +182,17 @@ INSERT INTO `monstrosity_species` VALUES (57, 57, "Sandworm", 1, 1, 2, 0x0000); INSERT INTO `monstrosity_species` VALUES (57, 334, "Pygmy Sandworm", 1, 1, 1, 0x0000); -- TODO INSERT INTO `monstrosity_species` VALUES (57, 335, "Gigaworm (Sandworm)", 1, 1, 2, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (58, 58, "Leech", 1, 1, 0, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (58, 336, "Azure Leech", 1, 1, 0, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (58, 58, "Leech", 1, 1, 0, 0x0114); +INSERT INTO `monstrosity_species` VALUES (58, 336, "Azure Leech", 1, 1, 0, 0x0115); INSERT INTO `monstrosity_species` VALUES (58, 337, "Obdella (Leech)", 1, 1, 0, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (60, 60, "Crab", 1, 1, 0, 0x0164); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (60, 60, "Crab", 1, 1, 0, 0x0164); INSERT INTO `monstrosity_species` VALUES (60, 340, "Vermillion Crab", 1, 1, 0, 0x0165); -- TODO: Look guessed not capped INSERT INTO `monstrosity_species` VALUES (60, 341, "Basket-burdened Crab", 1, 1, 0, 0x0000); -- TODO INSERT INTO `monstrosity_species` VALUES (60, 342, "Vermillion Basket-burdened Crab", 1, 1, 0, 0x0000); -- TODO INSERT INTO `monstrosity_species` VALUES (60, 343, "Porter Crab (Crab)", 1, 1, 0, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (61, 61, "Pugil", 1, 1, 0, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (61, 61, "Pugil", 1, 1, 0, 0x015C); INSERT INTO `monstrosity_species` VALUES (61, 344, "Jagil (Pugil)", 1, 1, 0, 0x0000); -- TODO INSERT INTO `monstrosity_species` VALUES (62, 62, "Sea Monk", 1, 1, 1, 0x0000); -- TODO @@ -238,9 +238,9 @@ INSERT INTO `monstrosity_species` VALUES (76, 76, "Amphiptere", 1, 4, 2, 0x0000) INSERT INTO `monstrosity_species` VALUES (76, 360, "Sanguiptere (Amphiptere)", 4, 1, 2, 0x0000); -- TODO INSERT INTO `monstrosity_species` VALUES (126, 254, "Astoltian Slime", 1, 1, 0, 0x0B41); -INSERT INTO `monstrosity_species` VALUES (126, 508, "Astoltian She-Slime", 1, 1, 0, 0x0B5B); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (126, 508, "Astoltian She-Slime", 1, 1, 0, 0x0B5B); INSERT INTO `monstrosity_species` VALUES (126, 509, "Astoltian Metal Slime", 4, 1, 0, 0x0B5C); -INSERT INTO `monstrosity_species` VALUES (127, 255, "Eorzean Spriggan", 1, 4, 0, 0x0B42); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (127, 510, "Eorzean Spriggan.C", 1, 4, 0, 0x0B5D); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (127, 511, "Eorzean Spriggan.G", 4, 1, 0, 0x0B5E); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (127, 255, "Eorzean Spriggan", 1, 4, 0, 0x0B42); +INSERT INTO `monstrosity_species` VALUES (127, 510, "Eorzean Spriggan.C", 1, 4, 0, 0x0B5D); +INSERT INTO `monstrosity_species` VALUES (127, 511, "Eorzean Spriggan.G", 4, 1, 0, 0x0B5E); From 12fa581d4c00cbe689fd87ebdd851b42d63635ae Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Mon, 16 Oct 2023 21:02:43 +0100 Subject: [PATCH 101/103] Allow status effects to make it out of Feretory --- scripts/globals/monstrosity.lua | 23 ++++++++++++++++++----- scripts/zones/Feretory/Zone.lua | 4 ++++ 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/scripts/globals/monstrosity.lua b/scripts/globals/monstrosity.lua index 7496b4c09f0..311b79c99d2 100644 --- a/scripts/globals/monstrosity.lua +++ b/scripts/globals/monstrosity.lua @@ -1079,7 +1079,10 @@ xi.monstrosity.onMonstrosityReturnToEntrance = function(player) -- TODO: Sanity check - -- TODO: Check settings to see if we should go to Feretory or not + for _, effect in pairs(player:getStatusEffects()) do + player:delStatusEffectSilent(effect:getEffectType()) + end + if xi.settings.main.MONSTROSITY_TELEPORT_TO_FERETORY == 1 then if player:getZoneID() ~= xi.zone.FERETORY then player:setPos(-358, -3.4, -440, 64, xi.zone.FERETORY) @@ -1213,10 +1216,6 @@ xi.monstrosity.odysseanPassageOnTrade = function(player, npc, trade) end xi.monstrosity.odysseanPassageOnTrigger = function(player, npc) - if xi.settings.main.ENABLE_MONSTROSITY ~= 1 then - return - end - local monSize = player:getMonstrositySize() local hasBelligerency = player:getBelligerencyFlag() and 1 or 0 @@ -1282,9 +1281,23 @@ xi.monstrosity.feretoryOnZoneIn = function(player, prevZone) player:changeJob(xi.job.MON) end + for _, effect in pairs(player:getStatusEffects()) do + player:delStatusEffectSilent(effect:getEffectType()) + end + return cs end +xi.monstrosity.feretoryOnZoneOut = function(player) + -- Mark all status effects so they'll survive zoning + -- (there are some routines that will force them off anyway) + for _, effect in pairs(player:getStatusEffects()) do + print(effect) + effect:delEffectFlag(xi.effectFlag.ON_ZONE) + effect:delEffectFlag(xi.effectFlag.LOGOUT) + end +end + xi.monstrosity.feretoryOnEventUpdate = function(player, csid, option, npc) end diff --git a/scripts/zones/Feretory/Zone.lua b/scripts/zones/Feretory/Zone.lua index c28aa4ad28e..70316be4be8 100644 --- a/scripts/zones/Feretory/Zone.lua +++ b/scripts/zones/Feretory/Zone.lua @@ -13,6 +13,10 @@ zoneObject.onZoneIn = function(player, prevZone) return xi.monstrosity.feretoryOnZoneIn(player, prevZone) end +zoneObject.onZoneOut = function(player) + xi.monstrosity.feretoryOnZoneOut(player) +end + zoneObject.onTriggerAreaEnter = function(player, triggerArea) -- Unused end From c05032f0c7c3fbcd83314b5303376d90089fbe04 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Mon, 16 Oct 2023 21:50:03 +0100 Subject: [PATCH 102/103] Add buff purchases from Teyrnon --- scripts/globals/monstrosity.lua | 153 +++++++++++++++++++++++++++++--- 1 file changed, 141 insertions(+), 12 deletions(-) diff --git a/scripts/globals/monstrosity.lua b/scripts/globals/monstrosity.lua index 311b79c99d2..469e6a3ba7a 100644 --- a/scripts/globals/monstrosity.lua +++ b/scripts/globals/monstrosity.lua @@ -1419,20 +1419,149 @@ xi.monstrosity.teyrnonOnEventFinish = function(player, csid, option, npc) end elseif optionType == 3 then + -- TODO: The casting effects and animations + + local tryPayCost = function(playerArg, cost) + if playerArg:getCurrency('infamy') < cost then + playerArg:messageSpecial(zones[xi.zone.FERETORY].text.THY_BRAZEN_DISREGARD) + return false + end + + playerArg:delCurrency('infamy', cost) + return true + end + local selectedEffect = bit.rshift(option, 8) + switch(selectedEffect): caseof + { + -- 0: Dedication 1 + -- 50% experience bonus 60 minutes or until a maximum bonus of 10,000 EXP is gained + [0] = function() + if not tryPayCost(player, 3000) then + return + end + + local effect = xi.effect.DEDICATION + local power = 50 + local duration = utils.minutes(60) + local subpower = 10000 + player:delStatusEffectSilent(power) + xi.itemUtils.addItemExpEffect(player, effect, power, duration, subpower) + end, + + -- 1: Dedication 2 + -- 100% experience bonus 60 minutes or until a maximum bonus of 2,000 EXP is gained + [1] = function() + if not tryPayCost(player, 400) then + return + end + + local effect = xi.effect.DEDICATION + local power = 100 + local duration = utils.minutes(60) + local subpower = 2000 + player:delStatusEffectSilent(power) + xi.itemUtils.addItemExpEffect(player, effect, power, duration, subpower) + end, + + -- 2: Regen + [2] = function() + if not tryPayCost(player, 10) then + return + end + + player:delStatusEffectSilent(xi.effect.REGEN) + player:addStatusEffect(xi.effect.REGEN, 1, 3, 3600) + end, + + -- 3: Refresh + [3] = function() + if not tryPayCost(player, 10) then + return + end + + player:delStatusEffectSilent(xi.effect.REFRESH) + player:delStatusEffect(xi.effect.SUBLIMATION_COMPLETE) + player:delStatusEffect(xi.effect.SUBLIMATION_ACTIVATED) + player:addStatusEffect(xi.effect.REFRESH, 1, 3, 3600, 0, 3) + end, + + -- 4: Protect + [4] = function() + if not tryPayCost(player, 100) then + return + end + + local mLvl = player:getMainLvl() + local power = 220 + local tier = 5 + + if mLvl < 27 then + power = 20 + tier = 1 + elseif mLvl < 47 then + power = 50 + tier = 2 + elseif mLvl < 63 then + power = 90 + tier = 3 + elseif mLvl < 76 then + power = 140 + tier = 4 + end + + local bonus = 0 + if player:getMod(xi.mod.ENHANCES_PROT_SHELL_RCVD) > 0 then + bonus = 2 -- 2x Tier from MOD + end + + power = power + (bonus * tier) + player:delStatusEffectSilent(xi.effect.PROTECT) + player:addStatusEffect(xi.effect.PROTECT, power, 0, 1800, 0, 0, tier) + end, + + -- 5: Shell + [5] = function() + if not tryPayCost(player, 100) then + return + end + + local mLvl = player:getMainLvl() + + -- Shell V (75/256) + local power = 2930 + local tier = 5 + + if mLvl < 37 then + power = 1055 -- Shell I (27/256) + tier = 1 + elseif mLvl < 57 then + power = 1641 -- Shell II (42/256) + tier = 2 + elseif mLvl < 68 then + power = 2188 -- Shell III (56/256) + tier = 3 + elseif mLvl < 76 then + power = 2617 -- Shell IV (67/256) + tier = 4 + end + + local bonus = 0 + if player:getMod(xi.mod.ENHANCES_PROT_SHELL_RCVD) > 0 then + bonus = 39 -- (1/256 bonus buff per tier of spell) + end - utils.unused(selectedEffect) - -- TODO: Use Abyssea buff method with tabled data here. Do we need a check to remove - -- these statuses when MON is removed? - - -- selectedEffect: - -- 0: Dedication 1 - -- 1: Dedication 2 - -- 2: Regen - -- 3: Refresh - -- 4: Protect - -- 5: Shell - -- 6: Haste + power = power + (bonus * tier) + player:delStatusEffectSilent(xi.effect.SHELL) + player:addStatusEffect(xi.effect.SHELL, power, 0, 1800, 0, 0, tier) + end, + + -- 6: Haste + [6] = function() + player:delStatusEffectSilent(xi.effect.HASTE) + player:addStatusEffect(xi.effect.HASTE, 1000, 0, 600) + end, + } end end From f96dc269844be7dba360e03f05a2d914dfdbc826 Mon Sep 17 00:00:00 2001 From: Zach Toogood Date: Tue, 17 Oct 2023 18:23:49 +0100 Subject: [PATCH 103/103] Update some Monstrosity model IDs --- documentation/model_ids.txt | 12 ++++++------ sql/monstrosity_species.sql | 18 +++++++++--------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/documentation/model_ids.txt b/documentation/model_ids.txt index 4b54a9467f2..0f879124404 100644 --- a/documentation/model_ids.txt +++ b/documentation/model_ids.txt @@ -27,11 +27,11 @@ ID Number Costume Name 25 Diabolos 26 Odin 27 Alexander -28 Mandragora -29 Mandragora -30 Mandragora +28 Cait Sith +29 Atomos +30 Siren 31 Mandragora -32 Hume Male +32 Hume Male with hood 33 34 35 @@ -1931,7 +1931,7 @@ ID Number Costume Name 1931 1932 Sturdy Pyxis 1933 Samursk(AbysNM) -1934 Leech (Black) Abys +1934 Leech (Black) Abys - Obdella 1935 Wyvern (Red) Abys 1936 Bee (Blue) Abys 1937 Rabbit(Orange) Abys @@ -2201,7 +2201,7 @@ ID Number Costume Name 2201 Dancer NPC2(HolidayDeco) 2202 Aphmau White (arm raised) 2203 Lion (dagger toss) -2204 Mandragora +2204 Tiny/Pygmy Mandragora 2205 Dark Ixion 2206 Ruszor 2207 Lynx diff --git a/sql/monstrosity_species.sql b/sql/monstrosity_species.sql index 29409d8ce33..1e6ac6c7697 100644 --- a/sql/monstrosity_species.sql +++ b/sql/monstrosity_species.sql @@ -53,8 +53,8 @@ INSERT INTO `monstrosity_species` VALUES (10, 10, "Buffalo", 7, 7, 2, 0x0000); - INSERT INTO `monstrosity_species` VALUES (11, 11, "Marid", 1, 1, 2, 0x06CA); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (12, 12, "Cerberus", 1, 1, 2, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (12, 269, "Orthrus (Cerberus)", 1, 4, 2, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (12, 12, "Cerberus", 1, 1, 2, 0x0701); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (12, 269, "Orthrus (Cerberus)", 1, 4, 2, 0x0702); -- TODO INSERT INTO `monstrosity_species` VALUES (13, 13, "Gnole", 2, 2, 1, 0x0000); -- TODO INSERT INTO `monstrosity_species` VALUES (13, 270, "Bipedal Gnole", 2, 2, 1, 0x0000); -- TODO @@ -72,16 +72,16 @@ INSERT INTO `monstrosity_species` VALUES (16, 276, "Necklaced Treant", 1, 4, 2, INSERT INTO `monstrosity_species` VALUES (17, 17, "Morbol", 1, 1, 2, 0x017C); INSERT INTO `monstrosity_species` VALUES (17, 277, "Pygmy Morbol", 1, 1, 1, 0x0950); INSERT INTO `monstrosity_species` VALUES (17, 278, "Scarce Morbol", 1, 1, 2, 0x00017D); -INSERT INTO `monstrosity_species` VALUES (17, 279, "Ameretat (Morbol)", 1, 1, 2, 0x0000); -- TODO -INSERT INTO `monstrosity_species` VALUES (17, 280, "Purbol (Morbol)", 1, 4, 2, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (17, 279, "Ameretat (Morbol)", 1, 1, 2, 0x08B6); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (17, 280, "Purbol (Morbol)", 1, 4, 2, 0x017F); -- TODO: Look guessed not capped INSERT INTO `monstrosity_species` VALUES (18, 18, "Mandragora", 2, 2, 0, 0x012C); INSERT INTO `monstrosity_species` VALUES (18, 281, "Korrigan (Mandragora)", 2, 4, 0, 0x012D); -- TODO: Look guessed not capped INSERT INTO `monstrosity_species` VALUES (18, 282, "Lycopodium (Mandragora)", 2, 3, 0, 0x08C7); -- TODO: Look guessed not capped -INSERT INTO `monstrosity_species` VALUES (18, 283, "Pygmy Mandragora", 2, 2, 0, 0x012C); -- TODO: Look guessed not capped, this is the wrong id -INSERT INTO `monstrosity_species` VALUES (18, 284, "Adenium (Mandragora)", 2, 5, 0, 0x012C); -- TODO: Look guessed not capped, this is the wrong id -INSERT INTO `monstrosity_species` VALUES (18, 285, "Pachypodium (Mandragora)", 2, 2, 0, 0x012C); -- TODO: Look guessed not capped, this is the wrong id -INSERT INTO `monstrosity_species` VALUES (18, 286, "Enlightened Mandragora", 2, 2, 0, 0x012C); -- TODO: Look guessed not capped, this is the wrong id +INSERT INTO `monstrosity_species` VALUES (18, 283, "Pygmy Mandragora", 2, 2, 0, 0x089C); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (18, 284, "Adenium (Mandragora)", 2, 5, 0, 0x0950); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (18, 285, "Pachypodium (Mandragora)", 2, 2, 0, 0x0949); -- TODO: Look guessed not capped +INSERT INTO `monstrosity_species` VALUES (18, 286, "Enlightened Mandragora", 2, 2, 0, 0x0930); -- TODO: Look guessed not capped, this is the wrong id? INSERT INTO `monstrosity_species` VALUES (18, 287, "New Year Mandragora", 2, 13, 0, 0x0B48); INSERT INTO `monstrosity_species` VALUES (19, 19, "Sabotender", 1, 1, 0, 0x0000); -- TODO @@ -184,7 +184,7 @@ INSERT INTO `monstrosity_species` VALUES (57, 335, "Gigaworm (Sandworm)", 1, 1, INSERT INTO `monstrosity_species` VALUES (58, 58, "Leech", 1, 1, 0, 0x0114); INSERT INTO `monstrosity_species` VALUES (58, 336, "Azure Leech", 1, 1, 0, 0x0115); -INSERT INTO `monstrosity_species` VALUES (58, 337, "Obdella (Leech)", 1, 1, 0, 0x0000); -- TODO +INSERT INTO `monstrosity_species` VALUES (58, 337, "Obdella (Leech)", 1, 1, 0, 0x078E); -- TODO: Look guessed not capped INSERT INTO `monstrosity_species` VALUES (60, 60, "Crab", 1, 1, 0, 0x0164); INSERT INTO `monstrosity_species` VALUES (60, 340, "Vermillion Crab", 1, 1, 0, 0x0165); -- TODO: Look guessed not capped