New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[WIP] Overthrow gamemode #39818
[WIP] Overthrow gamemode #39818
Changes from 7 commits
db9ccb4
41a77ff
ba352c4
0def32c
d6528b1
726e9e8
38a7120
6b9dbac
0b82b29
1aafb67
2fbfdd0
8a2adfe
f7d3bd2
c007c50
99e80e9
60f0fbe
b427efe
3340139
7c2f4db
0618242
b10d2d6
8a2ec0d
c98126a
03ed4b7
23f652f
ac86f84
f9d5c29
71f321c
f2d47b8
ada9f45
f3b6776
5c6d912
4032132
aa93562
89a3e26
7a52e44
b485deb
7a0957e
d8e939b
bd5ce57
dbee390
56e419a
7c36214
01823ce
263a723
e470c96
b2c534e
c6feb1d
2ec9da6
ae7ab84
73a62c5
f3130df
85c1fb9
b54895f
ea4a155
1f48059
61dcd3c
0df2bbc
a49da6c
76518c6
47a4cf2
39a07a7
d60cb8f
d2f7b33
d5f7700
f7cfb2b
2f38918
c94cc03
cfa6563
782f5fc
90e72f8
7a33344
beffb80
fbca9c9
f8aebc5
31a7b28
0884d09
d65e268
5de239c
5bbe1aa
0100765
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
// This is a point based objective. You can only lose if you fail to even handle one single command personnel, else you win. But, if you win, you're given a certain number of points, | ||
// based on the role of the head (Captain, HoP/HoS, other heads, warden, security officers) and whether you converted them, exiled or just killed (applying a modifier of 1.5, 1 and 0.5 respectively) | ||
// because this is meant for the overthrow gamemode, which is a bloodless coup, unlike revs. | ||
|
||
// Point system: | ||
// Base points for each role: | ||
// AI, Captain = 5; | ||
// Head of Personnel, Head of Security, target = 4; | ||
// Chief Engineer, Chief Medical Officer, Research Director = 3; | ||
// Warden = 2; | ||
// Security Officer = 1 | ||
|
||
// Modifiers: | ||
// Converting: 1.5 for the converting team, 1 for all the other ones; | ||
// Exiling: 1; | ||
// Killing: 0.5 | ||
|
||
#define CAPPTS 5 | ||
#define AIPTS 5 | ||
#define HOPPTS 4 | ||
#define HOSPTS 4 | ||
#define TARGETPTS 4 | ||
#define CEPTS 3 | ||
#define CMOPTS 3 | ||
#define RDPTS 3 | ||
#define WARDENPTS 2 | ||
#define SECPTS 1 | ||
|
||
#define CONVERTED_OURS 1.5 | ||
#define CONVERTED 1 | ||
#define EXILED 1 | ||
#define KILLED 0.5 | ||
|
||
/datum/objective/ovethrow_heads | ||
var/list/targets = list() // we want one objective for all the heads, instead of 1 objective per head, because you don't actually "lose" if you get atleast one head. | ||
// Also, this is an associative list, target = role. Modifiers (defines) are applied on points calculation at round end. | ||
|
||
/datum/objective/ovethrow_heads/proc/find_targets() | ||
var/list/datum/mind/owners = get_owners() | ||
for(var/datum/mind/possible_target in get_crewmember_minds()) | ||
if(!(possible_target in owners) && ishuman(possible_target.current)) | ||
if(possible_target.assigned_role in GLOB.command_positions) | ||
targets[possible_target] = possible_target.assigned_role | ||
update_explanation_text() | ||
|
||
/datum/objective/ovethrow_heads/update_explanation_text() | ||
explanation_text = "Convert, exile or kill " | ||
explanation_text += targets.Join(",") | ||
explanation_text += ". Converting to your team will give you more points, whereas killing will give you the least. Syndicates don't want to stir up too many troubles." | ||
|
||
/datum/objective/ovethrow_heads/check_completion() // This won't return a boolean as usual, but the amount of points earned. | ||
var/base_points = 0 | ||
for(var/i in targets) | ||
var/datum/mind/M = i | ||
if(M && M.current) | ||
var/target_points | ||
var/role = targets[M] | ||
switch(role) | ||
if("Captain") | ||
target_points = CAPPTS | ||
if("Head of Personnel") | ||
target_points = HOPPTS | ||
if("Head of Security") | ||
target_points = HOSPTS | ||
if("Chief Engineer") | ||
target_points = CEPTS | ||
if("Research Director") | ||
target_points = RDPTS | ||
if("Chief Medical Officer") | ||
target_points = CMOPTS | ||
// modifiers | ||
var/datum/antagonist/overthrow/O = M.has_antag_datum(/datum/antagonist/overthrow) | ||
if(M.current.stat == DEAD) // yeah, you gotta defend him even if you converted him | ||
target_points *= KILLED | ||
else if(!is_station_level(M.current.z) && !is_centcom_level(M.current.z)) // exiled. | ||
target_points *= EXILED | ||
else if(O) //dude's alive, on station/centcom and converted, nice | ||
target_points *= CONVERTED // doesn't matter to which team | ||
if(team == O.team) // converted to our team, nice | ||
target_points *= CONVERTED_OURS | ||
else // you didn't do anything, no reward for you | ||
target_points = 0 | ||
base_points += target_points | ||
return base_points | ||
|
||
/datum/objective/overthrow_AI // Basically a shared objective. All teams get the same amount of points, but obviously the one controlling the AI will have more power ingame. | ||
explanation_text = "Enslave the AI using the special AI module board in your storage implant. It is required you use said module." | ||
|
||
/datum/objective/overthrow_AI/check_completion() // Also returns points, just AIPTS. If you simply kill the Ai you get nothing, you need it to overthrow the heads. | ||
for(var/i in GLOB.ai_list) | ||
var/mob/living/silicon/ai/AI = i | ||
if(AI.laws && AI.laws.id == "overthrow") | ||
return AIPTS | ||
|
||
/datum/objective/overthrow_target | ||
|
||
/datum/objective/overthrow_target/update_explanation_text() | ||
if(target) | ||
explanation_text = "Convert, exile or kill [target.name], the [target.assigned_role]." | ||
|
||
/datum/objective/overthrow_target/is_unique_objective(datum/mind/possible_target) | ||
if(possible_target.assigned_role in GLOB.command_positions) | ||
return FALSE | ||
return TRUE | ||
|
||
/datum/objective/overthrow_target/check_completion() | ||
var/base_points | ||
if(target && target.current) | ||
base_points = TARGETPTS | ||
var/datum/antagonist/overthrow/O = target.has_antag_datum(/datum/antagonist/overthrow) | ||
if(target.current.stat == DEAD) | ||
base_points *= KILLED | ||
else if(!is_station_level(target.current.z) && !is_centcom_level(target.current.z)) // exiled. | ||
base_points *= EXILED | ||
else if(O) | ||
base_points *= CONVERTED | ||
if(team == O.team) | ||
base_points *= CONVERTED_OURS | ||
else | ||
base_points = 0 | ||
return base_points |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
/datum/game_mode/overthrow | ||
name = "overthrow" | ||
config_tag = "overthrow" | ||
antag_flag = ROLE_TRAITOR // they are traitors after all, with a twist | ||
restricted_jobs = list("Security Officer", "Warden", "Detective", "AI", "Cyborg","Captain", "Head of Personnel", "Head of Security", "Chief Engineer", "Research Director", "Chief Medical Officer") | ||
required_players = 1 //20 // the core idea is of a swift, bloodless coup, so it shouldn't be as chaotic as revs. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. gotta reset those values once the PR is ready |
||
required_enemies = 1 //2 minimum two teams, otherwise it's just nerfed revs. | ||
recommended_enemies = 4 | ||
|
||
announce_span = "danger" | ||
announce_text = "There are sleeping Syndicate agents on the station who are trying to stage a coup!\n\ | ||
<span class='danger'>Agents</span>: Accomplish your objectives, convert heads and targets, take control of the AI.\n\ | ||
<span class='notice'>Crew</span>: Do not let the agents succeed!" | ||
var/list/initial_agents = list() // Why doesn't this exist at /game_mode level? Literally every gamemode has some sort of version for this, what the fuck | ||
|
||
/datum/game_mode/overthrow/pre_setup() | ||
|
||
if(CONFIG_GET(flag/protect_roles_from_antagonist)) | ||
restricted_jobs += protected_jobs | ||
|
||
if(CONFIG_GET(flag/protect_assistant_from_antagonist)) | ||
restricted_jobs += "Assistant" | ||
|
||
var/sleeping_agents = required_enemies + round(num_players()*0.1) | ||
|
||
for (var/i in 1 to sleeping_agents) | ||
if (!antag_candidates.len) | ||
break | ||
var/datum/mind/sleeping_agent = antag_pick(antag_candidates) | ||
antag_candidates -= sleeping_agent | ||
initial_agents += sleeping_agent | ||
sleeping_agent.restricted_roles = restricted_jobs | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're not setting special role anywhere so it's possible for them to be kicked back to lobby and other unpleasantness. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. that's a thing?what the fuck, why does the special_role var still exist |
||
|
||
if(initial_agents.len < required_enemies) | ||
setup_error = "Not enough initial sleeping agents candidates" | ||
return FALSE | ||
return TRUE | ||
|
||
/datum/game_mode/overthrow/post_setup() | ||
for(var/i in initial_agents) // each agent will have its own team. | ||
var/datum/mind/agent = i | ||
agent.add_antag_datum(/datum/antagonist/overthrow/boss) // create_team called on_gain will create the team | ||
return ..() | ||
|
||
/datum/game_mode/overthrow/generate_report() | ||
return "Some sleeping agents have managed to get aboard. Their objective is to stage a coup and take over the station stealthly." |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
/obj/item/implant/overthrow | ||
name = "overthrow implant" | ||
desc = "Wakes up syndicate sleeping agents." | ||
uses = 2 | ||
|
||
/obj/item/implant/overthrow/implant(mob/living/target, mob/user) // Should probably also delete any mindshield implant. Not sure. | ||
if(target && target.mind && user && user.mind) | ||
var/datum/mind/target_mind = target.mind | ||
var/datum/mind/user_mind = user.mind | ||
var/datum/antagonist/overthrow/TO = target_mind.has_antag_datum(/datum/antagonist/overthrow) | ||
var/datum/antagonist/overthrow/UO = user_mind.has_antag_datum(/datum/antagonist/overthrow) | ||
if(!UO) | ||
to_chat(user, "<span class='danger'>You don't know how to use this thing!</span>") // It needs a valid team to work, if you aren't an antag don't use this thing | ||
return FALSE | ||
if(TO) | ||
to_chat(user, "<span class='notice'>[target.name] woke up already, the implant would be ineffective against him!</span>") | ||
return FALSE | ||
target_mind.add_antag_datum(/datum/antagonist/overthrow, UO.team) | ||
log_combat(user, target, "implanted", "\a [name]") | ||
qdel(src) | ||
return TRUE | ||
|
||
/obj/item/implanter/overthrow | ||
name = "implanter (overthrow)" | ||
imp_type = /obj/item/implant/overthrow | ||
|
||
/obj/item/implantcase/overthrow | ||
name = "implant case - 'overthrow'" | ||
desc = "A glass case containing an overthrow implant." | ||
imp_type = /obj/item/implant/overthrow |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
#define INITIAL_CRYSTALS 5 // initial telecrystals in the boss' uplink | ||
|
||
/datum/antagonist/overthrow | ||
name = "Syndicate mutineer" | ||
roundend_category = "syndicate mutineers" | ||
antagpanel_category = "Syndicate Mutineers" | ||
job_rank = ROLE_TRAITOR | ||
var/datum/team/overthrow/team | ||
var/static/list/possible_useful_items | ||
|
||
/datum/antagonist/overthrow/New() | ||
..() | ||
if(!possible_useful_items) | ||
possible_useful_items = list(/obj/item/gun/ballistic/automatic/pistol, /obj/item/storage/box/syndie_kit/throwing_weapons, /obj/item/pen/edagger, /obj/item/pen/sleepy, \ | ||
/obj/item/soap/syndie, /obj/item/card/id/syndicate, /obj/item/storage/box/syndie_kit/chameleon) | ||
|
||
/datum/antagonist/overthrow/on_gain() | ||
create_team() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You don't need to call it manually, |
||
objectives += team.objectives | ||
owner.objectives += objectives | ||
..() | ||
equip_overthrow() | ||
|
||
/datum/antagonist/overthrow/create_team() | ||
if(!team) | ||
team = new() | ||
team.add_member(owner) | ||
team.create_objectives() | ||
var/team_name = stripped_input(owner, "Name your team:", "Team name", , MAX_NAME_LEN) | ||
if(!team_name) | ||
to_chat(owner, "<span class='danger'>You must give a name to your team!</span>") | ||
create_team() | ||
return | ||
team.name = team_name | ||
else | ||
team.add_member(owner) | ||
|
||
/datum/antagonist/overthrow/apply_innate_effects() | ||
..() | ||
if(owner.assigned_role == "Clown") | ||
var/mob/living/carbon/human/traitor_mob = owner.current | ||
if(traitor_mob && istype(traitor_mob)) | ||
if(!silent) | ||
to_chat(traitor_mob, "Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself.") | ||
traitor_mob.dna.remove_mutation(CLOWNMUT) | ||
update_overthrow_icons_added() | ||
|
||
/datum/antagonist/overthrow/remove_innate_effects() | ||
update_overthrow_icons_removed() | ||
if(owner.assigned_role == "Clown") | ||
var/mob/living/carbon/human/traitor_mob = owner.current | ||
if(traitor_mob && istype(traitor_mob)) | ||
traitor_mob.dna.add_mutation(CLOWNMUT) | ||
..() | ||
// there is no promote command because there's no difference between a boss and the normal dude other than roundstart getting the items | ||
/datum/antagonist/overthrow/get_admin_commands() | ||
. = ..() | ||
.["Give random item"] = CALLBACK(src,.proc/equip_overthrow) | ||
.["Give overthrow boss equip"] = CALLBACK(src,.proc/equip_overthrow_boss) | ||
|
||
/datum/antagonist/overthrow/proc/update_overthrow_icons_added(datum/mind/traitor_mind) | ||
var/datum/atom_hud/antag/overthrowhud = GLOB.huds[team.hud_entry_num] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You should probably remove these at some point if team gets deleted. |
||
if(!overthrowhud) | ||
overthrowhud = new() | ||
team.hud_entry_num = GLOB.huds.len + 1 // the index of the hud inside huds list | ||
GLOB.huds += overthrowhud | ||
overthrowhud.join_hud(owner.current) | ||
set_antag_hud(owner.current, "traitor") | ||
|
||
/datum/antagonist/overthrow/proc/update_overthrow_icons_removed(datum/mind/traitor_mind) | ||
var/datum/atom_hud/antag/overthrowhud = GLOB.huds[team.hud_entry_num] | ||
if(overthrowhud) | ||
overthrowhud.leave_hud(owner.current) | ||
set_antag_hud(owner.current, null) | ||
|
||
// Gives the storage implant with a random item. They're sleeping agents, after all. | ||
/datum/antagonist/overthrow/proc/equip_overthrow() | ||
if(!owner || !owner.current) | ||
return | ||
var/obj/item/implant/storage/S = locate(/obj/item/implant/storage) in owner.current | ||
if(!S) | ||
S = new(owner.current) | ||
S.implant(owner.current) | ||
var/I = pick(possible_useful_items) | ||
if(ispath(I)) // in case some admin decides to fuck the list up for fun | ||
I = new I() | ||
SEND_SIGNAL(S, COMSIG_TRY_STORAGE_INSERT, I, null, TRUE, TRUE) | ||
|
||
/datum/antagonist/overthrow/proc/equip_overthrow_boss() | ||
if(!owner || !owner.current) | ||
return | ||
var/mob/living/carbon/human/H = owner.current | ||
// Give uplink | ||
var/obj/item/uplink_holder = owner.equip_traitor(uplink_owner = src) | ||
var/datum/component/uplink/uplink = uplink_holder.GetComponent(/datum/component/uplink) | ||
uplink.telecrystals = INITIAL_CRYSTALS | ||
// Give AI hacking board | ||
var/obj/item/aiModule/core/full/overthrow/O = new(H) | ||
var/list/slots = list ( | ||
"backpack" = SLOT_IN_BACKPACK, | ||
"left pocket" = SLOT_L_STORE, | ||
"right pocket" = SLOT_R_STORE | ||
) | ||
var/where = H.equip_in_one_of_slots(O, slots) | ||
if (!where) | ||
to_chat(H, "The Syndicate were unfortunately unable to get you the AI module.") | ||
else | ||
to_chat(H, "Use the AI board in your [where] to take control of the AI, as requested by the Syndicate.") | ||
// Give the implant converter | ||
var/obj/item/implanter/overthrow/I = new(H) | ||
where = H.equip_in_one_of_slots(I, slots) | ||
if (!where) | ||
to_chat(H, "The Syndicate were unfortunately unable to get you a converter implant.") | ||
else | ||
to_chat(H, "Use the implanter in your [where] to wake up sleeping syndicate agents, so that they can aid you.") | ||
|
||
/datum/antagonist/overthrow/get_team() | ||
return team | ||
|
||
/datum/antagonist/overthrow/greet() | ||
to_chat(owner.current, "<B><font size=3 color=red>You are a syndicate sleeping agent. Your job is to stage a swift, fairly bloodless coup. </font></B>") | ||
|
||
/datum/antagonist/overthrow/boss | ||
name = "Syndicate initial eutineers" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. typo |
||
|
||
/datum/antagonist/overthrow/boss/on_gain() | ||
..() | ||
equip_overthrow_boss() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
/datum/team/overthrow | ||
name = "overthrow" // The team name is set on creation by the leader. | ||
member_name = "syndicate agent" | ||
var/hud_entry_num // A number holding the hud's index inside 'huds' global list. Gets set on hud update, if a hud doesn't exist already. Must be a number, otherwise BYOND shits up with assoc lists and everything goes to hell. | ||
|
||
/datum/team/overthrow/proc/create_objectives() | ||
// Heads objective | ||
var/datum/objective/ovethrow_heads/heads = new() | ||
heads.team = src | ||
heads.find_target() | ||
objectives += heads | ||
// AI objective | ||
var/datum/objective/overthrow_AI/AI = new() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Didn't set owner here. |
||
objectives += AI | ||
// Target objective | ||
var/datum/objective/overthrow_target/target = new() | ||
target.team = src | ||
target.find_target() | ||
objectives += target |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Blown up don't count ?