Files
metin2-client/tests/host/autohunt_pull_test.cpp
T

121 lines
4.9 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Compile and execute the shipped server command against a small server API fixture.
#ifdef NDEBUG
#undef NDEBUG
#endif
#include <cassert>
#include <cstdarg>
#include <cstdio>
#include <cstring>
#include <map>
#include <string>
#include <vector>
#include <cmath>
#include <cstdint>
using DWORD = uint32_t;
constexpr int ENTITY_CHARACTER=1, ATTR_BANPK=4, MOB_RANK_BOSS=4, CHAT_TYPE_COMMAND=5;
struct Character;
using LPCHARACTER=Character*;
struct Entity { virtual ~Entity() {} bool IsType(int) { return true; } };
using LPENTITY=Entity*;
struct Party {};
struct Sectree {
bool safe=false;
std::vector<Entity*> entities;
DWORD GetAttribute(long,long) { return safe ? ATTR_BANPK : 0; }
template<class F> void ForEachAround(F& f) { for(auto* e:entities) f(e); }
};
struct Character: Entity {
DWORD vid=0;
int x=0,y=0,rank=0,hp=100,maxhp=100;
bool pc=false,dead=false,stun=false,canHandle=true;
Sectree* sec=nullptr;
Party* party=nullptr;
Character* victim=nullptr;
int next=0, beginCount=0;
std::vector<std::string> replies;
bool IsMonster() { return !pc; }
bool IsPC() { return pc; }
bool IsDead() { return dead; }
bool IsStun() { return stun; }
bool CanHandleItem() { return canHandle; }
bool CanBeginFight() { return !victim && !dead && !stun; }
int GetHP() { return hp; }
int GetMaxHP() { return maxhp; }
int GetX() { return x; }
int GetY() { return y; }
int GetMapIndex() { return 1; }
int GetMobRank() { return rank; }
Sectree* GetSectree() { return sec; }
Party* GetParty() { return party; }
DWORD GetVID() { return vid; }
Character* GetVictim() { return victim; }
int GetQuestFlag(const char*) { return next; }
void SetQuestFlag(const char*,int value) { next=value; }
void BeginFight(Character* owner) { victim=owner; ++beginCount; }
void ChatPacket(int,const char* format,...) {
char buf[256]; va_list ap; va_start(ap,format); vsnprintf(buf,sizeof(buf),format,ap); va_end(ap);
replies.emplace_back(buf);
}
};
struct CHARACTER_MANAGER {
std::map<DWORD,Character*> mobs;
static CHARACTER_MANAGER& instance() { static CHARACTER_MANAGER value; return value; }
Character* Find(DWORD vid) { return mobs[vid]; }
};
int now=100;
int get_global_time() { return now; }
#define DISTANCE_APPROX(x,y) int(std::hypot(x,y))
#define ACMD(name) void name(LPCHARACTER ch,const char* argument)
#define MT_AUTOHUNT_SERVER_TEST
#include "../../tools/server/autohunt-pull/autohunt_pull.h"
int main() {
Sectree sec;
Character owner; owner.pc=true; owner.sec=&sec;
Character other; other.pc=true;
std::vector<Party> parties(25);
std::vector<Character> mobs(50);
for(size_t i=0;i<mobs.size();++i) {
auto& mob=mobs[i]; mob.vid=100+i; mob.x=100+int(i/2)*100; mob.sec=&sec; mob.party=&parties[i/2];
sec.entities.push_back(&mob); CHARACTER_MANAGER::instance().mobs[mob.vid]=&mob;
}
// A group owned by another player and a boss group are both excluded as whole groups.
mobs[0].victim=&other;
mobs[2].rank=MOB_RANK_BOSS;
do_autohunt_pull(&owner,"3 5000 0 7");
assert(owner.replies.back()=="AutoHuntGather 7 done 3");
assert(owner.replies.size()==7); // 3 actual parties × 2 members, then completion
for(int i=4;i<10;++i) assert(mobs[i].victim==&owner && mobs[i].beginCount==1);
assert(!mobs[1].victim && !mobs[3].victim && !mobs[10].victim);
owner.replies.clear();
do_autohunt_pull(&owner,"20 5000 1 8");
assert(owner.replies.size()==1 && owner.replies.back()=="AutoHuntGather 8 done 0"); // cooldown
now+=10;
owner.replies.clear();
do_autohunt_pull(&owner,"20 5000 1 9");
assert(owner.replies.back()=="AutoHuntGather 9 done 20");
assert(owner.replies.size()==41);
assert(mobs[4].beginCount==1); // already on this player; no redundant restart
now+=10; owner.replies.clear(); owner.hp=49;
do_autohunt_pull(&owner,"10 5000 1 10");
assert(owner.replies.back()=="AutoHuntGather 10 done 0");
owner.hp=100; sec.safe=true; owner.replies.clear();
do_autohunt_pull(&owner,"10 5000 1 11");
assert(owner.replies.back()=="AutoHuntGather 11 done 0");
sec.safe=false; owner.replies.clear();
do_autohunt_pull(&owner,"21 5000 0 12");
do_autohunt_pull(&owner,"3 5001 0 12");
do_autohunt_pull(&owner,"3 5000 0 12 extra");
assert(owner.replies.empty());
// Unequal group sizes, exact 10-group cap and nearest ordering independent of input order.
std::vector<mt_autohunt::Candidate> candidates;
for(int g=24;g>=0;--g)
for(int m=0;m<g%4+1;++m) candidates.push_back({uint64_t(g),uint32_t(g*10+m),g*100,true});
auto ten=mt_autohunt::SelectGroups(candidates,10);
assert(ten.groups==10 && ten.mobs.front()==0);
for(auto vid:ten.mobs) assert(vid/10 < 10);
auto sparse=mt_autohunt::SelectGroups({{1,1,100,true},{1,2,110,true},{2,3,50,true}},20);
assert(sparse.groups==2 && sparse.mobs.size()==3 && sparse.mobs[0]==3);
puts("autohunt_pull_test: PASS");
}