-#define FOR_EACH_KH_KEY(v) for(v = kh_worldkeylist; v; v = v.kh_worldkeynext )
+#include "gamemode_keyhunt.qh"
+
+#include "gamemode.qh"
+
+int autocvar_g_keyhunt_point_leadlimit;
+bool autocvar_g_keyhunt_team_spawns;
+#define autocvar_g_keyhunt_point_limit cvar("g_keyhunt_point_limit")
+int autocvar_g_keyhunt_teams;
+int autocvar_g_keyhunt_teams_override;
// #define KH_PLAYER_USE_ATTACHMENT
// #define KH_PLAYER_USE_CARRIEDMODEL
.float kh_previous_owner_playerid;
.float kh_cp_duration;
-string kh_sound_capture = "kh/capture.wav";
-string kh_sound_destroy = "kh/destroy.wav";
-string kh_sound_drop = "kh/drop.wav";
-string kh_sound_collect = "kh/collect.wav";
-string kh_sound_alarm = "kh/alarm.wav"; // the new siren/alarm
-
float kh_key_dropped, kh_key_carried;
const float ST_KH_CAPS = 1;
}
float kh_KeyCarrier_waypointsprite_visible_for_player(entity e) // runs all the time
-{
+{SELFPARAM();
if(!IS_PLAYER(e) || self.team != e.team)
if(!kh_tracking_enabled)
return false;
}
float kh_Key_waypointsprite_visible_for_player(entity e) // ??
-{
+{SELFPARAM();
if(!kh_tracking_enabled)
return false;
if(!self.owner)
}
void kh_WaitForPlayers();
void kh_Controller_Think() // called a lot
-{
+{SELFPARAM();
if(intermission_running)
return;
if(self.cnt > 0)
}
// in any case:
setattachment(key, world, "");
- setorigin(key, key.owner.origin + '0 0 1' * (PL_MIN_z - KH_KEY_MIN_z));
+ setorigin(key, key.owner.origin + '0 0 1' * (PL_MIN.z - KH_KEY_MIN_z));
key.angles = key.owner.angles;
#else
setorigin(key, key.owner.origin + key.origin.z * '0 0 1');
if(key.kh_next == world)
{
// player is now a key carrier
- WaypointSprite_AttachCarrier("", player, RADARICON_FLAGCARRIER, colormapPaletteColor(player.team - 1, 0));
+ entity wp = WaypointSprite_AttachCarrier(WP_Null, player, RADARICON_FLAGCARRIER);
+ wp.colormod = colormapPaletteColor(player.team - 1, 0);
player.waypointsprite_attachedforcarrier.waypointsprite_visible_for_player = kh_KeyCarrier_waypointsprite_visible_for_player;
WaypointSprite_UpdateRule(player.waypointsprite_attachedforcarrier, player.team, SPRITERULE_TEAMPLAY);
if(player.team == NUM_TEAM_1)
- WaypointSprite_UpdateSprites(player.waypointsprite_attachedforcarrier, "keycarrier-red", "keycarrier-friend", "keycarrier-red");
+ WaypointSprite_UpdateSprites(player.waypointsprite_attachedforcarrier, WP_KeyCarrierRed, WP_KeyCarrierFriend, WP_KeyCarrierRed);
else if(player.team == NUM_TEAM_2)
- WaypointSprite_UpdateSprites(player.waypointsprite_attachedforcarrier, "keycarrier-blue", "keycarrier-friend", "keycarrier-blue");
+ WaypointSprite_UpdateSprites(player.waypointsprite_attachedforcarrier, WP_KeyCarrierBlue, WP_KeyCarrierFriend, WP_KeyCarrierBlue);
else if(player.team == NUM_TEAM_3)
- WaypointSprite_UpdateSprites(player.waypointsprite_attachedforcarrier, "keycarrier-yellow", "keycarrier-friend", "keycarrier-yellow");
+ WaypointSprite_UpdateSprites(player.waypointsprite_attachedforcarrier, WP_KeyCarrierYellow, WP_KeyCarrierFriend, WP_KeyCarrierYellow);
else if(player.team == NUM_TEAM_4)
- WaypointSprite_UpdateSprites(player.waypointsprite_attachedforcarrier, "keycarrier-pink", "keycarrier-friend", "keycarrier-pink");
+ WaypointSprite_UpdateSprites(player.waypointsprite_attachedforcarrier, WP_KeyCarrierPink, WP_KeyCarrierFriend, WP_KeyCarrierPink);
if(!kh_no_radar_circles)
WaypointSprite_Ping(player.waypointsprite_attachedforcarrier);
}
// audit all key carrier sprites, update them to RUN HERE
FOR_EACH_KH_KEY(k)
{
- if(k.owner)
- WaypointSprite_UpdateSprites(k.owner.waypointsprite_attachedforcarrier, k.owner.waypointsprite_attachedforcarrier.model1, "keycarrier-finish", k.owner.waypointsprite_attachedforcarrier.model3);
+ if (!k.owner) continue;
+ entity first = WP_Null;
+ FOREACH(Waypoints, it.netname == k.owner.waypointsprite_attachedforcarrier.model1, LAMBDA(first = it; break));
+ entity third = WP_Null;
+ FOREACH(Waypoints, it.netname == k.owner.waypointsprite_attachedforcarrier.model3, LAMBDA(third = it; break));
+ WaypointSprite_UpdateSprites(k.owner.waypointsprite_attachedforcarrier, first, WP_KeyCarrierFinish, third);
}
}
else
// audit all key carrier sprites, update them to RUN HERE
FOR_EACH_KH_KEY(k)
{
- if(k.owner)
- WaypointSprite_UpdateSprites(k.owner.waypointsprite_attachedforcarrier, k.owner.waypointsprite_attachedforcarrier.model1, "keycarrier-friend", k.owner.waypointsprite_attachedforcarrier.model3);
+ if (!k.owner) continue;
+ entity first = WP_Null;
+ FOREACH(Waypoints, it.netname == k.owner.waypointsprite_attachedforcarrier.model1, LAMBDA(first = it; break));
+ entity third = WP_Null;
+ FOREACH(Waypoints, it.netname == k.owner.waypointsprite_attachedforcarrier.model3, LAMBDA(third = it; break));
+ WaypointSprite_UpdateSprites(k.owner.waypointsprite_attachedforcarrier, first, WP_KeyCarrierFriend, third);
}
}
}
}
-void kh_Key_Damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
-{
+void kh_Key_Damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
+{SELFPARAM();
if(self.owner)
return;
if(ITEM_DAMAGE_NEEDKILL(deathtype))
void kh_Key_Collect(entity key, entity player) //a player picks up a dropped key
{
- sound(player, CH_TRIGGER, kh_sound_collect, VOL_BASE, ATTEN_NORM);
+ sound(player, CH_TRIGGER, SND_KH_COLLECT, VOL_BASE, ATTEN_NORM);
if(key.kh_dropperteam != player.team)
{
}
void kh_Key_Touch() // runs many, many times when a key has been dropped and can be picked up
-{
+{SELFPARAM();
if(intermission_running)
return;
midpoint = midpoint * (1 / kh_teams);
te_customflash(midpoint, 1000, 1, Team_ColorRGB(teem) * 0.5 + '0.5 0.5 0.5'); // make the color >=0.5 in each component
- play2all(kh_sound_capture);
+ play2all(SND(KH_CAPTURE));
kh_FinishRound();
}
Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_ENT_4(lostkey, INFO_KEYHUNT_LOST_), lostkey.kh_previous_owner.netname);
- play2all(kh_sound_destroy);
+ play2all(SND(KH_DESTROY));
te_tarexplosion(lostkey.origin);
kh_FinishRound();
}
void kh_Key_Think() // runs all the time
-{
+{SELFPARAM();
entity head;
//entity player; // needed by FOR_EACH_PLAYER
{
if(self.siren_time < time)
{
- sound(self.owner, CH_TRIGGER, kh_sound_alarm, VOL_BASE, ATTEN_NORM); // play a simple alarm
+ sound(self.owner, CH_TRIGGER, SND_KH_ALARM, VOL_BASE, ATTEN_NORM); // play a simple alarm
self.siren_time = time + 2.5; // repeat every 2.5 seconds
}
}
void key_reset()
-{
+{SELFPARAM();
kh_Key_AssignTo(self, world);
kh_Key_Remove(self);
}
Send_Notification(NOTIF_ONE, initial_owner, MSG_CENTER, APP_TEAM_NUM_4(initial_owner.team, CENTER_KEYHUNT_START_));
- WaypointSprite_Spawn("key-dropped", 0, 0, key, '0 0 1' * KH_KEY_WP_ZSHIFT, world, key.team, key, waypointsprite_attachedforcarrier, false, RADARICON_FLAG, '0 1 1');
+ WaypointSprite_Spawn(WP_KeyDropped, 0, 0, key, '0 0 1' * KH_KEY_WP_ZSHIFT, world, key.team, key, waypointsprite_attachedforcarrier, false, RADARICON_FLAG);
key.waypointsprite_attachedforcarrier.waypointsprite_visible_for_player = kh_Key_waypointsprite_visible_for_player;
kh_Key_AssignTo(key, initial_owner);
key.pushltime = time + autocvar_g_balance_keyhunt_protecttime;
key.kh_dropperteam = key.team;
- sound(player, CH_TRIGGER, kh_sound_drop, VOL_BASE, ATTEN_NORM);
+ sound(player, CH_TRIGGER, SND_KH_DROP, VOL_BASE, ATTEN_NORM);
}
void kh_Key_DropAll(entity player, float suicide) // runs whenever a player dies
if(suicide)
key.kh_dropperteam = player.team;
}
- sound(player, CH_TRIGGER, kh_sound_drop, VOL_BASE, ATTEN_NORM);
+ sound(player, CH_TRIGGER, SND_KH_DROP, VOL_BASE, ATTEN_NORM);
}
}
return 0;
}
+#define KH_READY_TEAMS() (!p1 + !p2 + ((kh_teams >= 3) ? !p3 : p3) + ((kh_teams >= 4) ? !p4 : p4))
+#define KH_READY_TEAMS_OK() (KH_READY_TEAMS() == kh_teams)
void kh_WaitForPlayers() // delay start of the round until enough players are present
{
if(time < game_starttime)
return;
}
+ static float prev_missing_teams_mask;
float p1 = kh_CheckPlayers(0), p2 = kh_CheckPlayers(1), p3 = kh_CheckPlayers(2), p4 = kh_CheckPlayers(3);
- if (!(p1 || p2 || p3 || p4))
+ if(KH_READY_TEAMS_OK())
{
+ if(prev_missing_teams_mask > 0)
+ Kill_Notification(NOTIF_ALL, world, MSG_CENTER_CPID, CPID_MISSING_TEAMS);
+ prev_missing_teams_mask = -1;
Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_KEYHUNT_ROUNDSTART, autocvar_g_balance_keyhunt_delay_round);
kh_Controller_SetThink(autocvar_g_balance_keyhunt_delay_round, kh_StartRound);
}
else
{
- Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_KEYHUNT_WAIT, p1, p2, p3, p4);
+ if(player_count == 0)
+ {
+ if(prev_missing_teams_mask > 0)
+ Kill_Notification(NOTIF_ALL, world, MSG_CENTER_CPID, CPID_MISSING_TEAMS);
+ prev_missing_teams_mask = -1;
+ }
+ else
+ {
+ float missing_teams_mask = (!!p1) + (!!p2) * 2;
+ if(kh_teams >= 3) missing_teams_mask += (!!p3) * 4;
+ if(kh_teams >= 4) missing_teams_mask += (!!p4) * 8;
+ if(prev_missing_teams_mask != missing_teams_mask)
+ {
+ Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_MISSING_TEAMS, missing_teams_mask);
+ prev_missing_teams_mask = missing_teams_mask;
+ }
+ }
kh_Controller_SetThink(1, kh_WaitForPlayers);
}
}
}
float p1 = kh_CheckPlayers(0), p2 = kh_CheckPlayers(1), p3 = kh_CheckPlayers(2), p4 = kh_CheckPlayers(3);
- if(p1 || p2 || p3 || p4)
+ if(!KH_READY_TEAMS_OK())
{
kh_Controller_SetThink(1, kh_WaitForPlayers);
- Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_KEYHUNT_WAIT, p1, p2, p3, p4);
return;
}
void kh_Initialize() // sets up th KH environment
{
- precache_sound(kh_sound_capture);
- precache_sound(kh_sound_destroy);
- precache_sound(kh_sound_drop);
- precache_sound(kh_sound_collect);
- precache_sound(kh_sound_alarm); // the new siren
-
-#ifdef KH_PLAYER_USE_CARRIEDMODEL
- precache_model("models/keyhunt/key-carried.md3");
-#endif
- precache_model("models/keyhunt/key.md3");
-
// setup variables
kh_teams = autocvar_g_keyhunt_teams_override;
if(kh_teams < 2)
kh_controller.think = kh_Controller_Think;
kh_Controller_SetThink(0, kh_WaitForPlayers);
- setmodel(kh_controller, "models/keyhunt/key.md3");
+ setmodel(kh_controller, MDL_KH_KEY);
kh_key_dropped = kh_controller.modelindex;
/*
dprint(vtos(kh_controller.mins));
dprint("\n");
*/
#ifdef KH_PLAYER_USE_CARRIEDMODEL
- setmodel(kh_controller, "models/keyhunt/key-carried.md3");
+ setmodel(kh_controller, MDL_KH_KEY_CARRIED);
kh_key_carried = kh_controller.modelindex;
#else
kh_key_carried = kh_key_dropped;
// register this as a mutator
-MUTATOR_HOOKFUNCTION(kh_Key_DropAll)
-{
+MUTATOR_HOOKFUNCTION(kh, ClientDisconnect)
+{SELFPARAM();
kh_Key_DropAll(self, true);
return 0;
}
-MUTATOR_HOOKFUNCTION(kh_PlayerDies)
-{
+MUTATOR_HOOKFUNCTION(kh, MakePlayerObserver)
+{SELFPARAM();
+ kh_Key_DropAll(self, true);
+ return 0;
+}
+
+MUTATOR_HOOKFUNCTION(kh, PlayerDies)
+{SELFPARAM();
if(self == other)
kh_Key_DropAll(self, true);
else if(IS_PLAYER(other))
return 0;
}
-MUTATOR_HOOKFUNCTION(kh_GiveFragsForKill)
+MUTATOR_HOOKFUNCTION(kh, GiveFragsForKill, CBC_ORDER_FIRST)
{
frag_score = kh_HandleFrags(frag_attacker, frag_target, frag_score);
return 0;
}
-MUTATOR_HOOKFUNCTION(kh_finalize)
+MUTATOR_HOOKFUNCTION(kh, MatchEnd)
{
kh_finalize();
return 0;
}
-MUTATOR_HOOKFUNCTION(kh_GetTeamCount)
+MUTATOR_HOOKFUNCTION(kh, GetTeamCount, CBC_ORDER_EXCLUSIVE)
{
ret_float = kh_teams;
return 0;
}
-MUTATOR_HOOKFUNCTION(kh_SpectateCopy)
-{
+MUTATOR_HOOKFUNCTION(kh, SpectateCopy)
+{SELFPARAM();
self.kh_state = other.kh_state;
return 0;
}
-MUTATOR_HOOKFUNCTION(kh_PlayerUseKey)
-{
+MUTATOR_HOOKFUNCTION(kh, PlayerUseKey)
+{SELFPARAM();
if(MUTATOR_RETURNVALUE == 0)
{
entity k;
return 0;
}
-MUTATOR_DEFINITION(gamemode_keyhunt)
+REGISTER_MUTATOR(kh, g_keyhunt)
{
- MUTATOR_HOOK(MakePlayerObserver, kh_Key_DropAll, CBC_ORDER_ANY);
- MUTATOR_HOOK(ClientDisconnect, kh_Key_DropAll, CBC_ORDER_ANY);
- MUTATOR_HOOK(PlayerDies, kh_PlayerDies, CBC_ORDER_ANY);
- MUTATOR_HOOK(GiveFragsForKill, kh_GiveFragsForKill, CBC_ORDER_FIRST);
- MUTATOR_HOOK(MatchEnd, kh_finalize, CBC_ORDER_ANY);
- MUTATOR_HOOK(GetTeamCount, kh_GetTeamCount, CBC_ORDER_EXCLUSIVE);
- MUTATOR_HOOK(SpectateCopy, kh_SpectateCopy, CBC_ORDER_ANY);
- MUTATOR_HOOK(PlayerUseKey, kh_PlayerUseKey, CBC_ORDER_ANY);
+ ActivateTeamplay();
+ SetLimits(autocvar_g_keyhunt_point_limit, autocvar_g_keyhunt_point_leadlimit, -1, -1);
+ if(autocvar_g_keyhunt_team_spawns)
+ have_team_spawns = -1; // request team spawns
MUTATOR_ONADD
{
MUTATOR_ONREMOVE
{
- print("This is a game type and it cannot be removed at runtime.");
+ LOG_INFO("This is a game type and it cannot be removed at runtime.");
return -1;
}