From ce9f4e0b4adc46e243fdc462697a73bc18f7bdad Mon Sep 17 00:00:00 2001 From: z411 Date: Fri, 2 Oct 2020 03:07:08 -0300 Subject: [PATCH] Initial duel alternate scoreboard --- qcsrc/client/hud/panel/scoreboard.qc | 284 ++++++++++++++++++++++++++- qcsrc/client/hud/panel/weapons.qc | 4 +- qcsrc/client/main.qc | 28 ++- qcsrc/client/main.qh | 6 + qcsrc/server/client.qc | 1 + qcsrc/server/weapons/accuracy.qc | 27 ++- 6 files changed, 334 insertions(+), 16 deletions(-) diff --git a/qcsrc/client/hud/panel/scoreboard.qc b/qcsrc/client/hud/panel/scoreboard.qc index 023a33d7a..e3451b3c7 100644 --- a/qcsrc/client/hud/panel/scoreboard.qc +++ b/qcsrc/client/hud/panel/scoreboard.qc @@ -45,6 +45,10 @@ string autocvar_hud_fontsize; string hud_fontsize_str; float max_namesize; +vector duel_score_fontsize; +vector duel_name_fontsize; +vector duel_score_size; + float sbt_bg_alpha; float sbt_fg_alpha; float sbt_fg_alpha_self; @@ -429,6 +433,10 @@ void Cmd_Scoreboard_SetFields(int argc) sbt_num_fields = 0; hud_fontsize = HUD_GetFontsize("hud_fontsize"); + + duel_score_fontsize = hud_fontsize * 3; + duel_name_fontsize = hud_fontsize * 1.5; + duel_score_size = vec2(duel_score_fontsize.x * 1.5, duel_score_fontsize.y * 1.25); for(i = 1; i < argc - 1; ++i) { @@ -617,8 +625,13 @@ string Scoreboard_GetField(entity pl, PlayerScoreField field) f = pl.ping; if(f == 0) return _("N/A"); - tmp = max(0, min(220, f-80)) / 220; - sbt_field_rgb = '1 1 1' - '0 1 1' * tmp; + if(f < 80) { + tmp = max(0, min(60, f-20)) / 60; // 20-80 range is green + sbt_field_rgb = '0 1 0' + '1 0 1' * tmp; + } else { + tmp = max(0, min(220, f-80)) / 220; // 80-300 range is red + sbt_field_rgb = '1 1 1' - '0 1 1' * tmp; + } return ftos(f); case SP_PL: @@ -1041,6 +1054,262 @@ vector Scoreboard_DrawOthers(vector item_pos, vector rgb, int this_team, entity return vec2(item_pos.x, item_pos.y + i * hud_fontsize.y * 1.25); } +vector Scoreboard_Duel_DrawPickup(vector pos, bool skinned, string icon, vector sz, float number, bool invert) +{ + vector tmp_in = pos; + vector tmp_sz, tmp_sz2; + string picpath; + + // Icon + if(skinned) { + picpath = strcat(hud_skin_path, "/", icon); + if(precache_pic(picpath) == "") + picpath = strcat("gfx/hud/default/", icon); + } else { + picpath = icon; + } + + tmp_sz = draw_getimagesize(picpath); + tmp_sz2 = vec2(sz.y*(tmp_sz.x/tmp_sz.y), sz.y); + + tmp_in.x = pos.x + ((sz.x - tmp_sz2.x) / 2); + drawpic(tmp_in, picpath, tmp_sz2, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + + // Number + if(invert) + tmp_in.x += tmp_sz2.x + hud_fontsize.x * 0.25; + else + tmp_in.x -= hud_fontsize.x * 0.25 + hud_fontsize.x; + tmp_in.y += (tmp_sz2.y - hud_fontsize.y) / 2; + drawstring(tmp_in, ftos(number), hud_fontsize, '0.5 0.5 0.5', panel_fg_alpha, DRAWFLAG_NORMAL); + + pos.y += sz.y * 1.1; + return pos; +} + +void Scoreboard_Duel_DrawTable(vector pos, bool invert, entity pl, entity tm) +{ + vector tmp, tmp_in, tmp_sz, tmp_acc; + string tmp_str; + float sz; + float average_acc = 0; + + panel_pos = pos; + + HUD_Panel_DrawBg(); + + // Stop here if there are no scores available + if(pl.team != tm.team) return; + + tmp = pos; + tmp.x += panel_bg_padding; + tmp.y += panel_bg_padding; + panel_size.x -= panel_bg_padding * 2; + + //if (sbt_bg_alpha) + // drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", tmp, panel_size, rgb, sbt_bg_alpha, DRAWFLAG_NORMAL); + + // Score: highlight + if(invert) { tmp.x += panel_size.x; tmp.x -= duel_score_size.x; } + drawfill(tmp, duel_score_size, '0 0 0', sbt_highlight_alpha, DRAWFLAG_NORMAL); + + // Score: text + tmp_str = ftos(pl.(scores(SP_SCORE))); + tmp_in = tmp; + tmp_in.x += (duel_score_size.x / 2) - (stringwidth(tmp_str, true, duel_score_fontsize) / 2); + tmp_in.y += (duel_score_size.y / 2) - (duel_score_fontsize.y / 2); + + draw_beginBoldFont(); + drawstring(tmp_in, tmp_str, duel_score_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + draw_endBoldFont(); + + // Player name + tmp_str = Scoreboard_GetField(pl, SP_NAME); + tmp_in = tmp; + if(invert) + tmp_in.x -= stringwidth_colors(tmp_str, duel_name_fontsize) + duel_name_fontsize.x * 0.5; + else + tmp_in.x += duel_score_size.x + duel_name_fontsize.x * 0.5; + tmp_in.y += (duel_score_size.y / 2) - (duel_name_fontsize.y / 2); + drawcolorcodedstring(tmp_in, tmp_str, duel_name_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL); + + // Header + float column_width = panel_size.x / 5; + tmp.x = pos.x + panel_bg_padding; + tmp.y += hud_fontsize.y * 3 + hud_fontsize.y; + + vector column_dim; + int i; + + i = (invert ? 4 : 0); + column_dim = vec2(column_width * 4, hud_fontsize.y); + + drawstring(tmp + eX * column_width * (invert ? i-- : i++) + (eX * column_width / 2) - eX * (stringwidth("kills", false, hud_fontsize) / 2), + "kills", hud_fontsize, '0.5 0.5 1', panel_fg_alpha, DRAWFLAG_NORMAL); + drawstring(tmp + eX * column_width * (invert ? i-- : i++) + (eX * column_width / 2) - eX * (stringwidth("dmg", false, hud_fontsize) / 2), + "dmg", hud_fontsize, '0.5 0.5 1', panel_fg_alpha, DRAWFLAG_NORMAL); + drawstring(tmp + eX * column_width * (invert ? i-- : i++) + (eX * column_width / 2) - eX * (stringwidth("acc", false, hud_fontsize) / 2), + "acc", hud_fontsize, '0.5 0.5 1', panel_fg_alpha, DRAWFLAG_NORMAL); + drawstring(tmp + eX * column_width * (invert ? i-- : i++) + (eX * column_width / 2) - eX * (stringwidth("hits", false, hud_fontsize) / 2), + "hits", hud_fontsize, '0.5 0.5 1', panel_fg_alpha, DRAWFLAG_NORMAL); + drawstring(tmp + eX * column_width * (invert ? i-- : i++) + (eX * column_width / 2) - eX * (stringwidth("ping", false, hud_fontsize) / 2), + "ping", hud_fontsize, '0.5 0.5 1', panel_fg_alpha, DRAWFLAG_NORMAL); + + tmp.x = pos.x + panel_bg_padding; + tmp.y += hud_fontsize.y; + + // Main row + i = (invert ? 4 : 0); + + tmp_str = ftos(pl.(scores(SP_KILLS))); + drawstring(tmp + eX * column_width * (invert ? i-- : i++) + (eX * column_width / 2) - eX * (stringwidth(tmp_str, false, hud_fontsize * 1.25) / 2), + tmp_str, hud_fontsize * 1.25, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + + tmp_str = ftos(pl.(scores(SP_DMG))); + drawstring(tmp + eX * column_width * (invert ? i-- : i++) + (eX * column_width / 2) - eX * (stringwidth(tmp_str, false, hud_fontsize * 1.25) / 2), + tmp_str, hud_fontsize * 1.25, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + + tmp_acc = tmp + eX * column_width * (invert ? i-- : i++) + (eX * column_width / 2); + + if(invert) + i--; + else + i++; + + tmp_str = Scoreboard_GetField(pl, SP_PING); + drawstring(tmp + eX * column_width * (invert ? i-- : i++) + (eX * column_width / 2) - eX * (stringwidth(tmp_str, false, hud_fontsize * 1.25) / 2), + tmp_str, hud_fontsize * 1.25, sbt_field_rgb, panel_fg_alpha, DRAWFLAG_NORMAL); + + tmp.x = pos.x + panel_bg_padding; + tmp.y += hud_fontsize.y * 2; + + tmp_in = tmp; + + int total_weapons = 0; + + // Accuracy rows + WepSet weapons_inmap = WepSet_GetFromStat_InMap(); + FOREACH(Weapons, it != WEP_Null, { + WepSet set = it.m_wepset; + if (!(weapons_inmap & set)) + continue; + if (it.spawnflags & WEP_TYPE_OTHER) + continue; + + int weapon_cnt_fired = pl.accuracy_cnt_fired[i - WEP_FIRST]; + int weapon_cnt_hit = pl.accuracy_cnt_hit[i - WEP_FIRST]; + int weapon_acc = floor((weapon_cnt_hit / weapon_cnt_fired) * 100); + average_acc += weapon_acc; + + string draw_str; + + // weapon stats + int c = (invert ? 4 : 0); + + drawfill(tmp_in + eX * column_width * (invert ? 1 : 0), column_dim, '0 0 0', sbt_highlight_alpha, DRAWFLAG_NORMAL); + + draw_str = ftos(pl.accuracy_frags[i - WEP_FIRST]); + drawstring(tmp_in + eX * column_width * (invert ? c-- : c++) + eX * ((column_width - stringwidth(draw_str, false, hud_fontsize)) / 2), + draw_str, hud_fontsize, '0.5 0.5 0.5', panel_fg_alpha, DRAWFLAG_NORMAL); + + draw_str = ftos(pl.accuracy_hit[i - WEP_FIRST]); + drawstring(tmp_in + eX * column_width * (invert ? c-- : c++) + eX * ((column_width - stringwidth(draw_str, false, hud_fontsize)) / 2), + draw_str, hud_fontsize, '0.5 0.5 0.5', panel_fg_alpha, DRAWFLAG_NORMAL); + + draw_str = sprintf("%d%%", weapon_acc); + drawstring(tmp_in + eX * column_width * (invert ? c-- : c++) + eX * ((column_width - stringwidth(draw_str, false, hud_fontsize)) / 2), + draw_str, hud_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + + draw_str = strcat(ftos(weapon_cnt_hit), " / ", ftos(weapon_cnt_fired)); + drawstring(tmp_in + eX * column_width * (invert ? c-- : c++) + eX * (column_width / 2) - eX * stringwidth("36 /", false, hud_fontsize), + draw_str,hud_fontsize, '0.5 0.5 0.5', panel_fg_alpha, DRAWFLAG_NORMAL); + + // weapon icon + if(invert) { + tmp_in.x = pos.x + panel_size.x - panel_bg_padding - hud_fontsize.x / 2; + drawpic_aspect_skin(tmp_in, it.model2, vec2(50, hud_fontsize.y), '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + } + + tmp_in.x = pos.x + panel_bg_padding; + tmp_in.y += hud_fontsize.y * 1.25; + + if(weapon_cnt_fired) + total_weapons++; + }); + average_acc = floor((average_acc / total_weapons) + 0.5); + + // draw total accuracy now + tmp_str = sprintf("%d%%", average_acc); + drawstring(tmp_acc - eX * (stringwidth(tmp_str, false, hud_fontsize * 1.25) / 2), + tmp_str, hud_fontsize * 1.25, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + + // Icon column + vector icon_sz = vec2(column_width, hud_fontsize.y*1.5); + + if(!invert) + tmp.x += column_width * 4; + // Medal rows + drawstring(tmp + eX * ((column_width - stringwidth("medals", false, hud_fontsize)) / 2), + "medals", hud_fontsize, '0.5 0.5 1', panel_fg_alpha, DRAWFLAG_NORMAL); + tmp.y += hud_fontsize.y * 1.25; + + tmp = Scoreboard_Duel_DrawPickup(tmp, false, "gfx/medal/yoda", icon_sz, 0, invert); + tmp = Scoreboard_Duel_DrawPickup(tmp, false, "gfx/medal/airshot", icon_sz, 0, invert); + tmp = Scoreboard_Duel_DrawPickup(tmp, false, "gfx/medal/firstblood", icon_sz, 0, invert); + + // Item rows + drawstring(tmp + eX * ((column_width - stringwidth("items", false, hud_fontsize)) / 2), + "items", hud_fontsize, '0.5 0.5 1', panel_fg_alpha, DRAWFLAG_NORMAL); + tmp.y += hud_fontsize.y * 1.25; + + FOREACH(Items, it.m_id == ITEM_ArmorMega.m_id || it.m_id == ITEM_HealthMega.m_id, { + tmp = Scoreboard_Duel_DrawPickup(tmp, true, it.m_icon, icon_sz, 0, invert); + + if(it.m_id == REGISTRY_MAX(Items)) + break; + }); +} +vector Scoreboard_MakeDuelTable(vector pos, entity tm, vector rgb, vector bg_size) +{ + vector end_pos = pos; + float screen_half = panel_size.x / 2; + float weapon_margin = hud_fontsize.x; + + // Get weapon count + WepSet weapons_inmap = WepSet_GetFromStat_InMap(); + int disownedcnt = 0; + int nHidden = 0; + FOREACH(Weapons, it != WEP_Null, { + WepSet set = it.m_wepset; + if(it.spawnflags & WEP_TYPE_OTHER) + { + ++nHidden; + continue; + } + if (!(weapons_inmap & set)) + { + if (it.spawnflags & (WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_SPECIALATTACK)) + ++nHidden; + else + ++disownedcnt; + } + }); + + int weapon_cnt = (REGISTRY_COUNT(Weapons) - 1) - disownedcnt - nHidden; + panel_size.x = screen_half - weapon_margin; + panel_size.y = (duel_score_size.y * 3) + (hud_fontsize.y * 1.25 * weapon_cnt); + + entity pl_left = players.sort_next; + entity pl_right = pl_left.sort_next; + + Scoreboard_Duel_DrawTable(pos, true, pl_left, tm); + Scoreboard_Duel_DrawTable(pos + eX * screen_half + eX * weapon_margin, false, pl_right, tm); + + end_pos.y += panel_size.y + (panel_bg_padding * 2); + panel_size.x = screen_half * 2; + return end_pos; +} + vector Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_size) { int max_players = 999; @@ -1635,7 +1904,7 @@ void Scoreboard_Draw() pos.y += sb_gameinfo_type_fontsize.y; // z411 servername - drawcolorcodedstring(pos + '0.5 0 0' * (panel_size.x - stringwidth(hostname_full, true, sb_gameinfo_detail_fontsize)), hostname_full, sb_gameinfo_detail_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL); + drawcolorcodedstring(pos + '0.5 0 0' * (panel_size.x - stringwidth_colors(hostname_full, sb_gameinfo_detail_fontsize)), hostname_full, sb_gameinfo_detail_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL); pos.y += sb_gameinfo_detail_fontsize.y; @@ -1822,6 +2091,15 @@ void Scoreboard_Draw() } panel_bg_color = panel_bg_color_save; } + else if(gametype == MAPINFO_TYPE_DUEL) + { + for(tm = teams.sort_next; tm; tm = tm.sort_next) + if(tm.team != NUM_SPECTATOR) + break; + + // z411 make DUEL TABLE + pos = Scoreboard_MakeDuelTable(pos, tm, panel_bg_color, bg_size); + } else { for(tm = teams.sort_next; tm; tm = tm.sort_next) diff --git a/qcsrc/client/hud/panel/weapons.qc b/qcsrc/client/hud/panel/weapons.qc index 03be211a2..5bc45bbf4 100644 --- a/qcsrc/client/hud/panel/weapons.qc +++ b/qcsrc/client/hud/panel/weapons.qc @@ -477,7 +477,7 @@ void HUD_Weapons() } // draw the weapon accuracy - if(autocvar_hud_panel_weapons_accuracy) + /* z411 if(autocvar_hud_panel_weapons_accuracy) { float panel_weapon_accuracy = weapon_accuracy[it.m_id-WEP_FIRST]; if(panel_weapon_accuracy >= 0) @@ -485,7 +485,7 @@ void HUD_Weapons() color = Accuracy_GetColor(panel_weapon_accuracy); drawpic_aspect_skin(weapon_pos, "weapon_accuracy", weapon_size, color, panel_fg_alpha, DRAWFLAG_NORMAL); } - } + }*/ vector weapon_size_real = noncurrent_size; float weapon_alpha_real = noncurrent_alpha; diff --git a/qcsrc/client/main.qc b/qcsrc/client/main.qc index e140fb5ec..fdd3a34eb 100644 --- a/qcsrc/client/main.qc +++ b/qcsrc/client/main.qc @@ -604,6 +604,8 @@ NET_HANDLE(ENT_CLIENT_RANDOMSEED, bool isnew) NET_HANDLE(ENT_CLIENT_ACCURACY, bool isnew) { make_pure(this); + float entnum = ReadByte(); + int sf = ReadInt24_t(); if (sf == 0) { for (int w = 0; w <= WEP_LAST - WEP_FIRST; ++w) @@ -614,16 +616,28 @@ NET_HANDLE(ENT_CLIENT_ACCURACY, bool isnew) int f = 1; for (int w = 0; w <= WEP_LAST - WEP_FIRST; ++w) { if (sf & f) { - int b = ReadByte(); - if (b == 0) - weapon_accuracy[w] = -1; - else if (b == 255) - weapon_accuracy[w] = 1.0; // no better error handling yet, sorry - else - weapon_accuracy[w] = (b - 1.0) / 100.0; + if(entnum > 0) { + playerslots[entnum-1].accuracy_frags[w] = ReadByte(); + playerslots[entnum-1].accuracy_hit[w] = ReadShort(); + playerslots[entnum-1].accuracy_cnt_hit[w] = ReadShort(); + playerslots[entnum-1].accuracy_cnt_fired[w] = ReadShort(); + + LOG_INFOF("Duel stats ?/%d", playerslots[entnum-1].accuracy_cnt_fired[w]); + } else { + int b = ReadByte(); + if (b == 0) + weapon_accuracy[w] = -1; + else if (b == 255) + weapon_accuracy[w] = 1.0; // no better error handling yet, sorry + else + weapon_accuracy[w] = (b - 1.0) / 100.0; + } } f = (f == 0x800000) ? 1 : f * 2; } + + LOG_INFOF("Accuracy stats Client %d", entnum); + return true; } diff --git a/qcsrc/client/main.qh b/qcsrc/client/main.qh index 70b952d9e..ae26b1091 100644 --- a/qcsrc/client/main.qh +++ b/qcsrc/client/main.qh @@ -122,6 +122,12 @@ string GetSpeedUnit(int speed_unit); .int enttype; // entity type sent from server .int sv_entnum; // entity number sent from server +// z411 accuracy info +.float accuracy_frags[REGISTRY_MAX(Weapons)]; +.float accuracy_hit[REGISTRY_MAX(Weapons)]; +.float accuracy_cnt_hit[REGISTRY_MAX(Weapons)]; +.float accuracy_cnt_fired[REGISTRY_MAX(Weapons)]; + .int team; .int team_size; diff --git a/qcsrc/server/client.qc b/qcsrc/server/client.qc index d1ac2b8dd..e3eabdcc8 100644 --- a/qcsrc/server/client.qc +++ b/qcsrc/server/client.qc @@ -2082,6 +2082,7 @@ void Join(entity this) this.team_selected = false; // z411 + // send constant ready notification if(warmup_stage) Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_MISSING_READY); } diff --git a/qcsrc/server/weapons/accuracy.qc b/qcsrc/server/weapons/accuracy.qc index 2ec4fc710..cba12ca68 100644 --- a/qcsrc/server/weapons/accuracy.qc +++ b/qcsrc/server/weapons/accuracy.qc @@ -24,6 +24,12 @@ bool accuracy_send(entity this, entity to, int sf) entity a = this.owner; if (IS_SPEC(a)) a = a.enemy; a = CS(a).accuracy; + + // z411 send entity number + if(g_duel) + WriteByte(MSG_ENTITY, etof(a.owner)); + else + WriteByte(MSG_ENTITY, 0); if (to != a.owner) if (!autocvar_sv_accuracy_data_share && !CS(a.owner).cvar_cl_accuracy_data_share) @@ -31,10 +37,20 @@ bool accuracy_send(entity this, entity to, int sf) // note: zero sendflags can never be sent... so we can use that to say that we send no accuracy! WriteInt24_t(MSG_ENTITY, sf); if (sf == 0) return true; + // note: we know that client and server agree about SendFlags... int f = 1; for (int w = 0; w <= WEP_LAST - WEP_FIRST; ++w) { - if (sf & f) WriteByte(MSG_ENTITY, accuracy_byte(a.accuracy_hit[w], a.accuracy_fired[w])); + if (sf & f) { + if(g_duel) { + WriteByte(MSG_ENTITY, a.accuracy_frags[w]); + WriteShort(MSG_ENTITY, a.accuracy_hit[w]); + WriteShort(MSG_ENTITY, a.accuracy_cnt_hit[w]); + WriteShort(MSG_ENTITY, a.accuracy_cnt_fired[w]); + } else { + WriteByte(MSG_ENTITY, accuracy_byte(a.accuracy_hit[w], a.accuracy_fired[w])); + } + } f = (f == 0x800000) ? 1 : f * 2; } return true; @@ -45,7 +61,8 @@ void accuracy_init(entity e) { entity a = CS(e).accuracy = new_pure(accuracy); a.owner = e; - a.drawonlytoclient = e; + if(!g_duel) // z411 + a.drawonlytoclient = e; Net_LinkEntity(a, false, 0, accuracy_send); } @@ -87,10 +104,12 @@ void accuracy_add(entity this, Weapon w, int fired, int hit) a.fired_time = time; } - if (b == accuracy_byte(a.accuracy_hit[wepid], a.accuracy_fired[wepid])) return; // no change + if (!g_duel && b == accuracy_byte(a.accuracy_hit[wepid], a.accuracy_fired[wepid])) return; // no change int sf = 1 << (wepid % 24); a.SendFlags |= sf; - FOREACH_CLIENT(IS_SPEC(it) && it.enemy == this, { CS(it).accuracy.SendFlags |= sf; }); + + if(!g_duel) + FOREACH_CLIENT(IS_SPEC(it) && it.enemy == this, { CS(it).accuracy.SendFlags |= sf; }); } bool accuracy_isgooddamage(entity attacker, entity targ) -- 2.39.2