diff --git a/scripts/enum/mob_mod.lua b/scripts/enum/mob_mod.lua index b937f2b770a..16f5d54983c 100644 --- a/scripts/enum/mob_mod.lua +++ b/scripts/enum/mob_mod.lua @@ -91,4 +91,7 @@ xi.mobMod = SKIP_ALLEGIANCE_CHECK = 80, -- Skip the allegiance check for valid target (allows for example a mob to cast a TARGET_ENEMY spell on itself) ABILITY_RESPONSE = 81, -- Mob can respond to player ability use with onPlayerAbilityUse() SPEED_BOOST_MULT = 82, -- Multiplier for the base speed of a mob when the mob's target is out of range (range between 100 and 500, 250 means 2.5x, mechanism is from retail) + DRAW_IN_FRONT = 83, -- Mob will draw in slightly in front of them instead of the center of their hitbox (HNMs such as Tiamat) + DRAW_IN_CUSTOM_RANGE = 84, -- Override the default range of MeleeRange*2 of when players start to get drawn-in + DRAW_IN_BIND = 85, -- Forces mob to draw in the moment you leave melee range (ex. Mimics) } diff --git a/src/map/ai/controllers/mob_controller.cpp b/src/map/ai/controllers/mob_controller.cpp index 4cbf1c84218..9598fb08ec5 100644 --- a/src/map/ai/controllers/mob_controller.cpp +++ b/src/map/ai/controllers/mob_controller.cpp @@ -708,9 +708,19 @@ void CMobController::Move() if (((currentDistance > closeDistance) || move) && PMob->PAI->CanFollowPath()) { // TODO: can this be moved to scripts entirely? - if (PMob->getMobMod(MOBMOD_DRAW_IN) > 0) + if (PMob->getMobMod(MOBMOD_DRAW_IN)) { - if (currentDistance >= PMob->GetMeleeRange() * 2 && battleutils::DrawIn(PTarget, PMob, PMob->GetMeleeRange() - 0.2f)) + uint8 drawInRange = PMob->getMobMod(MOBMOD_DRAW_IN_CUSTOM_RANGE) > 0 ? PMob->getMobMod(MOBMOD_DRAW_IN_CUSTOM_RANGE) : PMob->GetMeleeRange() * 2; + + // Draw in when target is farther than 2x melee range + if (currentDistance > drawInRange && battleutils::DrawIn(PTarget, PMob, PMob->GetMeleeRange() - 0.2f, drawInRange)) + { + FaceTarget(); + return; + } + // If i'm bound/can't move, draw in the moment they leave my melee range + else if ((PMob->getMobMod(MOBMOD_DRAW_IN_BIND) && PMob->speed == 0 || PMob->getMobMod(MOBMOD_NO_MOVE)) && + currentDistance > PMob->GetMeleeRange() && battleutils::DrawIn(PTarget, PMob, PMob->GetMeleeRange() - 1.2f, PMob->GetMeleeRange())) { FaceTarget(); return; diff --git a/src/map/lua/lua_baseentity.cpp b/src/map/lua/lua_baseentity.cpp index 5ba0f4c584f..6979a089539 100644 --- a/src/map/lua/lua_baseentity.cpp +++ b/src/map/lua/lua_baseentity.cpp @@ -17172,7 +17172,8 @@ void CLuaBaseEntity::drawIn(sol::variadic_args va) return; } - auto mobObj = dynamic_cast(m_PBaseEntity); + auto mobObj = dynamic_cast(m_PBaseEntity); + uint8 drawInRange = mobObj->GetMeleeRange() * 2; if (va.size() == 0) { @@ -17182,7 +17183,7 @@ void CLuaBaseEntity::drawIn(sol::variadic_args va) { return; } - battleutils::DrawIn(defaultTarget, mobObj, mobObj->GetMeleeRange() - 0.2f); + battleutils::DrawIn(defaultTarget, mobObj, mobObj->GetMeleeRange() - 0.2f, drawInRange); return; } @@ -17204,7 +17205,7 @@ void CLuaBaseEntity::drawIn(sol::variadic_args va) if (PTarget) { - battleutils::DrawIn(PTarget, mobObj, mobObj->GetMeleeRange() - 0.2f); + battleutils::DrawIn(PTarget, mobObj, mobObj->GetMeleeRange() - 0.2f, drawInRange); } return; diff --git a/src/map/mob_modifier.h b/src/map/mob_modifier.h index 4b54a0858ff..1668c0a5334 100644 --- a/src/map/mob_modifier.h +++ b/src/map/mob_modifier.h @@ -111,6 +111,9 @@ enum MOBMODIFIER : int MOBMOD_SKIP_ALLEGIANCE_CHECK = 80, // Skip the allegiance check for valid target (allows for example a mob to cast a TARGET_ENEMY spell on itself) MOBMOD_ABILITY_RESPONSE = 81, // Mob can respond to player ability use with onPlayerAbilityUse() MOBMOD_SPEED_BOOST_MULT = 82, // Multiplier for the base speed of a mob when the mob's target is out of range (range between 100 and 25500, 250 means 2.5x, mechanism is from retail) + MOBMOD_DRAW_IN_FRONT = 83, // Mob will draw in slightly in front of them instead of the center of their hitbox (HNMs such as Tiamat) + MOBMOD_DRAW_IN_CUSTOM_RANGE = 84, // Override the default range of MeleeRange*2 of when players start to get drawn-in + MOBMOD_DRAW_IN_BIND = 85, // Forces mob to draw in the moment you leave melee range (ex. Mimics) }; #endif diff --git a/src/map/utils/battleutils.cpp b/src/map/utils/battleutils.cpp index 0be1b1911ec..98b759e68a6 100644 --- a/src/map/utils/battleutils.cpp +++ b/src/map/utils/battleutils.cpp @@ -5595,8 +5595,14 @@ namespace battleutils } } - bool DrawIn(CBattleEntity* PTarget, CMobEntity* PMob, float offset) + bool DrawIn(CBattleEntity* PTarget, CMobEntity* PMob, float offset, uint8 drawInRange) { + // Check if time to draw in again, if not then exit + if (std::chrono::time_point_cast(server_clock::now()).time_since_epoch().count() - PMob->GetLocalVar("DrawInTime") < 2) + { + return false; + } + position_t& pos = PMob->loc.p; position_t nearEntity = nearPosition(pos, offset, (float)0); @@ -5622,19 +5628,13 @@ namespace battleutils // Move the target a little higher, just in case nearEntity.y -= 1.0f; - bool success = false; - float drawInDistance = (float)(PMob->getMobMod(MOBMOD_DRAW_IN) > 1 ? PMob->getMobMod(MOBMOD_DRAW_IN) : PMob->GetMeleeRange() * 2); + bool success = false; - if (std::chrono::time_point_cast(server_clock::now()).time_since_epoch().count() - PMob->GetLocalVar("DrawInTime") < 2) - { - return false; - } - - std::function drawInFunc = [PMob, drawInDistance, &nearEntity, &success](CBattleEntity* PMember) + std::function drawInFunc = [PMob, drawInRange, &nearEntity, &success](CBattleEntity* PMember) { float pDistance = distance(PMob->loc.p, PMember->loc.p); - if (PMob->loc.zone == PMember->loc.zone && pDistance > drawInDistance && PMember->status != STATUS_TYPE::CUTSCENE_ONLY) + if (PMob->loc.zone == PMember->loc.zone && pDistance > drawInRange && PMember->status != STATUS_TYPE::CUTSCENE_ONLY) { // don't draw in dead players for now! // see tractor @@ -5645,9 +5645,20 @@ namespace battleutils else { // draw in! - PMember->loc.p.x = nearEntity.x; - PMember->loc.p.y = nearEntity.y; - PMember->loc.p.z = nearEntity.z; + // Certain families (such as Wyrms) draw in in front of where they are looking + if (PMob->getMobMod(MOBMOD_DRAW_IN_FRONT)) + { + PMember->loc.p.x = nearEntity.x; + PMember->loc.p.y = nearEntity.y; + PMember->loc.p.z = nearEntity.z; + } + // Default behavior is to draw in to middle of hit box. + else + { + PMember->loc.p.x = PMob->loc.p.x; + PMember->loc.p.y = nearEntity.y; + PMember->loc.p.z = PMob->loc.p.z; + } if (PMember->objtype == TYPE_PC) { diff --git a/src/map/utils/battleutils.h b/src/map/utils/battleutils.h index 8eff76652cd..3cd036e7278 100644 --- a/src/map/utils/battleutils.h +++ b/src/map/utils/battleutils.h @@ -246,7 +246,7 @@ namespace battleutils WEATHER GetWeather(CBattleEntity* PEntity, bool ignoreScholar); WEATHER GetWeather(CBattleEntity* PEntity, bool ignoreScholar, uint16 zoneWeather); bool WeatherMatchesElement(WEATHER weather, uint8 element); - bool DrawIn(CBattleEntity* PEntity, CMobEntity* PMob, float offset); + bool DrawIn(CBattleEntity* PEntity, CMobEntity* PMob, float offset, uint8 drawInRange); void DoWildCardToEntity(CCharEntity* PCaster, CCharEntity* PTarget, uint8 roll); bool DoRandomDealToEntity(CCharEntity* PChar, CBattleEntity* PTarget);