1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780 |
- // Copyright (c) ZeniMax Media Inc.
- // Licensed under the GNU General Public License 2.0.
- #include "cg_local.h"
- constexpr int32_t STAT_MINUS = 10; // num frame for '-' stats digit
- constexpr const char *sb_nums[2][11] =
- {
- { "num_0", "num_1", "num_2", "num_3", "num_4", "num_5",
- "num_6", "num_7", "num_8", "num_9", "num_minus"
- },
- { "anum_0", "anum_1", "anum_2", "anum_3", "anum_4", "anum_5",
- "anum_6", "anum_7", "anum_8", "anum_9", "anum_minus"
- }
- };
- constexpr int32_t CHAR_WIDTH = 16;
- constexpr int32_t CONCHAR_WIDTH = 8;
- static int32_t font_y_offset;
- constexpr rgba_t alt_color { 112, 255, 52, 255 };
- static cvar_t *scr_usekfont;
- static cvar_t *scr_centertime;
- static cvar_t *scr_printspeed;
- static cvar_t *cl_notifytime;
- static cvar_t *scr_maxlines;
- static cvar_t *ui_acc_contrast;
- static cvar_t* ui_acc_alttypeface;
- // static temp data used for hud
- static struct
- {
- struct {
- struct {
- char text[24];
- } table_cells[6];
- } table_rows[11]; // just enough to store 8 levels + header + total (+ one slack)
- size_t column_widths[6];
- int32_t num_rows = 0;
- int32_t num_columns = 0;
- } hud_temp;
- #include <vector>
- // max number of centerprints in the rotating buffer
- constexpr size_t MAX_CENTER_PRINTS = 4;
- struct cl_bind_t {
- std::string bind;
- std::string purpose;
- };
- struct cl_centerprint_t {
- std::vector<cl_bind_t> binds; // binds
- std::vector<std::string> lines;
- bool instant; // don't type out
- size_t current_line; // current line we're typing out
- size_t line_count; // byte count to draw on current line
- bool finished; // done typing it out
- uint64_t time_tick, time_off; // time to remove at
- };
- inline bool CG_ViewingLayout(const player_state_t *ps)
- {
- return ps->stats[STAT_LAYOUTS] & (LAYOUTS_LAYOUT | LAYOUTS_INVENTORY);
- }
- inline bool CG_InIntermission(const player_state_t *ps)
- {
- return ps->stats[STAT_LAYOUTS] & LAYOUTS_INTERMISSION;
- }
- inline bool CG_HudHidden(const player_state_t *ps)
- {
- return ps->stats[STAT_LAYOUTS] & LAYOUTS_HIDE_HUD;
- }
- layout_flags_t CG_LayoutFlags(const player_state_t *ps)
- {
- return (layout_flags_t) ps->stats[STAT_LAYOUTS];
- }
- #include <optional>
- #include <array>
- constexpr size_t MAX_NOTIFY = 8;
- struct cl_notify_t {
- std::string message; // utf8 message
- bool is_active; // filled or not
- bool is_chat; // green or not
- uint64_t time; // rotate us when < CL_Time()
- };
- // per-splitscreen client hud storage
- struct hud_data_t {
- std::array<cl_centerprint_t, MAX_CENTER_PRINTS> centers; // list of centers
- std::optional<size_t> center_index; // current index we're drawing, or unset if none left
- std::array<cl_notify_t, MAX_NOTIFY> notify; // list of notifies
- };
- static std::array<hud_data_t, MAX_SPLIT_PLAYERS> hud_data;
- void CG_ClearCenterprint(int32_t isplit)
- {
- hud_data[isplit].center_index = {};
- }
- void CG_ClearNotify(int32_t isplit)
- {
- for (auto &msg : hud_data[isplit].notify)
- msg.is_active = false;
- }
- // if the top one is expired, cycle the ones ahead backwards (since
- // the times are always increasing)
- static void CG_Notify_CheckExpire(hud_data_t &data)
- {
- while (data.notify[0].is_active && data.notify[0].time < cgi.CL_ClientTime())
- {
- data.notify[0].is_active = false;
- for (size_t i = 1; i < MAX_NOTIFY; i++)
- if (data.notify[i].is_active)
- std::swap(data.notify[i], data.notify[i - 1]);
- }
- }
- // add notify to list
- static void CG_AddNotify(hud_data_t &data, const char *msg, bool is_chat)
- {
- size_t i = 0;
- if (scr_maxlines->integer <= 0)
- return;
- const int max = min(MAX_NOTIFY, (size_t)scr_maxlines->integer);
- for (; i < max; i++)
- if (!data.notify[i].is_active)
- break;
- // none left, so expire the topmost one
- if (i == max)
- {
- data.notify[0].time = 0;
- CG_Notify_CheckExpire(data);
- i = max - 1;
- }
-
- data.notify[i].message.assign(msg);
- data.notify[i].is_active = true;
- data.notify[i].is_chat = is_chat;
- data.notify[i].time = cgi.CL_ClientTime() + (cl_notifytime->value * 1000);
- }
- // draw notifies
- static void CG_DrawNotify(int32_t isplit, vrect_t hud_vrect, vrect_t hud_safe, int32_t scale)
- {
- auto &data = hud_data[isplit];
- CG_Notify_CheckExpire(data);
- int y;
-
- y = (hud_vrect.y * scale) + hud_safe.y;
- cgi.SCR_SetAltTypeface(ui_acc_alttypeface->integer && true);
- if (ui_acc_contrast->integer)
- {
- for (auto& msg : data.notify)
- {
- if (!msg.is_active || !msg.message.length())
- break;
- vec2_t sz = cgi.SCR_MeasureFontString(msg.message.c_str(), scale);
- sz.x += 10; // extra padding for black bars
- cgi.SCR_DrawColorPic((hud_vrect.x * scale) + hud_safe.x - 5, y, sz.x, 15 * scale, "_white", rgba_black);
- y += 10 * scale;
- }
- }
- y = (hud_vrect.y * scale) + hud_safe.y;
- for (auto &msg : data.notify)
- {
- if (!msg.is_active)
- break;
- cgi.SCR_DrawFontString(msg.message.c_str(), (hud_vrect.x * scale) + hud_safe.x, y, scale, msg.is_chat ? alt_color : rgba_white, true, text_align_t::LEFT);
- y += 10 * scale;
- }
- cgi.SCR_SetAltTypeface(false);
- // draw text input (only the main player can really chat anyways...)
- if (isplit == 0)
- {
- const char *input_msg;
- bool input_team;
- if (cgi.CL_GetTextInput(&input_msg, &input_team))
- cgi.SCR_DrawFontString(G_Fmt("{}: {}", input_team ? "say_team" : "say", input_msg).data(), (hud_vrect.x * scale) + hud_safe.x, y, scale, rgba_white, true, text_align_t::LEFT);
- }
- }
- /*
- ==============
- CG_DrawHUDString
- ==============
- */
- static int CG_DrawHUDString (const char *string, int x, int y, int centerwidth, int _xor, int scale, bool shadow = true)
- {
- int margin;
- char line[1024];
- int width;
- int i;
- margin = x;
- while (*string)
- {
- // scan out one line of text from the string
- width = 0;
- while (*string && *string != '\n')
- line[width++] = *string++;
- line[width] = 0;
- vec2_t size;
-
- if (scr_usekfont->integer)
- size = cgi.SCR_MeasureFontString(line, scale);
- if (centerwidth)
- {
- if (!scr_usekfont->integer)
- x = margin + ((centerwidth - width*CONCHAR_WIDTH*scale))/2;
- else
- x = margin + ((centerwidth - size.x))/2;
- }
- else
- x = margin;
- if (!scr_usekfont->integer)
- {
- for (i=0 ; i<width ; i++)
- {
- cgi.SCR_DrawChar (x, y, scale, line[i]^_xor, shadow);
- x += CONCHAR_WIDTH * scale;
- }
- }
- else
- {
- cgi.SCR_DrawFontString(line, x, y - (font_y_offset * scale), scale, _xor ? alt_color : rgba_white, true, text_align_t::LEFT);
- x += size.x;
- }
- if (*string)
- {
- string++; // skip the \n
- x = margin;
- if (!scr_usekfont->integer)
- y += CONCHAR_WIDTH * scale;
- else
- // TODO
- y += 10 * scale;//size.y;
- }
- }
- return x;
- }
- // Shamefully stolen from Kex
- size_t FindStartOfUTF8Codepoint(const std::string &str, size_t pos)
- {
- if(pos >= str.size())
- {
- return std::string::npos;
- }
- for(ptrdiff_t i = pos; i >= 0; i--)
- {
- const char &ch = str[i];
- if((ch & 0x80) == 0)
- {
- // character is one byte
- return i;
- }
- else if((ch & 0xC0) == 0x80)
- {
- // character is part of a multi-byte sequence, keep going
- continue;
- }
- else
- {
- // character is the start of a multi-byte sequence, so stop now
- return i;
- }
- }
- return std::string::npos;
- }
- size_t FindEndOfUTF8Codepoint(const std::string &str, size_t pos)
- {
- if(pos >= str.size())
- {
- return std::string::npos;
- }
- for(size_t i = pos; i < str.size(); i++)
- {
- const char &ch = str[i];
- if((ch & 0x80) == 0)
- {
- // character is one byte
- return i;
- }
- else if((ch & 0xC0) == 0x80)
- {
- // character is part of a multi-byte sequence, keep going
- continue;
- }
- else
- {
- // character is the start of a multi-byte sequence, so stop now
- return i;
- }
- }
- return std::string::npos;
- }
- void CG_NotifyMessage(int32_t isplit, const char *msg, bool is_chat)
- {
- CG_AddNotify(hud_data[isplit], msg, is_chat);
- }
- // centerprint stuff
- static cl_centerprint_t &CG_QueueCenterPrint(int isplit, bool instant)
- {
- auto &icl = hud_data[isplit];
- // just use first index
- if (!icl.center_index.has_value() || instant)
- {
- icl.center_index = 0;
- for (size_t i = 1; i < MAX_CENTER_PRINTS; i++)
- icl.centers[i].lines.clear();
- return icl.centers[0];
- }
- // pick the next free index if we can find one
- for (size_t i = 1; i < MAX_CENTER_PRINTS; i++)
- {
- auto ¢er = icl.centers[(icl.center_index.value() + i) % MAX_CENTER_PRINTS];
- if (center.lines.empty())
- return center;
- }
-
- // none, so update the current one (the new end of buffer)
- // and skip ahead
- auto ¢er = icl.centers[icl.center_index.value()];
- icl.center_index = (icl.center_index.value() + 1) % MAX_CENTER_PRINTS;
- return center;
- }
- /*
- ==============
- SCR_CenterPrint
- Called for important messages that should stay in the center of the screen
- for a few moments
- ==============
- */
- void CG_ParseCenterPrint (const char *str, int isplit, bool instant) // [Sam-KEX] Made 1st param const
- {
- const char *s;
- char line[64];
- int i, j, l;
- // handle center queueing
- cl_centerprint_t ¢er = CG_QueueCenterPrint(isplit, instant);
- center.lines.clear();
- // split the string into lines
- size_t line_start = 0;
- std::string string(str);
- center.binds.clear();
- // [Paril-KEX] pull out bindings. they'll always be at the start
- while (string.compare(0, 6, "%bind:") == 0)
- {
- size_t end_of_bind = string.find_first_of('%', 1);
- if (end_of_bind == std::string::npos)
- break;
- std::string bind = string.substr(6, end_of_bind - 6);
- if (auto purpose_index = bind.find_first_of(':'); purpose_index != std::string::npos)
- center.binds.emplace_back(cl_bind_t { bind.substr(0, purpose_index), bind.substr(purpose_index + 1) });
- else
- center.binds.emplace_back(cl_bind_t { bind });
- string = string.substr(end_of_bind + 1);
- }
- // echo it to the console
- cgi.Com_Print("\n\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n\n");
- s = string.c_str();
- do
- {
- // scan the width of the line
- for (l=0 ; l<40 ; l++)
- if (s[l] == '\n' || !s[l])
- break;
- for (i=0 ; i<(40-l)/2 ; i++)
- line[i] = ' ';
- for (j=0 ; j<l ; j++)
- {
- line[i++] = s[j];
- }
- line[i] = '\n';
- line[i+1] = 0;
- cgi.Com_Print(line);
- while (*s && *s != '\n')
- s++;
- if (!*s)
- break;
- s++; // skip the \n
- } while (1);
- cgi.Com_Print("\n\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n\n");
- CG_ClearNotify (isplit);
- for (size_t line_end = 0; ; )
- {
- line_end = FindEndOfUTF8Codepoint(string, line_end);
- if (line_end == std::string::npos)
- {
- // final line
- if (line_start < string.size())
- center.lines.emplace_back(string.c_str() + line_start);
- break;
- }
-
- // char part of current line;
- // if newline, end line and cut off
- const char &ch = string[line_end];
- if (ch == '\n')
- {
- if (line_end > line_start)
- center.lines.emplace_back(string.c_str() + line_start, line_end - line_start);
- else
- center.lines.emplace_back();
- line_start = line_end + 1;
- line_end++;
- continue;
- }
- line_end++;
- }
- if (center.lines.empty())
- {
- center.finished = true;
- return;
- }
- center.time_tick = cgi.CL_ClientRealTime() + (scr_printspeed->value * 1000);
- center.instant = instant;
- center.finished = false;
- center.current_line = 0;
- center.line_count = 0;
- }
- static void CG_DrawCenterString( const player_state_t *ps, const vrect_t &hud_vrect, const vrect_t &hud_safe, int isplit, int scale, cl_centerprint_t ¢er)
- {
- int32_t y = hud_vrect.y * scale;
-
- if (CG_ViewingLayout(ps))
- y += hud_safe.y;
- else if (center.lines.size() <= 4)
- y += (hud_vrect.height * 0.2f) * scale;
- else
- y += 48 * scale;
- int lineHeight = (scr_usekfont->integer ? 10 : 8) * scale;
- if (ui_acc_alttypeface->integer) lineHeight *= 1.5f;
- // easy!
- if (center.instant)
- {
- for (size_t i = 0; i < center.lines.size(); i++)
- {
- auto &line = center.lines[i];
- cgi.SCR_SetAltTypeface(ui_acc_alttypeface->integer && true);
- if (ui_acc_contrast->integer && line.length())
- {
- vec2_t sz = cgi.SCR_MeasureFontString(line.c_str(), scale);
- sz.x += 10; // extra padding for black bars
- int barY = ui_acc_alttypeface->integer ? y - 8 : y;
- cgi.SCR_DrawColorPic((hud_vrect.x + hud_vrect.width / 2) * scale - (sz.x / 2), barY, sz.x, lineHeight, "_white", rgba_black);
- }
- CG_DrawHUDString(line.c_str(), (hud_vrect.x + hud_vrect.width/2 + -160) * scale, y, (320 / 2) * 2 * scale, 0, scale);
- cgi.SCR_SetAltTypeface(false);
- y += lineHeight;
- }
- for (auto &bind : center.binds)
- {
- y += lineHeight * 2;
- cgi.SCR_DrawBind(isplit, bind.bind.c_str(), bind.purpose.c_str(), (hud_vrect.x + (hud_vrect.width / 2)) * scale, y, scale);
- }
- if (!center.finished)
- {
- center.finished = true;
- center.time_off = cgi.CL_ClientRealTime() + (scr_centertime->value * 1000);
- }
- return;
- }
- // hard and annoying!
- // check if it's time to fetch a new char
- const uint64_t t = cgi.CL_ClientRealTime();
- if (!center.finished)
- {
- if (center.time_tick < t)
- {
- center.time_tick = t + (scr_printspeed->value * 1000);
- center.line_count = FindEndOfUTF8Codepoint(center.lines[center.current_line], center.line_count + 1);
- if (center.line_count == std::string::npos)
- {
- center.current_line++;
- center.line_count = 0;
- if (center.current_line == center.lines.size())
- {
- center.current_line--;
- center.finished = true;
- center.time_off = t + (scr_centertime->value * 1000);
- }
- }
- }
- }
- // smallish byte buffer for single line of data...
- char buffer[256];
- for (size_t i = 0; i < center.lines.size(); i++)
- {
- cgi.SCR_SetAltTypeface(ui_acc_alttypeface->integer && true);
- auto &line = center.lines[i];
- buffer[0] = 0;
- if (center.finished || i != center.current_line)
- Q_strlcpy(buffer, line.c_str(), sizeof(buffer));
- else
- Q_strlcpy(buffer, line.c_str(), min(center.line_count + 1, sizeof(buffer)));
- int blinky_x;
- if (ui_acc_contrast->integer && line.length())
- {
- vec2_t sz = cgi.SCR_MeasureFontString(line.c_str(), scale);
- sz.x += 10; // extra padding for black bars
- int barY = ui_acc_alttypeface->integer ? y - 8 : y;
- cgi.SCR_DrawColorPic((hud_vrect.x + hud_vrect.width / 2) * scale - (sz.x / 2), barY, sz.x, lineHeight, "_white", rgba_black);
- }
-
- if (buffer[0])
- blinky_x = CG_DrawHUDString(buffer, (hud_vrect.x + hud_vrect.width/2 + -160) * scale, y, (320 / 2) * 2 * scale, 0, scale);
- else
- blinky_x = (hud_vrect.width / 2) * scale;
- cgi.SCR_SetAltTypeface(false);
- if (i == center.current_line && !ui_acc_alttypeface->integer)
- cgi.SCR_DrawChar(blinky_x, y, scale, 10 + ((cgi.CL_ClientRealTime() >> 8) & 1), true);
- y += lineHeight;
- if (i == center.current_line)
- break;
- }
- }
- static void CG_CheckDrawCenterString( const player_state_t *ps, const vrect_t &hud_vrect, const vrect_t &hud_safe, int isplit, int scale )
- {
- if (CG_InIntermission(ps))
- return;
- if (!hud_data[isplit].center_index.has_value())
- return;
- auto &data = hud_data[isplit];
- auto ¢er = data.centers[data.center_index.value()];
- // ran out of center time
- if (center.finished && center.time_off < cgi.CL_ClientRealTime())
- {
- center.lines.clear();
- size_t next_index = (data.center_index.value() + 1) % MAX_CENTER_PRINTS;
- auto &next_center = data.centers[next_index];
- // no more
- if (next_center.lines.empty())
- {
- data.center_index.reset();
- return;
- }
- // buffer rotated; start timer now
- data.center_index = next_index;
- next_center.current_line = next_center.line_count = 0;
- }
- if (!data.center_index.has_value())
- return;
- CG_DrawCenterString( ps, hud_vrect, hud_safe, isplit, scale, data.centers[data.center_index.value()] );
- }
- /*
- ==============
- CG_DrawString
- ==============
- */
- static void CG_DrawString (int x, int y, int scale, const char *s, bool alt = false, bool shadow = true)
- {
- while (*s)
- {
- cgi.SCR_DrawChar (x, y, scale, *s ^ (alt ? 0x80 : 0), shadow);
- x+=8*scale;
- s++;
- }
- }
- #include <charconv>
- /*
- ==============
- CG_DrawField
- ==============
- */
- static void CG_DrawField (int x, int y, int color, int width, int value, int scale)
- {
- char num[16], *ptr;
- int l;
- int frame;
- if (width < 1)
- return;
- // draw number string
- if (width > 5)
- width = 5;
- auto result = std::to_chars(num, num + sizeof(num) - 1, value);
- *(result.ptr) = '\0';
- l = (result.ptr - num);
- if (l > width)
- l = width;
- x += (2 + CHAR_WIDTH*(width - l)) * scale;
- ptr = num;
- while (*ptr && l)
- {
- if (*ptr == '-')
- frame = STAT_MINUS;
- else
- frame = *ptr -'0';
- int w, h;
- cgi.Draw_GetPicSize(&w, &h, sb_nums[color][frame]);
- cgi.SCR_DrawPic(x, y, w * scale, h * scale, sb_nums[color][frame]);
- x += CHAR_WIDTH * scale;
- ptr++;
- l--;
- }
- }
- // [Paril-KEX]
- static void CG_DrawTable(int x, int y, uint32_t width, uint32_t height, int32_t scale)
- {
- // half left
- int32_t width_pixels = width;
- x -= width_pixels / 2;
- y += CONCHAR_WIDTH * scale;
- // use Y as top though
- int32_t height_pixels = height;
-
- // draw border
- // KEX_FIXME method that requires less chars
- cgi.SCR_DrawChar(x - (CONCHAR_WIDTH * scale), y - (CONCHAR_WIDTH * scale), scale, 18, false);
- cgi.SCR_DrawChar((x + width_pixels), y - (CONCHAR_WIDTH * scale), scale, 20, false);
- cgi.SCR_DrawChar(x - (CONCHAR_WIDTH * scale), y + height_pixels, scale, 24, false);
- cgi.SCR_DrawChar((x + width_pixels), y + height_pixels, scale, 26, false);
- for (int cx = x; cx < x + width_pixels; cx += CONCHAR_WIDTH * scale)
- {
- cgi.SCR_DrawChar(cx, y - (CONCHAR_WIDTH * scale), scale, 19, false);
- cgi.SCR_DrawChar(cx, y + height_pixels, scale, 25, false);
- }
- for (int cy = y; cy < y + height_pixels; cy += CONCHAR_WIDTH * scale)
- {
- cgi.SCR_DrawChar(x - (CONCHAR_WIDTH * scale), cy, scale, 21, false);
- cgi.SCR_DrawChar((x + width_pixels), cy, scale, 23, false);
- }
- cgi.SCR_DrawColorPic(x, y, width_pixels, height_pixels, "_white", { 0, 0, 0, 255 });
- // draw in columns
- for (int i = 0; i < hud_temp.num_columns; i++)
- {
- for (int r = 0, ry = y; r < hud_temp.num_rows; r++, ry += (CONCHAR_WIDTH + font_y_offset) * scale)
- {
- int x_offset = 0;
- // center
- if (r == 0)
- {
- x_offset = ((hud_temp.column_widths[i]) / 2) -
- ((cgi.SCR_MeasureFontString(hud_temp.table_rows[r].table_cells[i].text, scale).x) / 2);
- }
- // right align
- else if (i != 0)
- {
- x_offset = (hud_temp.column_widths[i] - cgi.SCR_MeasureFontString(hud_temp.table_rows[r].table_cells[i].text, scale).x);
- }
- //CG_DrawString(x + x_offset, ry, scale, hud_temp.table_rows[r].table_cells[i].text, r == 0, true);
- cgi.SCR_DrawFontString(hud_temp.table_rows[r].table_cells[i].text, x + x_offset, ry - (font_y_offset * scale), scale, r == 0 ? alt_color : rgba_white, true, text_align_t::LEFT);
- }
- x += (hud_temp.column_widths[i] + cgi.SCR_MeasureFontString(" ", 1).x);
- }
- }
- /*
- ================
- CG_ExecuteLayoutString
- ================
- */
- static void CG_ExecuteLayoutString (const char *s, vrect_t hud_vrect, vrect_t hud_safe, int32_t scale, int32_t playernum, const player_state_t *ps)
- {
- int x, y;
- int w, h;
- int hx, hy;
- int value;
- const char *token;
- int width;
- int index;
- if (!s[0])
- return;
- x = hud_vrect.x;
- y = hud_vrect.y;
- width = 3;
- hx = 320 / 2;
- hy = 240 / 2;
- bool flash_frame = (cgi.CL_ClientTime() % 1000) < 500;
- // if non-zero, parse but don't affect state
- int32_t if_depth = 0; // current if statement depth
- int32_t endif_depth = 0; // at this depth, toggle skip_depth
- bool skip_depth = false; // whether we're in a dead stmt or not
- while (s)
- {
- token = COM_Parse (&s);
- if (!strcmp(token, "xl"))
- {
- token = COM_Parse (&s);
- if (!skip_depth)
- x = ((hud_vrect.x + atoi(token)) * scale) + hud_safe.x;
- continue;
- }
- if (!strcmp(token, "xr"))
- {
- token = COM_Parse (&s);
- if (!skip_depth)
- x = ((hud_vrect.x + hud_vrect.width + atoi(token)) * scale) - hud_safe.x;
- continue;
- }
- if (!strcmp(token, "xv"))
- {
- token = COM_Parse (&s);
- if (!skip_depth)
- x = (hud_vrect.x + hud_vrect.width/2 + (atoi(token) - hx)) * scale;
- continue;
- }
- if (!strcmp(token, "yt"))
- {
- token = COM_Parse (&s);
- if (!skip_depth)
- y = ((hud_vrect.y + atoi(token)) * scale) + hud_safe.y;
- continue;
- }
- if (!strcmp(token, "yb"))
- {
- token = COM_Parse (&s);
- if (!skip_depth)
- y = ((hud_vrect.y + hud_vrect.height + atoi(token)) * scale) - hud_safe.y;
- continue;
- }
- if (!strcmp(token, "yv"))
- {
- token = COM_Parse (&s);
- if (!skip_depth)
- y = (hud_vrect.y + hud_vrect.height/2 + (atoi(token) - hy)) * scale;
- continue;
- }
- if (!strcmp(token, "pic"))
- { // draw a pic from a stat number
- token = COM_Parse (&s);
- if (!skip_depth)
- {
- value = ps->stats[atoi(token)];
- if (value >= MAX_IMAGES)
- cgi.Com_Error("Pic >= MAX_IMAGES");
- const char *const pic = cgi.get_configstring(CS_IMAGES + value);
- if (pic && *pic)
- {
- cgi.Draw_GetPicSize (&w, &h, pic);
- cgi.SCR_DrawPic (x, y, w * scale, h * scale, pic);
- }
- }
- continue;
- }
- if (!strcmp(token, "client"))
- { // draw a deathmatch client block
- token = COM_Parse (&s);
- if (!skip_depth)
- {
- x = (hud_vrect.x + hud_vrect.width/2 + (atoi(token) - hx)) * scale;
- x += 8 * scale;
- }
- token = COM_Parse (&s);
- if (!skip_depth)
- {
- y = (hud_vrect.y + hud_vrect.height/2 + (atoi(token) - hy)) * scale;
- y += 7 * scale;
- }
- token = COM_Parse (&s);
- if (!skip_depth)
- {
- value = atoi(token);
- if (value >= MAX_CLIENTS || value < 0)
- cgi.Com_Error("client >= MAX_CLIENTS");
- }
- int score, ping;
- token = COM_Parse (&s);
- if (!skip_depth)
- score = atoi(token);
- token = COM_Parse (&s);
- if (!skip_depth)
- {
- ping = atoi(token);
- if (!scr_usekfont->integer)
- CG_DrawString (x + 32 * scale, y, scale, cgi.CL_GetClientName(value));
- else
- cgi.SCR_DrawFontString(cgi.CL_GetClientName(value), x + 32 * scale, y - (font_y_offset * scale), scale, rgba_white, true, text_align_t::LEFT);
-
- if (!scr_usekfont->integer)
- CG_DrawString (x + 32 * scale, y + 10 * scale, scale, G_Fmt("{}", score).data(), true);
- else
- cgi.SCR_DrawFontString(G_Fmt("{}", score).data(), x + 32 * scale, y + (10 - font_y_offset) * scale, scale, rgba_white, true, text_align_t::LEFT);
- cgi.SCR_DrawPic(x + 96 * scale, y + 10 * scale, 9 * scale, 9 * scale, "ping");
-
- if (!scr_usekfont->integer)
- CG_DrawString (x + 73 * scale + 32 * scale, y + 10 * scale, scale, G_Fmt("{}", ping).data());
- else
- cgi.SCR_DrawFontString (G_Fmt("{}", ping).data(), x + 107 * scale, y + (10 - font_y_offset) * scale, scale, rgba_white, true, text_align_t::LEFT);
- }
- continue;
- }
- if (!strcmp(token, "ctf"))
- { // draw a ctf client block
- int score, ping;
- token = COM_Parse (&s);
- if (!skip_depth)
- x = (hud_vrect.x + hud_vrect.width/2 - hx + atoi(token)) * scale;
- token = COM_Parse (&s);
- if (!skip_depth)
- y = (hud_vrect.y + hud_vrect.height/2 - hy + atoi(token)) * scale;
- token = COM_Parse (&s);
- if (!skip_depth)
- {
- value = atoi(token);
- if (value >= MAX_CLIENTS || value < 0)
- cgi.Com_Error("client >= MAX_CLIENTS");
- }
- token = COM_Parse (&s);
- if (!skip_depth)
- score = atoi(token);
- token = COM_Parse (&s);
- if (!skip_depth)
- {
- ping = atoi(token);
- if (ping > 999)
- ping = 999;
- }
- token = COM_Parse (&s);
- if (!skip_depth)
- {
- cgi.SCR_DrawFontString (G_Fmt("{}", score).data(), x, y - (font_y_offset * scale), scale, value == playernum ? alt_color : rgba_white, true, text_align_t::LEFT);
- x += 3 * 9 * scale;
- cgi.SCR_DrawFontString (G_Fmt("{}", ping).data(), x, y - (font_y_offset * scale), scale, value == playernum ? alt_color : rgba_white, true, text_align_t::LEFT);
- x += 3 * 9 * scale;
- cgi.SCR_DrawFontString (cgi.CL_GetClientName(value), x, y - (font_y_offset * scale), scale, value == playernum ? alt_color : rgba_white, true, text_align_t::LEFT);
- if (*token)
- {
- cgi.Draw_GetPicSize(&w, &h, token);
- cgi.SCR_DrawPic(x - ((w + 2) * scale), y, w * scale, h * scale, token);
- }
- }
- continue;
- }
- if (!strcmp(token, "picn"))
- { // draw a pic from a name
- token = COM_Parse (&s);
- if (!skip_depth)
- {
- cgi.Draw_GetPicSize(&w, &h, token);
- cgi.SCR_DrawPic(x, y, w * scale, h * scale, token);
- }
- continue;
- }
- if (!strcmp(token, "num"))
- { // draw a number
- token = COM_Parse (&s);
- if (!skip_depth)
- width = atoi(token);
- token = COM_Parse (&s);
- if (!skip_depth)
- {
- value = ps->stats[atoi(token)];
- CG_DrawField (x, y, 0, width, value, scale);
- }
- continue;
- }
- // [Paril-KEX] special handling for the lives number
- else if (!strcmp(token, "lives_num"))
- {
- token = COM_Parse (&s);
- if (!skip_depth)
- {
- value = ps->stats[atoi(token)];
- CG_DrawField(x, y, value <= 2 ? flash_frame : 0, 1, max(0, value - 2), scale);
- }
- }
- if (!strcmp(token, "hnum"))
- {
- // health number
- if (!skip_depth)
- {
- int color;
- width = 3;
- value = ps->stats[STAT_HEALTH];
- if (value > 25)
- color = 0; // green
- else if (value > 0)
- color = flash_frame; // flash
- else
- color = 1;
- if (ps->stats[STAT_FLASHES] & 1)
- {
- cgi.Draw_GetPicSize(&w, &h, "field_3");
- cgi.SCR_DrawPic(x, y, w * scale, h * scale, "field_3");
- }
- CG_DrawField (x, y, color, width, value, scale);
- }
- continue;
- }
- if (!strcmp(token, "anum"))
- {
- // ammo number
- if (!skip_depth)
- {
- int color;
- width = 3;
- value = ps->stats[STAT_AMMO];
- int32_t min_ammo = cgi.CL_GetWarnAmmoCount(ps->stats[STAT_ACTIVE_WEAPON]);
- if (!min_ammo)
- min_ammo = 5; // back compat
- if (value > min_ammo)
- color = 0; // green
- else if (value >= 0)
- color = flash_frame; // flash
- else
- continue; // negative number = don't show
- if (ps->stats[STAT_FLASHES] & 4)
- {
- cgi.Draw_GetPicSize(&w, &h, "field_3");
- cgi.SCR_DrawPic(x, y, w * scale, h * scale, "field_3");
- }
- CG_DrawField (x, y, color, width, value, scale);
- }
- continue;
- }
- if (!strcmp(token, "rnum"))
- {
- // armor number
- if (!skip_depth)
- {
- int color;
- width = 3;
- value = ps->stats[STAT_ARMOR];
- if (value < 0)
- continue;
- color = 0; // green
- if (ps->stats[STAT_FLASHES] & 2)
- {
- cgi.Draw_GetPicSize(&w, &h, "field_3");
- cgi.SCR_DrawPic(x, y, w * scale, h * scale, "field_3");
- }
- CG_DrawField (x, y, color, width, value, scale);
- }
- continue;
- }
- if (!strcmp(token, "stat_string"))
- {
- token = COM_Parse (&s);
- if (!skip_depth)
- {
- index = atoi(token);
- if (index < 0 || index >= MAX_STATS)
- cgi.Com_Error("Bad stat_string index");
- index = ps->stats[index];
- if (cgi.CL_ServerProtocol() <= PROTOCOL_VERSION_3XX)
- index = CS_REMAP(index).start / CS_MAX_STRING_LENGTH;
- if (index < 0 || index >= MAX_CONFIGSTRINGS)
- cgi.Com_Error("Bad stat_string index");
- if (!scr_usekfont->integer)
- CG_DrawString (x, y, scale, cgi.get_configstring(index));
- else
- cgi.SCR_DrawFontString(cgi.get_configstring(index), x, y - (font_y_offset * scale), scale, rgba_white, true, text_align_t::LEFT);
- }
- continue;
- }
- if (!strcmp(token, "cstring"))
- {
- token = COM_Parse (&s);
- if (!skip_depth)
- CG_DrawHUDString (token, x, y, hx*2*scale, 0, scale);
- continue;
- }
- if (!strcmp(token, "string"))
- {
- token = COM_Parse (&s);
- if (!skip_depth)
- {
- if (!scr_usekfont->integer)
- CG_DrawString (x, y, scale, token);
- else
- cgi.SCR_DrawFontString(token, x, y - (font_y_offset * scale), scale, rgba_white, true, text_align_t::LEFT);
- }
- continue;
- }
- if (!strcmp(token, "cstring2"))
- {
- token = COM_Parse (&s);
- if (!skip_depth)
- CG_DrawHUDString (token, x, y, hx*2*scale, 0x80, scale);
- continue;
- }
- if (!strcmp(token, "string2"))
- {
- token = COM_Parse (&s);
- if (!skip_depth)
- {
- if (!scr_usekfont->integer)
- CG_DrawString (x, y, scale, token, true);
- else
- cgi.SCR_DrawFontString(token, x, y - (font_y_offset * scale), scale, alt_color, true, text_align_t::LEFT);
- }
- continue;
- }
- if (!strcmp(token, "if"))
- {
- // if stmt
- token = COM_Parse (&s);
- if_depth++;
- // skip to endif
- if (!skip_depth && !ps->stats[atoi(token)])
- {
- skip_depth = true;
- endif_depth = if_depth;
- }
- continue;
- }
- if (!strcmp(token, "ifgef"))
- {
- // if stmt
- token = COM_Parse (&s);
- if_depth++;
- // skip to endif
- if (!skip_depth && cgi.CL_ServerFrame() < atoi(token))
- {
- skip_depth = true;
- endif_depth = if_depth;
- }
- continue;
- }
- if (!strcmp(token, "endif"))
- {
- if (skip_depth && (if_depth == endif_depth))
- skip_depth = false;
- if_depth--;
- if (if_depth < 0)
- cgi.Com_Error("endif without matching if");
- continue;
- }
- // localization stuff
- if (!strcmp(token, "loc_stat_string"))
- {
- token = COM_Parse (&s);
- if (!skip_depth)
- {
- index = atoi(token);
- if (index < 0 || index >= MAX_STATS)
- cgi.Com_Error("Bad stat_string index");
- index = ps->stats[index];
- if (cgi.CL_ServerProtocol() <= PROTOCOL_VERSION_3XX)
- index = CS_REMAP(index).start / CS_MAX_STRING_LENGTH;
- if (index < 0 || index >= MAX_CONFIGSTRINGS)
- cgi.Com_Error("Bad stat_string index");
- if (!scr_usekfont->integer)
- CG_DrawString (x, y, scale, cgi.Localize(cgi.get_configstring(index), nullptr, 0));
- else
- cgi.SCR_DrawFontString(cgi.Localize(cgi.get_configstring(index), nullptr, 0), x, y - (font_y_offset * scale), scale, rgba_white, true, text_align_t::LEFT);
- }
- continue;
- }
- if (!strcmp(token, "loc_stat_rstring"))
- {
- token = COM_Parse (&s);
- if (!skip_depth)
- {
- index = atoi(token);
- if (index < 0 || index >= MAX_STATS)
- cgi.Com_Error("Bad stat_string index");
- index = ps->stats[index];
- if (cgi.CL_ServerProtocol() <= PROTOCOL_VERSION_3XX)
- index = CS_REMAP(index).start / CS_MAX_STRING_LENGTH;
- if (index < 0 || index >= MAX_CONFIGSTRINGS)
- cgi.Com_Error("Bad stat_string index");
- const char *s = cgi.Localize(cgi.get_configstring(index), nullptr, 0);
- if (!scr_usekfont->integer)
- CG_DrawString (x - (strlen(s) * CONCHAR_WIDTH * scale), y, scale, s);
- else
- {
- vec2_t size = cgi.SCR_MeasureFontString(s, scale);
- cgi.SCR_DrawFontString(s, x - size.x, y - (font_y_offset * scale), scale, rgba_white, true, text_align_t::LEFT);
- }
- }
- continue;
- }
-
- if (!strcmp(token, "loc_stat_cstring"))
- {
- token = COM_Parse (&s);
- if (!skip_depth)
- {
- index = atoi(token);
- if (index < 0 || index >= MAX_STATS)
- cgi.Com_Error("Bad stat_string index");
- index = ps->stats[index];
- if (cgi.CL_ServerProtocol() <= PROTOCOL_VERSION_3XX)
- index = CS_REMAP(index).start / CS_MAX_STRING_LENGTH;
- if (index < 0 || index >= MAX_CONFIGSTRINGS)
- cgi.Com_Error("Bad stat_string index");
- CG_DrawHUDString (cgi.Localize(cgi.get_configstring(index), nullptr, 0), x, y, hx*2*scale, 0, scale);
- }
- continue;
- }
- if (!strcmp(token, "loc_stat_cstring2"))
- {
- token = COM_Parse (&s);
- if (!skip_depth)
- {
- index = atoi(token);
- if (index < 0 || index >= MAX_STATS)
- cgi.Com_Error("Bad stat_string index");
- index = ps->stats[index];
- if (cgi.CL_ServerProtocol() <= PROTOCOL_VERSION_3XX)
- index = CS_REMAP(index).start / CS_MAX_STRING_LENGTH;
- if (index < 0 || index >= MAX_CONFIGSTRINGS)
- cgi.Com_Error("Bad stat_string index");
- CG_DrawHUDString (cgi.Localize(cgi.get_configstring(index), nullptr, 0), x, y, hx*2*scale, 0x80, scale);
- }
- continue;
- }
- static char arg_tokens[MAX_LOCALIZATION_ARGS + 1][MAX_TOKEN_CHARS];
- static const char *arg_buffers[MAX_LOCALIZATION_ARGS];
- if (!strcmp(token, "loc_cstring"))
- {
- int32_t num_args = atoi(COM_Parse (&s));
- if (num_args < 0 || num_args >= MAX_LOCALIZATION_ARGS)
- cgi.Com_Error("Bad loc string");
- // parse base
- token = COM_Parse (&s);
- Q_strlcpy(arg_tokens[0], token, sizeof(arg_tokens[0]));
- // parse args
- for (int32_t i = 0; i < num_args; i++)
- {
- token = COM_Parse (&s);
- Q_strlcpy(arg_tokens[1 + i], token, sizeof(arg_tokens[0]));
- arg_buffers[i] = arg_tokens[1 + i];
- }
- if (!skip_depth)
- CG_DrawHUDString (cgi.Localize(arg_tokens[0], arg_buffers, num_args), x, y, hx*2*scale, 0, scale);
- continue;
- }
- if (!strcmp(token, "loc_string"))
- {
- int32_t num_args = atoi(COM_Parse (&s));
- if (num_args < 0 || num_args >= MAX_LOCALIZATION_ARGS)
- cgi.Com_Error("Bad loc string");
- // parse base
- token = COM_Parse (&s);
- Q_strlcpy(arg_tokens[0], token, sizeof(arg_tokens[0]));
- // parse args
- for (int32_t i = 0; i < num_args; i++)
- {
- token = COM_Parse (&s);
- Q_strlcpy(arg_tokens[1 + i], token, sizeof(arg_tokens[0]));
- arg_buffers[i] = arg_tokens[1 + i];
- }
-
- if (!skip_depth)
- {
- if (!scr_usekfont->integer)
- CG_DrawString (x, y, scale, cgi.Localize(arg_tokens[0], arg_buffers, num_args));
- else
- cgi.SCR_DrawFontString(cgi.Localize(arg_tokens[0], arg_buffers, num_args), x, y - (font_y_offset * scale), scale, rgba_white, true, text_align_t::LEFT);
- }
- continue;
- }
- if (!strcmp(token, "loc_cstring2"))
- {
- int32_t num_args = atoi(COM_Parse (&s));
- if (num_args < 0 || num_args >= MAX_LOCALIZATION_ARGS)
- cgi.Com_Error("Bad loc string");
- // parse base
- token = COM_Parse (&s);
- Q_strlcpy(arg_tokens[0], token, sizeof(arg_tokens[0]));
- // parse args
- for (int32_t i = 0; i < num_args; i++)
- {
- token = COM_Parse (&s);
- Q_strlcpy(arg_tokens[1 + i], token, sizeof(arg_tokens[0]));
- arg_buffers[i] = arg_tokens[1 + i];
- }
-
- if (!skip_depth)
- CG_DrawHUDString (cgi.Localize(arg_tokens[0], arg_buffers, num_args), x, y, hx*2*scale, 0x80, scale);
- continue;
- }
- if (!strcmp(token, "loc_string2") || !strcmp(token, "loc_rstring2") ||
- !strcmp(token, "loc_string") || !strcmp(token, "loc_rstring"))
- {
- bool green = token[strlen(token) - 1] == '2';
- bool rightAlign = !Q_strncasecmp(token, "loc_rstring", strlen("loc_rstring"));
- int32_t num_args = atoi(COM_Parse (&s));
- if (num_args < 0 || num_args >= MAX_LOCALIZATION_ARGS)
- cgi.Com_Error("Bad loc string");
- // parse base
- token = COM_Parse (&s);
- Q_strlcpy(arg_tokens[0], token, sizeof(arg_tokens[0]));
- // parse args
- for (int32_t i = 0; i < num_args; i++)
- {
- token = COM_Parse (&s);
- Q_strlcpy(arg_tokens[1 + i], token, sizeof(arg_tokens[0]));
- arg_buffers[i] = arg_tokens[1 + i];
- }
-
- if (!skip_depth)
- {
- const char *locStr = cgi.Localize(arg_tokens[0], arg_buffers, num_args);
- int xOffs = 0;
- if (rightAlign)
- {
- xOffs = scr_usekfont->integer ? cgi.SCR_MeasureFontString(locStr, scale).x : (strlen(locStr) * CONCHAR_WIDTH * scale);
- }
- if (!scr_usekfont->integer)
- CG_DrawString (x - xOffs, y, scale, locStr, green);
- else
- cgi.SCR_DrawFontString(locStr, x - xOffs, y - (font_y_offset * scale), scale, green ? alt_color : rgba_white, true, text_align_t::LEFT);
- }
- continue;
- }
- // draw time remaining
- if (!strcmp(token, "time_limit"))
- {
- // end frame
- token = COM_Parse (&s);
- if (!skip_depth)
- {
- int32_t end_frame = atoi(token);
- if (end_frame < cgi.CL_ServerFrame())
- continue;
- uint64_t remaining_ms = (end_frame - cgi.CL_ServerFrame()) * cgi.frame_time_ms;
- const bool green = true;
- arg_buffers[0] = G_Fmt("{:02}:{:02}", (remaining_ms / 1000) / 60, (remaining_ms / 1000) % 60).data();
- const char *locStr = cgi.Localize("$g_score_time", arg_buffers, 1);
- int xOffs = scr_usekfont->integer ? cgi.SCR_MeasureFontString(locStr, scale).x : (strlen(locStr) * CONCHAR_WIDTH * scale);
- if (!scr_usekfont->integer)
- CG_DrawString (x - xOffs, y, scale, locStr, green);
- else
- cgi.SCR_DrawFontString(locStr, x - xOffs, y - (font_y_offset * scale), scale, green ? alt_color : rgba_white, true, text_align_t::LEFT);
- }
- }
- // draw client dogtag
- if (!strcmp(token, "dogtag"))
- {
- token = COM_Parse (&s);
-
- if (!skip_depth)
- {
- value = atoi(token);
- if (value >= MAX_CLIENTS || value < 0)
- cgi.Com_Error("client >= MAX_CLIENTS");
- const std::string_view path = G_Fmt("/tags/{}", cgi.CL_GetClientDogtag(value));
- cgi.SCR_DrawPic(x, y, 198 * scale, 32 * scale, path.data());
- }
- }
- if (!strcmp(token, "start_table"))
- {
- token = COM_Parse (&s);
- value = atoi(token);
- if (!skip_depth)
- {
- if (value >= q_countof(hud_temp.table_rows[0].table_cells))
- cgi.Com_Error("table too big");
- hud_temp.num_columns = value;
- hud_temp.num_rows = 1;
- for (int i = 0; i < value; i++)
- hud_temp.column_widths[i] = 0;
- }
- for (int i = 0; i < value; i++)
- {
- token = COM_Parse (&s);
- if (!skip_depth)
- {
- token = cgi.Localize(token, nullptr, 0);
- Q_strlcpy(hud_temp.table_rows[0].table_cells[i].text, token, sizeof(hud_temp.table_rows[0].table_cells[i].text));
- hud_temp.column_widths[i] = max(hud_temp.column_widths[i], (size_t) cgi.SCR_MeasureFontString(hud_temp.table_rows[0].table_cells[i].text, scale).x);
- }
- }
- }
- if (!strcmp(token, "table_row"))
- {
- token = COM_Parse (&s);
- value = atoi(token);
- if (!skip_depth)
- {
- if (hud_temp.num_rows >= q_countof(hud_temp.table_rows))
- {
- cgi.Com_Error("table too big");
- return;
- }
- }
-
- auto &row = hud_temp.table_rows[hud_temp.num_rows];
- for (int i = 0; i < value; i++)
- {
- token = COM_Parse (&s);
- if (!skip_depth)
- {
- Q_strlcpy(row.table_cells[i].text, token, sizeof(row.table_cells[i].text));
- hud_temp.column_widths[i] = max(hud_temp.column_widths[i], (size_t) cgi.SCR_MeasureFontString(row.table_cells[i].text, scale).x);
- }
- }
-
- if (!skip_depth)
- {
- for (int i = value; i < hud_temp.num_columns; i++)
- row.table_cells[i].text[0] = '\0';
- hud_temp.num_rows++;
- }
- }
- if (!strcmp(token, "draw_table"))
- {
- if (!skip_depth)
- {
- // in scaled pixels, incl padding between elements
- uint32_t total_inner_table_width = 0;
- for (int i = 0; i < hud_temp.num_columns; i++)
- {
- if (i != 0)
- total_inner_table_width += cgi.SCR_MeasureFontString(" ", scale).x;
- total_inner_table_width += hud_temp.column_widths[i];
- }
- // in scaled pixels
- uint32_t total_table_height = hud_temp.num_rows * (CONCHAR_WIDTH + font_y_offset) * scale;
- CG_DrawTable(x, y, total_inner_table_width, total_table_height, scale);
- }
- }
- if (!strcmp(token, "stat_pname"))
- {
- token = COM_Parse(&s);
- if (!skip_depth)
- {
- index = atoi(token);
- if (index < 0 || index >= MAX_STATS)
- cgi.Com_Error("Bad stat_string index");
- index = ps->stats[index] - 1;
- if (!scr_usekfont->integer)
- CG_DrawString(x, y, scale, cgi.CL_GetClientName(index));
- else
- cgi.SCR_DrawFontString(cgi.CL_GetClientName(index), x, y - (font_y_offset * scale), scale, rgba_white, true, text_align_t::LEFT);
- }
- continue;
- }
- if (!strcmp(token, "health_bars"))
- {
- if (skip_depth)
- continue;
- const byte *stat = reinterpret_cast<const byte *>(&ps->stats[STAT_HEALTH_BARS]);
- const char *name = cgi.Localize(cgi.get_configstring(CONFIG_HEALTH_BAR_NAME), nullptr, 0);
- CG_DrawHUDString(name, (hud_vrect.x + hud_vrect.width/2 + -160) * scale, y, (320 / 2) * 2 * scale, 0, scale);
- float bar_width = ((hud_vrect.width * scale) - (hud_safe.x * 2)) * 0.50f;
- float bar_height = 4 * scale;
- y += cgi.SCR_FontLineHeight(scale);
- float x = ((hud_vrect.x + (hud_vrect.width * 0.5f)) * scale) - (bar_width * 0.5f);
- // 2 health bars, hardcoded
- for (size_t i = 0; i < 2; i++, stat++)
- {
- if (!(*stat & 0b10000000))
- continue;
- float percent = (*stat & 0b01111111) / 127.f;
- cgi.SCR_DrawColorPic(x, y, bar_width + scale, bar_height + scale, "_white", rgba_black);
- if (percent > 0)
- cgi.SCR_DrawColorPic(x, y, bar_width * percent, bar_height, "_white", rgba_red);
- if (percent < 1)
- cgi.SCR_DrawColorPic(x + (bar_width * percent), y, bar_width * (1.f - percent), bar_height, "_white", { 80, 80, 80, 255 });
- y += bar_height * 3;
- }
- }
- if (!strcmp(token, "story"))
- {
- const char *story_str = cgi.get_configstring(CONFIG_STORY);
- if (!*story_str)
- continue;
- const char *localized = cgi.Localize(story_str, nullptr, 0);
- vec2_t size = cgi.SCR_MeasureFontString(localized, scale);
- float centerx = ((hud_vrect.x + (hud_vrect.width * 0.5f)) * scale);
- float centery = ((hud_vrect.y + (hud_vrect.height * 0.5f)) * scale) - (size.y * 0.5f);
- cgi.SCR_DrawFontString(localized, centerx, centery, scale, rgba_white, true, text_align_t::CENTER);
- }
- }
- if (skip_depth)
- cgi.Com_Error("if with no matching endif");
- }
- static cvar_t *cl_skipHud;
- static cvar_t *cl_paused;
- /*
- ================
- CL_DrawInventory
- ================
- */
- constexpr size_t DISPLAY_ITEMS = 19;
- static void CG_DrawInventory(const player_state_t *ps, const std::array<int16_t, MAX_ITEMS> &inventory, vrect_t hud_vrect, int32_t scale)
- {
- int i;
- int num, selected_num, item;
- int index[MAX_ITEMS];
- int x, y;
- int width, height;
- int selected;
- int top;
- selected = ps->stats[STAT_SELECTED_ITEM];
- num = 0;
- selected_num = 0;
- for (i=0 ; i<MAX_ITEMS ; i++) {
- if ( i == selected ) {
- selected_num = num;
- }
- if ( inventory[i] ) {
- index[num] = i;
- num++;
- }
- }
- // determine scroll point
- top = selected_num - DISPLAY_ITEMS/2;
- if (num - top < DISPLAY_ITEMS)
- top = num - DISPLAY_ITEMS;
- if (top < 0)
- top = 0;
- x = hud_vrect.x * scale;
- y = hud_vrect.y * scale;
- width = hud_vrect.width;
- height = hud_vrect.height;
- x += ((width / 2) - (256 / 2)) * scale;
- y += ((height / 2) - (216 / 2)) * scale;
- int pich, picw;
- cgi.Draw_GetPicSize(&picw, &pich, "inventory");
- cgi.SCR_DrawPic(x, y+8*scale, picw * scale, pich * scale, "inventory");
- y += 27 * scale;
- x += 22 * scale;
- for (i=top ; i<num && i < top+DISPLAY_ITEMS ; i++)
- {
- item = index[i];
- if (item == selected) // draw a blinky cursor by the selected item
- {
- if ( (cgi.CL_ClientRealTime() * 10) & 1)
- cgi.SCR_DrawChar(x-8, y, scale, 15, false);
- }
- if (!scr_usekfont->integer)
- {
- CG_DrawString(x, y, scale,
- G_Fmt("{:3} {}", inventory[item],
- cgi.Localize(cgi.get_configstring(CS_ITEMS + item), nullptr, 0)).data(),
- item == selected, false);
- }
- else
- {
- const char *string = G_Fmt("{}", inventory[item]).data();
- cgi.SCR_DrawFontString(string, x + (216 * scale) - (16 * scale), y - (font_y_offset * scale), scale, (item == selected) ? alt_color : rgba_white, true, text_align_t::RIGHT);
- string = cgi.Localize(cgi.get_configstring(CS_ITEMS + item), nullptr, 0);
- cgi.SCR_DrawFontString(string, x + (16 * scale), y - (font_y_offset * scale), scale, (item == selected) ? alt_color : rgba_white, true, text_align_t::LEFT);
- }
-
- y += 8 * scale;
- }
- }
- extern uint64_t cgame_init_time;
- void CG_DrawHUD (int32_t isplit, const cg_server_data_t *data, vrect_t hud_vrect, vrect_t hud_safe, int32_t scale, int32_t playernum, const player_state_t *ps)
- {
- if (cgi.CL_InAutoDemoLoop())
- {
- if (cl_paused->integer) return; // demo is paused, menu is open
- uint64_t time = cgi.CL_ClientRealTime() - cgame_init_time;
- if (time < 20000 &&
- (time % 4000) < 2000)
- cgi.SCR_DrawFontString(cgi.Localize("$m_eou_press_button", nullptr, 0), hud_vrect.width * 0.5f * scale, (hud_vrect.height - 64.f) * scale, scale, rgba_green, true, text_align_t::CENTER);
- return;
- }
- // draw HUD
- if (!cl_skipHud->integer && !(ps->stats[STAT_LAYOUTS] & LAYOUTS_HIDE_HUD))
- CG_ExecuteLayoutString(cgi.get_configstring(CS_STATUSBAR), hud_vrect, hud_safe, scale, playernum, ps);
- // draw centerprint string
- CG_CheckDrawCenterString(ps, hud_vrect, hud_safe, isplit, scale);
- // draw notify
- CG_DrawNotify(isplit, hud_vrect, hud_safe, scale);
- // svc_layout still drawn with hud off
- if (ps->stats[STAT_LAYOUTS] & LAYOUTS_LAYOUT)
- CG_ExecuteLayoutString(data->layout, hud_vrect, hud_safe, scale, playernum, ps);
- // inventory too
- if (ps->stats[STAT_LAYOUTS] & LAYOUTS_INVENTORY)
- CG_DrawInventory(ps, data->inventory, hud_vrect, scale);
- }
- /*
- ================
- CG_TouchPics
- ================
- */
- void CG_TouchPics()
- {
- for (auto &nums : sb_nums)
- for (auto &str : nums)
- cgi.Draw_RegisterPic(str);
- cgi.Draw_RegisterPic("inventory");
- font_y_offset = (cgi.SCR_FontLineHeight(1) - CONCHAR_WIDTH) / 2;
- }
- void CG_InitScreen()
- {
- cl_paused = cgi.cvar("paused", "0", CVAR_NOFLAGS);
- cl_skipHud = cgi.cvar("cl_skipHud", "0", CVAR_ARCHIVE);
- scr_usekfont = cgi.cvar("scr_usekfont", "1", CVAR_NOFLAGS);
- scr_centertime = cgi.cvar ("scr_centertime", "5.0", CVAR_ARCHIVE); // [Sam-KEX] Changed from 2.5
- scr_printspeed = cgi.cvar ("scr_printspeed", "0.04", CVAR_NOFLAGS); // [Sam-KEX] Changed from 8
- cl_notifytime = cgi.cvar ("cl_notifytime", "5.0", CVAR_ARCHIVE);
- scr_maxlines = cgi.cvar ("scr_maxlines", "4", CVAR_ARCHIVE);
- ui_acc_contrast = cgi.cvar ("ui_acc_contrast", "0", CVAR_NOFLAGS);
- ui_acc_alttypeface = cgi.cvar("ui_acc_alttypeface", "0", CVAR_NOFLAGS);
- hud_data = {};
- }
|