X-Git-Url: http://de.git.xonotic.org/?a=blobdiff_plain;f=qcsrc%2Fserver%2Fmutators%2Fgamemode_keyhunt.qc;h=8a8ed5bca470ce16bc2cc21395bc164177e9431a;hb=79ab84695f689fd7024d60c3429988f7c8774552;hp=2daea2069a31da63e3a85fa008feb445c859e3e8;hpb=ac971ab6de8ded42ea2e339efe1f14788ba16166;p=xonotic%2Fxonotic-data.pk3dir.git diff --git a/qcsrc/server/mutators/gamemode_keyhunt.qc b/qcsrc/server/mutators/gamemode_keyhunt.qc index 2daea2069..8a8ed5bca 100644 --- a/qcsrc/server/mutators/gamemode_keyhunt.qc +++ b/qcsrc/server/mutators/gamemode_keyhunt.qc @@ -2,7 +2,6 @@ // #define KH_PLAYER_USE_ATTACHMENT // #define KH_PLAYER_USE_CARRIEDMODEL -// #define KH_KEY_ATTACHMENT_DEBUG #ifdef KH_PLAYER_USE_ATTACHMENT vector KH_PLAYER_ATTACHMENT_DIST_ROTATED = '0 -4 0'; @@ -68,6 +67,60 @@ string kh_sound_alarm = "kh/alarm.wav"; // the new siren/alarm float kh_key_dropped, kh_key_carried; +float kh_KeyCarrier_waypointsprite_visible_for_player(entity e) // runs all the time +{ + if(e.classname != "player" || self.team != e.team) + if(!kh_tracking_enabled) + return FALSE; + + return TRUE; +} + +float kh_Key_waypointsprite_visible_for_player(entity e) // ?? +{ + if(!kh_tracking_enabled) + return FALSE; + if(!self.owner) + return TRUE; + if(!self.owner.owner) + return TRUE; + return FALSE; // draw only when key is not owned +} + +void kh_update_state() +{ + entity player; + entity key; + float s; + float f; + + s = 0; + FOR_EACH_KH_KEY(key) + { + if(key.owner) + f = key.team; + else + f = 30; + s |= pow(32, key.count) * f; + } + + FOR_EACH_CLIENT(player) + { + player.kh_state = s; + } + + FOR_EACH_KH_KEY(key) + { + if(key.owner) + key.owner.kh_state |= pow(32, key.count) * 31; + } + //print(ftos((nextent(world)).kh_state), "\n"); +} + + + + +var kh_Think_t kh_Controller_Thinkfunc; void kh_Controller_SetThink(float t, string msg, kh_Think_t func) // runs occasionaly { kh_Controller_Thinkfunc = func; @@ -99,9 +152,10 @@ void kh_Controller_Think() // called a lot //dprint(s, "\n"); + // TODO limit centerprint spam and set the appropriated duration for each centerprinted msg FOR_EACH_PLAYER(e) if(clienttype(e) == CLIENTTYPE_REAL) - centerprint_atprio(e, CENTERPRIO_SPAM, s); + Send_CSQC_Centerprint_Generic(e, CPID_KH_MSG, s, 2, 0); // duration > 1 works with all the messages } self.cnt -= 1; } @@ -127,7 +181,7 @@ void kh_Scores_Event(entity player, entity key, string what, float frags_player, if(key && key.owner && frags_owner) UpdateFrags(key.owner, frags_owner); - if(!cvar("sv_eventlog")) //output extra info to the console or text file + if(!autocvar_sv_eventlog) //output extra info to the console or text file return; s = strcat(":keyhunt:", what, ":", ftos(player.playerid), ":", ftos(frags_player)); @@ -231,8 +285,8 @@ void kh_Key_Detach(entity key) // runs every time a key is dropped or lost. Runs key.flags = FL_ITEM; key.solid = SOLID_TRIGGER; key.movetype = MOVETYPE_TOSS; - key.pain_finished = time + cvar("g_balance_keyhunt_delay_return"); - key.damageforcescale = cvar("g_balance_keyhunt_damageforcescale"); + key.pain_finished = time + autocvar_g_balance_keyhunt_delay_return; + key.damageforcescale = autocvar_g_balance_keyhunt_damageforcescale; key.takedamage = DAMAGE_YES; // let key.team stay key.modelindex = kh_key_dropped; @@ -297,7 +351,7 @@ void kh_Key_AssignTo(entity key, entity player) // runs every time a key is pic if(key.kh_next == world) { // player is now a key carrier - WaypointSprite_AttachCarrier("", player); + WaypointSprite_AttachCarrier("", player, RADARICON_FLAGCARRIER, 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 == COLOR_TEAM1) @@ -308,7 +362,6 @@ void kh_Key_AssignTo(entity key, entity player) // runs every time a key is pic WaypointSprite_UpdateSprites(player.waypointsprite_attachedforcarrier, "keycarrier-yellow", "keycarrier-friend", "keycarrier-yellow"); else if(player.team == COLOR_TEAM4) WaypointSprite_UpdateSprites(player.waypointsprite_attachedforcarrier, "keycarrier-pink", "keycarrier-friend", "keycarrier-pink"); - WaypointSprite_UpdateTeamRadar(player.waypointsprite_attachedforcarrier, RADARICON_FLAGCARRIER, colormapPaletteColor(player.team - 1, 0)); if(!kh_no_radar_circles) WaypointSprite_Ping(player.waypointsprite_attachedforcarrier); } @@ -359,64 +412,36 @@ void kh_Key_Damage(entity inflictor, entity attacker, float damage, float deatht self.team = attacker.team; } -void key_reset() -{ - kh_Key_AssignTo(self, world); - kh_Key_Remove(self); -} - -void kh_Key_Spawn(entity initial_owner, float angle, float i) // runs every time a new flag is created, ie after all the keys have been collected +void kh_Key_Collect(entity key, entity player) //a player picks up a dropped key { - entity key; - key = spawn(); - key.count = i; - key.classname = STR_ITEM_KH_KEY; - key.touch = kh_Key_Touch; - key.think = kh_Key_Think; - key.nextthink = time; - key.items = IT_KEY1 | IT_KEY2; - key.cnt = angle; - key.angles = '0 360 0' * random(); - key.event_damage = kh_Key_Damage; - key.takedamage = DAMAGE_YES; - key.modelindex = kh_key_dropped; - key.model = "key"; - key.kh_dropperteam = 0; - key.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP; - setsize(key, KH_KEY_MIN, KH_KEY_MAX); - key.colormod = TeamColor(initial_owner.team) * KH_KEY_BRIGHTNESS; - key.reset = key_reset; + sound(player, CHAN_AUTO, kh_sound_collect, VOL_BASE, ATTN_NORM); - switch(initial_owner.team) + if(key.kh_dropperteam != player.team) { - case COLOR_TEAM1: - key.netname = "^1red key"; - break; - case COLOR_TEAM2: - key.netname = "^4blue key"; - break; - case COLOR_TEAM3: - key.netname = "^3yellow key"; - break; - case COLOR_TEAM4: - key.netname = "^6pink key"; - break; - default: - key.netname = "NETGIER key"; - break; + kh_Scores_Event(player, key, "collect", autocvar_g_balance_keyhunt_score_collect, 0); + PlayerScore_Add(player, SP_KH_PICKUPS, 1); } + key.kh_dropperteam = 0; + bprint(player.netname, "^7 picked up the ", key.netname, "\n"); - // link into key list - key.kh_worldkeynext = kh_worldkeylist; - kh_worldkeylist = key; - - centerprint(initial_owner, strcat("You are starting with the ", key.netname, "\n")); // message to player at start of round + kh_Key_AssignTo(key, player); // this also updates .kh_state +} - WaypointSprite_Spawn("key-dropped", 0, 0, key, '0 0 1' * KH_KEY_WP_ZSHIFT, world, key.team, key, waypointsprite_attachedforcarrier, FALSE); - key.waypointsprite_attachedforcarrier.waypointsprite_visible_for_player = kh_Key_waypointsprite_visible_for_player; - WaypointSprite_UpdateTeamRadar(key.waypointsprite_attachedforcarrier, RADARICON_FLAG, '0 1 1'); +void kh_Key_Touch() // runs many, many times when a key has been dropped and can be picked up +{ + if(intermission_running) + return; - kh_Key_AssignTo(key, initial_owner); + if(self.owner) // already carried + return; + if(other.classname != "player") + return; + if(other.deadflag != DEAD_NO) + return; + if(other == self.enemy) + if(time < self.kh_droptime + autocvar_g_balance_keyhunt_delay_collect) + return; // you just dropped it! + kh_Key_Collect(self, other); } void kh_Key_Remove(entity key) // runs after when all the keys have been collected or when a key has been dropped for more than X seconds @@ -451,228 +476,73 @@ void kh_Key_Remove(entity key) // runs after when all the keys have been collec kh_update_state(); } -// -1 when no team completely owns all keys yet -float kh_Key_AllOwnedByWhichTeam() // constantly called. check to see if all the keys are owned by the same team +void kh_FinishRound() // runs when a team captures the keys { + // prepare next round + kh_interferemsg_time = 0; entity key; - float teem; - float keys; - teem = -1; - keys = kh_teams; + kh_no_radar_circles = TRUE; FOR_EACH_KH_KEY(key) - { - if(!key.owner) - return -1; - if(teem == -1) - teem = key.team; - else if(teem != key.team) - return -1; - --keys; - } - if(keys != 0) - return -1; - return teem; -} - -void kh_Key_Collect(entity key, entity player) //a player picks up a dropped key -{ - sound(player, CHAN_AUTO, kh_sound_collect, VOL_BASE, ATTN_NORM); - - if(key.kh_dropperteam != player.team) - { - kh_Scores_Event(player, key, "collect", cvar("g_balance_keyhunt_score_collect"), 0); - PlayerScore_Add(player, SP_KH_PICKUPS, 1); - } - key.kh_dropperteam = 0; - bprint(player.netname, "^7 picked up the ", key.netname, "\n"); + kh_Key_Remove(key); + kh_no_radar_circles = FALSE; - kh_Key_AssignTo(key, player); // this also updates .kh_state + kh_Controller_SetThink(autocvar_g_balance_keyhunt_delay_round, "Round starts in ", kh_StartRound); } -void kh_Key_DropAll(entity player, float suicide) // runs whenever a player dies +void kh_WinnerTeam(float teem) // runs when a team wins { + // all key carriers get some points + vector firstorigin, lastorigin, midpoint; + float first; entity key; - entity mypusher; - if(player.kh_next) + float score; + score = (kh_teams - 1) * autocvar_g_balance_keyhunt_score_capture; + DistributeEvenly_Init(score, kh_teams); + // twice the score for 3 team games, three times the score for 4 team games! + // note: for a win by destroying the key, this should NOT be applied + FOR_EACH_KH_KEY(key) { - mypusher = world; - if(player.pusher) - if(time < player.pushltime) - mypusher = player.pusher; - while((key = player.kh_next)) - { - kh_Scores_Event(player, key, "losekey", 0, 0); - PlayerScore_Add(player, SP_KH_LOSSES, 1); - bprint(player.netname, "^7 died and lost the ", key.netname, "\n"); - kh_Key_AssignTo(key, world); - makevectors('-1 0 0' * (45 + 45 * random()) + '0 360 0' * random()); - key.velocity = W_CalculateProjectileVelocity(player.velocity, cvar("g_balance_keyhunt_dropvelocity") * v_forward); - key.pusher = mypusher; - key.pushltime = time + cvar("g_balance_keyhunt_protecttime"); - if(suicide) - key.kh_dropperteam = player.team; - } - sound(player, CHAN_AUTO, kh_sound_drop, VOL_BASE, ATTN_NORM); + float f; + f = DistributeEvenly_Get(1); + kh_Scores_Event(key.owner, key, "capture", f, 0); + PlayerTeamScore_Add(key.owner, SP_KH_CAPS, ST_KH_CAPS, 1); } -} -void kh_Key_Touch() // runs many, many times when a key has been dropped and can be picked up -{ - if(intermission_running) - return; - - if(self.owner) // already carried - return; - if(other.classname != "player") - return; - if(other.deadflag != DEAD_NO) - return; - if(other == self.enemy) - if(time < self.kh_droptime + cvar("g_balance_keyhunt_delay_collect")) - return; // you just dropped it! - kh_Key_Collect(self, other); -} - -void kh_Key_Think() // runs all the time -{ - entity head; - //entity player; // needed by FOR_EACH_PLAYER - - if(intermission_running) - return; - -#ifdef KH_KEY_ATTACHMENT_DEBUG - if(self.kh_prev == self.owner) - { - if(cvar_string("_angles") != "") + first = TRUE; + FOR_EACH_KH_KEY(key) + if(key.owner.kh_next == key) { - self.angles = stov(cvar_string("_angles")); - setorigin(self, stov(cvar_string("_origin"))); + if(!first) + bprint("^7, "); + bprint(key.owner.netname); + first = FALSE; } - } -#endif + bprint("^7 captured the keys for the ", ColoredTeamName(teem), "\n"); - if(self.owner) + first = TRUE; + midpoint = '0 0 0'; + FOR_EACH_KH_KEY(key) { -#ifndef KH_PLAYER_USE_ATTACHMENT - makevectors('0 1 0' * (self.cnt + mod(time, 360) * KH_KEY_XYSPEED)); - setorigin(self, v_forward * KH_KEY_XYDIST + '0 0 1' * self.origin_z); -#endif + vector thisorigin; - if(self.owner.BUTTON_USE) - if(time >= self.owner.kh_droptime + cvar("g_balance_keyhunt_delay_drop")) - { - self.owner.kh_droptime = time; - self.kh_droptime = time; // prevent collecting this one for some time - self.enemy = self.owner; - self.pusher = world; - kh_Scores_Event(self.owner, self, "dropkey", 0, 0); - bprint(self.owner.netname, "^7 dropped the ", self.netname, "\n"); - sound(self.owner, CHAN_AUTO, kh_sound_drop, VOL_BASE, ATTN_NORM); - makevectors(self.owner.v_angle); - self.velocity = W_CalculateProjectileVelocity(self.owner.velocity, cvar("g_balance_keyhunt_throwvelocity") * v_forward); - kh_Key_AssignTo(self, world); - self.pushltime = time + cvar("g_balance_keyhunt_protecttime"); - self.kh_dropperteam = self.team; - } + thisorigin = kh_AttachedOrigin(key); + //dprint("Key origin: ", vtos(thisorigin), "\n"); + midpoint += thisorigin; + + if(!first) + te_lightning2(world, lastorigin, thisorigin); + lastorigin = thisorigin; + if(first) + firstorigin = thisorigin; + first = FALSE; } - - // if in nodrop or time over, end the round - if(!self.owner) - if(time > self.pain_finished) - kh_LoserTeam(self.team, self); - - if(self.owner) - if(kh_Key_AllOwnedByWhichTeam() != -1) - { - if(self.siren_time < time) - { - sound(self.owner, CHAN_AUTO, kh_sound_alarm, VOL_BASE, ATTN_NORM); // play a simple alarm - self.siren_time = time + 2.5; // repeat every 2.5 seconds - } - - entity key; - vector p; - p = self.owner.origin; - FOR_EACH_KH_KEY(key) - if(vlen(key.owner.origin - p) > cvar("g_balance_keyhunt_maxdist")) - goto not_winning; - kh_WinnerTeam(self.team); -:not_winning - } - - if(kh_interferemsg_time && time > kh_interferemsg_time) - { - kh_interferemsg_time = 0; - FOR_EACH_PLAYER(head) - { - if(head.team == kh_interferemsg_team) - if(head.kh_next) - centerprint(head, "All keys are in your team's hands!\n\nMeet the other key carriers ^1NOW^7!"); - else - centerprint(head, "All keys are in your team's hands!\n\nHelp the key carriers to meet!"); - else - centerprint(head, strcat("All keys are in the ", ColoredTeamName(kh_interferemsg_team), "^7's hands!\n\nInterfere ^1NOW^7!")); - } - } - - self.nextthink = time + 0.05; -} - -void kh_WinnerTeam(float teem) // runs when a team wins -{ - // all key carriers get some points - vector firstorigin, lastorigin, midpoint; - float first; - entity key; - float score; - score = (kh_teams - 1) * cvar("g_balance_keyhunt_score_capture"); - DistributeEvenly_Init(score, kh_teams); - // twice the score for 3 team games, three times the score for 4 team games! - // note: for a win by destroying the key, this should NOT be applied - FOR_EACH_KH_KEY(key) - { - float f; - f = DistributeEvenly_Get(1); - kh_Scores_Event(key.owner, key, "capture", f, 0); - PlayerTeamScore_Add(key.owner, SP_KH_CAPS, ST_KH_CAPS, 1); - } - - first = TRUE; - FOR_EACH_KH_KEY(key) - if(key.owner.kh_next == key) - { - if(!first) - bprint("^7, "); - bprint(key.owner.netname); - first = FALSE; - } - bprint("^7 captured the keys for the ", ColoredTeamName(teem), "\n"); - - first = TRUE; - midpoint = '0 0 0'; - FOR_EACH_KH_KEY(key) - { - vector thisorigin; - - thisorigin = kh_AttachedOrigin(key); - //dprint("Key origin: ", vtos(thisorigin), "\n"); - midpoint += thisorigin; - - if(!first) - te_lightning2(world, lastorigin, thisorigin); - lastorigin = thisorigin; - if(first) - firstorigin = thisorigin; - first = FALSE; - } - if(kh_teams > 2) - { - te_lightning2(world, lastorigin, firstorigin); - } - midpoint = midpoint * (1 / kh_teams); - te_customflash(midpoint, 1000, 1, TeamColor(teem) * 0.5 + '0.5 0.5 0.5'); // make the color >=0.5 in each component + if(kh_teams > 2) + { + te_lightning2(world, lastorigin, firstorigin); + } + midpoint = midpoint * (1 / kh_teams); + te_customflash(midpoint, 1000, 1, TeamColor(teem) * 0.5 + '0.5 0.5 0.5'); // make the color >=0.5 in each component play2all(kh_sound_capture); kh_FinishRound(); @@ -696,9 +566,9 @@ void kh_LoserTeam(float teem, entity lostkey) // runs when a player pushes a fl if(attacker) { if(lostkey.kh_previous_owner) - kh_Scores_Event(lostkey.kh_previous_owner, world, "pushed", 0, -cvar("g_balance_keyhunt_score_push")); + kh_Scores_Event(lostkey.kh_previous_owner, world, "pushed", 0, -autocvar_g_balance_keyhunt_score_push); // don't actually GIVE him the -nn points, just log - kh_Scores_Event(attacker, world, "push", cvar("g_balance_keyhunt_score_push"), 0); + kh_Scores_Event(attacker, world, "push", autocvar_g_balance_keyhunt_score_push, 0); PlayerScore_Add(attacker, SP_KH_PUSHES, 1); centerprint(attacker, "Your push is the best!"); bprint("The ", ColoredTeamName(teem), "^7 could not take care of the ", lostkey.netname, "^7 when ", attacker.netname, "^7 came\n"); @@ -706,7 +576,7 @@ void kh_LoserTeam(float teem, entity lostkey) // runs when a player pushes a fl else { float of, fragsleft, i, j, thisteam; - of = cvar("g_balance_keyhunt_score_destroyed_ownfactor"); + of = autocvar_g_balance_keyhunt_score_destroyed_ownfactor; FOR_EACH_PLAYER(player) if(player.team != teem) @@ -717,13 +587,13 @@ void kh_LoserTeam(float teem, entity lostkey) // runs when a player pushes a fl ++keys; if(lostkey.kh_previous_owner) - kh_Scores_Event(lostkey.kh_previous_owner, world, "destroyed", 0, -cvar("g_balance_keyhunt_score_destroyed")); + kh_Scores_Event(lostkey.kh_previous_owner, world, "destroyed", 0, -autocvar_g_balance_keyhunt_score_destroyed); // don't actually GIVE him the -nn points, just log if(lostkey.kh_previous_owner.playerid == lostkey.kh_previous_owner_playerid) PlayerScore_Add(lostkey.kh_previous_owner, SP_KH_DESTROYS, 1); - DistributeEvenly_Init(cvar("g_balance_keyhunt_score_destroyed"), keys * of + players); + DistributeEvenly_Init(autocvar_g_balance_keyhunt_score_destroyed, keys * of + players); FOR_EACH_KH_KEY(key) if(key.owner && key.team != teem) @@ -769,18 +639,195 @@ void kh_LoserTeam(float teem, entity lostkey) // runs when a player pushes a fl kh_FinishRound(); } -void kh_FinishRound() // runs when a team captures the keys +void kh_Key_Think() // runs all the time +{ + entity head; + //entity player; // needed by FOR_EACH_PLAYER + + if(intermission_running) + return; + + if(self.owner) + { +#ifndef KH_PLAYER_USE_ATTACHMENT + makevectors('0 1 0' * (self.cnt + mod(time, 360) * KH_KEY_XYSPEED)); + setorigin(self, v_forward * KH_KEY_XYDIST + '0 0 1' * self.origin_z); +#endif + } + + // if in nodrop or time over, end the round + if(!self.owner) + if(time > self.pain_finished) + kh_LoserTeam(self.team, self); + + if(self.owner) + if(kh_Key_AllOwnedByWhichTeam() != -1) + { + if(self.siren_time < time) + { + sound(self.owner, CHAN_AUTO, kh_sound_alarm, VOL_BASE, ATTN_NORM); // play a simple alarm + self.siren_time = time + 2.5; // repeat every 2.5 seconds + } + + entity key; + vector p; + p = self.owner.origin; + FOR_EACH_KH_KEY(key) + if(vlen(key.owner.origin - p) > autocvar_g_balance_keyhunt_maxdist) + goto not_winning; + kh_WinnerTeam(self.team); +:not_winning + } + + if(kh_interferemsg_time && time > kh_interferemsg_time) + { + kh_interferemsg_time = 0; + FOR_EACH_PLAYER(head) + { + if(head.team == kh_interferemsg_team) + if(head.kh_next) + Send_CSQC_Centerprint_Generic(head, CPID_KH_MSG, "All keys are in your team's hands!\n\nMeet the other key carriers ^1NOW^7!", 0, 0); + else + Send_CSQC_Centerprint_Generic(head, CPID_KH_MSG, "All keys are in your team's hands!\n\nHelp the key carriers to meet!", 0, 0); + else + Send_CSQC_Centerprint_Generic(head, CPID_KH_MSG, strcat("All keys are in the ", ColoredTeamName(kh_interferemsg_team), "^7's hands!\n\nInterfere ^1NOW^7!"), 0, 0); + } + } + + self.nextthink = time + 0.05; +} + +void key_reset() +{ + kh_Key_AssignTo(self, world); + kh_Key_Remove(self); +} + +string STR_ITEM_KH_KEY = "item_kh_key"; +void kh_Key_Spawn(entity initial_owner, float angle, float i) // runs every time a new flag is created, ie after all the keys have been collected { - // prepare next round - kh_interferemsg_time = 0; entity key; + key = spawn(); + key.count = i; + key.classname = STR_ITEM_KH_KEY; + key.touch = kh_Key_Touch; + key.think = kh_Key_Think; + key.nextthink = time; + key.items = IT_KEY1 | IT_KEY2; + key.cnt = angle; + key.angles = '0 360 0' * random(); + key.event_damage = kh_Key_Damage; + key.takedamage = DAMAGE_YES; + key.modelindex = kh_key_dropped; + key.model = "key"; + key.kh_dropperteam = 0; + key.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP; + setsize(key, KH_KEY_MIN, KH_KEY_MAX); + key.colormod = TeamColor(initial_owner.team) * KH_KEY_BRIGHTNESS; + key.reset = key_reset; - kh_no_radar_circles = TRUE; + switch(initial_owner.team) + { + case COLOR_TEAM1: + key.netname = "^1red key"; + break; + case COLOR_TEAM2: + key.netname = "^4blue key"; + break; + case COLOR_TEAM3: + key.netname = "^3yellow key"; + break; + case COLOR_TEAM4: + key.netname = "^6pink key"; + break; + default: + key.netname = "NETGIER key"; + break; + } + + // link into key list + key.kh_worldkeynext = kh_worldkeylist; + kh_worldkeylist = key; + + centerprint(initial_owner, strcat("You are starting with the ", key.netname, "\n")); // message to player at start of round + + 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'); + key.waypointsprite_attachedforcarrier.waypointsprite_visible_for_player = kh_Key_waypointsprite_visible_for_player; + + kh_Key_AssignTo(key, initial_owner); +} + +// -1 when no team completely owns all keys yet +float kh_Key_AllOwnedByWhichTeam() // constantly called. check to see if all the keys are owned by the same team +{ + entity key; + float teem; + float keys; + + teem = -1; + keys = kh_teams; FOR_EACH_KH_KEY(key) - kh_Key_Remove(key); - kh_no_radar_circles = FALSE; + { + if(!key.owner) + return -1; + if(teem == -1) + teem = key.team; + else if(teem != key.team) + return -1; + --keys; + } + if(keys != 0) + return -1; + return teem; +} + +void kh_Key_DropOne(entity key) +{ + // prevent collecting this one for some time + entity player; + player = key.owner; + + key.kh_droptime = time; + key.enemy = player; + + kh_Scores_Event(player, key, "dropkey", 0, 0); + PlayerScore_Add(player, SP_KH_LOSSES, 1); + bprint(player.netname, "^7 dropped the ", key.netname, "\n"); + kh_Key_AssignTo(key, world); + makevectors(player.v_angle); + key.velocity = W_CalculateProjectileVelocity(player.velocity, autocvar_g_balance_keyhunt_throwvelocity * v_forward, FALSE); + key.pusher = world; + key.pushltime = time + autocvar_g_balance_keyhunt_protecttime; + key.kh_dropperteam = key.team; - kh_Controller_SetThink(cvar("g_balance_keyhunt_delay_round"), "Round starts in ", kh_StartRound); + sound(player, CHAN_AUTO, kh_sound_drop, VOL_BASE, ATTN_NORM); +} + +void kh_Key_DropAll(entity player, float suicide) // runs whenever a player dies +{ + entity key; + entity mypusher; + if(player.kh_next) + { + mypusher = world; + if(player.pusher) + if(time < player.pushltime) + mypusher = player.pusher; + while((key = player.kh_next)) + { + kh_Scores_Event(player, key, "losekey", 0, 0); + PlayerScore_Add(player, SP_KH_LOSSES, 1); + bprint(player.netname, "^7 died and lost the ", key.netname, "\n"); + kh_Key_AssignTo(key, world); + makevectors('-1 0 0' * (45 + 45 * random()) + '0 360 0' * random()); + key.velocity = W_CalculateProjectileVelocity(player.velocity, autocvar_g_balance_keyhunt_dropvelocity * v_forward, FALSE); + key.pusher = mypusher; + key.pushltime = time + autocvar_g_balance_keyhunt_protecttime; + if(suicide) + key.kh_dropperteam = player.team; + } + sound(player, CHAN_AUTO, kh_sound_drop, VOL_BASE, ATTN_NORM); + } } string kh_CheckEnoughPlayers() // checks enough player are present, runs after every completed round @@ -822,11 +869,22 @@ void kh_WaitForPlayers() // delay start of the round until enough players are p teams_missing = kh_CheckEnoughPlayers(); if(teams_missing == "") - kh_Controller_SetThink(cvar("g_balance_keyhunt_delay_round"), "Round starts in ", kh_StartRound); + kh_Controller_SetThink(autocvar_g_balance_keyhunt_delay_round, "Round starts in ", kh_StartRound); else kh_Controller_SetThink(1, strcat("Waiting for players to join...\n\nNeed active players for: ", teams_missing), kh_WaitForPlayers); } +void kh_EnableTrackingDevice() // runs after each round +{ + entity player; + + FOR_EACH_PLAYER(player) + if(clienttype(player) == CLIENTTYPE_REAL) + Send_CSQC_Centerprint_Generic_Expire(player, CPID_KH_MSG); + + kh_tracking_enabled = TRUE; +} + void kh_StartRound() // runs at the start of each round { string teams_missing; @@ -848,7 +906,7 @@ void kh_StartRound() // runs at the start of each round FOR_EACH_PLAYER(player) if(clienttype(player) == CLIENTTYPE_REAL) - centerprint_expire(player, CENTERPRIO_SPAM); + Send_CSQC_Centerprint_Generic_Expire(player, CPID_KH_MSG); for(i = 0; i < kh_teams; ++i) { @@ -868,38 +926,7 @@ void kh_StartRound() // runs at the start of each round } kh_tracking_enabled = FALSE; - kh_Controller_SetThink(cvar("g_balance_keyhunt_delay_tracking"), "Scanning frequency range...", kh_EnableTrackingDevice); -} - -void kh_EnableTrackingDevice() // runs after each round -{ - entity player; - - FOR_EACH_PLAYER(player) - if(clienttype(player) == CLIENTTYPE_REAL) - centerprint_expire(player, CENTERPRIO_SPAM); - - kh_tracking_enabled = TRUE; -} - -float kh_Key_waypointsprite_visible_for_player(entity e) // ?? -{ - if(!kh_tracking_enabled) - return FALSE; - if(!self.owner) - return TRUE; - if(!self.owner.owner) - return TRUE; - return FALSE; // draw only when key is not owned -} - -float kh_KeyCarrier_waypointsprite_visible_for_player(entity e) // runs all the time -{ - if(e.classname != "player" || self.team != e.team) - if(!kh_tracking_enabled) - return FALSE; - - return TRUE; + kh_Controller_SetThink(autocvar_g_balance_keyhunt_delay_tracking, "Scanning frequency range...", kh_EnableTrackingDevice); } float kh_HandleFrags(entity attacker, entity targ, float f) // adds to the player score @@ -916,11 +943,11 @@ float kh_HandleFrags(entity attacker, entity targ, float f) // adds to the play nk = 0; for(k = targ.kh_next; k != world; k = k.kh_next) ++nk; - kh_Scores_Event(attacker, targ.kh_next, "carrierfrag", -nk * cvar("g_balance_keyhunt_score_collect"), 0); + kh_Scores_Event(attacker, targ.kh_next, "carrierfrag", -nk * autocvar_g_balance_keyhunt_score_collect, 0); } else { - kh_Scores_Event(attacker, targ.kh_next, "carrierfrag", cvar("g_balance_keyhunt_score_carrierfrag")-1, 0); + kh_Scores_Event(attacker, targ.kh_next, "carrierfrag", autocvar_g_balance_keyhunt_score_carrierfrag-1, 0); PlayerScore_Add(attacker, SP_KH_KCKILLS, 1); // the frag gets added later } @@ -943,9 +970,9 @@ void kh_Initialize() // sets up th KH environment precache_model("models/keyhunt/key.md3"); // setup variables - kh_teams = cvar("g_keyhunt_teams_override"); + kh_teams = autocvar_g_keyhunt_teams_override; if(kh_teams < 2) - kh_teams = cvar("g_keyhunt_teams"); + kh_teams = autocvar_g_keyhunt_teams; kh_teams = bound(2, kh_teams, 4); // make a KH entity for controlling the game @@ -983,39 +1010,6 @@ void kh_finalize() kh_controller = world; } -void kh_update_state() -{ - entity player; - entity key; - float s; - float f; - - s = 0; - FOR_EACH_KH_KEY(key) - { - if(key.owner) - f = key.team; - else - f = 30; - s |= pow(32, key.count) * f; - } - - FOR_EACH_CLIENT(player) - { - player.kh_state = s; - } - - FOR_EACH_KH_KEY(key) - { - if(key.owner) - key.owner.kh_state |= pow(32, key.count) * 31; - } - //print(ftos((nextent(world)).kh_state), "\n"); -} - - - - // register this as a mutator MUTATOR_HOOKFUNCTION(kh_Key_DropAll) @@ -1047,17 +1041,48 @@ MUTATOR_HOOKFUNCTION(kh_finalize) return 0; } +MUTATOR_HOOKFUNCTION(kh_GetTeamCount) +{ + ret_float = kh_teams; + return 0; +} + +MUTATOR_HOOKFUNCTION(kh_SpectateCopy) +{ + self.kh_state = other.kh_state; + return 0; +} + +MUTATOR_HOOKFUNCTION(kh_PlayerUseKey) +{ + if(MUTATOR_RETURNVALUE == 0) + { + entity k; + k = self.kh_next; + if(k) + { + kh_Key_DropOne(k); + return 1; + } + } + return 0; +} + MUTATOR_DEFINITION(gamemode_keyhunt) { MUTATOR_HOOK(MakePlayerObserver, kh_Key_DropAll, CBC_ORDER_ANY); - MUTATOR_HOOK(MakePlayerSpectator, 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_EXCLUSIVE); + 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); MUTATOR_ONADD { + if(time > 1) // game loads at time 1 + error("This is a game type and it cannot be added at runtime."); g_keyhunt = 1; kh_Initialize(); } @@ -1070,8 +1095,3 @@ MUTATOR_DEFINITION(gamemode_keyhunt) return 0; } - -void kh_init() -{ - MUTATOR_ADD(gamemode_keyhunt); -}