mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-09 16:05:07 +00:00
[MIRROR] Refactors CTF [MDB IGNORE] (#20457)
* Refactors CTF (#74342) ## About The Pull Request CTF code is rather infamous for being poor and hard to actually work with, I wanted to add a feature for a mapping march project but the code would not allow for it, so here we are refactoring the entire thing. ## Why It's Good For The Game Replaces some really bad code with slightly less bad code. Should make it much easier to add features onto CTF in the future. ## Changelog 🆑 refactor: CTF has been entirely refactored. fix: Respawn times for CTF now work. qol: CTF players are alerted during control point games when one team is half way to winning. admin: CTF instagib mode can now be toggled from the secrets panel. /🆑 * Refactors CTF --------- Co-authored-by: NamelessFairy <40036527+NamelessFairy@users.noreply.github.com>
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
/turf/open/floor/plating,
|
||||
/area/centcom/ctf)
|
||||
"bp" = (
|
||||
/obj/item/ctf/red,
|
||||
/obj/item/ctf_flag/red,
|
||||
/turf/open/floor/circuit/green/anim,
|
||||
/area/centcom/ctf)
|
||||
"bM" = (
|
||||
@@ -269,7 +269,7 @@
|
||||
/turf/open/floor/circuit,
|
||||
/area/centcom/ctf)
|
||||
"AJ" = (
|
||||
/obj/machinery/capture_the_flag/blue,
|
||||
/obj/machinery/ctf/spawner/blue,
|
||||
/turf/open/floor/circuit/green/anim,
|
||||
/area/centcom/ctf)
|
||||
"AL" = (
|
||||
@@ -359,7 +359,7 @@
|
||||
/turf/open/floor/circuit/green/off,
|
||||
/area/centcom/ctf)
|
||||
"Gn" = (
|
||||
/obj/item/ctf/blue,
|
||||
/obj/item/ctf_flag/blue,
|
||||
/turf/open/floor/circuit/green/anim,
|
||||
/area/centcom/ctf)
|
||||
"GW" = (
|
||||
@@ -535,7 +535,7 @@
|
||||
/turf/open/floor/circuit/red,
|
||||
/area/centcom/ctf)
|
||||
"Th" = (
|
||||
/obj/machinery/capture_the_flag/red,
|
||||
/obj/machinery/ctf/spawner/red,
|
||||
/turf/open/floor/circuit/green/anim,
|
||||
/area/centcom/ctf)
|
||||
"TB" = (
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
/turf/closed/indestructible/syndicate,
|
||||
/area/centcom/ctf)
|
||||
"cu" = (
|
||||
/obj/machinery/capture_the_flag/green,
|
||||
/obj/machinery/ctf/spawner/green,
|
||||
/turf/open/floor/circuit/green,
|
||||
/area/centcom/ctf)
|
||||
"cy" = (
|
||||
@@ -53,7 +53,7 @@
|
||||
/turf/open/floor/plating,
|
||||
/area/centcom/ctf)
|
||||
"cO" = (
|
||||
/obj/item/ctf/green,
|
||||
/obj/item/ctf_flag/green,
|
||||
/turf/open/floor/circuit/green/anim,
|
||||
/area/centcom/ctf)
|
||||
"dq" = (
|
||||
@@ -273,7 +273,7 @@
|
||||
/turf/open/floor/mineral/titanium/tiled,
|
||||
/area/centcom/ctf)
|
||||
"pR" = (
|
||||
/obj/machinery/capture_the_flag/red,
|
||||
/obj/machinery/ctf/spawner/red,
|
||||
/turf/open/floor/circuit/red,
|
||||
/area/centcom/ctf)
|
||||
"rB" = (
|
||||
@@ -640,7 +640,7 @@
|
||||
/turf/open/floor/circuit/green/anim,
|
||||
/area/centcom/ctf)
|
||||
"LS" = (
|
||||
/obj/item/ctf/red,
|
||||
/obj/item/ctf_flag/red,
|
||||
/turf/open/floor/circuit/red/anim,
|
||||
/area/centcom/ctf)
|
||||
"MC" = (
|
||||
|
||||
@@ -205,7 +205,7 @@
|
||||
/turf/open/floor/iron/dark,
|
||||
/area/centcom/ctf)
|
||||
"eA" = (
|
||||
/obj/machinery/capture_the_flag/blue,
|
||||
/obj/machinery/ctf/spawner/blue,
|
||||
/turf/open/floor/circuit/green/anim,
|
||||
/area/centcom/ctf)
|
||||
"eC" = (
|
||||
@@ -467,7 +467,7 @@
|
||||
},
|
||||
/area/centcom/ctf)
|
||||
"jk" = (
|
||||
/obj/item/ctf/red,
|
||||
/obj/item/ctf_flag/red,
|
||||
/turf/open/floor/circuit/green/anim,
|
||||
/area/centcom/ctf)
|
||||
"jl" = (
|
||||
@@ -590,7 +590,7 @@
|
||||
/turf/open/floor/carpet/red,
|
||||
/area/centcom/ctf)
|
||||
"lK" = (
|
||||
/obj/item/ctf/blue,
|
||||
/obj/item/ctf_flag/blue,
|
||||
/turf/open/floor/circuit/green/anim,
|
||||
/area/centcom/ctf)
|
||||
"lL" = (
|
||||
@@ -1825,7 +1825,7 @@
|
||||
/turf/open/floor/iron,
|
||||
/area/centcom/ctf)
|
||||
"Oi" = (
|
||||
/obj/machinery/capture_the_flag/red,
|
||||
/obj/machinery/ctf/spawner/red,
|
||||
/turf/open/floor/circuit/green/anim,
|
||||
/area/centcom/ctf)
|
||||
"Ok" = (
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
/turf/open/floor/iron/dark,
|
||||
/area/centcom/ctf)
|
||||
"eF" = (
|
||||
/obj/machinery/capture_the_flag/blue,
|
||||
/obj/machinery/ctf/spawner/blue,
|
||||
/turf/open/floor/circuit/green/off,
|
||||
/area/centcom/ctf)
|
||||
"eH" = (
|
||||
@@ -197,10 +197,9 @@
|
||||
/turf/open/floor/iron/dark,
|
||||
/area/centcom/ctf)
|
||||
"uG" = (
|
||||
/obj/item/ctf{
|
||||
/obj/item/ctf_flag{
|
||||
desc = "A banner used to play capture the flag.";
|
||||
name = "capture flag";
|
||||
team = "WHITE_TEAM"
|
||||
name = "capture flag"
|
||||
},
|
||||
/turf/open/floor/circuit/green/anim,
|
||||
/area/centcom/ctf)
|
||||
@@ -229,7 +228,7 @@
|
||||
/turf/open/floor/iron/dark,
|
||||
/area/centcom/ctf)
|
||||
"xx" = (
|
||||
/obj/machinery/capture_the_flag/green,
|
||||
/obj/machinery/ctf/spawner/green,
|
||||
/turf/open/floor/circuit/green/off,
|
||||
/area/centcom/ctf)
|
||||
"zp" = (
|
||||
@@ -277,7 +276,7 @@
|
||||
/turf/template_noop,
|
||||
/area/template_noop)
|
||||
"DT" = (
|
||||
/obj/machinery/capture_the_flag/red,
|
||||
/obj/machinery/ctf/spawner/red,
|
||||
/turf/open/floor/circuit/green/off,
|
||||
/area/centcom/ctf)
|
||||
"EZ" = (
|
||||
@@ -285,7 +284,7 @@
|
||||
/turf/open/floor/plating,
|
||||
/area/centcom/ctf)
|
||||
"FW" = (
|
||||
/obj/machinery/capture_the_flag/yellow,
|
||||
/obj/machinery/ctf/spawner/yellow,
|
||||
/turf/open/floor/circuit/green/off,
|
||||
/area/centcom/ctf)
|
||||
"Gm" = (
|
||||
|
||||
@@ -217,7 +217,7 @@
|
||||
/turf/open/floor/carpet/blue,
|
||||
/area/centcom/ctf)
|
||||
"oy" = (
|
||||
/obj/machinery/control_point,
|
||||
/obj/machinery/ctf/control_point,
|
||||
/turf/open/floor/circuit/green/off,
|
||||
/area/centcom/ctf)
|
||||
"oK" = (
|
||||
@@ -308,7 +308,7 @@
|
||||
/turf/open/floor/carpet/red,
|
||||
/area/centcom/ctf)
|
||||
"rm" = (
|
||||
/obj/machinery/capture_the_flag/blue,
|
||||
/obj/machinery/ctf/spawner/blue,
|
||||
/turf/open/floor/circuit/green/off,
|
||||
/area/centcom/ctf)
|
||||
"rB" = (
|
||||
@@ -671,7 +671,7 @@
|
||||
/turf/open/floor/cult,
|
||||
/area/centcom/ctf)
|
||||
"LX" = (
|
||||
/obj/machinery/capture_the_flag/yellow,
|
||||
/obj/machinery/ctf/spawner/yellow,
|
||||
/turf/open/floor/circuit/green/off,
|
||||
/area/centcom/ctf)
|
||||
"Mc" = (
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
/turf/open/floor/iron/large,
|
||||
/area/centcom/ctf)
|
||||
"bT" = (
|
||||
/obj/machinery/capture_the_flag/blue,
|
||||
/obj/machinery/ctf/spawner/blue,
|
||||
/turf/open/floor/iron/white,
|
||||
/area/centcom/ctf)
|
||||
"da" = (
|
||||
@@ -387,7 +387,7 @@
|
||||
},
|
||||
/area/centcom/ctf)
|
||||
"KA" = (
|
||||
/obj/machinery/capture_the_flag/red,
|
||||
/obj/machinery/ctf/spawner/red,
|
||||
/turf/open/floor/iron/white,
|
||||
/area/centcom/ctf)
|
||||
"KL" = (
|
||||
@@ -490,8 +490,8 @@
|
||||
/turf/open/floor/iron/large,
|
||||
/area/centcom/ctf)
|
||||
"Qn" = (
|
||||
/obj/item/ctf/blue,
|
||||
/obj/effect/turf_decal/tile/blue/full,
|
||||
/obj/item/ctf_flag/blue,
|
||||
/turf/open/floor/iron/large,
|
||||
/area/centcom/ctf)
|
||||
"Qp" = (
|
||||
@@ -591,8 +591,8 @@
|
||||
/turf/open/floor/iron/large,
|
||||
/area/centcom/ctf)
|
||||
"Ub" = (
|
||||
/obj/item/ctf/red,
|
||||
/obj/effect/turf_decal/tile/red/full,
|
||||
/obj/item/ctf_flag/red,
|
||||
/turf/open/floor/iron/large,
|
||||
/area/centcom/ctf)
|
||||
"UD" = (
|
||||
|
||||
@@ -407,7 +407,7 @@
|
||||
/turf/open/floor/mineral/titanium/blue,
|
||||
/area/shuttle/escape)
|
||||
"rG" = (
|
||||
/obj/machinery/capture_the_flag/medisim/blue,
|
||||
/obj/machinery/ctf/spawner/medisim/blue,
|
||||
/turf/open/indestructible/binary,
|
||||
/area/shuttle/escape/simulation)
|
||||
"rK" = (
|
||||
@@ -417,7 +417,7 @@
|
||||
/turf/open/floor/stone,
|
||||
/area/shuttle/escape/simulation)
|
||||
"rQ" = (
|
||||
/obj/item/ctf/blue/medisim,
|
||||
/obj/item/ctf_flag/blue/medisim,
|
||||
/turf/open/floor/mineral/diamond,
|
||||
/area/shuttle/escape/simulation)
|
||||
"rT" = (
|
||||
@@ -1317,11 +1317,11 @@
|
||||
/turf/open/floor/plating,
|
||||
/area/shuttle/escape)
|
||||
"Zk" = (
|
||||
/obj/machinery/capture_the_flag/medisim/red,
|
||||
/obj/machinery/ctf/spawner/medisim/red,
|
||||
/turf/open/indestructible/binary,
|
||||
/area/shuttle/escape/simulation)
|
||||
"Zv" = (
|
||||
/obj/item/ctf/red/medisim,
|
||||
/obj/item/ctf_flag/red/medisim,
|
||||
/turf/open/floor/mineral/gold,
|
||||
/area/shuttle/escape/simulation)
|
||||
"Zx" = (
|
||||
|
||||
@@ -3,3 +3,9 @@
|
||||
|
||||
/// The game ID for normal ghost CTF
|
||||
#define CTF_GHOST_CTF_GAME_ID "centcom"
|
||||
|
||||
/// The game ID used by the medieval simulator shuttle.
|
||||
#define CTF_MEDISIM_CTF_GAME_ID "medisim"
|
||||
|
||||
/// A list containing all loaded CTF games
|
||||
GLOBAL_LIST_EMPTY(ctf_games)
|
||||
|
||||
@@ -56,6 +56,8 @@
|
||||
|
||||
///from mind/transfer_to. Sent to the receiving mob.
|
||||
#define COMSIG_MOB_MIND_TRANSFERRED_INTO "mob_mind_transferred_into"
|
||||
/// From /mob/proc/ghostize() Called when a mob sucessfully ghosts
|
||||
#define COMSIG_MOB_GHOSTIZED "mob_ghostized"
|
||||
|
||||
///from base of obj/allowed(mob/M): (/obj) returns ACCESS_ALLOWED if mob has id access to the obj
|
||||
#define COMSIG_MOB_TRIED_ACCESS "tried_access"
|
||||
@@ -82,6 +84,8 @@
|
||||
|
||||
///from base of /mob/living/proc/apply_damage(): (damage, damagetype, def_zone, blocked, wound_bonus, bare_wound_bonus, sharpness, attack_direction)
|
||||
#define COMSIG_MOB_APPLY_DAMAGE "mob_apply_damage"
|
||||
///from /mob/living/proc/apply_damage(), works like above but after the damage is actually inflicted: (damage, damagetype, def_zone, blocked, wound_bonus, bare_wound_bonus, sharpness, attack_direction)
|
||||
#define COMSIG_MOB_AFTER_APPLY_DAMAGE "mob_after_apply_damage"
|
||||
///from base of /mob/living/attack_alien(): (user)
|
||||
#define COMSIG_MOB_ATTACK_ALIEN "mob_attack_alien"
|
||||
///from base of /mob/throw_item(): (atom/target)
|
||||
|
||||
@@ -1529,7 +1529,7 @@
|
||||
else if(href_list["ctf_toggle"])
|
||||
if(!check_rights(R_ADMIN))
|
||||
return
|
||||
toggle_id_ctf(usr, "centcom")
|
||||
toggle_id_ctf(usr, CTF_GHOST_CTF_GAME_ID)
|
||||
|
||||
else if(href_list["rebootworld"])
|
||||
if(!check_rights(R_ADMIN))
|
||||
|
||||
@@ -737,7 +737,7 @@
|
||||
set name = "Unload CTF"
|
||||
set desc = "Despawns the majority of CTF"
|
||||
|
||||
toggle_id_ctf(usr, unload=TRUE)
|
||||
toggle_id_ctf(usr, CTF_GHOST_CTF_GAME_ID, unload=TRUE)
|
||||
|
||||
/client/proc/run_empty_query(val as num)
|
||||
set category = "Debug"
|
||||
|
||||
@@ -124,7 +124,7 @@ GLOBAL_DATUM(everyone_a_traitor, /datum/everyone_is_a_traitor_controller)
|
||||
holder.list_fingerprints()
|
||||
|
||||
if("ctfbutton")
|
||||
toggle_id_ctf(holder, "centcom")
|
||||
toggle_id_ctf(holder, CTF_GHOST_CTF_GAME_ID)
|
||||
|
||||
if("tdomereset")
|
||||
var/delete_mobs = tgui_alert(usr, "Clear all mobs?", "Thunderdome Reset", list("Yes", "No", "Cancel"))
|
||||
@@ -578,6 +578,23 @@ GLOBAL_DATUM(everyone_a_traitor, /datum/everyone_is_a_traitor_controller)
|
||||
teamsize--
|
||||
|
||||
return TRUE
|
||||
if("ctf_instagib")
|
||||
if(!is_funmin)
|
||||
return
|
||||
if(GLOB.ctf_games.len <= 0)
|
||||
tgui_alert(usr, "No CTF games are set up.")
|
||||
return
|
||||
var/selected_game = tgui_input_list(usr, "Select a CTF game to ruin.", "Instagib Mode", GLOB.ctf_games)
|
||||
if(isnull(selected_game))
|
||||
return
|
||||
var/datum/ctf_controller/ctf_controller = GLOB.ctf_games[selected_game]
|
||||
var/choice = tgui_alert(usr, "[ctf_controller.instagib_mode ? "Return to standard" : "Enable instagib"] mode?", "Instagib Mode", list("Yes", "No"))
|
||||
if(choice == "No")
|
||||
return
|
||||
ctf_controller.toggle_instagib_mode()
|
||||
message_admins("[key_name_admin(holder)] [ctf_controller.instagib_mode ? "enabled" : "disabled"] instagib mode in CTF game: [selected_game]")
|
||||
log_admin("[key_name_admin(holder)] [ctf_controller.instagib_mode ? "enabled" : "disabled"] instagib mode in CTF game: [selected_game]")
|
||||
|
||||
if(E)
|
||||
E.processing = FALSE
|
||||
if(E.announce_when>0)
|
||||
|
||||
238
code/modules/capture_the_flag/ctf_controller.dm
Normal file
238
code/modules/capture_the_flag/ctf_controller.dm
Normal file
@@ -0,0 +1,238 @@
|
||||
#define CTF_DEFAULT_RESPAWN 15 SECONDS
|
||||
#define CTF_INSTAGIB_RESPAWN 5 SECONDS
|
||||
|
||||
///The CTF controller acts as a manager for an individual CTF game, each CTF game should have its own, the controller should handle all game-wide functionality.
|
||||
/datum/ctf_controller
|
||||
///The ID associated with this CTF game.
|
||||
var/game_id = CTF_GHOST_CTF_GAME_ID
|
||||
///Whether or not this CTF game is running.
|
||||
var/ctf_enabled = FALSE
|
||||
///List of all team_datums participating in this game.
|
||||
var/list/datum/ctf_team/teams = list()
|
||||
///List of all control points used by the game, if they exist.
|
||||
var/list/obj/machinery/ctf/control_point/control_points = list()
|
||||
///List of all barricades that have been destroyed during this CTF game.
|
||||
var/list/obj/effect/ctf/dead_barricade/barricades = list()
|
||||
///How long till players who die can respawn.
|
||||
var/respawn_cooldown = CTF_DEFAULT_RESPAWN
|
||||
///How many points a team needs to win.
|
||||
var/points_to_win = 3
|
||||
///The text shown once this CTF match ends.
|
||||
var/victory_rejoin_text = "Teams have been cleared. Click on the machines to vote to begin another round."
|
||||
///When this CTF match ends should it automatically restart.
|
||||
var/auto_restart = FALSE
|
||||
///Weather or not instagib mode has been enabled.
|
||||
var/instagib_mode = FALSE
|
||||
|
||||
/datum/ctf_controller/New(game_id)
|
||||
. = ..()
|
||||
src.game_id = game_id
|
||||
GLOB.ctf_games[game_id] = src
|
||||
|
||||
/datum/ctf_controller/Destroy(force, ...)
|
||||
GLOB.ctf_games[game_id] = null
|
||||
return ..()
|
||||
|
||||
/datum/ctf_controller/proc/toggle_ctf()
|
||||
if(!ctf_enabled)
|
||||
start_ctf()
|
||||
return TRUE
|
||||
else
|
||||
stop_ctf()
|
||||
return FALSE
|
||||
|
||||
/datum/ctf_controller/proc/start_ctf()
|
||||
if(ctf_enabled)
|
||||
return //CTF is already running, don't notify ghosts again
|
||||
ctf_enabled = TRUE
|
||||
for(var/team in teams)
|
||||
var/obj/machinery/ctf/spawner/spawner = teams[team].spawner
|
||||
notify_ghosts("[spawner.name] has been activated!", source = spawner, action = NOTIFY_ORBIT, header = "CTF has been activated")
|
||||
|
||||
/datum/ctf_controller/proc/stop_ctf()
|
||||
ctf_enabled = FALSE
|
||||
clear_control_points()
|
||||
respawn_barricades()
|
||||
for(var/team in teams)
|
||||
teams[team].reset_team()
|
||||
|
||||
///Unloading CTF removes the map entirely and allows for a new map to be loaded in its place.
|
||||
/datum/ctf_controller/proc/unload_ctf()
|
||||
if(game_id != CTF_GHOST_CTF_GAME_ID)
|
||||
return //At present we only support unloading standard centcom ctf, if we intend to support ctf unloading elsewhere then this proc will need to be ammended.
|
||||
stop_ctf()
|
||||
new /obj/effect/landmark/ctf(get_turf(GLOB.ctf_spawner))
|
||||
|
||||
///Some CTF maps may require alternate rulesets, this proc is called by the medisim spawners and CTF maploading.
|
||||
/datum/ctf_controller/proc/setup_rules(
|
||||
points_to_win = 3,
|
||||
victory_rejoin_text = "Teams have been cleared. Click on the machines to vote to begin another round.",
|
||||
auto_restart = FALSE,
|
||||
)
|
||||
src.points_to_win = points_to_win
|
||||
src.victory_rejoin_text = victory_rejoin_text
|
||||
src.auto_restart = auto_restart
|
||||
|
||||
///Add an additional team to the current CTF game.
|
||||
/datum/ctf_controller/proc/add_team(obj/machinery/ctf/spawner/spawner)
|
||||
if(!isnull(teams[spawner.team]))
|
||||
return //CTF currently only supports one spawn point per team, if you want to add a map that uses more you'll need to modify add_team/remove_team and turn the spawner var on the team itself into a list
|
||||
teams[spawner.team] = new /datum/ctf_team(spawner)
|
||||
|
||||
///Called when a spawner is deleted, removes the team from this datum.
|
||||
/datum/ctf_controller/proc/remove_team(team_color)
|
||||
if(isnull(teams[team_color]))
|
||||
return //Cannot delete a team that doesn't exist
|
||||
QDEL_NULL(teams[team_color])
|
||||
teams -= team_color
|
||||
|
||||
///Adds a player and a reference to their player component to the corresponding team.
|
||||
/datum/ctf_controller/proc/add_player(team_color, ckey, datum/component/ctf_player/new_team_member)
|
||||
teams[team_color].team_members[ckey] = new_team_member
|
||||
|
||||
///Returns a reference to a players component (if it exists) when provided with a player's ckey
|
||||
/datum/ctf_controller/proc/get_player_component(team_color, ckey)
|
||||
return teams[team_color].team_members[ckey]
|
||||
|
||||
///Returns a list of all players in the provided team.
|
||||
/datum/ctf_controller/proc/get_players(team_color)
|
||||
return teams[team_color].team_members
|
||||
|
||||
///Returns a list of all players in all teams.
|
||||
/datum/ctf_controller/proc/get_all_players()
|
||||
var/list/players = list()
|
||||
for(var/team in teams)
|
||||
players += get_players(team)
|
||||
return players
|
||||
|
||||
///Identifies if the provided team is a valid team to join for the provided player.
|
||||
/datum/ctf_controller/proc/team_valid_to_join(team_color, mob/user)
|
||||
var/list/friendly_team_members = get_players(team_color)
|
||||
for(var/team in teams)
|
||||
if(team == team_color)
|
||||
continue
|
||||
var/list/enemy_team_members = get_players(team)
|
||||
if(user.ckey in enemy_team_members)
|
||||
to_chat(user, span_warning("No switching teams while the round is going!"))
|
||||
return FALSE
|
||||
if(friendly_team_members.len > enemy_team_members.len)
|
||||
to_chat(user, span_warning("[team_color] has more team members than [team]! Try joining [team] team to even things up."))
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
///Called when a flag is captured by the provided team. Messages players telling them who scored a point and if points are high enough declares victory.
|
||||
/datum/ctf_controller/proc/capture_flag(team_color, mob/living/user, team_span, obj/item/ctf_flag/flag)
|
||||
teams[team_color].score_points(flag.flag_value)
|
||||
message_all_teams("<span class='userdanger [team_span]'>[user.real_name] has captured \the [flag], scoring a point for [team_color] team! They now have [get_points(team_color)]/[points_to_win] points!</span>")
|
||||
if(get_points(team_color) >= points_to_win)
|
||||
victory(team_color)
|
||||
|
||||
///Called when points are scored at a control point. Messages players telling them when a team is half way to winning and if points are high enough declares victory.
|
||||
/datum/ctf_controller/proc/control_point_scoring(team_color, points)
|
||||
teams[team_color].score_points(points)
|
||||
if(get_points(team_color) == points_to_win/2)
|
||||
message_all_teams("<span class='userdanger [teams[team_color].team_span]'>[team_color] is half way to winning! they only need [points_to_win/2] more points to win!</span>")
|
||||
if(get_points(team_color) >= points_to_win)
|
||||
victory(team_color)
|
||||
|
||||
///Returns the current amount of points the provided team has.
|
||||
/datum/ctf_controller/proc/get_points(team_color)
|
||||
return teams[team_color].points
|
||||
|
||||
///Ends the current CTF game and informs all players which team won. Restarts CTF if auto_restart is enabled.
|
||||
/datum/ctf_controller/proc/victory(winning_team)
|
||||
ctf_enabled = FALSE
|
||||
clear_control_points()
|
||||
respawn_barricades()
|
||||
var/datum/ctf_team/winning_ctf_team = teams[winning_team]
|
||||
for(var/team in teams)
|
||||
var/datum/ctf_team/ctf_team = teams[team]
|
||||
ctf_team.message_team("<span class='narsie [winning_ctf_team.team_span]'>[winning_team] team wins!</span>")
|
||||
ctf_team.message_team(span_userdanger(victory_rejoin_text))
|
||||
ctf_team.reset_team()
|
||||
if(auto_restart)
|
||||
toggle_id_ctf(null, game_id, TRUE)
|
||||
|
||||
///Marks all control points as neutral, called when a CTF match ends.
|
||||
/datum/ctf_controller/proc/clear_control_points()
|
||||
for(var/obj/machinery/ctf/control_point/control_point in control_points)
|
||||
control_point.clear_point()
|
||||
|
||||
///Respawns all barricades destroyed during the current CTF game, called when the match ends.
|
||||
/datum/ctf_controller/proc/respawn_barricades()
|
||||
for(var/obj/effect/ctf/dead_barricade/barricade in barricades)
|
||||
barricade.respawn()
|
||||
barricades = list()
|
||||
|
||||
///Sends a message to all players in all CTF teams in this game.
|
||||
/datum/ctf_controller/proc/message_all_teams(message)
|
||||
for(var/team in teams)
|
||||
teams[team].message_team(message)
|
||||
|
||||
///Enables and disables instagib mode in this game. During instagib mode respawns are faster, players are faster and people die faster (instant).
|
||||
/datum/ctf_controller/proc/toggle_instagib_mode()
|
||||
if(!instagib_mode) // Normal > Instagib
|
||||
for(var/team in teams)
|
||||
var/datum/ctf_team/ctf_team = teams[team]
|
||||
ctf_team.spawner.ctf_gear = ctf_team.spawner.instagib_gear
|
||||
respawn_cooldown = CTF_INSTAGIB_RESPAWN
|
||||
else //Instagib > Normal
|
||||
for(var/team in teams)
|
||||
var/datum/ctf_team/ctf_team = teams[team]
|
||||
ctf_team.spawner.ctf_gear = ctf_team.spawner.default_gear
|
||||
respawn_cooldown = CTF_DEFAULT_RESPAWN
|
||||
instagib_mode = !instagib_mode
|
||||
|
||||
///A datum that holds details about individual CTF teams, any team specific CTF functionality should be implimented here.
|
||||
/datum/ctf_team
|
||||
///Reference to the spawn point that this team uses.
|
||||
var/obj/machinery/ctf/spawner/spawner
|
||||
///What color this team is, also acts as a team name.
|
||||
var/team_color
|
||||
///Total score that this team currently has.
|
||||
var/points = 0
|
||||
///Assoc list containing a list of team members ckeys and the associated ctf_player components.
|
||||
var/list/team_members = list()
|
||||
///Span used for messages sent to this team.
|
||||
var/team_span = ""
|
||||
|
||||
/datum/ctf_team/New(obj/machinery/ctf/spawner/spawner)
|
||||
. = ..()
|
||||
src.spawner = spawner
|
||||
team_color = spawner.team
|
||||
team_span = spawner.team_span
|
||||
|
||||
///If the team is destroyed all players in that team need their componenet removed.
|
||||
/datum/ctf_team/Destroy(force, ...)
|
||||
for(var/player in team_members)
|
||||
var/datum/component/ctf_player/ctf_player = team_members[player]
|
||||
ctf_player.end_game()
|
||||
return ..()
|
||||
|
||||
///Increases this teams number of points by the provided amount.
|
||||
/datum/ctf_team/proc/score_points(points_scored)
|
||||
points += points_scored
|
||||
|
||||
///Resets this teams score and clears its member list. All members will be dusted and have their player componenet removed.
|
||||
/datum/ctf_team/proc/reset_team()
|
||||
points = 0
|
||||
for(var/player in team_members)
|
||||
var/datum/component/ctf_player/ctf_player = team_members[player]
|
||||
ctf_player.end_game()
|
||||
team_members = list()
|
||||
|
||||
///Sends a message to all players in this team.
|
||||
/datum/ctf_team/proc/message_team(message)
|
||||
for(var/player in team_members)
|
||||
var/datum/component/ctf_player/ctf_player = team_members[player]
|
||||
ctf_player.send_message(message)
|
||||
|
||||
///Creates a CTF game with the provided teeam ID then returns a reference to the new controller. If a controller already exists provides a reference to it.
|
||||
/proc/create_ctf_game(game_id)
|
||||
if(GLOB.ctf_games[game_id])
|
||||
return GLOB.ctf_games[game_id]
|
||||
var/datum/ctf_controller/CTF = new(game_id)
|
||||
return CTF
|
||||
|
||||
#undef CTF_DEFAULT_RESPAWN
|
||||
#undef CTF_INSTAGIB_RESPAWN
|
||||
@@ -16,7 +16,7 @@
|
||||
if(is_ctf_target(target) && blocked == FALSE)
|
||||
if(iscarbon(target))
|
||||
var/mob/living/carbon/target_mob = target
|
||||
target_mob.adjustBruteLoss(150, 0)
|
||||
target_mob.apply_damage(150)
|
||||
return BULLET_ACT_HIT
|
||||
|
||||
/obj/item/ammo_box/magazine/recharge/ctf
|
||||
|
||||
@@ -3,11 +3,174 @@
|
||||
#define BLUE_TEAM "Blue"
|
||||
#define GREEN_TEAM "Green"
|
||||
#define YELLOW_TEAM "Yellow"
|
||||
#define FLAG_RETURN_TIME 200 // 20 seconds
|
||||
#define INSTAGIB_RESPAWN 50 //5 seconds
|
||||
#define DEFAULT_RESPAWN 150 //15 seconds
|
||||
#define FLAG_RETURN_TIME 20 SECONDS
|
||||
|
||||
/obj/item/ctf
|
||||
///Base CTF machines, if spawned in creates a CTF game with the provided game_id unless one already exists. If one exists associates itself with it.
|
||||
/obj/machinery/ctf
|
||||
name = "CTF Controller"
|
||||
desc = "Used for running friendly games of capture the flag."
|
||||
icon = 'icons/obj/device.dmi'
|
||||
icon_state = "syndbeacon"
|
||||
density = TRUE
|
||||
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
|
||||
///The game ID that this CTF machine is associated with.
|
||||
var/game_id = CTF_GHOST_CTF_GAME_ID
|
||||
///Reference to the CTF controller that this machine operates under.
|
||||
var/datum/ctf_controller/ctf_game
|
||||
|
||||
/obj/machinery/ctf/Initialize(mapload)
|
||||
. = ..()
|
||||
ctf_game = GLOB.ctf_games[game_id]
|
||||
if(isnull(ctf_game))
|
||||
ctf_game = create_ctf_game(game_id)
|
||||
|
||||
///A spawn point for CTF, ghosts can interact with this to vote for CTF or spawn in if a game is running.
|
||||
/obj/machinery/ctf/spawner
|
||||
///The team that this spawner is associated with.
|
||||
var/team = WHITE_TEAM
|
||||
///The span applied to messages associated with this team.
|
||||
var/team_span = ""
|
||||
///assoc list for classes. If there's only one, it'll just equip. Otherwise, it lets you pick which outfit!
|
||||
var/list/ctf_gear = list("Rifleman" = /datum/outfit/ctf, "Assaulter" = /datum/outfit/ctf/assault, "Marksman" = /datum/outfit/ctf/marksman)
|
||||
var/list/instagib_gear = list("Instagib" = /datum/outfit/ctf/instagib)
|
||||
///Var that holds a copy of ctf_gear so that if instagib mode is enabled (overwritting ctf_gear) it can be reverted with this var.
|
||||
var/list/default_gear = list()
|
||||
///The powerup dropped when a player spawned by this controller dies.
|
||||
var/ammo_type = /obj/effect/powerup/ammo/ctf
|
||||
// Fast paced gameplay, no real time for burn infections.
|
||||
var/player_traits = list(TRAIT_NEVER_WOUNDED)
|
||||
|
||||
/obj/machinery/ctf/spawner/Initialize(mapload)
|
||||
. = ..()
|
||||
ctf_game.add_team(src)
|
||||
SSpoints_of_interest.make_point_of_interest(src)
|
||||
default_gear = ctf_gear
|
||||
|
||||
/obj/machinery/ctf/spawner/Destroy()
|
||||
ctf_game.remove_team(team)
|
||||
return ..()
|
||||
|
||||
/obj/machinery/ctf/spawner/red
|
||||
name = "Red CTF Controller"
|
||||
icon_state = "syndbeacon"
|
||||
team = RED_TEAM
|
||||
team_span = "redteamradio"
|
||||
ctf_gear = list("Rifleman" = /datum/outfit/ctf/red, "Assaulter" = /datum/outfit/ctf/assault/red, "Marksman" = /datum/outfit/ctf/marksman/red)
|
||||
instagib_gear = list("Instagib" = /datum/outfit/ctf/red/instagib)
|
||||
|
||||
/obj/machinery/ctf/spawner/blue
|
||||
name = "Blue CTF Controller"
|
||||
icon_state = "bluebeacon"
|
||||
team = BLUE_TEAM
|
||||
team_span = "blueteamradio"
|
||||
ctf_gear = list("Rifleman" = /datum/outfit/ctf/blue, "Assaulter" = /datum/outfit/ctf/assault/blue, "Marksman" = /datum/outfit/ctf/marksman/blue)
|
||||
instagib_gear = list("Instagib" = /datum/outfit/ctf/blue/instagib)
|
||||
|
||||
/obj/machinery/ctf/spawner/green
|
||||
name = "Green CTF Controller"
|
||||
icon_state = "greenbeacon"
|
||||
team = GREEN_TEAM
|
||||
team_span = "greenteamradio"
|
||||
ctf_gear = list("Rifleman" = /datum/outfit/ctf/green, "Assaulter" = /datum/outfit/ctf/assault/green, "Marksman" = /datum/outfit/ctf/marksman/green)
|
||||
instagib_gear = list("Instagib" = /datum/outfit/ctf/green/instagib)
|
||||
|
||||
/obj/machinery/ctf/spawner/yellow
|
||||
name = "Yellow CTF Controller"
|
||||
icon_state = "yellowbeacon"
|
||||
team = YELLOW_TEAM
|
||||
team_span = "yellowteamradio"
|
||||
ctf_gear = list("Rifleman" = /datum/outfit/ctf/yellow, "Assaulter" = /datum/outfit/ctf/assault/yellow, "Marksman" = /datum/outfit/ctf/marksman/yellow)
|
||||
instagib_gear = list("Instagib" = /datum/outfit/ctf/yellow/instagib)
|
||||
|
||||
/obj/machinery/ctf/spawner/attack_ghost(mob/user)
|
||||
if(ctf_game.ctf_enabled == FALSE)
|
||||
if(user.client && user.client.holder)
|
||||
var/response = tgui_alert(user, "Enable this CTF game?", "CTF", list("Yes", "No"))
|
||||
if(response == "Yes")
|
||||
toggle_id_ctf(user, game_id)
|
||||
return
|
||||
|
||||
if(!(GLOB.ghost_role_flags & GHOSTROLE_MINIGAME))
|
||||
to_chat(user, span_warning("CTF has been temporarily disabled by admins."))
|
||||
return
|
||||
get_ctf_voting_controller(game_id).vote(user)
|
||||
return
|
||||
if(!SSticker.HasRoundStarted())
|
||||
to_chat(user, span_warning("Please wait until the round has started."))
|
||||
return
|
||||
if(user.ckey in ctf_game.get_players(team))
|
||||
var/datum/component/ctf_player/ctf_player_component = ctf_game.get_player_component(team, user.ckey)
|
||||
var/client/new_team_member = user.client
|
||||
if(isnull(ctf_player_component))
|
||||
spawn_team_member(new_team_member) //Player managed to lose their player component despite being on a team
|
||||
else
|
||||
if(ctf_player_component.can_respawn)
|
||||
spawn_team_member(new_team_member, ctf_player_component)
|
||||
else
|
||||
to_chat(user, span_warning("You cannot respawn yet!"))
|
||||
return
|
||||
if(ctf_game.team_valid_to_join(team, user))
|
||||
to_chat(user, span_userdanger("You are now a member of [src.team]. Get the enemy flag and bring it back to your team's controller!"))
|
||||
ctf_game.add_player(team, user.ckey)
|
||||
var/client/new_team_member = user.client
|
||||
spawn_team_member(new_team_member)
|
||||
|
||||
/obj/machinery/ctf/spawner/Topic(href, href_list)
|
||||
if(href_list["join"])
|
||||
var/mob/dead/observer/ghost = usr
|
||||
if(istype(ghost))
|
||||
attack_ghost(ghost)
|
||||
|
||||
/obj/machinery/ctf/spawner/proc/spawn_team_member(client/new_team_member, datum/component/ctf_player/ctf_player_component)
|
||||
var/datum/outfit/chosen_class
|
||||
|
||||
if(ctf_gear.len == 1) //no choices to make
|
||||
for(var/key in ctf_gear)
|
||||
chosen_class = ctf_gear[key]
|
||||
|
||||
else //There's a choice to make, present a radial menu
|
||||
var/list/display_classes = list()
|
||||
|
||||
for(var/key in ctf_gear)
|
||||
var/datum/outfit/ctf/class = ctf_gear[key]
|
||||
var/datum/radial_menu_choice/option = new
|
||||
option.image = image(icon = initial(class.icon), icon_state = initial(class.icon_state))
|
||||
option.info = "<span class='boldnotice'>[initial(class.class_description)]</span>"
|
||||
display_classes[key] = option
|
||||
|
||||
sort_list(display_classes)
|
||||
var/choice = show_radial_menu(new_team_member.mob, src, display_classes, radius = 38)
|
||||
if(!choice || !(GLOB.ghost_role_flags & GHOSTROLE_MINIGAME) || !isobserver(new_team_member.mob) || ctf_game.ctf_enabled == FALSE || !(new_team_member.ckey in ctf_game.get_players(team)))
|
||||
return //picked nothing, admin disabled it, cheating to respawn faster, cheating to respawn... while in game?,
|
||||
//there isn't a game going on any more, you are no longer a member of this team (perhaps a new match already started?)
|
||||
|
||||
chosen_class = ctf_gear[choice]
|
||||
|
||||
var/turf/spawn_point = pick(get_adjacent_open_turfs(get_turf(src)))
|
||||
var/mob/living/carbon/human/player_mob = new(spawn_point)
|
||||
new_team_member.prefs.safe_transfer_prefs_to(player_mob, is_antag = TRUE)
|
||||
if(player_mob.dna.species.outfit_important_for_life)
|
||||
player_mob.set_species(/datum/species/human)
|
||||
player_mob.ckey = new_team_member.ckey
|
||||
if(isnull(ctf_player_component))
|
||||
var/datum/component/ctf_player/player_component = player_mob.mind.AddComponent(/datum/component/ctf_player, team, ctf_game, ammo_type)
|
||||
ctf_game.add_player(team, player_mob.ckey, player_component)
|
||||
else
|
||||
player_mob.mind.TakeComponent(ctf_player_component)
|
||||
player_mob.faction += team
|
||||
player_mob.equipOutfit(chosen_class)
|
||||
player_mob.add_traits(player_traits, CAPTURE_THE_FLAG_TRAIT)
|
||||
return player_mob //used in medisim_game.dm
|
||||
|
||||
/obj/machinery/ctf/spawner/attackby(obj/item/item, mob/user, params)
|
||||
if(istype(item, /obj/item/ctf_flag))
|
||||
var/obj/item/ctf_flag/flag = item
|
||||
if(flag.team != team)
|
||||
ctf_game.capture_flag(team, user, team_span, flag)
|
||||
flag.reset_flag(capture = TRUE) //This might be buggy, confirm and fix if it is.
|
||||
|
||||
///A flag used for the CTF minigame.
|
||||
/obj/item/ctf_flag
|
||||
name = "banner"
|
||||
icon = 'icons/obj/banner.dmi'
|
||||
icon_state = "banner"
|
||||
@@ -23,45 +186,57 @@
|
||||
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
|
||||
anchored = TRUE
|
||||
item_flags = SLOWS_WHILE_IN_HAND
|
||||
///Team that this flag is associated with, a team cannot capture its own flag.
|
||||
var/team = WHITE_TEAM
|
||||
///Var used for tracking when this flag should be returned to base.
|
||||
var/reset_cooldown = 0
|
||||
///Can anyone pick up the flag or only players of the opposing team.
|
||||
var/anyonecanpickup = TRUE
|
||||
///Reference to an object that's location is used when this flag needs to respawn.
|
||||
var/obj/effect/ctf/flag_reset/reset
|
||||
var/reset_path = /obj/effect/ctf/flag_reset
|
||||
/// Which area we announce updates on the flag to. Should just generally be the area of the arena.
|
||||
var/game_area = /area/centcom/ctf
|
||||
///Game_id that this flag is associated with.
|
||||
var/game_id = CTF_GHOST_CTF_GAME_ID
|
||||
///Reference to the CTF controller associated with the above game ID.
|
||||
var/datum/ctf_controller/ctf_game
|
||||
///How many points this flag is worth when captured.
|
||||
var/flag_value = 1
|
||||
|
||||
/obj/item/ctf/Destroy()
|
||||
/obj/item/ctf_flag/Initialize(mapload)
|
||||
. = ..()
|
||||
if(isnull(reset))
|
||||
reset = new(get_turf(src))
|
||||
reset.flag = src
|
||||
reset.icon_state = icon_state
|
||||
reset.name = "[name] landmark"
|
||||
reset.desc = "This is where \the [name] will respawn in a game of CTF"
|
||||
return INITIALIZE_HINT_LATELOAD
|
||||
|
||||
/obj/item/ctf_flag/LateInitialize()
|
||||
. = ..()
|
||||
ctf_game = GLOB.ctf_games[game_id] //Flags don't create ctf games by themselves since you can get ctf flags from christmas trees.
|
||||
|
||||
/obj/item/ctf_flag/Destroy()
|
||||
QDEL_NULL(reset)
|
||||
return ..()
|
||||
|
||||
/obj/item/ctf/Initialize(mapload)
|
||||
. = ..()
|
||||
if(!reset)
|
||||
reset = new reset_path(get_turf(src))
|
||||
reset.flag = src
|
||||
|
||||
/obj/item/ctf/process()
|
||||
/obj/item/ctf_flag/process()
|
||||
if(is_ctf_target(loc)) //pickup code calls temporary drops to test things out, we need to make sure the flag doesn't reset from
|
||||
return PROCESS_KILL
|
||||
if(world.time > reset_cooldown)
|
||||
reset_flag()
|
||||
|
||||
/obj/item/ctf/proc/reset_flag(capture = FALSE)
|
||||
/obj/item/ctf_flag/proc/reset_flag(capture = FALSE)
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
|
||||
var/turf/our_turf = get_turf(src.reset)
|
||||
if(!our_turf)
|
||||
return TRUE
|
||||
forceMove(our_turf)
|
||||
for(var/mob/M in GLOB.player_list)
|
||||
var/area/mob_area = get_area(M)
|
||||
if(istype(mob_area, game_area))
|
||||
if(!capture)
|
||||
to_chat(M, span_userdanger("[src] has been returned to the base!"))
|
||||
if(!capture && !isnull(ctf_game))
|
||||
ctf_game.message_all_teams("[src] has been returned to the base!")
|
||||
|
||||
//working with attack hand feels like taking my brain and putting it through an industrial pill press so i'm gonna be a bit liberal with the comments
|
||||
/obj/item/ctf/attack_hand(mob/living/user, list/modifiers)
|
||||
//working with attack hand feels like taking my brain and putting it through an industrial pill press so i'm gonna be a bit liberal with the comments //Mood
|
||||
/obj/item/ctf_flag/attack_hand(mob/living/user, list/modifiers)
|
||||
//pre normal check item stuff, this is for our special flag checks
|
||||
if(!is_ctf_target(user) && !anyonecanpickup)
|
||||
to_chat(user, span_warning("Non-players shouldn't be moving the flag!"))
|
||||
@@ -72,10 +247,8 @@
|
||||
if(loc == user)
|
||||
if(!user.dropItemToGround(src))
|
||||
return
|
||||
for(var/mob/M in GLOB.player_list)
|
||||
var/area/mob_area = get_area(M)
|
||||
if(istype(mob_area, game_area))
|
||||
to_chat(M, span_userdanger("\The [initial(src.name)] has been taken!"))
|
||||
if(!isnull(ctf_game))
|
||||
ctf_game.message_all_teams(span_userdanger("\The [initial(name)] has been taken!"))
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
anchored = FALSE // Hacky usage that bypasses set_anchored(), because normal checks need this to be FALSE to pass
|
||||
. = ..() //this is the actual normal item checks
|
||||
@@ -86,60 +259,58 @@
|
||||
user.set_anchored(TRUE)
|
||||
user.status_flags &= ~CANPUSH
|
||||
|
||||
/obj/item/ctf/dropped(mob/user)
|
||||
/obj/item/ctf_flag/dropped(mob/user)
|
||||
..()
|
||||
user.anchored = FALSE // Hacky usage that bypasses set_anchored()
|
||||
user.status_flags |= CANPUSH
|
||||
reset_cooldown = world.time + 20 SECONDS
|
||||
START_PROCESSING(SSobj, src)
|
||||
for(var/mob/M in GLOB.player_list)
|
||||
var/area/mob_area = get_area(M)
|
||||
if(istype(mob_area, game_area))
|
||||
to_chat(M, span_userdanger("\The [initial(name)] has been dropped!"))
|
||||
if(!isnull(ctf_game))
|
||||
ctf_game.message_all_teams(span_userdanger("\The [initial(name)] has been dropped!"))
|
||||
anchored = TRUE // Avoid directly assigning to anchored and prefer to use set_anchored() on normal circumstances.
|
||||
|
||||
|
||||
/obj/item/ctf/red
|
||||
/obj/item/ctf_flag/red
|
||||
name = "red flag"
|
||||
icon_state = "banner-red"
|
||||
inhand_icon_state = "banner-red"
|
||||
desc = "A red banner used to play capture the flag."
|
||||
team = RED_TEAM
|
||||
reset_path = /obj/effect/ctf/flag_reset/red
|
||||
|
||||
|
||||
/obj/item/ctf/blue
|
||||
/obj/item/ctf_flag/blue
|
||||
name = "blue flag"
|
||||
icon_state = "banner-blue"
|
||||
inhand_icon_state = "banner-blue"
|
||||
desc = "A blue banner used to play capture the flag."
|
||||
team = BLUE_TEAM
|
||||
reset_path = /obj/effect/ctf/flag_reset/blue
|
||||
|
||||
/obj/item/ctf/green
|
||||
/obj/item/ctf_flag/green
|
||||
name = "green flag"
|
||||
icon_state = "banner-green"
|
||||
inhand_icon_state = "banner-green"
|
||||
desc = "A green banner used to play capture the flag."
|
||||
team = GREEN_TEAM
|
||||
reset_path = /obj/effect/ctf/flag_reset/green
|
||||
|
||||
|
||||
/obj/item/ctf/yellow
|
||||
/obj/item/ctf_flag/yellow
|
||||
name = "yellow flag"
|
||||
icon_state = "banner-yellow"
|
||||
inhand_icon_state = "banner-yellow"
|
||||
desc = "A yellow banner used to play capture the flag."
|
||||
team = YELLOW_TEAM
|
||||
reset_path = /obj/effect/ctf/flag_reset/yellow
|
||||
|
||||
/obj/effect/ctf
|
||||
density = FALSE
|
||||
anchored = TRUE
|
||||
invisibility = INVISIBILITY_OBSERVER
|
||||
alpha = 100
|
||||
resistance_flags = INDESTRUCTIBLE
|
||||
|
||||
/obj/effect/ctf/flag_reset
|
||||
name = "banner landmark"
|
||||
icon = 'icons/obj/banner.dmi'
|
||||
icon_state = "banner"
|
||||
desc = "This is where a banner with Nanotrasen's logo on it would go."
|
||||
desc = "This is where a CTF flag will respawn."
|
||||
layer = LOW_ITEM_LAYER
|
||||
var/obj/item/ctf/flag
|
||||
var/obj/item/ctf_flag/flag
|
||||
|
||||
/obj/effect/ctf/flag_reset/Destroy()
|
||||
if(flag)
|
||||
@@ -147,394 +318,62 @@
|
||||
flag = null
|
||||
return ..()
|
||||
|
||||
/obj/effect/ctf/flag_reset/red
|
||||
name = "red flag landmark"
|
||||
icon_state = "banner-red"
|
||||
desc = "This is where a red banner used to play capture the flag \
|
||||
would go."
|
||||
///Control point used for CTF for king of the hill or control point game modes. Teams need to maintain control of the point for a set time to win.
|
||||
/obj/machinery/ctf/control_point
|
||||
name = "control point"
|
||||
desc = "You should capture this"
|
||||
icon = 'icons/obj/machines/dominator.dmi'
|
||||
icon_state = "dominator"
|
||||
///Team that is currently controlling this point.
|
||||
var/controlling_team
|
||||
///Amount of points generated per second by this control point while captured.
|
||||
var/point_rate = 1
|
||||
|
||||
/obj/effect/ctf/flag_reset/blue
|
||||
name = "blue flag landmark"
|
||||
icon_state = "banner-blue"
|
||||
desc = "This is where a blue banner used to play capture the flag \
|
||||
would go."
|
||||
|
||||
/obj/effect/ctf/flag_reset/green
|
||||
name = "green flag landmark"
|
||||
icon_state = "banner"
|
||||
desc = "This is where a green banner used to play capture the flag \
|
||||
would go."
|
||||
|
||||
/obj/effect/ctf/flag_reset/yellow
|
||||
name = "yellow flag landmark"
|
||||
icon_state = "banner"
|
||||
desc = "This is where a yellow banner used to play capture the flag \
|
||||
would go."
|
||||
|
||||
#define CTF_LOADING_UNLOADED 0
|
||||
#define CTF_LOADING_LOADING 1
|
||||
#define CTF_LOADING_LOADED 2
|
||||
|
||||
/proc/toggle_id_ctf(user, activated_id, automated = FALSE, unload = FALSE)
|
||||
var/static/loading = CTF_LOADING_UNLOADED
|
||||
if(unload == TRUE)
|
||||
log_admin("[key_name_admin(user)] is attempting to unload CTF.")
|
||||
message_admins("[key_name_admin(user)] is attempting to unload CTF.")
|
||||
if(loading == CTF_LOADING_UNLOADED)
|
||||
to_chat(user, span_warning("CTF cannot be unloaded if it was not loaded in the first place"))
|
||||
return
|
||||
to_chat(user, span_warning("CTF is being unloaded"))
|
||||
for(var/obj/machinery/capture_the_flag/CTF as anything in GLOB.ctf_panel.ctf_machines)
|
||||
CTF.unload()
|
||||
log_admin("[key_name_admin(user)] has unloaded CTF.")
|
||||
message_admins("[key_name_admin(user)] has unloaded CTF.")
|
||||
loading = CTF_LOADING_UNLOADED
|
||||
return
|
||||
switch (loading)
|
||||
if (CTF_LOADING_UNLOADED)
|
||||
if (isnull(GLOB.ctf_spawner))
|
||||
to_chat(user, span_boldwarning("Couldn't find a CTF spawner. Call a maintainer!"))
|
||||
return
|
||||
|
||||
to_chat(user, span_notice("Loading CTF..."))
|
||||
|
||||
loading = CTF_LOADING_LOADING
|
||||
if(!GLOB.ctf_spawner.load_map(user))
|
||||
to_chat(user, span_warning("CTF loading was cancelled"))
|
||||
loading = CTF_LOADING_UNLOADED
|
||||
return
|
||||
loading = CTF_LOADING_LOADED
|
||||
if (CTF_LOADING_LOADING)
|
||||
to_chat(user, span_warning("CTF is loading!"))
|
||||
|
||||
return
|
||||
|
||||
var/ctf_enabled = FALSE
|
||||
var/area/A
|
||||
for(var/obj/machinery/capture_the_flag/CTF as anything in GLOB.ctf_panel.ctf_machines)
|
||||
if(activated_id != CTF.game_id)
|
||||
continue
|
||||
ctf_enabled = CTF.toggle_ctf()
|
||||
A = get_area(CTF)
|
||||
for(var/obj/machinery/power/emitter/E in A)
|
||||
E.active = ctf_enabled
|
||||
if(user)
|
||||
message_admins("[key_name_admin(user)] has [ctf_enabled ? "enabled" : "disabled"] CTF!")
|
||||
else if(automated)
|
||||
message_admins("CTF has finished a round and automatically restarted.")
|
||||
notify_ghosts("CTF has automatically restarted after a round finished in [A]!",'sound/effects/ghost2.ogg')
|
||||
else
|
||||
message_admins("The players have spoken! Voting has enabled CTF!")
|
||||
if(!automated)
|
||||
notify_ghosts("CTF has been [ctf_enabled? "enabled" : "disabled"] in [A]!",'sound/effects/ghost2.ogg')
|
||||
|
||||
#undef CTF_LOADING_UNLOADED
|
||||
#undef CTF_LOADING_LOADING
|
||||
#undef CTF_LOADING_LOADED
|
||||
|
||||
/obj/machinery/capture_the_flag
|
||||
name = "CTF Controller"
|
||||
desc = "Used for running friendly games of capture the flag."
|
||||
icon = 'icons/obj/device.dmi'
|
||||
icon_state = "syndbeacon"
|
||||
density = TRUE
|
||||
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
|
||||
var/game_id = CTF_GHOST_CTF_GAME_ID
|
||||
|
||||
var/victory_rejoin_text = "<span class='userdanger'>Teams have been cleared. Click on the machines to vote to begin another round.</span>"
|
||||
var/team = WHITE_TEAM
|
||||
var/team_span = ""
|
||||
//Capture the Flag scoring
|
||||
var/points = 0
|
||||
var/points_to_win = 3
|
||||
var/respawn_cooldown = DEFAULT_RESPAWN
|
||||
//Capture Point/King of the Hill scoring
|
||||
var/control_points = 0
|
||||
var/control_points_to_win = 180
|
||||
var/list/team_members = list()
|
||||
///assoc list: mob = outfit datum (class)
|
||||
var/list/spawned_mobs = list()
|
||||
var/list/recently_dead_ckeys = list()
|
||||
var/ctf_enabled = FALSE
|
||||
///assoc list for classes. If there's only one, it'll just equip. Otherwise, it lets you pick which outfit!
|
||||
var/list/ctf_gear = list("Rifleman" = /datum/outfit/ctf, "Assaulter" = /datum/outfit/ctf/assault, "Marksman" = /datum/outfit/ctf/marksman)
|
||||
var/list/instagib_gear = list("Instagib" = /datum/outfit/ctf/instagib)
|
||||
var/list/default_gear
|
||||
var/ammo_type = /obj/effect/powerup/ammo/ctf
|
||||
// Fast paced gameplay, no real time for burn infections.
|
||||
var/player_traits = list(TRAIT_NEVER_WOUNDED)
|
||||
|
||||
var/list/dead_barricades = list()
|
||||
|
||||
var/static/arena_reset = FALSE
|
||||
var/game_area = /area/centcom/ctf
|
||||
|
||||
/// This variable is needed because of ctf shitcode + we need to make sure we're deleting the current ctf landmark that spawned us in and not a new one.
|
||||
var/obj/effect/landmark/ctf/ctf_landmark
|
||||
|
||||
/obj/machinery/capture_the_flag/Initialize(mapload)
|
||||
/obj/machinery/ctf/control_point/Initialize(mapload)
|
||||
. = ..()
|
||||
GLOB.ctf_panel.ctf_machines += src
|
||||
SSpoints_of_interest.make_point_of_interest(src)
|
||||
default_gear = ctf_gear
|
||||
ctf_landmark = GLOB.ctf_spawner
|
||||
ctf_game.control_points += src
|
||||
|
||||
/obj/machinery/capture_the_flag/Destroy()
|
||||
ctf_landmark = null
|
||||
GLOB.ctf_panel.ctf_machines -= src
|
||||
/obj/machinery/ctf/control_point/Destroy()
|
||||
ctf_game.control_points.Remove(src)
|
||||
return ..()
|
||||
|
||||
/obj/machinery/capture_the_flag/process(delta_time)
|
||||
for(var/i in spawned_mobs)
|
||||
if(!i)
|
||||
spawned_mobs -= i
|
||||
continue
|
||||
// Anyone in crit, automatically reap
|
||||
var/mob/living/living_participant = i
|
||||
if(HAS_TRAIT(living_participant, TRAIT_CRITICAL_CONDITION) || living_participant.stat == DEAD || !living_participant.client) // If they're critted, dead or no longer in their body, dust them
|
||||
ctf_dust_old(living_participant)
|
||||
else
|
||||
// The changes that you've been hit with no shield but not
|
||||
// instantly critted are low, but have some healing.
|
||||
living_participant.adjustBruteLoss(-2.5 * delta_time)
|
||||
living_participant.adjustFireLoss(-2.5 * delta_time)
|
||||
/obj/machinery/ctf/control_point/process(delta_time)
|
||||
if(controlling_team)
|
||||
ctf_game.control_point_scoring(controlling_team, point_rate * delta_time)
|
||||
|
||||
var/scores
|
||||
|
||||
/obj/machinery/capture_the_flag/red
|
||||
name = "Red CTF Controller"
|
||||
icon_state = "syndbeacon"
|
||||
team = RED_TEAM
|
||||
team_span = "redteamradio"
|
||||
ctf_gear = list("Rifleman" = /datum/outfit/ctf/red, "Assaulter" = /datum/outfit/ctf/assault/red, "Marksman" = /datum/outfit/ctf/marksman/red)
|
||||
instagib_gear = list("Instagib" = /datum/outfit/ctf/red/instagib)
|
||||
|
||||
/obj/machinery/capture_the_flag/blue
|
||||
name = "Blue CTF Controller"
|
||||
icon_state = "bluebeacon"
|
||||
team = BLUE_TEAM
|
||||
team_span = "blueteamradio"
|
||||
ctf_gear = list("Rifleman" = /datum/outfit/ctf/blue, "Assaulter" = /datum/outfit/ctf/assault/blue, "Marksman" = /datum/outfit/ctf/marksman/blue)
|
||||
instagib_gear = list("Instagib" = /datum/outfit/ctf/blue/instagib)
|
||||
|
||||
/obj/machinery/capture_the_flag/green
|
||||
name = "Green CTF Controller"
|
||||
icon_state = "greenbeacon"
|
||||
team = GREEN_TEAM
|
||||
team_span = "greenteamradio"
|
||||
ctf_gear = list("Rifleman" = /datum/outfit/ctf/green, "Assaulter" = /datum/outfit/ctf/assault/green, "Marksman" = /datum/outfit/ctf/marksman/green)
|
||||
instagib_gear = list("Instagib" = /datum/outfit/ctf/green/instagib)
|
||||
|
||||
/obj/machinery/capture_the_flag/yellow
|
||||
name = "Yellow CTF Controller"
|
||||
icon_state = "yellowbeacon"
|
||||
team = YELLOW_TEAM
|
||||
team_span = "yellowteamradio"
|
||||
ctf_gear = list("Rifleman" = /datum/outfit/ctf/yellow, "Assaulter" = /datum/outfit/ctf/assault/yellow, "Marksman" = /datum/outfit/ctf/marksman/yellow)
|
||||
instagib_gear = list("Instagib" = /datum/outfit/ctf/yellow/instagib)
|
||||
|
||||
//ATTACK GHOST IGNORING PARENT RETURN VALUE
|
||||
/obj/machinery/capture_the_flag/attack_ghost(mob/user)
|
||||
if(ctf_enabled == FALSE)
|
||||
if(user.client && user.client.holder)
|
||||
var/response = tgui_alert(usr,"Enable this CTF game?", "CTF", list("Yes", "No"))
|
||||
if(response == "Yes")
|
||||
toggle_id_ctf(user, game_id)
|
||||
return
|
||||
|
||||
|
||||
if(!(GLOB.ghost_role_flags & GHOSTROLE_MINIGAME))
|
||||
to_chat(user, span_warning("CTF has been temporarily disabled by admins."))
|
||||
return
|
||||
for(var/obj/machinery/capture_the_flag/CTF as anything in GLOB.ctf_panel.ctf_machines)
|
||||
if(CTF.game_id != game_id && CTF.ctf_enabled)
|
||||
to_chat(user, span_warning("There is already an ongoing game in the [get_area(CTF)]!"))
|
||||
return
|
||||
get_ctf_voting_controller(game_id).vote(user)
|
||||
if(ctf_game.ctf_enabled)
|
||||
for(var/team in ctf_game.teams)
|
||||
var/datum/ctf_team/ctf_team = ctf_game.teams[team]
|
||||
scores += UNLINT("<span style='color: [ctf_team.team_color]'>[ctf_team.team_color] - [ctf_team.points]/[ctf_game.points_to_win]</span>\n")
|
||||
balloon_alert_to_viewers(scores)
|
||||
|
||||
/obj/machinery/ctf/control_point/attackby(mob/user, params)
|
||||
capture(user)
|
||||
|
||||
/obj/machinery/ctf/control_point/attack_hand(mob/user, list/modifiers)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
capture(user)
|
||||
|
||||
if(!SSticker.HasRoundStarted())
|
||||
return
|
||||
if(user.ckey in team_members)
|
||||
if(user.ckey in recently_dead_ckeys)
|
||||
to_chat(user, span_warning("It must be more than [DisplayTimeText(respawn_cooldown)] from your last death to respawn!"))
|
||||
///Proc called when a player interacts with the control point to handle capturing it. Performs a do after and then verifies what team ther capturer belongs to.
|
||||
/obj/machinery/ctf/control_point/proc/capture(mob/user)
|
||||
if(do_after(user, 3 SECONDS, target = src))
|
||||
var/datum/component/ctf_player/ctf_player = user.mind.GetComponent(/datum/component/ctf_player)
|
||||
if(isnull(ctf_player))
|
||||
to_chat(user, span_warning("Non-players shouldn't be capturing control points"))
|
||||
return
|
||||
var/client/new_team_member = user.client
|
||||
if(user.mind && user.mind.current)
|
||||
ctf_dust_old(user.mind.current)
|
||||
spawn_team_member(new_team_member)
|
||||
return
|
||||
controlling_team = ctf_player.team
|
||||
icon_state = "dominator-[controlling_team]"
|
||||
ctf_game.message_all_teams("<span class='userdanger [ctf_game.teams[controlling_team].team_span]'>[user.real_name] has captured \the [src], claiming it for [controlling_team]! Go take it back!</span>")
|
||||
|
||||
for(var/obj/machinery/capture_the_flag/CTF as anything in GLOB.ctf_panel.ctf_machines)
|
||||
if(CTF.game_id != game_id || CTF == src || CTF.ctf_enabled == FALSE)
|
||||
continue
|
||||
if(user.ckey in CTF.team_members)
|
||||
to_chat(user, span_warning("No switching teams while the round is going!"))
|
||||
return
|
||||
if(CTF.team_members.len < src.team_members.len)
|
||||
to_chat(user, span_warning("[src.team] has more team members than [CTF.team]! Try joining [CTF.team] team to even things up."))
|
||||
return
|
||||
|
||||
var/client/new_team_member = user.client
|
||||
team_members |= new_team_member.ckey
|
||||
to_chat(user, "<span class='userdanger'>You are now a member of [src.team]. Get the enemy flag and bring it back to your team's controller!</span>")
|
||||
spawn_team_member(new_team_member)
|
||||
|
||||
|
||||
//does not add to recently dead, because it dusts and that triggers ctf_qdelled_player
|
||||
/obj/machinery/capture_the_flag/proc/ctf_dust_old(mob/living/body)
|
||||
if(isliving(body) && (team in body.faction))
|
||||
var/turf/T = get_turf(body)
|
||||
if(ammo_type)
|
||||
new ammo_type(T)
|
||||
body.dust()
|
||||
|
||||
/obj/machinery/capture_the_flag/proc/ctf_qdelled_player(mob/living/body)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
recently_dead_ckeys += body.ckey
|
||||
spawned_mobs -= body
|
||||
addtimer(CALLBACK(src, PROC_REF(clear_cooldown), body.ckey), respawn_cooldown, TIMER_UNIQUE)
|
||||
|
||||
/obj/machinery/capture_the_flag/proc/clear_cooldown(ckey)
|
||||
recently_dead_ckeys -= ckey
|
||||
|
||||
/obj/machinery/capture_the_flag/proc/spawn_team_member(client/new_team_member)
|
||||
var/datum/outfit/chosen_class
|
||||
|
||||
if(ctf_gear.len == 1) //no choices to make
|
||||
for(var/key in ctf_gear)
|
||||
chosen_class = ctf_gear[key]
|
||||
|
||||
else //there's a choice to make, present a radial menu
|
||||
var/list/display_classes = list()
|
||||
|
||||
for(var/key in ctf_gear)
|
||||
var/datum/outfit/ctf/class = ctf_gear[key]
|
||||
var/datum/radial_menu_choice/option = new
|
||||
option.image = image(icon = initial(class.icon), icon_state = initial(class.icon_state))
|
||||
option.info = "<span class='boldnotice'>[initial(class.class_description)]</span>"
|
||||
display_classes[key] = option
|
||||
|
||||
sort_list(display_classes)
|
||||
var/choice = show_radial_menu(new_team_member.mob, src, display_classes, radius = 38)
|
||||
if(!choice || !(GLOB.ghost_role_flags & GHOSTROLE_MINIGAME) || (new_team_member.ckey in recently_dead_ckeys) || !isobserver(new_team_member.mob) || src.ctf_enabled == FALSE || !(new_team_member.ckey in src.team_members))
|
||||
return //picked nothing, admin disabled it, cheating to respawn faster, cheating to respawn... while in game?,
|
||||
//there isn't a game going on any more, you are no longer a member of this team (perhaps a new match already started?)
|
||||
chosen_class = ctf_gear[choice]
|
||||
|
||||
var/turf/spawn_point = pick(get_adjacent_open_turfs(get_turf(src)))
|
||||
var/mob/living/carbon/human/M = new /mob/living/carbon/human(spawn_point)
|
||||
new_team_member.prefs.safe_transfer_prefs_to(M, is_antag = TRUE)
|
||||
if(M.dna.species.outfit_important_for_life)
|
||||
M.set_species(/datum/species/human)
|
||||
M.key = new_team_member.key
|
||||
M.faction += team
|
||||
M.equipOutfit(chosen_class)
|
||||
RegisterSignal(M, COMSIG_PARENT_QDELETING, PROC_REF(ctf_qdelled_player)) //just in case CTF has some map hazards (read: chasms). bit shorter than dust
|
||||
M.add_traits(player_traits, CAPTURE_THE_FLAG_TRAIT)
|
||||
spawned_mobs[M] = chosen_class
|
||||
return M //used in medisim.dm
|
||||
|
||||
/obj/machinery/capture_the_flag/Topic(href, href_list)
|
||||
if(href_list["join"])
|
||||
var/mob/dead/observer/ghost = usr
|
||||
if(istype(ghost))
|
||||
attack_ghost(ghost)
|
||||
|
||||
/obj/machinery/capture_the_flag/attackby(obj/item/I, mob/user, params)
|
||||
if(istype(I, /obj/item/ctf))
|
||||
var/obj/item/ctf/flag = I
|
||||
if(flag.team != src.team)
|
||||
points++
|
||||
flag.reset_flag(capture = TRUE)
|
||||
for(var/mob/ctf_player in GLOB.player_list)
|
||||
var/area/mob_area = get_area(ctf_player)
|
||||
if(istype(mob_area, game_area))
|
||||
to_chat(ctf_player, "<span class='userdanger [team_span]'>[user.real_name] has captured \the [flag], scoring a point for [team] team! They now have [points]/[points_to_win] points!</span>")
|
||||
if(points >= points_to_win)
|
||||
victory()
|
||||
|
||||
/obj/machinery/capture_the_flag/proc/victory()
|
||||
for(var/mob/_competitor in GLOB.mob_living_list)
|
||||
var/mob/living/competitor = _competitor
|
||||
var/area/mob_area = get_area(competitor)
|
||||
if(istype(mob_area, game_area))
|
||||
to_chat(competitor, "<span class='narsie [team_span]'>[team] team wins!</span>")
|
||||
to_chat(competitor, victory_rejoin_text)
|
||||
for(var/obj/item/ctf/W in competitor)
|
||||
competitor.dropItemToGround(W)
|
||||
competitor.dust()
|
||||
control_point_reset()
|
||||
for(var/obj/machinery/capture_the_flag/CTF as anything in GLOB.ctf_panel.ctf_machines)
|
||||
if(CTF.game_id != game_id)
|
||||
continue
|
||||
if(CTF.ctf_enabled == TRUE)
|
||||
machine_reset(CTF)
|
||||
|
||||
/obj/machinery/capture_the_flag/proc/toggle_ctf()
|
||||
if(!ctf_enabled)
|
||||
start_ctf()
|
||||
. = TRUE
|
||||
else
|
||||
stop_ctf()
|
||||
. = FALSE
|
||||
|
||||
/obj/machinery/capture_the_flag/proc/start_ctf()
|
||||
ctf_enabled = TRUE
|
||||
for(var/d in dead_barricades)
|
||||
var/obj/effect/ctf/dead_barricade/D = d
|
||||
D.respawn()
|
||||
|
||||
dead_barricades.Cut()
|
||||
|
||||
notify_ghosts("[name] has been activated!", source = src, action=NOTIFY_ORBIT, header = "CTF has been activated")
|
||||
|
||||
/obj/machinery/capture_the_flag/proc/machine_reset(obj/machinery/capture_the_flag/CTF)
|
||||
CTF.points = 0
|
||||
CTF.control_points = 0
|
||||
CTF.ctf_enabled = FALSE
|
||||
CTF.team_members = list()
|
||||
CTF.arena_reset = FALSE
|
||||
|
||||
/obj/machinery/capture_the_flag/proc/control_point_reset()
|
||||
for(var/obj/machinery/control_point/control in GLOB.machines)
|
||||
control.icon_state = "dominator"
|
||||
control.controlling = null
|
||||
|
||||
/obj/machinery/capture_the_flag/proc/unload()
|
||||
if(!ctf_landmark)
|
||||
return
|
||||
|
||||
if(ctf_landmark == GLOB.ctf_spawner)
|
||||
stop_ctf()
|
||||
new /obj/effect/landmark/ctf(get_turf(GLOB.ctf_spawner))
|
||||
|
||||
|
||||
/obj/machinery/capture_the_flag/proc/stop_ctf()
|
||||
var/area/A = get_area(src)
|
||||
for(var/_competitor in GLOB.mob_living_list)
|
||||
var/mob/living/competitor = _competitor
|
||||
if((get_area(A) == A) && (competitor.ckey in team_members))
|
||||
competitor.dust()
|
||||
team_members.Cut()
|
||||
spawned_mobs.Cut()
|
||||
recently_dead_ckeys.Cut()
|
||||
control_point_reset()
|
||||
machine_reset(src)
|
||||
|
||||
/obj/machinery/capture_the_flag/proc/instagib_mode()
|
||||
for(var/obj/machinery/capture_the_flag/CTF as anything in GLOB.ctf_panel.ctf_machines)
|
||||
if(CTF.game_id != game_id)
|
||||
continue
|
||||
CTF.ctf_gear = CTF.instagib_gear
|
||||
CTF.respawn_cooldown = INSTAGIB_RESPAWN
|
||||
|
||||
/obj/machinery/capture_the_flag/proc/normal_mode()
|
||||
for(var/obj/machinery/capture_the_flag/CTF as anything in GLOB.ctf_panel.ctf_machines)
|
||||
if(CTF.game_id != game_id)
|
||||
continue
|
||||
CTF.ctf_gear = CTF.default_gear
|
||||
CTF.respawn_cooldown = DEFAULT_RESPAWN
|
||||
/obj/machinery/ctf/control_point/proc/clear_point()
|
||||
controlling_team = null
|
||||
icon_state = "dominator"
|
||||
|
||||
///A trap that when stepped on kills anyone who is not part of the associated CTF team.
|
||||
/obj/structure/trap/ctf
|
||||
name = "Spawn protection"
|
||||
desc = "Stay outta the enemy spawn!"
|
||||
@@ -548,13 +387,13 @@
|
||||
/obj/structure/trap/ctf/examine(mob/user)
|
||||
return
|
||||
|
||||
/obj/structure/trap/ctf/trap_effect(mob/living/L)
|
||||
if(!is_ctf_target(L))
|
||||
/obj/structure/trap/ctf/trap_effect(mob/living/living)
|
||||
if(!is_ctf_target(living))
|
||||
return
|
||||
if(!(src.team in L.faction))
|
||||
to_chat(L, span_danger("<B>Stay out of the enemy spawn!</B>"))
|
||||
L.investigate_log("has died from entering the enemy spawn in CTF.", INVESTIGATE_DEATHS)
|
||||
L.death()
|
||||
if(!(src.team in living.faction))
|
||||
to_chat(living, span_bolddanger("Stay out of the enemy spawn!"))
|
||||
living.investigate_log("has died from entering the enemy spawn in CTF.", INVESTIGATE_DEATHS)
|
||||
living.apply_damage(200) //Damage instead of instant death so we trigger the damage signal.
|
||||
|
||||
/obj/structure/trap/ctf/red
|
||||
team = RED_TEAM
|
||||
@@ -572,6 +411,7 @@
|
||||
team = YELLOW_TEAM
|
||||
icon_state = "trap-shock"
|
||||
|
||||
///A type of barricade that can be destroyed by CTF weapons and respawns at the end of CTF matches.
|
||||
/obj/structure/barricade/security/ctf
|
||||
name = "barrier"
|
||||
desc = "A barrier. Provides cover in fire fights."
|
||||
@@ -581,36 +421,21 @@
|
||||
/obj/structure/barricade/security/ctf/make_debris()
|
||||
new /obj/effect/ctf/dead_barricade(get_turf(src))
|
||||
|
||||
/obj/structure/table/reinforced/ctf
|
||||
resistance_flags = INDESTRUCTIBLE
|
||||
flags_1 = NODECONSTRUCT_1
|
||||
|
||||
/obj/effect/ctf
|
||||
density = FALSE
|
||||
anchored = TRUE
|
||||
invisibility = INVISIBILITY_OBSERVER
|
||||
alpha = 100
|
||||
resistance_flags = INDESTRUCTIBLE
|
||||
|
||||
/obj/effect/ctf/dead_barricade
|
||||
name = "dead barrier"
|
||||
desc = "It provided cover in fire fights. And now it's gone."
|
||||
icon = 'icons/obj/objects.dmi'
|
||||
icon_state = "barrier0"
|
||||
var/game_id = "centcom"
|
||||
var/game_id = CTF_GHOST_CTF_GAME_ID
|
||||
var/datum/ctf_controller/ctf_game
|
||||
|
||||
/obj/effect/ctf/dead_barricade/Initialize(mapload)
|
||||
. = ..()
|
||||
for(var/obj/machinery/capture_the_flag/CTF as anything in GLOB.ctf_panel.ctf_machines)
|
||||
if(CTF.game_id != game_id)
|
||||
continue
|
||||
CTF.dead_barricades += src
|
||||
ctf_game = GLOB.ctf_games[game_id]
|
||||
ctf_game.barricades += src
|
||||
|
||||
/obj/effect/ctf/dead_barricade/Destroy()
|
||||
for(var/obj/machinery/capture_the_flag/CTF as anything in GLOB.ctf_panel.ctf_machines)
|
||||
if(CTF.game_id != game_id)
|
||||
continue
|
||||
CTF.dead_barricades -= src
|
||||
ctf_game.barricades -= src
|
||||
return ..()
|
||||
|
||||
/obj/effect/ctf/dead_barricade/proc/respawn()
|
||||
@@ -618,66 +443,79 @@
|
||||
new /obj/structure/barricade/security/ctf(get_turf(src))
|
||||
qdel(src)
|
||||
|
||||
//Control Point
|
||||
/obj/structure/table/reinforced/ctf
|
||||
resistance_flags = INDESTRUCTIBLE
|
||||
flags_1 = NODECONSTRUCT_1
|
||||
|
||||
/obj/machinery/control_point
|
||||
name = "control point"
|
||||
desc = "You should capture this."
|
||||
icon = 'icons/obj/machines/dominator.dmi'
|
||||
icon_state = "dominator"
|
||||
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
|
||||
var/obj/machinery/capture_the_flag/controlling
|
||||
var/team = "none"
|
||||
///This is how many points are gained a second while controlling this point
|
||||
var/point_rate = 1
|
||||
var/game_area = /area/centcom/ctf
|
||||
#define CTF_LOADING_UNLOADED 0
|
||||
#define CTF_LOADING_LOADING 1
|
||||
#define CTF_LOADING_LOADED 2
|
||||
|
||||
/obj/machinery/control_point/process(delta_time)
|
||||
if(controlling)
|
||||
controlling.control_points += point_rate * delta_time
|
||||
if(controlling.control_points >= controlling.control_points_to_win)
|
||||
controlling.victory()
|
||||
|
||||
var/scores
|
||||
|
||||
for(var/obj/machinery/capture_the_flag/team as anything in GLOB.ctf_panel.ctf_machines)
|
||||
if (!team.ctf_enabled)
|
||||
continue
|
||||
scores += UNLINT("<span style='color: [team.team]'>[team.team] - [team.control_points]/[team.control_points_to_win]</span>\n")
|
||||
|
||||
balloon_alert_to_viewers(scores)
|
||||
|
||||
/obj/machinery/control_point/attackby(mob/user, params)
|
||||
capture(user)
|
||||
|
||||
/obj/machinery/control_point/attack_hand(mob/user, list/modifiers)
|
||||
. = ..()
|
||||
if(.)
|
||||
///Proc that handles toggling and unloading CTF.
|
||||
/proc/toggle_id_ctf(user, activated_id, automated = FALSE, unload = FALSE, area/ctf_area = /area/centcom/ctf)
|
||||
var/static/loading = CTF_LOADING_UNLOADED
|
||||
var/datum/ctf_controller/ctf_controller = GLOB.ctf_games[activated_id]
|
||||
if(isnull(ctf_controller))
|
||||
ctf_controller = create_ctf_game(activated_id)
|
||||
if(unload == TRUE)
|
||||
log_admin("[key_name_admin(user)] is attempting to unload CTF.")
|
||||
message_admins("[key_name_admin(user)] is attempting to unload CTF.")
|
||||
if(loading == CTF_LOADING_UNLOADED)
|
||||
to_chat(user, span_warning("CTF cannot be unloaded if it was not loaded in the first place"))
|
||||
return
|
||||
to_chat(user, span_warning("CTF is being unloaded"))
|
||||
ctf_controller.unload_ctf()
|
||||
log_admin("[key_name_admin(user)] has unloaded CTF.")
|
||||
message_admins("[key_name_admin(user)] has unloaded CTF.")
|
||||
loading = CTF_LOADING_UNLOADED
|
||||
return
|
||||
capture(user)
|
||||
switch (loading)
|
||||
if (CTF_LOADING_UNLOADED)
|
||||
if (isnull(GLOB.ctf_spawner))
|
||||
to_chat(user, span_boldwarning("Couldn't find a CTF spawner. Call a maintainer!"))
|
||||
return
|
||||
|
||||
/obj/machinery/control_point/proc/capture(mob/user)
|
||||
if(do_after(user, 30, target = src))
|
||||
for(var/obj/machinery/capture_the_flag/team as anything in GLOB.ctf_panel.ctf_machines)
|
||||
if(team.ctf_enabled && (user.ckey in team.team_members))
|
||||
controlling = team
|
||||
icon_state = "dominator-[team.team]"
|
||||
for(var/mob/M in GLOB.player_list)
|
||||
var/area/mob_area = get_area(M)
|
||||
if(istype(mob_area, game_area))
|
||||
to_chat(M, "<span class='userdanger [team.team_span]'>[user.real_name] has captured \the [src], claiming it for [team.team]! Go take it back!</span>")
|
||||
break
|
||||
to_chat(user, span_notice("Loading CTF..."))
|
||||
|
||||
loading = CTF_LOADING_LOADING
|
||||
if(activated_id == CTF_GHOST_CTF_GAME_ID) //Only ghost CTF supports map loading, if CTF is started by an admin elsewhere the map loader should not be used.
|
||||
if(!GLOB.ctf_spawner.load_map(user))
|
||||
to_chat(user, span_warning("CTF loading was cancelled"))
|
||||
loading = CTF_LOADING_UNLOADED
|
||||
return
|
||||
loading = CTF_LOADING_LOADED
|
||||
if (CTF_LOADING_LOADING)
|
||||
to_chat(user, span_warning("CTF is loading!"))
|
||||
|
||||
return
|
||||
|
||||
var/ctf_enabled = FALSE
|
||||
ctf_enabled = ctf_controller.toggle_ctf()
|
||||
for(var/obj/machinery/power/emitter/emitter in ctf_area)
|
||||
emitter.active = ctf_enabled
|
||||
if(user)
|
||||
message_admins("[key_name_admin(user)] has [ctf_enabled ? "enabled" : "disabled"] CTF!")
|
||||
else if(automated)
|
||||
message_admins("CTF has finished a round and automatically restarted.")
|
||||
notify_ghosts("CTF has automatically restarted after a round finished in [initial(ctf_area.name)]!",'sound/effects/ghost2.ogg')
|
||||
else
|
||||
message_admins("The players have spoken! Voting has enabled CTF!")
|
||||
if(!automated)
|
||||
notify_ghosts("CTF has been [ctf_enabled? "enabled" : "disabled"] in [initial(ctf_area.name)]!",'sound/effects/ghost2.ogg')
|
||||
|
||||
#undef CTF_LOADING_UNLOADED
|
||||
#undef CTF_LOADING_LOADING
|
||||
#undef CTF_LOADING_LOADED
|
||||
|
||||
///Proc that identifies if something is a valid target for CTF related checks, checks if an object is a ctf barrier or has ctf component if they are a player.
|
||||
/proc/is_ctf_target(atom/target)
|
||||
. = FALSE
|
||||
if(istype(target, /obj/structure/barricade/security/ctf))
|
||||
. = TRUE
|
||||
return TRUE
|
||||
if(ishuman(target))
|
||||
var/mob/living/carbon/human/H = target
|
||||
for(var/obj/machinery/capture_the_flag/CTF as anything in GLOB.ctf_panel.ctf_machines)
|
||||
if(H in CTF.spawned_mobs)
|
||||
. = TRUE
|
||||
break
|
||||
var/mob/living/carbon/human/human = target
|
||||
if(human.mind?.GetComponent(/datum/component/ctf_player))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
#undef WHITE_TEAM
|
||||
#undef RED_TEAM
|
||||
@@ -685,5 +523,3 @@
|
||||
#undef GREEN_TEAM
|
||||
#undef YELLOW_TEAM
|
||||
#undef FLAG_RETURN_TIME
|
||||
#undef INSTAGIB_RESPAWN
|
||||
#undef DEFAULT_RESPAWN
|
||||
|
||||
@@ -2,6 +2,7 @@ GLOBAL_DATUM(ctf_spawner, /obj/effect/landmark/ctf)
|
||||
|
||||
/obj/effect/landmark/ctf
|
||||
name = "CTF Map Spawner"
|
||||
var/game_id = CTF_GHOST_CTF_GAME_ID
|
||||
var/list/map_bounds
|
||||
|
||||
/obj/effect/landmark/ctf/Initialize(mapload)
|
||||
@@ -53,6 +54,9 @@ GLOBAL_DATUM(ctf_spawner, /obj/effect/landmark/ctf)
|
||||
|
||||
current_map = new current_map()
|
||||
|
||||
var/datum/ctf_controller/ctf_controller = GLOB.ctf_games[CTF_GHOST_CTF_GAME_ID]
|
||||
ctf_controller.setup_rules(current_map.points_to_win)
|
||||
|
||||
if(!spawn_area)
|
||||
CRASH("No spawn area detected for CTF!")
|
||||
else if(!current_map)
|
||||
@@ -65,6 +69,8 @@ GLOBAL_DATUM(ctf_spawner, /obj/effect/landmark/ctf)
|
||||
/datum/map_template/ctf
|
||||
should_place_on_top = FALSE
|
||||
var/description = ""
|
||||
///Score required to win CTF on this map.
|
||||
var/points_to_win = 3
|
||||
|
||||
/datum/map_template/ctf/classic
|
||||
name = "Classic"
|
||||
@@ -85,6 +91,7 @@ GLOBAL_DATUM(ctf_spawner, /obj/effect/landmark/ctf)
|
||||
name = "Limbo"
|
||||
description = "A KOTH map that takes place in a wizard den with looping hallways"
|
||||
mappath = "_maps/map_files/CTF/limbo.dmm"
|
||||
points_to_win = 180
|
||||
|
||||
/datum/map_template/ctf/cruiser
|
||||
name = "Crusier"
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
GLOBAL_DATUM_INIT(ctf_panel, /datum/ctf_panel, new())
|
||||
|
||||
/datum/ctf_panel
|
||||
///List of all CTF machines
|
||||
var/list/obj/machinery/capture_the_flag/ctf_machines = list()
|
||||
|
||||
/datum/ctf_panel/ui_state(mob/user)
|
||||
return GLOB.observer_state
|
||||
@@ -17,25 +15,28 @@ GLOBAL_DATUM_INIT(ctf_panel, /datum/ctf_panel, new())
|
||||
var/list/data = list()
|
||||
var/list/teams = list()
|
||||
|
||||
for(var/obj/machinery/capture_the_flag/team as anything in GLOB.ctf_panel.ctf_machines)
|
||||
if (!team.ctf_enabled)
|
||||
continue
|
||||
for(var/game_reference in GLOB.ctf_games)
|
||||
var/datum/ctf_controller/ctf_controller = GLOB.ctf_games[game_reference]
|
||||
for(var/datum/ctf_team/team_reference as anything in ctf_controller.teams)
|
||||
if (!ctf_controller.ctf_enabled)
|
||||
continue
|
||||
var/datum/ctf_team/team = ctf_controller.teams[team_reference]
|
||||
|
||||
var/list/this = list()
|
||||
this["name"] = team
|
||||
this["color"] = team.team
|
||||
this["score"] = team.points + team.control_points
|
||||
this["team_size"] = team.team_members.len
|
||||
this["refs"] += REF(team)
|
||||
teams += list(this)
|
||||
var/list/this = list()
|
||||
this["name"] = "[team.team_color] team"
|
||||
this["color"] = team.team_color
|
||||
this["score"] = team.points
|
||||
this["team_size"] = team.team_members.len
|
||||
this["refs"] += REF(team.spawner)
|
||||
teams += list(this)
|
||||
|
||||
if (teams.len == 0)
|
||||
// No CTF map has been spawned in yet
|
||||
var/datum/ctf_voting_controller/ctf_controller = get_ctf_voting_controller(CTF_GHOST_CTF_GAME_ID)
|
||||
var/datum/ctf_voting_controller/ctf_voting_controller = get_ctf_voting_controller(CTF_GHOST_CTF_GAME_ID)
|
||||
|
||||
data["voters"] = ctf_controller.volunteers.len
|
||||
data["voters"] = ctf_voting_controller.volunteers.len
|
||||
data["voters_required"] = CTF_REQUIRED_PLAYERS
|
||||
data["voted"] = (user.ckey in ctf_controller.volunteers)
|
||||
data["voted"] = (user.ckey in ctf_voting_controller.volunteers)
|
||||
else
|
||||
data["teams"] = teams
|
||||
|
||||
@@ -50,14 +51,14 @@ GLOBAL_DATUM_INIT(ctf_panel, /datum/ctf_panel, new())
|
||||
|
||||
switch(action)
|
||||
if("jump")
|
||||
var/obj/machinery/capture_the_flag/ctf_spawner = locate(params["refs"])
|
||||
var/obj/machinery/ctf/spawner/ctf_spawner = locate(params["refs"])
|
||||
if(istype(ctf_spawner))
|
||||
user.forceMove(get_turf(ctf_spawner))
|
||||
return TRUE
|
||||
if("join")
|
||||
var/obj/machinery/capture_the_flag/ctf_spawner = locate(params["refs"])
|
||||
var/obj/machinery/ctf/spawner/ctf_spawner = locate(params["refs"])
|
||||
if(istype(ctf_spawner))
|
||||
if(ctf_spawner.ctf_enabled)
|
||||
if(ctf_enabled())
|
||||
user.forceMove(get_turf(ctf_spawner))
|
||||
ctf_spawner.attack_ghost(user)
|
||||
return TRUE
|
||||
@@ -81,8 +82,7 @@ GLOBAL_DATUM_INIT(ctf_panel, /datum/ctf_panel, new())
|
||||
return TRUE
|
||||
|
||||
/datum/ctf_panel/proc/ctf_enabled()
|
||||
for (var/obj/machinery/capture_the_flag/ctf_machine as anything in GLOB.ctf_panel.ctf_machines)
|
||||
if (ctf_machine.ctf_enabled)
|
||||
return TRUE
|
||||
|
||||
var/datum/ctf_controller/ctf_enabled_check = GLOB.ctf_games[CTF_GHOST_CTF_GAME_ID]
|
||||
if(ctf_enabled_check.ctf_enabled)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
79
code/modules/capture_the_flag/ctf_player_component.dm
Normal file
79
code/modules/capture_the_flag/ctf_player_component.dm
Normal file
@@ -0,0 +1,79 @@
|
||||
///A component added to the mind of anyone who is playing in an ongoing CTF match. Any player specific CTF functionality should be implimented here. (someone should impliment score tracking here)
|
||||
/datum/component/ctf_player
|
||||
///The team that this player is associated with.
|
||||
var/team
|
||||
///A reference to the players mob, cleared after they die, restored on respawn.
|
||||
var/mob/living/player_mob
|
||||
///Weather or not the player is currently able to respawn.
|
||||
var/can_respawn = TRUE
|
||||
///Reference to the game this player is participating in.
|
||||
var/datum/ctf_controller/ctf_game
|
||||
///Item dropped on death,
|
||||
var/death_drop = /obj/effect/powerup/ammo/ctf
|
||||
///Reference to players ckey, used for sending messages to them relating to CTF.
|
||||
var/ckey_reference
|
||||
|
||||
/datum/component/ctf_player/Initialize(team, ctf_game, death_drop)
|
||||
src.team = team
|
||||
src.ctf_game = ctf_game
|
||||
src.death_drop = death_drop
|
||||
if(!istype(parent, /datum/mind))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
var/datum/mind/true_parent = parent
|
||||
player_mob = true_parent.current
|
||||
ckey_reference = player_mob.ckey
|
||||
setup_dusting()
|
||||
|
||||
/datum/component/ctf_player/PostTransfer()
|
||||
if(!istype(parent, /datum/mind))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
var/datum/mind/true_parent = parent
|
||||
player_mob = true_parent.current
|
||||
setup_dusting()
|
||||
|
||||
///CTF players are dusted upon taking damage that puts them into critical or leaving their body.
|
||||
/datum/component/ctf_player/proc/setup_dusting()
|
||||
RegisterSignal(player_mob, COMSIG_MOB_AFTER_APPLY_DAMAGE, PROC_REF(damage_type_check))
|
||||
RegisterSignal(player_mob, COMSIG_MOB_GHOSTIZED, PROC_REF(ctf_dust))
|
||||
|
||||
///Stamina and oxygen damage will not dust a player by themself.
|
||||
/datum/component/ctf_player/proc/damage_type_check(datum/source, damage, damage_type)
|
||||
SIGNAL_HANDLER
|
||||
if(damage_type != STAMINA && damage_type != OXY)
|
||||
ctf_dust()
|
||||
|
||||
///Dusts the player and starts a respawn countdown.
|
||||
/datum/component/ctf_player/proc/ctf_dust()
|
||||
SIGNAL_HANDLER
|
||||
if(!HAS_TRAIT(player_mob, TRAIT_CRITICAL_CONDITION) && !player_mob.stat == DEAD && player_mob.client)
|
||||
return
|
||||
UnregisterSignal(player_mob, list(COMSIG_MOB_AFTER_APPLY_DAMAGE, COMSIG_MOB_GHOSTIZED))
|
||||
var/turf/death_turf = get_turf(player_mob)
|
||||
player_mob.dust()
|
||||
player_mob = null
|
||||
can_respawn = FALSE
|
||||
addtimer(CALLBACK(src, PROC_REF(allow_respawns)), ctf_game.respawn_cooldown, TIMER_UNIQUE)
|
||||
if(death_drop)
|
||||
new death_drop(death_turf)
|
||||
|
||||
///Called after a period of time pulled from ctf_game, allows the player to respawn in CTF.
|
||||
/datum/component/ctf_player/proc/allow_respawns()
|
||||
can_respawn = TRUE
|
||||
send_message(span_notice("You can now respawn in CTF!"))
|
||||
|
||||
///Sends a message to the player.
|
||||
/datum/component/ctf_player/proc/send_message(message)
|
||||
to_chat(GLOB.directory[ckey_reference], message)
|
||||
|
||||
///Called when the associated CTF game ends or their associated team is deleted, dusts the player and deletes this component to ensure no data from it is carried over to future games.
|
||||
/datum/component/ctf_player/proc/end_game()
|
||||
if(player_mob)
|
||||
for(var/obj/item/ctf_flag/flag in player_mob)
|
||||
player_mob.dropItemToGround(flag)
|
||||
player_mob.dust()
|
||||
qdel(src)
|
||||
|
||||
/datum/component/ctf_player/Destroy(force, silent)
|
||||
if(player_mob)
|
||||
UnregisterSignal(player_mob, list(COMSIG_MOB_AFTER_APPLY_DAMAGE, COMSIG_MOB_GHOSTIZED))
|
||||
return ..()
|
||||
@@ -3,26 +3,22 @@
|
||||
#define REDFIELD_TEAM "Red"
|
||||
#define BLUESWORTH_TEAM "Blue"
|
||||
|
||||
/obj/machinery/capture_the_flag/medisim
|
||||
game_id = "medieval"
|
||||
game_area = /area/shuttle/escape/simulation
|
||||
ammo_type = null //no guns, no need
|
||||
victory_rejoin_text = "<span class='userdanger'>Teams have been cleared. The next game is starting automatically. Rejoin a team if you wish!</span>"
|
||||
///Variant of the CTF spawner used for the medieval simulation shuttle.
|
||||
/obj/machinery/ctf/spawner/medisim
|
||||
game_id = CTF_MEDISIM_CTF_GAME_ID
|
||||
ammo_type = null
|
||||
player_traits = list()
|
||||
|
||||
/obj/machinery/capture_the_flag/medisim/Initialize(mapload)
|
||||
/obj/machinery/ctf/spawner/medisim/Initialize(mapload)
|
||||
. = ..()
|
||||
start_ctf() //both machines initialize, so both will call start_ctf instead of toggle_id_ctf calling it for both, twice.
|
||||
ctf_game.setup_rules(victory_rejoin_text = "Teams have been cleared. The next game is starting automatically. Rejoin a team if you wish!", auto_restart = TRUE)
|
||||
return INITIALIZE_HINT_LATELOAD //Start CTF needs to run after both medisim spawners have initalized.
|
||||
|
||||
/obj/machinery/capture_the_flag/medisim/victory()
|
||||
/obj/machinery/ctf/spawner/medisim/LateInitialize()
|
||||
. = ..()
|
||||
toggle_id_ctf(null, game_id, automated = TRUE)//only one machine runs the victory proc, start_ctf proc would break the other machine
|
||||
ctf_game.start_ctf()
|
||||
|
||||
// We don't clean up for the medisim.
|
||||
/obj/machinery/capture_the_flag/medisim/unload()
|
||||
return
|
||||
|
||||
/obj/machinery/capture_the_flag/medisim/spawn_team_member(client/new_team_member)
|
||||
/obj/machinery/ctf/spawner/medisim/spawn_team_member(client/new_team_member)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
@@ -41,37 +37,37 @@
|
||||
human_knight.real_name = "[title] [oldname]"
|
||||
human_knight.name = human_knight.real_name
|
||||
|
||||
/obj/machinery/capture_the_flag/medisim/red
|
||||
/obj/machinery/ctf/spawner/medisim/red
|
||||
name = "\improper Redfield Data Realizer"
|
||||
icon_state = "syndbeacon"
|
||||
team = REDFIELD_TEAM
|
||||
team_span = "redteamradio"
|
||||
ctf_gear = list("knight" = /datum/outfit/ctf/medisim, "archer" = /datum/outfit/ctf/medisim/archer)
|
||||
|
||||
/obj/machinery/capture_the_flag/medisim/blue
|
||||
/obj/machinery/ctf/spawner/medisim/blue
|
||||
name = "\improper Bluesworth Data Realizer"
|
||||
icon_state = "bluebeacon"
|
||||
team = BLUESWORTH_TEAM
|
||||
team_span = "blueteamradio"
|
||||
ctf_gear = list("knight" = /datum/outfit/ctf/medisim/blue, "archer" = /datum/outfit/ctf/medisim/archer/blue)
|
||||
|
||||
/obj/item/ctf/red/medisim
|
||||
/obj/item/ctf_flag/red/medisim
|
||||
name = "\improper Redfield Castle Fair Maiden"
|
||||
desc = "Protect your maiden, and capture theirs!"
|
||||
icon = 'icons/obj/toys/plushes.dmi'
|
||||
icon_state = "plushie_nuke"
|
||||
force = 0
|
||||
game_area = /area/shuttle/escape
|
||||
movement_type = FLOATING //there are chasms, and resetting when they fall in is really lame so lets minimize that
|
||||
game_id = CTF_MEDISIM_CTF_GAME_ID
|
||||
|
||||
/obj/item/ctf/blue/medisim
|
||||
/obj/item/ctf_flag/blue/medisim
|
||||
name = "\improper Bluesworth Hold Fair Maiden"
|
||||
desc = "Protect your maiden, and capture theirs!"
|
||||
icon = 'icons/obj/toys/plushes.dmi'
|
||||
icon_state = "plushie_slime"
|
||||
force = 0
|
||||
game_area = /area/shuttle/escape
|
||||
movement_type = FLOATING //there are chasms, and resetting when they fall in is really lame so lets minimize that
|
||||
game_id = CTF_MEDISIM_CTF_GAME_ID
|
||||
|
||||
#undef REDFIELD_TEAM
|
||||
#undef BLUESWORTH_TEAM
|
||||
|
||||
@@ -303,6 +303,7 @@ Works together with spawning an observer, noted above.
|
||||
ghost.client?.init_verbs()
|
||||
if(!can_reenter_corpse)// Disassociates observer mind from the body mind
|
||||
ghost.mind = null
|
||||
SEND_SIGNAL(src, COMSIG_MOB_GHOSTIZED)
|
||||
return ghost
|
||||
|
||||
/mob/living/ghostize(can_reenter_corpse = TRUE)
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
adjustCloneLoss(damage_amount, forced = forced)
|
||||
if(STAMINA)
|
||||
adjustStaminaLoss(damage_amount, forced = forced)
|
||||
SEND_SIGNAL(src, COMSIG_MOB_AFTER_APPLY_DAMAGE, damage, damagetype, def_zone)
|
||||
return TRUE
|
||||
|
||||
//These procs fetch a cumulative total damage from all bodyparts
|
||||
|
||||
@@ -1482,6 +1482,7 @@ GLOBAL_LIST_EMPTY(features_by_species)
|
||||
if(BRAIN)
|
||||
var/damage_amount = forced ? damage : damage * hit_percent * H.physiology.brain_mod
|
||||
H.adjustOrganLoss(ORGAN_SLOT_BRAIN, damage_amount)
|
||||
SEND_SIGNAL(H, COMSIG_MOB_AFTER_APPLY_DAMAGE, damage, damagetype, def_zone, blocked, wound_bonus, bare_wound_bonus, sharpness, attack_direction)
|
||||
return 1
|
||||
|
||||
/datum/species/proc/on_hit(obj/projectile/P, mob/living/carbon/human/H)
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
adjustCloneLoss(damage_amount, forced = forced)
|
||||
if(STAMINA)
|
||||
adjustStaminaLoss(damage_amount, forced = forced)
|
||||
SEND_SIGNAL(src, COMSIG_MOB_AFTER_APPLY_DAMAGE, damage, damagetype, def_zone)
|
||||
return TRUE
|
||||
|
||||
///like [apply_damage][/mob/living/proc/apply_damage] except it always uses the damage procs
|
||||
|
||||
@@ -2972,10 +2972,12 @@
|
||||
#include "code\modules\buildmode\submodes\tweakcomps.dm"
|
||||
#include "code\modules\buildmode\submodes\variable_edit.dm"
|
||||
#include "code\modules\capture_the_flag\ctf_classes.dm"
|
||||
#include "code\modules\capture_the_flag\ctf_controller.dm"
|
||||
#include "code\modules\capture_the_flag\ctf_equipment.dm"
|
||||
#include "code\modules\capture_the_flag\ctf_game.dm"
|
||||
#include "code\modules\capture_the_flag\ctf_map_loading.dm"
|
||||
#include "code\modules\capture_the_flag\ctf_panel.dm"
|
||||
#include "code\modules\capture_the_flag\ctf_player_component.dm"
|
||||
#include "code\modules\capture_the_flag\ctf_voting.dm"
|
||||
#include "code\modules\capture_the_flag\medieval_sim\medisim_classes.dm"
|
||||
#include "code\modules\capture_the_flag\medieval_sim\medisim_game.dm"
|
||||
|
||||
@@ -279,12 +279,13 @@ const FunTab = (props, context) => {
|
||||
/>
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
<NoticeBox
|
||||
mb={-0.5}
|
||||
<Button
|
||||
icon="flag"
|
||||
lineHeight={lineHeightNormal}
|
||||
width={buttonWidthNormal}
|
||||
height={lineHeightNormal}>
|
||||
Your admin button here, coder!
|
||||
</NoticeBox>
|
||||
content="CTF Instagib Mode"
|
||||
onClick={() => act('ctf_instagib')}
|
||||
/>
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
<NoticeBox
|
||||
|
||||
Reference in New Issue
Block a user