diff --git a/code/__DEFINES/role_preferences.dm b/code/__DEFINES/role_preferences.dm
index 9af7868e33fb..dc9fbe6fe3b4 100644
--- a/code/__DEFINES/role_preferences.dm
+++ b/code/__DEFINES/role_preferences.dm
@@ -41,6 +41,7 @@ GLOBAL_LIST_INIT(special_roles, list(
ROLE_BLOB = /datum/game_mode/blob,
ROLE_NINJA,
ROLE_MONKEY = /datum/game_mode/monkey,
+ ROLE_GANG = /datum/game_mode/gang,
ROLE_REVENANT,
ROLE_ABDUCTOR = /datum/game_mode/abduction,
ROLE_DEVIL = /datum/game_mode/devil,
diff --git a/code/datums/hud.dm b/code/datums/hud.dm
index 9c37b46d105d..f9444898ceb2 100644
--- a/code/datums/hud.dm
+++ b/code/datums/hud.dm
@@ -74,7 +74,12 @@ GLOBAL_LIST_INIT(huds, list(
//MOB PROCS
/mob/proc/reload_huds()
- for(var/datum/atom_hud/hud in (GLOB.huds|GLOB.active_alternate_appearances))
+ var/gang_huds = list()
+ if(SSticker.mode)
+ for(var/datum/gang/G in SSticker.mode.gangs)
+ gang_huds += G.ganghud
+
+ for(var/datum/atom_hud/hud in (GLOB.huds|gang_huds|GLOB.active_alternate_appearances))
if(hud && hud.hudusers[src])
hud.add_hud_to(src)
diff --git a/code/datums/mind.dm b/code/datums/mind.dm
index 687dcf7daedb..439b178dc1ee 100644
--- a/code/datums/mind.dm
+++ b/code/datums/mind.dm
@@ -215,6 +215,11 @@
remove_objectives()
remove_antag_equip()
+
+/datum/mind/proc/remove_gang()
+ SSticker.mode.remove_gangster(src,0,1,1)
+ remove_objectives()
+
/datum/mind/proc/remove_antag_equip()
var/list/Mob_Contents = current.get_contents()
for(var/obj/item/I in Mob_Contents)
@@ -233,11 +238,15 @@
remove_wizard()
remove_cultist()
remove_rev()
+ remove_gang()
SSticker.mode.update_changeling_icons_removed(src)
SSticker.mode.update_traitor_icons_removed(src)
SSticker.mode.update_wiz_icons_removed(src)
SSticker.mode.update_cult_icons_removed(src)
SSticker.mode.update_rev_icons_removed(src)
+ if(gang_datum)
+ gang_datum.remove_gang_hud(src)
+
//Link a new mobs mind to the creator of said mob. They will join any team they are currently on, and will only switch teams when their creator does.
@@ -245,6 +254,9 @@
if(iscultist(creator))
SSticker.mode.add_cultist(src)
+ else if(is_gangster(creator))
+ SSticker.mode.add_gangster(src, creator.mind.gang_datum, TRUE)
+
else if(is_revolutionary_in_general(creator))
SSticker.mode.add_revolutionary(src)
@@ -342,6 +354,46 @@
sections["revolution"] = text
+ /** GANG ***/
+ text = "gang"
+ if (SSticker.mode.config_tag=="gang")
+ text = uppertext(text)
+ text = "[text]: "
+ text += "[current.isloyal() ? "LOYAL" : "loyal"]|"
+ if(src in SSticker.mode.get_all_gangsters())
+ text += "none"
+ else
+ text += "NONE"
+
+ if(current && current.client && (ROLE_GANG in current.client.prefs.be_special))
+ text += "|Enabled in Prefs
"
+ else
+ text += "|Disabled in Prefs
"
+
+ for(var/datum/gang/G in SSticker.mode.gangs)
+ text += "[G.name]: "
+ if(src in (G.gangsters))
+ text += "GANGSTER"
+ else
+ text += "gangster"
+ text += "|"
+ if(src in (G.bosses))
+ text += "GANG LEADER"
+ text += "|Equipment: give"
+ var/list/L = current.get_contents()
+ var/obj/item/device/gangtool/gangtool = locate() in L
+ if (gangtool)
+ text += "|take"
+
+ else
+ text += "gang leader"
+ text += "
"
+
+ if(GLOB.gang_colors_pool.len)
+ text += "Create New Gang"
+
+ sections["gang"] = text
+
/** Abductors **/
text = "Abductor"
if(SSticker.mode.config_tag == "abductor")
@@ -854,6 +906,75 @@
flash.crit_fail = 0
flash.update_icon()
+
+
+//////////////////// GANG MODE
+
+ else if (href_list["gang"])
+ switch(href_list["gang"])
+ if("clear")
+ remove_gang()
+ message_admins("[key_name_admin(usr)] has de-gang'ed [current].")
+ log_admin("[key_name(usr)] has de-gang'ed [current].")
+
+ if("equip")
+ switch(SSticker.mode.equip_gang(current,gang_datum))
+ if(1)
+ to_chat(usr, "Unable to equip territory spraycan!")
+ if(2)
+ to_chat(usr, "Unable to equip recruitment pen and spraycan!")
+ if(3)
+ to_chat(usr, "Unable to equip gangtool, pen, and spraycan!")
+
+ if("takeequip")
+ var/list/L = current.get_contents()
+ for(var/obj/item/weapon/pen/gang/pen in L)
+ qdel(pen)
+ for(var/obj/item/device/gangtool/gangtool in L)
+ qdel(gangtool)
+ for(var/obj/item/toy/crayon/spraycan/gang/SC in L)
+ qdel(SC)
+
+ if("new")
+ if(GLOB.gang_colors_pool.len)
+ var/list/names = list("Random") + GLOB.gang_name_pool
+ var/gangname = input("Pick a gang name.","Select Name") as null|anything in names
+ if(gangname && GLOB.gang_colors_pool.len) //Check again just in case another admin made max gangs at the same time
+ if(!(gangname in GLOB.gang_name_pool))
+ gangname = null
+ var/datum/gang/newgang = new(null,gangname)
+ SSticker.mode.gangs += newgang
+ message_admins("[key_name_admin(usr)] has created the [newgang.name] Gang.")
+ log_admin("[key_name(usr)] has created the [newgang.name] Gang.")
+
+ else if (href_list["gangboss"])
+ var/datum/gang/G = locate(href_list["gangboss"]) in SSticker.mode.gangs
+ if(!G || (src in G.bosses))
+ return
+ SSticker.mode.remove_gangster(src,0,2,1)
+ G.bosses += src
+ gang_datum = G
+ special_role = "[G.name] Gang Boss"
+ G.add_gang_hud(src)
+ to_chat(current, "You are a [G.name] Gang Boss!")
+ message_admins("[key_name_admin(usr)] has added [current] to the [G.name] Gang leadership.")
+ log_admin("[key_name(usr)] has added [current] to the [G.name] Gang leadership.")
+ SSticker.mode.forge_gang_objectives(src)
+ SSticker.mode.greet_gang(src,0)
+
+ else if (href_list["gangster"])
+ var/datum/gang/G = locate(href_list["gangster"]) in SSticker.mode.gangs
+ if(!G || (src in G.gangsters))
+ return
+ SSticker.mode.remove_gangster(src,0,2,1)
+ SSticker.mode.add_gangster(src,G,0)
+ message_admins("[key_name_admin(usr)] has added [current] to the [G.name] Gang (A).")
+ log_admin("[key_name(usr)] has added [current] to the [G.name] Gang (A).")
+
+/////////////////////////////////
+
+
+
else if (href_list["cult"])
switch(href_list["cult"])
if("clear")
@@ -1356,6 +1477,16 @@
// fail |= !SSticker.mode.equip_traitor(current, 1)
fail |= !SSticker.mode.equip_revolutionary(current)
+
+/datum/mind/proc/make_Gang(datum/gang/G)
+ special_role = "[G.name] Gang Boss"
+ G.bosses += src
+ gang_datum = G
+ G.add_gang_hud(src)
+ SSticker.mode.forge_gang_objectives(src)
+ SSticker.mode.greet_gang(src)
+ SSticker.mode.equip_gang(current,G)
+
/datum/mind/proc/make_Abductor()
var/role = alert("Abductor Role ?","Role","Agent","Scientist")
var/team = input("Abductor Team ?","Team ?") in list(1,2,3,4)
diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm
index 80f27e8b3312..fa93dbf61cf0 100644
--- a/code/game/gamemodes/game_mode.dm
+++ b/code/game/gamemodes/game_mode.dm
@@ -538,6 +538,7 @@
/datum/game_mode/proc/remove_antag_for_borging(datum/mind/newborgie)
SSticker.mode.remove_cultist(newborgie, 0, 0)
SSticker.mode.remove_revolutionary(newborgie, 0)
+ SSticker.mode.remove_gangster(newborgie, 0, remove_bosses=1)
/datum/game_mode/proc/generate_station_goals()
var/list/possible = list()
diff --git a/code/game/gamemodes/gang/dominator.dm b/code/game/gamemodes/gang/dominator.dm
new file mode 100644
index 000000000000..54099070cdd3
--- /dev/null
+++ b/code/game/gamemodes/gang/dominator.dm
@@ -0,0 +1,188 @@
+/obj/machinery/dominator
+ name = "dominator"
+ desc = "A visibly sinister device. Looks like you can break it if you hit it enough."
+ icon = 'icons/obj/machines/dominator.dmi'
+ icon_state = "dominator"
+ density = 1
+ anchored = 1
+ layer = HIGH_OBJ_LAYER
+ max_integrity = 300
+ obj_integrity = 300
+ integrity_failure = 100
+ armor = list(melee = 20, bullet = 50, laser = 50, energy = 50, bomb = 10, bio = 100, rad = 100, fire = 10, acid = 70)
+ var/datum/gang/gang
+ var/operating = 0 //0=standby or broken, 1=takeover
+ var/warned = 0 //if this device has set off the warning at <3 minutes yet
+ var/datum/effect_system/spark_spread/spark_system
+ var/obj/effect/countdown/dominator/countdown
+
+/obj/machinery/dominator/tesla_act()
+ qdel(src)
+
+/obj/machinery/dominator/New()
+ ..()
+ set_light(2)
+ GLOB.poi_list |= src
+ spark_system = new
+ spark_system.set_up(5, TRUE, src)
+ countdown = new(src)
+
+/obj/machinery/dominator/examine(mob/user)
+ ..()
+ if(stat & BROKEN)
+ return
+
+ var/time
+ if(gang && gang.is_dominating)
+ time = gang.domination_time_remaining()
+ if(time > 0)
+ to_chat(user, "Hostile Takeover in progress. Estimated [time] seconds remain.")
+ else
+ to_chat(user, "Hostile Takeover of [station_name()] successful. Have a great day.")
+ else
+ to_chat(user, "System on standby.")
+ to_chat(user, "System Integrity: [round((obj_integrity/max_integrity)*100,1)]%")
+
+/obj/machinery/dominator/process()
+ ..()
+ if(gang && gang.is_dominating)
+ var/time_remaining = gang.domination_time_remaining()
+ if(time_remaining > 0)
+ . = TRUE
+ playsound(loc, 'sound/items/timer.ogg', 10, 0)
+ if(!warned && (time_remaining < 180))
+ warned = 1
+ var/area/domloc = get_area(loc)
+ gang.message_gangtools("Less than 3 minutes remains in hostile takeover. Defend your dominator at [domloc.map_name]!")
+ for(var/datum/gang/G in SSticker.mode.gangs)
+ if(G != gang)
+ G.message_gangtools("WARNING: [gang.name] Gang takeover imminent. Their dominator at [domloc.map_name] must be destroyed!",1,1)
+
+ if(!.)
+ STOP_PROCESSING(SSmachines, src)
+
+/obj/machinery/dominator/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0)
+ switch(damage_type)
+ if(BRUTE)
+ if(damage_amount)
+ playsound(src, 'sound/effects/bang.ogg', 50, 1)
+ else
+ playsound(loc, 'sound/weapons/tap.ogg', 50, 1)
+ if(BURN)
+ playsound(src.loc, 'sound/items/Welder.ogg', 100, 1)
+
+/obj/machinery/dominator/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1)
+ . = ..()
+ if(.)
+ if(obj_integrity/max_integrity > 0.66)
+ if(prob(damage_amount*2))
+ spark_system.start()
+ else if(!(stat & BROKEN))
+ spark_system.start()
+ cut_overlays()
+ add_overlay("damage")
+
+/obj/machinery/dominator/obj_break(damage_flag)
+ if(!(stat & BROKEN) && !(flags & NODECONSTRUCT))
+ set_broken()
+
+/obj/machinery/dominator/deconstruct(disassembled = TRUE)
+ if(!(flags & NODECONSTRUCT))
+ if(!(stat & BROKEN))
+ set_broken()
+ new /obj/item/stack/sheet/plasteel(src.loc)
+ qdel(src)
+
+/obj/machinery/dominator/attacked_by(obj/item/I, mob/living/user)
+ add_fingerprint(user)
+ ..()
+
+/obj/machinery/dominator/proc/set_broken()
+ if(gang)
+ gang.is_dominating = FALSE
+
+ var/takeover_in_progress = 0
+ for(var/datum/gang/G in SSticker.mode.gangs)
+ if(G.is_dominating)
+ takeover_in_progress = 1
+ break
+ if(!takeover_in_progress)
+ var/was_stranded = SSshuttle.emergency.mode == SHUTTLE_STRANDED
+ SSshuttle.clearHostileEnvironment(src)
+ if(!was_stranded)
+ priority_announce("All hostile activity within station systems has ceased.","Network Alert")
+
+ if(get_security_level() == "delta")
+ set_security_level("red")
+
+ gang.message_gangtools("Hostile takeover cancelled: Dominator is no longer operational.[gang.dom_attempts ? " You have [gang.dom_attempts] attempt remaining." : " The station network will have likely blocked any more attempts by us."]",1,1)
+
+ set_light(0)
+ icon_state = "dominator-broken"
+ cut_overlays()
+ operating = 0
+ stat |= BROKEN
+ STOP_PROCESSING(SSmachines, src)
+
+/obj/machinery/dominator/Destroy()
+ if(!(stat & BROKEN))
+ set_broken()
+ GLOB.poi_list.Remove(src)
+ gang = null
+ QDEL_NULL(spark_system)
+ QDEL_NULL(countdown)
+ STOP_PROCESSING(SSmachines, src)
+ return ..()
+
+/obj/machinery/dominator/emp_act(severity)
+ take_damage(100, BURN, "energy", 0)
+ ..()
+
+/obj/machinery/dominator/attack_hand(mob/user)
+ if(operating || (stat & BROKEN))
+ examine(user)
+ return
+
+ var/datum/gang/tempgang
+
+ if(user.mind in SSticker.mode.get_all_gangsters())
+ tempgang = user.mind.gang_datum
+ else
+ examine(user)
+ return
+
+ if(tempgang.is_dominating)
+ to_chat(user, "Error: Hostile Takeover is already in progress.")
+ return
+
+ if(!tempgang.dom_attempts)
+ to_chat(user, "Error: Unable to breach station network. Firewall has logged our signature and is blocking all further attempts.")
+ return
+
+ var/time = round(determine_domination_time(tempgang)/60,0.1)
+ if(alert(user,"With [round((tempgang.territory.len/GLOB.start_state.num_territories)*100, 1)]% station control, a takeover will require [time] minutes.\nYour gang will be unable to gain influence while it is active.\nThe entire station will likely be alerted to it once it starts.\nYou have [tempgang.dom_attempts] attempt(s) remaining. Are you ready?","Confirm","Ready","Later") == "Ready")
+ if((tempgang.is_dominating) || !tempgang.dom_attempts || !in_range(src, user) || !isturf(loc))
+ return 0
+
+ var/area/A = get_area(loc)
+ var/locname = A.map_name
+
+ gang = tempgang
+ gang.dom_attempts --
+ priority_announce("Network breach detected in [locname]. The [gang.name] Gang is attempting to seize control of the station!","Network Alert")
+ gang.domination()
+ SSshuttle.registerHostileEnvironment(src)
+ src.name = "[gang.name] Gang [src.name]"
+ operating = 1
+ icon_state = "dominator-[gang.color]"
+
+ countdown.color = gang.color_hex
+ countdown.start()
+
+ set_light(3)
+ START_PROCESSING(SSmachines, src)
+
+ gang.message_gangtools("Hostile takeover in progress: Estimated [time] minutes until victory.[gang.dom_attempts ? "" : " This is your final attempt."]")
+ for(var/datum/gang/G in SSticker.mode.gangs)
+ if(G != gang)
+ G.message_gangtools("Enemy takeover attempt detected in [locname]: Estimated [time] minutes until our defeat.",1,1)
diff --git a/code/game/gamemodes/gang/gang.dm b/code/game/gamemodes/gang/gang.dm
new file mode 100644
index 000000000000..9c31edf55fb9
--- /dev/null
+++ b/code/game/gamemodes/gang/gang.dm
@@ -0,0 +1,318 @@
+//gang.dm
+//Gang War Game Mode
+
+GLOBAL_LIST_INIT(gang_name_pool, list("Clandestine", "Prima", "Zero-G", "Max", "Blasto", "Waffle", "North", "Omni", "Newton", "Cyber", "Donk", "Gene", "Gib", "Tunnel", "Diablo", "Psyke", "Osiron", "Sirius", "Sleeping Carp"))
+GLOBAL_LIST_INIT(gang_colors_pool, list("red","orange","yellow","green","blue","purple"))
+
+/datum/game_mode
+ var/list/datum/gang/gangs = list()
+ var/datum/gang_points/gang_points
+
+/proc/is_gangster(var/mob/living/M)
+ return istype(M) && M.mind && M.mind.gang_datum
+
+/proc/is_in_gang(var/mob/living/M, var/gang_type)
+ if(!is_gangster(M) || !gang_type)
+ return 0
+ var/datum/gang/G = M.mind.gang_datum
+ if(G.name == gang_type)
+ return 1
+ return 0
+
+/datum/game_mode/gang
+ name = "gang war"
+ config_tag = "gang"
+ antag_flag = ROLE_GANG
+ 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 = 20
+ required_enemies = 2
+ recommended_enemies = 2
+ enemy_minimum_age = 14
+
+ announce_span = "danger"
+ announce_text = "A violent turf war has erupted on the station!\n\
+ Gangsters: Take over the station with a dominator.\n\
+ Crew: Prevent the gangs from expanding and initiating takeover."
+
+///////////////////////////////////////////////////////////////////////////////
+//Gets the round setup, cancelling if there's not enough players at the start//
+///////////////////////////////////////////////////////////////////////////////
+/datum/game_mode/gang/pre_setup()
+ if(config.protect_roles_from_antagonist)
+ restricted_jobs += protected_jobs
+
+ if(config.protect_assistant_from_antagonist)
+ restricted_jobs += "Assistant"
+
+ //Spawn more bosses depending on server population
+ var/gangs_to_create = 2
+ if(prob(num_players() * 2))
+ gangs_to_create ++
+
+ for(var/i=1 to gangs_to_create)
+ if(!antag_candidates.len)
+ break
+
+ //Create the gang
+ var/datum/gang/G = new()
+ gangs += G
+
+ //Now assign a boss for the gang
+ var/datum/mind/boss = pick(antag_candidates)
+ antag_candidates -= boss
+ G.bosses += boss
+ boss.gang_datum = G
+ boss.special_role = "[G.name] Gang Boss"
+ boss.restricted_roles = restricted_jobs
+ log_game("[boss.key] has been selected as the Boss for the [G.name] Gang")
+
+ if(gangs.len < 2) //Need at least two gangs
+ return 0
+
+ return 1
+
+
+/datum/game_mode/gang/post_setup()
+ set waitfor = FALSE
+ ..()
+ sleep(rand(10,100))
+ for(var/datum/gang/G in gangs)
+ for(var/datum/mind/boss_mind in G.bosses)
+ G.add_gang_hud(boss_mind)
+ forge_gang_objectives(boss_mind)
+ greet_gang(boss_mind)
+ equip_gang(boss_mind.current,G)
+ modePlayer += boss_mind
+
+
+/datum/game_mode/proc/forge_gang_objectives(datum/mind/boss_mind)
+ var/datum/objective/rival_obj = new
+ rival_obj.owner = boss_mind
+ rival_obj.explanation_text = "Be the first gang to successfully takeover the station with a Dominator."
+ boss_mind.objectives += rival_obj
+
+/datum/game_mode/proc/greet_gang(datum/mind/boss_mind, you_are=1)
+ if (you_are)
+ to_chat(boss_mind.current, "You are the Boss of the [boss_mind.gang_datum.name] Gang!")
+ boss_mind.announce_objectives()
+
+///////////////////////////////////////////////////////////////////////////
+//This equips the bosses with their gear, and makes the clown not clumsy//
+///////////////////////////////////////////////////////////////////////////
+/datum/game_mode/proc/equip_gang(mob/living/carbon/human/mob, gang)
+ if(!istype(mob))
+ return
+
+ if (mob.mind)
+ if (mob.mind.assigned_role == "Clown")
+ to_chat(mob, "Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself.")
+ mob.dna.remove_mutation(CLOWNMUT)
+
+ var/obj/item/device/gangtool/gangtool = new(mob)
+ var/obj/item/weapon/pen/gang/T = new(mob)
+ var/obj/item/toy/crayon/spraycan/gang/SC = new(mob,gang)
+ var/obj/item/clothing/glasses/hud/security/chameleon/C = new(mob,gang)
+
+ var/list/slots = list (
+ "backpack" = slot_in_backpack,
+ "left pocket" = slot_l_store,
+ "right pocket" = slot_r_store
+ )
+
+ . = 0
+
+ var/where = mob.equip_in_one_of_slots(gangtool, slots)
+ if (!where)
+ to_chat(mob, "Your Syndicate benefactors were unfortunately unable to get you a Gangtool.")
+ . += 1
+ else
+ gangtool.register_device(mob)
+ to_chat(mob, "The Gangtool in your [where] will allow you to purchase weapons and equipment, send messages to your gang, and recall the emergency shuttle from anywhere on the station.")
+ to_chat(mob, "As the gang boss, you can also promote your gang members to lieutenant. Unlike regular gangsters, Lieutenants cannot be deconverted and are able to use recruitment pens and gangtools.")
+
+ var/where2 = mob.equip_in_one_of_slots(T, slots)
+ if (!where2)
+ to_chat(mob, "Your Syndicate benefactors were unfortunately unable to get you a recruitment pen to start.")
+ . += 1
+ else
+ to_chat(mob, "The recruitment pen in your [where2] will help you get your gang started. Stab unsuspecting crew members with it to recruit them.")
+
+ var/where3 = mob.equip_in_one_of_slots(SC, slots)
+ if (!where3)
+ to_chat(mob, "Your Syndicate benefactors were unfortunately unable to get you a territory spraycan to start.")
+ . += 1
+ else
+ to_chat(mob, "The territory spraycan in your [where3] can be used to claim areas of the station for your gang. The more territory your gang controls, the more influence you get. All gangsters can use these, so distribute them to grow your influence faster.")
+
+ var/where4 = mob.equip_in_one_of_slots(C, slots)
+ if (!where4)
+ to_chat(mob, "Your Syndicate benefactors were unfortunately unable to get you a chameleon security HUD.")
+ . += 1
+ else
+ to_chat(mob, "The chameleon security HUD in your [where4] will help you keep track of who is mindshield-implanted, and unable to be recruited.")
+ return .
+
+
+///////////////////////////////////////////
+//Deals with converting players to a gang//
+///////////////////////////////////////////
+/datum/game_mode/proc/add_gangster(datum/mind/gangster_mind, datum/gang/G, check = 1)
+ if(!G || (gangster_mind in get_all_gangsters()) || (gangster_mind.enslaved_to && !is_gangster(gangster_mind.enslaved_to)))
+ return 0
+ if(check && gangster_mind.current.isloyal()) //Check to see if the potential gangster is implanted
+ return 1
+ G.gangsters += gangster_mind
+ gangster_mind.gang_datum = G
+ if(check)
+ if(iscarbon(gangster_mind.current))
+ var/mob/living/carbon/carbon_mob = gangster_mind.current
+ carbon_mob.silent = max(carbon_mob.silent, 5)
+ carbon_mob.flash_act(1, 1)
+ gangster_mind.current.Stun(5)
+ if(G.is_deconvertible)
+ to_chat(gangster_mind.current, "You are now a member of the [G.name] Gang!")
+ to_chat(gangster_mind.current, "Help your bosses take over the station by claiming territory with special spraycans only they can provide. Simply spray on any unclaimed area of the station.")
+ to_chat(gangster_mind.current, "Their ultimate objective is to take over the station with a Dominator machine.")
+ to_chat(gangster_mind.current, "You can identify your bosses by their large, bright [G.color] \[G\] icon.")
+ gangster_mind.store_memory("You are a member of the [G.name] Gang!")
+ gangster_mind.current.log_message("Has been converted to the [G.name] Gang!", INDIVIDUAL_ATTACK_LOG)
+ gangster_mind.special_role = "[G.name] Gangster"
+
+ G.add_gang_hud(gangster_mind)
+ if(jobban_isbanned(gangster_mind.current, ROLE_GANG))
+ INVOKE_ASYNC(src, /datum/game_mode.proc/replace_jobbaned_player, gangster_mind.current, ROLE_GANG, ROLE_GANG)
+ return 2
+////////////////////////////////////////////////////////////////////
+//Deals with players reverting to neutral (Not a gangster anymore)//
+////////////////////////////////////////////////////////////////////
+/datum/game_mode/proc/remove_gangster(datum/mind/gangster_mind, beingborged, silent, remove_bosses=0)
+ var/datum/gang/gang = gangster_mind.gang_datum
+ if(!gang)
+ return 0
+
+ var/removed
+
+ for(var/datum/gang/G in gangs)
+ if(!G.is_deconvertible && !remove_bosses)
+ return 0
+ if(gangster_mind in G.gangsters)
+ G.gangsters -= gangster_mind
+ removed = 1
+ if(remove_bosses && (gangster_mind in G.bosses))
+ G.bosses -= gangster_mind
+ removed = 1
+
+ if(!removed)
+ return 0
+
+ gangster_mind.special_role = null
+ gangster_mind.gang_datum = null
+
+ if(silent < 2)
+ gangster_mind.current.log_message("Has reformed and defected from the [gang.name] Gang!", INDIVIDUAL_ATTACK_LOG)
+
+ if(beingborged)
+ if(!silent)
+ gangster_mind.current.visible_message("The frame beeps contentedly from the MMI before initalizing it.")
+ to_chat(gangster_mind.current, "The frame's firmware detects and deletes your criminal behavior! You are no longer a gangster!")
+ message_admins("[ADMIN_LOOKUPFLW(gangster_mind.current)] has been borged while being a member of the [gang.name] Gang. They are no longer a gangster.")
+ else
+ if(!silent)
+ gangster_mind.current.Paralyse(5)
+ gangster_mind.current.visible_message("[gangster_mind.current] looks like they've given up the life of crime!")
+ to_chat(gangster_mind.current, "You have been reformed! You are no longer a gangster!
You try as hard as you can, but you can't seem to recall any of the identities of your former gangsters...")
+ gangster_mind.memory = ""
+
+ gang.remove_gang_hud(gangster_mind)
+ return 1
+
+////////////////
+//Helper Procs//
+////////////////
+
+/datum/game_mode/proc/get_all_gangsters()
+ var/list/all_gangsters = list()
+ all_gangsters += get_gangsters()
+ all_gangsters += get_gang_bosses()
+ return all_gangsters
+
+/datum/game_mode/proc/get_gangsters()
+ var/list/gangsters = list()
+ for(var/datum/gang/G in gangs)
+ gangsters += G.gangsters
+ return gangsters
+
+/datum/game_mode/proc/get_gang_bosses()
+ var/list/gang_bosses = list()
+ for(var/datum/gang/G in gangs)
+ gang_bosses += G.bosses
+ return gang_bosses
+
+/proc/determine_domination_time(var/datum/gang/G)
+ return max(180,900 - (round((G.territory.len/GLOB.start_state.num_territories)*100, 1) * 12))
+
+//////////////////////////////////////////////////////////////////////
+//Announces the end of the game with all relavent information stated//
+//////////////////////////////////////////////////////////////////////
+
+/datum/game_mode/proc/auto_declare_completion_gang(datum/gang/winner)
+ if(!gangs.len)
+ return
+ if(!winner)
+ to_chat(world, "The station was [station_was_nuked ? "destroyed!" : "evacuated before a gang could claim it! The station wins!"]
")
+ SSblackbox.set_details("round_end_result","loss - gangs failed takeover")
+
+ SSticker.news_report = GANG_LOSS
+ else
+ to_chat(world, "The [winner.name] Gang successfully performed a hostile takeover of the station!
")
+ SSblackbox.set_details("round_end_result","win - gang domination complete")
+
+ SSticker.news_report = GANG_TAKEOVER
+
+ for(var/datum/gang/G in gangs)
+ var/text = "The [G.name] Gang was [winner==G ? "victorious" : "defeated"] with [round((G.territory.len/GLOB.start_state.num_territories)*100, 1)]% control of the station!"
+ text += "
The [G.name] Gang Bosses were:"
+ for(var/datum/mind/boss in G.bosses)
+ text += printplayer(boss, 1)
+ text += "
The [G.name] Gangsters were:"
+ for(var/datum/mind/gangster in G.gangsters)
+ text += printplayer(gangster, 1)
+ text += "
"
+ to_chat(world, text)
+
+//////////////////////////////////////////////////////////
+//Handles influence, territories, and the victory checks//
+//////////////////////////////////////////////////////////
+
+/datum/gang_points
+ var/next_point_interval = 1800
+ var/next_point_time
+
+/datum/gang_points/New()
+ next_point_time = world.time + next_point_interval
+ START_PROCESSING(SSobj, src)
+
+/datum/gang_points/process(seconds)
+ var/list/winners = list() //stores the winners if there are any
+
+ for(var/datum/gang/G in SSticker.mode.gangs)
+ if(world.time > next_point_time)
+ G.income()
+
+ if(G.is_dominating)
+ if(G.domination_time_remaining() < 0)
+ winners += G
+
+ if(world.time > next_point_time)
+ next_point_time = world.time + next_point_interval
+
+ if(winners.len)
+ if(winners.len > 1) //Edge Case: If more than one dominator complete at the same time
+ for(var/datum/gang/G in winners)
+ G.domination(0.5)
+ priority_announce("Multiple station takeover attempts have made simultaneously. Conflicting takeover attempts appears to have restarted.","Network Alert")
+ else
+ SSticker.mode.explosion_in_progress = 1
+ SSticker.station_explosion_cinematic(1)
+ SSticker.mode.explosion_in_progress = 0
+ SSticker.force_ending = pick(winners)
diff --git a/code/game/gamemodes/gang/gang_datum.dm b/code/game/gamemodes/gang/gang_datum.dm
new file mode 100644
index 000000000000..3c54d5436b7f
--- /dev/null
+++ b/code/game/gamemodes/gang/gang_datum.dm
@@ -0,0 +1,265 @@
+//gang_datum.dm
+//Datum-based gangs
+
+/datum/gang
+ var/name = "ERROR"
+ var/color = "white"
+ var/color_hex = "#FFFFFF"
+ var/list/datum/mind/gangsters = list() //gang B Members
+ var/list/datum/mind/bosses = list() //gang A Bosses
+ var/list/obj/item/device/gangtool/gangtools = list()
+ var/style
+ var/fighting_style = "normal"
+ var/list/territory = list()
+ var/list/territory_new = list()
+ var/list/territory_lost = list()
+ var/dom_attempts = 2
+ var/points = 15
+ var/datum/atom_hud/antag/gang/ganghud
+ var/is_deconvertible = TRUE //Can you deconvert normal gangsters from the gang
+
+ var/domination_timer
+ var/is_dominating
+
+ var/item_list
+ var/item_category_list
+ var/buyable_items = list(
+ /datum/gang_item/function/gang_ping,
+ /datum/gang_item/function/recall,
+ /datum/gang_item/function/outfit,
+ /datum/gang_item/weapon/switchblade,
+ /datum/gang_item/weapon/pistol,
+ /datum/gang_item/weapon/ammo/pistol_ammo,
+ /datum/gang_item/weapon/uzi,
+ /datum/gang_item/weapon/ammo/uzi_ammo,
+ /datum/gang_item/equipment/spraycan,
+ /datum/gang_item/equipment/c4,
+ /datum/gang_item/equipment/implant_breaker,
+ /datum/gang_item/equipment/pen,
+ /datum/gang_item/equipment/gangtool,
+ /datum/gang_item/equipment/necklace,
+ /datum/gang_item/equipment/dominator
+ )
+
+/datum/gang/New(loc,gangname)
+ if(!GLOB.gang_colors_pool.len)
+ message_admins("WARNING: Maximum number of gangs have been exceeded!")
+ throw EXCEPTION("Maximum number of gangs has been exceeded")
+ return
+ else
+ color = pick(GLOB.gang_colors_pool)
+ GLOB.gang_colors_pool -= color
+ switch(color)
+ if("red")
+ color_hex = "#DA0000"
+ if("orange")
+ color_hex = "#FF9300"
+ if("yellow")
+ color_hex = "#FFF200"
+ if("green")
+ color_hex = "#A8E61D"
+ if("blue")
+ color_hex = "#00B7EF"
+ if("purple")
+ color_hex = "#DA00FF"
+
+ name = (gangname ? gangname : pick(GLOB.gang_name_pool))
+ GLOB.gang_name_pool -= name
+
+ ganghud = new()
+ ganghud.color = color_hex
+ log_game("The [name] Gang has been created. Their gang color is [color].")
+ build_item_list()
+
+/datum/gang/proc/build_item_list()
+ item_list = list()
+ item_category_list = list()
+ for(var/V in buyable_items)
+ var/datum/gang_item/G = new V()
+ item_list[G.id] = G
+ var/list/Cat = item_category_list[G.category]
+ if(Cat)
+ Cat += G
+ else
+ item_category_list[G.category] = list(G)
+
+/datum/gang/proc/add_gang_hud(datum/mind/recruit_mind)
+ ganghud.join_hud(recruit_mind.current)
+ SSticker.mode.set_antag_hud(recruit_mind.current, ((recruit_mind in bosses) ? "gang_boss" : "gangster"))
+
+/datum/gang/proc/remove_gang_hud(datum/mind/defector_mind)
+ ganghud.leave_hud(defector_mind.current)
+ SSticker.mode.set_antag_hud(defector_mind.current, null)
+
+/datum/gang/proc/domination(modifier=1)
+ set_domination_time(determine_domination_time(src) * modifier)
+ is_dominating = TRUE
+ set_security_level("delta")
+
+/datum/gang/proc/set_domination_time(d)
+ domination_timer = world.time + (10 * d)
+
+/datum/gang/proc/domination_time_remaining()
+ var/diff = domination_timer - world.time
+ return diff / 10
+//////////////////////////////////////////// OUTFITS
+
+
+//Used by recallers when purchasing a gang outfit. First time a gang outfit is purchased the buyer decides a gang style which is stored so gang outfits are uniform
+/datum/gang/proc/gang_outfit(mob/living/carbon/user,obj/item/device/gangtool/gangtool)
+ if(!user || !gangtool)
+ return 0
+ if(!gangtool.can_use(user))
+ return 0
+
+ var/gang_style_list = list("Gang Colors","Black Suits","White Suits","Leather Jackets","Leather Overcoats","Puffer Jackets","Military Jackets","Tactical Turtlenecks","Soviet Uniforms")
+ if(!style && (user.mind in SSticker.mode.get_gang_bosses())) //Only the boss gets to pick a style
+ style = input("Pick an outfit style.", "Pick Style") as null|anything in gang_style_list
+
+ if(gangtool.can_use(user) && (gangtool.outfits >= 1))
+ var/outfit_path
+ switch(style)
+ if(null || "Gang Colors")
+ outfit_path = text2path("/obj/item/clothing/under/color/[color]")
+ if("Black Suits")
+ outfit_path = /obj/item/clothing/under/suit_jacket/charcoal
+ if("White Suits")
+ outfit_path = /obj/item/clothing/under/suit_jacket/white
+ if("Puffer Jackets")
+ outfit_path = /obj/item/clothing/suit/jacket/puffer
+ if("Leather Jackets")
+ outfit_path = /obj/item/clothing/suit/jacket/leather
+ if("Leather Overcoats")
+ outfit_path = /obj/item/clothing/suit/jacket/leather/overcoat
+ if("Military Jackets")
+ outfit_path = /obj/item/clothing/suit/jacket/miljacket
+ if("Soviet Uniforms")
+ outfit_path = /obj/item/clothing/under/soviet
+ if("Tactical Turtlenecks")
+ outfit_path = /obj/item/clothing/under/syndicate
+
+ if(outfit_path)
+ var/obj/item/clothing/outfit = new outfit_path(user.loc)
+ outfit.armor = list(melee = 20, bullet = 30, laser = 10, energy = 10, bomb = 20, bio = 0, rad = 0, fire = 30, acid = 30)
+ outfit.desc += " Tailored for the [name] Gang to offer the wearer moderate protection against ballistics and physical trauma."
+ outfit.gang = src
+ user.put_in_hands(outfit)
+ return 1
+
+ return 0
+
+
+//////////////////////////////////////////// MESSAGING
+
+
+/datum/gang/proc/message_gangtools(message,beep=1,warning)
+ if(!gangtools.len || !message)
+ return
+ for(var/obj/item/device/gangtool/tool in gangtools)
+ var/mob/living/mob = get(tool.loc,/mob/living)
+ if(mob && mob.mind && mob.stat == CONSCIOUS)
+ if(mob.mind.gang_datum == src)
+ to_chat(mob, "\icon[tool] [message]")
+ return
+
+
+//////////////////////////////////////////// INCOME
+
+
+/datum/gang/proc/income()
+ if(!bosses.len)
+ return
+ var/added_names = ""
+ var/lost_names = ""
+
+ //Re-add territories that were reclaimed, so if they got tagged over, they can still earn income if they tag it back before the next status report
+ var/list/reclaimed_territories = territory_new & territory_lost
+ territory |= reclaimed_territories
+ territory_new -= reclaimed_territories
+ territory_lost -= reclaimed_territories
+
+ //Process lost territories
+ for(var/area in territory_lost)
+ if(lost_names != "")
+ lost_names += ", "
+ lost_names += "[territory_lost[area]]"
+ territory -= area
+
+ //Count uniformed gangsters
+ var/uniformed = 0
+ for(var/datum/mind/gangmind in (gangsters|bosses))
+ if(ishuman(gangmind.current))
+ var/mob/living/carbon/human/gangster = gangmind.current
+ //Gangster must be alive and on station
+ if((gangster.stat == DEAD) || (gangster.z > ZLEVEL_STATION))
+ continue
+
+ var/obj/item/clothing/outfit
+ var/obj/item/clothing/gang_outfit
+ if(gangster.w_uniform)
+ outfit = gangster.w_uniform
+ if(outfit.gang == src)
+ gang_outfit = outfit
+ if(gangster.wear_suit)
+ outfit = gangster.wear_suit
+ if(outfit.gang == src)
+ gang_outfit = outfit
+
+ if(gang_outfit)
+ to_chat(gangster, "The [src] Gang's influence grows as you wear [gang_outfit].")
+ uniformed ++
+
+ //Calculate and report influence growth
+ var/message = "[src] Gang Status Report:
*---------*
"
+ if(is_dominating)
+ var/seconds_remaining = domination_time_remaining()
+ var/new_time = max(180, seconds_remaining - (uniformed * 4) - (territory.len * 2))
+ if(new_time < seconds_remaining)
+ message += "Takeover shortened by [seconds_remaining - new_time] seconds for defending [territory.len] territories and [uniformed] uniformed gangsters.
"
+ set_domination_time(new_time)
+ message += "[seconds_remaining] seconds remain in hostile takeover.
"
+ else
+ var/points_new = min(999,points + 15 + (uniformed * 2) + territory.len)
+ if(points_new != points)
+ message += "Gang influence has increased by [points_new - points] for defending [territory.len] territories and [uniformed] uniformed gangsters.
"
+ points = points_new
+ message += "Your gang now has [points] influence.
"
+
+ //Process new territories
+ for(var/area in territory_new)
+ if(added_names != "")
+ added_names += ", "
+ added_names += "[territory_new[area]]"
+ territory += area
+
+ //Report territory changes
+ message += "[territory_new.len] new territories:
[added_names]
"
+ message += "[territory_lost.len] territories lost:
[lost_names]
"
+
+ //Clear the lists
+ territory_new = list()
+ territory_lost = list()
+
+ var/control = round((territory.len/GLOB.start_state.num_territories)*100, 1)
+ message += "Your gang now has [control]% control of the station.
*---------*"
+ message_gangtools(message)
+
+ //Increase outfit stock
+ for(var/obj/item/device/gangtool/tool in gangtools)
+ tool.outfits = min(tool.outfits+1,5)
+
+
+//Multiverse
+
+/datum/gang/multiverse
+ dom_attempts = 0
+ points = 0
+ fighting_style = "multiverse"
+ is_deconvertible = FALSE
+
+/datum/gang/multiverse/New(loc, multiverse_override)
+ name = multiverse_override
+ ganghud = new()
+
+/datum/gang/multiverse/income()
+ return
\ No newline at end of file
diff --git a/code/game/gamemodes/gang/gang_items.dm b/code/game/gamemodes/gang/gang_items.dm
new file mode 100644
index 000000000000..a8732c104692
--- /dev/null
+++ b/code/game/gamemodes/gang/gang_items.dm
@@ -0,0 +1,294 @@
+/datum/gang_item
+ var/name
+ var/item_path
+ var/cost
+ var/spawn_msg
+ var/category
+ var/id
+
+/datum/gang_item/proc/purchase(mob/living/carbon/user, datum/gang/gang, obj/item/device/gangtool/gangtool, check_canbuy = TRUE)
+ if(check_canbuy && !can_buy(user, gang, gangtool))
+ return FALSE
+ var/real_cost = get_cost(user, gang, gangtool)
+ if(gang && real_cost)
+ gang.message_gangtools("A [get_name_display(user, gang, gangtool)] was purchased by [user.real_name] for [real_cost] Influence.")
+ log_game("A [id] was purchased by [key_name(user)] ([gang.name] Gang) for [real_cost] Influence.")
+ gang.points -= real_cost
+ spawn_item(user, gang, gangtool)
+ return TRUE
+
+/datum/gang_item/proc/spawn_item(mob/living/carbon/user, datum/gang/gang, obj/item/device/gangtool/gangtool)
+ if(item_path)
+ var/obj/item/O = new item_path(user.loc)
+ user.put_in_hands(O)
+ if(spawn_msg)
+ to_chat(user, spawn_msg)
+
+/datum/gang_item/proc/can_buy(mob/living/carbon/user, datum/gang/gang, obj/item/device/gangtool/gangtool)
+ return gang && (gang.points >= get_cost(user, gang, gangtool)) && can_see(user, gang, gangtool)
+
+/datum/gang_item/proc/can_see(mob/living/carbon/user, datum/gang/gang, obj/item/device/gangtool/gangtool)
+ return TRUE
+
+/datum/gang_item/proc/get_cost(mob/living/carbon/user, datum/gang/gang, obj/item/device/gangtool/gangtool)
+ return cost
+
+/datum/gang_item/proc/get_cost_display(mob/living/carbon/user, datum/gang/gang, obj/item/device/gangtool/gangtool)
+ return "([get_cost(user, gang, gangtool)] Influence)"
+
+/datum/gang_item/proc/get_name_display(mob/living/carbon/user, datum/gang/gang, obj/item/device/gangtool/gangtool)
+ return name
+
+/datum/gang_item/proc/isboss(mob/living/carbon/user, datum/gang/gang)
+ return user && gang && (user.mind == gang.bosses[1])
+
+/datum/gang_item/proc/get_extra_info(mob/living/carbon/user, datum/gang/gang, obj/item/device/gangtool/gangtool)
+ return
+
+///////////////////
+//FUNCTIONS
+///////////////////
+
+/datum/gang_item/function
+ category = "Gangtool Functions:"
+ cost = 0
+
+/datum/gang_item/function/get_cost_display(mob/living/carbon/user, datum/gang/gang, obj/item/device/gangtool/gangtool)
+ return ""
+
+
+/datum/gang_item/function/gang_ping
+ name = "Send Message to Gang"
+ id = "gang_ping"
+
+/datum/gang_item/function/gang_ping/spawn_item(mob/living/carbon/user, datum/gang/gang, obj/item/device/gangtool/gangtool)
+ if(gangtool)
+ gangtool.ping_gang(user)
+
+
+/datum/gang_item/function/recall
+ name = "Recall Emergency Shuttle"
+ id = "recall"
+
+/datum/gang_item/function/recall/can_see(mob/living/carbon/user, datum/gang/gang, obj/item/device/gangtool/gangtool)
+ return isboss(user, gang)
+
+/datum/gang_item/function/recall/spawn_item(mob/living/carbon/user, datum/gang/gang, obj/item/device/gangtool/gangtool)
+ if(gangtool)
+ gangtool.recall(user)
+
+
+/datum/gang_item/function/outfit
+ name = "Create Armored Gang Outfit"
+ id = "outfit"
+
+/datum/gang_item/function/outfit/can_buy(mob/living/carbon/user, datum/gang/gang, obj/item/device/gangtool/gangtool)
+ return gangtool && (gangtool.outfits > 0) && ..()
+
+/datum/gang_item/function/outfit/get_cost_display(mob/living/carbon/user, datum/gang/gang, obj/item/device/gangtool/gangtool)
+ if(gangtool && !gangtool.outfits)
+ return "(Restocking)"
+ return ..()
+
+/datum/gang_item/function/outfit/spawn_item(mob/living/carbon/user, datum/gang/gang, obj/item/device/gangtool/gangtool)
+ if(gang && gang.gang_outfit(user, gangtool))
+ to_chat(user, "Gang Outfits can act as armor with moderate protection against ballistic and melee attacks. Every gangster wearing one will also help grow your gang's influence.")
+ if(gangtool)
+ gangtool.outfits -= 1
+
+///////////////////
+//WEAPONS
+///////////////////
+
+/datum/gang_item/weapon
+ category = "Purchase Weapons:"
+
+/datum/gang_item/weapon/ammo
+
+/datum/gang_item/weapon/ammo/get_cost_display(mob/living/carbon/user, datum/gang/gang, obj/item/device/gangtool/gangtool)
+ return " ↳" + ..() //this is pretty hacky but it looks nice on the popup
+
+/datum/gang_item/weapon/switchblade
+ name = "Switchblade"
+ id = "switchblade"
+ cost = 10
+ item_path = /obj/item/weapon/switchblade
+
+/datum/gang_item/weapon/pistol
+ name = "10mm Pistol"
+ id = "pistol"
+ cost = 25
+ item_path = /obj/item/weapon/gun/ballistic/automatic/pistol
+
+/datum/gang_item/weapon/ammo/pistol_ammo
+ name = "10mm Ammo"
+ id = "pistol_ammo"
+ cost = 10
+ item_path = /obj/item/ammo_box/magazine/m10mm
+
+/datum/gang_item/weapon/uzi
+ name = "Uzi SMG"
+ id = "uzi"
+ cost = 60
+ item_path = /obj/item/weapon/gun/ballistic/automatic/mini_uzi
+ id = "uzi"
+
+/datum/gang_item/weapon/ammo/uzi_ammo
+ name = "Uzi Ammo"
+ id = "uzi_ammo"
+ cost = 40
+ item_path = /obj/item/ammo_box/magazine/uzim9mm
+
+//SLEEPING CARP
+
+/datum/gang_item/weapon/bostaff
+ name = "Bo Staff"
+ id = "bostaff"
+ cost = 10
+ item_path = /obj/item/weapon/twohanded/bostaff
+
+/datum/gang_item/weapon/sleeping_carp_scroll
+ name = "Sleeping Carp Scroll (one-use)"
+ id = "sleeping_carp_scroll"
+ cost = 30
+ item_path = /obj/item/weapon/sleeping_carp_scroll
+ spawn_msg = "Anyone who reads the sleeping carp scroll will learn secrets of the sleeping carp martial arts style."
+
+/datum/gang_item/weapon/wrestlingbelt
+ name = "Wrestling Belt"
+ id = "wrastling_belt"
+ cost = 20
+ item_path = /obj/item/weapon/storage/belt/champion/wrestling
+ spawn_msg = "Anyone wearing the wresting belt will know how to be effective with wrestling."
+
+
+///////////////////
+//EQUIPMENT
+///////////////////
+
+/datum/gang_item/equipment
+ category = "Purchase Equipment:"
+
+
+/datum/gang_item/equipment/spraycan
+ name = "Territory Spraycan"
+ id = "spraycan"
+ cost = 5
+ item_path = /obj/item/toy/crayon/spraycan/gang
+
+/datum/gang_item/equipment/necklace
+ name = "Gold Necklace"
+ id = "necklace"
+ cost = 1
+ item_path = /obj/item/clothing/neck/necklace/dope
+
+/datum/gang_item/equipment/c4
+ name = "C4 Explosive"
+ id = "c4"
+ cost = 10
+ item_path = /obj/item/weapon/grenade/plastic/c4
+
+/datum/gang_item/equipment/implant_breaker
+ name = "Implant Breaker"
+ id = "implant_breaker"
+ cost = 10
+ item_path = /obj/item/weapon/implanter/gang
+ spawn_msg = "The implant breaker is a single-use device that destroys all implants within the target before trying to recruit them to your gang. Also works on enemy gangsters."
+
+/datum/gang_item/equipment/implant_breaker/spawn_item(mob/living/carbon/user, datum/gang/gang, obj/item/device/gangtool/gangtool)
+ if(item_path)
+ var/obj/item/O = new item_path(user.loc, gang) //we need to override this whole proc for this one argument
+ user.put_in_hands(O)
+ if(spawn_msg)
+ to_chat(user, spawn_msg)
+
+/datum/gang_item/equipment/pen
+ name = "Recruitment Pen"
+ id = "pen"
+ cost = 50
+ item_path = /obj/item/weapon/pen/gang
+ spawn_msg = "More recruitment pens will allow you to recruit gangsters faster. Only gang leaders can recruit with pens."
+
+/datum/gang_item/equipment/pen/purchase(mob/living/carbon/user, datum/gang/gang, obj/item/device/gangtool/gangtool)
+ if(..())
+ gangtool.free_pen = FALSE
+ return TRUE
+ return FALSE
+
+/datum/gang_item/equipment/pen/get_cost(mob/living/carbon/user, datum/gang/gang, obj/item/device/gangtool/gangtool)
+ if(gangtool && gangtool.free_pen)
+ return 0
+ return ..()
+
+/datum/gang_item/equipment/pen/get_cost_display(mob/living/carbon/user, datum/gang/gang, obj/item/device/gangtool/gangtool)
+ if(gangtool && gangtool.free_pen)
+ return "(GET ONE FREE)"
+ return ..()
+
+
+/datum/gang_item/equipment/gangtool
+ id = "gangtool"
+ cost = 10
+
+/datum/gang_item/equipment/gangtool/spawn_item(mob/living/carbon/user, datum/gang/gang, obj/item/device/gangtool/gangtool)
+ var/item_type
+ if(gang && isboss(user, gang))
+ item_type = /obj/item/device/gangtool/spare/lt
+ if(gang.bosses.len < 3)
+ to_chat(user, "Gangtools allow you to promote a gangster to be your Lieutenant, enabling them to recruit and purchase items like you. Simply have them register the gangtool. You may promote up to [3-gang.bosses.len] more Lieutenants")
+ else
+ item_type = /obj/item/device/gangtool/spare
+ var/obj/item/device/gangtool/spare/tool = new item_type(user.loc)
+ user.put_in_hands(tool)
+
+/datum/gang_item/equipment/gangtool/get_name_display(mob/living/carbon/user, datum/gang/gang, obj/item/device/gangtool/gangtool)
+ if(gang && isboss(user, gang) && (gang.bosses.len < 3))
+ return "Promote a Gangster"
+ return "Spare Gangtool"
+
+
+/datum/gang_item/equipment/dominator
+ name = "Station Dominator"
+ id = "dominator"
+ cost = 30
+ item_path = /obj/machinery/dominator
+ spawn_msg = "The dominator will secure your gang's dominance over the station. Turn it on when you are ready to defend it."
+
+/datum/gang_item/equipment/dominator/can_buy(mob/living/carbon/user, datum/gang/gang, obj/item/device/gangtool/gangtool)
+ if(!gang || !gang.dom_attempts)
+ return FALSE
+ return ..()
+
+/datum/gang_item/equipment/dominator/get_name_display(mob/living/carbon/user, datum/gang/gang, obj/item/device/gangtool/gangtool)
+ if(!gang || !gang.dom_attempts)
+ return ..()
+ return "[..()]"
+
+/datum/gang_item/equipment/dominator/get_cost_display(mob/living/carbon/user, datum/gang/gang, obj/item/device/gangtool/gangtool)
+ if(!gang || !gang.dom_attempts)
+ return "(Out of stock)"
+ return ..()
+
+/datum/gang_item/equipment/dominator/get_extra_info(mob/living/carbon/user, datum/gang/gang, obj/item/device/gangtool/gangtool)
+ if(gang)
+ return "(Estimated Takeover Time: [round(determine_domination_time(gang)/60,0.1)] minutes)"
+
+/datum/gang_item/equipment/dominator/purchase(mob/living/carbon/user, datum/gang/gang, obj/item/device/gangtool/gangtool)
+ var/area/usrarea = get_area(user.loc)
+ var/usrturf = get_turf(user.loc)
+ if(initial(usrarea.name) == "Space" || isspaceturf(usrturf) || usr.z != 1)
+ to_chat(user, "You can only use this on the station!")
+ return FALSE
+
+ for(var/obj/obj in usrturf)
+ if(obj.density)
+ to_chat(user, "There's not enough room here!")
+ return FALSE
+
+ if(!(usrarea.type in gang.territory|gang.territory_new))
+ to_chat(user, "The dominator can be spawned only on territory controlled by your gang!")
+ return FALSE
+ return ..()
+
+/datum/gang_item/equipment/dominator/spawn_item(mob/living/carbon/user, datum/gang/gang, obj/item/device/gangtool/gangtool)
+ new item_path(user.loc)
diff --git a/code/game/gamemodes/gang/gang_pen.dm b/code/game/gamemodes/gang/gang_pen.dm
new file mode 100644
index 000000000000..621d40cc4583
--- /dev/null
+++ b/code/game/gamemodes/gang/gang_pen.dm
@@ -0,0 +1,60 @@
+/*
+ * Gang Boss Pens
+ */
+/obj/item/weapon/pen/gang
+ origin_tech = "materials=2;syndicate=3"
+ var/cooldown
+ var/last_used = 0
+ var/charges = 1
+
+/obj/item/weapon/pen/gang/New()
+ ..()
+ last_used = world.time
+
+/obj/item/weapon/pen/gang/attack(mob/living/M, mob/user, stealth = TRUE)
+ if(!istype(M))
+ return
+ if(ishuman(M) && ishuman(user) && M.stat != DEAD)
+ if(user.mind && (user.mind in SSticker.mode.get_gang_bosses()))
+ if(..(M,user,1))
+ if(cooldown)
+ to_chat(user, "[src] needs more time to recharge before it can be used.")
+ return
+ if(M.client)
+ M.mind_initialize() //give them a mind datum if they don't have one.
+ var/datum/gang/G = user.mind.gang_datum
+ var/recruitable = SSticker.mode.add_gangster(M.mind,G)
+ switch(recruitable)
+ if(2)
+ M.Paralyse(5)
+ cooldown(G)
+ if(1)
+ to_chat(user, "This mind is resistant to recruitment!")
+ else
+ to_chat(user, "This mind has already been recruited into a gang!")
+ return
+ ..()
+
+/obj/item/weapon/pen/gang/proc/cooldown(datum/gang/gang)
+ set waitfor = FALSE
+ var/cooldown_time = 600+(600*gang.bosses.len) // 1recruiter=2mins, 2recruiters=3mins, 3recruiters=4mins
+
+ cooldown = 1
+ icon_state = "pen_blink"
+
+ var/time_passed = world.time - last_used
+ var/time
+ for(time=time_passed, time>=cooldown_time, time-=cooldown_time) //get 1 charge every cooldown interval
+ charges++
+
+ charges = max(0,charges-1)
+
+ last_used = world.time - time
+
+ if(charges)
+ cooldown_time = 50
+ sleep(cooldown_time)
+ cooldown = 0
+ icon_state = "pen"
+ var/mob/M = get(src, /mob)
+ to_chat(M, "\icon[src] [src][(src.loc == M)?(""):(" in your [src.loc]")] vibrates softly. It is ready to be used again.")
diff --git a/code/game/gamemodes/gang/recaller.dm b/code/game/gamemodes/gang/recaller.dm
new file mode 100644
index 000000000000..0885b3261226
--- /dev/null
+++ b/code/game/gamemodes/gang/recaller.dm
@@ -0,0 +1,240 @@
+//gangtool device
+/obj/item/device/gangtool
+ name = "suspicious device"
+ desc = "A strange device of sorts. Hard to really make out what it actually does if you don't know how to operate it."
+ icon_state = "gangtool-white"
+ item_state = "walkietalkie"
+ throwforce = 0
+ w_class = WEIGHT_CLASS_TINY
+ throw_speed = 3
+ throw_range = 7
+ flags = CONDUCT
+ origin_tech = "programming=5;bluespace=2;syndicate=5"
+ var/datum/gang/gang //Which gang uses this?
+ var/recalling = 0
+ var/outfits = 3
+ var/free_pen = 0
+ var/promotable = 0
+
+/obj/item/device/gangtool/Initialize() //Initialize supply point income if it hasn't already been started
+ ..()
+ if(!SSticker.mode.gang_points)
+ SSticker.mode.gang_points = new /datum/gang_points(SSticker.mode)
+
+/obj/item/device/gangtool/attack_self(mob/user)
+ if (!can_use(user))
+ return
+
+ var/dat
+ if(!gang)
+ dat += "This device is not registered.
"
+ if(user.mind in SSticker.mode.get_gang_bosses())
+ if(promotable && user.mind.gang_datum.bosses.len < 3)
+ dat += "Give this device to another member of your organization to use to promote them to Lieutenant.
"
+ dat += "If this is meant as a spare device for yourself:
"
+ dat += "Register Device as Spare
"
+ else if (promotable)
+ if(user.mind.gang_datum.bosses.len < 3)
+ dat += "You have been selected for a promotion!
"
+ dat += "Accept Promotion
"
+ else
+ dat += "No promotions available: All positions filled.
"
+ else
+ dat += "This device is not authorized to promote.
"
+ else
+ if(gang.is_dominating)
+ dat += "
Takeover In Progress:
[gang.domination_time_remaining()] seconds remain"
+
+ var/isboss = (user.mind == gang.bosses[1])
+ dat += "Registration: [gang.name] Gang [isboss ? "Boss" : "Lieutenant"]
"
+ dat += "Organization Size: [gang.gangsters.len + gang.bosses.len] | Station Control: [round((gang.territory.len/GLOB.start_state.num_territories)*100, 1)]%
"
+ dat += "Gang Influence: [gang.points]
"
+ dat += "Time until Influence grows: [(gang.points >= 999) ? ("--:--") : (time2text(SSticker.mode.gang_points.next_point_time - world.time, "mm:ss"))]
"
+ dat += "
"
+
+
+ for(var/cat in gang.item_category_list)
+ dat += "[cat]
"
+ for(var/V in gang.item_category_list[cat])
+ var/datum/gang_item/G = V
+ if(!G.can_see(user, gang, src))
+ continue
+
+ var/cost = G.get_cost_display(user, gang, src)
+ if(cost)
+ dat += cost + " "
+
+ var/toAdd = G.get_name_display(user, gang, src)
+ if(G.can_buy(user, gang, src))
+ toAdd = "[toAdd]"
+ dat += toAdd
+ var/extra = G.get_extra_info(user, gang, src)
+ if(extra)
+ dat += "
[extra]"
+ dat += "
"
+ dat += "
"
+
+ dat += "Refresh
"
+
+ var/datum/browser/popup = new(user, "gangtool", "Welcome to GangTool v3.4", 340, 625)
+ popup.set_content(dat)
+ popup.open()
+
+
+
+/obj/item/device/gangtool/Topic(href, href_list)
+ if(!can_use(usr))
+ return
+
+ add_fingerprint(usr)
+
+ if(href_list["register"])
+ register_device(usr)
+
+ else if(!gang) //Gangtool must be registered before you can use the functions below
+ return
+
+ if(href_list["purchase"])
+ var/datum/gang_item/G = gang.item_list[href_list["purchase"]]
+ if(G && G.can_buy(usr, gang, src))
+ G.purchase(usr, gang, src, FALSE)
+
+ attack_self(usr)
+
+
+/obj/item/device/gangtool/proc/ping_gang(mob/user)
+ if(!user)
+ return
+ var/message = stripped_input(user,"Discreetly send a gang-wide message.","Send Message") as null|text
+ if(!message || !can_use(user))
+ return
+ if(user.z > 2)
+ to_chat(user, "\icon[src]Error: Station out of range.")
+ return
+ var/list/members = list()
+ members += gang.gangsters
+ members += gang.bosses
+ if(members.len)
+ var/gang_rank = gang.bosses.Find(user.mind)
+ switch(gang_rank)
+ if(1)
+ gang_rank = "Gang Boss"
+ if(2)
+ gang_rank = "1st Lieutenant"
+ if(3)
+ gang_rank = "2nd Lieutenant"
+ if(4)
+ gang_rank = "3rd Lieutenant"
+ else
+ gang_rank = "[gang_rank - 1]th Lieutenant"
+ var/ping = "[gang.name] [gang_rank]: [message]"
+ for(var/datum/mind/ganger in members)
+ if(ganger.current && (ganger.current.z <= 2) && (ganger.current.stat == CONSCIOUS))
+ to_chat(ganger.current, ping)
+ for(var/mob/M in GLOB.dead_mob_list)
+ var/link = FOLLOW_LINK(M, user)
+ to_chat(M, "[link] [ping]")
+ log_game("[key_name(user)] Messaged [gang.name] Gang: [message].")
+
+
+/obj/item/device/gangtool/proc/register_device(mob/user)
+ if(gang) //It's already been registered!
+ return
+ if((promotable && (user.mind in SSticker.mode.get_gangsters())) || (user.mind in SSticker.mode.get_gang_bosses()))
+ gang = user.mind.gang_datum
+ gang.gangtools += src
+ icon_state = "gangtool-[gang.color]"
+ if(!(user.mind in gang.bosses))
+ SSticker.mode.remove_gangster(user.mind, 0, 2)
+ gang.bosses += user.mind
+ user.mind.gang_datum = gang
+ user.mind.special_role = "[gang.name] Gang Lieutenant"
+ gang.add_gang_hud(user.mind)
+ log_game("[key_name(user)] has been promoted to Lieutenant in the [gang.name] Gang")
+ free_pen = 1
+ gang.message_gangtools("[user] has been promoted to Lieutenant.")
+ to_chat(user, "You have been promoted to Lieutenant!")
+ SSticker.mode.forge_gang_objectives(user.mind)
+ SSticker.mode.greet_gang(user.mind,0)
+ to_chat(user, "The Gangtool you registered will allow you to purchase weapons and equipment, and send messages to your gang.")
+ to_chat(user, "Unlike regular gangsters, you may use recruitment pens to add recruits to your gang. Use them on unsuspecting crew members to recruit them. Don't forget to get your one free pen from the gangtool.")
+ else
+ to_chat(usr, "ACCESS DENIED: Unauthorized user.")
+
+/obj/item/device/gangtool/proc/recall(mob/user)
+ if(!can_use(user))
+ return 0
+
+ if(recalling)
+ to_chat(usr, "Error: Recall already in progress.")
+ return 0
+
+ gang.message_gangtools("[usr] is attempting to recall the emergency shuttle.")
+ recalling = 1
+ to_chat(loc, "\icon[src]Generating shuttle recall order with codes retrieved from last call signal...")
+
+ sleep(rand(100,300))
+
+ if(SSshuttle.emergency.mode != SHUTTLE_CALL) //Shuttle can only be recalled when it's moving to the station
+ to_chat(user, "\icon[src]Emergency shuttle cannot be recalled at this time.")
+ recalling = 0
+ return 0
+ to_chat(loc, "\icon[src]Shuttle recall order generated. Accessing station long-range communication arrays...")
+
+ sleep(rand(100,300))
+
+ if(!gang.dom_attempts)
+ to_chat(user, "\icon[src]Error: Unable to access communication arrays. Firewall has logged our signature and is blocking all further attempts.")
+ recalling = 0
+ return 0
+
+ var/turf/userturf = get_turf(user)
+ if(userturf.z != 1) //Shuttle can only be recalled while on station
+ to_chat(user, "\icon[src]Error: Device out of range of station communication arrays.")
+ recalling = 0
+ return 0
+ var/datum/station_state/end_state = new /datum/station_state()
+ end_state.count()
+ if((100 * GLOB.start_state.score(end_state)) < 80) //Shuttle cannot be recalled if the station is too damaged
+ to_chat(user, "\icon[src]Error: Station communication systems compromised. Unable to establish connection.")
+ recalling = 0
+ return 0
+ to_chat(loc, "\icon[src]Comm arrays accessed. Broadcasting recall signal...")
+
+ sleep(rand(100,300))
+
+ recalling = 0
+ log_game("[key_name(user)] has tried to recall the shuttle with a gangtool.")
+ message_admins("[key_name_admin(user)] has tried to recall the shuttle with a gangtool.", 1)
+ userturf = get_turf(user)
+ if(userturf.z == 1) //Check one more time that they are on station.
+ if(SSshuttle.cancelEvac(user))
+ return 1
+
+ to_chat(loc, "\icon[src]No response recieved. Emergency shuttle cannot be recalled at this time.")
+ return 0
+
+/obj/item/device/gangtool/proc/can_use(mob/living/carbon/human/user)
+ if(!istype(user))
+ return 0
+ if(user.restrained() || user.lying || user.stat || user.stunned || user.weakened)
+ return 0
+ if(!(src in user.contents))
+ return 0
+ if(!user.mind)
+ return 0
+
+ if(gang) //If it's already registered, only let the gang's bosses use this
+ if(user.mind in gang.bosses)
+ return 1
+ else //If it's not registered, any gangster can use this to register
+ if(user.mind in SSticker.mode.get_all_gangsters())
+ return 1
+
+ return 0
+
+/obj/item/device/gangtool/spare
+ outfits = 1
+
+/obj/item/device/gangtool/spare/lt
+ promotable = 1
diff --git a/code/game/gamemodes/wizard/artefact.dm b/code/game/gamemodes/wizard/artefact.dm
index f009b8ada717..dde67fb664eb 100644
--- a/code/game/gamemodes/wizard/artefact.dm
+++ b/code/game/gamemodes/wizard/artefact.dm
@@ -239,14 +239,20 @@
assigned = "[user.real_name]"
user.faction = list("[user.real_name]")
to_chat(user, "You bind the sword to yourself. You can now use it to summon help.")
- to_chat(user, "With your new found power you could easily conquer the station!")
- var/datum/objective/hijackclone/hijack_objective = new /datum/objective/hijackclone
- hijack_objective.owner = user.mind
- user.mind.objectives += hijack_objective
- hijack_objective.explanation_text = "Ensure only [user.real_name] and their copies are on the shuttle!"
- to_chat(user, "Objective #[1]: [hijack_objective.explanation_text]")
- SSticker.mode.traitors += user.mind
- user.mind.special_role = "[user.real_name] Prime"
+ if(!is_gangster(user))
+ var/datum/gang/multiverse/G = new(src, "[user.real_name]")
+ SSticker.mode.gangs += G
+ G.bosses += user.mind
+ G.add_gang_hud(user.mind)
+ user.mind.gang_datum = G
+ to_chat(user, "With your new found power you could easily conquer the station!")
+ var/datum/objective/hijackclone/hijack_objective = new /datum/objective/hijackclone
+ hijack_objective.owner = user.mind
+ user.mind.objectives += hijack_objective
+ hijack_objective.explanation_text = "Ensure only [user.real_name] and their copies are on the shuttle!"
+ to_chat(user, "Objective #[1]: [hijack_objective.explanation_text]")
+ SSticker.mode.traitors += user.mind
+ user.mind.special_role = "[user.real_name] Prime"
else
var/list/candidates = get_candidates(ROLE_WIZARD)
if(candidates.len)
@@ -270,6 +276,7 @@
M.key = C.key
M.mind.name = user.real_name
to_chat(M, "You are an alternate version of [user.real_name] from another universe! Help them accomplish their goals at all costs.")
+ SSticker.mode.add_gangster(M.mind, user.mind.gang_datum, FALSE)
M.real_name = user.real_name
M.name = user.real_name
M.faction = list("[user.real_name]")
diff --git a/code/game/objects/effects/countdown.dm b/code/game/objects/effects/countdown.dm
index bd9bd3a33112..520158dfb0cc 100644
--- a/code/game/objects/effects/countdown.dm
+++ b/code/game/objects/effects/countdown.dm
@@ -97,6 +97,21 @@
var/completion = round(C.get_completion())
return completion
+/obj/effect/countdown/dominator
+ name = "dominator countdown"
+ text_size = 1
+ color = "#ff00ff" // Overwritten when the dominator starts
+
+/obj/effect/countdown/dominator/get_value()
+ var/obj/machinery/dominator/D = attached_to
+ if(!istype(D))
+ return
+ else if(D.gang && D.gang.is_dominating)
+ var/timer = D.gang.domination_time_remaining()
+ return timer
+ else
+ return "OFFLINE"
+
/obj/effect/countdown/clockworkgate
name = "gateway countdown"
text_size = 1
diff --git a/code/game/objects/effects/decals/crayon.dm b/code/game/objects/effects/decals/crayon.dm
index dce98cf5fd8b..6a41809eb177 100644
--- a/code/game/objects/effects/decals/crayon.dm
+++ b/code/game/objects/effects/decals/crayon.dm
@@ -8,14 +8,11 @@
/obj/effect/decal/cleanable/crayon/Initialize(mapload, main = "#FFFFFF", var/type = "rune1", var/e_name = "rune", var/rotation = 0, var/alt_icon = null)
..()
-
+
name = e_name
desc = "A [name] vandalizing the station."
-
- var/static/list/gang_name_pool = list("Clandestine", "Prima", "Zero-G", "Max", "Blasto", "Waffle", "North", "Omni", "Newton", "Cyber", "Donk", "Gene", "Gib", "Tunnel", "Diablo", "Psyke", "Osiron", "Sirius", "Sleeping Carp")
if(type == "poseur tag")
- type = pick(gang_name_pool)
-
+ type = pick(GLOB.gang_name_pool)
if(alt_icon)
icon = alt_icon
@@ -26,4 +23,31 @@
M.Turn(rotation)
src.transform = M
- add_atom_colour(main, FIXED_COLOUR_PRIORITY)
\ No newline at end of file
+ add_atom_colour(main, FIXED_COLOUR_PRIORITY)
+
+
+/obj/effect/decal/cleanable/crayon/gang
+ layer = HIGH_OBJ_LAYER //Harder to hide
+ do_icon_rotate = FALSE //These are designed to always face south, so no rotation please.
+ var/datum/gang/gang
+
+/obj/effect/decal/cleanable/crayon/gang/Initialize(mapload, var/datum/gang/G, var/e_name = "gang tag", var/rotation = 0)
+ if(!type || !G)
+ qdel(src)
+
+ var/area/territory = get_area(src)
+ gang = G
+ var/newcolor = G.color_hex
+ icon_state = G.name
+ G.territory_new |= list(territory.type = territory.name)
+
+ ..(mapload, newcolor, icon_state, e_name, rotation)
+
+/obj/effect/decal/cleanable/crayon/gang/Destroy()
+ var/area/territory = get_area(src)
+
+ if(gang)
+ gang.territory -= territory.type
+ gang.territory_new -= territory.type
+ gang.territory_lost |= list(territory.type = territory.name)
+ return ..()
diff --git a/code/game/objects/items/crayons.dm b/code/game/objects/items/crayons.dm
index 132b6af99f51..af8b9341f94b 100644
--- a/code/game/objects/items/crayons.dm
+++ b/code/game/objects/items/crayons.dm
@@ -54,6 +54,7 @@
var/self_contained = TRUE // If it deletes itself when it is empty
var/list/validSurfaces = list(/turf/open/floor)
+ var/gang = FALSE //For marking territory
var/edible = TRUE // That doesn't mean eating it is a good idea
@@ -276,6 +277,15 @@
else if(drawing in numerals)
temp = "number"
+ // If a gang member is using a gang spraycan, it'll behave differently
+ var/gang_mode = FALSE
+ if(gang && user.mind && user.mind.gang_datum)
+ gang_mode = TRUE
+
+ // discontinue if the area isn't valid for tagging because gang "honour"
+ if(gang_mode && (!can_claim_for_gang(user, target)))
+ return
+
var/graf_rot
if(drawing in oriented)
switch(user.dir)
@@ -296,6 +306,8 @@
playsound(user.loc, 'sound/effects/spray.ogg', 5, 1, 5)
var/takes_time = !instant
+ if(gang_mode)
+ takes_time = TRUE
var/wait_time = 50
if(PAINT_LARGE_HORIZONTAL)
@@ -312,21 +324,28 @@
var/list/turf/affected_turfs = list()
if(actually_paints)
- switch(paint_mode)
- if(PAINT_NORMAL)
- new /obj/effect/decal/cleanable/crayon(target, paint_color, drawing, temp, graf_rot)
- affected_turfs += target
- if(PAINT_LARGE_HORIZONTAL)
- var/turf/left = locate(target.x-1,target.y,target.z)
- var/turf/right = locate(target.x+1,target.y,target.z)
- if(is_type_in_list(left, validSurfaces) && is_type_in_list(right, validSurfaces))
- new /obj/effect/decal/cleanable/crayon(left, paint_color, drawing, temp, graf_rot, PAINT_LARGE_HORIZONTAL_ICON)
- affected_turfs += left
- affected_turfs += right
+ if(gang_mode)
+ // Double check it wasn't tagged in the meanwhile
+ if(!can_claim_for_gang(user, target))
+ return
+ tag_for_gang(user, target)
+ affected_turfs += target
+ else
+ switch(paint_mode)
+ if(PAINT_NORMAL)
+ new /obj/effect/decal/cleanable/crayon(target, paint_color, drawing, temp, graf_rot)
affected_turfs += target
- else
- to_chat(user, "There isn't enough space to paint!")
- return
+ if(PAINT_LARGE_HORIZONTAL)
+ var/turf/left = locate(target.x-1,target.y,target.z)
+ var/turf/right = locate(target.x+1,target.y,target.z)
+ if(is_type_in_list(left, validSurfaces) && is_type_in_list(right, validSurfaces))
+ new /obj/effect/decal/cleanable/crayon(left, paint_color, drawing, temp, graf_rot, PAINT_LARGE_HORIZONTAL_ICON)
+ affected_turfs += left
+ affected_turfs += right
+ affected_turfs += target
+ else
+ to_chat(user, "There isn't enough space to paint!")
+ return
if(!instant)
to_chat(user, "You finish drawing \the [temp].")
@@ -365,6 +384,47 @@
else
..()
+/obj/item/toy/crayon/proc/can_claim_for_gang(mob/user, atom/target)
+ // Check area validity.
+ // Reject space, player-created areas, and non-station z-levels.
+ var/area/A = get_area(target)
+ if(!A || (A.z != ZLEVEL_STATION) || !A.valid_territory)
+ to_chat(user, "[A] is unsuitable for tagging.")
+ return FALSE
+
+ var/spraying_over = FALSE
+ for(var/obj/effect/decal/cleanable/crayon/gang/G in target)
+ spraying_over = TRUE
+
+ for(var/obj/machinery/power/apc in target)
+ to_chat(user, "You can't tag an APC.")
+ return FALSE
+
+ var/occupying_gang = territory_claimed(A, user)
+ if(occupying_gang && !spraying_over)
+ to_chat(user, "[A] has already been tagged by the [occupying_gang] gang! You must get rid of or spray over the old tag first!")
+ return FALSE
+
+ // If you pass the gaunlet of checks, you're good to proceed
+ return TRUE
+
+/obj/item/toy/crayon/proc/territory_claimed(area/territory, mob/user)
+ for(var/datum/gang/G in SSticker.mode.gangs)
+ if(territory.type in (G.territory|G.territory_new))
+ . = G.name
+ break
+
+/obj/item/toy/crayon/proc/tag_for_gang(mob/user, atom/target)
+ //Delete any old markings on this tile, including other gang tags
+ for(var/obj/effect/decal/cleanable/crayon/old_marking in target)
+ qdel(old_marking)
+
+ var/gangID = user.mind.gang_datum
+ var/area/territory = get_area(target)
+
+ new /obj/effect/decal/cleanable/crayon/gang(target,gangID,"graffiti",0)
+ to_chat(user, "You tagged [territory] for your gang!")
+
/obj/item/toy/crayon/red
icon_state = "crayonred"
paint_color = "#DA0000"
@@ -613,6 +673,25 @@
spray_overlay.color = paint_color
add_overlay(spray_overlay)
+/obj/item/toy/crayon/spraycan/gang
+ //desc = "A modified container containing suspicious paint."
+ charges = 20
+ gang = TRUE
+
+ pre_noise = FALSE
+ post_noise = TRUE
+
+/obj/item/toy/crayon/spraycan/gang/New(loc, datum/gang/G)
+ ..()
+ if(G)
+ paint_color = G.color_hex
+ update_icon()
+
+/obj/item/toy/crayon/spraycan/gang/examine(mob/user)
+ . = ..()
+ if((user.mind && user.mind.gang_datum) || isobserver(user))
+ to_chat(user, "This spraycan has been specially modified for tagging territory.")
+
/obj/item/toy/crayon/spraycan/borg
name = "cyborg spraycan"
desc = "A metallic container containing shiny synthesised paint."
diff --git a/code/game/objects/items/weapons/explosives.dm b/code/game/objects/items/weapons/explosives.dm
index 899c066b1117..404c40c4b6aa 100644
--- a/code/game/objects/items/weapons/explosives.dm
+++ b/code/game/objects/items/weapons/explosives.dm
@@ -39,6 +39,8 @@
message_say = "FOR NAR-SIE!"
else if(role == "revolutionary" || role == "head revolutionary")
message_say = "VIVA LA REVOLUTION!"
+ else if(user.mind.gang_datum)
+ message_say = "[uppertext(user.mind.gang_datum.name)] RULES!"
user.say(message_say)
target = user
message_admins("[ADMIN_LOOKUPFLW(user)] suicided with [name] at [ADMIN_COORDJMP(src)]",0,1)
diff --git a/code/game/objects/items/weapons/grenades/plastic.dm b/code/game/objects/items/weapons/grenades/plastic.dm
index 2e6170fae718..fb5b92aee2e8 100644
--- a/code/game/objects/items/weapons/grenades/plastic.dm
+++ b/code/game/objects/items/weapons/grenades/plastic.dm
@@ -134,6 +134,8 @@
message_say = "FOR NAR-SIE!"
else if(role == "revolutionary" || role == "head revolutionary")
message_say = "VIVA LA REVOLUTION!"
+ else if(user.mind.gang_datum)
+ message_say = "[uppertext(user.mind.gang_datum.name)] RULES!"
user.say(message_say)
explosion(user,0,2,0) //Cheap explosion imitation because putting prime() here causes runtimes
user.gib(1, 1)
diff --git a/code/game/objects/items/weapons/implants/implant_loyality.dm b/code/game/objects/items/weapons/implants/implant_loyality.dm
index e9cb32b3e5e5..67182d48a8f9 100644
--- a/code/game/objects/items/weapons/implants/implant_loyality.dm
+++ b/code/game/objects/items/weapons/implants/implant_loyality.dm
@@ -19,12 +19,19 @@
/obj/item/weapon/implant/mindshield/implant(mob/living/target, mob/user, silent = 0)
if(..())
- if(target.mind in SSticker.mode.head_revolutionaries)
+ if((target.mind in (SSticker.mode.head_revolutionaries | SSticker.mode.get_gang_bosses())))
if(!silent)
target.visible_message("[target] seems to resist the implant!", "You feel something interfering with your mental conditioning, but you resist it!")
removed(target, 1)
qdel(src)
return 0
+ if(target.mind in SSticker.mode.get_gangsters())
+ SSticker.mode.remove_gangster(target.mind)
+ if(!silent)
+ target.visible_message("[src] was destroyed in the process!", "You feel a sense of peace and security. You are now protected from brainwashing.")
+ removed(target, 1)
+ qdel(src)
+ return 0
if(target.mind in SSticker.mode.revolutionaries)
SSticker.mode.remove_revolutionary(target.mind)
if(!silent)
diff --git a/code/modules/admin/player_panel.dm b/code/modules/admin/player_panel.dm
index 3fad50a06e4d..396d7d93fa04 100644
--- a/code/modules/admin/player_panel.dm
+++ b/code/modules/admin/player_panel.dm
@@ -441,6 +441,24 @@
dat += "PM | "
dat += ""
+ for(var/datum/gang/G in SSticker.mode.gangs)
+ dat += "
| [G.name] Gang: [G.points] Influence | [round((G.territory.len/GLOB.start_state.num_territories)*100, 1)]% Control | |
"
+ for(var/datum/mind/N in G.bosses)
+ var/mob/M = N.current
+ if(!M)
+ dat += "| [N.name]([N.key])Gang Boss body destroyed! | "
+ dat += "PM |
"
+ else
+ dat += "| [M.real_name] (Boss)[M.client ? "" : " (No Client)"][M.stat == 2 ? " (DEAD)" : ""] | "
+ dat += "PM | "
+ dat += "FLW |
"
+ for(var/datum/mind/N in G.gangsters)
+ var/mob/M = N.current
+ if(M)
+ dat += "| [M.real_name][M.client ? "" : " (No Client)"][M.stat == 2 ? " (DEAD)" : ""] | "
+ dat += "PM |
"
+ dat += "
"
+
if(SSticker.mode.changelings.len > 0)
dat += "
| Changelings | | |
"
for(var/datum/mind/changeling in SSticker.mode.changelings)
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index 3e47ce75a4a9..1f8778361fd0 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -94,6 +94,13 @@
message_admins("[key_name(usr)] spawned a blob with base resource gain [strength].")
log_admin("[key_name(usr)] spawned a blob with base resource gain [strength].")
new/datum/round_event/ghost_role/blob(TRUE, strength)
+ if("gangs")
+ if(src.makeGangsters())
+ message_admins("[key_name(usr)] created gangs.")
+ log_admin("[key_name(usr)] created gangs.")
+ else
+ message_admins("[key_name(usr)] tried to create gangs. Unfortunately, there were not enough candidates available.")
+ log_admin("[key_name(usr)] failed create gangs.")
if("centcom")
message_admins("[key_name(usr)] is creating a Centcom response team...")
if(src.makeEmergencyresponseteam())
@@ -1504,6 +1511,17 @@
usr.client.cmd_admin_animalize(M)
+ else if(href_list["gangpoints"])
+ var/datum/gang/G = locate(href_list["gangpoints"]) in SSticker.mode.gangs
+ if(G)
+ var/newpoints = input("Set [G.name ] Gang's influence.","Set Influence",G.points) as null|num
+ if(!newpoints)
+ return
+ message_admins("[key_name_admin(usr)] changed the [G.name] Gang's influence from [G.points] to [newpoints].")
+ log_admin("[key_name(usr)] changed the [G.name] Gang's influence from [G.points] to [newpoints].")
+ G.points = newpoints
+ G.message_gangtools("Your gang now has [G.points] influence.")
+
else if(href_list["adminplayeropts"])
var/mob/M = locate(href_list["adminplayeropts"])
show_player_panel(M)
diff --git a/code/modules/admin/verbs/one_click_antag.dm b/code/modules/admin/verbs/one_click_antag.dm
index 021853eb6a44..d7b06ee9c874 100644
--- a/code/modules/admin/verbs/one_click_antag.dm
+++ b/code/modules/admin/verbs/one_click_antag.dm
@@ -357,6 +357,43 @@
return
+
+/datum/admins/proc/makeGangsters()
+
+ var/datum/game_mode/gang/temp = new
+ if(config.protect_roles_from_antagonist)
+ temp.restricted_jobs += temp.protected_jobs
+
+ if(config.protect_assistant_from_antagonist)
+ temp.restricted_jobs += "Assistant"
+
+ var/list/mob/living/carbon/human/candidates = list()
+ var/mob/living/carbon/human/H = null
+
+ for(var/mob/living/carbon/human/applicant in GLOB.player_list)
+ if(ROLE_GANG in applicant.client.prefs.be_special)
+ var/turf/T = get_turf(applicant)
+ if(applicant.stat == CONSCIOUS && applicant.mind && !applicant.mind.special_role && T.z == ZLEVEL_STATION)
+ if(!jobban_isbanned(applicant, ROLE_GANG) && !jobban_isbanned(applicant, "Syndicate"))
+ if(temp.age_check(applicant.client))
+ if(!(applicant.job in temp.restricted_jobs))
+ candidates += applicant
+
+ if(candidates.len >= 2)
+ for(var/needs_assigned=2,needs_assigned>0,needs_assigned--)
+ H = pick(candidates)
+ if(GLOB.gang_colors_pool.len)
+ var/datum/gang/newgang = new()
+ SSticker.mode.gangs += newgang
+ H.mind.make_Gang(newgang)
+ candidates.Remove(H)
+ else if(needs_assigned == 2)
+ return 0
+ return 1
+
+ return 0
+
+
/datum/admins/proc/makeOfficial()
var/mission = input("Assign a task for the official", "Assign Task", "Conduct a routine preformance review of [station_name()] and its Captain.")
var/list/mob/dead/observer/candidates = pollCandidates("Do you wish to be considered to be a Centcom Official?", "deathsquad")
diff --git a/code/modules/admin/verbs/onlyone.dm b/code/modules/admin/verbs/onlyone.dm
index 776296807b86..feba659d725a 100644
--- a/code/modules/admin/verbs/onlyone.dm
+++ b/code/modules/admin/verbs/onlyone.dm
@@ -97,6 +97,12 @@ GLOBAL_VAR_INIT(highlander, FALSE)
to_chat(H, "You are the multiverse summoner. Activate your blade to summon copies of yourself from another universe to fight by your side.")
H.mind.announce_objectives()
+ var/datum/gang/multiverse/G = new(src, "[H.real_name]")
+ SSticker.mode.gangs += G
+ G.bosses += H.mind
+ G.add_gang_hud(H.mind)
+ H.mind.gang_datum = G
+
var/obj/item/slot_item_ID = H.get_item_by_slot(slot_wear_id)
qdel(slot_item_ID)
var/obj/item/slot_item_hand = H.get_item_for_held_index(2)
diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm
index b0641b5919a8..22dfa9e5ee71 100644
--- a/code/modules/admin/verbs/randomverbs.dm
+++ b/code/modules/admin/verbs/randomverbs.dm
@@ -967,6 +967,10 @@ GLOBAL_LIST_EMPTY(custom_outfits) //Admin created outfits
if(istype(H, /datum/atom_hud/antag))
(adding_hud) ? H.add_hud_to(usr) : H.remove_hud_from(usr)
+ for(var/datum/gang/G in SSticker.mode.gangs)
+ var/datum/atom_hud/antag/H = G.ganghud
+ (adding_hud) ? H.add_hud_to(usr) : H.remove_hud_from(usr)
+
to_chat(usr, "You toggled your admin antag HUD [adding_hud ? "ON" : "OFF"].")
message_admins("[key_name_admin(usr)] toggled their admin antag HUD [adding_hud ? "ON" : "OFF"].")
log_admin("[key_name(usr)] toggled their admin antag HUD [adding_hud ? "ON" : "OFF"].")
diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm
index 0bd38c010f83..fdec84bc55e0 100644
--- a/code/modules/mob/dead/observer/observer.dm
+++ b/code/modules/mob/dead/observer/observer.dm
@@ -296,6 +296,9 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
..()
if(statpanel("Status"))
if(SSticker.HasRoundStarted())
+ for(var/datum/gang/G in SSticker.mode.gangs)
+ if(G.is_dominating)
+ stat(null, "[G.name] Gang Takeover: [max(G.domination_time_remaining(), 0)]")
if(istype(SSticker.mode, /datum/game_mode/blob))
var/datum/game_mode/blob/B = SSticker.mode
if(B.message_sent)
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 20fb0d701f37..0421ccabefd8 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -733,6 +733,9 @@
if(statpanel("Status"))
if(SSticker && SSticker.mode)
+ for(var/datum/gang/G in SSticker.mode.gangs)
+ if(G.is_dominating)
+ stat(null, "[G.name] Gang Takeover: [max(G.domination_time_remaining(), 0)]")
if(istype(SSticker.mode, /datum/game_mode/blob))
var/datum/game_mode/blob/B = SSticker.mode
if(B.message_sent)
diff --git a/code/modules/mob/living/silicon/login.dm b/code/modules/mob/living/silicon/login.dm
index b0e9d4103851..9474b787ead1 100644
--- a/code/modules/mob/living/silicon/login.dm
+++ b/code/modules/mob/living/silicon/login.dm
@@ -2,4 +2,5 @@
if(mind && SSticker.mode)
SSticker.mode.remove_cultist(mind, 0, 0)
SSticker.mode.remove_revolutionary(mind, 0)
+ SSticker.mode.remove_gangster(mind, remove_bosses=1)
..()
diff --git a/code/modules/mob/living/silicon/robot/login.dm b/code/modules/mob/living/silicon/robot/login.dm
index 577832a229e0..742a4b20ce00 100644
--- a/code/modules/mob/living/silicon/robot/login.dm
+++ b/code/modules/mob/living/silicon/robot/login.dm
@@ -5,3 +5,4 @@
show_laws(0)
if(mind)
SSticker.mode.remove_revolutionary(mind)
+ SSticker.mode.remove_gangster(mind,1,remove_bosses=1)
diff --git a/code/modules/shuttle/emergency.dm b/code/modules/shuttle/emergency.dm
index 4f348c862ba0..6344689f0bdf 100644
--- a/code/modules/shuttle/emergency.dm
+++ b/code/modules/shuttle/emergency.dm
@@ -300,6 +300,12 @@
// Gangs only have one attempt left if the shuttle has
// docked with the station to prevent suffering from
// endless dominator delays
+ for(var/datum/gang/G in SSticker.mode.gangs)
+ if(G.is_dominating)
+ G.dom_attempts = 0
+ else
+ G.dom_attempts = min(1,G.dom_attempts)
+
if(SHUTTLE_DOCKED)
if(time_left <= ENGINES_START_TIME)
diff --git a/code/modules/uplink/uplink_item.dm b/code/modules/uplink/uplink_item.dm
index b28525f416d0..09334b24c8f9 100644
--- a/code/modules/uplink/uplink_item.dm
+++ b/code/modules/uplink/uplink_item.dm
@@ -217,6 +217,7 @@ GLOBAL_LIST_EMPTY(uplink_items) // Global list so we only initialize this once.
/datum/uplink_item/dangerous/smg/unrestricted
item = /obj/item/weapon/gun/ballistic/automatic/c20r/unrestricted
+ include_modes = list(/datum/game_mode/gang)
/datum/uplink_item/dangerous/machinegun
name = "L6 Squad Automatic Weapon"
@@ -261,7 +262,7 @@ GLOBAL_LIST_EMPTY(uplink_items) // Global list so we only initialize this once.
item = /obj/item/weapon/gun/energy/kinetic_accelerator/crossbow
cost = 12
surplus = 50
- exclude_modes = list(/datum/game_mode/nuclear)
+ exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/gang)
/datum/uplink_item/dangerous/flamethrower
name = "Flamethrower"
@@ -270,7 +271,7 @@ GLOBAL_LIST_EMPTY(uplink_items) // Global list so we only initialize this once.
item = /obj/item/weapon/flamethrower/full/tank
cost = 4
surplus = 40
- include_modes = list(/datum/game_mode/nuclear)
+ include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/gang)
/datum/uplink_item/dangerous/sword
name = "Energy Sword"
@@ -348,7 +349,7 @@ GLOBAL_LIST_EMPTY(uplink_items) // Global list so we only initialize this once.
item = /obj/item/weapon/reagent_containers/spray/chemsprayer/bioterror
cost = 20
surplus = 0
- include_modes = list(/datum/game_mode/nuclear)
+ include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/gang)
/datum/uplink_item/stealthy_weapons/virus_grenade
name = "Fungal Tuberculosis Grenade"
@@ -453,7 +454,7 @@ GLOBAL_LIST_EMPTY(uplink_items) // Global list so we only initialize this once.
These bullets pack a lot of punch that can knock most targets down, but do limited overall damage."
item = /obj/item/ammo_box/magazine/smgm45
cost = 3
- include_modes = list(/datum/game_mode/nuclear)
+ include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/gang)
/datum/uplink_item/ammo/smg/bag
name = ".45 Ammo Duffelbag"
@@ -594,7 +595,7 @@ GLOBAL_LIST_EMPTY(uplink_items) // Global list so we only initialize this once.
item = /obj/item/weapon/sleeping_carp_scroll
cost = 17
surplus = 0
- exclude_modes = list(/datum/game_mode/nuclear)
+ exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/gang)
/datum/uplink_item/stealthy_weapons/cqc
name = "CQC Manual"
@@ -624,6 +625,7 @@ GLOBAL_LIST_EMPTY(uplink_items) // Global list so we only initialize this once.
item = /obj/item/weapon/gun/ballistic/automatic/toy/pistol/riot
cost = 3
surplus = 10
+ exclude_modes = list(/datum/game_mode/gang)
/datum/uplink_item/stealthy_weapons/sleepy_pen
name = "Sleepy Pen"
@@ -633,7 +635,7 @@ GLOBAL_LIST_EMPTY(uplink_items) // Global list so we only initialize this once.
falls asleep, they will be able to move and act."
item = /obj/item/weapon/pen/sleepy
cost = 4
- exclude_modes = list(/datum/game_mode/nuclear)
+ exclude_modes = list(/datum/game_mode/nuclear,/datum/game_mode/gang)
/datum/uplink_item/stealthy_weapons/poison_pen
name = "Poison Pen"
@@ -663,7 +665,7 @@ GLOBAL_LIST_EMPTY(uplink_items) // Global list so we only initialize this once.
item = /obj/item/weapon/storage/box/syndie_kit/romerol
cost = 25
cant_discount = TRUE
- exclude_modes = list(/datum/game_mode/nuclear)
+ exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/gang)
/datum/uplink_item/stealthy_weapons/dart_pistol
name = "Dart Pistol"
@@ -772,6 +774,7 @@ GLOBAL_LIST_EMPTY(uplink_items) // Global list so we only initialize this once.
move the projector from their hand. Disguised users move slowly, and projectiles pass over them."
item = /obj/item/device/chameleon
cost = 7
+ exclude_modes = list(/datum/game_mode/gang)
/datum/uplink_item/stealthy_tools/camera_bug
name = "Camera Bug"
@@ -805,7 +808,7 @@ GLOBAL_LIST_EMPTY(uplink_items) // Global list so we only initialize this once.
item = /obj/item/weapon/reagent_containers/syringe/mulligan
cost = 4
surplus = 30
- exclude_modes = list(/datum/game_mode/nuclear)
+ exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/gang)
/datum/uplink_item/stealthy_tools/emplight
name = "EMP Flashlight"
@@ -833,6 +836,7 @@ GLOBAL_LIST_EMPTY(uplink_items) // Global list so we only initialize this once.
//Space Suits and Hardsuits
/datum/uplink_item/suits
category = "Space Suits and Hardsuits"
+ exclude_modes = list(/datum/game_mode/gang)
surplus = 40
/datum/uplink_item/suits/space_suit
@@ -881,6 +885,7 @@ GLOBAL_LIST_EMPTY(uplink_items) // Global list so we only initialize this once.
in electronic devices, subverts intended functions, and easily breaks security mechanisms."
item = /obj/item/weapon/card/emag
cost = 6
+ exclude_modes = list(/datum/game_mode/gang)
/datum/uplink_item/device_tools/toolbox
name = "Full Syndicate Toolbox"
@@ -910,7 +915,7 @@ GLOBAL_LIST_EMPTY(uplink_items) // Global list so we only initialize this once.
and other supplies helpful for a field medic."
item = /obj/item/weapon/storage/firstaid/tactical
cost = 4
- include_modes = list(/datum/game_mode/nuclear)
+ include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/gang)
/datum/uplink_item/device_tools/thermal
name = "Thermal Imaging Glasses"
@@ -1000,6 +1005,7 @@ GLOBAL_LIST_EMPTY(uplink_items) // Global list so we only initialize this once.
sends you a small beacon that will teleport the larger beacon to your location upon activation."
item = /obj/item/device/sbeacondrop
cost = 14
+ exclude_modes = list(/datum/game_mode/gang)
/datum/uplink_item/device_tools/syndicate_bomb
name = "Syndicate Bomb"
@@ -1045,7 +1051,7 @@ GLOBAL_LIST_EMPTY(uplink_items) // Global list so we only initialize this once.
item = /obj/item/weapon/shield/energy
cost = 16
surplus = 20
- include_modes = list(/datum/game_mode/nuclear)
+ include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/gang)
/datum/uplink_item/device_tools/medgun
name = "Medbeam Gun"
@@ -1290,7 +1296,7 @@ GLOBAL_LIST_EMPTY(uplink_items) // Global list so we only initialize this once.
you will receive."
item = /obj/item/weapon/storage/box/syndicate
cost = 20
- exclude_modes = list(/datum/game_mode/nuclear)
+ exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/gang)
cant_discount = TRUE
/datum/uplink_item/badass/surplus
@@ -1300,7 +1306,7 @@ GLOBAL_LIST_EMPTY(uplink_items) // Global list so we only initialize this once.
item = /obj/structure/closet/crate
cost = 20
player_minimum = 25
- exclude_modes = list(/datum/game_mode/nuclear)
+ exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/gang)
cant_discount = TRUE
/datum/uplink_item/badass/surplus/spawn_item(turf/loc, obj/item/device/uplink/U)
diff --git a/config/game_options.txt b/config/game_options.txt
index b9139a078a74..e519e9fa8c37 100644
--- a/config/game_options.txt
+++ b/config/game_options.txt
@@ -81,6 +81,7 @@ PROBABILITY TRAITORCHAN 4
PROBABILITY INTERNAL_AFFAIRS 3
PROBABILITY NUCLEAR 2
PROBABILITY REVOLUTION 2
+PROBABILITY GANG 2
PROBABILITY CULT 2
PROBABILITY CHANGELING 2
PROBABILITY WIZARD 4
@@ -105,6 +106,7 @@ CONTINUOUS TRAITORCHAN
CONTINUOUS DOUBLE_AGENTS
#CONTINUOUS NUCLEAR
#CONTINUOUS REVOLUTION
+CONTINUOUS GANG
CONTINUOUS CULT
CONTINUOUS CLOCKWORK_CULT
CONTINUOUS CHANGELING
@@ -129,6 +131,7 @@ MIDROUND_ANTAG TRAITORCHAN
MIDROUND_ANTAG DOUBLE_AGENTS
#MIDROUND_ANTAG NUCLEAR
#MIDROUND_ANTAG REVOLUTION
+#MIDROUND_ANTAG GANG
MIDROUND_ANTAG CULT
MIDROUND_ANTAG CLOCKWORK_CULT
MIDROUND_ANTAG CHANGELING
@@ -160,6 +163,9 @@ MIDROUND_ANTAG ABDUCTION
#MIN_POP REVOLUTION 20
#MAX_POP REVOLUTION -1
+#MIN_POP GANG 20
+#MAX_POP GANG -1
+
#MIN_POP CULT 24
#MAX_POP CULT -1
diff --git a/tgstation.dme b/tgstation.dme
index b8fd2d2b6c78..be10e723d952 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -447,6 +447,12 @@
#include "code\game\gamemodes\devil\true_devil\_true_devil.dm"
#include "code\game\gamemodes\devil\true_devil\inventory.dm"
#include "code\game\gamemodes\extended\extended.dm"
+#include "code\game\gamemodes\gang\dominator.dm"
+#include "code\game\gamemodes\gang\gang.dm"
+#include "code\game\gamemodes\gang\gang_datum.dm"
+#include "code\game\gamemodes\gang\gang_items.dm"
+#include "code\game\gamemodes\gang\gang_pen.dm"
+#include "code\game\gamemodes\gang\recaller.dm"
#include "code\game\gamemodes\malfunction\Malf_Modules.dm"
#include "code\game\gamemodes\meteor\meteor.dm"
#include "code\game\gamemodes\meteor\meteors.dm"
@@ -815,6 +821,7 @@
#include "code\game\objects\items\weapons\implants\implant_exile.dm"
#include "code\game\objects\items\weapons\implants\implant_explosive.dm"
#include "code\game\objects\items\weapons\implants\implant_freedom.dm"
+#include "code\game\objects\items\weapons\implants\implant_gang.dm"
#include "code\game\objects\items\weapons\implants\implant_krav_maga.dm"
#include "code\game\objects\items\weapons\implants\implant_loyality.dm"
#include "code\game\objects\items\weapons\implants\implant_misc.dm"