auto-forceteam (new autoteam), shuffleteams (skill sorted), autosendmap fix, better arenaspawn distribution (unfinished)
git-svn-id: svn://svn.code.sf.net/p/actiongame/code/trunk/ac@2443 e016b1c5-ed68-44aa-a2f7-31cac88cffde
This commit is contained in:
@@ -11,4 +11,5 @@ alias stopdemo [ callvote 9 ]
|
||||
alias cleardemo [ callvote 10 $arg1 ]
|
||||
alias cleardemos [ callvote 10 0 ]
|
||||
alias serverdesc [ callvote 11 $arg1 ]
|
||||
alias shuffleteams [ callvote 12 ]
|
||||
alias demo [ mode -3; map $arg1 ]
|
||||
|
||||
+21
-18
@@ -24,7 +24,7 @@ bool allowedittoggle()
|
||||
{
|
||||
bool allow = !curpeer || gamemode==1;
|
||||
if(!allow) conoutf("editing in multiplayer requires coopedit mode (1)");
|
||||
return allow;
|
||||
return allow;
|
||||
}
|
||||
|
||||
void setrate(int rate)
|
||||
@@ -62,7 +62,7 @@ void abortconnect()
|
||||
}
|
||||
|
||||
void connects(char *servername, char *serverport, char *password)
|
||||
{
|
||||
{
|
||||
if(connpeer)
|
||||
{
|
||||
conoutf("aborting connection attempt");
|
||||
@@ -97,13 +97,13 @@ void connects(char *servername, char *serverport, char *password)
|
||||
|
||||
if(clienthost)
|
||||
{
|
||||
connpeer = enet_host_connect(clienthost, &address, 3);
|
||||
connpeer = enet_host_connect(clienthost, &address, 3);
|
||||
enet_host_flush(clienthost);
|
||||
connmillis = lastmillis;
|
||||
connattempts = 0;
|
||||
if(!m_mp(gamemode)) gamemode = GMODE_TEAMDEATHMATCH;
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
conoutf("\f3could not connect to server");
|
||||
clientpassword[0] = '\0';
|
||||
@@ -120,7 +120,7 @@ void connectadmin(char *servername, char *serverport, char *password)
|
||||
void lanconnect()
|
||||
{
|
||||
connects(0);
|
||||
}
|
||||
}
|
||||
|
||||
void disconnect(int onlyclean, int async)
|
||||
{
|
||||
@@ -182,8 +182,8 @@ void trydisconnect()
|
||||
disconnect(0, !discmillis);
|
||||
}
|
||||
|
||||
void toserver(char *text)
|
||||
{
|
||||
void toserver(char *text)
|
||||
{
|
||||
bool toteam = text && text[0] == '%' && m_teammode;
|
||||
if(!toteam && text[0] == '%' && strlen(text) > 1) text++; // convert team-text to normal-text if no team-mode is active
|
||||
if(toteam) text++;
|
||||
@@ -267,7 +267,7 @@ void c2sinfo(playerent *d) // send update to the server
|
||||
{
|
||||
if(d->clientnum<0) return; // we haven't had a welcome message from the server yet
|
||||
if(lastmillis-lastupdate<40) return; // don't update faster than 25fps
|
||||
|
||||
|
||||
if(d->state==CS_ALIVE || d->state==CS_EDITING)
|
||||
{
|
||||
ENetPacket *packet = enet_packet_create(NULL, 100, 0);
|
||||
@@ -295,7 +295,7 @@ void c2sinfo(playerent *d) // send update to the server
|
||||
{
|
||||
ENetPacket *packet = enet_packet_create (NULL, MAXTRANS, 0);
|
||||
ucharbuf p(packet->data, packet->dataLength);
|
||||
|
||||
|
||||
if(!c2sinit) // tell other clients who I am
|
||||
{
|
||||
packet->flags = ENET_PACKET_FLAG_RELIABLE;
|
||||
@@ -311,6 +311,9 @@ void c2sinfo(playerent *d) // send update to the server
|
||||
putint(p, SV_ITEMLIST);
|
||||
if(!m_noitems) putitems(p);
|
||||
putint(p, -1);
|
||||
putint(p, SV_SPAWNLIST);
|
||||
putint(p, maploaded);
|
||||
if(maploaded > 0) loopi(3) putint(p, numspawn[i]);
|
||||
senditemstoserver = false;
|
||||
}
|
||||
int i = 0;
|
||||
@@ -383,7 +386,7 @@ void gets2c() // get updates from the server
|
||||
if(editmode) toggleedit();
|
||||
sendintro();
|
||||
break;
|
||||
|
||||
|
||||
case ENET_EVENT_TYPE_RECEIVE:
|
||||
if(discmillis) conoutf("attempting to disconnect...");
|
||||
else localservertoclient(event.channelID, event.packet->data, (int)event.packet->dataLength);
|
||||
@@ -434,19 +437,19 @@ void sendmap(char *mapname)
|
||||
{
|
||||
save_world(mapname);
|
||||
changemap(mapname); // FIXME!!
|
||||
}
|
||||
}
|
||||
else mapname = getclientmap();
|
||||
if(securemapcheck(mapname)) return;
|
||||
|
||||
|
||||
int mapsize, cfgsize;
|
||||
uchar *mapdata = readmap(path(mapname), &mapsize);
|
||||
uchar *mapdata = readmap(path(mapname), &mapsize);
|
||||
uchar *cfgdata = readmcfg(path(mapname), &cfgsize);
|
||||
if(!mapdata) return;
|
||||
if(!cfgdata) cfgsize = 0;
|
||||
|
||||
ENetPacket *packet = enet_packet_create(NULL, MAXTRANS + mapsize + cfgsize, ENET_PACKET_FLAG_RELIABLE);
|
||||
ucharbuf p(packet->data, packet->dataLength);
|
||||
|
||||
|
||||
putint(p, SV_SENDMAP);
|
||||
sendstring(mapname, p);
|
||||
putint(p, mapsize);
|
||||
@@ -459,14 +462,14 @@ void sendmap(char *mapname)
|
||||
enet_packet_destroy(packet);
|
||||
return;
|
||||
}
|
||||
p.put(mapdata, mapsize);
|
||||
delete[] mapdata;
|
||||
if(cfgsize)
|
||||
p.put(mapdata, mapsize);
|
||||
delete[] mapdata;
|
||||
if(cfgsize)
|
||||
{
|
||||
p.put(cfgdata, cfgsize);
|
||||
delete[] cfgdata;
|
||||
}
|
||||
|
||||
|
||||
enet_packet_resize(packet, p.length());
|
||||
sendpackettoserv(2, packet);
|
||||
conoutf("sending map %s to server...", mapname);
|
||||
|
||||
@@ -47,6 +47,7 @@ bool duplicatename(playerent *d, char *name = NULL)
|
||||
{
|
||||
if(!name) name = d->name;
|
||||
if(d!=player1 && !strcmp(name, player1->name)) return true;
|
||||
if(!strcmp(name, "you")) return true;
|
||||
loopv(players) if(players[i] && d!=players[i] && !strcmp(name, players[i]->name)) return true;
|
||||
return false;
|
||||
}
|
||||
@@ -333,13 +334,17 @@ float nearestenemy(vec place, char *team)
|
||||
else return nearestenemydist;
|
||||
}
|
||||
|
||||
void findplayerstart(playerent *d, bool mapcenter)
|
||||
void findplayerstart(playerent *d, bool mapcenter, int arenaspawn)
|
||||
{
|
||||
int r = fixspawn-->0 ? 4 : rnd(10)+1;
|
||||
entity *e = NULL;
|
||||
if(!mapcenter)
|
||||
{
|
||||
if(m_teammode || m_arena)
|
||||
if(m_arena && arenaspawn > -1)
|
||||
{
|
||||
; // unfinished
|
||||
}
|
||||
else if(m_teammode || m_arena)
|
||||
{
|
||||
int type = m_teammode ? team_int(d->team) : 100;
|
||||
loopi(r) spawncycle = findentity(PLAYERSTART, spawncycle+1, type);
|
||||
@@ -600,9 +605,9 @@ void startmap(const char *name) // called just after a map load
|
||||
intermission = false;
|
||||
minutesremaining = -1;
|
||||
if(*clientmap) conoutf("game mode is \"%s\"", modestr(gamemode, modeacronyms > 0));
|
||||
|
||||
|
||||
// run once
|
||||
if(firstrun)
|
||||
if(firstrun)
|
||||
{
|
||||
execfile("config/firstrun.cfg");
|
||||
firstrun = false;
|
||||
@@ -693,7 +698,7 @@ COMMAND(dropflag, ARG_NONE);
|
||||
|
||||
char *votestring(int type, char *arg1, char *arg2)
|
||||
{
|
||||
const char *msgs[] = { "kick player %s", "ban player %s", "remove all bans", "set mastermode to %s", "%s autoteam", "force player %s to the enemy team", "give admin to player %s", "load map %s in mode %s", "%s demo recording for the next match", "stop demo recording", "clear all demos", "set server description to '%s'"};
|
||||
const char *msgs[] = { "kick player %s", "ban player %s", "remove all bans", "set mastermode to %s", "%s autoteam", "force player %s to the enemy team", "give admin to player %s", "load map %s in mode %s", "%s demo recording for the next match", "stop demo recording", "clear all demos", "set server description to '%s'", "shuffle teams"};
|
||||
const char *msg = msgs[type];
|
||||
char *out = newstring(_MAXDEFSTR);
|
||||
out[_MAXDEFSTR] = '\0';
|
||||
@@ -767,6 +772,7 @@ void callvote(int type, char *arg1, char *arg2)
|
||||
break;
|
||||
case SA_STOPDEMO:
|
||||
case SA_REMBANS:
|
||||
case SA_SHUFFLETEAMS:
|
||||
break;
|
||||
default:
|
||||
putint(p, atoi(arg1));
|
||||
|
||||
@@ -233,6 +233,12 @@ void parsemessages(int cn, playerent *d, ucharbuf &p)
|
||||
break;
|
||||
}
|
||||
|
||||
case SV_SPAWNLIST:
|
||||
{
|
||||
if(getint(p) > 0) loopi(3) getint(p);
|
||||
break;
|
||||
}
|
||||
|
||||
case SV_MAPRELOAD: // server requests next map
|
||||
{
|
||||
getint(p);
|
||||
@@ -312,10 +318,11 @@ void parsemessages(int cn, playerent *d, ucharbuf &p)
|
||||
player1->armour = getint(p);
|
||||
player1->setprimary(getint(p));
|
||||
player1->selectweapon(getint(p));
|
||||
int arenaspawn = getint(p);
|
||||
loopi(NUMGUNS) player1->ammo[i] = getint(p);
|
||||
loopi(NUMGUNS) player1->mag[i] = getint(p);
|
||||
player1->state = CS_ALIVE;
|
||||
findplayerstart(player1);
|
||||
findplayerstart(player1, false, arenaspawn);
|
||||
if(player1->skin!=player1->nextskin) setskin(player1, player1->nextskin);
|
||||
if(m_arena)
|
||||
{
|
||||
@@ -606,18 +613,39 @@ void parsemessages(int cn, playerent *d, ucharbuf &p)
|
||||
break;
|
||||
}
|
||||
|
||||
case SV_FORCETEAM:
|
||||
{
|
||||
case SV_FORCETEAM:
|
||||
{
|
||||
int team = getint(p);
|
||||
bool respawn = getint(p) == 1;
|
||||
changeteam(team, respawn);
|
||||
break;
|
||||
}
|
||||
changeteam(team, respawn);
|
||||
break;
|
||||
}
|
||||
|
||||
case SV_FORCENOTIFY:
|
||||
{
|
||||
int fpl = getint(p);
|
||||
int fnt = getint(p);
|
||||
playerent *d = getclient(fpl);
|
||||
bool you = fpl == player1->clientnum; // sound?
|
||||
bool et = team_int(player1->team) != fnt;
|
||||
conoutf("the server forced %s to %s team", you ? "you" : d ? colorname(d) : "", et ? "the enemy" : "your");
|
||||
break;
|
||||
}
|
||||
|
||||
case SV_AUTOTEAM:
|
||||
autoteambalance = 1 == getint(p);
|
||||
{
|
||||
int na = getint(p);
|
||||
switch(na)
|
||||
{
|
||||
case AT_ENABLED:
|
||||
case AT_DISABLED:
|
||||
autoteambalance = na == AT_ENABLED;
|
||||
break;
|
||||
case AT_SHUFFLE: // sound?
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
case SV_CALLVOTE:
|
||||
{
|
||||
int type = getint(p);
|
||||
|
||||
@@ -44,6 +44,7 @@ extern int maxclients;
|
||||
extern hashtable<char *, enet_uint32> mapinfo;
|
||||
extern bool hasTE, hasMT, hasMDA;
|
||||
extern int hwtexsize;
|
||||
extern int numspawn[3], maploaded;
|
||||
|
||||
#include "protos.h" // external function decls
|
||||
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ enum // static entity types
|
||||
{
|
||||
NOTUSED = 0, // entity slot not in use in map
|
||||
LIGHT, // lightsource, attr1 = radius, attr2 = intensity
|
||||
PLAYERSTART, // attr1 = angle
|
||||
PLAYERSTART, // attr1 = angle, attr2 = team
|
||||
I_CLIPS, I_AMMO,I_GRENADE,
|
||||
I_HEALTH, I_ARMOUR, I_AKIMBO,
|
||||
MAPMODEL, // attr1 = angle, attr2 = idx
|
||||
|
||||
@@ -36,7 +36,7 @@ void putuint(ucharbuf &p, int n)
|
||||
p.put(0x80 | (n & 0x7F));
|
||||
p.put(n >> 7);
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
p.put(0x80 | (n & 0x7F));
|
||||
p.put(0x80 | ((n >> 7) & 0x7F));
|
||||
@@ -96,7 +96,7 @@ void filtertext(char *dst, const char *src, bool whitespace, int len)
|
||||
const char *modefullnames[] =
|
||||
{
|
||||
"team deathmatch", "coopedit", "deathmatch", "survivor",
|
||||
"team survivor", "ctf", "pistol frenzy", "bot team deathmatch", "bot deathmatch", "last swiss standing",
|
||||
"team survivor", "ctf", "pistol frenzy", "bot team deathmatch", "bot deathmatch", "last swiss standing",
|
||||
"one shot, one kill", "team one shot, one kill", "bot one shot, one kill"
|
||||
};
|
||||
|
||||
@@ -114,11 +114,11 @@ const char *modestr(int n, bool acronyms) { return acronyms ? acronymmodestr (n)
|
||||
const char *voteerrorstr(int n) { return (n>=0 && (size_t)n < sizeof(voteerrors)/sizeof(voteerrors[0])) ? voteerrors[n] : "unknown"; }
|
||||
|
||||
char msgsizesl[] = // size inclusive message token, 0 for variable or not-checked sizes
|
||||
{
|
||||
{
|
||||
SV_INITS2C, 5, SV_INITC2S, 0, SV_POS, 0, SV_TEXT, 0, SV_TEAMTEXT, 0, SV_SOUND, 2, SV_VOICECOM, 2, SV_VOICECOMTEAM, 2, SV_CDIS, 2,
|
||||
SV_SHOOT, 0, SV_EXPLODE, 0, SV_SUICIDE, 1, SV_AKIMBO, 2, SV_RELOAD, 3,
|
||||
SV_GIBDIED, 4, SV_DIED, 4, SV_GIBDAMAGE, 6, SV_DAMAGE, 6, SV_HITPUSH, 6, SV_SHOTFX, 9, SV_THROWNADE, 8,
|
||||
SV_TRYSPAWN, 1, SV_SPAWNSTATE, 22, SV_SPAWN, 3, SV_FORCEDEATH, 2, SV_RESUME, 0,
|
||||
SV_TRYSPAWN, 1, SV_SPAWNSTATE, 23, SV_SPAWN, 3, SV_FORCEDEATH, 2, SV_RESUME, 0,
|
||||
SV_TIMEUP, 2, SV_EDITENT, 10, SV_MAPRELOAD, 2, SV_NEXTMAP, 0, SV_ITEMACC, 2,
|
||||
SV_MAPCHANGE, 0, SV_ITEMSPAWN, 2, SV_ITEMPICKUP, 2,
|
||||
SV_PING, 2, SV_PONG, 2, SV_CLIENTPING, 2, SV_GAMEMODE, 2,
|
||||
@@ -127,14 +127,15 @@ char msgsizesl[] = // size inclusive message token, 0 for variable
|
||||
SV_MODELSKIN, 2,
|
||||
SV_FLAGPICKUP, 2, SV_FLAGDROP, 2, SV_FLAGRETURN, 2, SV_FLAGSCORE, 2, SV_FLAGRESET, 2, SV_FLAGINFO, 0, SV_FLAGS, 2,
|
||||
SV_ARENAWIN, 2,
|
||||
SV_SETADMIN, 0, SV_SERVOPINFO, 3,
|
||||
SV_SETADMIN, 0, SV_SERVOPINFO, 3,
|
||||
SV_CALLVOTE, 0, SV_CALLVOTESUC, 1, SV_CALLVOTEERR, 2, SV_VOTE, 2, SV_VOTERESULT, 2,
|
||||
SV_FORCETEAM, 2, SV_AUTOTEAM, 2, SV_CHANGETEAM, 1,
|
||||
SV_FORCETEAM, 2, SV_AUTOTEAM, 2, SV_CHANGETEAM, 1,
|
||||
SV_WHOIS, 2, SV_WHOISINFO, 3,
|
||||
SV_LISTDEMOS, 1, SV_SENDDEMOLIST, 0, SV_GETDEMO, 2, SV_SENDDEMO, 0, SV_DEMOPLAYBACK, 2,
|
||||
SV_CONNECT, 0,
|
||||
SV_LISTDEMOS, 1, SV_SENDDEMOLIST, 0, SV_GETDEMO, 2, SV_SENDDEMO, 0, SV_DEMOPLAYBACK, 2,
|
||||
SV_CONNECT, 0,
|
||||
SV_CLIENT, 0,
|
||||
SV_EXTENSION, 0,
|
||||
SV_SPAWNLIST, 0, SV_FORCENOTIFY, 3,
|
||||
-1
|
||||
};
|
||||
|
||||
|
||||
@@ -31,13 +31,15 @@ enum
|
||||
SV_LISTDEMOS, SV_SENDDEMOLIST, SV_GETDEMO, SV_SENDDEMO, SV_DEMOPLAYBACK,
|
||||
SV_CONNECT,
|
||||
SV_CLIENT,
|
||||
SV_EXTENSION
|
||||
SV_EXTENSION,
|
||||
SV_SPAWNLIST, SV_FORCENOTIFY
|
||||
};
|
||||
|
||||
enum { SA_KICK = 0, SA_BAN, SA_REMBANS, SA_MASTERMODE, SA_AUTOTEAM, SA_FORCETEAM, SA_GIVEADMIN, SA_MAP, SA_RECORDDEMO, SA_STOPDEMO, SA_CLEARDEMOS, SA_SERVERDESC, SA_NUM};
|
||||
enum { SA_KICK = 0, SA_BAN, SA_REMBANS, SA_MASTERMODE, SA_AUTOTEAM, SA_FORCETEAM, SA_GIVEADMIN, SA_MAP, SA_RECORDDEMO, SA_STOPDEMO, SA_CLEARDEMOS, SA_SERVERDESC, SA_SHUFFLETEAMS, SA_NUM};
|
||||
enum { VOTE_NEUTRAL = 0, VOTE_YES, VOTE_NO, VOTE_NUM };
|
||||
enum { VOTEE_DISABLED = 0, VOTEE_CUR, VOTEE_MUL, VOTEE_MAX, VOTEE_AREA, VOTEE_PERMISSION, VOTEE_INVALID, VOTEE_NUM };
|
||||
enum { MM_OPEN, MM_PRIVATE, MM_NUM };
|
||||
enum { AT_DISABLED = 0, AT_ENABLED, AT_SHUFFLE, AT_NUM };
|
||||
|
||||
#define DMF 16.0f
|
||||
#define DNF 100.0f
|
||||
|
||||
+1
-1
@@ -312,7 +312,7 @@ extern void flaginbase(int flag, int action, int act);
|
||||
extern void flagmsg(int flag, int action);
|
||||
extern void arenarespawn();
|
||||
extern bool tryrespawn();
|
||||
extern void findplayerstart(playerent *d, bool mapcenter=false);
|
||||
extern void findplayerstart(playerent *d, bool mapcenter=false, int arenaspawn=-1);
|
||||
extern void serveropcommand(int cmd, int arg1);
|
||||
extern void refreshsopmenu(void *menu, bool init);
|
||||
extern char *colorname(playerent *d, int num = 0, char *name = NULL, const char *prefix = "");
|
||||
|
||||
+148
-15
@@ -15,6 +15,7 @@
|
||||
|
||||
void resetmap(const char *newname, int newmode, int newtime = -1, bool notify = true);
|
||||
void disconnect_client(int n, int reason = -1);
|
||||
bool refillteams(bool now = false, bool notify = true);
|
||||
|
||||
servercontroller *svcctrl = NULL;
|
||||
struct log *logger = NULL;
|
||||
@@ -217,6 +218,8 @@ struct client // server side version of "dynent" type
|
||||
vector<uchar> position, messages;
|
||||
string lastsaytext;
|
||||
int saychars, lastsay, spamcount;
|
||||
int at3_score, at3_lastforce;
|
||||
bool at3_dontmove;
|
||||
|
||||
gameevent &addevent()
|
||||
{
|
||||
@@ -232,6 +235,7 @@ struct client // server side version of "dynent" type
|
||||
events.setsizenodelete(0);
|
||||
timesync = false;
|
||||
lastevent = 0;
|
||||
at3_lastforce = 0;
|
||||
}
|
||||
|
||||
void reset()
|
||||
@@ -288,7 +292,7 @@ void cleanworldstate(ENetPacket *packet)
|
||||
}
|
||||
}
|
||||
|
||||
int bsend = 0, brec = 0, laststatus = 0, servmillis = 0;
|
||||
int bsend = 0, brec = 0, laststatus = 0, servmillis = 0, lastfillup = 0;
|
||||
|
||||
void recordpacket(int chan, void *data, int len);
|
||||
|
||||
@@ -454,6 +458,7 @@ struct server_entity // server side version of "entity" type
|
||||
vector<server_entity> sents;
|
||||
|
||||
bool notgotitems = true; // true when map has changed and waiting for clients to send item
|
||||
int clnumspawn[3];
|
||||
|
||||
// allows the gamemode macros to work with the server mode
|
||||
#define gamemode smode
|
||||
@@ -542,9 +547,9 @@ void sendspawn(client *c)
|
||||
{
|
||||
clientstate &gs = c->state;
|
||||
spawnstate(c);
|
||||
sendf(c->clientnum, 1, "ri6vv", SV_SPAWNSTATE, gs.lifesequence,
|
||||
sendf(c->clientnum, 1, "ri7vv", SV_SPAWNSTATE, gs.lifesequence,
|
||||
gs.health, gs.armour,
|
||||
gs.primary, gs.gunselect,
|
||||
gs.primary, gs.gunselect, -1, // unfinished
|
||||
NUMGUNS, gs.ammo, NUMGUNS, gs.mag);
|
||||
gs.lastspawn = gamemillis;
|
||||
}
|
||||
@@ -954,6 +959,7 @@ void arenacheck()
|
||||
if(!dead) return;
|
||||
sendf(-1, 1, "ri2", SV_ARENAWIN, !alive ? -1 : alive->clientnum);
|
||||
arenaround = gamemillis+5000;
|
||||
if (autoteam && m_teammode) refillteams(true);
|
||||
}
|
||||
|
||||
#define SPAMREPEATINTERVAL 15 // detect doubled lines only if interval < 15 seconds
|
||||
@@ -1275,23 +1281,128 @@ void resetvotes()
|
||||
loopv(clients) clients[i]->vote = VOTE_NEUTRAL;
|
||||
}
|
||||
|
||||
void forceteam(int client, int team, bool respawn)
|
||||
void forceteam(int client, int team, bool respawn, bool notify = false)
|
||||
{
|
||||
if(!valid_client(client) || team < 0 || team > 1) return;
|
||||
sendf(client, 1, "riii", SV_FORCETEAM, team, respawn ? 1 : 0);
|
||||
if(notify) sendf(-1, 1, "riii", SV_FORCENOTIFY, client, team);
|
||||
}
|
||||
|
||||
void calcscores()
|
||||
{
|
||||
loopv(clients) if (clients[i]->type!=ST_EMPTY)
|
||||
{
|
||||
clients[i]->at3_score = clients[i]->state.frags * 100 / (clients[i]->state.deaths ? clients[i]->state.deaths : 1)
|
||||
+ clients[i]->state.flagscore * (33 + 33 * (clients[i]->state.flagscore < 3));
|
||||
}
|
||||
}
|
||||
|
||||
int cmpsc(const void *a, const void * b) { return clients[*((int *)a)]->at3_score - clients[*((int *)b)]->at3_score; }
|
||||
|
||||
void shuffleteams(bool respawn = true)
|
||||
{
|
||||
int numplayers = numclients();
|
||||
int teamsize[2] = {0, 0};
|
||||
loopv(clients) if(clients[i]->type!=ST_EMPTY)
|
||||
{
|
||||
int team = rnd(2);
|
||||
if(teamsize[team] >= numplayers/2) team = team_opposite(team);
|
||||
forceteam(i, team, respawn);
|
||||
teamsize[team]++;
|
||||
}
|
||||
int numplayers = numclients();
|
||||
if(gamemillis < 2 * 60 *1000)
|
||||
{ // random
|
||||
int teamsize[2] = {0, 0};
|
||||
loopv(clients) if(clients[i]->type!=ST_EMPTY)
|
||||
{
|
||||
int team = rnd(2);
|
||||
if(teamsize[team] >= numplayers/2) team = team_opposite(team);
|
||||
forceteam(i, team, respawn);
|
||||
teamsize[team]++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // skill sorted
|
||||
calcscores();
|
||||
int *list = new int[numplayers], t = rnd(2), j = 0;
|
||||
loopv(clients) if(clients[i]->type!=ST_EMPTY) list[j++] = i;
|
||||
qsort(list, numplayers, sizeof(int), cmpsc);
|
||||
loopi(numplayers)
|
||||
{
|
||||
forceteam(list[i], t, respawn);
|
||||
t = !t;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool refillteams(bool now, bool notify) // force only minimal amounts of players
|
||||
{
|
||||
static int lasttime_eventeams = 0;
|
||||
int teamsize[2] = {0, 0}, teamscore[2] = {0, 0}, moveable[2] = {0, 0};
|
||||
bool switched = false;
|
||||
|
||||
calcscores();
|
||||
loopv(clients) if (clients[i]->type!=ST_EMPTY) // playerlist stocktaking
|
||||
{
|
||||
clients[i]->at3_dontmove = true;
|
||||
if(!clients[i]->awaitdisc)
|
||||
{
|
||||
int t = 0;
|
||||
if(!strcmp(clients[i]->team, "CLA") || t++ || !strcmp(clients[i]->team, "RVSF")) // need exact teams here
|
||||
{
|
||||
teamsize[t]++;
|
||||
teamscore[t] += clients[i]->at3_score;
|
||||
if(!m_ctf || !((sflaginfos[0].state==CTFF_STOLEN && sflaginfos[0].actor_cn==i) ||
|
||||
(sflaginfos[1].state==CTFF_STOLEN && sflaginfos[1].actor_cn==i) ))
|
||||
{
|
||||
clients[i]->at3_dontmove = false;
|
||||
moveable[t]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
int bigteam = teamsize[1] > teamsize[0];
|
||||
int allplayers = teamsize[0] + teamsize[1];
|
||||
int diffnum = teamsize[bigteam] - teamsize[!bigteam];
|
||||
int diffscore = teamscore[bigteam] - teamscore[!bigteam];
|
||||
if(lasttime_eventeams > gamemillis) lasttime_eventeams = 0;
|
||||
if(diffnum > 1)
|
||||
{
|
||||
if(now || gamemillis - lasttime_eventeams > 8000 + allplayers * 1000 || diffnum > 2 + allplayers / 10)
|
||||
{
|
||||
// time to even out teams
|
||||
loopv(clients) if (clients[i]->type!=ST_EMPTY && team_int(clients[i]->team) != bigteam) clients[i]->at3_dontmove = true; // dont move small team players
|
||||
while(diffnum > 1 && moveable[bigteam] > 0)
|
||||
{
|
||||
// pick best fitting cn
|
||||
string atlog, buf; // debug logging - will be removed
|
||||
int pick = -1;
|
||||
int bestfit = 1e9;
|
||||
int targetscore = diffscore / (diffnum & ~1);
|
||||
s_sprintf(atlog)("at-target: %d, ", targetscore);
|
||||
loopv(clients) if (clients[i]->type!=ST_EMPTY && !clients[i]->at3_dontmove) // try all still movable players
|
||||
{
|
||||
int fit = targetscore - clients[i]->at3_score;
|
||||
if(fit < 0 ) fit = -(fit * 15) / 10; // avoid too good players
|
||||
int forcedelay = clients[i]->at3_lastforce ? 1000 - (gamemillis - clients[i]->at3_lastforce) / 5 * 60 : 0;
|
||||
if(forcedelay > 0) fit += (fit * forcedelay) / 600; // avoid lately forced players
|
||||
if(fit < bestfit + fit * rnd(100) / 400) // search 'almost' best fit
|
||||
{
|
||||
bestfit = fit;
|
||||
pick = i;
|
||||
}
|
||||
s_sprintf(buf)("%d:%d ", i, fit); s_strcat(atlog, buf);
|
||||
}
|
||||
if(pick < 0) break; // should really never happen
|
||||
// move picked player
|
||||
forceteam(pick, !bigteam, false);
|
||||
|
||||
diffnum -= 2;
|
||||
diffscore -= 2 * clients[pick]->at3_score;
|
||||
moveable[bigteam]--;
|
||||
clients[pick]->at3_dontmove = true;
|
||||
clients[pick]->at3_lastforce = gamemillis; // try not to force this player again for the next 5 minutes
|
||||
switched = true;
|
||||
s_sprintf(buf)(" pick:%d", pick); s_strcat(atlog, buf);
|
||||
logger->writeline(log::info,"%s", atlog);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(diffnum < 2)
|
||||
lasttime_eventeams = gamemillis;
|
||||
return switched;
|
||||
}
|
||||
|
||||
bool mapavailable(const char *mapname);
|
||||
@@ -1316,8 +1427,10 @@ void resetmap(const char *newname, int newmode, int newtime, bool notify)
|
||||
mapreload = false;
|
||||
interm = 0;
|
||||
laststatus = servmillis-61*1000;
|
||||
lastfillup = servmillis;
|
||||
resetvotes();
|
||||
resetitems();
|
||||
loopi(3) clnumspawn[i] = 0;
|
||||
scores.setsize(0);
|
||||
ctfreset();
|
||||
if(notify)
|
||||
@@ -1330,7 +1443,13 @@ void resetmap(const char *newname, int newmode, int newtime, bool notify)
|
||||
if(notify)
|
||||
{
|
||||
// shuffle if previous mode wasn't a team-mode
|
||||
if(m_teammode && !lastteammode) shuffleteams(false);
|
||||
if(m_teammode)
|
||||
{
|
||||
if(!lastteammode)
|
||||
shuffleteams(false);
|
||||
else if (autoteam)
|
||||
refillteams(true, false);
|
||||
}
|
||||
// send spawns
|
||||
loopv(clients) if(clients[i]->type!=ST_EMPTY)
|
||||
{
|
||||
@@ -1604,7 +1723,7 @@ bool sendmapserv(int n, string mapname, int mapsize, int cfgsize, uchar *data)
|
||||
|
||||
ENetPacket *getmapserv(int n)
|
||||
{
|
||||
if(!copydata) return NULL;
|
||||
if(!copydata || !mapavailable(smapname)) return NULL;
|
||||
ENetPacket *packet = enet_packet_create(NULL, MAXTRANS + copysize, ENET_PACKET_FLAG_RELIABLE);
|
||||
ucharbuf p(packet->data, packet->dataLength);
|
||||
putint(p, SV_RECVMAP);
|
||||
@@ -1624,6 +1743,7 @@ void getservermap(void)
|
||||
int cgzsize, cfgsize;
|
||||
const char *name = behindpath(smapname); // no paths allowed here
|
||||
|
||||
if(!strcmp(name, behindpath(copyname))) return;
|
||||
s_sprintf(cgzname)(SERVERMAP_PATH "%s.cgz", name);
|
||||
path(cgzname);
|
||||
if(fileexists(cgzname, "r"))
|
||||
@@ -1714,6 +1834,7 @@ void welcomepacket(ucharbuf &p, int n)
|
||||
putint(p, gs.armour);
|
||||
putint(p, gs.primary);
|
||||
putint(p, gs.gunselect);
|
||||
putint(p, -1); // unfinished
|
||||
loopi(NUMGUNS) putint(p, gs.ammo[i]);
|
||||
loopi(NUMGUNS) putint(p, gs.mag[i]);
|
||||
gs.lastspawn = gamemillis;
|
||||
@@ -1926,6 +2047,13 @@ void process(ENetPacket *packet, int sender, int chan) // sender may be -1
|
||||
break;
|
||||
}
|
||||
|
||||
case SV_SPAWNLIST:
|
||||
{
|
||||
if(getint(p) > 0) loopi(3) clnumspawn[i] = getint(p);
|
||||
QUEUE_MSG;
|
||||
break;
|
||||
}
|
||||
|
||||
case SV_ITEMPICKUP:
|
||||
{
|
||||
int n = getint(p);
|
||||
@@ -2173,6 +2301,9 @@ void process(ENetPacket *packet, int sender, int chan) // sender may be -1
|
||||
case SA_AUTOTEAM:
|
||||
vi->action = new autoteamaction(getint(p) > 0);
|
||||
break;
|
||||
case SA_SHUFFLETEAMS:
|
||||
vi->action = new shuffleteamaction();
|
||||
break;
|
||||
case SA_FORCETEAM:
|
||||
vi->action = new forceteamaction(getint(p));
|
||||
break;
|
||||
@@ -2383,6 +2514,8 @@ void serverslice(uint timeout) // main server update, called from cube main lo
|
||||
|
||||
serverms(smode, numclients(), minremain, smapname, servmillis, serverhost->address.port);
|
||||
|
||||
if(autoteam && m_teammode && !m_arena && !interm && servmillis - lastfillup > 1000 && refillteams()) lastfillup = servmillis;
|
||||
|
||||
if(servmillis-laststatus>60*1000) // display bandwidth stats, useful for server ops
|
||||
{
|
||||
laststatus = servmillis;
|
||||
|
||||
@@ -123,8 +123,8 @@ struct autoteamaction : enableaction
|
||||
{
|
||||
void perform()
|
||||
{
|
||||
sendf(-1, 1, "ri2", SV_AUTOTEAM, (autoteam = enable) == 1 ? 1 : 0);
|
||||
if(m_teammode && enable) shuffleteams();
|
||||
sendf(-1, 1, "ri2", SV_AUTOTEAM, (autoteam = enable) == 1 ? AT_ENABLED : AT_DISABLED);
|
||||
if(m_teammode && enable) refillteams(true);
|
||||
}
|
||||
autoteamaction(bool enable) : enableaction(enable)
|
||||
{
|
||||
@@ -132,6 +132,20 @@ struct autoteamaction : enableaction
|
||||
}
|
||||
};
|
||||
|
||||
struct shuffleteamaction : serveraction
|
||||
{
|
||||
void perform()
|
||||
{
|
||||
sendf(-1, 1, "ri2", SV_AUTOTEAM, AT_SHUFFLE);
|
||||
shuffleteams();
|
||||
}
|
||||
bool isvalid() { return serveraction::isvalid() && m_teammode; }
|
||||
shuffleteamaction()
|
||||
{
|
||||
if(isvalid()) s_strcpy(desc, "shuffle teams");
|
||||
}
|
||||
};
|
||||
|
||||
struct recorddemoaction : enableaction
|
||||
{
|
||||
void perform() { demonextmatch = enable; }
|
||||
|
||||
@@ -144,6 +144,18 @@ const char *findfile(const char *filename, const char *mode)
|
||||
return filename;
|
||||
}
|
||||
|
||||
int getfilesize(const char *filename)
|
||||
{
|
||||
const char *found = findfile(filename, "rb");
|
||||
if(!found) return -1;
|
||||
FILE *fp = fopen(found, "rb");
|
||||
if(!fp) return -1;
|
||||
fseek(fp, 0, SEEK_END);
|
||||
int len = ftell(fp);
|
||||
fclose(fp);
|
||||
return len;
|
||||
}
|
||||
|
||||
FILE *openfile(const char *filename, const char *mode)
|
||||
{
|
||||
const char *found = findfile(filename, mode);
|
||||
|
||||
@@ -501,6 +501,7 @@ extern bool createdir(const char *path);
|
||||
extern void sethomedir(const char *dir);
|
||||
extern void addpackagedir(const char *dir);
|
||||
extern const char *findfile(const char *filename, const char *mode);
|
||||
extern int getfilesize(const char *filename);
|
||||
extern FILE *openfile(const char *filename, const char *mode);
|
||||
extern gzFile opengzfile(const char *filename, const char *mode);
|
||||
extern char *loadfile(const char *fn, int *size, const char *mode = NULL);
|
||||
|
||||
@@ -229,6 +229,7 @@ void save_world(char *mname)
|
||||
}
|
||||
|
||||
extern void preparectf(bool cleanonly = false);
|
||||
int numspawn[3], maploaded = 0;
|
||||
|
||||
bool load_world(char *mname) // still supports all map formats that have existed since the earliest cube betas!
|
||||
{
|
||||
@@ -237,6 +238,7 @@ bool load_world(char *mname) // still supports all map formats that have
|
||||
pruneundos();
|
||||
clearworldsounds();
|
||||
setnames(mname);
|
||||
maploaded = getfilesize(cgzname);
|
||||
gzFile f = opengzfile(cgzname, "rb9");
|
||||
if(!f) { conoutf("\f3could not read map %s", cgzname); return false; }
|
||||
loadingscreen();
|
||||
@@ -256,6 +258,7 @@ bool load_world(char *mname) // still supports all map formats that have
|
||||
hdr.waterlevel = -100000;
|
||||
}
|
||||
ents.setsize(0);
|
||||
loopi(3) numspawn[i] = 0;
|
||||
loopi(hdr.numents)
|
||||
{
|
||||
entity &e = ents.add();
|
||||
@@ -304,6 +307,13 @@ bool load_world(char *mname) // still supports all map formats that have
|
||||
e.type=NOTUSED;
|
||||
}
|
||||
}
|
||||
if(e.type == PLAYERSTART && (e.attr2 == 0 || e.attr2 == 1 || e.attr2 == 100))
|
||||
{
|
||||
if(e.attr2 == 100)
|
||||
numspawn[2]++;
|
||||
else
|
||||
numspawn[e.attr2]++;
|
||||
}
|
||||
}
|
||||
delete[] world;
|
||||
setupworld(hdr.sfactor);
|
||||
|
||||
Reference in New Issue
Block a user