home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2007 September / maximum-cd-2007-09.iso / Assets / data / AssaultCube_v0.93.exe / source / src / clientgame.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2007-06-05  |  18.5 KB  |  674 lines

  1. // clientgame.cpp: core game related stuff
  2.  
  3. #include "cube.h"
  4. #include "bot/bot.h"
  5.  
  6. int nextmode = 0;         // nextmode becomes gamemode after next map load
  7. VAR(gamemode, 1, 0, 0);
  8.  
  9. flaginfo flaginfos[2];
  10.  
  11. void mode(int n) 
  12. {
  13.     if(m_mp(n) || !multiplayer()) addmsg(SV_GAMEMODE, "ri", nextmode = n); 
  14. }
  15. COMMAND(mode, ARG_1INT);
  16.  
  17. bool intermission = false;
  18. bool autoteambalance = false;
  19.  
  20. playerent *player1 = newplayerent();          // our client
  21. vector<playerent *> players;                        // other clients
  22.  
  23. int lastmillis = 0;
  24. int curtime;
  25. string clientmap;
  26.  
  27. extern int framesinmap;
  28.  
  29. char *getclientmap() { return clientmap; }
  30.  
  31. extern bool c2sinit, senditemstoserver;
  32. extern int dblend;
  33.  
  34. void setskin(playerent *pl, uint skin)
  35.     if(!pl) return;
  36.     if(pl == player1) c2sinit=false;
  37.     const int maxskin[2] = { 3, 5 };
  38.     pl->skin = skin % (maxskin[team_int(pl->team)]+1);
  39. }
  40.  
  41. bool duplicatename(playerent *d, char *name = NULL)
  42. {
  43.     if(!name) name = d->name;
  44.     if(d!=player1 && !strcmp(name, player1->name)) return true;
  45.     loopv(players) if(players[i] && d!=players[i] && !strcmp(name, players[i]->name)) return true;
  46.     return false;
  47. }
  48.  
  49. char *colorname(playerent *d, int num, char *name, char *prefix)
  50. {
  51.     if(!name) name = d->name;
  52.     if(name[0] && !duplicatename(d, name)) return name;
  53.     static string cname[4];
  54.     s_sprintf(cname[num])("%s%s \fs\f6(%d)\fr", prefix, name, d->clientnum);
  55.     return cname[num];
  56. }
  57.  
  58. void newname(char *name) 
  59. {
  60.     if(name[0])
  61.     {
  62.         c2sinit = false; 
  63.         filtertext(player1->name, name, false, MAXNAMELEN);
  64.         if(!player1->name[0]) s_strcpy(player1->name, "unarmed");
  65.     }
  66.     else conoutf("your name is: %s", player1->name);
  67. }   
  68.     
  69. int smallerteam()
  70. {
  71.     int teamsize[2] = {0, 0};
  72.     loopv(players) if(players[i]) teamsize[team_int(players[i]->team)]++;
  73.     teamsize[team_int(player1->team)]++;
  74.     if(teamsize[0] == teamsize[1]) return -1;
  75.     return teamsize[0] < teamsize[1] ? 0 : 1;
  76. }
  77.     
  78. void changeteam(int team) // force team and respawn
  79. {
  80.     c2sinit = false;
  81.     if(m_ctf) tryflagdrop(NULL);
  82.     filtertext(player1->team, team_string(team), false, MAXTEAMLEN);
  83.     deathstate(player1);
  84. }
  85.  
  86. void newteam(char *name)
  87. {
  88.     if(name[0])
  89.     {
  90.         if(m_teammode)
  91.         {
  92.             if(!strcmp(name, player1->team)) return; // same team
  93.             if(!team_valid(name)) { conoutf("\f3\"%s\" is not a valid team name (try CLA or RVSF)", name); return; }
  94.  
  95.             bool checkteamsize =  autoteambalance && players.length() >= 1 && !m_botmode;
  96.             int freeteam = smallerteam();
  97.  
  98.             if(team_valid(name))
  99.             {
  100.                 int team = team_int(name);
  101.                 if(checkteamsize && team != freeteam)
  102.                 {
  103.                     conoutf("\f3the %s team is already full", name);
  104.                     return;
  105.                 }
  106.                 changeteam(team);
  107.             }
  108.             else changeteam(checkteamsize ? (uint)freeteam : rnd(2)); // random assignement
  109.         }
  110.     }
  111.     else conoutf("your team is: %s", player1->team);
  112. }
  113.  
  114. void newskin(int skin) { player1->nextskin = skin; }
  115.  
  116. COMMANDN(team, newteam, ARG_1STR);
  117. COMMANDN(name, newname, ARG_1STR);
  118. COMMANDN(skin, newskin, ARG_1INT);
  119.  
  120. extern void thrownade(playerent *d, const vec &vel, bounceent *p);
  121.  
  122. void deathstate(playerent *pl)
  123. {
  124.     pl->lastaction = lastmillis;
  125.     pl->attacking = false;
  126.     pl->state = CS_DEAD;
  127.     pl->pitch = 0;
  128.     pl->roll = 60;
  129.     pl->strafe = 0;
  130.     dblend = 0;
  131.     if(pl == player1 && pl->inhandnade) thrownade(pl, vec(0,0,0), pl->inhandnade);
  132. }
  133.  
  134. void spawnstate(playerent *d)              // reset player state not persistent accross spawns
  135. {
  136.     d->respawn();
  137.     //d->lastaction = lastmillis;
  138.     if(d==player1) 
  139.     {
  140.         gun_changed = true;
  141.         if(player1->skin!=player1->nextskin) setskin(player1, player1->nextskin);
  142.         setscope(false);
  143.     }
  144.     equip(d);
  145.     if(m_osok) d->health = 1;
  146. }
  147.     
  148. playerent *newplayerent()                 // create a new blank player
  149. {
  150.     playerent *d = new playerent;
  151.     d->lastupdate = lastmillis;
  152.     setskin(d, rnd(6));
  153.     spawnstate(d);
  154.     return d;
  155. }
  156.  
  157. botent *newbotent()                 // create a new blank player
  158. {
  159.     botent *d = new botent;
  160.     d->lastupdate = lastmillis;
  161.     setskin(d, rnd(6));
  162.     spawnstate(d);
  163.     loopv(players) if(i!=getclientnum() && !players[i])
  164.     {
  165.         players[i] = d;
  166.         d->clientnum = i;
  167.         return d;
  168.     }
  169.     if(players.length()==getclientnum()) players.add(NULL);
  170.     d->clientnum = players.length();
  171.     players.add(d);
  172.     return d;
  173. }
  174.  
  175. void freebotent(botent *d)
  176. {
  177.     loopv(players) if(players[i]==d)
  178.     {
  179.         DELETEP(players[i]);
  180.     }
  181. }
  182.  
  183.  
  184.  
  185. void respawnself()
  186. {
  187.     spawnplayer(player1);
  188.     showscores(false);
  189. }
  190.  
  191. void respawn()
  192. {
  193.     if(player1->state==CS_DEAD && lastmillis>player1->lastpain+(m_ctf ? 5000 : 2000))
  194.     { 
  195.         player1->attacking = false;
  196.         if(m_arena) { conoutf("waiting for new round to start..."); return; }
  197.         respawnself();
  198.         weaponswitch(player1->primary);
  199.         player1->lastaction -= WEAPONCHANGE_TIME/2;
  200.     }
  201. }
  202.  
  203. void arenacount(playerent *d, int &alive, int &dead, char *&lastteam, char *&lastname, bool &oneteam)
  204. {
  205.     if(d->state!=CS_DEAD)
  206.     {
  207.         alive++;
  208.         if(lastteam && strcmp(lastteam, d->team)) oneteam = false;
  209.         lastteam = d->team;
  210.         lastname = d->name;
  211.     }
  212.     else
  213.     {
  214.         dead++;
  215.     }
  216. }
  217.  
  218. void checkakimbo()
  219. {
  220.     if(player1->akimbo && player1->akimbomillis && player1->akimbomillis<=lastmillis)
  221.     {
  222.         player1->akimbo = 0;
  223.         player1->akimbomillis = 0;
  224.         player1->mag[GUN_PISTOL] = min(magsize(GUN_PISTOL), player1->mag[GUN_PISTOL]);
  225.         if(player1->gunselect==GUN_PISTOL) weaponswitch(GUN_PISTOL);
  226.         playsoundc(S_PUPOUT);
  227.     }
  228. }
  229.  
  230. void zapplayer(playerent *&d)
  231. {
  232.     DELETEP(d);
  233. }
  234.  
  235. extern int democlientnum;
  236.  
  237. void otherplayers()
  238. {
  239.     loopv(players) if(players[i] && players[i]->type==ENT_PLAYER && players[i]->state==CS_ALIVE)
  240.     {
  241.         const int lagtime = lastmillis-players[i]->lastupdate;
  242.         if(lagtime>1000)
  243.         {
  244.             players[i]->state = CS_LAGGED;
  245.             continue;
  246.         }
  247.         else if(!lagtime || intermission) continue;
  248.         if(!demoplayback || i!=democlientnum) moveplayer(players[i], 2, false);   // use physics to extrapolate player position
  249.     }
  250. }
  251.  
  252. struct scriptsleep { int wait; char *cmd; };
  253. vector<scriptsleep> sleeps;
  254.  
  255. void addsleep(char *msec, char *cmd) 
  256.     scriptsleep &s = sleeps.add();
  257.     s.wait = atoi(msec)+lastmillis;
  258.     s.cmd = newstring(cmd);
  259. }
  260.  
  261. COMMANDN(sleep, addsleep, ARG_2STR);
  262.  
  263. void updateworld(int curtime, int lastmillis)        // main game update loop
  264. {
  265.     loopv(sleeps) 
  266.     {
  267.         if(sleeps[i].wait && lastmillis > sleeps[i].wait) 
  268.         {
  269.             execute(sleeps[i].cmd);
  270.             delete[] sleeps[i].cmd;
  271.             sleeps.remove(i--); 
  272.         }
  273.     }
  274.     physicsframe();
  275.     checkakimbo();
  276.     checkweaponswitch();
  277.     //if(m_arena) arenarespawn();
  278.     //arenarespawn();
  279.     moveprojectiles((float)curtime);
  280.     demoplaybackstep();
  281.     if(!demoplayback)
  282.     {
  283.         if(getclientnum()>=0) shoot(player1, worldpos);     // only shoot when connected to server
  284.         gets2c();           // do this first, so we have most accurate information when our player moves
  285.     }
  286.     movebounceents();
  287.     otherplayers();
  288.     if(!demoplayback)
  289.     {
  290.         //monsterthink();
  291.         
  292.         // Added by Rick: let bots think
  293.         if(m_botmode) BotManager.Think();            
  294.         
  295.         //put game mode extra call here
  296.         if(player1->state==CS_DEAD)
  297.         {
  298.             if(lastmillis-player1->lastaction<2000)
  299.             {
  300.                 player1->move = player1->strafe = 0;
  301.                 moveplayer(player1, 10, false);
  302.             }
  303.         }
  304.         else if(!intermission)
  305.         {
  306.             moveplayer(player1, 20, true);
  307.             checkitems(player1);
  308.         }
  309.         c2sinfo(player1);   // do this last, to reduce the effective frame lag
  310.     }
  311. }
  312.  
  313. #define SECURESPAWNDIST 15
  314. int spawncycle = -1;
  315. int fixspawn = 2;
  316.  
  317. // returns -2 for a free place, else dist to the nearest enemy
  318. float nearestenemy(vec place, char *team)
  319. {
  320.     float nearestenemydist = -1;
  321.     loopv(players)
  322.     {
  323.         playerent *other = players[i];
  324.         if(!other || isteam(team, other->team)) continue;
  325.         float dist = place.dist(other->o);
  326.         if(dist < nearestenemydist || nearestenemydist == -1) nearestenemydist = dist;
  327.     }
  328.     if(nearestenemydist >= SECURESPAWNDIST || nearestenemydist == -1) return -2;
  329.     else return nearestenemydist;
  330. }
  331.  
  332. int findplayerstart(playerent *d)
  333. {
  334.     int r = fixspawn-->0 ? 4 : rnd(10)+1;
  335.  
  336.     if(m_teammode) loopi(r) spawncycle = findentity(PLAYERSTART, spawncycle+1, team_int(d->team));
  337.     else if(m_arena) loopi(r) spawncycle = findentity(PLAYERSTART, spawncycle+1, 100);
  338.     else
  339.     {
  340.         int bestent = -1;
  341.         float bestdist = -1;
  342.  
  343.         loopi(r)
  344.         {
  345.             spawncycle = findentity(PLAYERSTART, spawncycle+1);
  346.             if(spawncycle < 0 || spawncycle >= ents.length()) continue;
  347.             float dist = nearestenemy(vec(ents[spawncycle].x, ents[spawncycle].y, ents[spawncycle].z), d->team);
  348.             if(dist == -2 || (bestdist != -2 && dist > bestdist) || bestent == -1) { bestent = spawncycle; bestdist = dist; }
  349.         }
  350.  
  351.         return bestent;
  352.     }
  353.     return spawncycle;
  354. }
  355.  
  356. void spawnplayer(playerent *d)   // place at random spawn
  357. {
  358.     int e = findplayerstart(d);
  359.     if(e!=-1)
  360.     {
  361.         d->o.x = ents[e].x;
  362.         d->o.y = ents[e].y;
  363.         d->o.z = ents[e].z;
  364.         d->yaw = ents[e].attr1;
  365.         d->pitch = 0;
  366.         d->roll = 0;
  367.     }
  368.     else
  369.     {
  370.         d->o.x = d->o.y = (float)ssize/2;
  371.         d->o.z = 4;
  372.     }
  373.  
  374.     entinmap(d);
  375.     spawnstate(d);
  376.     d->state = CS_ALIVE;
  377. }
  378.  
  379. void showteamkill() { player1->lastteamkill = lastmillis; }
  380.  
  381. // damage arriving from the network, monsters, yourself, all ends up here.
  382.  
  383. void dodamage(int damage, int actor, playerent *act, bool gib, playerent *pl)
  384. {   
  385.     if(!act) return;
  386.     if(pl->state!=CS_ALIVE || editmode || intermission) return;
  387.     if(pl==player1)
  388.     {
  389.         damageblend(damage);
  390.         demoblend(damage);
  391.     }
  392.     pl->lastpain = lastmillis;
  393.     int ad = damage*30/100; // let armour absorb when possible
  394.     if(ad>pl->armour) ad = pl->armour;
  395.     pl->armour -= ad;
  396.     damage -= ad;
  397.     float droll = damage/0.5f;
  398.     pl->roll += pl->roll>0 ? droll : (pl->roll<0 ? -droll : (rnd(2) ? droll : -droll));  // give player a kick depending on amount of damage
  399.     if((pl->health -= damage)<=0)
  400.     {
  401.         s_sprintfd(death)("%s", gib ? "gibbed" : "fragged");
  402.         if(pl->type==ENT_BOT)
  403.         {
  404.             if(pl==act) 
  405.             { 
  406.                 --pl->frags; 
  407.                 conoutf("\f2%s suicided", colorname(pl)); 
  408.             }
  409.             else if(isteam(pl->team, act->team))
  410.             {
  411.                 --act->frags; 
  412.                 conoutf("\f2%s %s %s teammate (%s)", act==player1 ? "you" : colorname(act, 0), death, act==player1 ? "a" : "his", colorname(pl, 1));
  413.                 if(act==player1) showteamkill();
  414.             }
  415.             else
  416.             {
  417.                 act->frags += gib ? 2 : 1;
  418.                 conoutf("\f2%s %s %s", act==player1 ? "you" : colorname(act, 0), death, colorname(pl, 1));
  419.             }
  420.         }
  421.         else if(act==pl)
  422.         {
  423.             actor = getclientnum();
  424.             conoutf("\f2you suicided!");
  425.             addmsg(SV_FRAGS, "ri", --pl->frags);
  426.         }
  427.         else if(act)
  428.         {
  429.             if(isteam(act->team, player1->team)) conoutf("\f2you got %s by a teammate (%s)", death, colorname(act));
  430.             else conoutf("\f2you got %s by %s", death, colorname(act));
  431.         }
  432.         if(pl==player1) 
  433.         {
  434.             showscores(true);
  435.             setscope(false);
  436.             addmsg(gib ? SV_GIBDIED : SV_DIED, "ri", actor);
  437.             if(m_ctf) tryflagdrop(act && isteam(act->team, player1->team));
  438.         }
  439.         deathstate(pl);
  440.         pl->lifesequence++;
  441.         playsound(S_DIE1+rnd(2), pl!=player1 ? &pl->o : NULL);
  442.         if(act && act->gunselect == GUN_SNIPER && gib) playsound(S_HEADSHOT);
  443.         if(gib) addgib(pl);
  444.         if(pl!=player1 || act->type==ENT_BOT) act->frags += gib ? 2 : 1;
  445.     }
  446.     else
  447.     {
  448.         playsound(S_PAIN6, pl!=player1 ? &pl->o : NULL);
  449.     }
  450. }
  451.  
  452. VAR(minutesremaining, 1, 0, 0);
  453.  
  454. void timeupdate(int timeremain)
  455. {
  456.     minutesremaining = timeremain;
  457.     if(!timeremain)
  458.     {
  459.         intermission = true;
  460.         player1->attacking = false;
  461.         conoutf("intermission:");
  462.         conoutf("game has ended!");
  463.         showscores(true);
  464.         execute("start_intermission");
  465.     }
  466.     else
  467.     {
  468.         conoutf("time remaining: %d minutes", timeremain);
  469.     }
  470. }
  471.  
  472. playerent *newclient(int cn)   // ensure valid entity
  473. {
  474.     if(cn<0 || cn>=MAXCLIENTS)
  475.     {
  476.         neterr("clientnum");
  477.         return NULL;
  478.     }
  479.     while(cn>=players.length()) players.add(NULL);
  480.     playerent *d = players[cn];
  481.     if(d) return d;
  482.     d = newplayerent();
  483.     players[cn] = d;
  484.     d->clientnum = cn;
  485.     return d;
  486. }
  487.  
  488. playerent *getclient(int cn)   // ensure valid entity
  489. {
  490.     return players.inrange(cn) ? players[cn] : NULL;
  491. }
  492.  
  493. void initclient()
  494. {
  495.     clientmap[0] = 0;
  496.     newname("unnarmed");
  497.     changeteam(rnd(2));
  498. }
  499.  
  500. entity flagdummies[2]; // in case the map does not provide flags
  501.  
  502. void preparectf(bool cleanonly=false)
  503. {
  504.     loopi(2) flaginfos[i].flag = &flagdummies[i];
  505.     if(!cleanonly)
  506.     {
  507.         loopv(ents)
  508.         {
  509.             entity &e = ents[i];
  510.             if(e.type==CTF_FLAG) 
  511.             {
  512.                 e.spawned = true;
  513.                 if(e.attr2>2) { conoutf("\f3invalid ctf-flag entity (%i)", i); e.attr2 = 0; }
  514.                 flaginfo &f = flaginfos[e.attr2];
  515.                 f.team = e.attr2;
  516.                 f.flag = &e;
  517.                 f.state = CTFF_INBASE;
  518.                 f.originalpos.x = (float) e.x;
  519.                 f.originalpos.y = (float) e.y;
  520.                 f.originalpos.z = (float) e.z;
  521.             }
  522.         }
  523.     }
  524. }
  525.  
  526. void startmap(char *name)   // called just after a map load
  527. {
  528.     clearminimap();
  529.     //if(netmapstart()) { gamemode = 0;}  //needs fixed to switch modes?
  530.     senditemstoserver = true;
  531.     //monsterclear();
  532.     // Added by Rick
  533.     if(m_botmode) BotManager.BeginMap(name);
  534.     else kickallbots();
  535.     // End add by Rick            
  536.     projreset();
  537.     resetspawns();
  538.     if(m_ctf) preparectf();
  539.     particlereset();
  540.     spawncycle = -1;
  541.     spawnplayer(player1);
  542.     player1->frags = player1->flagscore = player1->lifesequence = 0;
  543.     loopv(players) if(players[i]) players[i]->frags = players[i]->flagscore = players[i]->lifesequence = 0;
  544.     s_strcpy(clientmap, name);
  545.     if(editmode) toggleedit();
  546.     setvar("gamespeed", 100);
  547.     setvar("fog", 180);
  548.     setvar("fogcolour", 0x8099B3);
  549.     showscores(false);
  550.     intermission = false;
  551.     if(*clientmap) conoutf("game mode is \"%s\"", modestr(gamemode));
  552.     clearbounceents();
  553. }
  554.  
  555. COMMANDN(map, changemap, ARG_1STR);
  556.  
  557. void suicide()
  558. {
  559.     if(player1->state==CS_DEAD) return;
  560.     dodamage(1000, -1, player1);
  561.     demodamage(1000, player1->o);
  562. }
  563.  
  564. COMMAND(suicide, ARG_NONE);
  565.  
  566. // console and audio feedback
  567.  
  568. void flagmsg(int flag, int action) 
  569. {
  570.     flaginfo &f = flaginfos[flag];
  571.     if(!f.actor || !f.ack) return;
  572.     bool own = flag == team_int(player1->team);
  573.     switch(action)
  574.     {
  575.         case SV_FLAGPICKUP:
  576.         {
  577.             playsound(S_FLAGPICKUP);
  578.             if(f.actor==player1) conoutf("\f2you got the enemy flag");
  579.             else conoutf("\f2%s got %s flag", colorname(f.actor), (own ? "your": "the enemy"));
  580.             break;
  581.         }
  582.         case SV_FLAGDROP:
  583.         {
  584.             playsound(S_FLAGDROP);
  585.             if(f.actor==player1) conoutf("\f2you lost the flag");
  586.             else conoutf("\f2%s lost %s flag", colorname(f.actor), (own ? "your" : "the enemy"));
  587.             break;
  588.         }
  589.         case SV_FLAGRETURN:
  590.         {
  591.             playsound(S_FLAGRETURN);
  592.             if(f.actor==player1) conoutf("\f2you returned your flag");
  593.             else conoutf("\f2%s returned %s flag", colorname(f.actor), (own ? "your" : "the enemy"));
  594.             break;
  595.         }
  596.         case SV_FLAGSCORE:
  597.         {
  598.             playsound(S_FLAGSCORE);
  599.             if(f.actor==player1) 
  600.             {
  601.                 conoutf("\f2you scored");
  602.                 addmsg(SV_FLAGS, "ri", ++player1->flagscore);
  603.             }
  604.             else conoutf("\f2%s scored for %s team", colorname(f.actor), (own ? "the enemy" : "your"));
  605.             break;
  606.         }
  607.         case SV_FLAGRESET:
  608.         {
  609.             playsound(S_FLAGRETURN);
  610.             conoutf("the server reset the flag");
  611.             break;
  612.         }
  613.         default: break;
  614.     }
  615. }
  616.  
  617. // server administration
  618.  
  619. void setmaster(int claim)
  620. {
  621.     addmsg(SV_SETMASTER, "ri", claim != 0 ? 1 : 0);
  622. }
  623.  
  624. void setadmin(char *claim, char *password)
  625. {
  626.     if(!claim) return;
  627.     if(strlen(password) > 15) { conoutf("the admin password has a maximum length of 15 characters"); return; }
  628.     else addmsg(SV_SETADMIN, "ris", atoi(claim) != 0 ? 1 : 0, password);
  629. }
  630.  
  631. void serveropcommand(int cmd, int a) // one of MCMD_*
  632. {
  633.     addmsg(SV_SERVOPCMD, "rii", cmd, a);
  634. }
  635.  
  636. void kick(int player) { serveropcommand(SOPCMD_KICK, player); }
  637. void ban(int player) { serveropcommand(SOPCMD_BAN, player); }
  638. void removebans() { serveropcommand(SOPCMD_REMBANS, 0); }
  639. void autoteam(int enable) { serveropcommand(SOPCMD_AUTOTEAM, enable); }
  640. void mastermode(int mode) { serveropcommand(SOPCMD_MASTERMODE, mode); }
  641. void forceteam(int player) { serveropcommand(SOPCMD_FORCETEAM, player); }
  642. void givemaster(int player) { serveropcommand(SOPCMD_GIVEMASTER, player); }
  643.  
  644. COMMAND(setmaster, ARG_1INT);
  645. COMMAND(setadmin, ARG_2STR);
  646. COMMAND(kick, ARG_1INT);
  647. COMMAND(ban, ARG_1INT);
  648. COMMAND(removebans, ARG_NONE);
  649. COMMAND(autoteam, ARG_1INT);
  650. COMMAND(mastermode, ARG_1INT);
  651. COMMAND(forceteam, ARG_1INT);
  652. COMMAND(givemaster, ARG_1INT);
  653.  
  654. struct mline { string name, cmd; };
  655. static vector<mline> mlines;
  656.  
  657. void *kickmenu = NULL, *banmenu = NULL, *forceteammenu = NULL, *givemastermenu = NULL;
  658.  
  659. void refreshsopmenu(void *menu, bool init)
  660. {
  661.     int item = 0;
  662.     mlines.setsize(0);
  663.     loopv(players) if(players[i])
  664.     {
  665.         mline &m = mlines.add();
  666.         s_strcpy(m.name, colorname(players[i]));
  667.         s_sprintf(m.cmd)("%s %d", menu==kickmenu ? "kick" : (menu==banmenu ? "ban" : (menu==forceteammenu ? "forceteam" : "givemaster")), i);
  668.         menumanual(menu, item++, m.name, m.cmd);
  669.     }
  670. }
  671.  
  672.