From 17d42e746a70e1c8abdd8cbeeab04ccee9dc46d0 Mon Sep 17 00:00:00 2001 From: z411 Date: Wed, 21 Jun 2023 14:16:18 -0400 Subject: [PATCH] Initial medal code port --- qcsrc/client/hud/panel/centerprint.qc | 54 +++++++++++++++ qcsrc/client/hud/panel/centerprint.qh | 4 ++ qcsrc/client/hud/panel/scoreboard.qc | 66 +++++++++++++++++++ .../gamemode/clanarena/sv_clanarena.qc | 40 ++++++++++- qcsrc/common/gamemodes/gamemode/ctf/sv_ctf.qc | 30 +++++++-- qcsrc/common/gamemodes/gamemode/ctf/sv_ctf.qh | 2 + .../gamemode/freezetag/sv_freezetag.qc | 25 +++++++ qcsrc/common/notifications/all.inc | 39 ++++++++++- qcsrc/common/notifications/all.qc | 65 +++++++++++++++--- qcsrc/common/notifications/all.qh | 26 +++++++- qcsrc/common/scores.qh | 23 +++++++ qcsrc/common/weapons/weapon/devastator.qc | 5 +- qcsrc/common/weapons/weapon/mortar.qc | 10 +-- qcsrc/common/weapons/weapon/vaporizer.qc | 7 +- qcsrc/common/weapons/weapon/vortex.qc | 7 +- qcsrc/server/client.qh | 7 ++ qcsrc/server/damage.qc | 28 +++++++- qcsrc/server/damage.qh | 2 + qcsrc/server/weapons/tracing.qc | 8 ++- 19 files changed, 415 insertions(+), 33 deletions(-) diff --git a/qcsrc/client/hud/panel/centerprint.qc b/qcsrc/client/hud/panel/centerprint.qc index 1e388c4d8..6765f5989 100644 --- a/qcsrc/client/hud/panel/centerprint.qc +++ b/qcsrc/client/hud/panel/centerprint.qc @@ -45,6 +45,10 @@ float centerprint_expire_time[CENTERPRINT_MAX_MSGS]; int centerprint_countdown_num[CENTERPRINT_MAX_MSGS]; bool centerprint_showing; +float centerprint_medal_expire_time; +string centerprint_medal_icon; +float centerprint_medal_times; + string centerprint_title; string centerprint_title_left; string centerprint_title_right; @@ -151,6 +155,19 @@ void centerprint_KillAll() } } +void centerprint_Medal(string icon, float times) +{ + if(!autocvar_hud_panel_centerprint_medals) return; + + centerprint_medal_expire_time = time + MSG_MEDAL_TIME; + centerprint_medal_times = times; + if(centerprint_medal_icon) + strunzone(centerprint_medal_icon); + centerprint_medal_icon = strzone(strcat("gfx/medal/", icon)); + + centerprint_showing = true; +} + void centerprint_SetDuelTitle(string left, string right) { float namesize = autocvar_hud_panel_scoreboard_namesize * hud_fontsize.x; @@ -266,6 +283,43 @@ void HUD_CenterPrint() pos.y += panel_size.y; align = bound(0, autocvar_hud_panel_centerprint_align, 1); + // Draw medals first + // TODO : This should be moved to a separate panel + if (autocvar_hud_panel_centerprint_medals && time < centerprint_medal_expire_time) { + float height = vid_conheight/50 * 4; + pos.y -= height; + + if(time < centerprint_medal_expire_time - MSG_MEDAL_FADE_TIME) + a = 1; + else + a = (centerprint_medal_expire_time - time) / MSG_MEDAL_FADE_TIME; + + vector tmp_in = pos; + + vector mysize = draw_getimagesize(centerprint_medal_icon); + vector newsize = vec2(height*(mysize.x/mysize.y), height); + vector fontsize = '1 1 0' * (newsize.y/2); + + tmp_in.x += (panel_size.x - newsize.x) / 2; // center medal icon + + if(centerprint_medal_times < autocvar_hud_panel_centerprint_medals_max) { + tmp_in.x -= ((newsize.x * 1.1) * (centerprint_medal_times - 1) / 2); + for(int t = 0; t < centerprint_medal_times; t++) { + drawpic(tmp_in, centerprint_medal_icon, newsize, '1 1 1', a, DRAWFLAG_NORMAL); + tmp_in.x += newsize.x * 1.1; + } + } else { + drawpic(tmp_in, centerprint_medal_icon, newsize, '1 1 1', a, DRAWFLAG_NORMAL); + tmp_in.x += newsize.x + fontsize.x * 0.25; // draw times next to it + tmp_in.y += (newsize.y - fontsize.y) / 2; + drawstring(tmp_in, ftos(centerprint_medal_times), fontsize, '1 1 1', a, DRAWFLAG_NORMAL); + } + + pos.y += height; + + all_messages_expired = false; + } + // Show title if available if(centerprint_title != "" || centerprint_title_left != "") { vector fontsize = cp_fontsize * autocvar_hud_panel_centerprint_fontscale_title; diff --git a/qcsrc/client/hud/panel/centerprint.qh b/qcsrc/client/hud/panel/centerprint.qh index 98f268b34..c99a55b0a 100644 --- a/qcsrc/client/hud/panel/centerprint.qh +++ b/qcsrc/client/hud/panel/centerprint.qh @@ -18,6 +18,9 @@ float autocvar_hud_panel_centerprint_fontscale_title = 1.8; bool autocvar_hud_panel_centerprint_dynamichud = true; float autocvar_hud_panel_centerprint_time; +bool autocvar_hud_panel_centerprint_medals = true; +int autocvar_hud_panel_centerprint_medals_max = 5; + void centerprint_Add(int new_id, string strMessage, float duration, int countdown_num); void centerprint_AddStandard(string strMessage); void centerprint_Kill(int id); @@ -26,3 +29,4 @@ void centerprint_KillAll(); void centerprint_SetDuelTitle(string left, string right); void centerprint_SetTitle(string title); void centerprint_ClearTitle(); +void centerprint_Medal(string icon, int times); diff --git a/qcsrc/client/hud/panel/scoreboard.qc b/qcsrc/client/hud/panel/scoreboard.qc index 220872095..2f32bf89a 100644 --- a/qcsrc/client/hud/panel/scoreboard.qc +++ b/qcsrc/client/hud/panel/scoreboard.qc @@ -49,6 +49,8 @@ string autocvar_hud_fontsize; string hud_fontsize_str; float max_namesize; +int total_medals; + float sbt_bg_alpha; float sbt_fg_alpha; float sbt_fg_alpha_self; @@ -1497,6 +1499,25 @@ 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_DrawMedal(vector pos, string icon, float height, float number) +{ + if(!number) return pos; + total_medals += number; + + vector tmp_sz, tmp_sz2; + tmp_sz = draw_getimagesize(icon); + tmp_sz2 = vec2(height*(tmp_sz.x/tmp_sz.y), height); + string val = ftos(number); + + drawpic(pos, icon, tmp_sz2, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + + pos.x += tmp_sz2.x + hud_fontsize.x * 0.25; + drawstring(pos + eY * ((tmp_sz2.y - hud_fontsize.y) / 2), val, hud_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + + pos.x += stringwidth(val, false, hud_fontsize) + hud_fontsize.x * 0.5; + return pos; +} + vector Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_size) { int max_players = 999; @@ -1644,6 +1665,49 @@ bool Scoreboard_WouldDraw() return false; } +vector Scoreboard_MedalStats_Draw(vector pos) +{ + vector orig = pos; + float height = hud_fontsize.y * 2; + + entity pl = playerslots[current_player]; + + vector title_pos = pos; + pos.x += 0.5 * hud_fontsize.x + panel_bg_padding; + pos.y += 1.25 * hud_fontsize.y; + + total_medals = 0; + + pos = Scoreboard_DrawMedal(pos, "gfx/medal/airshot", height, pl.(scores(SP_MEDAL_AIRSHOT))); + pos = Scoreboard_DrawMedal(pos, "gfx/medal/damage", height, pl.(scores(SP_MEDAL_DAMAGE))); + pos = Scoreboard_DrawMedal(pos, "gfx/medal/electrobitch", height, pl.(scores(SP_MEDAL_ELECTROBITCH))); + pos = Scoreboard_DrawMedal(pos, "gfx/medal/excellent", height, pl.(scores(SP_MEDAL_EXCELLENT))); + pos = Scoreboard_DrawMedal(pos, "gfx/medal/firstblood", height, pl.(scores(SP_MEDAL_FIRSTBLOOD))); + pos = Scoreboard_DrawMedal(pos, "gfx/medal/headshot", height, pl.(scores(SP_MEDAL_HEADSHOT))); + pos = Scoreboard_DrawMedal(pos, "gfx/medal/humiliation", height, pl.(scores(SP_MEDAL_HUMILIATION))); + pos = Scoreboard_DrawMedal(pos, "gfx/medal/impressive", height, pl.(scores(SP_MEDAL_IMPRESSIVE))); + pos = Scoreboard_DrawMedal(pos, "gfx/medal/yoda", height, pl.(scores(SP_MEDAL_YODA))); + pos = Scoreboard_DrawMedal(pos, "gfx/medal/telefrag", height, pl.(scores(SP_MEDAL_TELEFRAG))); + + if(total_medals) + pos.x += hud_fontsize.x; + + pos = Scoreboard_DrawMedal(pos, "gfx/medal/accuracy", height, pl.(scores(SP_MEDAL_ACCURACY))); + pos = Scoreboard_DrawMedal(pos, "gfx/medal/assist", height, pl.(scores(SP_MEDAL_ASSIST))); + pos = Scoreboard_DrawMedal(pos, "gfx/medal/capture", height, pl.(scores(SP_MEDAL_CAPTURE))); + pos = Scoreboard_DrawMedal(pos, "gfx/medal/defense", height, pl.(scores(SP_MEDAL_DEFENSE))); + pos = Scoreboard_DrawMedal(pos, "gfx/medal/perfect", height, pl.(scores(SP_MEDAL_PERFECT))); + + if(!total_medals) return orig; + + drawstring(title_pos, sprintf(_("Medal stats (total %d)"), total_medals), + hud_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + + pos.x = orig.x; + pos.y += height + hud_fontsize.y * 0.5; + return pos; +} + float average_accuracy; vector Scoreboard_AccuracyStats_Draw(vector pos, vector rgb, vector bg_size) { @@ -2504,6 +2568,8 @@ void Scoreboard_Draw() pos = Scoreboard_MakeTable(pos, tm, panel_bg_color, bg_size); } + pos = Scoreboard_MedalStats_Draw(pos); + // draw scoreboard spectators before accuracy and item stats if (autocvar_hud_panel_scoreboard_spectators_position == 0) { pos = Scoreboard_Spectators_Draw(pos); diff --git a/qcsrc/common/gamemodes/gamemode/clanarena/sv_clanarena.qc b/qcsrc/common/gamemodes/gamemode/clanarena/sv_clanarena.qc index 7a16141fc..ca70fa6b3 100644 --- a/qcsrc/common/gamemodes/gamemode/clanarena/sv_clanarena.qc +++ b/qcsrc/common/gamemodes/gamemode/clanarena/sv_clanarena.qc @@ -44,6 +44,21 @@ void CA_count_alive_players() void nades_Clear(entity player); +entity ca_LastPlayer(float tm) +{ + entity last_pl = NULL; + FOREACH_CLIENT(IS_PLAYER(it) && it.team == tm, { + if (!IS_DEAD(it)) + { + if (!last_pl) + last_pl = it; + else + return NULL; + } + }); + return last_pl; +} + int CA_PreventStalemate() { //LOG_INFO("PreventStalemate running"); @@ -140,11 +155,25 @@ float CA_CheckWinner() if (!winner_team) return 0; + bool perfect = false; if(winner_team > 0) { + // Defense medal: If you won the round alone + //entity tm = Team_GetTeam(winner_team); + //entity last_pl = ca_LastPlayer(winner_team); + + // TODO : Get number of players in the team + //if(last_pl && Team_GetNumberOfPlayers(tm) >= 3) { + // GIVE_MEDAL(last_pl, DEFENSE); + //} + Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, APP_TEAM_NUM(winner_team, CENTER_ROUND_TEAM_WIN)); Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(winner_team, INFO_ROUND_TEAM_WIN)); TeamScore_AddToTeam(winner_team, ST_CA_ROUNDS, +1); + + // TODO : Get number of players in the team + //if (Team_GetNumberOfPlayers(tm) >= 3 && Team_GetNumberOfAlivePlayers(tm) == Team_GetNumberOfPlayers(tm)) + // perfect = true; } else if(winner_team == -1) { @@ -161,7 +190,16 @@ float CA_CheckWinner() game_stopped = true; round_handler_Init(5, autocvar_g_ca_warmup, autocvar_g_ca_round_timelimit); - FOREACH_CLIENT(IS_PLAYER(it), { nades_Clear(it); }); + FOREACH_CLIENT(IS_PLAYER(it), { + nades_Clear(it); + + // Give perfect medal if everyone in the winner team is alive + if(perfect && it.team == winner_team) { + GIVE_MEDAL(it, PERFECT); + } + + // TODO : Accuracy medal + }); return 1; } diff --git a/qcsrc/common/gamemodes/gamemode/ctf/sv_ctf.qc b/qcsrc/common/gamemodes/gamemode/ctf/sv_ctf.qc index e16ed9138..c145c1aa6 100644 --- a/qcsrc/common/gamemodes/gamemode/ctf/sv_ctf.qc +++ b/qcsrc/common/gamemodes/gamemode/ctf/sv_ctf.qc @@ -614,6 +614,8 @@ void ctf_Handle_Capture(entity flag, entity toucher, int capturetype) if(!old_time || new_time < old_time) GameRules_scoring_add(player, CTF_CAPTIME, new_time - old_time); + GIVE_MEDAL(player, CAPTURE); + // effects Send_Effect_(flag.capeffect, flag.origin, '0 0 0', 1); #if 0 @@ -626,8 +628,10 @@ void ctf_Handle_Capture(entity flag, entity toucher, int capturetype) WaypointSprite_Kill(player.wps_flagcarrier); if(flag.speedrunning) { ctf_FakeTimeLimit(player, -1); } - if((enemy_flag.ctf_dropper) && (player != enemy_flag.ctf_dropper)) - { GameRules_scoring_add_team(enemy_flag.ctf_dropper, SCORE, ((enemy_flag.score_assist) ? enemy_flag.score_assist : autocvar_g_ctf_score_capture_assist)); } + if((enemy_flag.ctf_dropper) && (player != enemy_flag.ctf_dropper)) { + GameRules_scoring_add_team(enemy_flag.ctf_dropper, SCORE, ((enemy_flag.score_assist) ? enemy_flag.score_assist : autocvar_g_ctf_score_capture_assist)); + GIVE_MEDAL(enemy_flag.ctf_dropper, ASSIST); + } } flag.enemy = toucher; @@ -2250,8 +2254,26 @@ MUTATOR_HOOKFUNCTION(ctf, PlayerDies) if((frag_attacker != frag_target) && (IS_PLAYER(frag_attacker)) && (frag_target.flagcarried)) { - GameRules_scoring_add_team(frag_attacker, SCORE, ((SAME_TEAM(frag_attacker, frag_target)) ? -autocvar_g_ctf_score_kill : autocvar_g_ctf_score_kill)); - GameRules_scoring_add(frag_attacker, CTF_FCKILLS, 1); + if(frag_target.flagcarried) { + // Killing an enemy flag carrier + GameRules_scoring_add_team(frag_attacker, SCORE, ((SAME_TEAM(frag_attacker, frag_target)) ? -autocvar_g_ctf_score_kill : autocvar_g_ctf_score_kill)); + GameRules_scoring_add(frag_attacker, CTF_FCKILLS, 1); + } else if(frag_attacker.flagcarried) { + // Defense medal for killing a flag carrier + GIVE_MEDAL(frag_attacker, DEFENSE); + } else { + entity tmp_entity; + for(tmp_entity = ctf_worldflaglist; tmp_entity; tmp_entity = tmp_entity.ctf_worldflagnext) + if(tmp_entity.ctf_status == FLAG_BASE && CTF_SAMETEAM(tmp_entity, frag_attacker)) + { + if(CTF_IS_NEAR(frag_target, tmp_entity, '1 1 1' * 1500)) + { + // Defense medal for killing an enemy near your flag + GIVE_MEDAL(frag_attacker, DEFENSE); + } + break; + } + } } if(frag_target.flagcarried) diff --git a/qcsrc/common/gamemodes/gamemode/ctf/sv_ctf.qh b/qcsrc/common/gamemodes/gamemode/ctf/sv_ctf.qh index adb061809..21a02f690 100644 --- a/qcsrc/common/gamemodes/gamemode/ctf/sv_ctf.qh +++ b/qcsrc/common/gamemodes/gamemode/ctf/sv_ctf.qh @@ -176,3 +176,5 @@ void havocbot_role_ctf_setrole(entity bot, int role); // team checking #define CTF_SAMETEAM(a,b) ((autocvar_g_ctf_reverse || (ctf_oneflag && autocvar_g_ctf_oneflag_reverse)) ? DIFF_TEAM(a,b) : SAME_TEAM(a,b)) #define CTF_DIFFTEAM(a,b) ((autocvar_g_ctf_reverse || (ctf_oneflag && autocvar_g_ctf_oneflag_reverse)) ? SAME_TEAM(a,b) : DIFF_TEAM(a,b)) +#define CTF_IS_NEAR(player, it, extra_size) \ + boxesoverlap(player.absmin - extra_size, player.absmax + extra_size, it.absmin, it.absmax) diff --git a/qcsrc/common/gamemodes/gamemode/freezetag/sv_freezetag.qc b/qcsrc/common/gamemodes/gamemode/freezetag/sv_freezetag.qc index f1e6b1ce2..a192ed8e1 100644 --- a/qcsrc/common/gamemodes/gamemode/freezetag/sv_freezetag.qc +++ b/qcsrc/common/gamemodes/gamemode/freezetag/sv_freezetag.qc @@ -86,6 +86,21 @@ bool freezetag_CheckTeams() void nades_Clear(entity); void nades_GiveBonus(entity player, float score); +entity freezetag_LastPlayer(float tm) +{ + entity last_pl = NULL; + FOREACH_CLIENT(IS_PLAYER(it) && it.team == tm, { + if (STAT(FROZEN, it) != FROZEN_NORMAL && GetResource(it, RES_HEALTH) >= 1) + { + if (!last_pl) + last_pl = it; + else + return NULL; + } + }); + return last_pl; +} + bool freezetag_CheckWinner() { if(round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0) @@ -108,6 +123,12 @@ bool freezetag_CheckWinner() if(winner_team > 0) { + entity last_pl = freezetag_LastPlayer(winner_team); + if(last_pl) { + // Defense medal for winning the round alone + GIVE_MEDAL(last_pl, DEFENSE); + } + Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, APP_TEAM_NUM(winner_team, CENTER_ROUND_TEAM_WIN)); Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(winner_team, INFO_ROUND_TEAM_WIN)); TeamScore_AddToTeam(winner_team, ST_FT_ROUNDS, +1); @@ -637,6 +658,10 @@ MUTATOR_HOOKFUNCTION(ft, PlayerPreThink, CBC_ORDER_FIRST) Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_FREEZETAG_REVIVED, revivers_first.netname); Send_Notification(NOTIF_ONE, revivers_first, MSG_CENTER, CENTER_FREEZETAG_REVIVE, player.netname); Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_FREEZETAG_REVIVED, player.netname, revivers_first.netname); + + // Assist medal for reviving a team mate + GIVE_MEDAL(revivers_first, ASSIST); + if(autocvar_sv_eventlog) { string revivers = ""; diff --git a/qcsrc/common/notifications/all.inc b/qcsrc/common/notifications/all.inc index effed26d9..dd5a69d18 100644 --- a/qcsrc/common/notifications/all.inc +++ b/qcsrc/common/notifications/all.inc @@ -116,29 +116,35 @@ // MSG_ANNCE_NOTIFICATIONS MSG_ANNCE_NOTIF(ACHIEVEMENT_AIRSHOT, N_GNTLOFF, "airshot", CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME) MSG_ANNCE_NOTIF(ACHIEVEMENT_AMAZING, N_GNTLOFF, "amazing", CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME) + MSG_ANNCE_NOTIF(ACHIEVEMENT_ASSIST, N_GNTLOFF, "assist", CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME) MSG_ANNCE_NOTIF(ACHIEVEMENT_AWESOME, N_GNTLOFF, "awesome", CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME) + MSG_ANNCE_NOTIF(ACHIEVEMENT_DAMAGE, N_GNTLOFF, "damage", CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME) + MSG_ANNCE_NOTIF(ACHIEVEMENT_DEFENSE, N_GNTLOFF, "defense", CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME) + MSG_ANNCE_NOTIF(ACHIEVEMENT_EXCELLENT, N_GNTLOFF, "excellent", CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME) MSG_ANNCE_NOTIF(ACHIEVEMENT_BOTLIKE, N_GNTLOFF, "botlike", CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME) MSG_ANNCE_NOTIF(ACHIEVEMENT_ELECTROBITCH, N__ALWAYS, "electrobitch", CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME) MSG_ANNCE_NOTIF(ACHIEVEMENT_IMPRESSIVE, N_GNTLOFF, "impressive", CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME) MSG_ANNCE_NOTIF(ACHIEVEMENT_YODA, N_GNTLOFF, "yoda", CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME) + MSG_ANNCE_NOTIF(ACHIEVEMENT_PERFECT, N_GNTLOFF, "perfect", CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME) + MSG_ANNCE_NOTIF(ACHIEVEMENT_ACCURACY, N_GNTLOFF, "accuracy", CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME) + MSG_ANNCE_NOTIF(ACHIEVEMENT_TELEFRAG, N_GNTLOFF, "telefrag", CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME) MSG_ANNCE_NOTIF(BEGIN, N__ALWAYS, "begin", CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_INSTANT) MSG_ANNCE_NOTIF(HEADSHOT, N__ALWAYS, "headshot", CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME) + MSG_ANNCE_NOTIF(HUMILIATION, N__ALWAYS, "humiliation", CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME) MSG_ANNCE_NOTIF(KILLSTREAK_03, N_GNTLOFF, "03kills", CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME) MSG_ANNCE_NOTIF(KILLSTREAK_05, N_GNTLOFF, "05kills", CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME) MSG_ANNCE_NOTIF(KILLSTREAK_10, N_GNTLOFF, "10kills", CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME) MSG_ANNCE_NOTIF(KILLSTREAK_15, N_GNTLOFF, "15kills", CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME) - MSG_ANNCE_NOTIF(KILLSTREAK_20, N_GNTLOFF, "20kills", CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME) - MSG_ANNCE_NOTIF(KILLSTREAK_25, N_GNTLOFF, "25kills", CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME) - MSG_ANNCE_NOTIF(KILLSTREAK_30, N_GNTLOFF, "30kills", CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME) MSG_ANNCE_NOTIF(INSTAGIB_LASTSECOND, N_GNTLOFF, "lastsecond", CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME) MSG_ANNCE_NOTIF(INSTAGIB_NARROWLY, N_GNTLOFF, "narrowly", CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME) MSG_ANNCE_NOTIF(INSTAGIB_TERMINATED, N_GNTLOFF, "terminated", CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME) MSG_ANNCE_NOTIF(MULTIFRAG, N___NEVER, "multifrag", CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME) + MSG_ANNCE_NOTIF(FIRSTBLOOD, N_GNTLOFF, "firstblood", CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME) MSG_ANNCE_NOTIF(NUM_1, N__ALWAYS, "1", CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_INSTANT) MSG_ANNCE_NOTIF(NUM_2, N__ALWAYS, "2", CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_INSTANT) @@ -210,6 +216,33 @@ MSG_ANNCE_NOTIF(VOTE_CALL, N__ALWAYS, "votecall", CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME) MSG_ANNCE_NOTIF(VOTE_FAIL, N__ALWAYS, "votefail", CH_INFO, VOL_BASEVOICE, ATTEN_NONE, ANNCE_DEFTIME) +// MSG_MEDAL_NOTIFICATIONS + +#define MSG_MEDAL_TIME 2 +#define MSG_MEDAL_FADE_TIME 0.5 + + MSG_MEDAL_NOTIF(AIRSHOT, N__ALWAYS, "airshot", ANNCE_ACHIEVEMENT_AIRSHOT) + MSG_MEDAL_NOTIF(ASSIST, N__ALWAYS, "assist", ANNCE_ACHIEVEMENT_ASSIST) + MSG_MEDAL_NOTIF(DAMAGE, N__ALWAYS, "damage", ANNCE_ACHIEVEMENT_DAMAGE) + MSG_MEDAL_NOTIF(DEFENSE, N__ALWAYS, "defense", ANNCE_ACHIEVEMENT_DEFENSE) + MSG_MEDAL_NOTIF(ELECTROBITCH, N__ALWAYS, "electrobitch", ANNCE_ACHIEVEMENT_ELECTROBITCH) + MSG_MEDAL_NOTIF(EXCELLENT, N__ALWAYS, "excellent", ANNCE_ACHIEVEMENT_EXCELLENT) + MSG_MEDAL_NOTIF(FIRSTBLOOD, N__ALWAYS, "firstblood", ANNCE_FIRSTBLOOD) + MSG_MEDAL_NOTIF(HEADSHOT, N__ALWAYS, "headshot", ANNCE_HEADSHOT) + MSG_MEDAL_NOTIF(HUMILIATION, N__ALWAYS, "humiliation", ANNCE_HUMILIATION) + MSG_MEDAL_NOTIF(IMPRESSIVE, N__ALWAYS, "impressive", ANNCE_ACHIEVEMENT_IMPRESSIVE) + MSG_MEDAL_NOTIF(YODA, N__ALWAYS, "yoda", ANNCE_ACHIEVEMENT_YODA) + MSG_MEDAL_NOTIF(TELEFRAG, N__ALWAYS, "telefrag", ANNCE_ACHIEVEMENT_TELEFRAG) + + MSG_MEDAL_NOTIF(CAPTURE, N__ALWAYS, "capture", NULL) + MSG_MEDAL_NOTIF(PERFECT, N__ALWAYS, "perfect", ANNCE_ACHIEVEMENT_PERFECT) + MSG_MEDAL_NOTIF(ACCURACY, N__ALWAYS, "accuracy", ANNCE_ACHIEVEMENT_ACCURACY) + + MSG_MEDAL_NOTIF(KILLSTREAK_03, N__ALWAYS, "killstreak_03", ANNCE_KILLSTREAK_03) + MSG_MEDAL_NOTIF(KILLSTREAK_05, N__ALWAYS, "killstreak_05", ANNCE_KILLSTREAK_05) + MSG_MEDAL_NOTIF(KILLSTREAK_10, N__ALWAYS, "killstreak_10", ANNCE_KILLSTREAK_10) + MSG_MEDAL_NOTIF(KILLSTREAK_15, N__ALWAYS, "killstreak_15", ANNCE_KILLSTREAK_15) + #undef N___NEVER #undef N_GNTLOFF #undef N__ALWAYS diff --git a/qcsrc/common/notifications/all.qc b/qcsrc/common/notifications/all.qc index 58910887e..b8a8a2e20 100644 --- a/qcsrc/common/notifications/all.qc +++ b/qcsrc/common/notifications/all.qc @@ -419,6 +419,7 @@ void Create_Notification_Entity(entity notif, case MSG_CENTER: case MSG_MULTI: case MSG_CHOICE: + case MSG_MEDAL: break; default: LOG_INFOF( @@ -757,6 +758,17 @@ void Create_Notification_Entity_Choice(entity notif, } } +void Create_Notification_Entity_Medal(entity notif, + float var_cvar, + string namestring, + /* MSG_MEDAL */ + string icon, + Notification anncename) + { + notif.nent_floatcount = 1; + if (icon != "") { notif.nent_icon = strzone(icon); } + if (anncename) { notif.nent_msgannce = anncename; } + } // =============== // Cvar Handling @@ -829,6 +841,7 @@ void Dump_Notifications(int fh, bool alsoprint) int NOTIF_CENTER_COUNT = 0; int NOTIF_MULTI_COUNT = 0; int NOTIF_CHOICE_COUNT = 0; + int NOTIF_MEDAL_COUNT = 0; FOREACH(Notifications, true, { switch (it.nent_type) { @@ -837,6 +850,7 @@ void Dump_Notifications(int fh, bool alsoprint) case MSG_CENTER: ++NOTIF_CENTER_COUNT; break; case MSG_MULTI: ++NOTIF_MULTI_COUNT; break; case MSG_CHOICE: ++NOTIF_CHOICE_COUNT; break; + case MSG_MEDAL: ++NOTIF_MEDAL_COUNT; break; } }); @@ -877,6 +891,13 @@ void Dump_Notifications(int fh, bool alsoprint) ); }); + NOTIF_WRITE(sprintf("\n// MSG_MEDAL notifications:\n")); + FOREACH(Notifications, it.nent_type == MSG_MEDAL && (!it.nent_teamnum || it.nent_teamnum == NUM_TEAM_1), { + NOTIF_WRITE_ENTITY(it, + "0 = off, 1 = enabled" + ); + }); + // edit these to match whichever cvars are used for specific notification options NOTIF_WRITE("\n// HARD CODED notification variables:\n"); @@ -965,19 +986,21 @@ void Dump_Notifications(int fh, bool alsoprint) ); LOG_INFOF("Notification counts (total = %d): " - "MSG_ANNCE = %d, MSG_INFO = %d, MSG_CENTER = %d, MSG_MULTI = %d, MSG_CHOICE = %d\n", + "MSG_ANNCE = %d, MSG_INFO = %d, MSG_CENTER = %d, MSG_MULTI = %d, MSG_CHOICE = %d, MSG_MEDAL = %d\n", ( NOTIF_ANNCE_COUNT + NOTIF_INFO_COUNT + NOTIF_CENTER_COUNT + NOTIF_MULTI_COUNT + - NOTIF_CHOICE_COUNT + NOTIF_CHOICE_COUNT + + NOTIF_MEDAL_COUNT ), NOTIF_ANNCE_COUNT, NOTIF_INFO_COUNT, NOTIF_CENTER_COUNT, NOTIF_MULTI_COUNT, - NOTIF_CHOICE_COUNT + NOTIF_CHOICE_COUNT, + NOTIF_MEDAL_COUNT ); #undef NOTIF_WRITE_HARDCODED #undef NOTIF_WRITE_ENTITY @@ -1180,7 +1203,7 @@ void Local_Notification_centerprint_Add( centerprint_Add(ORDINAL(cpid), input, stof(arg_slot[0]), stof(arg_slot[1])); } -void Local_Notification_Queue_Run(MSG net_type, entity notif) +void Local_Notification_Queue_Run(MSG net_type, entity notif, float f1) { switch (net_type) { @@ -1189,10 +1212,22 @@ void Local_Notification_Queue_Run(MSG net_type, entity notif) Local_Notification_sound(notif.nent_channel, notif.nent_snd, notif.nent_vol, notif.nent_position); break; } + + case MSG_MEDAL: + { + centerprint_Medal(notif.nent_icon, f1); + if(notif.nent_msgannce) + Local_Notification_sound( + notif.nent_msgannce.nent_channel, + notif.nent_msgannce.nent_snd, + notif.nent_msgannce.nent_vol, + notif.nent_msgannce.nent_position); + break; + } } } -void Local_Notification_Queue_Add(MSG net_type, entity notif, float queue_time) +void Local_Notification_Queue_Add(MSG net_type, entity notif, float queue_time, float f1) { // Guess length if required if(queue_time == 0) @@ -1200,7 +1235,7 @@ void Local_Notification_Queue_Add(MSG net_type, entity notif, float queue_time) if(queue_time == -1 || time > notif_queue_next_time) { // Run immediately - Local_Notification_Queue_Run(net_type, notif); + Local_Notification_Queue_Run(net_type, notif, f1); notif_queue_next_time = time + queue_time; } else { // Put in queue @@ -1209,6 +1244,7 @@ void Local_Notification_Queue_Add(MSG net_type, entity notif, float queue_time) notif_queue_type[notif_queue_length] = net_type; notif_queue_entity[notif_queue_length] = notif; notif_queue_time[notif_queue_length] = notif_queue_next_time; + notif_queue_f1[notif_queue_length] = f1; notif_queue_next_time += queue_time; ++notif_queue_length; @@ -1220,7 +1256,7 @@ void Local_Notification_Queue_Process() if(!notif_queue_length || notif_queue_time[0] > time) return; - Local_Notification_Queue_Run(notif_queue_type[0], notif_queue_entity[0]); + Local_Notification_Queue_Run(notif_queue_type[0], notif_queue_entity[0], notif_queue_f1[0]); // Shift queue to the left --notif_queue_length; @@ -1228,6 +1264,7 @@ void Local_Notification_Queue_Process() notif_queue_type[j] = notif_queue_type[j+1]; notif_queue_entity[j] = notif_queue_entity[j+1]; notif_queue_time[j] = notif_queue_time[j+1]; + notif_queue_f1[j] = notif_queue_f1[j+1]; } } @@ -1303,7 +1340,7 @@ void Local_Notification(MSG net_type, Notification net_name, ...count) case MSG_ANNCE: { #ifdef CSQC - Local_Notification_Queue_Add(net_type, notif, notif.nent_queuetime); + Local_Notification_Queue_Add(net_type, notif, notif.nent_queuetime, f1); #else backtrace("MSG_ANNCE on server?... Please notify Samual immediately!\n"); #endif @@ -1413,6 +1450,18 @@ void Local_Notification(MSG net_type, Notification net_name, ...count) f1, f2, f3, f4); break; } + + #ifdef CSQC + case MSG_MEDAL: + { + Local_Notification_Queue_Add( + net_type, + notif, + (notif.nent_msgannce ? MSG_MEDAL_TIME : 0), + f1); + break; + } + #endif } } diff --git a/qcsrc/common/notifications/all.qh b/qcsrc/common/notifications/all.qh index 00bd94bb7..e1c1de10c 100644 --- a/qcsrc/common/notifications/all.qh +++ b/qcsrc/common/notifications/all.qh @@ -25,6 +25,8 @@ ENUMCLASS(MSG) CASE(MSG, CHOICE) /** Kill centerprint message @deprecated */ CASE(MSG, CENTER_KILL) + /** Medal notification */ + CASE(MSG, MEDAL) ENUMCLASS_END(MSG) string Get_Notif_TypeName(MSG net_type) @@ -36,6 +38,7 @@ string Get_Notif_TypeName(MSG net_type) case MSG_CENTER: return "MSG_CENTER"; case MSG_MULTI: return "MSG_MULTI"; case MSG_CHOICE: return "MSG_CHOICE"; + case MSG_MEDAL: return "MSG_MEDAL"; case MSG_CENTER_KILL: return "MSG_CENTER_KILL"; } LOG_WARNF("Get_Notif_TypeName(%d): Improper net type!", ORDINAL(net_type)); @@ -159,6 +162,13 @@ void Create_Notification_Entity_Choice(entity notif, Notification optiona, Notification optionb); +void Create_Notification_Entity_Medal(entity notif, + float var_cvar, + string namestring, + /* MSG_MEDAL */ + string icon, + Notification anncename); + void Dump_Notifications(int fh, bool alsoprint); #define DEFAULT_FILENAME "notifications_dump.cfg" @@ -391,6 +401,7 @@ const int NOTIF_QUEUE_MAX = 10; entity notif_queue_entity[NOTIF_QUEUE_MAX]; MSG notif_queue_type[NOTIF_QUEUE_MAX]; float notif_queue_time[NOTIF_QUEUE_MAX]; +float notif_queue_f1[NOTIF_QUEUE_MAX]; float notif_queue_next_time; int notif_queue_length; @@ -468,9 +479,6 @@ MACRO_END SPREE_ITEM(5, 05, _("RAGE! "), _("%s^K1 unlocked RAGE! %s^BG"), _("%s^K1 made FIVE SCORES IN A ROW! %s^BG")) \ SPREE_ITEM(10, 10, _("MASSACRE! "), _("%s^K1 started a MASSACRE! %s^BG"), _("%s^K1 made TEN SCORES IN A ROW! %s^BG")) \ SPREE_ITEM(15, 15, _("MAYHEM! "), _("%s^K1 executed MAYHEM! %s^BG"), _("%s^K1 made FIFTEEN SCORES IN A ROW! %s^BG")) \ - SPREE_ITEM(20, 20, _("BERSERKER! "), _("%s^K1 is a BERSERKER! %s^BG"), _("%s^K1 made TWENTY SCORES IN A ROW! %s^BG")) \ - SPREE_ITEM(25, 25, _("CARNAGE! "), _("%s^K1 inflicts CARNAGE! %s^BG"), _("%s^K1 made TWENTY FIVE SCORES IN A ROW! %s^BG")) \ - SPREE_ITEM(30, 30, _("ARMAGEDDON! "), _("%s^K1 unleashes ARMAGEDDON! %s^BG"), _("%s^K1 made THIRTY SCORES IN A ROW! %s^BG")) #ifdef CSQC string notif_arg_frag_ping(bool newline, float fping) @@ -866,6 +874,18 @@ Notification Get_Notif_Ent(MSG net_type, int net_name) optionb); /* optionb */ \ } +#define MSG_MEDAL_NOTIF(name, defaultvalue, icon, anncename) \ + NOTIF_ADD_AUTOCVAR(MEDAL_##name, defaultvalue) \ + MSG_MEDAL_NOTIF_(0, MEDAL_##name, MEDAL_##name, defaultvalue, icon, anncename) + +#define MSG_MEDAL_NOTIF_(teamnum, name, cvarname, defaultvalue, icon, anncename) \ + REGISTER(Notifications, name, m_id, new_pure(msg_medal_notification)) { \ + Create_Notification_Entity (this, defaultvalue, ACVNN(cvarname), MSG_MEDAL, strtoupper(#name), teamnum); \ + Create_Notification_Entity_Medal(this, ACVNN(cvarname), strtoupper(#name), \ + icon, \ + anncename); \ + } + REGISTRY_BEGIN(Notifications) { notif_global_error = false; diff --git a/qcsrc/common/scores.qh b/qcsrc/common/scores.qh index 8a01893f1..fc5f418f5 100644 --- a/qcsrc/common/scores.qh +++ b/qcsrc/common/scores.qh @@ -91,6 +91,29 @@ REGISTER_SP(SEPARATOR); REGISTER_SP(KDRATIO); // kills / deaths REGISTER_SP(SUM); // kills - deaths REGISTER_SP(FRAGS); // kills - suicides + +// Medals +REGISTER_SP(MEDAL_AIRSHOT); +REGISTER_SP(MEDAL_DAMAGE); +REGISTER_SP(MEDAL_ELECTROBITCH); +REGISTER_SP(MEDAL_EXCELLENT); +REGISTER_SP(MEDAL_FIRSTBLOOD); +REGISTER_SP(MEDAL_HEADSHOT); +REGISTER_SP(MEDAL_HUMILIATION); +REGISTER_SP(MEDAL_IMPRESSIVE); +REGISTER_SP(MEDAL_YODA); +REGISTER_SP(MEDAL_TELEFRAG); + +REGISTER_SP(MEDAL_ACCURACY); +REGISTER_SP(MEDAL_ASSIST); +REGISTER_SP(MEDAL_CAPTURE); +REGISTER_SP(MEDAL_DEFENSE); +REGISTER_SP(MEDAL_PERFECT); + +REGISTER_SP(MEDAL_KILLSTREAK_03); +REGISTER_SP(MEDAL_KILLSTREAK_05); +REGISTER_SP(MEDAL_KILLSTREAK_10); +REGISTER_SP(MEDAL_KILLSTREAK_15); #endif diff --git a/qcsrc/common/weapons/weapon/devastator.qc b/qcsrc/common/weapons/weapon/devastator.qc index 6f428dc6c..d005e00d2 100644 --- a/qcsrc/common/weapons/weapon/devastator.qc +++ b/qcsrc/common/weapons/weapon/devastator.qc @@ -22,8 +22,9 @@ void W_Devastator_Explode(entity this, entity directhitentity) if(IS_PLAYER(directhitentity)) if(DIFF_TEAM(this.realowner, directhitentity)) if(!IS_DEAD(directhitentity)) - if(IsFlying(directhitentity)) - Send_Notification(NOTIF_ONE, this.realowner, MSG_ANNCE, ANNCE_ACHIEVEMENT_AIRSHOT); + if(IsFlying(directhitentity)) { + GIVE_MEDAL(this.realowner, AIRSHOT); + } this.event_damage = func_null; this.takedamage = DAMAGE_NO; diff --git a/qcsrc/common/weapons/weapon/mortar.qc b/qcsrc/common/weapons/weapon/mortar.qc index 442f88265..1af84e679 100644 --- a/qcsrc/common/weapons/weapon/mortar.qc +++ b/qcsrc/common/weapons/weapon/mortar.qc @@ -8,8 +8,9 @@ void W_Mortar_Grenade_Explode(entity this, entity directhitentity) if(IS_PLAYER(directhitentity)) if(DIFF_TEAM(this.realowner, directhitentity)) if(!IS_DEAD(directhitentity)) - if(IsFlying(directhitentity)) - Send_Notification(NOTIF_ONE, this.realowner, MSG_ANNCE, ANNCE_ACHIEVEMENT_AIRSHOT); + if(IsFlying(directhitentity)) { + GIVE_MEDAL(this.realowner, AIRSHOT); + } this.event_damage = func_null; this.takedamage = DAMAGE_NO; @@ -33,8 +34,9 @@ void W_Mortar_Grenade_Explode2(entity this, entity directhitentity) if(IS_PLAYER(directhitentity)) if(DIFF_TEAM(this.realowner, directhitentity)) if(!IS_DEAD(directhitentity)) - if(IsFlying(directhitentity)) - Send_Notification(NOTIF_ONE, this.realowner, MSG_ANNCE, ANNCE_ACHIEVEMENT_AIRSHOT); + if(IsFlying(directhitentity)) { + GIVE_MEDAL(this.realowner, AIRSHOT); + } this.event_damage = func_null; this.takedamage = DAMAGE_NO; diff --git a/qcsrc/common/weapons/weapon/vaporizer.qc b/qcsrc/common/weapons/weapon/vaporizer.qc index 4be54b6b2..e20fcf5c2 100644 --- a/qcsrc/common/weapons/weapon/vaporizer.qc +++ b/qcsrc/common/weapons/weapon/vaporizer.qc @@ -132,12 +132,17 @@ void W_Vaporizer_Attack(Weapon thiswep, entity actor, .entity weaponentity) SendCSQCVaporizerBeamParticle(actor, impressive_hits); if(yoda && flying) - Send_Notification(NOTIF_ONE, actor, MSG_ANNCE, ANNCE_ACHIEVEMENT_YODA); + { + GIVE_MEDAL(actor, YODA); + } + + /* We're going to disable Impressive medals for the vaporizer. if(impressive_hits && actor.vaporizer_lasthit) { Send_Notification(NOTIF_ONE, actor, MSG_ANNCE, ANNCE_ACHIEVEMENT_IMPRESSIVE); impressive_hits = 0; // only every second time } + */ actor.vaporizer_lasthit = impressive_hits; diff --git a/qcsrc/common/weapons/weapon/vortex.qc b/qcsrc/common/weapons/weapon/vortex.qc index 4d4e43ec6..9cc02f25e 100644 --- a/qcsrc/common/weapons/weapon/vortex.qc +++ b/qcsrc/common/weapons/weapon/vortex.qc @@ -130,10 +130,13 @@ void W_Vortex_Attack(Weapon thiswep, entity actor, .entity weaponentity, float i FireRailgunBullet(actor, weaponentity, w_shotorg, w_shotorg + w_shotdir * max_shot_distance, mydmg, false, myforce, mymindist, mymaxdist, myhalflife, myforcehalflife, dtype); if(yoda && flying) - Send_Notification(NOTIF_ONE, actor, MSG_ANNCE, ANNCE_ACHIEVEMENT_YODA); + { + GIVE_MEDAL(actor, YODA); + } + if(impressive_hits && actor.vortex_lasthit) { - Send_Notification(NOTIF_ONE, actor, MSG_ANNCE, ANNCE_ACHIEVEMENT_IMPRESSIVE); + GIVE_MEDAL(actor, IMPRESSIVE); impressive_hits = 0; // only every second time } diff --git a/qcsrc/server/client.qh b/qcsrc/server/client.qh index 8c607cf9a..eb5d8073f 100644 --- a/qcsrc/server/client.qh +++ b/qcsrc/server/client.qh @@ -57,6 +57,8 @@ int autocvar_sv_spectate; bool autocvar_sv_teamnagger; float autocvar_sv_player_scale; +float autocvar_g_medals_excellent_time = 2; + // WEAPONTODO .string weaponorder_byimpulse; @@ -410,3 +412,8 @@ void Join(entity this); #define SPECTATE_COPYFIELD(fld) SPECTATE_COPY() { this.(fld) = spectatee.(fld); } const int MAX_SPECTATORS = 7; + +float _medal_times; +#define GIVE_MEDAL(entity,medalname) \ + _medal_times = GameRules_scoring_add(entity, MEDAL_##medalname, 1); \ + Send_Notification(NOTIF_ONE, entity, MSG_MEDAL, MEDAL_##medalname, _medal_times); diff --git a/qcsrc/server/damage.qc b/qcsrc/server/damage.qc index 429d3e262..011ff4397 100644 --- a/qcsrc/server/damage.qc +++ b/qcsrc/server/damage.qc @@ -169,6 +169,7 @@ void Obituary_SpecialDeath( float Obituary_WeaponDeath( entity notif_target, + entity attacker, float murder, int deathtype, string s1, string s2, string s3, @@ -201,6 +202,20 @@ float Obituary_WeaponDeath( s1, s2, s3, "", f1, f2, 0, 0 ); + + // Special deaths for medals + if(attacker) { + switch(death_message) { + case WEAPON_SHOTGUN_MURDER_SLAP: + if(!cvar("g_melee_only")) { // don't spam humiliation if we're in melee_only mode + GIVE_MEDAL(attacker, HUMILIATION); + } + break; + case WEAPON_ELECTRO_MURDER_COMBO: + GIVE_MEDAL(attacker, ELECTROBITCH); + break; + } + } } else { @@ -292,7 +307,7 @@ void Obituary(entity attacker, entity inflictor, entity targ, int deathtype, .en } } } - else if (!Obituary_WeaponDeath(targ, false, deathtype, targ.netname, deathlocation, "", CS(targ).killcount, 0)) + else if (!Obituary_WeaponDeath(targ, NULL, false, deathtype, targ.netname, deathlocation, "", CS(targ).killcount, 0)) { backtrace("SUICIDE: what the hell happened here?\n"); return; @@ -335,7 +350,7 @@ void Obituary(entity attacker, entity inflictor, entity targ, int deathtype, .en // these 2 macros are spread over multiple files #define SPREE_ITEM(counta,countb,center,normal,gentle) \ case counta: \ - Send_Notification(NOTIF_ONE, attacker, MSG_ANNCE, ANNCE_KILLSTREAK_##countb); \ + GIVE_MEDAL(attacker, KILLSTREAK_##countb); \ if (!warmup_stage) \ PlayerStats_GameReport_Event_Player(attacker, PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_##counta, 1); \ break; @@ -351,6 +366,7 @@ void Obituary(entity attacker, entity inflictor, entity targ, int deathtype, .en { checkrules_firstblood = true; notif_firstblood = true; // modify the current messages so that they too show firstblood information + GIVE_MEDAL(attacker, FIRSTBLOOD); PlayerStats_GameReport_Event_Player(attacker, PLAYERSTATS_ACHIEVEMENT_FIRSTBLOOD, 1); PlayerStats_GameReport_Event_Player(targ, PLAYERSTATS_ACHIEVEMENT_FIRSTVICTIM, 1); @@ -364,6 +380,12 @@ void Obituary(entity attacker, entity inflictor, entity targ, int deathtype, .en kill_count_to_target = 0; } + // Excellent check + if(attacker.lastkill && attacker.lastkill > time - autocvar_g_medals_excellent_time) { + GIVE_MEDAL(attacker, EXCELLENT); + } + attacker.lastkill = time; + if(targ.istypefrag) { Send_Notification( @@ -415,7 +437,7 @@ void Obituary(entity attacker, entity inflictor, entity targ, int deathtype, .en if(deathtype == DEATH_BUFF.m_id) f3 = buff_FirstFromFlags(attacker).m_id; - if (!Obituary_WeaponDeath(targ, true, deathtype, targ.netname, attacker_name, deathlocation, CS(targ).killcount, kill_count_to_attacker)) + if (!Obituary_WeaponDeath(targ, attacker, true, deathtype, targ.netname, attacker_name, deathlocation, CS(targ).killcount, kill_count_to_attacker)) Obituary_SpecialDeath(targ, true, deathtype, targ.netname, attacker_name, deathlocation, CS(targ).killcount, kill_count_to_attacker, f3); } } diff --git a/qcsrc/server/damage.qh b/qcsrc/server/damage.qh index a1dadc1a2..0f6055e5a 100644 --- a/qcsrc/server/damage.qh +++ b/qcsrc/server/damage.qh @@ -64,6 +64,7 @@ int impressive_hits; .float spawnshieldtime; .int totalfrags; +.float lastkill; .bool canteamdamage; @@ -97,6 +98,7 @@ void Obituary_SpecialDeath( float w_deathtype; float Obituary_WeaponDeath( entity notif_target, + entity attacker, float murder, int deathtype, string s1, string s2, string s3, diff --git a/qcsrc/server/weapons/tracing.qc b/qcsrc/server/weapons/tracing.qc index dbd3d5b27..2c45e84cd 100644 --- a/qcsrc/server/weapons/tracing.qc +++ b/qcsrc/server/weapons/tracing.qc @@ -335,7 +335,9 @@ void FireRailgunBullet (entity this, .entity weaponentity, vector start, vector IL_CLEAR(g_railgunhit); if(headshot) - Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_HEADSHOT); + { + GIVE_MEDAL(this, HEADSHOT); + } // calculate hits and fired shots for hitscan if(this.(weaponentity)) @@ -490,7 +492,9 @@ void fireBullet_antilag(entity this, .entity weaponentity, vector start, vector } if(headshot) - Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_HEADSHOT); + { + GIVE_MEDAL(this, HEADSHOT); + } if(lag) antilag_restore_all(this); -- 2.39.2