set g_ctf_flag_health 0
set g_ctf_flag_dropped_waypoint 2 "show dropped flag waypointsprite when a flag is lost. 1 = team only, 2 = for all players"
set g_ctf_flag_dropped_floatinwater 200 "move upwards while in water at this velocity"
-set g_ctf_flag_pickup_verbosename 0 "show the name of the person who picked up the flag too"
set g_ctf_throw 1 "throwing allows circumventing carrierkill score, so enable this with care!"
set g_ctf_throw_angle_max 90 "maximum upwards angle you can throw the flag"
set g_ctf_throw_angle_min -90 "minimum downwards angle you can throw the flag"
// needs to be done so early because of the constants they create
CALL_ACCUMULATED_FUNCTION(RegisterWeapons);
CALL_ACCUMULATED_FUNCTION(RegisterGametypes);
-
+ CALL_ACCUMULATED_FUNCTION(RegisterNotifications);
+ CALL_ACCUMULATED_FUNCTION(RegisterDeathtypes);
+
WaypointSprite_Load();
// precaches
cl_notice_read();
bHandled = true;
break;
+ case TE_CSQC_NOTIFICATION:
+ Read_Notification();
+ bHandled = true;
+ break;
default:
// No special logic for this temporary entity; return 0 so the engine can handle it
bHandled = false;
../common/util.qh
../common/items.qh
+../common/deathtypes.qh
../common/explosion_equation.qh
../common/mapinfo.qh
../common/command/markup.qh
../csqcmodellib/cl_player.qh
projectile.qh
+../common/notifications.qc
+
sortlist.qc
miscfunctions.qc
teamplay.qc
const float TE_CSQC_HAGAR_MAXROCKETS = 118;
const float TE_CSQC_VEHICLESETUP = 119;
const float TE_CSQC_SVNOTICE = 120;
+const float TE_CSQC_NOTIFICATION = 121;
const float RACE_NET_CHECKPOINT_HIT_QUALIFYING = 0; // byte checkpoint, short time, short recordtime, string recordholder
const float RACE_NET_CHECKPOINT_CLEAR = 1;
float SPECIES_ROBOT_SHINY = 5;
float SPECIES_RESERVED = 15;
-// Deathtypes (weapon deathtypes are the IT_* constants below)
-// NOTE: when adding death types, please add an explanation to Docs/spamlog.txt too.
-float DEATH_SPECIAL_START = 10000;
-float DEATH_FALL = 10000;
-float DEATH_TELEFRAG = 10001;
-float DEATH_DROWN = 10002;
-float DEATH_HURTTRIGGER = 10003;
-float DEATH_LAVA = 10004;
-float DEATH_SLIME = 10005;
-float DEATH_KILL = 10006;
-float DEATH_NOAMMO = 10007;
-float DEATH_SWAMP = 10008;
-float DEATH_TEAMCHANGE = 10009;
-float DEATH_AUTOTEAMCHANGE = 10010;
-float DEATH_CAMP = 10011;
-float DEATH_SHOOTING_STAR = 10012;
-float DEATH_ROT = 10013;
-float DEATH_MIRRORDAMAGE = 10014;
-float DEATH_TOUCHEXPLODE = 10015;
-float DEATH_CHEAT = 10016;
-float DEATH_FIRE = 10017;
-float DEATH_QUIET = 10021;
-
-float DEATH_VHFIRST = 10030;
-float DEATH_VHCRUSH = 10030;
-float DEATH_SBMINIGUN = 10031;
-float DEATH_SBROCKET = 10032;
-float DEATH_SBBLOWUP = 10033;
-float DEATH_WAKIGUN = 10034;
-float DEATH_WAKIROCKET = 10035;
-float DEATH_WAKIBLOWUP = 10036;
-float DEATH_RAPTOR_CANNON = 10037;
-float DEATH_RAPTOR_BOMB = 10038;
-float DEATH_RAPTOR_BOMB_SPLIT = 10039;
-float DEATH_RAPTOR_DEATH = 10040;
-float DEATH_BUMB_GUN = 10041;
-float DEATH_BUMB_RAY = 10042;
-float DEATH_BUMB_RAY_HEAL = 10043;
-float DEATH_BUMB_DEATH = 10044;
-float DEATH_VHLAST = 10044;
-#define DEATH_ISVEHICLE(t) ((t) >= DEATH_VHFIRST && (t) <= DEATH_VHLAST)
-
-float DEATH_GENERIC = 10050;
-
-float DEATH_WEAPON = 10100;
-
-float DEATH_CUSTOM = 10300;
-
-float DEATH_TURRET = 10500;
-float DEATH_TURRET_EWHEEL = 10501;
-float DEATH_TURRET_FLAC = 10502;
-float DEATH_TURRET_MACHINEGUN = 10503;
-float DEATH_TURRET_WALKER_GUN = 10504;
-float DEATH_TURRET_WALKER_MEELE = 10505;
-float DEATH_TURRET_WALKER_ROCKET = 10506;
-float DEATH_TURRET_HELLION = 10507;
-float DEATH_TURRET_HK = 10508;
-float DEATH_TURRET_MLRS = 10509;
-float DEATH_TURRET_PLASMA = 10510;
-float DEATH_TURRET_PHASER = 10511;
-float DEATH_TURRET_TESLA = 10512;
-float DEATH_TURRET_LAST = 10512;
-
-float DEATH_WEAPONMASK = 0xFF;
-float DEATH_HITTYPEMASK = 0x1F00; // which is WAY below 10000 used for normal deaths
-float HITTYPE_SECONDARY = 0x100;
-float HITTYPE_SPLASH = 0x200; // automatically set by RadiusDamage
-float HITTYPE_BOUNCE = 0x400;
-float HITTYPE_HEADSHOT = 0x800; // automatically set by Damage (if headshotbonus is set)
-float HITTYPE_RESERVED = 0x1000; // unused yet
-
-// macros to access these
-#define DEATH_ISTURRET(t) ((t) >= DEATH_TURRET && (t) <= DEATH_TURRET_LAST)
-#define DEATH_ISSPECIAL(t) ((t) >= DEATH_SPECIAL_START)
-#define DEATH_WEAPONOFWEAPONDEATH(t) ((t) & DEATH_WEAPONMASK)
-#define DEATH_ISWEAPON(t,w) (!DEATH_ISSPECIAL(t) && DEATH_WEAPONOFWEAPONDEATH(t) == (w))
-#define DEATH_WEAPONOF(t) (DEATH_ISSPECIAL(t) ? 0 : DEATH_WEAPONOFWEAPONDEATH(t))
-#define WEP_VALID(w) ((w) >= WEP_FIRST && (w) <= WEP_LAST)
-
#define FRAGS_PLAYER 0
#define FRAGS_SPECTATOR -666
#define FRAGS_LMS_LOSER -616
float CPID_KH_MSG = 10;
float CPID_PREVENT_JOIN = 11;
-// CSQC centerprint/notify message types
-float MSG_SUICIDE = 0;
-float MSG_KILL = 1;
-float MSG_SPREE = 2;
-float MSG_KILL_ACTION = 3;
-float MSG_KILL_ACTION_SPREE = 4;
-float MSG_INFO = 5;
-float MSG_KA = 6;
-float MSG_RACE = 10;
-
-float KILL_TEAM_RED = 12001;
-float KILL_TEAM_BLUE = 12002;
-float KILL_TEAM_SPREE = 12003;
-float KILL_FIRST_BLOOD = 12004;
-float KILL_FIRST_VICTIM = 12005;
-float KILL_TYPEFRAG = 12006;
-float KILL_TYPEFRAGGED = 12007;
-float KILL_FRAG = 12008;
-float KILL_FRAGGED = 12009;
-float KILL_SPREE = 12010;
-float KILL_END_SPREE = 12011;
-float KILL_SPREE_3 = 12012;
-float KILL_SPREE_5 = 12013;
-float KILL_SPREE_10 = 12014;
-float KILL_SPREE_15 = 12015;
-float KILL_SPREE_20 = 12016;
-float KILL_SPREE_25 = 12017;
-float KILL_SPREE_30 = 12018;
-
-float INFO_GOTFLAG = 13001;
-float INFO_PICKUPFLAG = 13002;
-float INFO_LOSTFLAG = 13003;
-float INFO_RETURNFLAG = 13004;
-float INFO_CAPTUREFLAG = 13005;
-
-float KA_PICKUPBALL = 14001;
-float KA_DROPBALL = 14002;
-
-float RACE_SERVER_RECORD = 15001;
-float RACE_NEW_TIME = 15002;
-float RACE_NEW_RANK = 15003;
-float RACE_FAIL = 15004;
-
// weapon requests
float WR_SETUP = 1; // (SVQC) setup weapon data
float WR_THINK = 2; // (SVQC) logic to run every frame
--- /dev/null
+// Deathtypes (weapon deathtypes are the IT_* constants below)
+// NOTE: when adding death types, please add an explanation to Docs/spamlog.txt too.
+#define DT_FIRST 10000
+#define DT_MAX 1024 // limit of recursive functions with ACCUMULATE_FUNCTION
+float DT_COUNT;
+
+#define DT_MATCH(a,b) if(min(DT_MAX, a) == b)
+
+#define DEATHTYPE(name,msg_info,msg_center,position) \
+ float name; \
+ float position; \
+ void RegisterDeathtype_##name() \
+ { \
+ SET_FIRST_OR_LAST(position, DT_FIRST, DT_COUNT) \
+ SET_FIELD_COUNT(name, DT_FIRST, DT_COUNT) \
+ CHECK_MAX_COUNT(name, DT_MAX, DT_COUNT, "deathtypes") \
+ } \
+ ACCUMULATE_FUNCTION(RegisterDeathtypes, RegisterDeathtype_##name)
+
+#define DEATHTYPES \
+ DEATHTYPE(DEATH_FALL, FALSE, FALSE, DEATH_SPECIAL_START) \
+ DEATHTYPE(DEATH_TELEFRAG, FALSE, FALSE, NORMAL_POS) \
+ DEATHTYPE(DEATH_DROWN, FALSE, FALSE, NORMAL_POS) \
+ DEATHTYPE(DEATH_HURTTRIGGER, FALSE, FALSE, NORMAL_POS) \
+ DEATHTYPE(DEATH_LAVA, FALSE, FALSE, NORMAL_POS) \
+ DEATHTYPE(DEATH_SLIME, FALSE, FALSE, NORMAL_POS) \
+ DEATHTYPE(DEATH_KILL, FALSE, FALSE, NORMAL_POS) \
+ DEATHTYPE(DEATH_NOAMMO, FALSE, FALSE, NORMAL_POS) \
+ DEATHTYPE(DEATH_SWAMP, FALSE, FALSE, NORMAL_POS) \
+ DEATHTYPE(DEATH_TEAMCHANGE, FALSE, FALSE, NORMAL_POS) \
+ DEATHTYPE(DEATH_AUTOTEAMCHANGE, FALSE, FALSE, NORMAL_POS) \
+ DEATHTYPE(DEATH_CAMP, FALSE, FALSE, NORMAL_POS) \
+ DEATHTYPE(DEATH_SHOOTING_STAR, FALSE, FALSE, NORMAL_POS) \
+ DEATHTYPE(DEATH_ROT, FALSE, FALSE, NORMAL_POS) \
+ DEATHTYPE(DEATH_MIRRORDAMAGE, FALSE, FALSE, NORMAL_POS) \
+ DEATHTYPE(DEATH_TOUCHEXPLODE, FALSE, FALSE, NORMAL_POS) \
+ DEATHTYPE(DEATH_CHEAT, FALSE, FALSE, NORMAL_POS) \
+ DEATHTYPE(DEATH_FIRE, FALSE, FALSE, NORMAL_POS) \
+ DEATHTYPE(DEATH_QUIET, FALSE, FALSE, NORMAL_POS) \
+ DEATHTYPE(DEATH_VHCRUSH, FALSE, FALSE, DEATH_VHFIRST) \
+ DEATHTYPE(DEATH_SBMINIGUN, FALSE, FALSE, NORMAL_POS) \
+ DEATHTYPE(DEATH_SBROCKET, FALSE, FALSE, NORMAL_POS) \
+ DEATHTYPE(DEATH_SBBLOWUP, FALSE, FALSE, NORMAL_POS) \
+ DEATHTYPE(DEATH_WAKIGUN, FALSE, FALSE, NORMAL_POS) \
+ DEATHTYPE(DEATH_WAKIROCKET, FALSE, FALSE, NORMAL_POS) \
+ DEATHTYPE(DEATH_WAKIBLOWUP, FALSE, FALSE, NORMAL_POS) \
+ DEATHTYPE(DEATH_RAPTOR_CANNON, FALSE, FALSE, NORMAL_POS) \
+ DEATHTYPE(DEATH_RAPTOR_BOMB, FALSE, FALSE, NORMAL_POS) \
+ DEATHTYPE(DEATH_RAPTOR_BOMB_SPLIT, FALSE, FALSE, NORMAL_POS) \
+ DEATHTYPE(DEATH_RAPTOR_DEATH, FALSE, FALSE, NORMAL_POS) \
+ DEATHTYPE(DEATH_BUMB_GUN, FALSE, FALSE, NORMAL_POS) \
+ DEATHTYPE(DEATH_BUMB_RAY, FALSE, FALSE, NORMAL_POS) \
+ DEATHTYPE(DEATH_BUMB_RAY_HEAL, FALSE, FALSE, NORMAL_POS) \
+ DEATHTYPE(DEATH_BUMB_DEATH, FALSE, FALSE, DEATH_VHLAST) \
+ DEATHTYPE(DEATH_TURRET, FALSE, FALSE, DEATH_TURRET_FIRST) \
+ DEATHTYPE(DEATH_TURRET_EWHEEL, FALSE, FALSE, NORMAL_POS) \
+ DEATHTYPE(DEATH_TURRET_FLAC, FALSE, FALSE, NORMAL_POS) \
+ DEATHTYPE(DEATH_TURRET_MACHINEGUN, FALSE, FALSE, NORMAL_POS) \
+ DEATHTYPE(DEATH_TURRET_WALKER_GUN, FALSE, FALSE, NORMAL_POS) \
+ DEATHTYPE(DEATH_TURRET_WALKER_MEELE, FALSE, FALSE, NORMAL_POS) \
+ DEATHTYPE(DEATH_TURRET_WALKER_ROCKET, FALSE, FALSE, NORMAL_POS) \
+ DEATHTYPE(DEATH_TURRET_HELLION, FALSE, FALSE, NORMAL_POS) \
+ DEATHTYPE(DEATH_TURRET_HK, FALSE, FALSE, NORMAL_POS) \
+ DEATHTYPE(DEATH_TURRET_MLRS, FALSE, FALSE, NORMAL_POS) \
+ DEATHTYPE(DEATH_TURRET_PLASMA, FALSE, FALSE, NORMAL_POS) \
+ DEATHTYPE(DEATH_TURRET_PHASER, FALSE, FALSE, NORMAL_POS) \
+ DEATHTYPE(DEATH_TURRET_TESLA, FALSE, FALSE, DEATH_TURRET_LAST) \
+ DEATHTYPE(DEATH_GENERIC, FALSE, FALSE, NORMAL_POS) \
+ DEATHTYPE(DEATH_WEAPON, FALSE, FALSE, NORMAL_POS) \
+ DEATHTYPE(DEATH_CUSTOM, FALSE, FALSE, NORMAL_POS) \
+ #undef DEATHTYPE
+
+DEATHTYPES
+
+#define DEATH_ISSPECIAL(t) ((t) >= DEATH_SPECIAL_START)
+#define DEATH_ISVEHICLE(t) ((t) >= DEATH_VHFIRST && (t) <= DEATH_VHLAST)
+#define DEATH_ISTURRET(t) ((t) >= DEATH_TURRET_FIRST && (t) <= DEATH_TURRET_LAST)
+#define DEATH_WEAPONOFWEAPONDEATH(t) ((t) & DEATH_WEAPONMASK)
+#define DEATH_ISWEAPON(t,w) (!DEATH_ISSPECIAL(t) && DEATH_WEAPONOFWEAPONDEATH(t) == (w))
+#define DEATH_WEAPONOF(t) (DEATH_ISSPECIAL(t) ? 0 : DEATH_WEAPONOFWEAPONDEATH(t))
+#define WEP_VALID(w) ((w) >= WEP_FIRST && (w) <= WEP_LAST)
+
+float DEATH_WEAPONMASK = 0xFF;
+float DEATH_HITTYPEMASK = 0x1F00; // which is WAY below 10000 used for normal deaths
+float HITTYPE_SECONDARY = 0x100;
+float HITTYPE_SPLASH = 0x200; // automatically set by RadiusDamage
+float HITTYPE_BOUNCE = 0x400;
+float HITTYPE_HEADSHOT = 0x800; // automatically set by Damage (if headshotbonus is set)
+float HITTYPE_RESERVED = 0x1000; // unused yet
+
+// macros to access these
+
+// CSQC centerprint/notify message types
+float MSG_SUICIDE = 0;
+float MSG_KILL = 1;
+float MSG_SPREE = 2;
+float MSG_KILL_ACTION = 3;
+float MSG_KILL_ACTION_SPREE = 4;
+float MSG_INFO = 5;
+float MSG_KA = 6;
+float MSG_RACE = 10;
+
+float KILL_TEAM_RED = 12001;
+float KILL_TEAM_BLUE = 12002;
+float KILL_TEAM_SPREE = 12003;
+float KILL_FIRST_BLOOD = 12004;
+float KILL_FIRST_VICTIM = 12005;
+float KILL_TYPEFRAG = 12006;
+float KILL_TYPEFRAGGED = 12007;
+float KILL_FRAG = 12008;
+float KILL_FRAGGED = 12009;
+float KILL_SPREE = 12010;
+float KILL_END_SPREE = 12011;
+float KILL_SPREE_3 = 12012;
+float KILL_SPREE_5 = 12013;
+float KILL_SPREE_10 = 12014;
+float KILL_SPREE_15 = 12015;
+float KILL_SPREE_20 = 12016;
+float KILL_SPREE_25 = 12017;
+float KILL_SPREE_30 = 12018;
+
+
+
+// this shit has got to go
+float INFO_GOTFLAG = 13001;
+float INFO_PICKUPFLAG = 13002;
+float INFO_LOSTFLAG = 13003;
+float INFO_RETURNFLAG = 13004;
+float INFO_CAPTUREFLAG = 13005;
+
+float KA_PICKUPBALL = 14001;
+float KA_DROPBALL = 14002;
+
+float RACE_SERVER_RECORD = 15001;
+float RACE_NEW_TIME = 15002;
+float RACE_NEW_RANK = 15003;
+float RACE_FAIL = 15004;
+
+ /*print("Obituary_Notification(): ", ftos(deathtype), ".\n");
+ switch(deathtype)
+ {
+ // suicide
+ case DEATH_AUTOTEAMCHANGE:
+ case DEATH_CAMP:
+ case DEATH_NOAMMO:
+ case DEATH_ROT:
+ case DEATH_TEAMCHANGE:
+
+ case KILL_TEAM_SUICIDE_RED:
+ case KILL_TEAM_SUICIDE_BLUE:
+
+ // murder
+ case KILL_TEAM_FRAG_RED:
+ case KILL_TEAM_FRAG_BLUE:
+
+ case KILL_FIRST_BLOOD:
+ case KILL_FIRST_VICTIM:
+
+ case KILL_FRAG:
+ case KILL_TYPEFRAG:
+ case KILL_FRAGGED:
+ case KILL_TYPEFRAGGED:
+
+ // accident
+ case WATCH_STEP:
+
+ case DEATH_QUIET: break;
+
+ // ideally we should have ALL deathtypes listed here
+ default:
+ backtrace(strcat("Unhandled deathtype: ", ftos(deathtype), ". Please notify Samual!\n"));
+ break;
+ }*/
--- /dev/null
+// ================================================
+// Unified notification system, written by Samual
+// Last updated: September, 2012
+// ================================================
+
+// main types/groups of notifications
+#define MSG_INFO 1 // "Global" information messages (sent to console, and notify panel if it has an icon)
+#define MSG_CENTER 2 // "Personal" centerprint messages
+#define MSG_WEAPON 3 // "Personal" weapon messages (like "You got the Nex", sent to weapon notify panel)
+
+#define NO_STR_ARG ""
+#define NO_FL_ARG -12345
+
+#define F_NAME 1
+#define F_STRNUM 2
+#define F_FLNUM 3
+
+// allow sending of notifications to also pass through to spectators (specifically for centerprints)
+#ifdef SVQC
+#define WRITESPECTATABLE_MSG_ONE_VARNAME(varname,statement) entity varname; varname = msg_entity; FOR_EACH_REALCLIENT(msg_entity) if(msg_entity == varname || (msg_entity.classname == STR_SPECTATOR && msg_entity.enemy == varname)) statement msg_entity = varname
+#define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
+#define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
+#endif
+
+#define NOTIF_MATCH(a,b) if(min(NOTIF_MAX, a) == b)
+#ifdef CSQC
+string got_commandkey;
+#define ADD_CSQC_AUTOCVAR(name) var float autocvar_notification_##name = TRUE;
+var float autocvar_notification_ctf_capture_verbose = TRUE;
+var float autocvar_notification_ctf_pickup_team_verbose = TRUE;
+var float autocvar_notification_ctf_pickup_enemy_verbose = TRUE;
+#define CHECK_AUTOCVAR(name) if(autocvar_notification_##name)
+#define HANDLE_CPID(cpid) ((min(NOTIF_MAX, cpid) == NO_CPID) ? FALSE : cpid)
+#define PASS_KEY ((((got_commandkey = getcommandkey("pass", "+use")) != "pass") && !(strstrofs(got_commandkey, "not bound", 0) >= 0)) ? sprintf(CCR(_(" ^F1(Press %s)")), got_commandkey) : "")
+#else
+#define ADD_CSQC_AUTOCVAR(name)
+#endif
+
+string team_name_red = _("RED");
+string team_name_blue = _("BLUE");
+string team_color_red = _("^1");
+string team_color_blue = _("^5");
+
+
+// ====================================
+// Notifications List and Information
+// ====================================
+/*
+ List of all notifications (including identifiers and display information)
+ Format: name, strnum, flnum, args, *icon/CPID, *durcnt, normal, gentle
+ Asterisked fields are not present in all notification types.
+ Specifications:
+ Name of notification
+ Number of STRING arguments (so that networking knows how many to send/receive)
+ Number of FLOAT arguments (so that networking knows how many to send/receive)
+ Arguments for sprintf(string, args), if no args needed then use ""
+ *Icon/CPID:
+ MSG_INFO: STRING: icon string name for the hud notify panel, "" if no icon is used
+ MSG_CENTER: FLOAT: centerprint ID number (CPID_*), NO_CPID if no CPID is needed
+ *Duration/Countdown:
+ MSG_CENTER: XPND2(FLOAT, FLOAT): extra arguments for centerprint messages
+ Normal message (string for sprintf when gentle messages are NOT enabled)
+ Gentle message (string for sprintf when gentle messages ARE enabled)
+
+ Messages have ^F1, ^F2, and ^BG in them-- these are replaced
+ with colors according to the cvars the user has chosen.
+ ^F1 = highest priority, "primary"
+ ^F2 = next highest priority, "secondary"
+ ^BG = normal/less important priority, "tertiary"
+
+ Guidlines (please try and follow these):
+ -ALWAYS start the string with a color, preferably background.
+ -ALWAYS reset a color after a name (this way they don't set it for the whole string).
+ -NEVER re-declare an event twice.
+ -NEVER add or remove fields from the format, it SHOULD already work.
+ -MSG_INFO messages must ALWAYS end with a new line: \n
+ -Be clean and simple with your notification naming,
+ nothing too long for the name field... Abbreviations are your friend. :D
+ -Keep the spacing as clean as possible... if the arguments are abnormally long,
+ it's okay to go out of line a bit... but try and keep it clean still.
+ -Keep the notifications in alphabetical order.
+ ARIRE unir frk jvgu lbhe bja zbgure. (gvc sbe zvxrrhfn) -- Don't pay attention to this ^_^
+*/
+
+// flag.netname = ((teamnumber) ? "^1RED^7 flag" : "^4BLUE^7 flag");
+// weaponorder[f1].netname
+
+#define TWO_TEAMS_INFO(prefix,strnum,flnum,args,icon,normal,gentle) \
+ MSG_INFO_NOTIF(prefix##RED, strnum, flnum, args, sprintf(icon, "red"), TEAM_CCR(normal, team_color_red, team_name_red), TEAM_CCR(gentle, team_color_red, team_name_red)) \
+ MSG_INFO_NOTIF(prefix##BLUE, strnum, flnum, args, sprintf(icon, "blue"), TEAM_CCR(normal, team_color_blue, team_name_blue), TEAM_CCR(gentle, team_color_blue, team_name_blue))
+#define MSG_INFO_NOTIFICATIONS \
+ MSG_INFO_NOTIF(INFO_EMPTY, 0, 0, NO_STR_ARG, "", "", "") \
+ TWO_TEAMS_INFO(INFO_CTF_FLAGRETURN_DROPPED_, 0, 0, NO_STR_ARG, "", _("^BGThe ^TC^TT^BG flag was dropped in the base and returned itself\n"), "") \
+ TWO_TEAMS_INFO(INFO_CTF_FLAGRETURN_DAMAGED_, 0, 0, NO_STR_ARG, "", _("^BGThe ^TC^TT^BG flag was destroyed and returned to base\n"), "") \
+ TWO_TEAMS_INFO(INFO_CTF_FLAGRETURN_SPEEDRUN_, 0, 1, f1/100, "", _("^BGThe ^TC^TT^BG flag became impatient after ^F1%.2f^BG seconds and returned itself\n"), "") \
+ TWO_TEAMS_INFO(INFO_CTF_FLAGRETURN_NEEDKILL_, 0, 0, NO_STR_ARG, "", _("^BGThe ^TC^TT^BG flag fell somewhere it couldn't be reached and returned to base\n"), "") \
+ TWO_TEAMS_INFO(INFO_CTF_FLAGRETURN_ABORTRUN_, 0, 0, NO_STR_ARG, "", _("^BGThe ^TC^TT^BG flag was returned to base by its owner\n"), "") \
+ TWO_TEAMS_INFO(INFO_CTF_FLAGRETURN_TIMEOUT_, 0, 0, NO_STR_ARG, "", _("^BGThe ^TC^TT^BG flag has returned to the base\n"), "") \
+ TWO_TEAMS_INFO(INFO_CTF_PICKUP_, 1, 0, s1, "notify_%s_taken", _("^BG%s^BG got the ^TC^TT^BG flag\n"), "") \
+ TWO_TEAMS_INFO(INFO_CTF_RETURN_, 1, 0, s1, "notify_%s_returned", _("^BG%s^BG returned the ^TC^TT^BG flag\n"), "") \
+ TWO_TEAMS_INFO(INFO_CTF_LOST_, 1, 0, s1, "notify_%s_lost", _("^BG%s^BG lost the ^TC^TT^BG flag\n"), "") \
+ TWO_TEAMS_INFO(INFO_CTF_CAPTURE_, 1, 0, s1, "notify_%s_capture", _("^BG%s^BG captured the ^TC^TT^BG flag\n"), "") \
+ TWO_TEAMS_INFO(INFO_CTF_CAPTURE_TIME_, 1, 1, XPND2(s1, f1/100), "notify_%s_capture", _("^BG%s^BG captured the ^TC^TT^BG flag in ^F1%.2f^BG seconds\n"), "") \
+ TWO_TEAMS_INFO(INFO_CTF_CAPTURE_BROKEN_, 2, 2, XPND4(s1, f1/100, s2, f2/100), "notify_%s_capture", _("^BG%s^BG captured the ^TC^TT^BG flag in ^F1%.2f^BG seconds, breaking ^BG%s^BG's previous record of ^F2%.2f^BG seconds\n"), "") \
+ TWO_TEAMS_INFO(INFO_CTF_CAPTURE_UNBROKEN_, 2, 2, XPND4(s1, f1/100, s2, f2/100), "notify_%s_capture", _("^BG%s^BG captured the ^TC^TT^BG flag in ^F2%.2f^BG seconds, failing to break ^BG%s^BG's previous record of ^F1%.2f^BG seconds\n"), "") \
+ #undef MSG_INFO_NOTIF
+
+#define TWO_TEAMS_CENTER(prefix,strnum,flnum,args,cpid,durcnt,normal,gentle) \
+ MSG_CENTER_NOTIF(prefix##RED, strnum, flnum, args, cpid, durcnt, TEAM_CCR(normal, team_color_red, team_name_red), TEAM_CCR(gentle, team_color_red, team_name_red)) \
+ MSG_CENTER_NOTIF(prefix##BLUE, strnum, flnum, args, cpid, durcnt, TEAM_CCR(normal, team_color_blue, team_name_blue), TEAM_CCR(gentle, team_color_blue, team_name_blue))
+#define MSG_CENTER_NOTIFICATIONS \
+ MSG_CENTER_NOTIF(CENTER_EMPTY, 0, 0, NO_STR_ARG, NO_CPID, XPND2(0, 0), "", "") \
+ MSG_CENTER_NOTIF(CENTER_CTF_CAPTURESHIELD_SHIELDED, 0, 0, NO_STR_ARG, CPID_CTF_CAPSHIELD, XPND2(0, 0), _("^BGYou are now ^F1shielded^BG from the flag\n^BGfor ^F2too many unsuccessful attempts^BG to capture.\n^BGMake some defensive scores before trying again."), "") \
+ MSG_CENTER_NOTIF(CENTER_CTF_CAPTURESHIELD_FREE, 0, 0, NO_STR_ARG, CPID_CTF_CAPSHIELD, XPND2(0, 0), _("^BGYou are now free.\n^BGFeel free to ^F2try to capture^BG the flag again\n^BGif you think you will succeed."), "") \
+ TWO_TEAMS_CENTER(CENTER_CTF_PASS_OTHER_, 2, 0, XPND2(s1, s2), CPID_CTF_PASS, XPND2(0, 0), _("^BG%s^BG passed the ^TC^TT^BG flag to %s"), "") \
+ TWO_TEAMS_CENTER(CENTER_CTF_PASS_SENT_, 1, 0, s1, CPID_CTF_PASS, XPND2(0, 0), _("^BGYou passed the ^TC^TT^BG flag to %s"), "") \
+ TWO_TEAMS_CENTER(CENTER_CTF_PASS_RECEIVED_, 1, 0, s1, CPID_CTF_PASS, XPND2(0, 0), _("^BGYou received the ^TC^TT^BG flag from %s"), "") \
+ MSG_CENTER_NOTIF(CENTER_CTF_PASS_REQUESTING, 1, 0, s1, CPID_CTF_PASS, XPND2(0, 0), _("^BGRequesting %s^BG to pass you the flag"), "") \
+ MSG_CENTER_NOTIF(CENTER_CTF_PASS_REQUESTED, 1, 0, XPND2(s1, PASS_KEY), CPID_CTF_PASS, XPND2(0, 0), _("^BG%s^BG requests you to pass the flag%s"), "") \
+ TWO_TEAMS_CENTER(CENTER_CTF_RETURN_, 0, 0, NO_STR_ARG, CPID_CTF_LOWPRIO, XPND2(0, 0), _("^BGYou returned the ^TC^TT^BG flag!"), "") \
+ TWO_TEAMS_CENTER(CENTER_CTF_CAPTURE_, 0, 0, NO_STR_ARG, CPID_CTF_LOWPRIO, XPND2(0, 0), _("^BGYou captured the ^TC^TT^BG flag!"), "") \
+ TWO_TEAMS_CENTER(CENTER_CTF_PICKUP_, 0, 0, NO_STR_ARG, CPID_CTF_LOWPRIO, XPND2(0, 0), _("^BGYou got the ^TC^TT^BG flag!"), "") \
+ MSG_CENTER_NOTIF(CENTER_CTF_PICKUP_TEAM, 1, 0, s1, CPID_CTF_LOWPRIO, XPND2(0, 0), _("^BGYour %steam mate^BG got the flag! Protect them!"), "") \
+ MSG_CENTER_NOTIF(CENTER_CTF_PICKUP_TEAM_VERBOSE, 2, 0, XPND3(s1, s2, s1), CPID_CTF_LOWPRIO, XPND2(0, 0), _("^BGYour %steam mate (^BG%s%s)^BG got the flag! Protect them!"), "") \
+ MSG_CENTER_NOTIF(CENTER_CTF_PICKUP_ENEMY, 1, 0, s1, CPID_CTF_LOWPRIO, XPND2(0, 0), _("^BGThe %senemy^BG got your flag! Retrieve it!"), "") \
+ MSG_CENTER_NOTIF(CENTER_CTF_PICKUP_ENEMY_VERBOSE, 2, 0, XPND3(s1, s2, s1), CPID_CTF_LOWPRIO, XPND2(0, 0), _("^BGThe %senemy (^BG%s%s)^BG got your flag! Retrieve it!"), "") \
+ MSG_CENTER_NOTIF(CENTER_CTF_STALEMATE_CARRIER, 0, 0, NO_STR_ARG, CPID_STALEMATE, XPND2(0, 0), _("^BGStalemate! Enemies can now see you on radar!"), "") \
+ MSG_CENTER_NOTIF(CENTER_CTF_STALEMATE_OTHER, 0, 0, NO_STR_ARG, CPID_STALEMATE, XPND2(0, 0), _("^BGStalemate! Flag carriers can now be seen by enemies on radar!"), "") \
+ MSG_CENTER_NOTIF(CENTER_CTF_FLAG_THROW_PUNISH, 0, 1, f1, CPID_CTF_LOWPRIO, XPND2(0, 0), _("^BGToo many flag throws! Throwing disabled for %d seconds."), "") \
+ #undef MSG_CENTER_NOTIF
+
+#define MSG_WEAPON_NOTIFICATIONS \
+ MSG_WEAPON_NOTIF(DEATH_MARBLES_LOST3, 2, 1, XPND3(s1, s2, f1), _("^F1%s^BG lost their marbles against ^F1%s^BG using the ^F2%s^BG\n"), "") \
+ #undef MSG_WEAPON_NOTIF
+
+
+// ====================================
+// Initialization/Create Declarations
+// ====================================
+
+#define NOTIF_FIRST 1
+#define NOTIF_MAX 1024 // limit of recursive functions with ACCUMULATE_FUNCTION
+float NOTIF_INFO_COUNT;
+float NOTIF_CENTER_COUNT;
+float NOTIF_WEAPON_COUNT;
+float NOTIF_CPID_COUNT;
+
+#define MSG_INFO_NOTIF(name,strnum,flnum,args,icon,normal,gentle) \
+ ADD_CSQC_AUTOCVAR(name) \
+ float name; \
+ void RegisterNotification_##name() \
+ { \
+ SET_FIELD_COUNT(name, NOTIF_FIRST, NOTIF_INFO_COUNT) \
+ CHECK_MAX_COUNT(name, NOTIF_MAX, NOTIF_INFO_COUNT, "notifications") \
+ } \
+ ACCUMULATE_FUNCTION(RegisterNotifications, RegisterNotification_##name)
+
+#define MSG_CENTER_NOTIF(name,strnum,flnum,args,cpid,durcnt,normal,gentle) \
+ ADD_CSQC_AUTOCVAR(name) \
+ float name; \
+ float cpid; \
+ void RegisterNotification_##name() \
+ { \
+ SET_FIELD_COUNT(name, NOTIF_FIRST, NOTIF_CENTER_COUNT) \
+ SET_FIELD_COUNT(cpid, NOTIF_FIRST, NOTIF_CPID_COUNT) \
+ CHECK_MAX_COUNT(name, NOTIF_MAX, NOTIF_CENTER_COUNT, "notifications") \
+ } \
+ ACCUMULATE_FUNCTION(RegisterNotifications, RegisterNotification_##name)
+
+#define MSG_WEAPON_NOTIF(name,strnum,flnum,args,normal,gentle) \
+ ADD_CSQC_AUTOCVAR(name) \
+ float name; \
+ void RegisterNotification_##name() \
+ { \
+ SET_FIELD_COUNT(name, NOTIF_FIRST, NOTIF_WEAPON_COUNT) \
+ CHECK_MAX_COUNT(name, NOTIF_MAX, NOTIF_WEAPON_COUNT, "notifications") \
+ } \
+ ACCUMULATE_FUNCTION(RegisterNotifications, RegisterNotification_##name)
+
+// NOW we actually activate the declarations
+MSG_INFO_NOTIFICATIONS
+MSG_CENTER_NOTIFICATIONS
+MSG_WEAPON_NOTIFICATIONS
+
+
+// ======================
+// Supporting Functions
+// ======================
+
+// select between the normal or the gentle message string based on client (or server) settings
+string normal_or_gentle(string normal, string gentle)
+{
+ #ifdef CSQC
+ if(autocvar_cl_gentle || autocvar_cl_gentle_messages)
+ #else
+ if(autocvar_sv_gentle)
+ #endif
+ return ((gentle != "") ? gentle : normal);
+ else
+ return normal;
+}
+
+float notif_stringcount(string s1, string s2)
+{
+ float stringcount;
+ if(s1 != NO_STR_ARG) ++stringcount;
+ if(s2 != NO_STR_ARG) ++stringcount;
+ return stringcount;
+}
+
+float notif_floatcount(float f1, float f2, float f3)
+{
+ float floatcount;
+ if(f1 != NO_FL_ARG) ++floatcount;
+ if(f2 != NO_FL_ARG) ++floatcount;
+ if(f3 != NO_FL_ARG) ++floatcount;
+ return floatcount;
+}
+
+// get the actual name of a notification and return it as a string
+string Get_Field_Value(float field, float net_type, float net_name)
+{
+ string output;
+
+ #define GET_FIELD_VALUE_OUTPUT(field,name,strnum,flnum) \
+ if(field == F_NAME) { output = VAR_TO_TEXT(name); } \
+ else if(field == F_STRNUM) { output = ftos(strnum); } \
+ else if(field == F_FLNUM) { output = ftos(flnum); }
+
+ switch(net_type)
+ {
+ case MSG_INFO:
+ {
+ #define MSG_INFO_NOTIF(name,strnum,flnum,args,icon,normal,gentle) \
+ { NOTIF_MATCH(name, net_name) { GET_FIELD_VALUE_OUTPUT(field,name,strnum,flnum) } }
+ MSG_INFO_NOTIFICATIONS
+ break;
+ }
+ case MSG_CENTER:
+ {
+ #define MSG_CENTER_NOTIF(name,strnum,flnum,args,cpid,durcnt,normal,gentle) \
+ { NOTIF_MATCH(name, net_name) { GET_FIELD_VALUE_OUTPUT(field,name,strnum,flnum) } }
+ MSG_CENTER_NOTIFICATIONS
+ break;
+ }
+ case MSG_WEAPON:
+ {
+ #define MSG_WEAPON_NOTIF(name,strnum,flnum,args,normal,gentle) \
+ { NOTIF_MATCH(name, net_name) { GET_FIELD_VALUE_OUTPUT(field,name,strnum,flnum) } }
+ MSG_WEAPON_NOTIFICATIONS
+ break;
+ }
+ }
+
+ #undef GET_FIELD_VALUE_OUTPUT
+ return output;
+}
+
+string TEAM_CCR(string input, string teamcolor, string teamtext)
+{
+ input = strreplace("^TC", teamcolor, input);
+ input = strreplace("^TT", teamtext, input);
+
+ return input;
+}
+
+// color code replace, place inside of sprintf and parse the string
+string CCR(string input)
+{
+ input = strreplace("^F1", "^2", input); // autocvar_notification_colors_F1
+ input = strreplace("^F2", "^3", input); // autocvar_notification_colors_F2
+ input = strreplace("^K1", "^1", input); // autocvar_notification_colors_K1
+ input = strreplace("^K2", "^3", input); // autocvar_notification_colors_K2
+ input = strreplace("^BG", "^7", input); // autocvar_notification_colors_BG
+
+ input = strreplace("^N", "^7", input); // "none"-- reset to white
+
+ return input;
+}
+
+
+// ===============================
+// Frontend Notification Pushing
+// ===============================
+
+#ifdef CSQC
+void Local_Notification(float net_type, float net_name, string s1, string s2, float f1, float f2, float f3)
+{
+ switch(net_type)
+ {
+ case MSG_INFO:
+ {
+ #define MSG_INFO_NOTIF(name,strnum,flnum,args,icon,normal,gentle) \
+ { NOTIF_MATCH(name, net_name) CHECK_AUTOCVAR(name) { print(sprintf(CCR(normal_or_gentle(normal, gentle)), args)); } }
+ MSG_INFO_NOTIFICATIONS
+ break;
+ }
+ case MSG_CENTER:
+ {
+ #define MSG_CENTER_NOTIF(name,strnum,flnum,args,cpid,durcnt,normal,gentle) \
+ { NOTIF_MATCH(name, net_name) CHECK_AUTOCVAR(name) { centerprint_generic(HANDLE_CPID(cpid), sprintf(CCR(normal_or_gentle(normal, gentle)), args), durcnt); } }
+ MSG_CENTER_NOTIFICATIONS
+ break;
+ }
+ case MSG_WEAPON:
+ {
+ #define MSG_WEAPON_NOTIF(name,strnum,flnum,args,normal,gentle) \
+ { NOTIF_MATCH(name, net_name) CHECK_AUTOCVAR(name) { print("unhandled\n"); } }
+ MSG_WEAPON_NOTIFICATIONS
+ break;
+ }
+ }
+}
+#endif
+
+
+// =========================
+// Notification Networking
+// =========================
+
+#ifdef CSQC
+void Read_Notification(void)
+{
+ float net_type = ReadByte();
+ float net_name = ReadShort();
+
+ float stringcount = stof(Get_Field_Value(F_STRNUM, net_type, net_name));
+ float floatcount = stof(Get_Field_Value(F_FLNUM, net_type, net_name));
+
+ Local_Notification(net_type, net_name,
+ ((stringcount >= 1) ? ReadString() : ""),
+ ((stringcount == 2) ? ReadString() : ""),
+ ((floatcount >= 1) ? ReadLong() : 0),
+ ((floatcount >= 2) ? ReadLong() : 0),
+ ((floatcount == 3) ? ReadLong() : 0));
+}
+#endif
+
+#ifdef SVQC
+void Send_Notification(entity client, float net_type, float net_name, string s1, string s2, float f1, float f2, float f3)
+{
+ if(net_type && net_name)
+ {
+ print("notification: ", Get_Field_Value(F_NAME, net_type, net_name), ": ", ftos(net_name), ".\n");
+
+ float stringcount = stof(Get_Field_Value(F_STRNUM, net_type, net_name));
+ float floatcount = stof(Get_Field_Value(F_FLNUM, net_type, net_name));
+
+ if(notif_stringcount(s1, s2) > stringcount) { backtrace("Too many string arguments for notification!\n"); return; }
+ if(notif_floatcount(f1, f2, f3) > floatcount) { backtrace("Too many float arguments for notification!\n"); return; }
+
+ if(client && (clienttype(client) == CLIENTTYPE_REAL) && (client.flags & FL_CLIENT))
+ {
+ // personal/direct notification sent to ONE person and their spectators
+ msg_entity = client;
+ WRITESPECTATABLE_MSG_ONE({
+ WriteByte(MSG_ONE, SVC_TEMPENTITY);
+ WriteByte(MSG_ONE, TE_CSQC_NOTIFICATION);
+ WriteByte(MSG_ONE, net_type);
+ WriteShort(MSG_ONE, net_name);
+ if(stringcount >= 1) { WriteString(MSG_ONE, s1); }
+ if(stringcount == 2) { WriteString(MSG_ONE, s2); }
+ if(floatcount >= 1) { WriteLong(MSG_ONE, f1); }
+ if(floatcount >= 2) { WriteLong(MSG_ONE, f2); }
+ if(floatcount == 3) { WriteLong(MSG_ONE, f3); }
+ });
+ }
+ else
+ {
+ // global notification sent to EVERYONE
+ WriteByte(MSG_ALL, SVC_TEMPENTITY);
+ WriteByte(MSG_ALL, TE_CSQC_NOTIFICATION);
+ WriteByte(MSG_ALL, net_type);
+ WriteShort(MSG_ALL, net_name);
+ if(stringcount >= 1) { WriteString(MSG_ALL, s1); }
+ if(stringcount == 2) { WriteString(MSG_ALL, s2); }
+ if(floatcount >= 1) { WriteLong(MSG_ALL, f1); }
+ if(floatcount >= 2) { WriteLong(MSG_ALL, f2); }
+ if(floatcount == 3) { WriteLong(MSG_ALL, f3); }
+ }
+
+ if(!server_is_local && (net_type == MSG_INFO))
+ {
+ #define MSG_INFO_NOTIF(name,strnum,flnum,args,icon,normal,gentle) \
+ { NOTIF_MATCH(name, net_name) { print(sprintf(CCR(normal_or_gentle(normal, gentle)), args)); } }
+ MSG_INFO_NOTIFICATIONS
+ }
+ }
+ else { backtrace("Incorrect usage of Send_Notification!\n"); }
+}
+
+void Send_Notification_ToTeam(float targetteam, entity except, float net_type, float net_name, string s1, string s2, float f1, float f2, float f3)
+{
+ entity tmp_entity;
+ FOR_EACH_REALCLIENT(tmp_entity)
+ {
+ if(tmp_entity.classname == STR_PLAYER)
+ if(tmp_entity.team == targetteam)
+ if(tmp_entity != except)
+ {
+ Send_Notification(tmp_entity, net_type, net_name, s1, s2, f1, f2, f3);
+ }
+ }
+}
+
+// WARNING: use this ONLY if you need exceptions or want to exclude spectators, otherwise use Send_Notification(..., world, ...)
+void Send_Notification_ToAll(entity except, float spectators, float net_type, float net_name, string s1, string s2, float f1, float f2, float f3)
+{
+ entity tmp_entity;
+ FOR_EACH_REALCLIENT(tmp_entity)
+ {
+ if((tmp_entity.classname == STR_PLAYER) || spectators)
+ if(tmp_entity != except)
+ {
+ Send_Notification(tmp_entity, net_type, net_name, s1, s2, f1, f2, f3);
+ }
+ }
+}
+
+
+// =============================
+// LEGACY NOTIFICATION SYSTEMS
+// =============================
+
+void Send_KillNotification(string s1, string s2, string s3, float msg, float type)
+{
+ WriteByte(MSG_ALL, SVC_TEMPENTITY);
+ WriteByte(MSG_ALL, TE_CSQC_KILLNOTIFY);
+ WriteString(MSG_ALL, s1);
+ WriteString(MSG_ALL, s2);
+ WriteString(MSG_ALL, s3);
+ WriteShort(MSG_ALL, msg);
+ WriteByte(MSG_ALL, type);
+}
+
+// Function is used to send a generic centerprint whose content CSQC gets to decide (gentle version or not in the below cases)
+void Send_CSQC_KillCenterprint(entity e, string s1, string s2, float msg, float type)
+{
+ if (clienttype(e) == CLIENTTYPE_REAL)
+ {
+ msg_entity = e;
+ WRITESPECTATABLE_MSG_ONE({
+ WriteByte(MSG_ONE, SVC_TEMPENTITY);
+ WriteByte(MSG_ONE, TE_CSQC_KILLCENTERPRINT);
+ WriteString(MSG_ONE, s1);
+ WriteString(MSG_ONE, s2);
+ WriteShort(MSG_ONE, msg);
+ WriteByte(MSG_ONE, type);
+ });
+ }
+}
+
+void Send_CSQC_Centerprint_Generic(entity e, float id, string s, float duration, float countdown_num)
+{
+ if ((clienttype(e) == CLIENTTYPE_REAL) && (e.flags & FL_CLIENT))
+ {
+ msg_entity = e;
+ WRITESPECTATABLE_MSG_ONE({
+ WriteByte(MSG_ONE, SVC_TEMPENTITY);
+ WriteByte(MSG_ONE, TE_CSQC_CENTERPRINT_GENERIC);
+ WriteByte(MSG_ONE, id);
+ WriteString(MSG_ONE, s);
+ if (id != 0 && s != "")
+ {
+ WriteByte(MSG_ONE, duration);
+ WriteByte(MSG_ONE, countdown_num);
+ }
+ });
+ }
+}
+void Send_CSQC_Centerprint_Generic_Expire(entity e, float id)
+{
+ Send_CSQC_Centerprint_Generic(e, id, "", 1, 0);
+}
+#endif
// a dummy macro that prevents the "hanging ;" warning
#define ENDS_WITH_CURLY_BRACE
+// return the actual code name of a var as a string
+#define VAR_TO_TEXT(var) #var
+
#ifdef HAVE_YO_DAWG_CPP
// TODO make ascii art pic of xzibit
// YO DAWG!
ACCUMULATE_call(#func)
#endif
+// used for simplifying ACCUMULATE_FUNCTIONs
+#define SET_FIRST_OR_LAST(input,first,count) if(!input) { input = (first + count); }
+#define SET_FIELD_COUNT(field,first,count) if(!field) { field = (first + count); ++count; }
+#define CHECK_MAX_COUNT(name,max,count,type) if(count == max) { error(strcat("Maximum ", type, " hit: ", VAR_TO_TEXT(name), ": ", ftos(count), ".\n")); }
+
// this returns a tempstring containing a copy of s with additional \n newlines added, it also replaces \n in the text with a real newline
// NOTE: s IS allowed to be a tempstring
string wordwrap(string s, float l);
typedef entity(entity cur, entity near, entity pass) findNextEntityNearFunction_t;
typedef float(entity a, entity b, entity pass) isConnectedFunction_t;
void FindConnectedComponent(entity e, .entity fld, findNextEntityNearFunction_t nxt, isConnectedFunction_t iscon, entity pass);
+
+// expand multiple arguments into one argument
+#define XPND5(a,b,c,d,e) a, b, c, d, e
+#define XPND4(a,b,c,d) a, b, c, d
+#define XPND3(a,b,c) a, b, c
+#define XPND2(a,b) a, b
float autocvar_g_ctf_flag_dropped_floatinwater;
float autocvar_g_ctf_flag_glowtrails;
float autocvar_g_ctf_flag_health;
-float autocvar_g_ctf_flag_pickup_verbosename;
string autocvar_g_ctf_flag_red_model;
float autocvar_g_ctf_flag_red_skin;
float autocvar_g_ctf_flag_return_time;
float autocvar_sv_friction;
float autocvar_sv_friction_on_land;
float autocvar_sv_gameplayfix_q2airaccelerate;
+float autocvar_sv_gentle;
#define autocvar_sv_gravity cvar("sv_gravity")
string autocvar_sv_intermission_cdtrack;
string autocvar_sv_jumpspeedcap_max;
stuffcmd(e, "cl_cmd settemp cl_movecliptokeyboard 2\n");
if(autocvar_g_antilag == 3) // client side hitscan
stuffcmd(e, "cl_cmd settemp cl_prydoncursor_notrace 0\n");
- if(sv_gentle)
+ if(autocvar_sv_gentle)
stuffcmd(e, "cl_cmd settemp cl_gentle 1\n");
/*
* we no longer need to stuff this. Remove this comment block if you feel
self.flags = FL_CLIENT;
self.version_nagtime = time + 10 + random() * 10;
+ if(self.netaddress == "local")
+ {
+ print("^3server is local!\n");
+
+ if(server_is_local)
+ print("Multiple local clients???");
+ else
+ server_is_local = TRUE;
+ }
+
if(player_count<0)
{
dprint("BUG player count is lower than zero, this cannot happen!\n");
{
self.pain_finished = time + 0.5; //Supajoe
- if(sv_gentle < 1) {
+ if(autocvar_sv_gentle < 1) {
if(self.classname != "body") // pain anim is BORKED on our ZYMs, FIXME remove this once we have good models
{
if (!self.animstate_override)
if(valid_damage_for_weaponstats)
WeaponStats_LogKill(awep, abot, self.weapon, vbot);
- if(sv_gentle < 1) // TODO make a "gentle" version?
+ if(autocvar_sv_gentle < 1) // TODO make a "gentle" version?
if(sound_allowed(MSG_BROADCAST, attacker))
{
if(deathtype == DEATH_DROWN)
// set up to fade out later
SUB_SetFade (self, time + 6 + random (), 1);
- if(sv_gentle > 0 || autocvar_ekg) {
+ if(autocvar_sv_gentle > 0 || autocvar_ekg) {
// remove corpse
PlayerCorpseDamage (inflictor, attacker, autocvar_sv_gibhealth+1.0, deathtype, hitloc, force);
}
break;
if(!sv_taunt)
break;
- if(sv_gentle)
+ if(autocvar_sv_gentle)
break;
tauntrand = random();
msg_entity = self;
setanim(self, self.anim_taunt, FALSE, TRUE, TRUE);
if(!sv_taunt)
break;
- if(sv_gentle)
+ if(autocvar_sv_gentle)
break;
msg_entity = self;
if (msg_entity.cvar_cl_voice_directional >= 1)
break;
if(!sv_taunt)
break;
- if(sv_gentle)
+ if(autocvar_sv_gentle)
break;
tauntrand = random();
FOR_EACH_REALCLIENT(msg_entity)
setanim(self, self.anim_taunt, FALSE, TRUE, TRUE);
if(!sv_taunt)
break;
- if(sv_gentle)
+ if(autocvar_sv_gentle)
break;
FOR_EACH_REALCLIENT(msg_entity)
{
float g_jetpack;
float sv_clones;
-float sv_gentle;
float sv_foginterval;
entity activator;
float maxclients;
+float server_is_local; // innocent until proven guilty by ClientConnect() in cl_client.qc
+
// Fields
.void(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) event_damage;
GameLogEcho(s);
}
-void Send_KillNotification (string s1, string s2, string s3, float msg, float type)
+void Obituary_Notification(entity notif_target, string s1, string s2, string s3, float deathtype)
{
- WriteByte(MSG_ALL, SVC_TEMPENTITY);
- WriteByte(MSG_ALL, TE_CSQC_KILLNOTIFY);
- WriteString(MSG_ALL, s1);
- WriteString(MSG_ALL, s2);
- WriteString(MSG_ALL, s3);
- WriteShort(MSG_ALL, msg);
- WriteByte(MSG_ALL, type);
-}
-
-// Function is used to send a generic centerprint whose content CSQC gets to decide (gentle version or not in the below cases)
-void Send_CSQC_KillCenterprint(entity e, string s1, string s2, float msg, float type)
-{
- if (clienttype(e) == CLIENTTYPE_REAL)
+ /*if(deathtype)
{
- msg_entity = e;
- WRITESPECTATABLE_MSG_ONE({
- WriteByte(MSG_ONE, SVC_TEMPENTITY);
- WriteByte(MSG_ONE, TE_CSQC_KILLCENTERPRINT);
- WriteString(MSG_ONE, s1);
- WriteString(MSG_ONE, s2);
- WriteShort(MSG_ONE, msg);
- WriteByte(MSG_ONE, type);
- });
- }
+ #define DEATHTYPE(name,type,notification,first,last) \
+ { if((deathtype == max(0, name)) && max(0, type) && max(0, notification)) { Send_Notification(type, notif_target, notification, s1, s2, s3); return; } }
+
+ DEATHTYPES
+ backtrace("Unhandled deathtype. Please notify Samual!\n");
+ }*/
}
void Obituary (entity attacker, entity inflictor, entity targ, float deathtype)
string s, a, msg;
float w, type;
+ string s1, s2, s3;
+
if (targ.classname == "player")
{
s = targ.netname;
if (targ == attacker) // suicides
{
- if (deathtype == DEATH_TEAMCHANGE || deathtype == DEATH_AUTOTEAMCHANGE)
- msg = ColoredTeamName(targ.team); // TODO: check if needed?
- else
- msg = "";
- if(!g_cts) // no "killed your own dumb self" message in CTS
- Send_CSQC_KillCenterprint(targ, msg, "", deathtype, MSG_SUICIDE);
+ s1 = ((deathtype == DEATH_TEAMCHANGE || deathtype == DEATH_AUTOTEAMCHANGE) ? ColoredTeamName(targ.team) : "");
+
+ // no "killed your own dumb self" message in CTS
+ if(!g_cts) { Obituary_Notification(targ, s1, "", "", deathtype); }
if(deathtype != DEATH_TEAMCHANGE && deathtype != DEATH_QUIET)
{
GiveFrags(attacker, targ, -1, deathtype);
}
- if (targ.killcount > 2)
- msg = ftos(targ.killcount);
- else
- msg = "";
- if(teamplay && deathtype == DEATH_MIRRORDAMAGE)
- {
- if(attacker.team == COLOR_TEAM1)
- deathtype = KILL_TEAM_RED;
- else
- deathtype = KILL_TEAM_BLUE;
- }
+ s1 = targ.netname;
+ s2 = ((targ.killcount > 2) ? ftos(targ.killcount) : "");
+
+ //if(teamplay && deathtype == DEATH_MIRRORDAMAGE)
+ // { deathtype = ((attacker.team == COLOR_TEAM1) ? KILL_TEAM_SUICIDE_RED : KILL_TEAM_SUICIDE_BLUE); }
- Send_KillNotification(s, msg, ftos(w), deathtype, MSG_SUICIDE);
+ Obituary_Notification(world, s1, s2, "", deathtype);
+ //Send_KillNotification(s, s2, ftos(w), deathtype, MSG_SUICIDE);
}
else if (attacker.classname == "player")
{
if(!IsDifferentTeam(attacker, targ))
{
- if(attacker.team == COLOR_TEAM1)
- type = KILL_TEAM_RED;
- else
- type = KILL_TEAM_BLUE;
+ //type = ((attacker.team == COLOR_TEAM1) ? KILL_TEAM_FRAG_RED : KILL_TEAM_FRAG_BLUE);
GiveFrags(attacker, targ, -1, deathtype);
- Send_CSQC_KillCenterprint(attacker, s, "", type, MSG_KILL);
+ //Send_CSQC_KillCenterprint(attacker, s, "", type);
if (targ.killcount > 2)
msg = ftos(targ.killcount);
// needs to be done so early because of the constants they create
CALL_ACCUMULATED_FUNCTION(RegisterWeapons);
CALL_ACCUMULATED_FUNCTION(RegisterGametypes);
+ CALL_ACCUMULATED_FUNCTION(RegisterNotifications);
+ CALL_ACCUMULATED_FUNCTION(RegisterDeathtypes);
MapInfo_Enumerate();
MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0);
// needs to be done so early because of the constants they create
CALL_ACCUMULATED_FUNCTION(RegisterWeapons);
CALL_ACCUMULATED_FUNCTION(RegisterGametypes);
+ CALL_ACCUMULATED_FUNCTION(RegisterNotifications);
+ CALL_ACCUMULATED_FUNCTION(RegisterDeathtypes);
ServerProgsDB = db_load(strcat("server.db", autocvar_sessionid));
#endif
sv_clones = cvar("sv_clones");
- sv_gentle = cvar("sv_gentle");
sv_foginterval = cvar("sv_foginterval");
g_cloaked = cvar("g_cloaked");
if(g_cts)
#endif
}
-// sorry, but using \ in macros breaks line numbers
-#define WRITESPECTATABLE_MSG_ONE_VARNAME(varname,statement) entity varname; varname = msg_entity; FOR_EACH_REALCLIENT(msg_entity) if(msg_entity == varname || (msg_entity.classname == STR_SPECTATOR && msg_entity.enemy == varname)) statement msg_entity = varname
-#define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
-#define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
-
-
-void Send_CSQC_Centerprint_Generic(entity e, float id, string s, float duration, float countdown_num)
-{
- if ((clienttype(e) == CLIENTTYPE_REAL) && (e.flags & FL_CLIENT))
- {
- msg_entity = e;
- WRITESPECTATABLE_MSG_ONE({
- WriteByte(MSG_ONE, SVC_TEMPENTITY);
- WriteByte(MSG_ONE, TE_CSQC_CENTERPRINT_GENERIC);
- WriteByte(MSG_ONE, id);
- WriteString(MSG_ONE, s);
- if (id != 0 && s != "")
- {
- WriteByte(MSG_ONE, duration);
- WriteByte(MSG_ONE, countdown_num);
- }
- });
- }
-}
-void Send_CSQC_Centerprint_Generic_Expire(entity e, float id)
-{
- Send_CSQC_Centerprint_Generic(e, id, "", 1, 0);
-}
// WARNING: this kills the trace globals
#define EXACTTRIGGER_TOUCH if(WarpZoneLib_ExactTrigger_Touch()) return
#define EXACTTRIGGER_INIT WarpZoneLib_ExactTrigger_Init()
GameLogEcho(strcat(":ctf:", mode, ":", ftos(flagteam), ((actor != world) ? (strcat(":", ftos(actor.playerid))) : "")));
}
-string ctf_CaptureRecord(entity flag, entity player)
+void ctf_CaptureRecord(entity flag, entity player)
{
- float cap_time, cap_record, success;
- string cap_message, refername;
-
- if((autocvar_g_ctf_captimerecord_always) || (player_count - currentbots))
- {
- cap_record = ctf_captimerecord;
- cap_time = (time - flag.ctf_pickuptime);
-
- refername = db_get(ServerProgsDB, strcat(GetMapname(), "/captimerecord/netname"));
- refername = ((refername == player.netname) ? "their" : strcat(refername, "^7's"));
-
- if(!ctf_captimerecord)
- { cap_message = strcat(" in ", ftos_decimals(cap_time, 2), " seconds"); success = TRUE; }
- else if(cap_time < cap_record)
- { cap_message = strcat(" in ", ftos_decimals(cap_time, 2), " seconds, breaking ", refername, " previous record of ", ftos_decimals(cap_record, 2), " seconds"); success = TRUE; }
- else
- { cap_message = strcat(" in ", ftos_decimals(cap_time, 2), " seconds, failing to break ", refername, " record of ", ftos_decimals(cap_record, 2), " seconds"); success = FALSE; }
+ entity tmp_entity;
+ float notification, success;
+ float cap_record = ctf_captimerecord;
+ float cap_time = (time - flag.ctf_pickuptime);
+ float f1, f2 = NO_FL_ARG;
+ string s1, s2 = NO_STR_ARG;
+ string refername = db_get(ServerProgsDB, strcat(GetMapname(), "/captimerecord/netname"));
+
+ // figure shit out
+ if(!ctf_captimerecord)
+ { notification = RED_OR_BLUE(flag, INFO_CTF_CAPTURE_TIME_); s1 = player.netname; f1 = (cap_time * 100); success = TRUE; }
+ else if(cap_time < cap_record)
+ { notification = RED_OR_BLUE(flag, INFO_CTF_CAPTURE_BROKEN_); s1 = player.netname; s2 = refername; f1 = (cap_time * 100); f2 = (cap_record * 100); success = TRUE; }
+ else
+ { notification = RED_OR_BLUE(flag, INFO_CTF_CAPTURE_UNBROKEN_); s1 = player.netname; s2 = refername; f1 = (cap_time * 100); f2 = (cap_record * 100); success = FALSE; }
- if(success)
- {
- ctf_captimerecord = cap_time;
- db_put(ServerProgsDB, strcat(GetMapname(), "/captimerecord/time"), ftos(cap_time));
- db_put(ServerProgsDB, strcat(GetMapname(), "/captimerecord/netname"), player.netname);
- write_recordmarker(player, (time - cap_time), cap_time);
- }
+ // notify about shit
+ FOR_EACH_REALCLIENT(tmp_entity)
+ {
+ if not(tmp_entity.CAPTURE_VERBOSE) { notification = RED_OR_BLUE(flag, INFO_CTF_CAPTURE_); s2 = NO_STR_ARG; f1 = f2 = NO_FL_ARG; }
+ Send_Notification(tmp_entity, MSG_INFO, notification, s1, s2, f1, f2, NO_FL_ARG);
}
-
- return cap_message;
+
+ // write that shit in the database
+ if(success)
+ {
+ ctf_captimerecord = cap_time;
+ db_put(ServerProgsDB, strcat(GetMapname(), "/captimerecord/time"), ftos(cap_time));
+ db_put(ServerProgsDB, strcat(GetMapname(), "/captimerecord/netname"), player.netname);
+ write_recordmarker(player, (time - cap_time), cap_time);
+ }
}
void ctf_FlagcarrierWaypoints(entity player)
if((wanted_status == player.ctf_captureshielded) && (updated_status != wanted_status)) // 0: shield only, 1: unshield only
{
if(updated_status) // TODO csqc notifier for this // Samual: How?
- Send_CSQC_Centerprint_Generic(player, CPID_CTF_CAPTURESHIELD, "^3You are now ^4shielded^3 from the flag\n^3for ^1too many unsuccessful attempts^3 to capture.\n\n^3Make some defensive scores before trying again.", 5, 0);
+ Send_Notification(player, MSG_CENTER, CENTER_CTF_CAPTURESHIELD_SHIELDED, NO_STR_ARG, NO_STR_ARG, NO_FL_ARG, NO_FL_ARG, NO_FL_ARG);
else
- Send_CSQC_Centerprint_Generic(player, CPID_CTF_CAPTURESHIELD, "^3You are now free.\n\n^3Feel free to ^1try to capture^3 the flag again\n^3if you think you will succeed.", 5, 0);
+ Send_Notification(player, MSG_CENTER, CENTER_CTF_CAPTURESHIELD_FREE, NO_STR_ARG, NO_STR_ARG, NO_FL_ARG, NO_FL_ARG, NO_FL_ARG);
player.ctf_captureshielded = updated_status;
}
vector othermid = (other.absmin + other.absmax) * 0.5;
Damage(other, self, self, 0, DEATH_HURTTRIGGER, mymid, normalize(othermid - mymid) * ctf_captureshield_force);
- Send_CSQC_Centerprint_Generic(other, CPID_CTF_CAPTURESHIELD, "^3You are ^4shielded^3 from the flag\n^3for ^1too many unsuccessful attempts^3 to capture.\n\n^3Get some defensive scores before trying again.", 5, 0);
+ Send_Notification(other, MSG_CENTER, CENTER_CTF_CAPTURESHIELD_SHIELDED, NO_STR_ARG, NO_STR_ARG, NO_FL_ARG, NO_FL_ARG, NO_FL_ARG);
}
void ctf_CaptureShield_Spawn(entity flag)
flag.ctf_status = FLAG_DROPPED;
// messages and sounds
- Send_KillNotification(player.netname, flag.netname, "", INFO_LOSTFLAG, MSG_INFO);
+ Send_Notification(world, MSG_INFO, RED_OR_BLUE(flag, INFO_CTF_LOST_), player.netname, NO_STR_ARG, NO_FL_ARG, NO_FL_ARG, NO_FL_ARG);
sound(flag, CH_TRIGGER, flag.snd_flag_dropped, VOL_BASE, ATTN_NONE);
ctf_EventLog("dropped", player.team, player);
FOR_EACH_REALPLAYER(tmp_player)
{
if(tmp_player == sender)
- centerprint(tmp_player, strcat("You passed the ", flag.netname, " to ", player.netname));
+ Send_Notification(tmp_player, MSG_CENTER, RED_OR_BLUE(flag, CENTER_CTF_PASS_SENT_), player.netname, NO_STR_ARG, NO_FL_ARG, NO_FL_ARG, NO_FL_ARG);
else if(tmp_player == player)
- centerprint(tmp_player, strcat("You received the ", flag.netname, " from ", sender.netname));
+ Send_Notification(tmp_player, MSG_CENTER, RED_OR_BLUE(flag, CENTER_CTF_PASS_RECEIVED_), sender.netname, NO_STR_ARG, NO_FL_ARG, NO_FL_ARG, NO_FL_ARG);
else if(!IsDifferentTeam(tmp_player, sender))
- centerprint(tmp_player, strcat(sender.netname, " passed the ", flag.netname, " to ", player.netname));
+ Send_Notification(tmp_player, MSG_CENTER, RED_OR_BLUE(flag, CENTER_CTF_PASS_OTHER_), sender.netname, player.netname, NO_FL_ARG, NO_FL_ARG, NO_FL_ARG);
}
// create new waypoint
if not(player) { return; } // without someone to give the reward to, we can't possibly cap
// messages and sounds
- Send_KillNotification(player.netname, enemy_flag.netname, ctf_CaptureRecord(enemy_flag, player), INFO_CAPTUREFLAG, MSG_INFO);
+ ctf_CaptureRecord(enemy_flag, player);
sound(player, CH_TRIGGER, flag.snd_flag_capture, VOL_BASE, ATTN_NONE);
switch(capturetype)
void ctf_Handle_Return(entity flag, entity player)
{
// messages and sounds
- //centerprint(player, strcat("You returned the ", flag.netname));
- Send_KillNotification(player.netname, flag.netname, "", INFO_RETURNFLAG, MSG_INFO);
+ Send_Notification(player, MSG_CENTER, RED_OR_BLUE(flag, CENTER_CTF_RETURN_), NO_STR_ARG, NO_STR_ARG, NO_FL_ARG, NO_FL_ARG, NO_FL_ARG);
+ Send_Notification(world, MSG_INFO, RED_OR_BLUE(flag, INFO_CTF_RETURN_), player.netname, NO_STR_ARG, NO_FL_ARG, NO_FL_ARG, NO_FL_ARG);
sound(player, CH_TRIGGER, flag.snd_flag_returned, VOL_BASE, ATTN_NONE);
ctf_EventLog("return", flag.team, player);
{
// declarations
entity tmp_player; // temporary entity which the FOR_EACH_PLAYER loop uses to scan players
- string verbosename; // holds the name of the player OR no name at all for printing in the centerprints
float pickup_dropped_score; // used to calculate dropped pickup score
// attach the flag to the player
}
// messages and sounds
- Send_KillNotification (player.netname, flag.netname, "", INFO_GOTFLAG, MSG_INFO);
+ Send_Notification(world, MSG_INFO, RED_OR_BLUE(flag, INFO_CTF_PICKUP_), player.netname, NO_STR_ARG, NO_FL_ARG, NO_FL_ARG, NO_FL_ARG);
sound(player, CH_TRIGGER, flag.snd_flag_taken, VOL_BASE, ATTN_NONE);
- verbosename = ((autocvar_g_ctf_flag_pickup_verbosename) ? strcat(Team_ColorCode(player.team), "(^7", player.netname, Team_ColorCode(player.team), ") ") : "");
-
+
FOR_EACH_REALPLAYER(tmp_player)
{
if(tmp_player == player)
{
- centerprint(tmp_player, strcat("You got the ", flag.netname, "!"));
- //if(ctf_stalemate) { centerprint(tmp_player, "Stalemate! Enemies can see you on radar!"); }
+ Send_Notification(tmp_player, MSG_CENTER, RED_OR_BLUE(flag, CENTER_CTF_PICKUP_), NO_STR_ARG, NO_STR_ARG, NO_FL_ARG, NO_FL_ARG, NO_FL_ARG);
+ if(ctf_stalemate) { Send_Notification(player, MSG_CENTER, CENTER_CTF_STALEMATE_CARRIER, NO_STR_ARG, NO_STR_ARG, NO_FL_ARG, NO_FL_ARG, NO_FL_ARG); }
}
- //else if(!IsDifferentTeam(tmp_player, player))
- // centerprint(tmp_player, strcat("Your ", Team_ColorCode(player.team), "team mate ", verbosename, "^7got the flag! Protect them!"));
- else if(!IsDifferentTeam(tmp_player, flag))
- centerprint(tmp_player, strcat("The ", Team_ColorCode(player.team), "enemy ", verbosename, "^7got your flag! Retrieve it!"));
+ else if(!IsDifferentTeam(tmp_player, player) && tmp_player != player)
+ Send_Notification(tmp_player, MSG_CENTER, (tmp_player.PICKUP_TEAM_VERBOSE ? CENTER_CTF_PICKUP_TEAM_VERBOSE : CENTER_CTF_PICKUP_TEAM), Team_ColorCode(player.team), (tmp_player.PICKUP_TEAM_VERBOSE ? player.netname : NO_STR_ARG), NO_FL_ARG, NO_FL_ARG, NO_FL_ARG);
+ else if(IsDifferentTeam(tmp_player, player))
+ Send_Notification(tmp_player, MSG_CENTER, (tmp_player.PICKUP_ENEMY_VERBOSE ? CENTER_CTF_PICKUP_ENEMY_VERBOSE : CENTER_CTF_PICKUP_ENEMY), Team_ColorCode(player.team), (tmp_player.PICKUP_ENEMY_VERBOSE ? player.netname : NO_STR_ARG), NO_FL_ARG, NO_FL_ARG, NO_FL_ARG);
}
// scoring
{
switch(returntype)
{
- case RETURN_DROPPED: bprint("The ", flag.netname, " was dropped in the base and returned itself\n"); break;
- case RETURN_DAMAGE: bprint("The ", flag.netname, " was destroyed and returned to base\n"); break;
- case RETURN_SPEEDRUN: bprint("The ", flag.netname, " became impatient after ", ftos_decimals(ctf_captimerecord, 2), " seconds and returned itself\n"); break;
- case RETURN_NEEDKILL: bprint("The ", flag.netname, " fell somewhere it couldn't be reached and returned to base\n"); break;
+ case RETURN_DROPPED: Send_Notification(world, MSG_INFO, RED_OR_BLUE(flag, INFO_CTF_FLAGRETURN_DROPPED_), NO_STR_ARG, NO_STR_ARG, NO_FL_ARG, NO_FL_ARG, NO_FL_ARG); break;
+ case RETURN_DAMAGE: Send_Notification(world, MSG_INFO, RED_OR_BLUE(flag, INFO_CTF_FLAGRETURN_DAMAGED_), NO_STR_ARG, NO_STR_ARG, NO_FL_ARG, NO_FL_ARG, NO_FL_ARG); break;
+ case RETURN_SPEEDRUN: Send_Notification(world, MSG_INFO, RED_OR_BLUE(flag, INFO_CTF_FLAGRETURN_SPEEDRUN_), NO_STR_ARG, NO_STR_ARG, ctf_captimerecord, NO_FL_ARG, NO_FL_ARG); break;
+ case RETURN_NEEDKILL: Send_Notification(world, MSG_INFO, RED_OR_BLUE(flag, INFO_CTF_FLAGRETURN_NEEDKILL_), NO_STR_ARG, NO_STR_ARG, NO_FL_ARG, NO_FL_ARG, NO_FL_ARG); break;
default:
case RETURN_TIMEOUT:
- { bprint("The ", flag.netname, " has returned to base\n"); break; }
+ { Send_Notification(world, MSG_INFO, RED_OR_BLUE(flag, INFO_CTF_FLAGRETURN_TIMEOUT_), NO_STR_ARG, NO_STR_ARG, NO_FL_ARG, NO_FL_ARG, NO_FL_ARG); break; }
}
sound(flag, CH_TRIGGER, flag.snd_flag_respawn, VOL_BASE, ATTN_NONE);
ctf_EventLog("returned", flag.team, world);
{
FOR_EACH_REALPLAYER(tmp_entity)
if(tmp_entity.flagcarried)
- centerprint(tmp_entity, "Stalemate! Enemies can now see you on radar!");
+ Send_Notification(tmp_entity, MSG_CENTER, CENTER_CTF_STALEMATE_CARRIER, NO_STR_ARG, NO_STR_ARG, NO_FL_ARG, NO_FL_ARG, NO_FL_ARG);
else
- centerprint(tmp_entity, "Stalemate! Flag carriers can now be seen by enemies on radar!");
+ Send_Notification(tmp_entity, MSG_CENTER, CENTER_CTF_STALEMATE_OTHER, NO_STR_ARG, NO_STR_ARG, NO_FL_ARG, NO_FL_ARG, NO_FL_ARG);
wpforenemy_announced = TRUE;
}
setattachment(flag, world, "");
- flag.netname = ((teamnumber) ? "^1RED^7 flag" : "^4BLUE^7 flag");
+ flag.netname = ((teamnumber) ? "^1REPLACETHIS^7" : "^4REPLACETHIS^7"); // ((teamnumber) ? "^1RED^7 flag" : "^4BLUE^7 flag");
flag.team = ((teamnumber) ? COLOR_TEAM1 : COLOR_TEAM2); // COLOR_TEAM1: color 4 team (red) - COLOR_TEAM2: color 13 team (blue)
flag.items = ((teamnumber) ? IT_KEY2 : IT_KEY1); // IT_KEY2: gold key (redish enough) - IT_KEY1: silver key (bluish enough)
flag.classname = "item_flag_team";
{
if(clienttype(head) == CLIENTTYPE_BOT)
{
- centerprint(player, strcat("Requesting ", head.netname, " to pass you the ", head.flagcarried.netname));
+ Send_Notification(player, MSG_CENTER, CENTER_CTF_PASS_REQUESTING, head.netname, NO_STR_ARG, NO_FL_ARG, NO_FL_ARG, NO_FL_ARG);
ctf_Handle_Throw(head, player, DROP_PASS);
}
else
{
- centerprint(head, strcat(player.netname, " requests you to pass the ", head.flagcarried.netname));
- centerprint(player, strcat("Requesting ", head.netname, " to pass you the ", head.flagcarried.netname));
+ Send_Notification(head, MSG_CENTER, CENTER_CTF_PASS_REQUESTED, player.netname, NO_STR_ARG, NO_FL_ARG, NO_FL_ARG, NO_FL_ARG);
+ Send_Notification(player, MSG_CENTER, CENTER_CTF_PASS_REQUESTING, head.netname, NO_STR_ARG, NO_FL_ARG, NO_FL_ARG, NO_FL_ARG);
}
player.throw_antispam = time + autocvar_g_ctf_pass_wait;
return TRUE;
}
else
{
- centerprint(player, strcat("Too many flag throws, throwing disabled for ", ftos(rint((player.throw_prevtime + autocvar_g_ctf_throw_punish_delay) - time)), " seconds."));
+ Send_Notification(player, MSG_CENTER, CENTER_CTF_FLAG_THROW_PUNISH, NO_STR_ARG, NO_STR_ARG, rint((player.throw_prevtime + autocvar_g_ctf_throw_punish_delay) - time), NO_FL_ARG, NO_FL_ARG);
return FALSE;
}
}
{
if(self.flagcarried)
{
- bprint("The ", self.flagcarried.netname, " was returned to base by its carrier\n");
- ctf_RespawnFlag(self);
+ Send_Notification(world, MSG_INFO, RED_OR_BLUE(self.flagcarried, INFO_CTF_FLAGRETURN_ABORTRUN_), NO_STR_ARG, NO_STR_ARG, NO_FL_ARG, NO_FL_ARG, NO_FL_ARG);
+ ctf_RespawnFlag(self.flagcarried);
return TRUE;
}
return TRUE;
}
+MUTATOR_HOOKFUNCTION(ctf_GetCvars)
+{
+ GetCvars_handleFloat(get_cvars_s, get_cvars_f, CAPTURE_VERBOSE, "notification_ctf_capture_verbose");
+ GetCvars_handleFloat(get_cvars_s, get_cvars_f, PICKUP_TEAM_VERBOSE, "notification_ctf_pickup_team_verbose");
+ GetCvars_handleFloat(get_cvars_s, get_cvars_f, PICKUP_ENEMY_VERBOSE, "notification_ctf_pickup_enemy_verbose");
+ return TRUE;
+}
+
// ==========
// Spawnfuncs
MUTATOR_HOOK(VehicleExit, ctf_VehicleExit, CBC_ORDER_ANY);
MUTATOR_HOOK(AbortSpeedrun, ctf_AbortSpeedrun, CBC_ORDER_ANY);
MUTATOR_HOOK(HavocBot_ChooseRule, ctf_BotRoles, CBC_ORDER_ANY);
+ MUTATOR_HOOK(GetCvars, ctf_GetCvars, CBC_ORDER_ANY);
MUTATOR_ONADD
{
float havocbot_ctf_middlepoint_radius;
void havocbot_role_ctf_setrole(entity bot, float role);
+
+// client notification stuff
+#define RED_OR_BLUE(ent,prefix) ((ent.team == COLOR_TEAM1) ? prefix##RED : prefix##BLUE)
+.float CAPTURE_VERBOSE;
+.float PICKUP_TEAM_VERBOSE;
+.float PICKUP_ENEMY_VERBOSE;
../common/constants.qh
../common/util.qh
../common/items.qh
+../common/deathtypes.qh
../common/explosion_equation.qh
../common/urllib.qh
../common/command/markup.qh
miscfunctions.qc
+../common/notifications.qc
+
waypointsprites.qc
bot/bot.qc