From 74632f9723e12b216a39bb87c756278ab722b06d Mon Sep 17 00:00:00 2001 From: blub0 Date: Sun, 13 Jul 2008 17:42:30 +0000 Subject: [PATCH] map previews when voting, g_maplist_textonly for old style new sorting in the scoreboard -Fparms in Makefile for future FTE version check git-svn-id: svn://svn.icculus.org/nexuiz/trunk@3819 f962a42d-fe04-0410-a3ab-8c8b0445ebaa --- data/Makefile | 16 +- data/defaultNexuiz.cfg | 3 +- data/qcsrc/client/Main.qc | 115 ++++++++-- data/qcsrc/client/View.qc | 10 - data/qcsrc/client/csqc_builtins.qc | 3 +- data/qcsrc/client/main.qh | 7 +- data/qcsrc/client/mapvoting.qc | 276 +++++++++++++++++++++++ data/qcsrc/client/miscfunctions.qc | 48 +++- data/qcsrc/client/progs.src | 5 +- data/qcsrc/client/sbar.qc | 325 ++++++++++++++++++++++------ data/qcsrc/client/sortlist.qc | 79 ++++++- data/qcsrc/client/teamplay.qc | 3 + data/qcsrc/common/constants.qh | 16 +- data/qcsrc/server/cl_client.qc | 5 + data/qcsrc/server/clientcommands.qc | 2 + data/qcsrc/server/g_world.qc | 176 +++++++++++---- 16 files changed, 919 insertions(+), 170 deletions(-) create mode 100644 data/qcsrc/client/mapvoting.qc diff --git a/data/Makefile b/data/Makefile index 7e40b817e..38c60b2a1 100644 --- a/data/Makefile +++ b/data/Makefile @@ -3,6 +3,18 @@ PERL ?= perl PK3NAME ?= `date +../data%Y%m%d.pk3` ZIP ?= 7za a -tzip -mx=9 +# -Fparm: define PARM0, RETURN, etc. for use in asm{} +# This will make it possible to make a non-FTE asm{} block +# checking if the client's engine supports -TFTE. +# At some point CSQC should use -TFTE too, and then, for at least +# some time, it would be useful to have a well-formatted error message +# followed by localcmd("disconnect") if the client doesn't support -TFTE +# instead of letting him guess what the huge QCVM error message means... +QCFLAGS_CSQC ?= -Fparm + +# to be enabled when possible +# QCFLAGS_SVQC ?= -TFTE + all: qc .PHONY: qc @@ -19,10 +31,10 @@ clean: rm -f progs.dat menu.dat csprogs.dat csprogs.dat: qcsrc/client/*.* qcsrc/common/*.* - ( cd qcsrc/client; $(FTEQCC) ) + ( cd qcsrc/client; $(FTEQCC) $(QCFLAGS_CSQC) ) progs.dat: qcsrc/server/*.* qcsrc/common/*.* - ( cd qcsrc/server; $(FTEQCC) ) + ( cd qcsrc/server; $(FTEQCC) $(QCFLAGS_SVQC) ) menu.dat: qcsrc/menu/*.* qcsrc/menu/*/*.* qcsrc/common/*.* ( cd qcsrc/menu; $(FTEQCC) ) diff --git a/data/defaultNexuiz.cfg b/data/defaultNexuiz.cfg index 0059e8e6e..39a7037dc 100644 --- a/data/defaultNexuiz.cfg +++ b/data/defaultNexuiz.cfg @@ -811,6 +811,7 @@ seta g_maplist_votable_suggestions 2 seta g_maplist_votable_suggestions_override_mostrecent 0 seta g_maplist_votable_nodetail 0 // nodetail only shows total count instead of all vote counts per map, so votes don't influence others that much seta g_maplist_votable_abstain 0 // when 1, you can abstain from your vote +seta g_maplist_textonly 0 // use old style centerprint alias suggestmap "cmd suggestmap $1" set g_chat_flood_spl 0 // seconds between lines to not count as flooding @@ -1053,5 +1054,3 @@ set sbar_fontsize 11 alias sbar_font "loadfont user1 $*; sbar_columns_set" sbar_font gfx/vera-sans -// supporting stringwidth -seta csqc_flags 1 diff --git a/data/qcsrc/client/Main.qc b/data/qcsrc/client/Main.qc index 807a22304..b1b37811b 100644 --- a/data/qcsrc/client/Main.qc +++ b/data/qcsrc/client/Main.qc @@ -15,11 +15,34 @@ void() menu_sub_null = }; // let's make this a general data buffer... -float databuf; float using_gps; +float __engine_check; + +void testpassedyay() { + asm { + RETURN 0; + } +} + void CSQC_Init(void) { +#if 0 + asm { + STORE_F "DP_SV_WRITEPICTURE" PARM0; + CALL1 checkextension; + STORE_F RETURN __engine_check; + IF __engine_check 6; + STORE_F "^3Your engine build is outdated\n^3This Server uses a newer QC VM. Please update!\n" PARM0; + CALL1 print; + STORE_F "\ndisconnect\n" PARM0; + CALL1 localcmd; + DONE; + } +#else + __engine_check = true; +#endif + float i; CSQC_CheckEngine(); drawfont = 0; @@ -47,6 +70,18 @@ void CSQC_Init(void) sbar_title[i] = strzone("(null)"); postinit = false; + + Sbar_Init(); +} + +// CSQC_Shutdown : Called every time the CSQC code is shutdown (changing maps, quitting, etc) +void CSQC_Shutdown(void) +{ + asm { + IF __engine_check 2; + RETURN 0; + } + buf_del(databuf); } void PostInit(void) @@ -67,11 +102,6 @@ void PostInit(void) postinit = true; } -// CSQC_Shutdown : Called every time the CSQC code is shutdown (changing maps, quitting, etc) -void CSQC_Shutdown(void) -{ - buf_del(databuf); -} // CSQC_ConsoleCommand : Used to parse commands in the console that have been registered with the "registercmd" function // Return value should be 1 if CSQC handled the command, otherwise return 0 to have the engine handle it. @@ -104,6 +134,21 @@ float CSQC_ConsoleCommand(string strMessage) return false; } + +float GameCommand(string msg) +{ + float argc; + argc = tokenize(msg); + string cmd; + cmd = argv(0); + if(cmd == "mv_download") { + Cmd_MapVote_MapDownload(argc); + return true; + } + + return false; +} + // CSQC_InputEvent : Used to perform actions based on any key pressed, key released and mouse on the client. // Return value should be 1 if CSQC handled the input, otherwise return 0 to have the input passed to the engine. // All keys are in ascii. @@ -219,7 +264,6 @@ void Gamemode_Init() local float file; local vector mi_min, mi_max; - draw_enginesbar = true; gametype = cvar("gametype"); if(gametype == GAME_ONSLAUGHT) { if(!strcasecmp(substring(mapname, 0, 5), "maps/")) @@ -248,7 +292,7 @@ void Gamemode_Init() } } } else { - print(strcat("^1Error: ^7Mapinfo file '", mapinfo, "' missing! Minimap will be screwed.\n")); + print(strcat("Map has no .info file (", mapinfo, ").\n")); } fclose(file); @@ -268,7 +312,6 @@ void Gamemode_Init() } else if(gametype == GAME_KEYHUNT) { precache_pic("gfx/sb_key_carrying"); precache_pic("gfx/sb_key_carrying_outline"); - draw_enginesbar = false; } } // CSQC_Parse_StuffCmd : Provides the stuffcmd string in the first parameter that the server provided. To execute standard behavior, simply execute localcmd with the string. @@ -293,7 +336,8 @@ void CSQC_Parse_CenterPrint(string strMessage) } void CSQC_CheckRevision(); -void ReadInit() + +void Net_ReadInit() { csqc_revision = ReadShort(); maxclients = ReadByte(); @@ -301,7 +345,7 @@ void ReadInit() CSQC_CheckRevision(); } -void ReadPings() +void Net_ReadPings() { float plnum, ping; for(plnum = ReadByte(); plnum != 0; plnum = ReadByte()) @@ -311,7 +355,7 @@ void ReadPings() } } -void ReadCaptures() +void Net_ReadCaptures() { float plnum, caps, mode; mode = ReadByte(); @@ -324,7 +368,7 @@ void ReadCaptures() } } -void ReadDatabuf(float ofs) +void Net_ReadDatabuf(float ofs) { float plnum, data; for(plnum = ReadByte(); plnum != 0; plnum = ReadByte()) @@ -334,6 +378,26 @@ void ReadDatabuf(float ofs) } } +void Net_ReadPicture() +{ + string img; + if(csqc_flags & CSQC_FLAG_READPICTURE) + { + img = ReadPicture(); + print(strcat("Got Picture: ", img, "\n")); + } else { + img = ReadString(); + print(strcat("^3Warning: ^7Couldn't download ", img, ". This is probably because your engine build is outdated.\n")); + float psize, i; + psize = ReadShort(); + // Can I be sure that ReadShort is 2 bytes and ReadLong is 4 bytes? + // Because then this could be optimized to first get all 4-byte-groups, + // then the remaining 2, then the remaining 1 + for(i = 0; i < psize; ++i) + ReadByte(); + } +} + // CSQC_Parse_TempEntity : Handles all temporary entity network data in the CSQC layer. // You must ALWAYS first acquire the temporary ID, which is sent as a byte. // Return value should be 1 if CSQC handled the temporary entity, otherwise return 0 to have the engine process the event. @@ -344,29 +408,39 @@ float CSQC_Parse_TempEntity() // Acquire TE ID local float nTEID; nTEID = ReadByte(); - + + // NOTE: Could just do return instead of break... switch(nTEID) { case TE_CSQC_INIT: - ReadInit(); + Net_ReadInit(); bHandled = true; break; case TE_CSQC_PING: - ReadPings(); + Net_ReadPings(); bHandled = true; break; case TE_CSQC_CAPTURES: - ReadCaptures(); + Net_ReadCaptures(); bHandled = true; break; case TE_CSQC_RETURNS: - ReadDatabuf(DATABUF_RETURNS); + Net_ReadDatabuf(DATABUF_RETURNS); bHandled = true; break; case TE_CSQC_DEATHS: - ReadDatabuf(DATABUF_DEATHS); + Net_ReadDatabuf(DATABUF_DEATHS); bHandled = true; break; + case TE_CSQC_PICTURE: + Net_ReadPicture(); + bHandled = true; + break; + case TE_CSQC_MAPVOTE: + Net_Mapvote(); + bHandled = true; + break; + default: // No special logic for this temporary entity; return 0 so the engine can handle it bHandled = false; @@ -382,7 +456,8 @@ float CSQC_Parse_TempEntity() // COMMIT-TODO: Update if necessare, before committing float csqc_svn_map[CSQC_REVISION] = { - 3795, + 3812, // 3795, + 3815 }; // COMMIT-TODO: Update if necessare, before committing diff --git a/data/qcsrc/client/View.qc b/data/qcsrc/client/View.qc index 003c22c3c..f82075f98 100644 --- a/data/qcsrc/client/View.qc +++ b/data/qcsrc/client/View.qc @@ -1,15 +1,5 @@ //include "main.qh" -float sbar_alpha_fg; -float last_weapon; -float activeweapon; -float weapontime; -float sbar_hudselector; -float teamplay; -float myteam; - -float numteams; // NOTE: This is changed in Sbar_SortFrags, so use it only AFTER that - void CSQC_common_hud(void); void CSQC_kh_hud(void); diff --git a/data/qcsrc/client/csqc_builtins.qc b/data/qcsrc/client/csqc_builtins.qc index 47a9e17dc..fd8c1a412 100644 --- a/data/qcsrc/client/csqc_builtins.qc +++ b/data/qcsrc/client/csqc_builtins.qc @@ -98,7 +98,6 @@ void drawsetcliparea(float x, float y, float width, float height) = #324; void drawresetcliparea(void) = #325; float drawcolorcodedstring(vector position, string text, vector scale, float alpha, float flag) = #326; float stringwidth_engine(string text, float handleColors) = #327; -float stringwidth(string text, float handleColors); float drawsubpic(vector position, vector size, string pic, vector srcPosition, vector srcSize, vector rgb, float alpha, float flag) = #328; @@ -260,4 +259,6 @@ float(string s1, string s2, float len) strncasecmp = #230; float(string str, string sub, float startoffs) strstrofs = #221; //float(string str, string sub) strstrofs = #221; entity(float num) edict_num = #459; +string(void) ReadPicture = #501; +string(string filename) whichpack = #503; float(entity ent) num_for_edict = #512; diff --git a/data/qcsrc/client/main.qh b/data/qcsrc/client/main.qh index 2fae3e87b..82b750568 100644 --- a/data/qcsrc/client/main.qh +++ b/data/qcsrc/client/main.qh @@ -11,10 +11,9 @@ void() menu_show_error; void() menu_sub_null; -void() menu_show; float menu_visible; - -float(float bInputType, float nPrimary, float nSecondary) menu_action; +var void() menu_show; +var float(float bInputType, float nPrimary, float nSecondary) menu_action; // -------------------------------------------------------------------------- // CTF @@ -86,4 +85,4 @@ float sbar_num_fields; float sbar_font; float csqc_flags; -#define CSQC_FLAG_STRINGWIDTH 1 +#define CSQC_FLAG_READPICTURE 1 diff --git a/data/qcsrc/client/mapvoting.qc b/data/qcsrc/client/mapvoting.qc new file mode 100644 index 000000000..863b372b9 --- /dev/null +++ b/data/qcsrc/client/mapvoting.qc @@ -0,0 +1,276 @@ +float mv_num_maps; + +string mv_maps[MAPVOTE_COUNT]; +string mv_pics[MAPVOTE_COUNT]; +string mv_pk3[MAPVOTE_COUNT]; +float mv_preview[MAPVOTE_COUNT]; +float mv_votes[MAPVOTE_COUNT]; +entity mv_pk3list; +float mv_abstain; +float mv_ownvote; +float mv_detail; + +void MapVote_DrawMapItem(vector pos, float isize, string map, string pic, float count, float id) +{ + vector img_size; + //img_size_x = img_size_y = isize; + img_size_y = isize; + img_size_x = isize / 0.75; // 4:3 x can be stretched easily, height is defined in isize + + drawpic(pos, pic, img_size, '1 1 1', 1, DRAWFLAG_NORMAL); + + // half size for the impulse number + img_size_x = img_size_y = isize*0.5; + pos_y += img_size_y*0.5; + + if(id == mv_ownvote) + drawstring(pos - '40 0', strcat(ftos(id+1), ")"), img_size, '1 1 0', 1, DRAWFLAG_NORMAL); + else + drawstring(pos - '40 0', strcat(ftos(id+1), ")"), img_size, '1 1 1', 1, DRAWFLAG_NORMAL); + + // half again for the mapname + img_size = img_size * 0.5; // *= broken??? + pos_y += img_size_y*0.5; + pos_x += isize/0.75 + 10; + + if(mv_detail) + drawstring(pos, strcat(ftos(count), " : ", map), img_size, '1 1 1', 1, DRAWFLAG_NORMAL); + else + drawstring(pos, map, img_size, '1 1 1', 1, DRAWFLAG_NORMAL); +} + +void MapVote_DrawMapNotAvailable(vector pos, float isize, string map, float count, float id) +{ + vector img_size, a, b; + img_size_y = isize; + img_size_x = isize / 0.75; + + a_x = img_size_x; // for the lines + b_y = img_size_y; + drawfill(pos, img_size, '.5 .5 .5', .7, DRAWFLAG_NORMAL); + drawline(2, pos, pos + a, '1 1 1', 1, DRAWFLAG_NORMAL); + drawline(2, pos, pos + b, '1 1 1', 1, DRAWFLAG_NORMAL); + drawline(2, pos + img_size, pos + a, '1 1 1', 1, DRAWFLAG_NORMAL); + drawline(2, pos + img_size, pos + b, '1 1 1', 1, DRAWFLAG_NORMAL); + + img_size_x = img_size_y = isize*0.5; + pos_y += img_size_y*0.5; + + if(id == mv_ownvote) + drawstring(pos - '40 0', strcat(ftos(id+1), ")"), img_size, '1 1 0', 1, DRAWFLAG_NORMAL); + else + drawstring(pos - '40 0', strcat(ftos(id+1), ")"), img_size, '1 1 1', 1, DRAWFLAG_NORMAL); + + img_size = img_size * 0.5; // *= broken??? + pos_y += img_size_y*0.5; + pos_x += isize/0.75 + 10; + + if(mv_detail) + drawstring(pos, strcat(ftos(count), " : ", map), img_size, '1 1 1', 1, DRAWFLAG_NORMAL); + else + drawstring(pos, map, img_size, '1 1 1', 1, DRAWFLAG_NORMAL); +} + +void MapVote_DrawAbstain(vector pos, float isize, float count, float id) +{ + vector img_size; + img_size_y = isize; + img_size_x = isize / 0.75; + + img_size_x = img_size_y = isize*0.5; + pos_y += img_size_y*0.5; + + if(id == mv_ownvote) + drawstring(pos - '40 0', strcat(ftos(id+1), ")"), img_size, '1 1 0', 1, DRAWFLAG_NORMAL); + else + drawstring(pos - '40 0', strcat(ftos(id+1), ")"), img_size, '1 1 1', 1, DRAWFLAG_NORMAL); + + img_size = img_size * 0.5; // *= broken??? + pos_y += img_size_y*0.5; + pos_x += isize/0.75 + 10; + + if(mv_detail) + drawstring(pos, strcat(ftos(count), " : Don't care"), img_size, '1 1 1', 1, DRAWFLAG_NORMAL); + else + drawstring(pos, "Don't care", img_size, '1 1 1', 1, DRAWFLAG_NORMAL); +} + +void MapVote_Draw() +{ + string map; + float i, tmp; + vector pos; + vector center; + float isize; + + center_x = (vid_conwidth - 1)/2; + xmin = vid_conwidth*0.2; + xmax = vid_conwidth - xmin; + ymin = 24; + i = cvar("con_chatpos"); //*cvar("con_chatsize"); + if(i < 0) + ymax = vid_conheight + (i - cvar("con_chat")) * cvar("con_chatsize"); + if(i >= 0 || ymax < (vid_conheight*0.5)) + ymax = vid_conheight - ymin; + + drawfont = sbar_font; + sbar_fontsize = stov(cvar_string("sbar_fontsize")); + if(sbar_fontsize_x == 0) + sbar_fontsize = '8 8 0'; + if(sbar_fontsize_y == 0) + sbar_fontsize_y = sbar_fontsize_x; + + pos_y = ymin; + pos_z = 0; + pos_x = center_x - stringwidth("Vote for a map", false) * 0.5 * 24; + drawstring(pos, "Vote for a map", '24 24', '1 1 1', 1, DRAWFLAG_NORMAL); + + pos_x = xmin; + pos_y += 32; + isize = (ymax - ymin - mv_num_maps*10) / mv_num_maps; + isize = min(isize, 64); + for(i = 0; i < (mv_num_maps - mv_abstain); ++i) // - mv_abstain safe? make sure it's 0 or 1 + { + tmp = mv_votes[i]; // FTEQCC bug: too many array accesses in the function call screw it up + if(tmp < 0) + continue; + map = mv_maps[i]; + if(mv_preview[i]) + MapVote_DrawMapItem(pos, isize, map, mv_pics[i], tmp, i); + else + MapVote_DrawMapNotAvailable(pos, isize, map, tmp, i); + pos_y += isize + 10; + } + if(mv_abstain && i < mv_num_maps) { + tmp = mv_votes[i]; + MapVote_DrawAbstain(pos, isize, tmp, i); + } +} + +void Cmd_MapVote_MapDownload(float argc) +{ + float id; + entity pak; + + if(argc != 2 || !mv_pk3list) + { + print("mv_mapdownload: ^3You're not supposed to use this command on your own!\n"); + return; + } + + id = stof(argv(1)); + for(pak = mv_pk3list; pak; pak = pak.chain) + if(pak.sv_entnum == id) + break; + + if(!pak || pak.sv_entnum != id) { + print("^1Error:^7 Couldn't find pak index.\n"); + return; + } + + //print(strcat("^3Adding: ", ftos(id), " - ", pak.message, " - ")); + + if(PreviewExists(pak.message)) + { + mv_preview[id] = true; + //print("^2Found...\n"); + return; + } else if(csqc_flags & CSQC_FLAG_READPICTURE) { + print("Requesting preview...\n"); + localcmd(strcat("\ncmd mv_getpic ", ftos(id), "\n")); + } else { + print("^3Missing map preview - Update to a newer build to be able to see them.\n"); + } +} + +void MapVote_CheckPK3(string pic, string pk3, float id) +{ + entity pak; + pak = spawn(); + pak.netname = pk3; + pak.message = pic; + pak.sv_entnum = id; + + pak.chain = mv_pk3list; + mv_pk3list = pak; + + localcmd(strcat("\ncurl --pak ", pk3, "; wait; cl_cmd mv_download ", ftos(id), "\n")); +} + +void MapVote_CheckPic(string pic, string pk3, float id) +{ + if(PreviewExists(pic)) + { + //print(strcat("^2Exists... ", pic, "\n")); + mv_preview[id] = true; + return; + } + MapVote_CheckPK3(pic, pk3, id); +} + +void MapVote_Init() +{ + float i; + string map, pk3; + + mv_num_maps = min(MAPVOTE_COUNT, ReadByte()); + mv_abstain = ReadByte(); + if(mv_abstain) + mv_abstain = 1; // must be 1 + mv_detail = ReadByte(); + mv_ownvote = -1; + + // Assume mv_pk3list is NULL, there should only be 1 mapvote per round + mv_pk3list = NULL; // I'm still paranoid! + + for(i = 0; i < mv_num_maps; ++i) + { + mv_votes[i] = 0; + map = strzone(ReadString()); + pk3 = strzone(ReadString()); + mv_maps[i] = map; + mv_pk3[i] = pk3; + map = strzone(strcat("maps/", map)); + mv_pics[i] = map; + + mv_preview[i] = false; + + //print(strcat("RECV: ", map, " in ", pk3, "\n")); + MapVote_CheckPic(map, pk3, i); + } +} + +void MapVote_Update() +{ + float i, e; + for(i = 0; i < mv_num_maps; ++i) + { + e = ReadByte(); + mv_votes[i] = ReadByte(); + if(!e) + mv_votes[i] = -1; + } +} + +void Net_Mapvote() +{ + float type; + type = ReadByte(); + switch(type) + { + case MAPVOTE_NET_INIT: + MapVote_Init(); + break; + case MAPVOTE_NET_UPDATE: + MapVote_Update(); + break; + case MAPVOTE_NET_OWNVOTE: + mv_ownvote = ReadByte()-1; + break; + case MAPVOTE_NET_PIC: + type = ReadByte(); + mv_preview[type] = true; + //print(strcat("MAPVOTE_NET_PIC: ^2", ftos(type), "\n")); + break; + } +} diff --git a/data/qcsrc/client/miscfunctions.qc b/data/qcsrc/client/miscfunctions.qc index 47e59ef2b..abea2e0da 100644 --- a/data/qcsrc/client/miscfunctions.qc +++ b/data/qcsrc/client/miscfunctions.qc @@ -1,3 +1,6 @@ +float databuf; +var float(string text, float handleColors) stringwidth; + float stringwidth_oldfont(string text, float handleColors) { float i, len, ch, width; @@ -23,14 +26,18 @@ float stringwidth_oldfont(string text, float handleColors) void CSQC_CheckEngine() { - float i, tmp; + /* registercvar("csqc_flags", "0"); csqc_flags = cvar("csqc_flags"); + */ - if(csqc_flags & CSQC_FLAG_STRINGWIDTH) + csqc_flags = 0; + + if(checkextension("DP_SV_WRITEPICTURE")) { stringwidth = stringwidth_engine; sbar_font = FONT_USER+1; + csqc_flags |= CSQC_FLAG_READPICTURE; } else { stringwidth = stringwidth_oldfont; sbar_font = FONT_DEFAULT; @@ -39,7 +46,42 @@ void CSQC_CheckEngine() vector Sbar_GetFontsize() { - if(csqc_flags & CSQC_FLAG_STRINGWIDTH) + if(csqc_flags & CSQC_FLAG_READPICTURE) return stov(cvar_string("sbar_fontsize")); return '8 8' ; } + +float PreviewExists(string name) +{ + float f; + string file; + file = strcat(name, ".tga"); + f = fopen(file, FILE_READ); + if(f >= 0) + { + fclose(f); + return true; + } + file = strcat(name, ".png"); + f = fopen(file, FILE_READ); + if(f >= 0) + { + fclose(f); + return true; + } + file = strcat(name, ".jpg"); + f = fopen(file, FILE_READ); + if(f >= 0) + { + fclose(f); + return true; + } + file = strcat(name, ".pcx"); + f = fopen(file, FILE_READ); + if(f >= 0) + { + fclose(f); + return true; + } + return false; +} diff --git a/data/qcsrc/client/progs.src b/data/qcsrc/client/progs.src index 8f83a2bc4..ca960441b 100644 --- a/data/qcsrc/client/progs.src +++ b/data/qcsrc/client/progs.src @@ -14,6 +14,9 @@ teamplay.qc ons.qc ctf.qc +sbar.qc +mapvoting.qc + Main.qc View.qc -sbar.qc + diff --git a/data/qcsrc/client/sbar.qc b/data/qcsrc/client/sbar.qc index 000610650..09c7c049a 100644 --- a/data/qcsrc/client/sbar.qc +++ b/data/qcsrc/client/sbar.qc @@ -1,8 +1,14 @@ +float last_weapon; +float activeweapon; +float weapontime; + float sb_lines; // still don't know what to do with that NOTE: check dp's sbar.c to see what that should be vector sbar; vector sbar_fontsize; +float sbar_alpha_fg; +float sbar_hudselector; entity sortedPlayers; entity sortedTeams; @@ -12,13 +18,22 @@ entity sortedTeams; .float sb_player; .float sb_caps; +entity team1, team2, team3, team4, teamspec; + +void CSQC_kh_hud(); +void CSQC_ctf_hud(); +void MapVote_Draw(); void Sbar_FinaleOverlay() { - vector pos; + /*vector pos; pos_x = (vid_conwidth - 1)/2; pos_y = 16; - pos_z = 0; - drawpic(pos, "gfx/finale", '0 0', '1 1 1', sbar_alpha_fg, DRAWFLAG_NORMAL); + pos_z = 0;*/ + + //drawpic(pos, "gfx/finale", '0 0', '1 1 1', sbar_alpha_fg, DRAWFLAG_NORMAL); + + //drawstring(pos, "END", sbar_fontsize, '1 1 1', 1, DRAWFLAG_NORMAL); + MapVote_Draw(); } void Sbar_DrawWeapon(float nr, float fade, float active) @@ -151,94 +166,269 @@ float Sbar_TeamCmp(entity l, entity r) return (l.sb_player > r.sb_player); } +void Sbar_Init() +{ + entity tm; + sortedPlayers = Sort_New(Sbar_PlayerCmp); + sortedTeams = Sort_New(Sbar_TeamCmp); + team1 = Sort_Next(sortedTeams); + team1.sb_team = COLOR_TEAM1; + team2 = Sort_Next(sortedTeams); + team2.sb_team = COLOR_TEAM2; + team3 = Sort_Next(sortedTeams); + team3.sb_team = COLOR_TEAM3; + team4 = Sort_Next(sortedTeams); + team4.sb_team = COLOR_TEAM4; + teamspec = Sort_Next(sortedTeams); + teamspec.sb_team = COLOR_SPECTATOR; +} + +void Sbar_UpdatePosFrags(entity player) +{ + other = player.sort_prev; + while(other != sortedPlayers && player.sb_frags > other.sb_frags) + { + SORT_SWAP(other, player); + other = player.sort_prev; + } + + other = player.sort_next; + while(other && player.sb_frags < other.sb_frags) + { + SORT_SWAP(player, other); + other = player.sort_next; + } +} +void Sbar_UpdatePosFragsCTF(entity player) +{ + other = player.sort_prev; + while(other != sortedPlayers && player.sb_frags > other.sb_frags && player.sb_caps == other.sb_caps) + { + SORT_SWAP(other, player); + other = player.sort_prev; + } + + other = player.sort_next; + while(other && player.sb_frags < other.sb_frags && player.sb_caps == other.sb_caps) + { + SORT_SWAP(player, other); + other = player.sort_next; + } +} +void Sbar_UpdatePosCaps(entity player) +{ + other = player.sort_prev; + while(other != sortedPlayers && player.sb_caps > other.sb_caps) + { + SORT_SWAP(other, player); + other = player.sort_prev; + } + other = player.sort_next; + while(other && player.sb_caps < other.sb_caps) + { + SORT_SWAP(player, other); + other = player.sort_next; + } + // let it update the frags now too, so if we have more frags then the next guy with the same caps + // we beat his ass :) + player.sb_frags -= 1; +} + +void Sbar_UpdatePosTeam(entity player) +{ + player.sb_caps -= 1; // team change needs a full update + other = player.sort_prev; + while(other != sortedPlayers && player.sb_team > other.sb_team) + { + SORT_SWAP(other, player); + other = player.sort_prev; + } + + other = player.sort_next; + while(other && player.sb_team < other.sb_team) + { + SORT_SWAP(player, other); + other = player.sort_next; + } +} + +void Sbar_UpdatePlayer(entity player) +{ + float i; + + if(player.sb_frags == -666) + i = COLOR_SPECTATOR; + else + i = GetPlayerColor(player.sb_player); + + if(player.sb_team != i) + { + player.sb_team = i; + Sbar_UpdatePosTeam(player); + } + + if(gametype == GAME_CTF) + { + i = stof(bufstr_get(databuf, DATABUF_CAPTURES + player.sb_player)); + if(player.sb_caps != i) + { + player.sb_caps = i; + Sbar_UpdatePosCaps(player); + } + i = stof(getplayerkey(player.sb_player, "frags")); + if(player.sb_frags != i) + { + player.sb_frags = i; + Sbar_UpdatePosFragsCTF(player); + } + } else { + i = stof(getplayerkey(player.sb_player, "frags")); + if(player.sb_frags != i) + { + player.sb_frags = i; + Sbar_UpdatePosFrags(player); + } + } +} + +void Sbar_UpdateTeamPosCaps(entity tm) +{ + other = tm.sort_prev; + while(other != sortedTeams && tm.sb_caps > other.sb_caps) + { + SORT_SWAP(other, tm); + other = tm.sort_prev; + } + + other = tm.sort_next; + while(other && tm.sb_caps < tm.sb_caps) + { + SORT_SWAP(tm, other); + other = tm.sort_next; + } +} +void Sbar_UpdateTeamPosFrags(entity tm) +{ + other = tm.sort_prev; + while(other != sortedTeams && tm.sb_caps == other.sb_caps && tm.sb_frags > other.sb_frags) + { + SORT_SWAP(other, tm); + other = tm.sort_prev; + } + + other = tm.sort_next; + while(other && tm.sb_caps == tm.sb_caps && tm.sb_frags < other.sb_frags) + { + SORT_SWAP(tm, other); + other = tm.sort_next; + } +} + void Sbar_SortFrags() { float i; entity tmp; - entity t1, t2, t3, t4, ts; + float t1f, t2f, t3f, t4f; - Sort_Remove(sortedPlayers); - sortedPlayers = Sort_New(Sbar_PlayerCmp); + Sort_Reset(sortedPlayers); numteams = 0; if(teamplay) { - Sort_Remove(sortedTeams); - - t1 = spawn(); - t2 = spawn(); - t3 = spawn(); - t4 = spawn(); - ts = spawn(); - - t1.sb_team = COLOR_TEAM1; - t2.sb_team = COLOR_TEAM2; - t3.sb_team = COLOR_TEAM3; - t4.sb_team = COLOR_TEAM4; - ts.sb_team = COLOR_SPECTATOR; - - t1.sb_player = t2.sb_player = t3.sb_player = t4.sb_player = ts.sb_player = 0; - t1.sb_frags = t2.sb_frags = t3.sb_frags = t4.sb_frags = 0; - t1.sb_caps = caps_team1; - t2.sb_caps = caps_team2; - sortedTeams = Sort_New(Sbar_TeamCmp); + Sort_Reset(sortedTeams); + tmp = Sort_Next(sortedTeams); + + team1.sb_player = 0; + team2.sb_player = 0; + team3.sb_player = 0; + team4.sb_player = 0; + teamspec.sb_player = 0; + + t1f = team1.sb_frags; + t2f = team2.sb_frags; + t3f = team3.sb_frags; + t4f = team4.sb_frags; + + team1.sb_frags = 0; + team2.sb_frags = 0; + team3.sb_frags = 0; + team4.sb_frags = 0; for(i = 0; i < maxclients; ++i) { if(strlen(getplayerkey(i, "name")) <= 0) continue; - - tmp = spawn(); + + Sort_Reset(sortedPlayers); + + tmp = NULL; + while(Sort_HasNext(sortedPlayers)) + { + tmp = Sort_Next(sortedPlayers); + if(tmp.sb_player == i) + break; + } + if(!tmp || tmp.sb_player != i) + tmp = Sort_Next(sortedPlayers); + tmp.sb_player = i; - tmp.sb_frags = stof(getplayerkey(i, "frags")); - tmp.sb_caps = stof(bufstr_get(databuf, DATABUF_CAPTURES + tmp.sb_player)); + tmp.frame = time; + Sbar_UpdatePlayer(tmp); - if(tmp.sb_frags == -666) - tmp.sb_team = COLOR_SPECTATOR; - else - tmp.sb_team = GetPlayerColor(i); - switch(tmp.sb_team) { - case COLOR_TEAM1: t1.sb_frags += tmp.sb_frags; t1.sb_player++; break; - case COLOR_TEAM2: t2.sb_frags += tmp.sb_frags; t2.sb_player++; break; - case COLOR_TEAM3: t3.sb_frags += tmp.sb_frags; t3.sb_player++; break; - case COLOR_TEAM4: t4.sb_frags += tmp.sb_frags; t4.sb_player++; break; - case COLOR_SPECTATOR: ts.sb_frags += tmp.sb_frags; ts.sb_player++; break; + case COLOR_TEAM1: team1.sb_frags += tmp.sb_frags; team1.sb_player++; break; + case COLOR_TEAM2: team2.sb_frags += tmp.sb_frags; team2.sb_player++; break; + case COLOR_TEAM3: team3.sb_frags += tmp.sb_frags; team3.sb_player++; break; + case COLOR_TEAM4: team4.sb_frags += tmp.sb_frags; team4.sb_player++; break; + case COLOR_SPECTATOR: teamspec.sb_frags += tmp.sb_frags; teamspec.sb_player++; break; } if(i == player_localentnum-1) myteam = tmp.sb_team; - - Sort_Add(sortedPlayers, tmp); } - if(t1.sb_player) ++numteams; - if(t2.sb_player) ++numteams; - if(t3.sb_player) ++numteams; - if(t4.sb_player) ++numteams; - - Sort_Add(sortedTeams, t1); - Sort_Add(sortedTeams, t2); - Sort_Add(sortedTeams, t3); - Sort_Add(sortedTeams, t4); - Sort_Add(sortedTeams, ts); + if(team1.sb_player) ++numteams; + if(team2.sb_player) ++numteams; + if(team3.sb_player) ++numteams; + if(team4.sb_player) ++numteams; + if(team1.sb_caps != caps_team1) + { + team1.sb_caps = caps_team1; + Sbar_UpdateTeamPosCaps(team1); + } + if(team2.sb_caps != caps_team2) + { + team2.sb_caps = caps_team2; + Sbar_UpdateTeamPosCaps(team2); + } + if(team1.sb_frags != t1f) Sbar_UpdateTeamPosFrags(team1); + if(team2.sb_frags != t2f) Sbar_UpdateTeamPosFrags(team2); + if(team3.sb_frags != t3f) Sbar_UpdateTeamPosFrags(team3); + if(team4.sb_frags != t4f) Sbar_UpdateTeamPosFrags(team4); } else { for(i = 0; i < maxclients; ++i) { if(strlen(getplayerkey(i, "name")) <= 0) continue; - tmp = spawn(); + Sort_Reset(sortedPlayers); + tmp = NULL; + while(Sort_HasNext(sortedPlayers)) + { + tmp = Sort_Next(sortedPlayers); + if(tmp.sb_player == i) + break; + } + if(!tmp || tmp.sb_player != i) + tmp = Sort_Next(sortedPlayers); + tmp.sb_player = i; - tmp.sb_frags = stof(getplayerkey(i, "frags")); - if(tmp.sb_frags == -666) - tmp.sb_team = COLOR_SPECTATOR; - else - tmp.sb_team = COLOR_TEAM1; - Sort_Add(sortedPlayers, tmp); + tmp.frame = time; + Sbar_UpdatePlayer(tmp); } } + Sort_RemoveOld(sortedPlayers); } void Cmd_Sbar_Help(float argc) @@ -367,7 +557,8 @@ string Sbar_GetField(entity pl, float field) return str; } } - +#define SBAR_DEFAULT_MASK 0 +#define SBAR_MASK_SPECTATORS 1 float Sbar_IsFieldMasked(float field, float mask) { if(mask&1) // spectator @@ -425,7 +616,7 @@ float xmin, xmax, ymin, ymax, sbwidth, sbheight; void Sbar_PrintScoreboardItem(vector pos, entity pl, float is_self, float mask) { vector tmp; - string str, tempstr; + string str; float i, field, len; // Layout: @@ -517,7 +708,6 @@ void Sbar_DrawScoreboard() entity pl, tm; float specs, i; float center_x; - string str; sbar_fontsize = Sbar_GetFontsize(); if(sbar_fontsize_x == 0) @@ -557,7 +747,7 @@ void Sbar_DrawScoreboard() for(i = 0; i < sbar_num_fields; ++i) { - if(Sbar_IsFieldMasked(sbar_field[i])) + if(Sbar_IsFieldMasked(sbar_field[i], SBAR_DEFAULT_MASK)) continue; if(sbar_field[i] == SBF_SEPARATOR) break; @@ -571,7 +761,7 @@ void Sbar_DrawScoreboard() tmp_y = tmp_z = 0; for(i = sbar_num_fields-1; i > 0; --i) { - if(Sbar_IsFieldMasked(sbar_field[i])) + if(Sbar_IsFieldMasked(sbar_field[i], SBAR_DEFAULT_MASK)) continue; if(sbar_field[i] == SBF_SEPARATOR) break; @@ -639,7 +829,7 @@ void Sbar_DrawScoreboard() { if(pl.sb_team != tm.sb_team) continue; - Sbar_PrintScoreboardItem(pos, pl, (pl.sb_player == player_localentnum - 1), 0); + Sbar_PrintScoreboardItem(pos, pl, (pl.sb_player == player_localentnum - 1), SBAR_DEFAULT_MASK); pos_y += 1.25 * sbar_fontsize_y; tmp_y -= 1.25 * sbar_fontsize_y; } @@ -654,7 +844,7 @@ void Sbar_DrawScoreboard() if(pl.sb_team != COLOR_SPECTATOR) continue; //drawcolorcodedstring(pos, getplayerkey(pl.sb_player, "name"), '8 8 0', 1, 0); - Sbar_PrintScoreboardItem(pos, pl, (pl.sb_player == player_localentnum - 1), 1); + Sbar_PrintScoreboardItem(pos, pl, (pl.sb_player == player_localentnum - 1), SBAR_MASK_SPECTATORS); pos += '0 1.25 0' * sbar_fontsize_y; ++specs; } @@ -667,11 +857,10 @@ void Sbar_DrawScoreboard() { if(pl.sb_team == COLOR_SPECTATOR) continue; - Sbar_PrintScoreboardItem(pos, pl, (pl.sb_player == player_localentnum - 1), 0); + Sbar_PrintScoreboardItem(pos, pl, (pl.sb_player == player_localentnum - 1), SBAR_DEFAULT_MASK); pos_y += 1.25 * sbar_fontsize_y; tmp_y -= 1.25 * sbar_fontsize_y; } - pos_y += tmp_y + 1.5 * sbar_fontsize_y; // rgb := tempvector :) rgb = pos + '0 1.5 0' * sbar_fontsize_y; @@ -682,7 +871,7 @@ void Sbar_DrawScoreboard() if(pl.sb_team != COLOR_SPECTATOR) continue; //drawcolorcodedstring(pos, getplayerkey(pl.sb_player, "name"), '8 8 0', 1, 0); - Sbar_PrintScoreboardItem(pos, pl, (pl.sb_player == player_localentnum - 1), 1); + Sbar_PrintScoreboardItem(pos, pl, (pl.sb_player == player_localentnum - 1), SBAR_MASK_SPECTATORS); pos += '0 1.25 0' * sbar_fontsize_y; ++specs; } diff --git a/data/qcsrc/client/sortlist.qc b/data/qcsrc/client/sortlist.qc index 8a1f34736..991d869d3 100644 --- a/data/qcsrc/client/sortlist.qc +++ b/data/qcsrc/client/sortlist.qc @@ -1,5 +1,3 @@ - - .float(entity,entity) sort_cmp; .entity sort_next, sort_prev; @@ -9,6 +7,7 @@ entity Sort_New(float(entity,entity) cmp) sort = spawn(); sort.sort_cmp = cmp; sort.sort_next = NULL; + sort.chain = sort; return sort; } @@ -29,8 +28,10 @@ void Sort_Add(entity sort, entity ent) entity next, parent; parent = sort; next = sort.sort_next; - while(next && sort.sort_cmp(next, ent)) + while(next) { + if(!sort.sort_cmp(next, ent)) + break; parent = next; next = next.sort_next; } @@ -41,6 +42,45 @@ void Sort_Add(entity sort, entity ent) next.sort_prev = ent; } +void Sort_Reset(entity sort) +{ + sort.chain = sort; +} + +float Sort_HasNext(entity sort) +{ + return (sort.chain.sort_next != NULL); +} + +entity Sort_Next(entity sort) +{ + entity next; + next = sort.chain.sort_next; + if(!next) { + next = spawn(); + sort.chain.sort_next = next; + next.sort_prev = sort.chain; + next.sort_next = NULL; + } + sort.chain = next; + return next; +} + +void Sort_Finish(entity sort) +{ + entity next; + next = sort.chain; + if(!next) + return; + + while(next.sort_next) + { + sort = next.sort_next; + next.sort_next = sort.sort_next; + remove(sort); + } +} + entity Sort_Get(entity sort, float i) { for(; sort.sort_next && i > 0; --i) @@ -48,16 +88,31 @@ entity Sort_Get(entity sort, float i) return sort; } -void Sort_DoSort(entity sort) +#define SORT_SWAP(a,b) \ + b.sort_prev = a.sort_prev; \ + a.sort_next = b.sort_next; \ + if(b.sort_next) b.sort_next.sort_prev = a; \ + a.sort_prev.sort_next = b; \ + a.sort_prev = b; \ + b.sort_next = a + +void Sort_Erase(entity ent) { - entity newsort, next, tmp; - newsort = Sort_New(sort.sort_cmp); - next = sort.sort_next; - while(next) + ent.sort_prev.sort_next = ent.sort_next; + if(ent.sort_next) + ent.sort_next.sort_prev = ent.sort_prev; + remove(ent); +} + +void Sort_RemoveOld(entity sort) +{ + entity tmp; + for(tmp = sort.sort_next; tmp; tmp = tmp.sort_next) { - tmp = next.sort_next; - Sort_Add(newsort, next); - next = tmp; + if(tmp.frame < time) + { + tmp = tmp.sort_prev; + Sort_Erase(tmp.sort_next); + } } - sort.sort_next = newsort.sort_next; } diff --git a/data/qcsrc/client/teamplay.qc b/data/qcsrc/client/teamplay.qc index 8d2c7af88..39d6e52c8 100644 --- a/data/qcsrc/client/teamplay.qc +++ b/data/qcsrc/client/teamplay.qc @@ -1,3 +1,6 @@ +float numteams; // NOTE: This is changed in Sbar_SortFrags, so use it only AFTER that +float teamplay; +float myteam; float TeamByColor(float color) { diff --git a/data/qcsrc/common/constants.qh b/data/qcsrc/common/constants.qh index 65da3fab9..d82e203a1 100644 --- a/data/qcsrc/common/constants.qh +++ b/data/qcsrc/common/constants.qh @@ -1,5 +1,7 @@ // COMMIT-TODO: Update if necessary before committing -#define CSQC_REVISION 1 +// Revision 1: additional statistics sent (flag caps, returns, deaths) +// Revision 2: Mapvote preview pictures +#define CSQC_REVISION 2 // probably put these in common/ // so server/ and client/ can be synced better @@ -163,14 +165,13 @@ const float ENTCS_MSG_END = 0; const float ENTCS_MSG_ONS_GPS = 1; const float ENTCS_MSG_ONS_REMOVE = 2; -const float TE_CSQC_START = 100; const float TE_CSQC_INIT = 100; const float TE_CSQC_PING = 101; const float TE_CSQC_CAPTURES = 102; const float TE_CSQC_RETURNS = 103; const float TE_CSQC_DEATHS = 104; - -const float TE_CSQC_END = 105; +const float TE_CSQC_PICTURE = 105; +const float TE_CSQC_MAPVOTE = 106; const float STAT_KH_KEYS = 32; const float STAT_CTF_STATE = 33; @@ -178,3 +179,10 @@ const float CTF_STATE_ATTACK = 1; const float CTF_STATE_DEFEND = 2; const float CTF_STATE_COMMANDER = 3; +// moved that here so the client knows the max. +// # of maps, I'll use arrays for them :P +#define MAPVOTE_COUNT 10 +const float MAPVOTE_NET_INIT = 0; +const float MAPVOTE_NET_UPDATE = 1; +const float MAPVOTE_NET_PIC = 2; +const float MAPVOTE_NET_OWNVOTE = 3; diff --git a/data/qcsrc/server/cl_client.qc b/data/qcsrc/server/cl_client.qc index c73025af1..01ab1a073 100644 --- a/data/qcsrc/server/cl_client.qc +++ b/data/qcsrc/server/cl_client.qc @@ -1147,6 +1147,11 @@ void ClientConnect (void) ctf_UpdateCaptures(MSG_ONE); ctf_UpdateReturns(MSG_ONE); net_UpdateDeaths(MSG_ONE); + if(mapvote_initialized && !cvar("g_maplist_textonly")) + { + MapVote_SendData(MSG_ONE); + MapVote_UpdateData(MSG_ONE); + } } } diff --git a/data/qcsrc/server/clientcommands.qc b/data/qcsrc/server/clientcommands.qc index 7d05afbc1..cf45fa268 100644 --- a/data/qcsrc/server/clientcommands.qc +++ b/data/qcsrc/server/clientcommands.qc @@ -172,6 +172,8 @@ void SV_ParseClientCommand(string s) { if(GameCommand_Vote(s, self)) { return; + } else if(GameCommand_MapVote(argv(0))) { + return; } else if(argv(0) == "autoswitch") { // be backwards compatible with older clients (enabled) self.autoswitch = ("0" != argv(1)); diff --git a/data/qcsrc/server/g_world.qc b/data/qcsrc/server/g_world.qc index 621f7fee6..f6446a5f4 100644 --- a/data/qcsrc/server/g_world.qc +++ b/data/qcsrc/server/g_world.qc @@ -1921,7 +1921,6 @@ float mapvote_keeptwotime; float mapvote_timeout; string mapvote_message; -#define MAPVOTE_COUNT 10 float mapvote_count; float mapvote_count_real; string mapvote_maps[MAPVOTE_COUNT]; @@ -2006,6 +2005,7 @@ void MapVote_AddVotable(string nextMap, float isSuggestion) mapvote_count += 1; } +void MapVote_SendData(float target); void MapVote_Init() { float i; @@ -2053,7 +2053,87 @@ void MapVote_Init() if(mapvote_count_real < 3 || mapvote_keeptwotime <= time) mapvote_keeptwotime = 0; mapvote_message = "Choose a map and press its key!"; + + if(!cvar("g_maplist_textonly")) + MapVote_SendData(MSG_BROADCAST); +} + +void MapVote_SendPicture(float id) +{ + msg_entity = self; + WriteByte(MSG_ONE, SVC_TEMPENTITY); + WriteByte(MSG_ONE, TE_CSQC_PICTURE); + WritePicture(MSG_ONE, strcat("maps/", mapvote_maps[id]), 1024); + WriteByte(MSG_ONE, SVC_TEMPENTITY); + WriteByte(MSG_ONE, TE_CSQC_MAPVOTE); + WriteByte(MSG_ONE, MAPVOTE_NET_PIC); + WriteByte(MSG_ONE, id); +} + +float GameCommand_MapVote(string cmd) +{ + if(!intermission_running) + return FALSE; + if(!cvar("g_maplist_textonly")) + { + if(cmd == "mv_getpic") + { + MapVote_SendPicture(stof(argv(1))); + return TRUE; + } + } + + return FALSE; +} + +void MapVote_SendData(float targ) +{ + string mapfile; + float i, o, c; + WriteByte(targ, SVC_TEMPENTITY); + WriteByte(targ, TE_CSQC_MAPVOTE); + WriteByte(targ, MAPVOTE_NET_INIT); + WriteByte(targ, mapvote_count); + WriteByte(targ, mapvote_abstain); + WriteByte(targ, mapvote_detail); + for(i = 0; i < mapvote_count; ++i) + { + WriteString(targ, mapvote_maps[i]); + mapfile = strcat("maps/", mapvote_maps[i], ".bsp"); + localcmd(strcat("\ncurl --pak ", whichpack(mapfile), "\n")); + mapfile = whichpack(mapfile); + for(o = strstr(mapfile, "/", 0)+1; o > 0; o = strstr(mapfile, "/", 0)+1) + mapfile = substring(mapfile, o, 999); + for(o = strstr(mapfile, "\\", 0)+1; o > 0; o = strstr(mapfile, "\\", 0)+1) + mapfile = substring(mapfile, o, 999); + WriteString(targ, mapfile); + } +} + +void MapVote_UpdateData(float targ) +{ + float i; + WriteByte(targ, SVC_TEMPENTITY); + WriteByte(targ, TE_CSQC_MAPVOTE); + WriteByte(targ, MAPVOTE_NET_UPDATE); + for(i = 0; i < mapvote_count; ++i) + { + if(strlen(mapvote_maps[i]) <= 0) + WriteByte(targ, 0); + else + WriteByte(targ, 1); + WriteByte(targ, mapvote_votes[i]); + } } +void MapVote_TellVote(float targ, float vote) +{ + float i; + WriteByte(targ, SVC_TEMPENTITY); + WriteByte(targ, TE_CSQC_MAPVOTE); + WriteByte(targ, MAPVOTE_NET_OWNVOTE); + WriteByte(targ, vote); +} + float MapVote_Finished(float mappos) { string result; @@ -2111,6 +2191,13 @@ void MapVote_CheckRules_1() mapvote_votes[i] = mapvote_votes[i] + 1; } } + + if(!cvar("g_maplist_textonly")) + { + MapVote_UpdateData(MSG_BROADCAST); + FOR_EACH_REALCLIENT(msg_entity) + MapVote_TellVote(MSG_ONE, msg_entity.mapvote); + } } float MapVote_CheckRules_2() @@ -2231,51 +2318,54 @@ void MapVote_Tick() MapVote_CheckRules_1(); // just count - FOR_EACH_REALCLIENT(other) + if(cvar("g_maplist_textonly")) { - // display voting screen - msgstr = "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"; - msgstr = substring(msgstr, 0, strlen(msgstr) - mapvote_count); - if(mapvote_abstain) - msgstr = substring(msgstr, 1, strlen(msgstr) - 1); - msgstr = strcat(msgstr, mapvote_message); - msgstr = strcat(msgstr, "\n\n"); - for(i = 0; i < mapvote_count; ++i) - if(mapvote_maps[i] == "") - msgstr = strcat(msgstr, "\n"); - else - { - tmp = mapvote_maps[i]; - tmp = strpad(mapvote_maxlen, tmp); - tmp = strcat(ftos(mod(i + 1, 10)), ": ", tmp); - if(mapvote_detail) - { - tmp = strcat(tmp, " ^2(", ftos(mapvote_votes[i]), " vote"); - if(mapvote_votes[i] != 1) - tmp = strcat(tmp, "s"); - tmp = strcat(tmp, ")"); - tmp = strpad(mapvote_maxlen + 15, tmp); - } - if(mapvote_abstain) - if(i == mapvote_count - 1) - msgstr = strcat(msgstr, "\n"); - if(other.mapvote == i + 1) - msgstr = strcat(msgstr, "^3> ", tmp, "\n"); + FOR_EACH_REALCLIENT(other) + { + // display voting screen + msgstr = "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"; + msgstr = substring(msgstr, 0, strlen(msgstr) - mapvote_count); + if(mapvote_abstain) + msgstr = substring(msgstr, 1, strlen(msgstr) - 1); + msgstr = strcat(msgstr, mapvote_message); + msgstr = strcat(msgstr, "\n\n"); + for(i = 0; i < mapvote_count; ++i) + if(mapvote_maps[i] == "") + msgstr = strcat(msgstr, "\n"); else - msgstr = strcat(msgstr, "^7 ", tmp, "\n"); - } + { + tmp = mapvote_maps[i]; + tmp = strpad(mapvote_maxlen, tmp); + tmp = strcat(ftos(mod(i + 1, 10)), ": ", tmp); + if(mapvote_detail) + { + tmp = strcat(tmp, " ^2(", ftos(mapvote_votes[i]), " vote"); + if(mapvote_votes[i] != 1) + tmp = strcat(tmp, "s"); + tmp = strcat(tmp, ")"); + tmp = strpad(mapvote_maxlen + 15, tmp); + } + if(mapvote_abstain) + if(i == mapvote_count - 1) + msgstr = strcat(msgstr, "\n"); + if(other.mapvote == i + 1) + msgstr = strcat(msgstr, "^3> ", tmp, "\n"); + else + msgstr = strcat(msgstr, "^7 ", tmp, "\n"); + } - msgstr = strcat(msgstr, "\n\n^2", ftos(totalvotes), " vote"); - if(totalvotes != 1) - msgstr = strcat(msgstr, "s"); - msgstr = strcat(msgstr, " cast"); - i = ceil(mapvote_timeout - time); - msgstr = strcat(msgstr, "\n", ftos(i), " second"); - if(i != 1) - msgstr = strcat(msgstr, "s"); - msgstr = strcat(msgstr, " left"); - - centerprint_atprio(other, CENTERPRIO_MAPVOTE, msgstr); + msgstr = strcat(msgstr, "\n\n^2", ftos(totalvotes), " vote"); + if(totalvotes != 1) + msgstr = strcat(msgstr, "s"); + msgstr = strcat(msgstr, " cast"); + i = ceil(mapvote_timeout - time); + msgstr = strcat(msgstr, "\n", ftos(i), " second"); + if(i != 1) + msgstr = strcat(msgstr, "s"); + msgstr = strcat(msgstr, " left"); + + centerprint_atprio(other, CENTERPRIO_MAPVOTE, msgstr); + } } } void MapVote_Start() -- 2.39.2