1 #define MAX_RPN_STACK 16
5 string rpn_stack[MAX_RPN_STACK];
9 return rpn_stack[rpn_sp];
11 print("rpn: stack underflow\n");
16 void rpn_push(string s) {
17 if(rpn_sp < MAX_RPN_STACK) {
18 rpn_stack[rpn_sp] = s;
21 print("rpn: stack overflow\n");
27 return rpn_stack[rpn_sp - 1];
29 print("rpn: empty stack\n");
34 void rpn_set(string s) {
36 rpn_stack[rpn_sp - 1] = s;
38 print("rpn: empty stack\n");
42 float rpn_getf() { return stof(rpn_get()); }
43 float rpn_popf() { return stof(rpn_pop()); }
44 void rpn_pushf(float f) { return rpn_push(ftos(f)); }
45 void rpn_setf(float f) { return rpn_set(ftos(f)); }
47 float mapvote_nextthink;
48 float mapvote_initialized;
49 float mapvote_keeptwotime;
50 float mapvote_timeout;
51 string mapvote_message;
52 string mapvote_screenshot_dir;
55 float mapvote_count_real;
56 string mapvote_maps[MAPVOTE_COUNT];
57 float mapvote_maps_suggested[MAPVOTE_COUNT];
58 string mapvote_suggestions[MAPVOTE_COUNT];
59 float mapvote_suggestion_ptr;
62 float mapvote_votes[MAPVOTE_COUNT];
65 float mapvote_abstain;
69 void MapVote_ClearAllVotes()
71 FOR_EACH_CLIENT(other)
75 string MapVote_Suggest(string m)
79 return "That's not how to use this command.";
80 if(!cvar("g_maplist_votable_suggestions"))
81 return "Suggestions are not accepted on this server.";
82 if(mapvote_initialized)
83 return "Can't suggest - voting is already in progress!";
84 m = MapInfo_FixName(m);
86 return "The map you suggested is not available on this server.";
87 if(!cvar("g_maplist_votable_override_mostrecent"))
89 return "This server does not allow for recent maps to be played again. Please be patient for some rounds.";
91 if(!MapInfo_CheckMap(m))
92 return "The map you suggested does not support the current game mode.";
93 for(i = 0; i < mapvote_suggestion_ptr; ++i)
94 if(mapvote_suggestions[i] == m)
95 return "This map was already suggested.";
96 if(mapvote_suggestion_ptr >= MAPVOTE_COUNT)
98 i = floor(random() * mapvote_suggestion_ptr);
102 i = mapvote_suggestion_ptr;
103 mapvote_suggestion_ptr += 1;
105 if(mapvote_suggestions[i] != "")
106 strunzone(mapvote_suggestions[i]);
107 mapvote_suggestions[i] = strzone(m);
108 if(cvar("sv_eventlog"))
109 GameLogEcho(strcat(":vote:suggested:", m, ":", ftos(self.playerid)));
110 return strcat("Suggestion of ", m, " accepted.");
113 void MapVote_AddVotable(string nextMap, float isSuggestion)
118 for(j = 0; j < mapvote_count; ++j)
119 if(mapvote_maps[j] == nextMap)
121 if(strlen(nextMap) > mapvote_maxlen)
122 mapvote_maxlen = strlen(nextMap);
123 mapvote_maps[mapvote_count] = strzone(nextMap);
124 mapvote_maps_suggested[mapvote_count] = isSuggestion;
128 void MapVote_SendData(float target);
134 MapVote_ClearAllVotes();
137 mapvote_detail = !cvar("g_maplist_votable_nodetail");
138 mapvote_abstain = cvar("g_maplist_votable_abstain");
141 nmax = min(MAPVOTE_COUNT - 1, cvar("g_maplist_votable"));
143 nmax = min(MAPVOTE_COUNT, cvar("g_maplist_votable"));
144 smax = min3(nmax, cvar("g_maplist_votable_suggestions"), mapvote_suggestion_ptr);
146 if(mapvote_suggestion_ptr)
147 for(i = 0; i < 100 && mapvote_count < smax; ++i)
148 MapVote_AddVotable(mapvote_suggestions[floor(random() * mapvote_suggestion_ptr)], TRUE);
150 for(i = 0; i < 100 && mapvote_count < nmax; ++i)
151 MapVote_AddVotable(GetNextMap(), FALSE);
153 if(mapvote_count == 0)
155 bprint( "Maplist contains no single playable map! Resetting it to default map list.\n" );
156 cvar_set("g_maplist", MapInfo_ListAllowedMaps(0, MAPINFO_FLAG_HIDDEN));
157 localcmd("\nmenu_cmd sync\n");
158 for(i = 0; i < 100 && mapvote_count < nmax; ++i)
159 MapVote_AddVotable(GetNextMap(), FALSE);
162 mapvote_count_real = mapvote_count;
164 MapVote_AddVotable("don't care", 0);
166 //dprint("mapvote count is ", ftos(mapvote_count), "\n");
168 mapvote_keeptwotime = time + cvar("g_maplist_votable_keeptwotime");
169 mapvote_timeout = time + cvar("g_maplist_votable_timeout");
170 if(mapvote_count_real < 3 || mapvote_keeptwotime <= time)
171 mapvote_keeptwotime = 0;
172 mapvote_message = "Choose a map and press its key!";
174 mapvote_screenshot_dir = cvar_string("g_maplist_votable_screenshot_dir");
175 if(mapvote_screenshot_dir == "")
176 mapvote_screenshot_dir = "maps";
177 mapvote_screenshot_dir = strzone(mapvote_screenshot_dir);
179 if(!cvar("g_maplist_textonly"))
180 MapVote_SendData(MSG_ALL);
183 void MapVote_SendPicture(float id)
186 WriteByte(MSG_ONE, SVC_TEMPENTITY);
187 WriteByte(MSG_ONE, TE_CSQC_MAPVOTE);
188 WriteByte(MSG_ONE, MAPVOTE_NET_PIC);
189 WriteByte(MSG_ONE, id);
190 WritePicture(MSG_ONE, strcat(mapvote_screenshot_dir, "/", mapvote_maps[id]), 3072);
193 float GameCommand_MapVote(string cmd)
195 if(!intermission_running)
197 if(!cvar("g_maplist_textonly"))
199 if(cmd == "mv_getpic")
201 MapVote_SendPicture(stof(argv(1)));
209 float MapVote_GetMapMask()
211 float mask, i, power;
213 for(i = 0, power = 1; i < mapvote_count; ++i, power *= 2)
214 if(mapvote_maps[i] != "")
219 void MapVote_SendData(float targ)
221 string mapfile, pakfile;
223 WriteByte(targ, SVC_TEMPENTITY);
224 WriteByte(targ, TE_CSQC_CONFIG);
225 WriteString(targ, "mv_screenshot_dir");
226 WriteString(targ, mapvote_screenshot_dir);
228 WriteByte(targ, SVC_TEMPENTITY);
229 WriteByte(targ, TE_CSQC_MAPVOTE);
230 WriteByte(targ, MAPVOTE_NET_INIT);
232 WriteByte(targ, mapvote_count);
233 WriteByte(targ, mapvote_abstain);
234 WriteByte(targ, mapvote_detail);
235 WriteCoord(targ, mapvote_timeout);
236 if(mapvote_count <= 8)
237 WriteByte(targ, MapVote_GetMapMask());
239 WriteShort(targ, MapVote_GetMapMask());
240 for(i = 0; i < mapvote_count; ++i)
241 if(mapvote_maps[i] != "")
243 WriteString(targ, mapvote_maps[i]);
244 mapfile = strcat(mapvote_screenshot_dir, "/", mapvote_maps[i]);
245 pakfile = whichpack(strcat(mapfile, ".tga"));
247 pakfile = whichpack(strcat(mapfile, ".jpg"));
249 pakfile = whichpack(strcat(mapfile, ".png"));
250 print("pakfile is ", pakfile, "\n");
251 for(o = strstr(pakfile, "/", 0)+1; o > 0; o = strstr(pakfile, "/", 0)+1)
252 pakfile = substring(pakfile, o, 999);
253 WriteString(targ, pakfile);
257 void MapVote_UpdateData(float targ)
260 WriteByte(targ, SVC_TEMPENTITY);
261 WriteByte(targ, TE_CSQC_MAPVOTE);
262 WriteByte(targ, MAPVOTE_NET_UPDATE);
263 if(mapvote_count <= 8)
264 WriteByte(targ, MapVote_GetMapMask());
266 WriteShort(targ, MapVote_GetMapMask());
268 for(i = 0; i < mapvote_count; ++i)
269 if(mapvote_maps[i] != "")
270 WriteByte(targ, mapvote_votes[i]);
273 void MapVote_TellVote(float targ, float vote)
275 WriteByte(targ, SVC_TEMPENTITY);
276 WriteByte(targ, TE_CSQC_MAPVOTE);
277 WriteByte(targ, MAPVOTE_NET_OWNVOTE);
278 WriteByte(targ, vote);
281 float MapVote_Finished(float mappos)
287 if(cvar("sv_eventlog"))
289 result = strcat(":vote:finished:", mapvote_maps[mappos]);
290 result = strcat(result, ":", ftos(mapvote_votes[mappos]), "::");
291 didntvote = mapvote_voters;
292 for(i = 0; i < mapvote_count; ++i)
293 if(mapvote_maps[i] != "")
295 didntvote -= mapvote_votes[i];
298 result = strcat(result, ":", mapvote_maps[i]);
299 result = strcat(result, ":", ftos(mapvote_votes[i]));
302 result = strcat(result, ":didn't vote:", ftos(didntvote));
305 if(mapvote_maps_suggested[mappos])
306 GameLogEcho(strcat(":vote:suggestion_accepted:", mapvote_maps[mappos]));
309 FOR_EACH_REALCLIENT(other)
310 FixClientCvars(other);
312 Map_Goto_SetStr(mapvote_maps[mappos]);
314 alreadychangedlevel = TRUE;
317 void MapVote_CheckRules_1()
321 for(i = 0; i < mapvote_count; ++i) if(mapvote_maps[i] != "")
323 //dprint("Map ", ftos(i), ": "); dprint(mapvote_maps[i], "\n");
324 mapvote_votes[i] = 0;
328 FOR_EACH_REALCLIENT(other)
333 i = other.mapvote - 1;
334 //dprint("Player ", other.netname, " vote = ", ftos(other.mapvote - 1), "\n");
335 mapvote_votes[i] = mapvote_votes[i] + 1;
340 float MapVote_CheckRules_2()
343 float firstPlace, secondPlace;
344 float firstPlaceVotes, secondPlaceVotes;
345 float mapvote_voters_real;
348 mapvote_voters_real = mapvote_voters;
350 mapvote_voters_real -= mapvote_votes[mapvote_count - 1];
352 RandomSelection_Init();
353 for(i = 0; i < mapvote_count_real; ++i) if(mapvote_maps[i] != "")
354 RandomSelection_Add(world, i, 1, mapvote_votes[i]);
355 firstPlace = RandomSelection_chosen_float;
356 firstPlaceVotes = RandomSelection_best_priority;
357 //dprint("First place: ", ftos(firstPlace), "\n");
358 //dprint("First place votes: ", ftos(firstPlaceVotes), "\n");
360 RandomSelection_Init();
361 for(i = 0; i < mapvote_count_real; ++i) if(mapvote_maps[i] != "")
363 RandomSelection_Add(world, i, 1, mapvote_votes[i]);
364 secondPlace = RandomSelection_chosen_float;
365 secondPlaceVotes = RandomSelection_best_priority;
366 //dprint("Second place: ", ftos(secondPlace), "\n");
367 //dprint("Second place votes: ", ftos(secondPlaceVotes), "\n");
370 error("No first place in map vote... WTF?");
372 if(secondPlace == -1 || time > mapvote_timeout || (mapvote_voters_real - firstPlaceVotes) < firstPlaceVotes)
373 return MapVote_Finished(firstPlace);
375 if(mapvote_keeptwotime)
376 if(time > mapvote_keeptwotime || (mapvote_voters_real - firstPlaceVotes - secondPlaceVotes) < secondPlaceVotes)
379 mapvote_dirty = TRUE;
380 mapvote_message = "Now decide between the TOP TWO!";
381 mapvote_keeptwotime = 0;
382 result = strcat(":vote:keeptwo:", mapvote_maps[firstPlace]);
383 result = strcat(result, ":", ftos(firstPlaceVotes));
384 result = strcat(result, ":", mapvote_maps[secondPlace]);
385 result = strcat(result, ":", ftos(secondPlaceVotes), "::");
386 didntvote = mapvote_voters;
387 for(i = 0; i < mapvote_count; ++i)
388 if(mapvote_maps[i] != "")
390 didntvote -= mapvote_votes[i];
394 result = strcat(result, ":", mapvote_maps[i]);
395 result = strcat(result, ":", ftos(mapvote_votes[i]));
396 if(i < mapvote_count_real)
398 strunzone(mapvote_maps[i]);
399 mapvote_maps[i] = "";
403 result = strcat(result, ":didn't vote:", ftos(didntvote));
404 if(cvar("sv_eventlog"))
418 keeptwo = mapvote_keeptwotime;
419 MapVote_CheckRules_1(); // count
420 if(MapVote_CheckRules_2()) // decide
424 FOR_EACH_REALCLIENT(other)
426 // hide scoreboard again
427 if(other.health != 2342)
431 if(clienttype(other) == CLIENTTYPE_REAL)
433 if(cvar("g_maplist_textonly"))
434 stuffcmd(other, "\nin_bind 7 1 \"impulse 1\"; in_bind 7 2 \"impulse 2\"; in_bind 7 3 \"impulse 3\"; in_bind 7 4 \"impulse 4\"; in_bind 7 5 \"impulse 5\"; in_bind 7 6 \"impulse 6\"; in_bind 7 7 \"impulse 7\"; in_bind 7 8 \"impulse 8\"; in_bind 7 9 \"impulse 9\"; in_bind 7 0 \"impulse 10\"; in_bind 7 KP_1 \"impulse 1\"; in_bind 7 KP_2 \"impulse 2\"; in_bind 7 KP_3 \"impulse 3\"; in_bind 7 KP_4 \"impulse 4\"; in_bind 7 KP_5 \"impulse 5\"; in_bind 7 KP_6 \"impulse 6\"; in_bind 7 KP_7 \"impulse 7\"; in_bind 7 KP_8 \"impulse 8\"; in_bind 7 KP_9 \"impulse 9\"; in_bind 7 KP_0 \"impulse 10\"; in_bindmap 7 0\n");
437 WriteByte(MSG_ONE, SVC_FINALE);
438 WriteString(MSG_ONE, "");
442 // notify about keep-two
443 if(keeptwo != 0 && mapvote_keeptwotime == 0)
444 play2(other, "misc/invshot.wav");
446 // clear possibly invalid votes
447 if(mapvote_maps[other.mapvote - 1] == "")
449 // use impulses as new vote
450 if(other.impulse >= 1 && other.impulse <= mapvote_count)
451 if(mapvote_maps[other.impulse - 1] != "")
453 other.mapvote = other.impulse;
455 mapvote_dirty = TRUE;
458 MapVote_TellVote(MSG_ONE, other.mapvote);
466 MapVote_CheckRules_1(); // just count
468 if(!cvar("g_maplist_textonly"))
469 if(mapvote_dirty) // 1 if "keeptwo" or "impulse" happened before
471 MapVote_UpdateData(MSG_BROADCAST);
472 mapvote_dirty = FALSE;
475 if(cvar("g_maplist_textonly"))
477 FOR_EACH_REALCLIENT(other)
479 // display voting screen
480 msgstr = "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n";
481 msgstr = substring(msgstr, 0, strlen(msgstr) - mapvote_count);
483 msgstr = substring(msgstr, 1, strlen(msgstr) - 1);
484 msgstr = strcat(msgstr, mapvote_message);
485 msgstr = strcat(msgstr, "\n\n");
486 for(i = 0; i < mapvote_count; ++i)
487 if(mapvote_maps[i] == "")
488 msgstr = strcat(msgstr, "\n");
491 tmp = mapvote_maps[i];
492 tmp = strpad(mapvote_maxlen, tmp);
493 tmp = strcat(ftos(mod(i + 1, 10)), ": ", tmp);
496 tmp = strcat(tmp, " ^2(", ftos(mapvote_votes[i]), " vote");
497 if(mapvote_votes[i] != 1)
498 tmp = strcat(tmp, "s");
499 tmp = strcat(tmp, ")");
500 tmp = strpad(mapvote_maxlen + 15, tmp);
503 if(i == mapvote_count - 1)
504 msgstr = strcat(msgstr, "\n");
505 if(other.mapvote == i + 1)
506 msgstr = strcat(msgstr, "^3> ", tmp, "\n");
508 msgstr = strcat(msgstr, "^7 ", tmp, "\n");
511 msgstr = strcat(msgstr, "\n\n^2", ftos(totalvotes), " vote");
513 msgstr = strcat(msgstr, "s");
514 msgstr = strcat(msgstr, " cast");
515 i = ceil(mapvote_timeout - time);
516 msgstr = strcat(msgstr, "\n", ftos(i), " second");
518 msgstr = strcat(msgstr, "s");
519 msgstr = strcat(msgstr, " left");
521 centerprint_atprio(other, CENTERPRIO_MAPVOTE, msgstr);
531 if(MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), 0, (g_maplist_allow_hidden ? MAPINFO_FLAG_HIDDEN : 0), 1))
539 if(alreadychangedlevel)
542 if(time < mapvote_nextthink)
546 mapvote_nextthink = time + 0.5;
548 if(!mapvote_initialized)
550 mapvote_initialized = TRUE;
551 if(DoNextMapOverride())
553 if(!cvar("g_maplist_votable") || player_count <= 0)
564 string GotoMap(string m)
566 if(!MapInfo_CheckMap(m))
567 return "The map you chose is not available on this server.";
568 cvar_set("nextmap", m);
569 cvar_set("timelimit", "-1");
570 if(mapvote_initialized || alreadychangedlevel)
572 if(DoNextMapOverride())
573 return "Map switch initiated.";
575 return "Hm... no. For some reason I like THIS map more.";
578 return "Map switch will happen after scoreboard.";
581 float GameCommand_Generic(string command)
586 argc = tokenize_sane(command);
587 if(argv(0) == "help")
589 print(" rpn EXPRESSION... - a RPN calculator.\n");
590 print(" Operator description (x: string, s: set, f: float):\n");
591 print(" x pop -----------------------------> : removes the top\n");
592 print(" x dup -----------------------------> x x : duplicates the top\n");
593 print(" x x exch --------------------------> x x : swap the top two\n");
594 print(" /cvarname load --------------------> x : loads a cvar\n");
595 print(" /cvarname x def -------------------> : writes to a cvar\n");
596 print(" f f add|sub|mul|div|mod|max|min ---> f : adds/... two numbers\n");
597 print(" f f eq|ne|gt|ge|lt|le -------------> f : compares two numbers\n");
598 print(" f neg|abs|sgn|rand|floor|ceil------> f : negates/... a number\n");
599 print(" f f f bound -----------------------> f : bounds the middle number\n");
600 print(" f1 f2 b when ----------------------> f : f1 if b, f2 otherwise\n");
601 print(" s s union|intersection|difference -> s : set operations\n");
602 print(" s shuffle -------------------------> s : randomly arrange elements\n");
603 print(" x dbpush --------------------------> : pushes the top onto the database\n");
604 print(" dbpop|dbget -----------------------> x : removes/reads DB's top\n");
605 print(" dblen|dbat ------------------------> f : gets the DB's size/cursor pos\n");
606 print(" dbclr -----------------------------> : clear the DB\n");
607 print(" s dbsave|dbload--------------------> : save/load the DB to/from a file\n");
608 print(" x dbins ---------------------------> : moves the top into the DB\n");
609 print(" dbext|dbread ----------------------> x : extract/get from the DB's cursor\n");
610 print(" f dbmov|dbgoto --------------------> : move or set the DB's cursor\n");
611 print(" Set operations operate on 'such''strings'.\n");
612 print(" Unknown tokens insert their cvar value.\n");
613 print(" maplist add map\n");
614 print(" maplist remove map\n");
615 print(" maplist shuffle\n");
616 print(" suggestmap map\n");
620 if(argv(0) == "maplist")
622 if(argv(1) == "add" && argc == 3)
624 f = fopen(strcat("maps/", argv(2), ".bsp"), FILE_READ);
628 print("maplist: ERROR: ", argv(2), " does not exist!\n");
631 if(cvar_string("g_maplist") == "")
632 cvar_set("g_maplist", argv(2));
634 cvar_set("g_maplist", strcat(argv(2), " ", cvar_string("g_maplist")));
637 else if(argv(1) == "remove" && argc == 3)
640 n = tokenizebyseparator(cvar_string("g_maplist"), " ");
642 for(i = 0; i < n; ++i)
645 s2 = strcat(s2, " ", argv(i));
647 s2 = substring(s2, 1, strlen(s2) - 1);
648 cvar_set("g_maplist", s2);
651 else if(argv(1) == "shuffle" && argc == 2)
653 s = cvar_string("g_maplist");
654 for(i = 1; i < (n = tokenizebyseparator(s, " ")); ++i)
656 // swap i-th item at a random position from 0 to i
657 // proof for even distribution:
660 // item n+1 gets at any position with chance 1/(n+1)
661 // all others will get their 1/n chance reduced by factor n/(n+1)
662 // to be on place n+1, their chance will be 1/(n+1)
663 // 1/n * n/(n+1) = 1/(n+1)
665 f = floor(random() * (i + 1)); // 0 to i
667 continue; // no change
670 for(j = 0; j < n; ++j)
671 s2 = strcat(s2, " ", argv((j == i) ? f : (j == f) ? i : j));
672 s = substring(s2, 1, strlen(s2) - 1);
674 cvar_set("g_maplist", s);
678 else if(argv(0) == "rpn")
682 rpn_db = db_create();
683 db_put(rpn_db, "stack.pointer", "0");
684 db_put(rpn_db, "stack.pos", "-1");
693 for(rpnpos = 1; rpnpos < argc; ++rpnpos)
695 rpncmd = argv(rpnpos);
698 } else if(stof(substring(rpncmd, 0, 1)) > 0) {
700 } else if(substring(rpncmd, 0, 1) == "0") {
702 } else if(f >= 2 && substring(rpncmd, 0, 1) == "+") {
704 } else if(f >= 2 && substring(rpncmd, 0, 1) == "-") {
706 } else if(f >= 2 && substring(rpncmd, 0, 1) == "/") {
707 rpn_push(substring(rpncmd, 1, strlen(rpncmd) - 1));
708 } else if(rpncmd == "clear") {
710 } else if(rpncmd == "def" || rpncmd == "=") {
714 registercvar(s2, "", 0);
716 registercvar(s2, "");
718 if(!rpn_error) // don't change cvars if a stack error had happened!
720 } else if(rpncmd == "defs" || rpncmd == "@") {
724 while(rpn_sp > 1 && (j || i > 0))
726 s = strcat("/", rpn_pop(), " ", s);
731 registercvar(s2, "", 0);
733 registercvar(s2, "");
735 if(!rpn_error) // don't change cvars if a stack error had happened!
737 } else if(rpncmd == "load") {
738 rpn_set(cvar_string(rpn_get()));
739 } else if(rpncmd == "exch") {
744 } else if(rpncmd == "dup") {
746 } else if(rpncmd == "pop") {
748 } else if(rpncmd == "add" || rpncmd == "+") {
750 rpn_setf(rpn_getf() + f);
751 } else if(rpncmd == "sub" || rpncmd == "-") {
753 rpn_setf(rpn_getf() - f);
754 } else if(rpncmd == "mul" || rpncmd == "*") {
756 rpn_setf(rpn_getf() * f);
757 } else if(rpncmd == "div" || rpncmd == "/") {
759 rpn_setf(rpn_getf() / f);
760 } else if(rpncmd == "mod" || rpncmd == "%") {
763 rpn_setf(f2 - f * floor(f2 / f));
764 } else if(rpncmd == "abs") {
765 rpn_setf(fabs(rpn_getf()));
766 } else if(rpncmd == "sgn") {
774 } else if(rpncmd == "neg" || rpncmd == "~") {
775 rpn_setf(-rpn_getf());
776 } else if(rpncmd == "floor" || rpncmd == "f") {
777 rpn_setf(floor(rpn_getf()));
778 } else if(rpncmd == "ceil" || rpncmd == "c") {
779 rpn_setf(ceil(rpn_getf()));
780 } else if(rpncmd == "max") {
783 rpn_setf(max(f2, f));
784 } else if(rpncmd == "min") {
787 rpn_setf(min(f2, f));
788 } else if(rpncmd == "bound") {
792 rpn_setf(bound(f3, f2, f));
793 } else if(rpncmd == "when") {
801 } else if(rpncmd == ">" || rpncmd == "gt") {
803 rpn_setf(rpn_getf() > f);
804 } else if(rpncmd == "<" || rpncmd == "lt") {
806 rpn_setf(rpn_getf() < f);
807 } else if(rpncmd == "==" || rpncmd == "eq") {
809 rpn_setf(rpn_getf() == f);
810 } else if(rpncmd == ">=" || rpncmd == "ge") {
812 rpn_setf(rpn_getf() >= f);
813 } else if(rpncmd == "<=" || rpncmd == "le") {
815 rpn_setf(rpn_getf() <= f);
816 } else if(rpncmd == "!=" || rpncmd == "ne") {
818 rpn_setf(rpn_getf() != f);
819 } else if(rpncmd == "rand") {
820 rpn_setf(ceil(random() * rpn_getf()) - 1);
821 } else if(rpncmd == "dbpush") {
825 i = stof(db_get(rpn_db, "stack.pointer"));
826 db_put(rpn_db, "stack.pointer", ftos(i+1));
827 db_put(rpn_db, strcat("stack.", ftos(i)), s);
830 db_put(rpn_db, "stack.pos", "0");
831 } else if(rpncmd == "dbpop") {
832 i = stof(db_get(rpn_db, "stack.pointer"));
836 db_put(rpn_db, "stack.pointer", s);
837 rpn_push(db_get(rpn_db, strcat("stack.", s)));
838 j = stof(db_get(rpn_db, "stack.pos"));
840 db_put(rpn_db, "stack.pos", ftos(i-2));
843 print("rpn: database underflow\n");
845 } else if(rpncmd == "dbget") {
847 i = stof(db_get(rpn_db, "stack.pointer"));
850 rpn_push(db_get(rpn_db, strcat("stack.", ftos(i-1))));
853 print("rpn: database empty\n");
855 } else if(rpncmd == "dblen") {
856 rpn_push(db_get(rpn_db, "stack.pointer"));
857 } else if(rpncmd == "dbclr") {
859 rpn_db = db_create();
860 db_put(rpn_db, "stack.pointer", "0");
861 db_put(rpn_db, "stack.pos", "-1");
862 } else if(rpncmd == "dbsave") {
866 } else if(rpncmd == "dbload") {
873 } else if(rpncmd == "dbins") {
878 j = stof(db_get(rpn_db, "stack.pointer"));
879 i = stof(db_get(rpn_db, "stack.pos"));
884 db_put(rpn_db, "stack.pos", "0");
887 db_put(rpn_db, "stack.pointer", ftos(j+1));
888 for(--j; j >= i; --j)
890 db_put(rpn_db, strcat("stack.", ftos(j+1)),
891 db_get(rpn_db, (strcat("stack.", ftos(j))))
894 db_put(rpn_db, strcat("stack.", ftos(i)), s);
896 } else if(rpncmd == "dbext") {
897 j = stof(db_get(rpn_db, "stack.pointer"));
898 i = stof(db_get(rpn_db, "stack.pos"));
902 print("rpn: empty database\n");
905 rpn_push(db_get(rpn_db, strcat("stack.", ftos(i))));
906 db_put(rpn_db, "stack.pointer", ftos(j));
909 db_put(rpn_db, "stack.pos", ftos(j-1));
913 db_put(rpn_db, strcat("stack.", ftos(i)),
914 db_get(rpn_db, (strcat("stack.", ftos(i+1))))
920 } else if(rpncmd == "dbread") {
921 s = db_get(rpn_db, "stack.pos");
924 rpn_push(db_get(rpn_db, strcat("stack.", s)));
927 print("rpn: empty database\n");
929 } else if(rpncmd == "dbat") {
930 rpn_push(db_get(rpn_db, "stack.pos"));
931 } else if(rpncmd == "dbmov") {
932 j = stof(db_get(rpn_db, "stack.pointer"));
933 i = stof(db_get(rpn_db, "stack.pos"));
939 print("rpn: database cursor out of bounds\n");
944 db_put(rpn_db, "stack.pos", ftos(i));
947 } else if(rpncmd == "dbgoto") {
949 j = stof(db_get(rpn_db, "stack.pointer"));
953 print("rpn: empty database, cannot move cursor\n");
958 i = stof(db_get(rpn_db, "stack.pointer"))-1;
964 j = stof(db_get(rpn_db, "stack.pointer"));
967 print("rpn: database cursor destination out of bounds\n");
972 db_put(rpn_db, "stack.pos", ftos(i));
975 } else if(rpncmd == "union") {
979 f = tokenize_sane(s);
980 f2 = tokenize_sane(strcat(s, " ", s2));
981 // tokens 0..(f-1) represent s
982 // tokens f..f2 represent s2
983 // UNION: add all tokens to s that are in s2 but not in s
985 for(i = 0; i < f; ++i)
986 s = strcat(s, " ", argv(i));
987 for(i = f; i < f2; ++i) {
988 for(j = 0; j < f; ++j)
989 if(argv(i) == argv(j))
991 s = strcat(s, " ", argv(i));
994 if(substring(s, 0, 1) == " ")
995 s = substring(s, 1, 99999);
997 tokenize_sane(command);
998 } else if(rpncmd == "intersection") {
1002 f = tokenize_sane(s);
1003 f2 = tokenize_sane(strcat(s, " ", s2));
1004 // tokens 0..(f-1) represent s
1005 // tokens f..f2 represent s2
1006 // INTERSECTION: keep only the tokens from s that are also in s2
1008 for(i = 0; i < f; ++i) {
1009 for(j = f; j < f2; ++j)
1010 if(argv(i) == argv(j))
1012 s = strcat(s, " ", argv(i));
1016 if(substring(s, 0, 1) == " ")
1017 s = substring(s, 1, 99999);
1019 tokenize_sane(command);
1020 } else if(rpncmd == "difference") {
1024 f = tokenize_sane(s);
1025 f2 = tokenize_sane(strcat(s, " ", s2));
1026 // tokens 0..(f-1) represent s
1027 // tokens f..f2 represent s2
1028 // DIFFERENCE: keep only the tokens from s that are not in s2
1030 for(i = 0; i < f; ++i) {
1031 for(j = f; j < f2; ++j)
1032 if(argv(i) == argv(j))
1033 goto skip_difference;
1034 s = strcat(s, " ", argv(i));
1037 if(substring(s, 0, 1) == " ")
1038 s = substring(s, 1, 99999);
1040 tokenize_sane(command);
1041 } else if(rpncmd == "shuffle") {
1044 f = tokenize_sane(s);
1046 for(i = 0; i < f - 1; ++i) {
1047 // move a random item from i..f-1 to position i
1049 f2 = floor(random() * (f - i) + i);
1050 for(j = 0; j < i; ++j)
1051 s = strcat(s, " ", argv(j));
1052 s = strcat(s, " ", argv(f2));
1053 for(j = i; j < f; ++j)
1055 s = strcat(s, " ", argv(j));
1056 f = tokenize_sane(s);
1059 if(substring(s, 0, 1) == " ")
1060 s = substring(s, 1, 99999);
1062 tokenize_sane(command);
1063 } else if(rpncmd == "fexists_assert") {
1067 f = fopen(s, FILE_READ);
1071 print("rpn: ERROR: ", s, " does not exist!\n");
1076 rpn_push(cvar_string(rpncmd));
1084 print("rpn: still on stack: ", s, "\n");
1089 } else if(argv(0) == "cp") {
1093 for(i = 2; i < argc; ++i)
1094 s = strcat(s, " ", argv(i));
1095 centerprint(unescape(s));
1099 } else if(argv(0) == "suggestmap") {
1100 print(strcat(MapVote_Suggest(argv(1)), "\n"));