Files
metin2-client/tools/server/autohunt-pull-range/server.patch
T

177 lines
7.4 KiB
Diff

--- a/server/game/src/sectree_manager.h
+++ b/server/game/src/sectree_manager.h
@@ -88,6 +88,18 @@
}
*/
}
+
+ // PORT: snapshot traversal for the complete auto-hunt radius.
+ template<typename Func>
+ void for_each_in_rect(DWORD minX, DWORD minY, DWORD maxX, DWORD maxY, Func& func) {
+ FCollectEntity collector;
+ for (DWORD x = minX / SECTREE_SIZE * SECTREE_SIZE; x <= maxX; x += SECTREE_SIZE)
+ for (DWORD y = minY / SECTREE_SIZE * SECTREE_SIZE; y <= maxY; y += SECTREE_SIZE) {
+ LPSECTREE tree = Find(x, y);
+ if (tree) tree->for_each_entity(collector);
+ }
+ collector.ForEach(func);
+ }
void DumpAllToSysErr() {
SECTREE_MAP::MapType::iterator i;
--- a/server/game/src/autohunt_pull.h
+++ b/server/game/src/autohunt_pull.h
@@ -3,8 +3,37 @@
#include "group_select.h"
#else
#include "sectree_manager.h"
+#include "autohunt_pull_range.h"
#include "autohunt_group_select.h"
#endif
+
+// PORT: only mobs pulled by this command receive a temporary extended pursuit leash.
+struct AutoHuntPullLease {
+ LPCHARACTER mob;
+ DWORD ownerPID;
+ int radius;
+ int expires;
+};
+static std::map<DWORD, AutoHuntPullLease> s_autoHuntPullLeases;
+int AutoHuntPullLeash(LPCHARACTER mob, LPCHARACTER victim) {
+ std::map<DWORD, AutoHuntPullLease>::iterator it = s_autoHuntPullLeases.find((DWORD)mob->GetVID());
+ if (it == s_autoHuntPullLeases.end()) return 4000;
+ const AutoHuntPullLease& lease = it->second;
+ if (lease.mob != mob || !victim || !victim->IsPC() || victim->IsDead() ||
+ victim->GetPlayerID() != lease.ownerPID || mob->GetMapIndex() != victim->GetMapIndex() ||
+ get_global_time() >= lease.expires) {
+ s_autoHuntPullLeases.erase(it);
+ return 4000;
+ }
+ return std::max(4000, lease.radius + 1);
+}
+static void AutoHuntPullPrune(DWORD cancelPID = 0) {
+ for (std::map<DWORD, AutoHuntPullLease>::iterator it = s_autoHuntPullLeases.begin(); it != s_autoHuntPullLeases.end();) {
+ if (it->second.expires <= get_global_time() || (cancelPID && it->second.ownerPID == cancelPID))
+ s_autoHuntPullLeases.erase(it++);
+ else ++it;
+ }
+}
struct AutoHuntPullCollector {
LPCHARACTER owner;
@@ -35,8 +64,11 @@
unsigned int request = 0;
char extra = 0;
if (sscanf(argument, "%d %d %d %u %c", &groups, &radius, &bosses, &request, &extra) != 4 ||
- (groups != 3 && groups != 10 && groups != 20) || radius < 500 || radius > 5000 ||
+ (groups != 0 && groups != 3 && groups != 10 && groups != 20) || radius < 500 || radius > 15000 ||
(bosses != 0 && bosses != 1)) return;
+ if (!ch->IsPC()) return;
+ AutoHuntPullPrune(groups == 0 ? ch->GetPlayerID() : 0);
+ if (groups == 0) return;
const int now = get_global_time();
if (!ch->IsPC() || ch->IsDead() || ch->IsStun() || !ch->CanHandleItem() || !ch->GetSectree() ||
(ch->GetSectree()->GetAttribute(ch->GetX(), ch->GetY()) & ATTR_BANPK) ||
@@ -47,13 +79,23 @@
// Server-side cooldown survives reconnects and switching auto hunt on/off.
ch->SetQuestFlag("autohunt.next_pull", now + 10);
AutoHuntPullCollector collector(ch, radius, bosses != 0);
- ch->GetSectree()->ForEachAround(collector);
+ // ForEachAround only covers nine cells: visit every cell intersecting the requested radius.
+ LPSECTREE_MAP map = SECTREE_MANAGER::instance().GetMap(ch->GetMapIndex());
+ if (map) map->for_each_in_rect(std::max(0, int(ch->GetX()) - radius),
+ std::max(0, int(ch->GetY()) - radius), ch->GetX() + radius, ch->GetY() + radius, collector);
const mt_autohunt::Selection selected = mt_autohunt::SelectGroups(collector.candidates, groups);
for (size_t i = 0; i < selected.mobs.size(); ++i) {
LPCHARACTER mob = CHARACTER_MANAGER::instance().Find(selected.mobs[i]);
if (!mob) continue;
+ if (mob->IsDead() || (mob->GetVictim() && mob->GetVictim() != ch)) continue;
+ AutoHuntPullLease lease = {mob, ch->GetPlayerID(), radius, now + 180};
+ s_autoHuntPullLeases[(DWORD)mob->GetVID()] = lease;
if (!mob->GetVictim()) mob->BeginFight(ch);
ch->ChatPacket(CHAT_TYPE_COMMAND, "AutoHuntGather %u mob %u", request, selected.mobs[i]);
}
ch->ChatPacket(CHAT_TYPE_COMMAND, "AutoHuntGather %u done %d", request, selected.groups);
}
+
+#ifndef MT_AUTOHUNT_SERVER_TEST
+#include "server_bonuses.h"
+#endif
--- a/server/game/src/char_state.cpp
+++ b/server/game/src/char_state.cpp
@@ -3,6 +3,7 @@
#include "utils.h"
#include "vector.h"
#include "char.h"
+#include "autohunt_pull_range.h"
#include "battle.h"
#include "char_manager.h"
#include "packet.h"
@@ -1035,7 +1036,7 @@
float fDist = DISTANCE_APPROX(GetX() - victim->GetX(), GetY() - victim->GetY());
- if (fDist >= 4000.0f) // 40미터 이상 멀어지면 포기
+ if (fDist >= AutoHuntPullLeash(this, victim)) // 40미터 이상 멀어지면 포기
{
MonsterLog("타겟이 멀어서 포기");
SetVictim(NULL);
--- a/server/game/src/cmd.cpp
+++ b/server/game/src/cmd.cpp
@@ -7,6 +7,7 @@
#include "desc.h"
ACMD(do_autohunt_pull);
+ACMD(do_server_bonuses);
ACMD(do_user_horse_ride);
ACMD(do_user_horse_back);
ACMD(do_user_horse_feed);
@@ -281,6 +282,7 @@
{
{ "!RESERVED!", NULL, 0, POS_DEAD, GM_IMPLEMENTOR }, /* 반드시 이 것이 처음이어야 한다. */
{ "autohunt_pull", do_autohunt_pull, 0, POS_STANDING, GM_PLAYER },
+ { "server_bonuses", do_server_bonuses, 0, POS_DEAD, GM_PLAYER },
{ "who", do_who, 0, POS_DEAD, GM_IMPLEMENTOR },
{ "war", do_war, 0, POS_DEAD, GM_PLAYER },
{ "warp", do_warp, 0, POS_DEAD, GM_LOW_WIZARD },
--- /dev/null
+++ b/server/game/src/autohunt_pull_range.h
@@ -0,0 +1,3 @@
+#pragma once
+// PORT extension: ordinary monster pursuit remains 40 metres.
+int AutoHuntPullLeash(LPCHARACTER mob, LPCHARACTER victim);
--- /dev/null
+++ b/server/game/src/server_bonuses.h
@@ -0,0 +1,12 @@
+// PORT: report actual global rates and the character's effective event privileges.
+#include "priv_manager.h"
+ACMD(do_server_bonuses) {
+ if (!ch->IsPC() || get_global_time() < ch->GetQuestFlag("server_bonuses.next_query")) return;
+ ch->SetQuestFlag("server_bonuses.next_query", get_global_time() + 2);
+ CHARACTER_MANAGER& cm = CHARACTER_MANAGER::instance();
+ CPrivManager& pm = CPrivManager::instance();
+ ch->ChatPacket(CHAT_TYPE_COMMAND, "ServerBonuses %d %d %d %d %d %d %d %d",
+ cm.GetMobExpRate(NULL), cm.GetMobGoldAmountRate(NULL), cm.GetMobGoldDropRate(NULL), cm.GetMobItemRate(NULL),
+ pm.GetPriv(ch, PRIV_EXP_PCT), pm.GetPriv(ch, PRIV_ITEM_DROP),
+ pm.GetPriv(ch, PRIV_GOLD_DROP), pm.GetPriv(ch, PRIV_GOLD10_DROP));
+}
--- a/server/game/src/char.cpp
+++ b/server/game/src/char.cpp
@@ -4,6 +4,7 @@
#include "../../common/VnumHelper.h"
#include "char.h"
+#include "autohunt_pull_range.h"
#include "config.h"
#include "utils.h"
@@ -5440,7 +5441,7 @@
if (get_dword_time() - m_pkMobInst->m_dwLastAttackedTime >= 15000) // 마지막으로 공격받은지 15초가 지났고
{
// 마지막 맞은 곳으로 부터 50미터 이상 차이나면 포기하고 돌아간다.
- if (5000 < DISTANCE_APPROX(m_pkMobInst->m_posLastAttacked.x - GetX(), m_pkMobInst->m_posLastAttacked.y - GetY()))
+ if (MAX(5000, AutoHuntPullLeash(this, pkChr)) < DISTANCE_APPROX(m_pkMobInst->m_posLastAttacked.x - GetX(), m_pkMobInst->m_posLastAttacked.y - GetY()))
if (Return())
return true;
}