- Put Client Commands in a seperate file
[divverent/nexuiz.git] / TeamNexuiz / game / gamec / tn_clientcommands.c
1 // Team:Nexuiz Client Commands w/ Nexuiz 1.5 RCON/Vote system\r
2 .float adminstatus;\r
3 .float autoswitch;                      // need function for this!!\r
4 .float version;\r
5 .float ready;\r
6 // stahl's voting\r
7 float votecalled;\r
8 string votecalledvote;\r
9 float votecalledmaster;\r
10 entity votecaller;\r
11 float votefinished;\r
12 .float vote_master;\r
13 .float vote_next;\r
14 .float vote_vote;\r
15 void VoteThink();\r
16 string VoteParse();\r
17 float VoteAllowed(string vote);\r
18 void VoteReset();\r
19 void VoteAccept();\r
20 void VoteReject();\r
21 void VoteTimeout();\r
22 void VoteStop(entity stopper);\r
23 void VoteCount();\r
24 void ReadyCount();\r
25 // Parse team join, class etc.\r
26 void SV_ParseClientCommand (string s)\r
27 {\r
28         local float index;\r
29         local float args;\r
30 //      local float h;\r
31         local string c;\r
32         local string d;\r
33         local string f;\r
34         local string g;\r
35         local string i;\r
36         args = tokenize(s);\r
37         c = argv(0);\r
38         d = argv(1);\r
39         f = argv(2);\r
40         g = argv(3);\r
41         i = argv(4);\r
42 \r
43         if (c == "join")\r
44     {\r
45                 return;\r
46         }\r
47         if (c == "changeclass")\r
48         {\r
49                 stuffcmd(self, "set scmenu_directmenu ClassSelect; togglemenu\n");\r
50                 return;\r
51         }\r
52         if (c == "selectteam")\r
53     {\r
54                 if (self.team_no > 0)\r
55                 {\r
56                         sprint(self, "You are already on a team!\n");\r
57                         return;\r
58                 }\r
59                 if (d == "auto" || d == "5")\r
60                 {\r
61                         TeamFortress_TeamPutPlayerInTeam();\r
62                         PutClientInServer ();\r
63                 }\r
64                 if (d == "blue" || d == "1")\r
65                 {\r
66                         TeamFortress_TeamSet (1);\r
67                         PutClientInServer ();\r
68                 }\r
69                 if (d == "red" || d == "2")\r
70                 {\r
71                         TeamFortress_TeamSet (2);\r
72                         PutClientInServer ();\r
73                 }\r
74                 if (d == "yellow" || d == "3")\r
75                 {\r
76                         TeamFortress_TeamSet (3);\r
77                         PutClientInServer ();\r
78                 }\r
79                 if (d == "green" || d == "pink" || d == "4")            // Morphed set it to Pink in the menus. I think\r
80                 {                                                                                                       // we should make it white instead.\r
81                         TeamFortress_TeamSet (4);\r
82                         PutClientInServer ();\r
83                 }\r
84                 return;\r
85         }\r
86         if (c == "selectclass")\r
87     {\r
88                 if (self.team_no < 1)\r
89                 {\r
90                         sprint(self, "You must select a team to join first!\n");\r
91                         return;\r
92                 }\r
93                 if (TeamFortress_TeamIsCivilian (self.team_no))\r
94                 {\r
95                         self.impulse = 1;\r
96                         TeamFortress_ChangeClass ();\r
97                         return;\r
98                 }\r
99                 if (self.playerclass > 0)               // if a class is already chosen\r
100                 {\r
101                         self.tfstate = self.tfstate - (self.tfstate & 8);\r
102                         local string cname;\r
103                         if (d == ftos(TF_CLASS_SCOUT))\r
104                         {\r
105                                 cname = "scout";\r
106                         }\r
107                         if (d == ftos(TF_CLASS_SOLDIER))\r
108                         {\r
109                                 cname = "soldier";\r
110                         }\r
111                         if (d == ftos(TF_CLASS_MEDIC))\r
112                         {\r
113                                 cname = "medic";\r
114                         }\r
115                         if (d == ftos(TF_CLASS_PYRO))\r
116                         {\r
117                                 cname = "pyro";\r
118                         }\r
119                         if (d == ftos(TF_CLASS_ENGINEER))\r
120                         {\r
121                                 cname = "engineer";\r
122                         }\r
123                         if (d == ftos(TF_CLASS_SPY))\r
124                         {\r
125                                 cname = "spy";\r
126                         }\r
127                         if (d == ftos(TF_CLASS_RANDOM))\r
128                         {\r
129                                 self.tfstate = self.tfstate | 8;\r
130                                 sprint(self, "You will respawn as a random class.\n");\r
131                         }\r
132                         if (cname != "")\r
133                         {\r
134 //                              stuffcmd(self, "_cl_playermodel \"models/class/");\r
135 //                              stuffcmd(self, cname);\r
136 //                              stuffcmd(self, "_mechanical.zym\"\n");\r
137                                 if (cname == "engineer")\r
138                                 {\r
139                                         self.playermodel = strcat("models/class/",cname,"_other.zym");\r
140                                 }\r
141                                 else\r
142                                 {\r
143                                         self.playermodel = strcat("models/class/",cname,"_mechanical.zym");\r
144                                 }\r
145                                 if (self.is_dead == 1)          // if player is dead, can change class\r
146                                         CheckForClassChange();\r
147                         }\r
148                 }\r
149                 else                    // if no class is chosen\r
150                 {\r
151                         if (d == ftos(TF_CLASS_SCOUT))\r
152                         {\r
153                                 self.impulse = TF_CLASS_SCOUT;\r
154                         }\r
155                         if (d == ftos(TF_CLASS_SOLDIER))\r
156                         {\r
157                                 self.impulse = TF_CLASS_SOLDIER;\r
158                         }\r
159                         if (d == ftos(TF_CLASS_MEDIC))\r
160                         {\r
161                                 self.impulse = TF_CLASS_MEDIC;\r
162                         }\r
163                         if (d == ftos(TF_CLASS_PYRO))\r
164                         {\r
165                                 self.impulse = TF_CLASS_PYRO;\r
166                         }\r
167                         if (d == ftos(TF_CLASS_ENGINEER))\r
168                         {\r
169                                 self.impulse = TF_CLASS_ENGINEER;\r
170                         }\r
171                         if (d == ftos(TF_CLASS_SPY))\r
172                         {\r
173                                 self.impulse = TF_CLASS_SPY;\r
174                         }\r
175                         if (d == ftos(TF_CLASS_RANDOM))\r
176                         {\r
177                                 self.impulse = TF_CLASS_RANDOM;\r
178                         }\r
179                         TeamFortress_ChangeClass();\r
180                 }\r
181                 return;\r
182         }\r
183         else if (c == "check_val")\r
184         {\r
185                 if (d == "r_bloom")\r
186                 {\r
187                         if (f == "1")\r
188                                 self.uses_bloom = 1;\r
189                         else\r
190                                 self.uses_bloom = 0;\r
191                 }\r
192         }\r
193         else if(argv(0) == "clogin") {\r
194                 if(cvar("sv_clientcommands")) {\r
195                         if(self.adminstatus < -5) {\r
196                                 sprint(self, "Too many unsuccessful tries.\n");\r
197                         } else if(argv(1) == cvar_string("sv_clientcommands_password")) {\r
198                                 self.adminstatus = 1;\r
199                                 sprint(self, "You now have remote admin status.\n");\r
200                         } else {\r
201                                 sprint(self, "Wrong password.\n");\r
202                                 // use of -- produces compiler warning in the if() line???\r
203                                 self.adminstatus = self.adminstatus - 1;\r
204                                 if(self.adminstatus == 0)\r
205                                         sprint(self, "You lost remote admin status.\n");\r
206                         }\r
207                 } else {\r
208                         sprint(self, "Clientside commands NOT allowed.\n");\r
209                 }\r
210         } else if(argv(0) == "ccmd") {\r
211                 if(cvar("sv_clientcommands")) {\r
212                         if(self.adminstatus > 0) {\r
213                                 local string command;\r
214                                 command = argv(1);\r
215                                 index = 2;\r
216                                 while(argv(index) != "") {\r
217                                         command = strcat(command, " ", argv(index));\r
218                                         index++;\r
219                                 }\r
220                                 localcmd(command);\r
221                         } else\r
222                                 sprint(self, "You don't have remote admin status.\n");\r
223                 } else {\r
224                         sprint(self, "Clientside commands NOT allowed.\n");\r
225                 }\r
226         } else if(argv(0) == "vote") {\r
227                 if(argv(1) == "help") {\r
228                         local string vmasterdis;\r
229                         if(!cvar("sv_vote_master")) {\r
230                                 vmasterdis = " ^1(disabled)";\r
231                         }\r
232                         local string vcalldis;\r
233                         if(!cvar("sv_vote_call")) {\r
234                                 vcalldis = " ^1(disabled)";\r
235                         }\r
236                         sprint(self, "^7You can use voting with \"^2cmd vote help^7\" \"^2cmd vote status^7\" \"^2cmd vote call ^3COMMAND ARGUMENTS^7\" \"^2cmd vote stop^7\" \"^2cmd vote master^7\" \"^2cmd vote do ^3COMMAND ARGUMENTS^7\" \"^2cmd vote yes^7\" \"^2cmd vote no^7\".\n");\r
237                         sprint(self, "^7Or if your version is up to date you can use these aliases \"^2vhelp^7\" \"^2vstatus^7\" \"^2vcall ^3COMMAND ARGUMENTS^7\" \"^2vstop^7\" \"^2vmaster^7\" \"^2vdo ^3COMMAND ARGUMENTS^7\" \"^2vyes^7\" \"^2vno^7\".\n");\r
238                         sprint(self, "^7\"^2help^7\" shows this info.\n");\r
239                         sprint(self, "^7\"^2status^7\" shows if there is a vote called and who called it.\n");\r
240                         sprint(self, strcat("^7\"^2call^7\" is used to call a vote. See the list of allowed commands.", vcalldis, "^7\n"));\r
241                         sprint(self, "^7\"^2stop^7\" can be used by the vote caller or an admin to stop a vote and maybe correct it.\n");\r
242                         sprint(self, strcat("^7\"^2master^7\" is used to call a vote to become a master.", vmasterdis, "^7\n"));\r
243                         sprint(self, "^7\"^2do^7\" If you are a master you can execute a command without a vote. See the list of allowed commands.\n");\r
244                         sprint(self, "^7\"^2yes^7\" and \"^2no^7\" to make your vote.\n");\r
245                         sprint(self, "^7If more then 50% of the players vote yes the vote is accepted.\n");\r
246                         sprint(self, "^7If more then 50% of the players vote no the vote is rejected.\n");\r
247                         sprint(self, strcat("^7The vote will end after ", cvar_string("sv_vote_timeout"), "^7 seconds.\n"));\r
248                         sprint(self, "^7You can call a vote for or execute these commands:\n");\r
249                         sprint(self, strcat("^3", cvar_string("sv_vote_commands"), "^7 and maybe further ^3arguments^7\n"));\r
250                 } else if(argv(1) == "status") {\r
251                         if(votecalled) {\r
252                                 sprint(self, strcat("^7Vote for \"^1", votecalledvote, "^7\" called by \"^7", votecaller.netname, "^7\".\n"));\r
253                         } else {\r
254                                 sprint(self, "^1No vote called.\n");\r
255                         }\r
256                 } else if(argv(1) == "call") {\r
257                         if(cvar("sv_vote_call")) {\r
258                                 if(votecalled) {\r
259                                         sprint(self, "^1There is already a vote called.\n");\r
260                                 } else {\r
261                                         local string vote;\r
262                                         vote = VoteParse();\r
263                                         if(vote == "") {\r
264                                                 sprint(self, "^1Your vote is empty. See help for more info.\n");\r
265                                         } else if(time < self.vote_next) {\r
266                                                 sprint(self, strcat("^1You have to wait ^2", ftos(self.vote_next - time), "^1 seconds before you can again call a vote.\n"));\r
267                                         } else if(VoteAllowed(strcat(argv(2)))) { // strcat seems to be necessary\r
268                                                 votecalled = TRUE;\r
269                                                 votecalledmaster = FALSE;\r
270                                                 votecalledvote = strzone(vote);\r
271                                                 votecaller = self; // remember who called the vote\r
272                                                 votefinished = time + cvar("sv_vote_timeout");\r
273                                                 votecaller.vote_vote = 1; // of course you vote yes\r
274                                                 votecaller.vote_next = time + cvar("sv_vote_wait");\r
275                                                 bprint(strcat("^3Vote for \"^1", votecalledvote, "^3\" called by \"^7", votecaller.netname, "^3\".\n"));\r
276                                                 VoteCount(); // needed if you are the only one\r
277                                         } else {\r
278                                                 sprint(self, "^1This vote is not ok. See help for more info.\n");\r
279                                         }\r
280                                 }\r
281                         } else {\r
282                                 sprint(self, "^1Vote calling is NOT allowed.\n");\r
283                         }\r
284                 } else if(argv(1) == "stop") {\r
285                         if(!votecalled) {\r
286                                 sprint(self, "^1No vote called.\n");\r
287                         } else if(self == votecaller\r
288                                   || self.adminstatus > 0) { // the votecaller and admins can stop a vote\r
289                                 VoteStop(self);\r
290                         } else {\r
291                                 sprint(self, "^1You are not allowed to stop that Vote.\n");\r
292                         }\r
293                 } else if(argv(1) == "master") {\r
294                         if(cvar("sv_vote_master")) {\r
295                                 if(votecalled) {\r
296                                         sprint(self, "^1There is already a vote called.\n");\r
297                                 } else {\r
298                                         votecalled = TRUE;\r
299                                         votecalledmaster = TRUE;\r
300                                         votecalledvote = strzone("^3master");\r
301                                         votecaller = self; // remember who called the vote\r
302                                         votefinished = time + cvar("sv_vote_timeout");\r
303                                         votecaller.vote_vote = 1; // of course you vote yes\r
304                                         votecaller.vote_next = time + cvar("sv_vote_wait");\r
305                                         bprint(strcat("\"^3", votecaller.netname, "^3\" called a vote to become ^3master^3.\n"));\r
306                                         VoteCount(); // needed if you are the only one\r
307                                 }\r
308                         } else {\r
309                                 sprint(self, "^1Vote to become master is NOT allowed.\n");\r
310                         }\r
311                 } else if(argv(1) == "do") {\r
312                         if(self.vote_master) {\r
313                                 local string dovote;\r
314                                 dovote = VoteParse();\r
315                                 if(dovote == "") {\r
316                                         sprint(self, "^1Your command was empty. See help for more info.\n");\r
317                                 } else if(VoteAllowed(strcat(argv(2)))) { // strcat seems to be necessary\r
318                                         bprint("\"^7", strcat(self.netname, "^2 used his ^3master^2 status to do \"^2", dovote, "^2\".\n"));\r
319                                         localcmd(dovote);\r
320                                 } else {\r
321                                         sprint(self, "^1This command is not ok. See help for more info.\n");\r
322                                 }\r
323                         } else {\r
324                                 sprint(self, "^1You are NOT a master.\n");\r
325                         }\r
326                 } else if(argv(1) == "yes") {\r
327                         if(!votecalled) {\r
328                                 sprint(self, "^1No vote called.\n");\r
329                         } else if(self.vote_vote == 0\r
330                                   || cvar("sv_vote_change")) {\r
331                                 sprint(self, "^1You accepted the vote.\n");\r
332                                 self.vote_vote = 1;\r
333                                 if(!cvar("sv_vote_singlecount")) {\r
334                                         VoteCount();\r
335                                 }\r
336                         } else {\r
337                                 sprint(self, "^1You have already voted.\n");\r
338                         }\r
339                 } else if(argv(1) == "no") {\r
340                         if(!votecalled) {\r
341                                 sprint(self, "^1No vote called.\n");\r
342                         } else if(self.vote_vote == 0\r
343                                   || cvar("sv_vote_change")) {\r
344                                 sprint(self, "^1You rejected the vote.\n");\r
345                                 self.vote_vote = -1;\r
346                                 if(!cvar("sv_vote_singlecount")) {\r
347                                         VoteCount();\r
348                                 }\r
349                         } else {\r
350                                 sprint(self, "^1You have already voted.\n");\r
351                         }\r
352                 } else {\r
353                         // ignore this?\r
354                         sprint(self, "^1Unknown vote command.\n");\r
355                 }\r
356         } else if(argv(0) == "autoswitch") {\r
357                 // be backwards compatible with older clients (enabled)\r
358                 self.autoswitch = ("0" != argv(1));\r
359                 local string autoswitchmsg;\r
360                 if (self.autoswitch) {\r
361                         autoswitchmsg = "on";\r
362                 } else {\r
363                         autoswitchmsg = "off";\r
364                 }\r
365                 sprint(self, strcat("^1autoswitch turned ", autoswitchmsg, "\n"));\r
366         } else if(argv(0) == "clientversion") {\r
367                 if (argv(1) == "$g_nexuizversion_major") {\r
368                         //versionmsg = "^1client is too old to get versioninfo.\nUPDATE!!! (http://www.nexuiz.com)^8";\r
369                         // either that or someone wants to be funny\r
370                         self.version = 1;\r
371                 } else {\r
372                         self.version = stof(argv(1));\r
373                 }\r
374         } else if(argv(0) == "spectate") {\r
375                 if(cvar("g_lms"))\r
376                         return; // don't allow spectating in lms, unless player runs out of lives\r
377                 if(self.classname == "player" && cvar("sv_spectate") == 1) {\r
378                         self.classname = "observer";\r
379                         PutClientInServer();\r
380                 }\r
381         } else if(argv(0) == "join") {\r
382                 self.classname = "player";\r
383                 self.frags = 0;\r
384                 // TODO: I have no idea whether this is needed or not\r
385                 if(!cvar("g_lms")) {\r
386                         bprint (strcat("^4", self.netname, "^4 is playing now\n"));\r
387                 }\r
388                 PutClientInServer();\r
389         } else if(argv(0) == "ready") {\r
390                 if(cvar("sv_ready_restart"))\r
391                 {\r
392                         self.ready = TRUE;\r
393                         bprint(self.netname, "^2 is ready\n");\r
394                         ReadyCount();\r
395                 }\r
396         }\r
397         else\r
398         {\r
399                 clientcommand(self, s);\r
400         }\r
401 };\r
402 \r
403 void VoteThink() {\r
404         if(votefinished > 0 // a vote was called\r
405             && time > votefinished) // time is up\r
406         {\r
407                 VoteCount();\r
408         }\r
409 }\r
410 \r
411 string VoteParse() {\r
412         local float index;\r
413         index = 3;\r
414         local string vote;\r
415         vote = argv(2);\r
416         while(argv(index) != "") {\r
417                 vote = strcat(vote, " ", argv(index));\r
418                 index++;\r
419         }\r
420 \r
421         // necessary for some of the string operations\r
422         vote = strzone(vote);\r
423 \r
424         // now we remove some things that could be misused\r
425         index = 0;\r
426         local float found;\r
427         found = FALSE;\r
428         local float votelength;\r
429         votelength = strlen(vote);\r
430         while(!found && index < votelength)\r
431         {\r
432                 local string badchar;\r
433                 badchar = substring(vote, index, 1);\r
434                 if(badchar == ";"\r
435                    || badchar == "\n")\r
436                 {\r
437                         found = TRUE;\r
438                 } else {\r
439                         index++;\r
440                 }\r
441         }\r
442         return substring(vote, 0, index);\r
443 }\r
444 \r
445 float VoteAllowed(string votecommand) {\r
446         tokenize(cvar_string("sv_vote_commands"));\r
447         local float index;\r
448         index = 0;\r
449         while(argv(index) != "") {\r
450                 local string allowed;\r
451                 allowed = argv(index);\r
452                 if(votecommand == allowed) {\r
453                         return TRUE;\r
454                 }\r
455                 index++;\r
456         }\r
457         return FALSE;\r
458 }\r
459 \r
460 void VoteReset() {\r
461         local string searchclass;\r
462         searchclass = "player";\r
463 \r
464         while (TRUE)\r
465         {\r
466                 local entity player;\r
467                 player = find(player, classname, searchclass);\r
468                 while(player)\r
469                 {\r
470                         player.vote_vote = 0;\r
471                         player = find(player, classname, searchclass);\r
472                 }\r
473 \r
474                 if("player" == searchclass) {\r
475                         searchclass = "observer";\r
476                 } else if("observer" == searchclass) {\r
477                         searchclass = "spectator";\r
478                 } else {\r
479                         break;\r
480                 }\r
481         }\r
482 \r
483         votecalled = FALSE;\r
484         votecalledmaster = FALSE;\r
485         votefinished = 0;\r
486 }\r
487 \r
488 void VoteAccept() {\r
489         bprint(strcat("^2The vote for \"^1", votecalledvote, "^2\" from \"^7", votecaller.netname, "^2\" was accepted.\n"));\r
490         if(votecalledmaster)\r
491         {\r
492                 votecaller.vote_master = 1;\r
493         } else {\r
494                 localcmd(votecalledvote);\r
495         }\r
496         votecaller.vote_next = 0; // people like your votes, no wait for next vote\r
497         VoteReset();\r
498 }\r
499 \r
500 void VoteReject() {\r
501         bprint(strcat("^2The vote for \"^1", votecalledvote, "^2\" from \"^7", votecaller.netname, "^2\" was rejected.\n"));\r
502         VoteReset();\r
503 }\r
504 \r
505 void VoteTimeout() {\r
506         bprint(strcat("^5The vote for \"^1", votecalledvote, "^5\" from \"^7", votecaller.netname, "^5\" did timeout.\n"));\r
507         VoteReset();\r
508 }\r
509 \r
510 void VoteStop(entity stopper) {\r
511         bprint(strcat("^5The vote for \"^1", votecalledvote, "^5\" from \"^7", votecaller.netname, "^5\" was stopped by \"^5", stopper.netname, "^5\".\n"));\r
512         if(stopper == votecaller) {\r
513                 // no wait for next vote so you can correct your vote\r
514                 votecaller.vote_next = 0;\r
515         }\r
516         VoteReset();\r
517 }\r
518 \r
519 void VoteCount() {\r
520         local float playercount;\r
521         playercount = 0;\r
522         local float yescount;\r
523         yescount = 0;\r
524         local float nocount;\r
525         nocount = 0;\r
526         local string searchclass;\r
527         searchclass = "player";\r
528 \r
529         while (TRUE)\r
530         {\r
531                 local entity player;\r
532                 player = find(player, classname, searchclass);\r
533 \r
534                 while(player)\r
535                 {\r
536                         if(clienttype(player) != CLIENTTYPE_BOT) {\r
537                                 if(player.vote_vote < 0) {\r
538                                         nocount++;\r
539                                 } else if(player.vote_vote > 0) {\r
540                                         yescount++;\r
541                                 }\r
542                                 playercount++;\r
543                         }\r
544                         player = find(player, classname, searchclass);\r
545                 }\r
546 \r
547                 if("player" == searchclass) {\r
548                         searchclass = "observer";\r
549                 } else if("observer" == searchclass) {\r
550                         searchclass = "specator";\r
551                 } else {\r
552                         break;\r
553                 }\r
554         }\r
555 \r
556         if((playercount == 1) && votecalledmaster) {\r
557                 // if only one player is on the server becoming vote\r
558                 // master is not allowed.  This could be used for\r
559                 // trolling or worse. 'self' is the user who has\r
560                 // called the vote because this function is called\r
561                 // by SV_ParseClientCommand. Maybe all voting should\r
562                 // be disabled for a single player?\r
563                 sprint(self, "^1You are the only player on this server so you can not become vote master.\n");\r
564                 votecaller.vote_next = 0;\r
565                 VoteReset();\r
566         } else if((playercount / 2) < yescount) { // vote accepted\r
567                 VoteAccept();\r
568         } else if((playercount / 2) < nocount) { // vote rejected\r
569                 VoteReject();\r
570         } else if(time > votefinished) { // vote timedout\r
571                 VoteTimeout();\r
572         } // else still running\r
573 }\r
574 \r
575 \r
576 void ReadyCount()\r
577 {\r
578         local entity e;\r
579         local float r, p;\r
580 \r
581         e = find(world, classname, "player");\r
582 \r
583         while(e)\r
584         {\r
585                 if(clienttype(e) == CLIENTTYPE_REAL)\r
586                 {\r
587                         p += 1;\r
588                         if(e.ready) r += 1;\r
589                 }\r
590                 e = find(e, classname, "player");\r
591         }\r
592 \r
593         if(p && r == p)\r
594         {\r
595                 bprint("^1Server is restarting...\n");\r
596                 localcmd("restart\n");\r
597         }\r
598 }\r