Skip to content
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

Gangs Rework - Gamemode #18236

Merged
merged 22 commits into from Mar 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions _std/defines/layer.dm
Expand Up @@ -18,6 +18,8 @@
#define COG2_WINDOW_LAYER (TURF_LAYER+0.95)

// More specific obj layers
#define SUB_TAG_LAYER (OBJ_LAYER - 0.03) //! Graffiti that's been sprayed over sits here
#define TAG_LAYER (OBJ_LAYER - 0.02) //! Graffiti layer for gangs, this is the topmost (ie. most recent) tag on a turf
#define STORAGE_LAYER (OBJ_LAYER - 0.01) // Keep lockers etc below items
#define ABOVE_OBJ_LAYER (OBJ_LAYER + 0.01) // For objects that should generally layer above other objects

Expand Down
3 changes: 3 additions & 0 deletions _std/macros/antag_popups.dm
Expand Up @@ -198,6 +198,9 @@
if ("gang_member")
window_title = "You've joined a Gang!"
filename = "html/traitorTips/gang_member_added.html"
if ("gang_leader")
window_title = "You've started a Gang!"
filename = "html/traitorTips/gang_leader.html"

// vamp/changeling
if ("vampire")
Expand Down
47 changes: 47 additions & 0 deletions browserassets/html/traitorTips/gang_leader.html
@@ -0,0 +1,47 @@
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="{{resource("css/style.css")}}">
</head>
<body class="traitor-tips">
<h1 class="center">You are a gang leader!</h1>
<img src="{{resource("images/antagTips/gangmember-image.png")}}" class="center" />

<p>1. Work together with your subordinates to rack up as many points as you can!<br>
You can get points by:</p>
<ul>
<li>Claiming popular parts of the station with spray cans. Gang tags that see people generate points!<br>
</li>
<li>Collecting weapon drops and crates. Watch the radio to find out about tip-offs!<br>
</li>
<li>Cram large quantities of cash into your locker to launder it into points!<br>
</li>
<li>Finally, cram guns, drugs & weed into your locker for even more points to spend!<br>
</li>
</ul>

<p>2. <b>Be prepared!</b> You have access to the leader-specific 'Street Cred':</p>
<ul>
<li>Street cred is earned the same as points, and spendable in the top right of the Locker.</li>
<li>Revival syringes let you bring a dead gang member back to life; or let gang members bring you back to life!</li>
<li>If there's no corpse to revive, you can buy a new gang member, who'll spawn at your locker in their place!</li>
</ul>

<p>3. Use the prefix :z to speak on your gang's radio frequency! Pay attention to it:</p>
<ul>
<li>Every so often, your gang will PDA message the location of a bag of weapons to a civilian! <br>
</li>
<li>You will only know which civilian recieved the message. <em>Ask them for it! Bribe them! Steal their PDA!</em><br>
</li>
<li>Weapon crates will occasionally spawn, too. Drag these to your locker and open them for guns & ammo! <br>
</li>
</ul>

<p>4. Wear your gang outfit proudly, and don't hide it under suits! Gang members in enemy territory will get a debuff if they aren't wearing their gangs' getup.</p>

<p>5. Remember, you're free to harm anyone who isn't in your gang, but they can do the same to you, so don't pick fights willy-nilly!</p>

<p>For more information, consult <a href='?src={ref};wiki=Gang'>the wiki</a></p>
</body>
</html>

20 changes: 16 additions & 4 deletions browserassets/html/traitorTips/gang_member_added.html
Expand Up @@ -10,17 +10,29 @@ <h1 class="center">You are a gang member!</h1>
<p>1. Work together with your gangster buddies to rack up as many points as you can!<br>
You can get points by:</p>
<ul>
<li>Tagging the station with your gang's spray can.<br>
<li>Claiming popular parts of the station with spray cans. Gang tags that see people generate points!<br>
</li>
<li>Stuffing illicit drugs (like meth and cannabis), guns, and cash into your locker.<br>
<li>Collecting weapon drops and crates.<br>
</li>
<li>Cram large quantities of cash into your locker to launder it into points!<br>
</li>
<li>Finally, cram guns, drugs & weed into your locker for even more points to spend!<br>
</li>
</ul>

<p>2. Use the prefix :z to speak on your gang's radio frequency! Occassionally you'll hear an announcement over this channel about priority areas - make sure to tag them when you can.</p>
<p>2. Use the prefix :z to speak on your gang's radio frequency! Pay attention to it:</p>
<ul>
<li>Every so often, your gang will PDA message the location of a bag of weapons to a civilian! <br>
</li>
<li>You will only know which civilian recieved the message. <em>Ask them for it! Bribe them! Steal their PDA!</em><br>
</li>
<li>Weapon crates will occasionally spawn, too. Drag these to your locker and open them for guns & ammo! <br>
</li>
</ul>

<p>3. <em>Don't attack fellow gang members and listen to your leader!</em> You can identify them by the G icon. Blue is your leader, red are your fellow henchmen.</p>

<p>4. <em>Defend your locker!</em> It's where you get your gang outfit, spraycan, and special gang-tuned headset! If another gang breaks it, you'll have to start all over.</p>
<p>4. Wear your gang outfit proudly! Gang members in enemy territory will get a debuff if they aren't wearing their gangs' getup!</p>

<p>5. Remember, you're free to harm anyone who isn't in your gang, but they can do the same to you, so don't pick fights willy-nilly!</p>

Expand Down
27 changes: 27 additions & 0 deletions code/datums/abilities/ghost_observer.dm
Expand Up @@ -152,6 +152,9 @@ var/global/datum/spooktober_ghost_handler/spooktober_GH = new()
src.addAbility(/datum/targetable/ghost_observer/reenter_corpse)
src.addAbility(/datum/targetable/ghost_observer/toggle_lighting)
src.addAbility(/datum/targetable/ghost_observer/toggle_ghosts)
if (istype(ticker.mode, /datum/game_mode/gang))
src.addAbility(/datum/targetable/ghost_observer/toggle_gang_overlay)

// src.addAbility(/datum/targetable/ghost_observer/afterlife_Bar)
// src.addAbility(/datum/targetable/ghost_observer/respawn_animal) //moved to respawn_options menu
src.addAbility(/datum/targetable/ghost_observer/respawn_options)
Expand Down Expand Up @@ -182,6 +185,8 @@ var/global/datum/spooktober_ghost_handler/spooktober_GH = new()
// src.removeAbility(/datum/targetable/ghost_observer/afterlife_Bar)
// src.removeAbility(/datum/targetable/ghost_observer/respawn_animal)
src.removeAbility(/datum/targetable/ghost_observer/respawn_options)
if (istype(ticker.mode, /datum/game_mode/gang))
src.removeAbility(/datum/targetable/ghost_observer/toggle_gang_overlay)

#ifdef HALLOWEEN
src.removeAbility(/datum/targetable/ghost_observer/spooktober_hud)
Expand Down Expand Up @@ -611,3 +616,25 @@ var/global/datum/spooktober_ghost_handler/spooktober_GH = new()
boutput(holder.owner, SPAN_ALERT("You stop being spooky!"))

#endif

/datum/targetable/ghost_observer/toggle_gang_overlay
name = "Toggle gang territory overlay"
desc = "Toggles the colored gang overlay."
icon_state = "gang_overlay"
targeted = 0
cooldown = 0
cast()
if (!holder)
return TRUE
var/mob/M = holder.owner

var/datum/client_image_group/imgroup = get_image_group(CLIENT_IMAGE_GROUP_GANGS)
var/togglingOn = FALSE
if (imgroup.subscribed_minds_with_subcount[M.mind] > 0)
imgroup.remove_mind(M.mind)
else
togglingOn = TRUE
imgroup.add_mind(M.mind)

boutput(M, "Gang territories turned [togglingOn ? "on" : "off"].")
return FALSE
82 changes: 82 additions & 0 deletions code/datums/actions/actions.dm
Expand Up @@ -1772,3 +1772,85 @@
target.anchored = UNANCHORED
else
target.anchored = ANCHORED


/datum/action/bar/icon/doorhack
duration = 3 SECONDS
interrupt_flags = INTERRUPT_MOVE | INTERRUPT_ACT | INTERRUPT_STUNNED | INTERRUPT_ACTION
icon = 'icons/obj/items/gang.dmi'
icon_state = "quickhack_fire"
id = "quickhacking"
var/maximum_range = 1
var/obj/machinery/door/airlock/target
var/obj/item/tool/quickhack/hack_tool

New(Owner, Target, Hack)
owner = Owner
target = Target
hack_tool = Hack
..()

onUpdate()
..()
if(!IN_RANGE(src.owner, target, maximum_range) || target == null || owner == null)
interrupt(INTERRUPT_ALWAYS)
return

onStart()
if (!src.owner)
interrupt(INTERRUPT_ALWAYS)
if (target && !IN_RANGE(src.owner, target, maximum_range))
interrupt(INTERRUPT_ALWAYS)
boutput(src.owner, "<span class='alert'>You press the [src.hack_tool.name] against the [src.target.name]...</span>")
..()

onEnd()
..()
if (!src.owner)
interrupt(INTERRUPT_ALWAYS)
if (src.target && !IN_RANGE(owner, target, maximum_range))
interrupt(INTERRUPT_ALWAYS)
else
hack_tool.force_open(owner, target)



/datum/action/bar/icon/janktanktwo
duration = JANKTANK2_CHANNEL_TIME
interrupt_flags = INTERRUPT_MOVE | INTERRUPT_ACT | INTERRUPT_STUNNED | INTERRUPT_ACTION
icon = 'icons/obj/items/gang.dmi'
icon_state = "janktank_2_inj"
id = "janktanktwo"
var/mob/living/carbon/human/target
var/obj/item/tool/janktanktwo/injector

New(Owner, Target, Injector)
owner = Owner
target = Target
injector = Injector
..()

onStart()
if (!src.owner)
interrupt(INTERRUPT_ALWAYS)
if (target && !IN_RANGE(src.owner, target, 1))
interrupt(INTERRUPT_ALWAYS)
boutput(src.owner, "<span class='alert'>You prepare the [injector.name], aiming right for [target]'s heart!</span>")
..()

onUpdate()
..()
if(!IN_RANGE(src.owner, target, 1) || target == null || owner == null)
interrupt(INTERRUPT_ALWAYS)
return

onEnd()
..()
if (!src.owner)
interrupt(INTERRUPT_ALWAYS)
if (src.target && !IN_RANGE(owner, target, 1))
interrupt(INTERRUPT_ALWAYS)
else
playsound(target.loc, 'sound/impact_sounds/Generic_Stab_1.ogg', 50, 0)
injector.inject(owner, target)

162 changes: 162 additions & 0 deletions code/datums/controllers/process/gang.dm
@@ -0,0 +1,162 @@
/datum/controller/process/gang_tag_vision
setup()
name = "Gang_Tags"
schedule_interval = GANG_TAG_SCAN_RATE

doWork()
for_by_tcl(tag, /obj/decal/gangtag)
if (tag.active)
tag.find_players()



/datum/controller/process/gang_tag_score
setup()
name = "Gang_Tags_Score"
schedule_interval = GANG_TAG_SCORE_INTERVAL

doWork()
var/topHeat = 0
// calculate all heats
for_by_tcl(I, /obj/decal/gangtag)
if (I.active)
topHeat = max(I.calculate_heat(), topHeat)

for_by_tcl(I, /obj/decal/gangtag)
if (I.active)
I.apply_score(topHeat)



/datum/controller/process/gang_spray_regen
setup()
name = "Gang_Tags_Spraypaint_Regen"
schedule_interval = GANG_SPRAYPAINT_REGEN
doWork()
if (istype(ticker.mode, /datum/game_mode/gang))
var/datum/game_mode/gang/gamemode = ticker.mode
broadcast_to_all_gangs("Each gang has [GANG_SPRAYPAINT_REGEN_QUANTITY > 1 ? "extra spray cans" : "an extra spray can" ] available from their locker.")
for(var/datum/gang/gang as anything in gamemode.gangs)
gang.spray_paint_remaining += GANG_SPRAYPAINT_REGEN_QUANTITY


/datum/controller/process/gang_launder_money
setup()
name = "Gang_Money_laundering"
schedule_interval = GANG_LAUNDER_DELAY
doWork()
for_by_tcl(locker, /obj/ganglocker)
if (!locker)
return
if (locker.stored_cash < 1)
locker.default_screen_overlay = image('icons/obj/large_storage.dmi', "gang_overlay_yellow")
locker.UpdateIcon()
return

var/launder_rate = GANG_LAUNDER_RATE
if (locker.superlaunder_stacks > 0)
locker.superlaunder_stacks -= 1
launder_rate = GANG_LAUNDER_RATE * 1.5

var/amount = round(min(locker.stored_cash, launder_rate))
var/points = round(amount/GANG_CASH_DIVISOR) // only launder full points
if (points < 1)
locker.default_screen_overlay = image('icons/obj/large_storage.dmi', "gang_overlay_yellow")
locker.UpdateIcon()
return

if (locker.superlaunder_stacks)
locker.default_screen_overlay = image('icons/obj/large_storage.dmi', "gang_overlay_superlaunder")
else
locker.default_screen_overlay = image('icons/obj/large_storage.dmi', "gang_overlay_launder")
locker.UpdateIcon()
locker.stored_cash -= (points*GANG_CASH_DIVISOR)
locker.gang.score_cash += points
locker.gang.add_points(points, location = get_turf(locker), showText = TRUE)


/datum/controller/process/gang_crate_drop
var/list/potential_hot_zones[0]
var/crate_spawn_repeats = 0
name = "Gang_Crate_Drops"
setup()
schedule_interval = GANG_CRATE_INITIAL_DROP
var/list/area/areas = get_accessible_station_areas()
for(var/area/area in areas)
if(istype(areas[area], /area/station/security) || areas[area].teleport_blocked || istype(areas[area], /area/station/turret_protected))
continue
potential_hot_zones += areas[area]
doWork()
if (!istype(ticker.mode, /datum/game_mode/gang))
src.disable()
return
crate_spawn_repeats++
if (crate_spawn_repeats == 1)
return //do nothing on our first run
else if (crate_spawn_repeats == 2)
schedule_interval = GANG_CRATE_DROP_FREQUENCY
var/turfList[0]
var/attempts = 0
var/area/drop_zone

while (attempts <= 3 && !length(turfList))
attempts++
drop_zone = pick(potential_hot_zones)
for (var/turf/simulated/floor/T in drop_zone.contents)
if (!is_blocked_turf(T))
turfList.Add(T)
if (!length(turfList))
logTheThing(LOG_DEBUG, null, "Couldn't find a valid location to drop a weapons crate inside [drop_zone.name].")
if (!length(turfList))
message_admins("All attempts to find a valid location to spawn a weapons crate failed!")
return
var/obj/storage/crate/gang_crate/guns_and_gear/crate = new(pick(turfList))
broadcast_to_all_gangs("<span style='font-size:24px'> We've dropped off weapons & ammunition at <b>\the [drop_zone.name]!</b> It's anchored in place for 5 minutes, so get fortifying!</span>")


SPAWN(GANG_CRATE_LOCK_TIME - 1 MINUTE)
if(drop_zone != null)
broadcast_to_all_gangs("The weapons crate at the [drop_zone.name] can be moved in 1 minute!")
logTheThing(LOG_GAMEMODE, crate, "The crate in [drop_zone.name] has 1 minute left. Current location: [crate.x],[crate.y].")
sleep(1 MINUTE)
if(drop_zone != null)
broadcast_to_all_gangs("The weapons crate at the [drop_zone.name] is free! Drag it to your locker.")
logTheThing(LOG_GAMEMODE, crate, "The crate in [drop_zone.name] is freed. Current location: [crate.x],[crate.y].")

/datum/controller/process/gang_duffle_drop
var/repeats = 1
var/duffle_spawn_repeats = 0
setup()
name = "Gang_Duffle_Drops"
schedule_interval = GANG_LOOT_INITIAL_DROP
src.repeats = GANG_LOOT_DROP_VOLUME_PER_GANG
doWork()
if (!istype(ticker.mode, /datum/game_mode/gang))
src.disable()
return
duffle_spawn_repeats++
if (duffle_spawn_repeats == 1)
return //do nothing on our first run
else if (duffle_spawn_repeats == 2)
schedule_interval = GANG_LOOT_DROP_FREQUENCY
var/datum/game_mode/gang/gamemode = ticker.mode
var/list/civiliansAlreadyPinged = list()// try not to have the same person picked twice
for(var/datum/gang/targetGang as anything in gamemode.gangs) //create loot bags for this gang (so they get pinged)
var/list/datum/mind/gangChosenCivvies = list() //which civilians have been picked for this gang
for(var/i = 1 to repeats)
var/datum/mind/civvie = targetGang.get_random_civvie(civiliansAlreadyPinged)
civiliansAlreadyPinged += civvie
if (!(civvie in gangChosenCivvies))
gangChosenCivvies += civvie
targetGang.target_loot_spawn(civvie)
var/broadcast_string = "<span style='font-size:20px'> Our associates have hidden [repeats] bag[s_es(repeats)] of weapons & supplies on board. The location[s_es(repeats)] have been tipped off to the PDAs of: "
if (length(gangChosenCivvies) > 1)
for (var/name=1 to length(gangChosenCivvies)-1)
broadcast_string += "[gangChosenCivvies[name].current.real_name] the [gangChosenCivvies[name].assigned_role]."
broadcast_string += "and [gangChosenCivvies[length(gangChosenCivvies)].current.real_name] the [gangChosenCivvies[length(gangChosenCivvies)].assigned_role]."
else
broadcast_string += "[gangChosenCivvies[1].current.real_name] the [gangChosenCivvies[1].assigned_role],"
broadcast_string += "</span>"
targetGang.broadcast_to_gang(broadcast_string)