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;
}
}
+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;
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;
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);
void centerprint_SetDuelTitle(string left, string right);
void centerprint_SetTitle(string title);
void centerprint_ClearTitle();
+void centerprint_Medal(string icon, int times);
string hud_fontsize_str;
float max_namesize;
+int total_medals;
+
float sbt_bg_alpha;
float sbt_fg_alpha;
float sbt_fg_alpha_self;
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;
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)
{
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);
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");
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)
{
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;
}
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
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;
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)
// 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)
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)
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);
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 = "";
// 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)
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
case MSG_CENTER:
case MSG_MULTI:
case MSG_CHOICE:
+ case MSG_MEDAL:
break;
default:
LOG_INFOF(
}
}
+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
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)
{
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;
}
});
);
});
+ 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");
);
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
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)
{
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)
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
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;
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;
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];
}
}
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
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
}
}
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)
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));
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"
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;
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)
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;
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
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;
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;
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;
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;
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
}
bool autocvar_sv_teamnagger;
float autocvar_sv_player_scale;
+float autocvar_g_medals_excellent_time = 2;
+
// WEAPONTODO
.string weaponorder_byimpulse;
#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);
float Obituary_WeaponDeath(
entity notif_target,
+ entity attacker,
float murder,
int deathtype,
string s1, string s2, string s3,
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
{
}
}
}
- 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;
// 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;
{
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);
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(
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);
}
}
.float spawnshieldtime;
.int totalfrags;
+.float lastkill;
.bool canteamdamage;
float w_deathtype;
float Obituary_WeaponDeath(
entity notif_target,
+ entity attacker,
float murder,
int deathtype,
string s1, string s2, string s3,
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))
}
if(headshot)
- Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_HEADSHOT);
+ {
+ GIVE_MEDAL(this, HEADSHOT);
+ }
if(lag)
antilag_restore_all(this);