mirror of
https://github.com/SPLURT-Station/S.P.L.U.R.T-Station-13.git
synced 2025-12-10 01:49:19 +00:00
Merge branch 'master' of https://github.com/Citadel-Station-13/Citadel-Station-13
This commit is contained in:
2
Build.sh
2
Build.sh
@@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
cd "$(dirname "$(readlink -f "$0")")"
|
||||
cd ./tools/build
|
||||
sudo bash ./build.sh
|
||||
sudo bash ./build
|
||||
|
||||
@@ -203,7 +203,7 @@
|
||||
/area/awaymission/jungleresort)
|
||||
"cK" = (
|
||||
/obj/structure/closet/crate,
|
||||
/obj/item/clothing/head/collectable/petehat/gang,
|
||||
/obj/item/clothing/head/collectable/petehat,
|
||||
/turf/open/floor/plating/rust,
|
||||
/area/awaymission/jungleresort)
|
||||
"cT" = (
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#define SHOVABLE_ONTO (1<<13)//called on turf.shove_act() to consider whether an object should have a niche effect (defined in their own shove_act()) when someone is pushed onto it, or do a sanity CanPass() check.
|
||||
#define EXAMINE_SKIP (1<<14) /// Makes the Examine proc not read out this item.
|
||||
#define IN_STORAGE (1<<15) //is this item in the storage item, such as backpack? used for tooltips
|
||||
#define HAND_ITEM (1<<16) // If an item is just your hand (circled hand, slapper) and shouldn't block things like riding
|
||||
|
||||
/// Integrity defines for clothing (not flags but close enough)
|
||||
#define CLOTHING_PRISTINE 0 // We have no damage on the clothing
|
||||
|
||||
@@ -120,3 +120,6 @@ GLOBAL_LIST_EMPTY(living_heart_cache) //A list of all living hearts in existance
|
||||
/// TC to charge someone if they get a free implant through choice or
|
||||
/// because they have nothing else that supports an implant.
|
||||
#define UPLINK_IMPLANT_TELECRYSTAL_COST 4
|
||||
|
||||
/// The dimensions of the antagonist preview icon. Will be scaled to this size.
|
||||
#define ANTAGONIST_PREVIEW_ICON_SIZE 96
|
||||
|
||||
6
code/__DEFINES/color/color_priority.dm
Normal file
6
code/__DEFINES/color/color_priority.dm
Normal file
@@ -0,0 +1,6 @@
|
||||
//different types of atom colorations
|
||||
#define ADMIN_COLOUR_PRIORITY 1 //only used by rare effects like greentext coloring mobs and when admins varedit color
|
||||
#define TEMPORARY_COLOUR_PRIORITY 2 //e.g. purple effect of the revenant on a mob, black effect when mob electrocuted
|
||||
#define WASHABLE_COLOUR_PRIORITY 3 //color splashed onto an atom (e.g. paint on turf)
|
||||
#define FIXED_COLOUR_PRIORITY 4 //color inherent to the atom (e.g. blob color)
|
||||
#define COLOUR_PRIORITY_AMOUNT 4 //how many priority levels there are.
|
||||
4
code/__DEFINES/color/lum.dm
Normal file
4
code/__DEFINES/color/lum.dm
Normal file
@@ -0,0 +1,4 @@
|
||||
//Luma coefficients suggested for HDTVs. If you change these, make sure they add up to 1.
|
||||
#define LUMA_R 0.213
|
||||
#define LUMA_G 0.715
|
||||
#define LUMA_B 0.072
|
||||
@@ -472,6 +472,14 @@
|
||||
#define COMSIG_MINE_TRIGGERED "minegoboom" ///from [/obj/effect/mine/proc/triggermine]:
|
||||
// Uncovered information
|
||||
#define COMPONENT_DEEPSCAN_UNCOVERED_INFORMATION 1
|
||||
///Called when an item is being offered, from [/obj/item/proc/on_offered(mob/living/carbon/offerer)]
|
||||
#define COMSIG_ITEM_OFFERING "item_offering"
|
||||
///Interrupts the offer proc
|
||||
#define COMPONENT_OFFER_INTERRUPT (1<<0)
|
||||
///Called when an someone tries accepting an offered item, from [/obj/item/proc/on_offer_taken(mob/living/carbon/offer, mob/living/carbon/taker)]
|
||||
#define COMSIG_ITEM_OFFER_TAKEN "item_offer_taken"
|
||||
///Interrupts the offer acceptance
|
||||
#define COMPONENT_OFFER_TAKE_INTERRUPT (1<<0)
|
||||
///from [/obj/structure/closet/supplypod/proc/endlaunch]:
|
||||
#define COMSIG_SUPPLYPOD_LANDED "supplypodgoboom"
|
||||
|
||||
|
||||
@@ -5,9 +5,10 @@
|
||||
|
||||
#define CLICKCATCHER_PLANE -99
|
||||
|
||||
#define PLANE_SPACE -95
|
||||
#define PLANE_SPACE -98
|
||||
#define PLANE_SPACE_RENDER_TARGET "PLANE_SPACE"
|
||||
#define PLANE_SPACE_PARALLAX -90
|
||||
|
||||
#define PLANE_SPACE_PARALLAX -95
|
||||
#define PLANE_SPACE_PARALLAX_RENDER_TARGET "PLANE_SPACE_PARALLAX"
|
||||
|
||||
#define OPENSPACE_LAYER 17 //Openspace layer over all
|
||||
|
||||
@@ -345,18 +345,6 @@ GLOBAL_LIST_INIT(pda_reskins, list(PDA_SKIN_CLASSIC = 'icons/obj/pda.dmi', PDA_S
|
||||
//TODO Move to a pref
|
||||
#define STATION_GOAL_BUDGET 1
|
||||
|
||||
//Luma coefficients suggested for HDTVs. If you change these, make sure they add up to 1.
|
||||
#define LUMA_R 0.213
|
||||
#define LUMA_G 0.715
|
||||
#define LUMA_B 0.072
|
||||
|
||||
//different types of atom colorations
|
||||
#define ADMIN_COLOUR_PRIORITY 1 //only used by rare effects like greentext coloring mobs and when admins varedit color
|
||||
#define TEMPORARY_COLOUR_PRIORITY 2 //e.g. purple effect of the revenant on a mob, black effect when mob electrocuted
|
||||
#define WASHABLE_COLOUR_PRIORITY 3 //color splashed onto an atom (e.g. paint on turf)
|
||||
#define FIXED_COLOUR_PRIORITY 4 //color inherent to the atom (e.g. blob color)
|
||||
#define COLOUR_PRIORITY_AMOUNT 4 //how many priority levels there are.
|
||||
|
||||
//Endgame Results
|
||||
#define NUKE_NEAR_MISS 1
|
||||
#define NUKE_MISS_STATION 2
|
||||
@@ -380,7 +368,8 @@ GLOBAL_LIST_INIT(pda_reskins, list(PDA_SKIN_CLASSIC = 'icons/obj/pda.dmi', PDA_S
|
||||
#define CLOCK_SILICONS 22
|
||||
#define CLOCK_PROSELYTIZATION 23
|
||||
#define SHUTTLE_HIJACK 24
|
||||
#define GANG_VICTORY 25
|
||||
#define GANG_DESTROYED 25
|
||||
#define GANG_OPERATING 26
|
||||
|
||||
#define FIELD_TURF 1
|
||||
#define FIELD_EDGE 2
|
||||
@@ -568,3 +557,8 @@ GLOBAL_LIST_INIT(pda_reskins, list(PDA_SKIN_CLASSIC = 'icons/obj/pda.dmi', PDA_S
|
||||
#define SHOES_KNOTTED 2
|
||||
|
||||
#define WANTED_FILE "wanted_message.json"
|
||||
|
||||
// Notification action types
|
||||
#define NOTIFY_JUMP "jump"
|
||||
#define NOTIFY_ATTACK "attack"
|
||||
#define NOTIFY_ORBIT "orbit"
|
||||
|
||||
@@ -34,12 +34,6 @@
|
||||
|
||||
#define TOGGLES_DEFAULT_CHAT (CHAT_OOC|CHAT_DEAD|CHAT_GHOSTEARS|CHAT_GHOSTSIGHT|CHAT_PRAYER|CHAT_RADIO|CHAT_PULLR|CHAT_GHOSTWHISPER|CHAT_GHOSTPDA|CHAT_GHOSTRADIO|CHAT_LOOC|CHAT_BANKCARD)
|
||||
|
||||
#define PARALLAX_INSANE -1 //for show offs
|
||||
#define PARALLAX_HIGH 0 //default.
|
||||
#define PARALLAX_MED 1
|
||||
#define PARALLAX_LOW 2
|
||||
#define PARALLAX_DISABLE 3 //this option must be the highest number
|
||||
|
||||
#define PIXEL_SCALING_AUTO 0
|
||||
#define PIXEL_SCALING_1X 1
|
||||
#define PIXEL_SCALING_1_2X 1.5
|
||||
@@ -50,10 +44,6 @@
|
||||
#define SCALING_METHOD_DISTORT "distort"
|
||||
#define SCALING_METHOD_BLUR "blur"
|
||||
|
||||
#define PARALLAX_DELAY_DEFAULT world.tick_lag
|
||||
#define PARALLAX_DELAY_MED 1
|
||||
#define PARALLAX_DELAY_LOW 2
|
||||
|
||||
#define SEC_DEPT_NONE "None"
|
||||
#define SEC_DEPT_RANDOM "Random"
|
||||
#define SEC_DEPT_ENGINEERING "Engineering"
|
||||
|
||||
@@ -60,11 +60,8 @@
|
||||
#define ANTAG_HUD_BROTHER 23
|
||||
#define ANTAG_HUD_BLOODSUCKER 24
|
||||
#define ANTAG_HUD_FUGITIVE 25
|
||||
#define ANTAG_HUD_HERETIC 26
|
||||
|
||||
// Notification action types
|
||||
#define NOTIFY_JUMP "jump"
|
||||
#define NOTIFY_ATTACK "attack"
|
||||
#define NOTIFY_ORBIT "orbit"
|
||||
#define ANTAG_HUD_HERETIC 26
|
||||
#define ANTAG_HUD_SPACECOP 27
|
||||
#define ANTAG_HUD_GANGSTER 28
|
||||
|
||||
#define ADD_HUD_TO_COOLDOWN 20 //cooldown for being shown the images for any particular data hud
|
||||
13
code/__DEFINES/rendering/parallax.dm
Normal file
13
code/__DEFINES/rendering/parallax.dm
Normal file
@@ -0,0 +1,13 @@
|
||||
#define PARALLAX_DELAY_DEFAULT world.tick_lag
|
||||
#define PARALLAX_DELAY_MED 1
|
||||
#define PARALLAX_DELAY_LOW 2
|
||||
|
||||
// WARNING - client.prefs uses this, if you change these make sure to update the code in preferences!
|
||||
#define PARALLAX_DISABLE 0
|
||||
#define PARALLAX_LOW 1
|
||||
#define PARALLAX_MED 2
|
||||
#define PARALLAX_HIGH 3
|
||||
#define PARALLAX_INSANE 4 // default
|
||||
|
||||
// keep this false until we can fix it being a seizure hazard/ugly as sin
|
||||
#define PARALLAX_ROTATION_ANIMATIONS FALSE
|
||||
@@ -36,7 +36,8 @@
|
||||
#define ROLE_DEATHSQUAD "deathsquad"
|
||||
#define ROLE_LAVALAND "lavaland"
|
||||
#define ROLE_INTERNAL_AFFAIRS "internal affairs agent"
|
||||
#define ROLE_GANG "gangster"
|
||||
#define ROLE_FAMILIES "family boss"
|
||||
#define ROLE_FAMILY_HEAD_ASPIRANT "family head aspirant"
|
||||
#define ROLE_BLOODSUCKER "bloodsucker"
|
||||
#define ROLE_SPACE_DRAGON "Space Dragon"
|
||||
//#define ROLE_MONSTERHUNTER "monster hunter" Disabled for now
|
||||
@@ -72,9 +73,9 @@ GLOBAL_LIST_INIT(special_roles, list(
|
||||
ROLE_OVERTHROW = /datum/game_mode/overthrow,
|
||||
ROLE_INTERNAL_AFFAIRS = /datum/game_mode/traitor/internal_affairs,
|
||||
ROLE_SENTIENCE,
|
||||
ROLE_GANG = /datum/game_mode/gang,
|
||||
ROLE_HERETIC = /datum/game_mode/heretics,
|
||||
ROLE_BLOODSUCKER = /datum/game_mode/bloodsucker,
|
||||
ROLE_FAMILIES = /datum/game_mode/gang,
|
||||
ROLE_SPACE_DRAGON
|
||||
//ROLE_MONSTERHUNTER Disabled for now
|
||||
))
|
||||
|
||||
@@ -44,7 +44,6 @@
|
||||
|
||||
#define SHUTTLE_TRANSIT_BORDER 10
|
||||
|
||||
#define PARALLAX_LOOP_TIME 25
|
||||
#define HYPERSPACE_END_TIME 5
|
||||
|
||||
#define HYPERSPACE_WARMUP 1
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
#define span_greenannounce(str) ("<span class='greenannounce'>" + str + "</span>")
|
||||
#define span_greenteamradio(str) ("<span class='greenteamradio'>" + str + "</span>")
|
||||
#define span_greentext(str) ("<span class='greentext'>" + str + "</span>")
|
||||
#define span_gangradio(str) ("<span class='gangradio'>" + str + "</span>")
|
||||
#define span_hear(str) ("<span class='hear'>" + str + "</span>")
|
||||
#define span_hidden(str) ("<span class='hidden'>" + str + "</span>")
|
||||
#define span_hierophant(str) ("<span class='hierophant'>" + str + "</span>")
|
||||
|
||||
@@ -136,6 +136,10 @@
|
||||
|
||||
#define STATUS_EFFECT_INLOVE /datum/status_effect/in_love //Displays you as being in love with someone else, and makes hearts appear around them.
|
||||
|
||||
#define STATUS_EFFECT_OFFERING /datum/status_effect/offering // you are offering up an item to people
|
||||
|
||||
#define STATUS_EFFECT_HANDSHAKE /datum/status_effect/offering/secret_handshake // you are attempting to perform a secret Family handshake
|
||||
|
||||
/////////////
|
||||
// SLIME //
|
||||
/////////////
|
||||
|
||||
@@ -202,6 +202,7 @@
|
||||
#define TRAIT_FRIENDLY "friendly"
|
||||
#define TRAIT_SNOB "snob"
|
||||
#define TRAIT_MULTILINGUAL "multilingual"
|
||||
#define TRAIT_HEARING_SENSITIVE "hearing_sensitive"
|
||||
#define TRAIT_CULT_EYES "cult_eyes"
|
||||
#define TRAIT_AUTO_CATCH_ITEM "auto_catch_item"
|
||||
#define TRAIT_CLOWN_MENTALITY "clown_mentality" // The future is now, clownman.
|
||||
|
||||
@@ -21,7 +21,9 @@
|
||||
#define LAZYACCESS(L, I) (L ? (isnum(I) ? (I > 0 && I <= length(L) ? L[I] : null) : L[I]) : null)
|
||||
#define LAZYSET(L, K, V) if(!L) { L = list(); } L[K] = V;
|
||||
#define LAZYLEN(L) length(L)
|
||||
///Sets a list to null
|
||||
///This is used to add onto lazy assoc list when the value you're adding is a /list/. This one has extra safety over lazyaddassoc because the value could be null (and thus cant be used to += objects)
|
||||
#define LAZYADDASSOCLIST(L, K, V) if(!L) { L = list(); } L[K] += list(V);
|
||||
//Sets a list to null
|
||||
#define LAZYNULL(L) L = null
|
||||
#define LAZYADDASSOC_TG(L, K, V) if(!L) { L = list(); } L[K] += V;
|
||||
///This is used to add onto lazy assoc list when the value you're adding is a /list/. This one has extra safety over lazyaddassoc because the value could be null (and thus cant be used to += objects)
|
||||
|
||||
@@ -1,85 +1,3 @@
|
||||
/matrix/proc/TurnTo(old_angle, new_angle)
|
||||
. = new_angle - old_angle
|
||||
Turn(.) //BYOND handles cases such as -270, 360, 540 etc. DOES NOT HANDLE 180 TURNS WELL, THEY TWEEN AND LOOK LIKE SHIT
|
||||
|
||||
/atom/proc/SpinAnimation(speed = 10, loops = -1, clockwise = 1, segments = 3, parallel = TRUE)
|
||||
if(!segments)
|
||||
return
|
||||
var/segment = 360/segments
|
||||
if(!clockwise)
|
||||
segment = -segment
|
||||
var/list/matrices = list()
|
||||
for(var/i in 1 to segments-1)
|
||||
var/matrix/M = matrix(transform)
|
||||
M.Turn(segment*i)
|
||||
matrices += M
|
||||
var/matrix/last = matrix(transform)
|
||||
matrices += last
|
||||
|
||||
speed /= segments
|
||||
|
||||
if(parallel)
|
||||
animate(src, transform = matrices[1], time = speed, loops , flags = ANIMATION_PARALLEL)
|
||||
else
|
||||
animate(src, transform = matrices[1], time = speed, loops)
|
||||
|
||||
for(var/i in 2 to segments) //2 because 1 is covered above
|
||||
animate(transform = matrices[i], time = speed)
|
||||
//doesn't have an object argument because this is "Stacking" with the animate call above
|
||||
//3 billion% intentional
|
||||
|
||||
//Dumps the matrix data in format a-f
|
||||
/matrix/proc/tolist()
|
||||
. = list()
|
||||
. += a
|
||||
. += b
|
||||
. += c
|
||||
. += d
|
||||
. += e
|
||||
. += f
|
||||
|
||||
//Dumps the matrix data in a matrix-grid format
|
||||
/*
|
||||
a d 0
|
||||
b e 0
|
||||
c f 1
|
||||
*/
|
||||
/matrix/proc/togrid()
|
||||
. = list()
|
||||
. += a
|
||||
. += d
|
||||
. += 0
|
||||
. += b
|
||||
. += e
|
||||
. += 0
|
||||
. += c
|
||||
. += f
|
||||
. += 1
|
||||
|
||||
//The X pixel offset of this matrix
|
||||
/matrix/proc/get_x_shift()
|
||||
. = c
|
||||
|
||||
//The Y pixel offset of this matrix
|
||||
/matrix/proc/get_y_shift()
|
||||
. = f
|
||||
|
||||
/matrix/proc/get_x_skew()
|
||||
. = b
|
||||
|
||||
/matrix/proc/get_y_skew()
|
||||
. = d
|
||||
|
||||
//Skews a matrix in a particular direction
|
||||
//Missing arguments are treated as no skew in that direction
|
||||
|
||||
//As Rotation is defined as a scale+skew, these procs will break any existing rotation
|
||||
//Unless the result is multiplied against the current matrix
|
||||
/matrix/proc/set_skew(x = 0, y = 0)
|
||||
b = x
|
||||
d = y
|
||||
|
||||
|
||||
/////////////////////
|
||||
// COLOUR MATRICES //
|
||||
/////////////////////
|
||||
@@ -114,12 +32,64 @@ list(0.393,0.349,0.272,0, 0.769,0.686,0.534,0, 0.189,0.168,0.131,0, 0,0,0,1, 0,0
|
||||
|
||||
return list(R + value,R,R,0, G,G + value,G,0, B,B,B + value,0, 0,0,0,1, 0,0,0,0)
|
||||
|
||||
/**
|
||||
* Exxagerates or removes colors
|
||||
*/
|
||||
/proc/color_matrix_saturation_percent(percent)
|
||||
if(percent == 0)
|
||||
return color_matrix_identity()
|
||||
percent = clamp(percent, -100, 100)
|
||||
if(percent > 0)
|
||||
percent *= 3
|
||||
var/x = 1 + percent / 100
|
||||
var/inv = 1 - x
|
||||
var/R = LUMA_R * inv
|
||||
var/G = LUMA_G * inv
|
||||
var/B = LUMA_B * inv
|
||||
|
||||
return list(R + x,R,R, G,G + x,G, B,B,B + x)
|
||||
|
||||
//Changes distance colors have from rgb(127,127,127) grey
|
||||
//1 is identity. 0 makes everything grey >1 blows out colors and greys
|
||||
/proc/color_matrix_contrast(value)
|
||||
var/add = (1 - value) / 2
|
||||
return list(value,0,0,0, 0,value,0,0, 0,0,value,0, 0,0,0,1, add,add,add,0)
|
||||
|
||||
/**
|
||||
* Exxagerates or removes brightness
|
||||
*/
|
||||
/proc/color_matrix_contrast_percent(percent)
|
||||
var/static/list/delta_index = list(
|
||||
0, 0.01, 0.02, 0.04, 0.05, 0.06, 0.07, 0.08, 0.1, 0.11,
|
||||
0.12, 0.14, 0.15, 0.16, 0.17, 0.18, 0.20, 0.21, 0.22, 0.24,
|
||||
0.25, 0.27, 0.28, 0.30, 0.32, 0.34, 0.36, 0.38, 0.40, 0.42,
|
||||
0.44, 0.46, 0.48, 0.5, 0.53, 0.56, 0.59, 0.62, 0.65, 0.68,
|
||||
0.71, 0.74, 0.77, 0.80, 0.83, 0.86, 0.89, 0.92, 0.95, 0.98,
|
||||
1.0, 1.06, 1.12, 1.18, 1.24, 1.30, 1.36, 1.42, 1.48, 1.54,
|
||||
1.60, 1.66, 1.72, 1.78, 1.84, 1.90, 1.96, 2.0, 2.12, 2.25,
|
||||
2.37, 2.50, 2.62, 2.75, 2.87, 3.0, 3.2, 3.4, 3.6, 3.8,
|
||||
4.0, 4.3, 4.7, 4.9, 5.0, 5.5, 6.0, 6.5, 6.8, 7.0,
|
||||
7.3, 7.5, 7.8, 8.0, 8.4, 8.7, 9.0, 9.4, 9.6, 9.8,
|
||||
10.0)
|
||||
percent = clamp(percent, -100, 100)
|
||||
if(percent == 0)
|
||||
return color_matrix_identity()
|
||||
|
||||
var/x = 0
|
||||
if (percent < 0)
|
||||
x = 127 + percent / 100 * 127;
|
||||
else
|
||||
x = percent % 1
|
||||
if(x == 0)
|
||||
x = delta_index[percent]
|
||||
else
|
||||
x = delta_index[percent] * (1-x) + delta_index[percent+1] * x//use linear interpolation for more granularity.
|
||||
x = x * 127 + 127
|
||||
|
||||
var/mult = x / 127
|
||||
var/add = 0.5 * (127-x) / 255
|
||||
return list(mult,0,0, 0,mult,0, 0,0,mult, add,add,add)
|
||||
|
||||
//Moves all colors angle degrees around the color wheel while maintaining intensity of the color and not affecting greys
|
||||
//0 is identity, 120 moves reds to greens, 240 moves reds to blues
|
||||
/proc/color_matrix_rotate_hue(angle)
|
||||
@@ -134,6 +104,26 @@ round(cos_inv_third+sqrt3_sin, 0.001), round(cos_inv_third-sqrt3_sin, 0.001), ro
|
||||
0,0,0,1,
|
||||
0,0,0,0)
|
||||
|
||||
/**
|
||||
* Moves all colors angle degrees around the color wheel while maintaining intensity of the color and not affecting whites
|
||||
* TODO: Need a version that only affects one color (ie shift red to blue but leave greens and blues alone)
|
||||
*/
|
||||
/proc/color_matrix_rotation(angle)
|
||||
if(angle == 0)
|
||||
return color_matrix_identity()
|
||||
angle = clamp(angle, -180, 180)
|
||||
var/cos = cos(angle)
|
||||
var/sin = sin(angle)
|
||||
|
||||
var/constA = 0.143
|
||||
var/constB = 0.140
|
||||
var/constC = -0.283
|
||||
return list(
|
||||
LUMA_R + cos * (1-LUMA_R) + sin * -LUMA_R, LUMA_R + cos * -LUMA_R + sin * constA, LUMA_R + cos * -LUMA_R + sin * -(1-LUMA_R),
|
||||
LUMA_G + cos * -LUMA_G + sin * -LUMA_G, LUMA_G + cos * (1-LUMA_G) + sin * constB, LUMA_G + cos * -LUMA_G + sin * LUMA_G,
|
||||
LUMA_B + cos * -LUMA_B + sin * (1-LUMA_B), LUMA_B + cos * -LUMA_B + sin * constC, LUMA_B + cos * (1-LUMA_B) + sin * LUMA_B
|
||||
)
|
||||
|
||||
//These next three rotate values about one axis only
|
||||
//x is the red axis, y is the green axis, z is the blue axis.
|
||||
/proc/color_matrix_rotate_x(angle)
|
||||
@@ -183,3 +173,9 @@ round(cos_inv_third+sqrt3_sin, 0.001), round(cos_inv_third-sqrt3_sin, 0.001), ro
|
||||
*/
|
||||
/proc/rgb_construct_color_matrix(rr = 1, rg, rb, gr, gg = 1, gb, br, bg, bb = 1, cr, cg, cb)
|
||||
return list(rr, rg, rb, gr, gg, gb, br, bg, bb, cr, cg, cb)
|
||||
|
||||
/**
|
||||
* Assembles a color matrix, defaulting to identity
|
||||
*/
|
||||
/proc/rgba_construct_color_matrix(rr = 1, rg, rb, ra, gr, gg = 1, gb, ga, br, bg, bb = 1, ba, ar, ag, ab, aa = 1, cr, cg, cb, ca)
|
||||
return list(rr, rg, rb, ra, gr, gg, gb, ga, br, bg, bb, ba, ar, ag, ab, aa, cr, cg, cb, ca)
|
||||
86
code/__HELPERS/matrices/transform_matrix.dm
Normal file
86
code/__HELPERS/matrices/transform_matrix.dm
Normal file
@@ -0,0 +1,86 @@
|
||||
/matrix/proc/TurnTo(old_angle, new_angle)
|
||||
. = new_angle - old_angle
|
||||
Turn(.) //BYOND handles cases such as -270, 360, 540 etc. DOES NOT HANDLE 180 TURNS WELL, THEY TWEEN AND LOOK LIKE SHIT
|
||||
|
||||
/atom/proc/SpinAnimation(speed = 10, loops = -1, clockwise = 1, segments = 3, parallel = TRUE)
|
||||
if(!segments)
|
||||
return
|
||||
var/segment = 360/segments
|
||||
if(!clockwise)
|
||||
segment = -segment
|
||||
var/list/matrices = list()
|
||||
for(var/i in 1 to segments-1)
|
||||
var/matrix/M = matrix(transform)
|
||||
M.Turn(segment*i)
|
||||
matrices += M
|
||||
var/matrix/last = matrix(transform)
|
||||
matrices += last
|
||||
|
||||
speed /= segments
|
||||
|
||||
if(parallel)
|
||||
animate(src, transform = matrices[1], time = speed, loops , flags = ANIMATION_PARALLEL)
|
||||
else
|
||||
animate(src, transform = matrices[1], time = speed, loops)
|
||||
|
||||
for(var/i in 2 to segments) //2 because 1 is covered above
|
||||
animate(transform = matrices[i], time = speed)
|
||||
//doesn't have an object argument because this is "Stacking" with the animate call above
|
||||
//3 billion% intentional
|
||||
|
||||
//Dumps the matrix data in format a-f
|
||||
/matrix/proc/tolist()
|
||||
. = list()
|
||||
. += a
|
||||
. += b
|
||||
. += c
|
||||
. += d
|
||||
. += e
|
||||
. += f
|
||||
|
||||
//Dumps the matrix data in a matrix-grid format
|
||||
/*
|
||||
a d 0
|
||||
b e 0
|
||||
c f 1
|
||||
*/
|
||||
/matrix/proc/togrid()
|
||||
. = list()
|
||||
. += a
|
||||
. += d
|
||||
. += 0
|
||||
. += b
|
||||
. += e
|
||||
. += 0
|
||||
. += c
|
||||
. += f
|
||||
. += 1
|
||||
|
||||
//The X pixel offset of this matrix
|
||||
/matrix/proc/get_x_shift()
|
||||
. = c
|
||||
|
||||
//The Y pixel offset of this matrix
|
||||
/matrix/proc/get_y_shift()
|
||||
. = f
|
||||
|
||||
/matrix/proc/get_x_skew()
|
||||
. = b
|
||||
|
||||
/matrix/proc/get_y_skew()
|
||||
. = d
|
||||
|
||||
//Skews a matrix in a particular direction
|
||||
//Missing arguments are treated as no skew in that direction
|
||||
|
||||
//As Rotation is defined as a scale+skew, these procs will break any existing rotation
|
||||
//Unless the result is multiplied against the current matrix
|
||||
/matrix/proc/set_skew(x = 0, y = 0)
|
||||
b = x
|
||||
d = y
|
||||
|
||||
/**
|
||||
* constructs a transform matrix, defaulting to identity
|
||||
*/
|
||||
/proc/transform_matrix_construct(a = 1, b, c, d = 1, e, f)
|
||||
return matrix(a, b, c, d, e, f)
|
||||
@@ -473,3 +473,6 @@ GLOBAL_LIST_EMPTY(species_datums)
|
||||
/// Gets the client of the mob, allowing for mocking of the client.
|
||||
/// You only need to use this if you know you're going to be mocking clients somewhere else.
|
||||
#define GET_CLIENT(mob) (##mob.client || ##mob.mock_client)
|
||||
|
||||
//check if the person is dead, not sure where to put this
|
||||
#define IS_DEAD_OR_INCAP(source) (source.incapacitated() || source.stat)
|
||||
|
||||
@@ -152,7 +152,7 @@
|
||||
continue
|
||||
to_chat(mob_to_teleport, announcement)
|
||||
SEND_SOUND(mob_to_teleport, meeting_sound) //no preferences here, you must hear the funny sound
|
||||
mob_to_teleport.overlay_fullscreen("emergency_meeting", /atom/movable/screen/fullscreen/emergency_meeting, 1)
|
||||
mob_to_teleport.overlay_fullscreen("emergency_meeting", /atom/movable/screen/fullscreen/scaled/emergency_meeting, 1)
|
||||
addtimer(CALLBACK(mob_to_teleport, /mob/.proc/clear_fullscreen, "emergency_meeting"), 3 SECONDS)
|
||||
|
||||
if (is_station_level(mob_to_teleport.z)) //teleport the mob to the crew meeting
|
||||
|
||||
@@ -590,8 +590,8 @@
|
||||
var/list/all_teams = list()
|
||||
var/list/all_antagonists = list()
|
||||
|
||||
// for(var/datum/team/A in GLOB.antagonist_teams)
|
||||
// all_teams |= A
|
||||
for(var/datum/team/A in GLOB.antagonist_teams)
|
||||
all_teams |= A
|
||||
|
||||
for(var/datum/antagonist/A in GLOB.antagonists)
|
||||
if(!A.owner)
|
||||
|
||||
@@ -52,6 +52,8 @@ GLOBAL_LIST_EMPTY(current_observers_list)
|
||||
|
||||
//Dynamic Port
|
||||
GLOBAL_LIST_EMPTY(new_player_list) //all /mob/dead/new_player, in theory all should have clients and those that don't are in the process of spawning and get deleted when done.
|
||||
//Family Port
|
||||
GLOBAL_LIST_EMPTY(pre_setup_antags) //minds that have been picked as antag by the gamemode. removed as antag datums are set.
|
||||
|
||||
/proc/update_config_movespeed_type_lookup(update_mobs = TRUE)
|
||||
// NOTE: This is entirely based on the fact that byond typesof/subtypesof gets longer/deeper paths before shallower ones.
|
||||
|
||||
@@ -470,51 +470,6 @@
|
||||
else
|
||||
setDir(WEST, ismousemovement)
|
||||
|
||||
//debug
|
||||
/atom/movable/screen/proc/scale_to(x1,y1)
|
||||
if(!y1)
|
||||
y1 = x1
|
||||
var/matrix/M = new
|
||||
M.Scale(x1,y1)
|
||||
transform = M
|
||||
|
||||
/atom/movable/screen/click_catcher
|
||||
icon = 'icons/mob/screen_gen.dmi'
|
||||
icon_state = "catcher"
|
||||
plane = CLICKCATCHER_PLANE
|
||||
mouse_opacity = MOUSE_OPACITY_OPAQUE
|
||||
screen_loc = "CENTER"
|
||||
|
||||
#define MAX_SAFE_BYOND_ICON_SCALE_TILES (MAX_SAFE_BYOND_ICON_SCALE_PX / world.icon_size)
|
||||
#define MAX_SAFE_BYOND_ICON_SCALE_PX (33 * 32) //Not using world.icon_size on purpose.
|
||||
|
||||
/atom/movable/screen/click_catcher/proc/UpdateGreed(view_size_x = 15, view_size_y = 15)
|
||||
var/icon/newicon = icon('icons/mob/screen_gen.dmi', "catcher")
|
||||
var/ox = min(MAX_SAFE_BYOND_ICON_SCALE_TILES, view_size_x)
|
||||
var/oy = min(MAX_SAFE_BYOND_ICON_SCALE_TILES, view_size_y)
|
||||
var/px = view_size_x * world.icon_size
|
||||
var/py = view_size_y * world.icon_size
|
||||
var/sx = min(MAX_SAFE_BYOND_ICON_SCALE_PX, px)
|
||||
var/sy = min(MAX_SAFE_BYOND_ICON_SCALE_PX, py)
|
||||
newicon.Scale(sx, sy)
|
||||
icon = newicon
|
||||
screen_loc = "CENTER-[(ox-1)*0.5],CENTER-[(oy-1)*0.5]"
|
||||
var/matrix/M = new
|
||||
M.Scale(px/sx, py/sy)
|
||||
transform = M
|
||||
|
||||
/atom/movable/screen/click_catcher/Click(location, control, params)
|
||||
var/list/modifiers = params2list(params)
|
||||
if(modifiers["middle"] && iscarbon(usr))
|
||||
var/mob/living/carbon/C = usr
|
||||
C.swap_hand()
|
||||
else
|
||||
var/turf/T = params2turf(modifiers["screen-loc"], get_turf(usr.client ? usr.client.eye : usr), usr.client)
|
||||
params += "&catcher=1"
|
||||
if(T)
|
||||
T.Click(location, control, params)
|
||||
. = 1
|
||||
|
||||
/* MouseWheelOn */
|
||||
|
||||
/mob/proc/MouseWheelOn(atom/A, delta_x, delta_y, params)
|
||||
|
||||
@@ -176,9 +176,6 @@
|
||||
#define ui_ghost_mafia "SOUTH: 6, CENTER+2:24"
|
||||
#define ui_ghost_spawners "SOUTH: 6, CENTER+1:24" // LEGACY. SAME LOC AS PAI
|
||||
|
||||
// #define ui_wanted_lvl "NORTH,11"
|
||||
|
||||
|
||||
//UI position overrides for 1:1 screen layout. (default is 7:5)
|
||||
#define ui_stamina "EAST-1:28,CENTER:17" // replacing internals button
|
||||
#define ui_overridden_resist "EAST-3:24,SOUTH+1:7"
|
||||
@@ -190,3 +187,5 @@
|
||||
#define ui_boxarea "EAST-4:6,SOUTH+1:6"
|
||||
#define ui_boxlang "EAST-5:22,SOUTH+1:6"
|
||||
#define ui_boxvore "EAST-5:22,SOUTH+1:6"
|
||||
|
||||
#define ui_wanted_lvl "NORTH,11"
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
|
||||
//PUBLIC - call these wherever you want
|
||||
|
||||
|
||||
/mob/proc/throw_alert(category, type, severity, obj/new_master, override = FALSE)
|
||||
|
||||
/* Proc to create or update an alert. Returns the alert if the alert is new or updated, 0 if it was thrown already
|
||||
category is a text string. Each mob may only have one alert per category; the previous one will be replaced
|
||||
path is a type path of the actual alert type to throw
|
||||
@@ -14,6 +11,7 @@
|
||||
Clicks are forwarded to master
|
||||
Override makes it so the alert is not replaced until cleared by a clear_alert with clear_override, and it's used for hallucinations.
|
||||
*/
|
||||
/mob/proc/throw_alert(category, type, severity, obj/new_master, override = FALSE)
|
||||
|
||||
if(!category || QDELETED(src))
|
||||
return
|
||||
@@ -42,7 +40,7 @@
|
||||
thealert.override_alerts = override
|
||||
if(override)
|
||||
thealert.timeout = null
|
||||
thealert.mob_viewer = src
|
||||
thealert.owner = src
|
||||
|
||||
if(new_master)
|
||||
var/old_layer = new_master.layer
|
||||
@@ -97,8 +95,10 @@
|
||||
var/severity = 0
|
||||
var/alerttooltipstyle = ""
|
||||
var/override_alerts = FALSE //If it is overriding other alerts of the same type
|
||||
var/mob/mob_viewer //the mob viewing this alert
|
||||
var/mob/owner //Alert owner
|
||||
|
||||
/// Boolean. If TRUE, the Click() proc will attempt to Click() on the master first if there is a master.
|
||||
var/click_master = TRUE
|
||||
|
||||
/atom/movable/screen/alert/MouseEntered(location,control,params)
|
||||
if(!QDELETED(src))
|
||||
@@ -251,6 +251,8 @@ or something covering your eyes."
|
||||
|
||||
/atom/movable/screen/alert/mind_control/Click()
|
||||
var/mob/living/L = usr
|
||||
if(L != owner)
|
||||
return
|
||||
to_chat(L, "<span class='mind_control'>[command]</span>")
|
||||
|
||||
/atom/movable/screen/alert/hypnosis
|
||||
@@ -271,7 +273,7 @@ If you're feeling frisky, examine yourself and click the underlined item to pull
|
||||
icon_state = "embeddedobject"
|
||||
|
||||
/atom/movable/screen/alert/embeddedobject/Click()
|
||||
if(isliving(usr))
|
||||
if(isliving(usr) && usr == owner)
|
||||
var/mob/living/carbon/M = usr
|
||||
return M.help_shake_act(M)
|
||||
|
||||
@@ -300,7 +302,7 @@ or shoot a gun to move around via Newton's 3rd Law of Motion."
|
||||
|
||||
/atom/movable/screen/alert/fire/Click()
|
||||
var/mob/living/L = usr
|
||||
if(!istype(L) || !L.can_resist())
|
||||
if(!istype(L) || !L.can_resist() || L != owner)
|
||||
return
|
||||
L.MarkResistTime()
|
||||
if(CHECK_MOBILITY(L, MOBILITY_MOVE))
|
||||
@@ -308,37 +310,110 @@ or shoot a gun to move around via Newton's 3rd Law of Motion."
|
||||
|
||||
/atom/movable/screen/alert/give // information set when the give alert is made
|
||||
icon_state = "default"
|
||||
var/mob/living/carbon/giver
|
||||
var/mob/living/carbon/offerer
|
||||
var/obj/item/receiving
|
||||
|
||||
/**
|
||||
* Handles assigning most of the variables for the alert that pops up when an item is offered
|
||||
*
|
||||
* Handles setting the name, description and icon of the alert and tracking the person giving
|
||||
* and the item being offered, also registers a signal that removes the alert from anyone who moves away from the giver
|
||||
* Arguments:
|
||||
* * taker - The person receiving the alert
|
||||
* * giver - The person giving the alert and item
|
||||
* * receiving - The item being given by the giver
|
||||
*/
|
||||
/atom/movable/screen/alert/give/proc/setup(mob/living/carbon/taker, mob/living/carbon/giver, obj/item/receiving)
|
||||
name = "[giver] is offering [receiving]"
|
||||
desc = "[giver] is offering [receiving]. Click this alert to take it."
|
||||
* Handles assigning most of the variables for the alert that pops up when an item is offered
|
||||
*
|
||||
* Handles setting the name, description and icon of the alert and tracking the person giving
|
||||
* and the item being offered, also registers a signal that removes the alert from anyone who moves away from the offerer
|
||||
* Arguments:
|
||||
* * taker - The person receiving the alert
|
||||
* * offerer - The person giving the alert and item
|
||||
* * receiving - The item being given by the offerer
|
||||
*/
|
||||
/atom/movable/screen/alert/give/proc/setup(mob/living/carbon/taker, mob/living/carbon/offerer, obj/item/receiving)
|
||||
name = "[offerer] is offering [receiving]"
|
||||
desc = "[offerer] is offering [receiving]. Click this alert to take it."
|
||||
icon_state = "template"
|
||||
cut_overlays()
|
||||
add_overlay(receiving)
|
||||
src.receiving = receiving
|
||||
src.giver = giver
|
||||
RegisterSignal(taker, COMSIG_MOVABLE_MOVED, .proc/removeAlert)
|
||||
|
||||
/atom/movable/screen/alert/give/proc/removeAlert()
|
||||
to_chat(usr, "<span class='warning'>You moved out of range of [giver]!</span>")
|
||||
usr.clear_alert("[giver]")
|
||||
src.offerer = offerer
|
||||
RegisterSignal(taker, COMSIG_MOVABLE_MOVED, .proc/check_in_range, override = TRUE) //Override to prevent runtimes when people offer a item multiple times
|
||||
|
||||
/atom/movable/screen/alert/give/Click(location, control, params)
|
||||
. = ..()
|
||||
var/mob/living/carbon/C = usr
|
||||
C.take(giver, receiving)
|
||||
if(!.)
|
||||
return
|
||||
if(!iscarbon(usr))
|
||||
CRASH("User for [src] is of type \[[usr.type]\]. This should never happen.")
|
||||
handle_transfer()
|
||||
|
||||
/// An overrideable proc used simply to hand over the item when claimed, this is a proc so that high-fives can override them since nothing is actually transferred
|
||||
/atom/movable/screen/alert/give/proc/handle_transfer()
|
||||
var/mob/living/carbon/taker = owner
|
||||
taker.take(offerer, receiving)
|
||||
|
||||
/// Simply checks if the other person is still in range
|
||||
/atom/movable/screen/alert/give/proc/check_in_range(atom/taker)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(!offerer.CanReach(taker))
|
||||
to_chat(owner, span_warning("You moved out of range of [offerer]!"))
|
||||
owner.clear_alert("[offerer]")
|
||||
|
||||
/atom/movable/screen/alert/give/highfive/setup(mob/living/carbon/taker, mob/living/carbon/offerer, obj/item/receiving)
|
||||
. = ..()
|
||||
name = "[offerer] is offering a high-five!"
|
||||
desc = "[offerer] is offering a high-five! Click this alert to slap it."
|
||||
RegisterSignal(offerer, COMSIG_PARENT_EXAMINE_MORE, .proc/check_fake_out)
|
||||
|
||||
/atom/movable/screen/alert/give/highfive/handle_transfer()
|
||||
var/mob/living/carbon/taker = owner
|
||||
if(receiving && (receiving in offerer.held_items))
|
||||
receiving.on_offer_taken(offerer, taker)
|
||||
return
|
||||
too_slow_p1()
|
||||
|
||||
/// If the person who offered the high five no longer has it when we try to accept it, we get pranked hard
|
||||
/atom/movable/screen/alert/give/highfive/proc/too_slow_p1()
|
||||
var/mob/living/carbon/rube = owner
|
||||
if(!rube || !offerer)
|
||||
qdel(src)
|
||||
return
|
||||
|
||||
offerer.visible_message(span_notice("[rube] rushes in to high-five [offerer], but-"), span_nicegreen("[rube] falls for your trick just as planned, lunging for a high-five that no longer exists! Classic!"), ignored_mobs=rube)
|
||||
to_chat(rube, span_nicegreen("You go in for [offerer]'s high-five, but-"))
|
||||
addtimer(CALLBACK(src, .proc/too_slow_p2, offerer, rube), 0.5 SECONDS)
|
||||
|
||||
/// Part two of the ultimate prank
|
||||
/atom/movable/screen/alert/give/highfive/proc/too_slow_p2()
|
||||
var/mob/living/carbon/rube = owner
|
||||
if(!rube || !offerer)
|
||||
qdel(src)
|
||||
return
|
||||
|
||||
offerer.visible_message(span_danger("[offerer] pulls away from [rube]'s slap at the last second, dodging the high-five entirely!"), span_nicegreen("[rube] fails to make contact with your hand, making an utter fool of [rube.p_them()]self!"), span_hear("You hear a disappointing sound of flesh not hitting flesh!"), ignored_mobs=rube)
|
||||
var/all_caps_for_emphasis = uppertext("NO! [offerer] PULLS [offerer.p_their()] HAND AWAY FROM YOURS! YOU'RE TOO SLOW!")
|
||||
to_chat(rube, span_userdanger("[all_caps_for_emphasis]"))
|
||||
playsound(offerer, 'sound/weapons/thudswoosh.ogg', 100, TRUE, 1)
|
||||
rube.Knockdown(1 SECONDS)
|
||||
SEND_SIGNAL(offerer, COMSIG_ADD_MOOD_EVENT, "high_five", /datum/mood_event/down_low)
|
||||
SEND_SIGNAL(rube, COMSIG_ADD_MOOD_EVENT, "high_five", /datum/mood_event/too_slow)
|
||||
qdel(src)
|
||||
|
||||
/// If someone examine_more's the offerer while they're trying to pull a too-slow, it'll tip them off to the offerer's trickster ways
|
||||
/atom/movable/screen/alert/give/highfive/proc/check_fake_out(datum/source, mob/user, list/examine_list)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(!receiving)
|
||||
examine_list += "[span_warning("[offerer]'s arm appears tensed up, as if [offerer.p_they()] plan on pulling it back suddenly...")]\n"
|
||||
|
||||
/// Families handshakes
|
||||
/atom/movable/screen/alert/give/secret_handshake
|
||||
icon_state = "default"
|
||||
|
||||
/atom/movable/screen/alert/give/secret_handshake/setup(mob/living/carbon/taker, mob/living/carbon/offerer, obj/item/receiving)
|
||||
name = "[offerer] is offering a Handshake"
|
||||
desc = "[offerer] wants to teach you the Secret Handshake for their Family and induct you! Click on this alert to accept."
|
||||
icon_state = "template"
|
||||
cut_overlays()
|
||||
add_overlay(receiving)
|
||||
src.receiving = receiving
|
||||
src.offerer = offerer
|
||||
RegisterSignal(taker, COMSIG_MOVABLE_MOVED, .proc/check_in_range, override = TRUE) //Override to prevent runtimes when people offer a item multiple times
|
||||
|
||||
//ALIENS
|
||||
|
||||
@@ -393,10 +468,10 @@ or shoot a gun to move around via Newton's 3rd Law of Motion."
|
||||
/atom/movable/screen/alert/bloodsense/process()
|
||||
var/atom/blood_target
|
||||
|
||||
if(!mob_viewer.mind)
|
||||
if(!owner.mind)
|
||||
return
|
||||
|
||||
var/datum/antagonist/cult/antag = mob_viewer.mind.has_antag_datum(/datum/antagonist/cult,TRUE)
|
||||
var/datum/antagonist/cult/antag = owner.mind.has_antag_datum(/datum/antagonist/cult,TRUE)
|
||||
if(!antag?.cult_team)
|
||||
return
|
||||
var/datum/objective/sacrifice/sac_objective = locate() in antag.cult_team.objectives
|
||||
@@ -433,7 +508,7 @@ or shoot a gun to move around via Newton's 3rd Law of Motion."
|
||||
add_overlay(narnar)
|
||||
return
|
||||
var/turf/P = get_turf(blood_target)
|
||||
var/turf/Q = get_turf(mob_viewer)
|
||||
var/turf/Q = get_turf(owner)
|
||||
if(!P || !Q || (P.z != Q.z)) //The target is on a different Z level, we cannot sense that far.
|
||||
icon_state = "runed_sense2"
|
||||
desc = "You can no longer sense your target's presence."
|
||||
@@ -497,7 +572,7 @@ or shoot a gun to move around via Newton's 3rd Law of Motion."
|
||||
for(var/mob/living/L in GLOB.alive_mob_list)
|
||||
if(is_servant_of_ratvar(L))
|
||||
servants++
|
||||
var/datum/antagonist/clockcult/C = mob_viewer.mind.has_antag_datum(/datum/antagonist/clockcult,TRUE)
|
||||
var/datum/antagonist/clockcult/C = owner.mind.has_antag_datum(/datum/antagonist/clockcult,TRUE)
|
||||
if(C && C.clock_team)
|
||||
textlist += "[C.clock_team.eminence ? "There is an Eminence." : "<b>There is no Eminence! Get one ASAP!</b>"]<br>"
|
||||
textlist += "There are currently <b>[servants]</b> servant[servants > 1 ? "s" : ""] of Ratvar.<br>"
|
||||
@@ -595,7 +670,7 @@ so as to remain in compliance with the most up-to-date laws."
|
||||
var/atom/target = null
|
||||
|
||||
/atom/movable/screen/alert/hackingapc/Click()
|
||||
if(!usr || !usr.client)
|
||||
if(!usr || !usr.client || usr != owner)
|
||||
return
|
||||
if(!target)
|
||||
return
|
||||
@@ -621,7 +696,7 @@ so as to remain in compliance with the most up-to-date laws."
|
||||
timeout = 300
|
||||
|
||||
/atom/movable/screen/alert/notify_cloning/Click()
|
||||
if(!usr || !usr.client)
|
||||
if(!usr || !usr.client || usr != owner)
|
||||
return
|
||||
var/mob/dead/observer/G = usr
|
||||
G.reenter_corpse()
|
||||
@@ -635,7 +710,7 @@ so as to remain in compliance with the most up-to-date laws."
|
||||
var/action = NOTIFY_JUMP
|
||||
|
||||
/atom/movable/screen/alert/notify_action/Click()
|
||||
if(!usr || !usr.client)
|
||||
if(!usr || !usr.client || usr != owner)
|
||||
return
|
||||
if(!target)
|
||||
return
|
||||
@@ -669,14 +744,14 @@ so as to remain in compliance with the most up-to-date laws."
|
||||
|
||||
/atom/movable/screen/alert/restrained/Click()
|
||||
var/mob/living/L = usr
|
||||
if(!istype(L) || !L.can_resist())
|
||||
if(!istype(L) || !L.can_resist() || L != owner)
|
||||
return
|
||||
L.MarkResistTime()
|
||||
return L.resist_restraints()
|
||||
|
||||
/atom/movable/screen/alert/restrained/buckled/Click()
|
||||
var/mob/living/L = usr
|
||||
if(!istype(L) || !L.can_resist())
|
||||
if(!istype(L) || !L.can_resist() || L != owner)
|
||||
return
|
||||
L.MarkResistTime()
|
||||
return L.resist_buckle()
|
||||
@@ -693,7 +768,7 @@ so as to remain in compliance with the most up-to-date laws."
|
||||
|
||||
/atom/movable/screen/alert/shoes/Click()
|
||||
var/mob/living/carbon/C = usr
|
||||
if(!istype(C) || !C.can_resist() || C != mob_viewer || !C.shoes)
|
||||
if(!istype(C) || !C.can_resist() || C != owner || !C.shoes)
|
||||
return
|
||||
C.MarkResistTime()
|
||||
C.shoes.handle_tying(C)
|
||||
@@ -701,11 +776,14 @@ so as to remain in compliance with the most up-to-date laws."
|
||||
// PRIVATE = only edit, use, or override these if you're editing the system as a whole
|
||||
|
||||
// Re-render all alerts - also called in /datum/hud/show_hud() because it's needed there
|
||||
/datum/hud/proc/reorganize_alerts()
|
||||
/datum/hud/proc/reorganize_alerts(mob/viewmob)
|
||||
var/mob/screenmob = viewmob || mymob
|
||||
if(!screenmob.client)
|
||||
return
|
||||
var/list/alerts = mymob.alerts
|
||||
if(!hud_shown)
|
||||
for(var/i = 1, i <= alerts.len, i++)
|
||||
mymob.client.screen -= alerts[alerts[i]]
|
||||
screenmob.client.screen -= alerts[alerts[i]]
|
||||
return 1
|
||||
for(var/i = 1, i <= alerts.len, i++)
|
||||
var/atom/movable/screen/alert/alert = alerts[alerts[i]]
|
||||
@@ -725,22 +803,29 @@ so as to remain in compliance with the most up-to-date laws."
|
||||
else
|
||||
. = ""
|
||||
alert.screen_loc = .
|
||||
mymob.client.screen |= alert
|
||||
screenmob.client.screen |= alert
|
||||
if(!viewmob)
|
||||
for(var/M in mymob.observers)
|
||||
reorganize_alerts(M)
|
||||
return 1
|
||||
|
||||
/atom/movable/screen/alert/Click(location, control, params)
|
||||
if(!usr || !usr.client)
|
||||
return
|
||||
return FALSE
|
||||
if(usr != owner)
|
||||
return FALSE
|
||||
var/paramslist = params2list(params)
|
||||
if(paramslist["shift"]) // screen objects don't do the normal Click() stuff so we'll cheat
|
||||
to_chat(usr, "<span class='boldnotice'>[name]</span> - <span class='info'>[desc]</span>")
|
||||
return
|
||||
if(master)
|
||||
return FALSE
|
||||
if(master && click_master)
|
||||
return usr.client.Click(master, location, control, params)
|
||||
|
||||
return TRUE
|
||||
|
||||
/atom/movable/screen/alert/Destroy()
|
||||
. = ..()
|
||||
severity = 0
|
||||
master = null
|
||||
mob_viewer = null
|
||||
owner = null
|
||||
screen_loc = ""
|
||||
|
||||
27
code/_onclick/hud/families.dm
Normal file
27
code/_onclick/hud/families.dm
Normal file
@@ -0,0 +1,27 @@
|
||||
/atom/movable/screen/wanted
|
||||
name = "Space Police Alertness"
|
||||
desc = "Shows the current level of hostility the space police is planning to rain down on you. Better be careful."
|
||||
icon = 'icons/obj/gang/wanted_160x32.dmi'
|
||||
icon_state = "wanted_0"
|
||||
screen_loc = ui_wanted_lvl
|
||||
/// Wanted level, affects the hud icon. Level 0 is default, and the level 0 icon is blank, so in case of no families gamemode (and thus no wanted level), this HUD element will never appear.
|
||||
level = 2
|
||||
/// Boolean, have the cops arrived? If so, the icon stops changing and remains the same.
|
||||
var/cops_arrived = 0
|
||||
|
||||
/atom/movable/screen/wanted/New()
|
||||
return ..()
|
||||
|
||||
/atom/movable/screen/wanted/Initialize()
|
||||
. = ..()
|
||||
update_icon()
|
||||
|
||||
/atom/movable/screen/wanted/MouseEntered(location,control,params)
|
||||
openToolTip(usr,src,params,title = name,content = desc, theme = "alerttooltipstyle")
|
||||
|
||||
/atom/movable/screen/wanted/MouseExited()
|
||||
closeToolTip(usr)
|
||||
|
||||
/atom/movable/screen/wanted/update_icon_state()
|
||||
. = ..()
|
||||
icon_state = "wanted_[level][cops_arrived ? "_active" : ""]"
|
||||
@@ -1,192 +0,0 @@
|
||||
/mob/proc/overlay_fullscreen(category, type, severity)
|
||||
var/atom/movable/screen/fullscreen/screen = screens[category]
|
||||
if (!screen || screen.type != type)
|
||||
// needs to be recreated
|
||||
clear_fullscreen(category, FALSE)
|
||||
screens[category] = screen = new type()
|
||||
else if ((!severity || severity == screen.severity) && (!client || screen.screen_loc != "CENTER-7,CENTER-7" || screen.view == client.view))
|
||||
// doesn't need to be updated
|
||||
return screen
|
||||
|
||||
screen.icon_state = "[initial(screen.icon_state)][severity]"
|
||||
screen.severity = severity
|
||||
if (client && screen.should_show_to(src))
|
||||
screen.update_for_view(client.view)
|
||||
client.screen += screen
|
||||
|
||||
return screen
|
||||
|
||||
/mob/proc/clear_fullscreen(category, animated = 10)
|
||||
var/atom/movable/screen/fullscreen/screen = screens[category]
|
||||
if(!screen)
|
||||
return
|
||||
|
||||
screens -= category
|
||||
|
||||
if(animated)
|
||||
animate(screen, alpha = 0, time = animated)
|
||||
addtimer(CALLBACK(src, .proc/clear_fullscreen_after_animate, screen), animated, TIMER_CLIENT_TIME)
|
||||
else
|
||||
if(client)
|
||||
client.screen -= screen
|
||||
qdel(screen)
|
||||
|
||||
/mob/proc/clear_fullscreen_after_animate(atom/movable/screen/fullscreen/screen)
|
||||
if(client)
|
||||
client.screen -= screen
|
||||
qdel(screen)
|
||||
|
||||
/mob/proc/clear_fullscreens()
|
||||
for(var/category in screens)
|
||||
clear_fullscreen(category)
|
||||
|
||||
/mob/proc/hide_fullscreens()
|
||||
if(client)
|
||||
for(var/category in screens)
|
||||
client.screen -= screens[category]
|
||||
|
||||
/mob/proc/reload_fullscreen()
|
||||
if(client)
|
||||
var/atom/movable/screen/fullscreen/screen
|
||||
for(var/category in screens)
|
||||
screen = screens[category]
|
||||
if(screen.should_show_to(src))
|
||||
screen.update_for_view(client.view)
|
||||
client.screen |= screen
|
||||
else
|
||||
client.screen -= screen
|
||||
|
||||
/atom/movable/screen/fullscreen
|
||||
icon = 'icons/mob/screen_full.dmi'
|
||||
icon_state = "default"
|
||||
screen_loc = "CENTER-7,CENTER-7"
|
||||
layer = FULLSCREEN_LAYER
|
||||
plane = FULLSCREEN_PLANE
|
||||
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
||||
var/view = 7
|
||||
var/severity = 0
|
||||
var/show_when_dead = FALSE
|
||||
|
||||
/atom/movable/screen/fullscreen/proc/update_for_view(client_view)
|
||||
if (screen_loc == "CENTER-7,CENTER-7" && view != client_view)
|
||||
var/list/actualview = getviewsize(client_view)
|
||||
view = client_view
|
||||
transform = matrix(actualview[1]/FULLSCREEN_OVERLAY_RESOLUTION_X, 0, 0, 0, actualview[2]/FULLSCREEN_OVERLAY_RESOLUTION_Y, 0)
|
||||
|
||||
/atom/movable/screen/fullscreen/proc/should_show_to(mob/mymob)
|
||||
if(!show_when_dead && mymob.stat == DEAD)
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/atom/movable/screen/fullscreen/Destroy()
|
||||
severity = 0
|
||||
. = ..()
|
||||
|
||||
/atom/movable/screen/fullscreen/emergency_meeting
|
||||
icon_state = "emergency_meeting"
|
||||
show_when_dead = TRUE
|
||||
layer = CURSE_LAYER
|
||||
plane = SPLASHSCREEN_PLANE
|
||||
|
||||
/atom/movable/screen/fullscreen/brute
|
||||
icon_state = "brutedamageoverlay"
|
||||
layer = UI_DAMAGE_LAYER
|
||||
plane = FULLSCREEN_PLANE
|
||||
|
||||
/atom/movable/screen/fullscreen/oxy
|
||||
icon_state = "oxydamageoverlay"
|
||||
layer = UI_DAMAGE_LAYER
|
||||
plane = FULLSCREEN_PLANE
|
||||
|
||||
/atom/movable/screen/fullscreen/crit
|
||||
icon_state = "passage"
|
||||
layer = CRIT_LAYER
|
||||
plane = FULLSCREEN_PLANE
|
||||
|
||||
/atom/movable/screen/fullscreen/crit/vision
|
||||
icon_state = "oxydamageoverlay"
|
||||
layer = BLIND_LAYER
|
||||
|
||||
/atom/movable/screen/fullscreen/blind
|
||||
icon_state = "blackimageoverlay"
|
||||
layer = BLIND_LAYER
|
||||
plane = FULLSCREEN_PLANE
|
||||
|
||||
/atom/movable/screen/fullscreen/curse
|
||||
icon_state = "curse"
|
||||
layer = CURSE_LAYER
|
||||
plane = FULLSCREEN_PLANE
|
||||
|
||||
/atom/movable/screen/fullscreen/impaired
|
||||
icon_state = "impairedoverlay"
|
||||
|
||||
/atom/movable/screen/fullscreen/blurry
|
||||
icon = 'icons/mob/screen_gen.dmi'
|
||||
screen_loc = "WEST,SOUTH to EAST,NORTH"
|
||||
icon_state = "blurry"
|
||||
|
||||
/atom/movable/screen/fullscreen/flash
|
||||
icon = 'icons/mob/screen_gen.dmi'
|
||||
screen_loc = "WEST,SOUTH to EAST,NORTH"
|
||||
icon_state = "flash"
|
||||
|
||||
/atom/movable/screen/fullscreen/flash/static
|
||||
icon = 'icons/mob/screen_gen.dmi'
|
||||
screen_loc = "WEST,SOUTH to EAST,NORTH"
|
||||
icon_state = "noise"
|
||||
|
||||
/atom/movable/screen/fullscreen/high
|
||||
icon = 'icons/mob/screen_gen.dmi'
|
||||
screen_loc = "WEST,SOUTH to EAST,NORTH"
|
||||
icon_state = "druggy"
|
||||
|
||||
/atom/movable/screen/fullscreen/color_vision
|
||||
icon = 'icons/mob/screen_gen.dmi'
|
||||
screen_loc = "WEST,SOUTH to EAST,NORTH"
|
||||
icon_state = "flash"
|
||||
alpha = 80
|
||||
|
||||
/atom/movable/screen/fullscreen/color_vision/green
|
||||
color = "#00ff00"
|
||||
|
||||
/atom/movable/screen/fullscreen/color_vision/red
|
||||
color = "#ff0000"
|
||||
|
||||
/atom/movable/screen/fullscreen/color_vision/blue
|
||||
color = "#0000ff"
|
||||
|
||||
/atom/movable/screen/fullscreen/cinematic_backdrop
|
||||
icon = 'icons/mob/screen_gen.dmi'
|
||||
screen_loc = "WEST,SOUTH to EAST,NORTH"
|
||||
icon_state = "flash"
|
||||
plane = SPLASHSCREEN_PLANE
|
||||
layer = SPLASHSCREEN_LAYER - 1
|
||||
color = "#000000"
|
||||
show_when_dead = TRUE
|
||||
|
||||
/atom/movable/screen/fullscreen/lighting_backdrop
|
||||
icon = 'icons/mob/screen_gen.dmi'
|
||||
icon_state = "flash"
|
||||
transform = matrix(200, 0, 0, 0, 200, 0)
|
||||
plane = LIGHTING_PLANE
|
||||
blend_mode = BLEND_OVERLAY
|
||||
show_when_dead = TRUE
|
||||
|
||||
//Provides darkness to the back of the lighting plane
|
||||
/atom/movable/screen/fullscreen/lighting_backdrop/lit
|
||||
invisibility = INVISIBILITY_LIGHTING
|
||||
layer = BACKGROUND_LAYER+21
|
||||
color = "#000"
|
||||
show_when_dead = TRUE
|
||||
|
||||
//Provides whiteness in case you don't see lights so everything is still visible
|
||||
/atom/movable/screen/fullscreen/lighting_backdrop/unlit
|
||||
layer = BACKGROUND_LAYER+20
|
||||
show_when_dead = TRUE
|
||||
|
||||
/atom/movable/screen/fullscreen/see_through_darkness
|
||||
icon_state = "nightvision"
|
||||
plane = LIGHTING_PLANE
|
||||
layer = LIGHTING_LAYER
|
||||
blend_mode = BLEND_ADD
|
||||
show_when_dead = TRUE
|
||||
@@ -93,3 +93,10 @@
|
||||
screenmob.client.screen -= static_inventory
|
||||
else
|
||||
screenmob.client.screen += static_inventory
|
||||
|
||||
//We should only see observed mob alerts.
|
||||
/datum/hud/ghost/reorganize_alerts(mob/viewmob)
|
||||
var/mob/dead/observer/O = mymob
|
||||
if (istype(O) && O.observetarget)
|
||||
return
|
||||
. = ..()
|
||||
|
||||
@@ -60,6 +60,7 @@ GLOBAL_LIST_INIT(available_ui_styles, list(
|
||||
var/atom/movable/screen/healthdoll
|
||||
var/atom/movable/screen/internals
|
||||
|
||||
var/atom/movable/screen/wanted/wanted_lvl
|
||||
// subtypes can override this to force a specific UI style
|
||||
var/ui_style
|
||||
|
||||
@@ -82,6 +83,7 @@ GLOBAL_LIST_INIT(available_ui_styles, list(
|
||||
plane_masters["[instance.plane]"] = instance
|
||||
instance.backdrop(mymob)
|
||||
|
||||
|
||||
/datum/hud/Destroy()
|
||||
if(mymob.hud_used == src)
|
||||
mymob.hud_used = null
|
||||
@@ -102,6 +104,7 @@ GLOBAL_LIST_INIT(available_ui_styles, list(
|
||||
|
||||
healths = null
|
||||
healthdoll = null
|
||||
wanted_lvl = null
|
||||
internals = null
|
||||
lingchemdisplay = null
|
||||
devilsouldisplay = null
|
||||
@@ -133,7 +136,7 @@ GLOBAL_LIST_INIT(available_ui_styles, list(
|
||||
return FALSE
|
||||
|
||||
screenmob.client.screen = list()
|
||||
screenmob.client.apply_clickcatcher()
|
||||
screenmob.client.update_clickcatcher()
|
||||
|
||||
var/display_hud_version = version
|
||||
if(!display_hud_version) //If 0 or blank, display the next hud version
|
||||
@@ -193,8 +196,6 @@ GLOBAL_LIST_INIT(available_ui_styles, list(
|
||||
persistent_inventory_update(screenmob)
|
||||
screenmob.update_action_buttons(1)
|
||||
reorganize_alerts()
|
||||
screenmob.reload_fullscreen()
|
||||
update_parallax_pref(screenmob)
|
||||
|
||||
// ensure observers get an accurate and up-to-date view
|
||||
if (!viewmob)
|
||||
@@ -204,6 +205,8 @@ GLOBAL_LIST_INIT(available_ui_styles, list(
|
||||
else if (viewmob.hud_used)
|
||||
viewmob.hud_used.plane_masters_update()
|
||||
|
||||
screenmob.reload_rendering()
|
||||
|
||||
return TRUE
|
||||
|
||||
/datum/hud/proc/plane_masters_update()
|
||||
|
||||
@@ -174,7 +174,7 @@
|
||||
|
||||
/datum/hud/human/New(mob/living/carbon/human/owner)
|
||||
..()
|
||||
owner.overlay_fullscreen("see_through_darkness", /atom/movable/screen/fullscreen/see_through_darkness)
|
||||
owner.overlay_fullscreen("see_through_darkness", /atom/movable/screen/fullscreen/special/see_through_darkness)
|
||||
|
||||
var/widescreenlayout = FALSE //CIT CHANGE - adds support for different hud layouts depending on widescreen pref
|
||||
if(owner.client && owner.client.prefs && owner.client.prefs.widescreenpref) //CIT CHANGE - ditto
|
||||
|
||||
@@ -1,322 +0,0 @@
|
||||
|
||||
/datum/hud/proc/create_parallax(mob/viewmob)
|
||||
var/mob/screenmob = viewmob || mymob
|
||||
var/client/C = screenmob.client
|
||||
if (!apply_parallax_pref(viewmob)) //don't want shit computers to crash when specing someone with insane parallax, so use the viewer's pref
|
||||
return
|
||||
|
||||
if(!length(C.parallax_layers_cached))
|
||||
C.parallax_layers_cached = list()
|
||||
C.parallax_layers_cached += new /atom/movable/screen/parallax_layer/layer_1(null, C.view)
|
||||
C.parallax_layers_cached += new /atom/movable/screen/parallax_layer/layer_2(null, C.view)
|
||||
C.parallax_layers_cached += new /atom/movable/screen/parallax_layer/planet(null, C.view)
|
||||
if(SSparallax.random_layer)
|
||||
C.parallax_layers_cached += new SSparallax.random_layer
|
||||
C.parallax_layers_cached += new /atom/movable/screen/parallax_layer/layer_3(null, C.view)
|
||||
|
||||
C.parallax_layers = C.parallax_layers_cached.Copy()
|
||||
|
||||
if (length(C.parallax_layers) > C.parallax_layers_max)
|
||||
C.parallax_layers.len = C.parallax_layers_max
|
||||
|
||||
C.screen |= (C.parallax_layers)
|
||||
var/atom/movable/screen/plane_master/PM = screenmob.hud_used.plane_masters["[PLANE_SPACE]"]
|
||||
if(screenmob != mymob)
|
||||
C.screen -= locate(/atom/movable/screen/plane_master/parallax_white) in C.screen
|
||||
C.screen += PM
|
||||
PM.color = list(
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
1, 1, 1, 1,
|
||||
0, 0, 0, 0
|
||||
)
|
||||
|
||||
|
||||
/datum/hud/proc/remove_parallax(mob/viewmob)
|
||||
var/mob/screenmob = viewmob || mymob
|
||||
var/client/C = screenmob.client
|
||||
C.screen -= (C.parallax_layers_cached)
|
||||
var/atom/movable/screen/plane_master/PM = screenmob.hud_used.plane_masters["[PLANE_SPACE]"]
|
||||
if(screenmob != mymob)
|
||||
C.screen -= locate(/atom/movable/screen/plane_master/parallax_white) in C.screen
|
||||
C.screen += PM
|
||||
PM.color = initial(PM.color)
|
||||
C.parallax_layers = null
|
||||
|
||||
/datum/hud/proc/apply_parallax_pref(mob/viewmob)
|
||||
var/mob/screenmob = viewmob || mymob
|
||||
var/client/C = screenmob.client
|
||||
if(C.prefs)
|
||||
var/pref = C.prefs.parallax
|
||||
if (isnull(pref))
|
||||
pref = PARALLAX_HIGH
|
||||
switch(C.prefs.parallax)
|
||||
if (PARALLAX_INSANE)
|
||||
C.parallax_throttle = FALSE
|
||||
C.parallax_layers_max = 5
|
||||
return TRUE
|
||||
|
||||
if (PARALLAX_MED)
|
||||
C.parallax_throttle = PARALLAX_DELAY_MED
|
||||
C.parallax_layers_max = 3
|
||||
return TRUE
|
||||
|
||||
if (PARALLAX_LOW)
|
||||
C.parallax_throttle = PARALLAX_DELAY_LOW
|
||||
C.parallax_layers_max = 1
|
||||
return TRUE
|
||||
|
||||
if (PARALLAX_DISABLE)
|
||||
return FALSE
|
||||
|
||||
//This is high parallax.
|
||||
C.parallax_throttle = PARALLAX_DELAY_DEFAULT
|
||||
C.parallax_layers_max = 4
|
||||
return TRUE
|
||||
|
||||
/datum/hud/proc/update_parallax_pref(mob/viewmob)
|
||||
remove_parallax(viewmob)
|
||||
create_parallax(viewmob)
|
||||
update_parallax()
|
||||
|
||||
// This sets which way the current shuttle is moving (returns true if the shuttle has stopped moving so the caller can append their animation)
|
||||
/datum/hud/proc/set_parallax_movedir(new_parallax_movedir, skip_windups)
|
||||
. = FALSE
|
||||
var/client/C = mymob.client
|
||||
if(new_parallax_movedir == C.parallax_movedir)
|
||||
return
|
||||
var/animatedir = new_parallax_movedir
|
||||
if(new_parallax_movedir == FALSE)
|
||||
var/animate_time = 0
|
||||
for(var/thing in C.parallax_layers)
|
||||
var/atom/movable/screen/parallax_layer/L = thing
|
||||
L.icon_state = initial(L.icon_state)
|
||||
L.update_o(C.view)
|
||||
var/T = PARALLAX_LOOP_TIME / L.speed
|
||||
if (T > animate_time)
|
||||
animate_time = T
|
||||
C.dont_animate_parallax = world.time + min(animate_time, PARALLAX_LOOP_TIME)
|
||||
animatedir = C.parallax_movedir
|
||||
|
||||
var/matrix/newtransform
|
||||
switch(animatedir)
|
||||
if(NORTH)
|
||||
newtransform = matrix(1, 0, 0, 0, 1, 480)
|
||||
if(SOUTH)
|
||||
newtransform = matrix(1, 0, 0, 0, 1,-480)
|
||||
if(EAST)
|
||||
newtransform = matrix(1, 0, 480, 0, 1, 0)
|
||||
if(WEST)
|
||||
newtransform = matrix(1, 0,-480, 0, 1, 0)
|
||||
|
||||
var/shortesttimer
|
||||
if(!skip_windups)
|
||||
for(var/thing in C.parallax_layers)
|
||||
var/atom/movable/screen/parallax_layer/L = thing
|
||||
|
||||
var/T = PARALLAX_LOOP_TIME / L.speed
|
||||
if (isnull(shortesttimer))
|
||||
shortesttimer = T
|
||||
if (T < shortesttimer)
|
||||
shortesttimer = T
|
||||
L.transform = newtransform
|
||||
animate(L, transform = matrix(), time = T, easing = QUAD_EASING | (new_parallax_movedir ? EASE_IN : EASE_OUT), flags = ANIMATION_END_NOW)
|
||||
if (new_parallax_movedir)
|
||||
L.transform = newtransform
|
||||
animate(transform = matrix(), time = T) //queue up another animate so lag doesn't create a shutter
|
||||
|
||||
C.parallax_movedir = new_parallax_movedir
|
||||
if (C.parallax_animate_timer)
|
||||
deltimer(C.parallax_animate_timer)
|
||||
var/datum/callback/CB = CALLBACK(src, .proc/update_parallax_motionblur, C, animatedir, new_parallax_movedir, newtransform)
|
||||
if(skip_windups)
|
||||
CB.InvokeAsync()
|
||||
else
|
||||
C.parallax_animate_timer = addtimer(CB, min(shortesttimer, PARALLAX_LOOP_TIME), TIMER_CLIENT_TIME|TIMER_STOPPABLE)
|
||||
|
||||
|
||||
/datum/hud/proc/update_parallax_motionblur(client/C, animatedir, new_parallax_movedir, matrix/newtransform)
|
||||
if(!C)
|
||||
return
|
||||
C.parallax_animate_timer = FALSE
|
||||
for(var/thing in C.parallax_layers)
|
||||
var/atom/movable/screen/parallax_layer/L = thing
|
||||
if (!new_parallax_movedir)
|
||||
animate(L)
|
||||
continue
|
||||
|
||||
var/newstate = initial(L.icon_state)
|
||||
var/T = PARALLAX_LOOP_TIME / L.speed
|
||||
|
||||
if (newstate in icon_states(L.icon))
|
||||
L.icon_state = newstate
|
||||
L.update_o(C.view)
|
||||
|
||||
L.transform = newtransform
|
||||
|
||||
animate(L, transform = matrix(), time = T, loop = -1, flags = ANIMATION_END_NOW)
|
||||
|
||||
/datum/hud/proc/update_parallax()
|
||||
var/client/C = mymob.client
|
||||
var/turf/posobj = get_turf(C.eye)
|
||||
if(!posobj)
|
||||
return
|
||||
var/area/areaobj = posobj.loc
|
||||
|
||||
// Update the movement direction of the parallax if necessary (for shuttles)
|
||||
set_parallax_movedir(areaobj.parallax_movedir, FALSE)
|
||||
|
||||
var/force
|
||||
if(!C.previous_turf || (C.previous_turf.z != posobj.z))
|
||||
C.previous_turf = posobj
|
||||
force = TRUE
|
||||
|
||||
if (!force && world.time < C.last_parallax_shift+C.parallax_throttle)
|
||||
return
|
||||
|
||||
//Doing it this way prevents parallax layers from "jumping" when you change Z-Levels.
|
||||
var/offset_x = posobj.x - C.previous_turf.x
|
||||
var/offset_y = posobj.y - C.previous_turf.y
|
||||
|
||||
if(!offset_x && !offset_y && !force)
|
||||
return
|
||||
|
||||
var/last_delay = world.time - C.last_parallax_shift
|
||||
last_delay = min(last_delay, C.parallax_throttle)
|
||||
C.previous_turf = posobj
|
||||
C.last_parallax_shift = world.time
|
||||
|
||||
for(var/thing in C.parallax_layers)
|
||||
var/atom/movable/screen/parallax_layer/L = thing
|
||||
L.update_status(mymob)
|
||||
if (L.view_sized != C.view)
|
||||
L.update_o(C.view)
|
||||
|
||||
var/change_x
|
||||
var/change_y
|
||||
|
||||
if(L.absolute)
|
||||
L.offset_x = -(posobj.x - SSparallax.planet_x_offset) * L.speed
|
||||
L.offset_y = -(posobj.y - SSparallax.planet_y_offset) * L.speed
|
||||
else
|
||||
change_x = offset_x * L.speed
|
||||
L.offset_x -= change_x
|
||||
change_y = offset_y * L.speed
|
||||
L.offset_y -= change_y
|
||||
|
||||
if(L.offset_x > 240)
|
||||
L.offset_x -= 480
|
||||
if(L.offset_x < -240)
|
||||
L.offset_x += 480
|
||||
if(L.offset_y > 240)
|
||||
L.offset_y -= 480
|
||||
if(L.offset_y < -240)
|
||||
L.offset_y += 480
|
||||
|
||||
|
||||
if(!areaobj.parallax_movedir && C.dont_animate_parallax <= world.time && (offset_x || offset_y) && abs(offset_x) <= max(C.parallax_throttle/world.tick_lag+1,1) && abs(offset_y) <= max(C.parallax_throttle/world.tick_lag+1,1) && (round(abs(change_x)) > 1 || round(abs(change_y)) > 1))
|
||||
L.transform = matrix(1, 0, offset_x*L.speed, 0, 1, offset_y*L.speed)
|
||||
animate(L, transform=matrix(), time = last_delay)
|
||||
|
||||
L.screen_loc = "CENTER-7:[round(L.offset_x,1)],CENTER-7:[round(L.offset_y,1)]"
|
||||
|
||||
/atom/movable/proc/update_parallax_contents()
|
||||
if(length(client_mobs_in_contents))
|
||||
for(var/thing in client_mobs_in_contents)
|
||||
var/mob/M = thing
|
||||
if(M?.client && M.hud_used && length(M.client.parallax_layers))
|
||||
M.hud_used.update_parallax()
|
||||
|
||||
/mob/proc/update_parallax_teleport() //used for arrivals shuttle
|
||||
if(client?.eye && hud_used && length(client.parallax_layers))
|
||||
var/area/areaobj = get_area(client.eye)
|
||||
hud_used.set_parallax_movedir(areaobj.parallax_movedir, TRUE)
|
||||
|
||||
/atom/movable/screen/parallax_layer
|
||||
icon = 'icons/effects/parallax.dmi'
|
||||
var/speed = 1
|
||||
var/offset_x = 0
|
||||
var/offset_y = 0
|
||||
var/view_sized
|
||||
var/absolute = FALSE
|
||||
blend_mode = BLEND_ADD
|
||||
plane = PLANE_SPACE_PARALLAX
|
||||
screen_loc = "CENTER-7,CENTER-7"
|
||||
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
||||
|
||||
|
||||
/atom/movable/screen/parallax_layer/Initialize(mapload, view)
|
||||
. = ..()
|
||||
if (!view)
|
||||
view = world.view
|
||||
update_o(view)
|
||||
|
||||
/atom/movable/screen/parallax_layer/proc/update_o(view)
|
||||
if (!view)
|
||||
view = world.view
|
||||
|
||||
var/list/viewscales = getviewsize(view)
|
||||
var/countx = CEILING((viewscales[1]/2)/(480/world.icon_size), 1)+1
|
||||
var/county = CEILING((viewscales[2]/2)/(480/world.icon_size), 1)+1
|
||||
var/list/new_overlays = new
|
||||
for(var/x in -countx to countx)
|
||||
for(var/y in -county to county)
|
||||
if(x == 0 && y == 0)
|
||||
continue
|
||||
var/mutable_appearance/texture_overlay = mutable_appearance(icon, icon_state)
|
||||
texture_overlay.transform = matrix(1, 0, x*480, 0, 1, y*480)
|
||||
new_overlays += texture_overlay
|
||||
cut_overlays()
|
||||
add_overlay(new_overlays)
|
||||
view_sized = view
|
||||
|
||||
/atom/movable/screen/parallax_layer/proc/update_status(mob/M)
|
||||
return
|
||||
|
||||
/atom/movable/screen/parallax_layer/layer_1
|
||||
icon_state = "layer1"
|
||||
speed = 0.6
|
||||
layer = 1
|
||||
|
||||
/atom/movable/screen/parallax_layer/layer_2
|
||||
icon_state = "layer2"
|
||||
speed = 1
|
||||
layer = 2
|
||||
|
||||
/atom/movable/screen/parallax_layer/layer_3
|
||||
icon_state = "layer3"
|
||||
speed = 1.4
|
||||
layer = 3
|
||||
|
||||
/atom/movable/screen/parallax_layer/random
|
||||
blend_mode = BLEND_OVERLAY
|
||||
speed = 3
|
||||
layer = 3
|
||||
|
||||
/atom/movable/screen/parallax_layer/random/space_gas
|
||||
icon_state = "space_gas"
|
||||
|
||||
/atom/movable/screen/parallax_layer/random/space_gas/Initialize(mapload, view)
|
||||
. = ..()
|
||||
src.add_atom_colour(SSparallax.random_parallax_color, ADMIN_COLOUR_PRIORITY)
|
||||
|
||||
/atom/movable/screen/parallax_layer/random/asteroids
|
||||
icon_state = "asteroids"
|
||||
|
||||
/atom/movable/screen/parallax_layer/planet
|
||||
icon_state = "planet"
|
||||
blend_mode = BLEND_OVERLAY
|
||||
absolute = TRUE //Status of seperation
|
||||
speed = 3
|
||||
layer = 30
|
||||
|
||||
/atom/movable/screen/parallax_layer/planet/update_status(mob/M)
|
||||
var/client/C = M.client
|
||||
var/turf/posobj = get_turf(C.eye)
|
||||
if(!posobj)
|
||||
return
|
||||
invisibility = is_station_level(posobj.z) ? 0 : INVISIBILITY_ABSTRACT
|
||||
|
||||
/atom/movable/screen/parallax_layer/planet/update_o()
|
||||
return //Shit won't move
|
||||
@@ -124,8 +124,8 @@
|
||||
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
||||
|
||||
/atom/movable/screen/plane_master/lighting/backdrop(mob/mymob)
|
||||
mymob.overlay_fullscreen("lighting_backdrop_lit", /atom/movable/screen/fullscreen/lighting_backdrop/lit)
|
||||
mymob.overlay_fullscreen("lighting_backdrop_unlit", /atom/movable/screen/fullscreen/lighting_backdrop/unlit)
|
||||
mymob.overlay_fullscreen("lighting_backdrop_lit", /atom/movable/screen/fullscreen/special/lighting_backdrop/lit)
|
||||
mymob.overlay_fullscreen("lighting_backdrop_unlit", /atom/movable/screen/fullscreen/special/lighting_backdrop/unlit)
|
||||
|
||||
/*!
|
||||
* This system works by exploiting BYONDs color matrix filter to use layers to handle emissive blockers.
|
||||
@@ -143,7 +143,6 @@
|
||||
add_filter("emissives", 1, alpha_mask_filter(render_source = EMISSIVE_RENDER_TARGET, flags = MASK_INVERSE))
|
||||
add_filter("object_lighting", 2, alpha_mask_filter(render_source = O_LIGHTING_VISUAL_RENDER_TARGET, flags = MASK_INVERSE))
|
||||
|
||||
|
||||
/**
|
||||
* Handles emissive overlays and emissive blockers.
|
||||
*/
|
||||
@@ -163,9 +162,10 @@
|
||||
plane = PLANE_SPACE_PARALLAX
|
||||
blend_mode = BLEND_MULTIPLY
|
||||
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
||||
render_target = PLANE_SPACE_PARALLAX_RENDER_TARGET
|
||||
|
||||
/atom/movable/screen/plane_master/parallax_white
|
||||
name = "parallax whitifier plane master"
|
||||
name = "parallax backdrop/space turf plane master"
|
||||
plane = PLANE_SPACE
|
||||
|
||||
/atom/movable/screen/plane_master/camera_static
|
||||
@@ -174,7 +174,6 @@
|
||||
appearance_flags = PLANE_MASTER
|
||||
blend_mode = BLEND_OVERLAY
|
||||
|
||||
|
||||
//Reserved to chat messages, so they are still displayed above the field of vision masking.
|
||||
/atom/movable/screen/plane_master/chat_messages
|
||||
name = "runechat plane master"
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
// Otherwise jump
|
||||
else if(A.loc)
|
||||
forceMove(get_turf(A))
|
||||
update_parallax_contents()
|
||||
|
||||
/mob/dead/observer/ClickOn(var/atom/A, var/params)
|
||||
if(check_click_intercept(params,A))
|
||||
|
||||
@@ -124,9 +124,3 @@ GLOBAL_LIST_INIT(huds, list(
|
||||
|
||||
/mob/dead/new_player/reload_huds()
|
||||
return
|
||||
|
||||
/mob/proc/add_click_catcher()
|
||||
client.screen += client.void
|
||||
|
||||
/mob/dead/new_player/add_click_catcher()
|
||||
return
|
||||
74
code/_rendering/clickcatcher/clickcatcher.dm
Normal file
74
code/_rendering/clickcatcher/clickcatcher.dm
Normal file
@@ -0,0 +1,74 @@
|
||||
/atom/movable/screen/click_catcher
|
||||
icon = 'icons/screen/clickcatcher.dmi'
|
||||
icon_state = "catcher"
|
||||
appearance_flags = TILE_BOUND | NO_CLIENT_COLOR | RESET_TRANSFORM | RESET_COLOR | RESET_ALPHA
|
||||
plane = CLICKCATCHER_PLANE
|
||||
plane = CLICKCATCHER_PLANE
|
||||
mouse_opacity = MOUSE_OPACITY_OPAQUE
|
||||
screen_loc = "CENTER"
|
||||
|
||||
/*
|
||||
#define MAX_SAFE_BYOND_ICON_SCALE_TILES (MAX_SAFE_BYOND_ICON_SCALE_PX / world.icon_size)
|
||||
#define MAX_SAFE_BYOND_ICON_SCALE_PX (33 * 32) //Not using world.icon_size on purpose.
|
||||
|
||||
/atom/movable/screen/click_catcher/proc/UpdateFill(view_size_x = 15, view_size_y = 15)
|
||||
var/icon/newicon = icon('icons/mob/screen_gen.dmi', "catcher")
|
||||
var/ox = min(MAX_SAFE_BYOND_ICON_SCALE_TILES, view_size_x)
|
||||
var/oy = min(MAX_SAFE_BYOND_ICON_SCALE_TILES, view_size_y)
|
||||
var/px = view_size_x * world.icon_size
|
||||
var/py = view_size_y * world.icon_size
|
||||
var/sx = min(MAX_SAFE_BYOND_ICON_SCALE_PX, px)
|
||||
var/sy = min(MAX_SAFE_BYOND_ICON_SCALE_PX, py)
|
||||
newicon.Scale(sx, sy)
|
||||
icon = newicon
|
||||
screen_loc = "CENTER-[(ox-1)*0.5],CENTER-[(oy-1)*0.5]"
|
||||
var/matrix/M = new
|
||||
M.Scale(px/sx, py/sy)
|
||||
transform = M
|
||||
|
||||
#undef MAX_SAFE_BYOND_ICON_SCALE_TILES
|
||||
#undef MAX_SAFE_BYOND_ICON_SCALE_PX
|
||||
*/
|
||||
|
||||
/atom/movable/screen/click_catcher/proc/UpdateFill(view_size_x, view_size_y)
|
||||
screen_loc = "1,1 to [view_size_x],[view_size_y]"
|
||||
|
||||
/atom/movable/screen/click_catcher/Click(location, control, params)
|
||||
var/list/modifiers = params2list(params)
|
||||
if(modifiers["middle"] && iscarbon(usr))
|
||||
var/mob/living/carbon/C = usr
|
||||
C.swap_hand()
|
||||
else
|
||||
var/turf/T = Parse(modifiers["screen-loc"], get_turf(usr.client?.eye || usr), usr.client)
|
||||
params += "&catcher=1"
|
||||
if(T)
|
||||
T.Click(location, control, params)
|
||||
return TRUE
|
||||
|
||||
/atom/movable/screen/click_catcher/proc/Parse(scr_loc, turf/origin, client/C)
|
||||
// screen-loc: Pixel coordinates in screen_loc format ("[tile_x]:[pixel_x],[tile_y]:[pixel_y]")
|
||||
if(!scr_loc)
|
||||
return null
|
||||
var/tX = splittext(scr_loc, ",")
|
||||
var/tY = splittext(tX[2], ":")
|
||||
var/tZ = origin.z
|
||||
tY = tY[1]
|
||||
tX = splittext(tX[1], ":")
|
||||
tX = tX[1]
|
||||
var/list/actual_view = getviewsize(C ? C.view : world.view)
|
||||
tX = clamp(origin.x + text2num(tX) - round(actual_view[1] / 2) - 1, 1, world.maxx)
|
||||
tY = clamp(origin.y + text2num(tY) - round(actual_view[2] / 2) - 1, 1, world.maxy)
|
||||
return locate(tX, tY, tZ)
|
||||
|
||||
/**
|
||||
* Makes a clickcatcher if necessary, and ensures it's fit to our size.
|
||||
*/
|
||||
/client/proc/update_clickcatcher(list/view_override)
|
||||
if(!click_catcher)
|
||||
click_catcher = new
|
||||
screen |= click_catcher
|
||||
if(view_override)
|
||||
click_catcher.UpdateFill(view_override[1], view_override[2])
|
||||
else
|
||||
var/list/view_list = getviewsize(view)
|
||||
click_catcher.UpdateFill(view_list[1], view_list[2])
|
||||
234
code/_rendering/fullscreen/fullscreen.dm
Normal file
234
code/_rendering/fullscreen/fullscreen.dm
Normal file
@@ -0,0 +1,234 @@
|
||||
/**
|
||||
* Adds a fullscreen overlay
|
||||
*
|
||||
* @params
|
||||
* - category - string - must exist. will overwrite any other screen in this category. defaults to type.
|
||||
* - type - the typepath of the screen
|
||||
* - severity - severity - different screen objects have differing severities
|
||||
*/
|
||||
/mob/proc/overlay_fullscreen(category, type, severity)
|
||||
ASSERT(type)
|
||||
if(!category)
|
||||
category = type
|
||||
var/atom/movable/screen/fullscreen/screen = fullscreens[category]
|
||||
if (!screen || screen.type != type)
|
||||
// needs to be recreated
|
||||
clear_fullscreen(category, 0)
|
||||
fullscreens[category] = screen = new type
|
||||
screen.SetSeverity(severity)
|
||||
if(client && screen.ShouldShow(src))
|
||||
screen.SetView(client.view)
|
||||
client.screen += screen
|
||||
return screen
|
||||
|
||||
/**
|
||||
* Wipes a fullscreen of a certain category
|
||||
*
|
||||
* Second argument is for animation delay.
|
||||
*/
|
||||
/mob/proc/clear_fullscreen(category, animated = 10)
|
||||
if(!fullscreens)
|
||||
return
|
||||
var/atom/movable/screen/fullscreen/screen = fullscreens[category]
|
||||
fullscreens -= category
|
||||
if(!screen)
|
||||
return
|
||||
if(animated > 0)
|
||||
animate(screen, alpha = 0, time = animated)
|
||||
addtimer(CALLBACK(src, .proc/_remove_fullscreen_direct, screen), animated, TIMER_CLIENT_TIME)
|
||||
else
|
||||
if(client)
|
||||
client.screen -= screen
|
||||
qdel(screen)
|
||||
|
||||
/mob/proc/_remove_fullscreen_direct(atom/movable/screen/fullscreen/screen)
|
||||
if(client)
|
||||
client.screen -= screen
|
||||
qdel(screen)
|
||||
|
||||
/**
|
||||
* Wipes all fullscreens
|
||||
*/
|
||||
/mob/proc/wipe_fullscreens()
|
||||
for(var/category in fullscreens)
|
||||
clear_fullscreen(category)
|
||||
|
||||
/**
|
||||
* Removes fullscreens from client but not the mob
|
||||
*/
|
||||
/mob/proc/hide_fullscreens()
|
||||
if(client)
|
||||
for(var/category in fullscreens)
|
||||
client.screen -= fullscreens[category]
|
||||
|
||||
/**
|
||||
* Ensures all fullscreens are on client.
|
||||
*/
|
||||
/mob/proc/reload_fullscreen()
|
||||
if(client)
|
||||
var/atom/movable/screen/fullscreen/screen
|
||||
for(var/category in fullscreens)
|
||||
screen = fullscreens[category]
|
||||
if(screen.ShouldShow(src))
|
||||
screen.SetView(client.view)
|
||||
client.screen |= screen
|
||||
else
|
||||
client.screen -= screen
|
||||
|
||||
/atom/movable/screen/fullscreen
|
||||
icon = 'icons/screen/fullscreen_15x15.dmi'
|
||||
icon_state = "default"
|
||||
screen_loc = "CENTER-7,CENTER-7"
|
||||
layer = FULLSCREEN_LAYER
|
||||
plane = FULLSCREEN_PLANE
|
||||
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
||||
/// current view we're adapted to
|
||||
var/view_current
|
||||
/// min severity
|
||||
var/severity_min = 0
|
||||
/// max severity
|
||||
var/severity_max = INFINITY
|
||||
/// current severity
|
||||
var/severity = 0
|
||||
/// show this while dead
|
||||
var/show_when_dead = FALSE
|
||||
|
||||
/atom/movable/screen/fullscreen/proc/SetSeverity(severity)
|
||||
src.severity = clamp(severity, severity_min, severity_max)
|
||||
icon_state = "[initial(icon_state)][severity]"
|
||||
|
||||
/atom/movable/screen/fullscreen/proc/SetView(client_view)
|
||||
view_current = client_view
|
||||
|
||||
/atom/movable/screen/fullscreen/proc/ShouldShow(mob/M)
|
||||
if(!show_when_dead && M.stat == DEAD)
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/atom/movable/screen/fullscreen/Destroy()
|
||||
SetSeverity(0)
|
||||
return ..()
|
||||
|
||||
/atom/movable/screen/fullscreen/scaled
|
||||
icon = 'icons/screen/fullscreen_15x15.dmi'
|
||||
screen_loc = "CENTER-7,CENTER-7"
|
||||
/// size of sprite in tiles
|
||||
var/size_x = 15
|
||||
/// size of sprite in tiles
|
||||
var/size_y = 15
|
||||
|
||||
/atom/movable/screen/fullscreen/scaled/SetView(client_view)
|
||||
if(view_current != client_view)
|
||||
var/list/actualview = getviewsize(client_view)
|
||||
view_current = client_view
|
||||
transform = matrix(actualview[1] / size_x, 0, 0, 0, actualview[2] / size_y, 0)
|
||||
return ..()
|
||||
|
||||
/atom/movable/screen/fullscreen/scaled/brute
|
||||
icon_state = "brutedamageoverlay"
|
||||
layer = UI_DAMAGE_LAYER
|
||||
plane = FULLSCREEN_PLANE
|
||||
|
||||
/atom/movable/screen/fullscreen/scaled/oxy
|
||||
icon_state = "oxydamageoverlay"
|
||||
layer = UI_DAMAGE_LAYER
|
||||
plane = FULLSCREEN_PLANE
|
||||
|
||||
/atom/movable/screen/fullscreen/scaled/crit
|
||||
icon_state = "passage"
|
||||
layer = CRIT_LAYER
|
||||
plane = FULLSCREEN_PLANE
|
||||
|
||||
/atom/movable/screen/fullscreen/scaled/crit/vision
|
||||
icon_state = "oxydamageoverlay"
|
||||
layer = BLIND_LAYER
|
||||
|
||||
/atom/movable/screen/fullscreen/scaled/blind
|
||||
icon_state = "blackimageoverlay"
|
||||
layer = BLIND_LAYER
|
||||
plane = FULLSCREEN_PLANE
|
||||
|
||||
/atom/movable/screen/fullscreen/scaled/curse
|
||||
icon_state = "curse"
|
||||
layer = CURSE_LAYER
|
||||
plane = FULLSCREEN_PLANE
|
||||
|
||||
/atom/movable/screen/fullscreen/scaled/impaired
|
||||
icon_state = "impairedoverlay"
|
||||
|
||||
/atom/movable/screen/fullscreen/scaled/emergency_meeting
|
||||
icon_state = "emergency_meeting"
|
||||
show_when_dead = TRUE
|
||||
layer = CURSE_LAYER
|
||||
plane = SPLASHSCREEN_PLANE
|
||||
|
||||
/atom/movable/screen/fullscreen/tiled/blurry
|
||||
icon = 'icons/mob/screen_gen.dmi'
|
||||
screen_loc = "WEST,SOUTH to EAST,NORTH"
|
||||
icon_state = "cloudy"
|
||||
|
||||
/atom/movable/screen/fullscreen/tiled/flash
|
||||
icon = 'icons/mob/screen_gen.dmi'
|
||||
screen_loc = "WEST,SOUTH to EAST,NORTH"
|
||||
icon_state = "flash"
|
||||
|
||||
/atom/movable/screen/fullscreen/tiled/flash/static
|
||||
icon = 'icons/mob/screen_gen.dmi'
|
||||
screen_loc = "WEST,SOUTH to EAST,NORTH"
|
||||
icon_state = "noise"
|
||||
|
||||
/atom/movable/screen/fullscreen/tiled/high
|
||||
icon = 'icons/mob/screen_gen.dmi'
|
||||
screen_loc = "WEST,SOUTH to EAST,NORTH"
|
||||
icon_state = "druggy"
|
||||
|
||||
/atom/movable/screen/fullscreen/tiled/color_vision
|
||||
icon = 'icons/mob/screen_gen.dmi'
|
||||
screen_loc = "WEST,SOUTH to EAST,NORTH"
|
||||
icon_state = "flash"
|
||||
alpha = 80
|
||||
|
||||
/atom/movable/screen/fullscreen/tiled/color_vision/green
|
||||
color = "#00ff00"
|
||||
|
||||
/atom/movable/screen/fullscreen/tiled/color_vision/red
|
||||
color = "#ff0000"
|
||||
|
||||
/atom/movable/screen/fullscreen/tiled/color_vision/blue
|
||||
color = "#0000ff"
|
||||
|
||||
/atom/movable/screen/fullscreen/tiled/cinematic_backdrop
|
||||
icon = 'icons/mob/screen_gen.dmi'
|
||||
screen_loc = "WEST,SOUTH to EAST,NORTH"
|
||||
icon_state = "flash"
|
||||
plane = SPLASHSCREEN_PLANE
|
||||
layer = SPLASHSCREEN_LAYER - 1
|
||||
color = "#000000"
|
||||
show_when_dead = TRUE
|
||||
|
||||
/atom/movable/screen/fullscreen/special/lighting_backdrop
|
||||
icon = 'icons/mob/screen_gen.dmi'
|
||||
icon_state = "flash"
|
||||
transform = matrix(200, 0, 0, 0, 200, 0)
|
||||
plane = LIGHTING_PLANE
|
||||
blend_mode = BLEND_OVERLAY
|
||||
show_when_dead = TRUE
|
||||
|
||||
//Provides darkness to the back of the lighting plane
|
||||
/atom/movable/screen/fullscreen/special/lighting_backdrop/lit
|
||||
invisibility = INVISIBILITY_LIGHTING
|
||||
layer = BACKGROUND_LAYER+21
|
||||
color = "#000"
|
||||
show_when_dead = TRUE
|
||||
|
||||
//Provides whiteness in case you don't see lights so everything is still visible
|
||||
/atom/movable/screen/fullscreen/special/lighting_backdrop/unlit
|
||||
layer = BACKGROUND_LAYER+20
|
||||
show_when_dead = TRUE
|
||||
|
||||
/atom/movable/screen/fullscreen/special/see_through_darkness
|
||||
icon_state = "nightvision"
|
||||
plane = LIGHTING_PLANE
|
||||
layer = LIGHTING_LAYER
|
||||
blend_mode = BLEND_ADD
|
||||
show_when_dead = TRUE
|
||||
21
code/_rendering/mob.dm
Normal file
21
code/_rendering/mob.dm
Normal file
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* initializes screen rendering. call on mob new
|
||||
*/
|
||||
/mob/proc/init_rendering()
|
||||
|
||||
/**
|
||||
* loads screen rendering. call on mob login
|
||||
*/
|
||||
/mob/proc/reload_rendering()
|
||||
if(!client.parallax_holder)
|
||||
client.CreateParallax()
|
||||
else
|
||||
client.parallax_holder.Reset(force = TRUE)
|
||||
client.update_clickcatcher()
|
||||
reload_fullscreen()
|
||||
|
||||
/**
|
||||
* destroys screen rendering. call on mob del
|
||||
*/
|
||||
/mob/proc/dispose_rendering()
|
||||
wipe_fullscreens()
|
||||
28
code/_rendering/parallax/parallax.dm
Normal file
28
code/_rendering/parallax/parallax.dm
Normal file
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Holds parallax information.
|
||||
*/
|
||||
/datum/parallax
|
||||
/// List of parallax objects - these are cloned to a parallax holder using Clone on each.
|
||||
var/list/atom/movable/screen/parallax_layer/objects
|
||||
/// Parallax layers
|
||||
var/layers = 0
|
||||
|
||||
/datum/parallax/New(create_objects = TRUE)
|
||||
if(create_objects)
|
||||
objects = CreateObjects()
|
||||
layers = objects.len
|
||||
|
||||
/datum/parallax/Destroy()
|
||||
QDEL_LIST(objects)
|
||||
return ..()
|
||||
|
||||
/**
|
||||
* Gets a new version of the objects inside - used when applying to a holder.
|
||||
*/
|
||||
/datum/parallax/proc/GetObjects()
|
||||
. = list()
|
||||
for(var/atom/movable/screen/parallax_layer/layer in objects)
|
||||
. += layer.Clone()
|
||||
|
||||
/datum/parallax/proc/CreateObjects()
|
||||
. = objects = list()
|
||||
311
code/_rendering/parallax/parallax_holder.dm
Normal file
311
code/_rendering/parallax/parallax_holder.dm
Normal file
@@ -0,0 +1,311 @@
|
||||
/**
|
||||
* # Parallax holders
|
||||
*
|
||||
* Holds all the information about a client's parallax
|
||||
*
|
||||
* Not on mob because parallax is area based, not mob based.
|
||||
*
|
||||
* How parallax works:
|
||||
* - Layers - normal layers, scroll with movement to relative position, can scroll
|
||||
* - Absolute - absolute layers, scroll with movement to absolute position, cannot scroll
|
||||
* - Vis - vis_contents-like model - things in this are directly applied and get no processing whatsoever. Things like overmap ships can use this.
|
||||
*/
|
||||
/datum/parallax_holder
|
||||
/// Client that owns us
|
||||
var/client/owner
|
||||
/// The parallax object we're currently rendering
|
||||
var/datum/parallax/parallax
|
||||
/// Eye we were last anchored to - used to detect eye changes
|
||||
var/atom/cached_eye
|
||||
/// force this eye as the "real" eye - useful for secondary maps
|
||||
var/atom/forced_eye
|
||||
/// last turf loc
|
||||
var/turf/last
|
||||
/// last area - for parallax scrolling/loop animations
|
||||
var/area/last_area
|
||||
/// Holder object for vis
|
||||
var/atom/movable/screen/parallax_vis/vis_holder
|
||||
/// are we not on the main map? if so, put map id here
|
||||
var/secondary_map
|
||||
/// all layers
|
||||
var/list/atom/movable/screen/parallax_layer/layers
|
||||
/// vis contents
|
||||
var/list/atom/movable/vis
|
||||
/// currently scrolling?
|
||||
var/scrolling = FALSE
|
||||
/// current scroll speed in DS per scroll
|
||||
var/scroll_speed
|
||||
/// current scroll turn - applied after angle. if angle is 0 (picture moving north) and turn is 90, it would be like if you turned your viewport 90 deg clockwise.
|
||||
var/scroll_turn
|
||||
/// override planemaster we manipulate for turning and other effects
|
||||
var/atom/movable/screen/plane_master/parallax/planemaster_override
|
||||
|
||||
/datum/parallax_holder/New(client/C, secondary_map, forced_eye, planemaster_override)
|
||||
owner = C
|
||||
if(!owner)
|
||||
CRASH("No client")
|
||||
src.secondary_map = secondary_map
|
||||
src.forced_eye = forced_eye
|
||||
src.planemaster_override = planemaster_override
|
||||
Reset()
|
||||
|
||||
/datum/parallax_holder/Destroy()
|
||||
if(owner)
|
||||
if(owner.parallax_holder == src)
|
||||
owner.parallax_holder = null
|
||||
Remove()
|
||||
HardResetAnimations()
|
||||
QDEL_NULL(vis_holder)
|
||||
QDEL_NULL(parallax)
|
||||
layers = null
|
||||
vis = null
|
||||
last = null
|
||||
forced_eye = cached_eye = null
|
||||
owner = null
|
||||
return ..()
|
||||
|
||||
/datum/parallax_holder/proc/Reset(auto_z_change, force)
|
||||
if(!(cached_eye = Eye()))
|
||||
// if no eye, tear down
|
||||
last = cached_eye = last_area = null
|
||||
SetParallax(null, null, auto_z_change)
|
||||
return
|
||||
// first, check loc
|
||||
var/turf/T = get_turf(cached_eye)
|
||||
if(!T)
|
||||
// if in nullspace, tear down
|
||||
last = cached_eye = last_area = null
|
||||
SetParallax(null, null, auto_z_change)
|
||||
return
|
||||
// set last loc and eye
|
||||
last = T
|
||||
last_area = T.loc
|
||||
// rebuild parallax
|
||||
SetParallax(SSparallax.get_parallax_datum(T.z), null, auto_z_change, force)
|
||||
// hard reset positions to correct positions
|
||||
for(var/atom/movable/screen/parallax_layer/L in layers)
|
||||
L.ResetPosition(T.x, T.y)
|
||||
|
||||
// better updates via client_mobs_in_contents can be created again when important recursive contents is ported!
|
||||
/datum/parallax_holder/proc/Update(full)
|
||||
if(!full && !cached_eye || (get_turf(cached_eye) == last))
|
||||
return
|
||||
if(!owner) // why are we here
|
||||
if(!QDELETED(src))
|
||||
qdel(src)
|
||||
return
|
||||
if(cached_eye != Eye())
|
||||
// eye mismatch, reset
|
||||
Reset()
|
||||
return
|
||||
var/turf/T = get_turf(cached_eye)
|
||||
if(!last || T.z != last.z)
|
||||
// z mismatch, reset
|
||||
Reset()
|
||||
return
|
||||
// get rel offsets
|
||||
var/rel_x = T.x - last.x
|
||||
var/rel_y = T.y - last.y
|
||||
// set last
|
||||
last = T
|
||||
// move
|
||||
for(var/atom/movable/screen/parallax_layer/L in layers)
|
||||
L.RelativePosition(T.x, T.y, rel_x, rel_y)
|
||||
// process scrolling/movedir
|
||||
if(last_area != T.loc)
|
||||
last_area = T.loc
|
||||
UpdateMotion()
|
||||
|
||||
/**
|
||||
* Gets the eye we should be centered on
|
||||
*/
|
||||
/datum/parallax_holder/proc/Eye()
|
||||
return forced_eye || owner?.eye
|
||||
|
||||
/**
|
||||
* Gets the base parallax planemaster for things like turning
|
||||
*/
|
||||
/datum/parallax_holder/proc/GetPlaneMaster()
|
||||
return planemaster_override || (owner && (locate(/atom/movable/screen/plane_master/parallax) in owner?.screen))
|
||||
|
||||
/**
|
||||
* Syncs us to our parallax objects. Does NOT check if we should have those objects, that's Reset()'s job.
|
||||
*
|
||||
* Doesn't move/update positions/screen locs either.
|
||||
*
|
||||
* Also ensures movedirs are correct for the eye's pos.
|
||||
*/
|
||||
/datum/parallax_holder/proc/Sync(auto_z_change, force)
|
||||
layers = list()
|
||||
for(var/atom/movable/screen/parallax_layer/L in parallax.objects)
|
||||
layers += L
|
||||
L.map_id = secondary_map
|
||||
if(!istype(vis_holder))
|
||||
vis_holder = new /atom/movable/screen/parallax_vis
|
||||
var/turf/T = get_turf(cached_eye)
|
||||
vis_holder.vis_contents = vis = T? SSparallax.get_parallax_vis_contents(T.z) : list()
|
||||
UpdateMotion(auto_z_change, force)
|
||||
|
||||
/**
|
||||
* Updates motion if needed
|
||||
*/
|
||||
/datum/parallax_holder/proc/UpdateMotion(auto_z_change, force)
|
||||
var/turf/T = get_turf(cached_eye)
|
||||
if(!T)
|
||||
if(scroll_speed || scroll_turn)
|
||||
HardResetAnimations()
|
||||
return
|
||||
var/list/ret = SSparallax.get_parallax_motion(T.z)
|
||||
if(ret)
|
||||
Animation(ret[1], ret[2], auto_z_change? 0 : ret[3], auto_z_change? 0 : ret[4], force)
|
||||
else
|
||||
var/area/A = T.loc
|
||||
Animation(A.parallax_move_speed, A.parallax_move_angle, auto_z_change? 0 : null, auto_z_change? 0 : null, force)
|
||||
|
||||
/datum/parallax_holder/proc/Apply(client/C = owner)
|
||||
if(QDELETED(C))
|
||||
return
|
||||
. = list()
|
||||
for(var/atom/movable/screen/parallax_layer/L in layers)
|
||||
if(L.parallax_intensity > owner.prefs.parallax)
|
||||
continue
|
||||
if(!L.ShouldSee(C, last))
|
||||
continue
|
||||
L.SetView(C.view, TRUE)
|
||||
. |= L
|
||||
C.screen |= .
|
||||
if(!secondary_map)
|
||||
var/atom/movable/screen/plane_master/parallax_white/PM = locate() in C.screen
|
||||
if(PM)
|
||||
PM.color = list(
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
1, 1, 1, 1,
|
||||
0, 0, 0, 0
|
||||
)
|
||||
|
||||
/datum/parallax_holder/proc/Remove(client/C = owner)
|
||||
if(QDELETED(C))
|
||||
return
|
||||
C.screen -= layers
|
||||
if(!secondary_map)
|
||||
var/atom/movable/screen/plane_master/parallax_white/PM = locate() in C.screen
|
||||
if(PM)
|
||||
PM.color = initial(PM.color)
|
||||
|
||||
/datum/parallax_holder/proc/SetParallaxType(path)
|
||||
if(!ispath(path, /datum/parallax))
|
||||
CRASH("Invalid path")
|
||||
SetParallax(new path)
|
||||
|
||||
/datum/parallax_holder/proc/SetParallax(datum/parallax/P, delete_old = TRUE, auto_z_change, force)
|
||||
if(P == parallax)
|
||||
return
|
||||
Remove()
|
||||
if(delete_old && istype(parallax) && !QDELETED(parallax))
|
||||
qdel(parallax)
|
||||
HardResetAnimations()
|
||||
parallax = P
|
||||
if(!parallax)
|
||||
return
|
||||
Sync(auto_z_change, force)
|
||||
Apply()
|
||||
|
||||
/**
|
||||
* Runs a modifier to parallax as an animation.
|
||||
*
|
||||
* @params
|
||||
* speed - ds per loop
|
||||
* turn - angle clockwise from north to turn the motion to
|
||||
* windup - ds to spend on windups. 0 for immediate.
|
||||
* turn_speed - ds to spend on turning. 0 for immediate.
|
||||
*/
|
||||
/datum/parallax_holder/proc/Animation(speed = 25, turn = 0, windup = speed, turn_speed = speed, force)
|
||||
// Parallax doesn't currently use this method of rotating.
|
||||
|
||||
// #if !PARALLAX_ROTATION_ANIMATIONS
|
||||
// turn_speed = 0
|
||||
// #endif
|
||||
|
||||
if(speed == 0)
|
||||
StopScrolling(turn = turn, time = windup)
|
||||
return
|
||||
// if(turn != scroll_turn && GetPlaneMaster())
|
||||
// // first handle turn. we turn the planemaster
|
||||
// var/matrix/turn_transform = matrix()
|
||||
// turn_transform.Turn(turn)
|
||||
// scroll_turn = turn
|
||||
// animate(GetPlaneMaster(), transform = turn_transform, time = turn_speed, easing = QUAD_EASING | EASE_IN, flags = ANIMATION_END_NOW | ANIMATION_LINEAR_TRANSFORM)
|
||||
if(scroll_speed == speed && !force)
|
||||
// we're done
|
||||
return
|
||||
// speed diff?
|
||||
scroll_speed = speed
|
||||
scrolling = TRUE
|
||||
// always scroll from north; turn handles everything
|
||||
for(var/atom/movable/screen/parallax_layer/P in layers)
|
||||
if(P.absolute)
|
||||
continue
|
||||
var/matrix/translate_matrix = matrix()
|
||||
translate_matrix.Translate(cos(turn) * 480, sin(turn) * 480)
|
||||
var/matrix/target_matrix = matrix()
|
||||
var/move_speed = speed * P.speed
|
||||
// do the first segment by shifting down one screen
|
||||
P.transform = translate_matrix
|
||||
animate(P, transform = target_matrix, time = move_speed, easing = QUAD_EASING|EASE_IN, flags = ANIMATION_END_NOW)
|
||||
// queue up another incase lag makes QueueLoop not fire on time, this time by shifting up
|
||||
animate(transform = translate_matrix, time = 0)
|
||||
animate(transform = target_matrix, time = move_speed)
|
||||
P.QueueLoop(move_speed, speed * P.speed, translate_matrix, target_matrix)
|
||||
|
||||
/**
|
||||
* Smoothly stops the animation, turning to a certain angle as needed.
|
||||
*/
|
||||
/datum/parallax_holder/proc/StopScrolling(turn = 0, time = 30)
|
||||
// reset turn
|
||||
if(turn != scroll_turn && GetPlaneMaster())
|
||||
var/matrix/turn_transform = matrix()
|
||||
turn_transform.Turn(turn)
|
||||
scroll_turn = turn
|
||||
animate(GetPlaneMaster(), transform = turn_transform, time = time, easing = QUAD_EASING | EASE_OUT, flags = ANIMATION_END_NOW | ANIMATION_LINEAR_TRANSFORM)
|
||||
if(scroll_speed == 0)
|
||||
// we're done
|
||||
scrolling = FALSE
|
||||
scroll_speed = 0
|
||||
return
|
||||
scrolling = FALSE
|
||||
scroll_speed = 0
|
||||
// someone can do the math for "stop after a smooth iteration" later.
|
||||
for(var/atom/movable/screen/parallax_layer/P in layers)
|
||||
if(P.absolute)
|
||||
continue
|
||||
P.CancelAnimation()
|
||||
var/matrix/translate_matrix = matrix()
|
||||
translate_matrix.Translate(cos(turn) * 480, sin(turn) * 480)
|
||||
P.transform = translate_matrix
|
||||
animate(P, transform = matrix(), time = time, easing = QUAD_EASING | EASE_OUT)
|
||||
|
||||
/**
|
||||
* fully resets animation state
|
||||
*/
|
||||
/datum/parallax_holder/proc/HardResetAnimations()
|
||||
// reset vars
|
||||
scroll_turn = 0
|
||||
scroll_speed = 0
|
||||
scrolling = FALSE
|
||||
// reset turn
|
||||
if(GetPlaneMaster())
|
||||
animate(GetPlaneMaster(), transform = matrix(), time = 0, flags = ANIMATION_END_NOW)
|
||||
// reset objects
|
||||
for(var/atom/movable/screen/parallax_layer/P in layers)
|
||||
if(P.absolute)
|
||||
continue
|
||||
P.CancelAnimation()
|
||||
animate(P, transform = matrix(), time = 0, flags = ANIMATION_END_NOW)
|
||||
|
||||
/client/proc/CreateParallax()
|
||||
if(!parallax_holder)
|
||||
parallax_holder = new(src)
|
||||
/atom/movable/screen/parallax_vis
|
||||
screen_loc = "CENTER,CENTER"
|
||||
137
code/_rendering/parallax/parallax_object.dm
Normal file
137
code/_rendering/parallax/parallax_object.dm
Normal file
@@ -0,0 +1,137 @@
|
||||
|
||||
/atom/movable/screen/parallax_layer
|
||||
icon = 'icons/screen/parallax.dmi'
|
||||
blend_mode = BLEND_ADD
|
||||
plane = PLANE_SPACE_PARALLAX
|
||||
screen_loc = "CENTER-7,CENTER-7"
|
||||
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
||||
appearance_flags = PIXEL_SCALE | KEEP_TOGETHER
|
||||
|
||||
// notice - all parallax layers are 15x15 tiles. They roll over every 240 pixels.
|
||||
/// pixel x/y shift per real x/y
|
||||
var/speed = 1
|
||||
/// current cached offset x
|
||||
var/offset_x = 0
|
||||
/// current cached offset y
|
||||
var/offset_y = 0
|
||||
/// normal centered x
|
||||
var/center_x = 0
|
||||
/// normal centered y
|
||||
var/center_y = 0
|
||||
/// absolute - always determine shift x/y as a function of real x/y instead of allowing for relative scroll.
|
||||
var/absolute = FALSE
|
||||
/// parallax level required to see this
|
||||
var/parallax_intensity = PARALLAX_INSANE
|
||||
/// current view we're adapted to
|
||||
var/view_current
|
||||
/// dynamic self tile - tile to our view size. set this to false for static parallax layers.
|
||||
var/dynamic_self_tile = TRUE
|
||||
/// map id
|
||||
var/map_id
|
||||
/// queued animation timerid
|
||||
var/queued_animation
|
||||
|
||||
/atom/movable/screen/parallax_layer/proc/ResetPosition(x, y)
|
||||
// remember that our offsets/directiosn are relative to the player's viewport
|
||||
// this means we need to scroll reverse to them.
|
||||
offset_x = -(center_x + speed * x)
|
||||
offset_y = -(center_y + speed * y)
|
||||
if(!absolute)
|
||||
if(offset_x > 240)
|
||||
offset_x -= 480
|
||||
if(offset_x < -240)
|
||||
offset_x += 480
|
||||
if(offset_y > 240)
|
||||
offset_y -= 480
|
||||
if(offset_y < -240)
|
||||
offset_y += 480
|
||||
screen_loc = "[map_id && "[map_id]:"]CENTER-7:[round(offset_x,1)],CENTER-7:[round(offset_y,1)]"
|
||||
|
||||
/atom/movable/screen/parallax_layer/proc/RelativePosition(x, y, rel_x, rel_y)
|
||||
if(absolute)
|
||||
return ResetPosition(x, y)
|
||||
offset_x -= rel_x * speed
|
||||
offset_y -= rel_y * speed
|
||||
if(offset_x > 240)
|
||||
offset_x -= 480
|
||||
if(offset_x < -240)
|
||||
offset_x += 480
|
||||
if(offset_y > 240)
|
||||
offset_y -= 480
|
||||
if(offset_y < -240)
|
||||
offset_y += 480
|
||||
screen_loc = "[map_id && "[map_id]:"]CENTER-7:[round(offset_x,1)],CENTER-7:[round(offset_y,1)]"
|
||||
|
||||
/atom/movable/screen/parallax_layer/proc/SetView(client_view = world.view, force_update = FALSE)
|
||||
if(view_current == client_view && !force_update)
|
||||
return
|
||||
view_current = client_view
|
||||
if(!dynamic_self_tile)
|
||||
return
|
||||
var/list/real_view = getviewsize(client_view)
|
||||
var/count_x = CEILING((real_view[1] / 2) / 15, 1) + 1
|
||||
var/count_y = CEILING((real_view[2] / 2) / 15, 1) + 1
|
||||
var/list/new_overlays = GetOverlays()
|
||||
for(var/x in -count_x to count_x)
|
||||
for(var/y in -count_y to count_y)
|
||||
if(!x && !y)
|
||||
continue
|
||||
var/mutable_appearance/clone = new
|
||||
// appearance clone
|
||||
clone.icon = icon
|
||||
clone.icon_state = icon_state
|
||||
clone.overlays = GetOverlays()
|
||||
// do NOT inherit our overlays! parallax layers should never have overlays,
|
||||
// because if it inherited us it'll result in exponentially increasing overlays
|
||||
// due to cut_overlays() above over there being a queue operation and not instant!
|
||||
// clone.overlays = list()
|
||||
// currently instantly using overlays =.
|
||||
// clone.blend_mode = blend_mode
|
||||
// clone.mouse_opacity = mouse_opacity
|
||||
// clone.plane = plane
|
||||
// clone.layer = layer
|
||||
// shift to position
|
||||
clone.transform = matrix(1, 0, x * 480, 0, 1, y * 480)
|
||||
new_overlays += clone
|
||||
overlays = new_overlays
|
||||
|
||||
/atom/movable/screen/parallax_layer/proc/ShouldSee(client/C, atom/location)
|
||||
return TRUE
|
||||
|
||||
/**
|
||||
* Return "natural" overlays, as we're goin to do some fuckery to overlays above.
|
||||
*/
|
||||
/atom/movable/screen/parallax_layer/proc/GetOverlays()
|
||||
return list()
|
||||
|
||||
/atom/movable/screen/parallax_layer/proc/Clone()
|
||||
var/atom/movable/screen/parallax_layer/layer = new type
|
||||
layer.speed = speed
|
||||
layer.offset_x = offset_x
|
||||
layer.offset_y = offset_y
|
||||
layer.absolute = absolute
|
||||
layer.parallax_intensity = parallax_intensity
|
||||
layer.view_current = view_current
|
||||
layer.appearance = appearance
|
||||
|
||||
/atom/movable/screen/parallax_layer/proc/default_x()
|
||||
return center_x
|
||||
|
||||
/atom/movable/screen/parallax_layer/proc/default_y()
|
||||
return center_y
|
||||
|
||||
/atom/movable/screen/parallax_layer/proc/QueueLoop(delay, speed, matrix/translate_matrix, matrix/target_matrix)
|
||||
if(queued_animation)
|
||||
CancelAnimation()
|
||||
queued_animation = addtimer(CALLBACK(src, .proc/_loop, speed, translate_matrix, target_matrix), delay, TIMER_STOPPABLE)
|
||||
|
||||
/atom/movable/screen/parallax_layer/proc/_loop(speed, matrix/translate_matrix = matrix(1, 0, 0, 0, 1, 480), matrix/target_matrix = matrix())
|
||||
transform = translate_matrix
|
||||
animate(src, transform = target_matrix, time = speed, loop = -1)
|
||||
animate(transform = translate_matrix, time = 0)
|
||||
queued_animation = null
|
||||
|
||||
/atom/movable/screen/parallax_layer/proc/CancelAnimation()
|
||||
if(queued_animation)
|
||||
deltimer(queued_animation)
|
||||
queued_animation = null
|
||||
66
code/_rendering/parallax/types/space.dm
Normal file
66
code/_rendering/parallax/types/space.dm
Normal file
@@ -0,0 +1,66 @@
|
||||
/datum/parallax/space
|
||||
var/static/planet_offset_x = rand(100, 160)
|
||||
var/static/planet_offset_y = rand(100, 160)
|
||||
var/static/random_layer = pickweightAllowZero(list(
|
||||
/atom/movable/screen/parallax_layer/space/random/asteroids = 35,
|
||||
/atom/movable/screen/parallax_layer/space/random/space_gas = 35,
|
||||
null = 30
|
||||
))
|
||||
var/static/random_gas_color = pick(COLOR_TEAL, COLOR_GREEN, COLOR_YELLOW, COLOR_CYAN, COLOR_ORANGE, COLOR_PURPLE)
|
||||
|
||||
/datum/parallax/space/CreateObjects()
|
||||
. = ..()
|
||||
. += new /atom/movable/screen/parallax_layer/space/layer_1
|
||||
. += new /atom/movable/screen/parallax_layer/space/layer_2
|
||||
. += new /atom/movable/screen/parallax_layer/space/layer_3
|
||||
var/atom/movable/screen/parallax_layer/space/planet/P = new
|
||||
P.pixel_x = planet_offset_x
|
||||
P.pixel_y = planet_offset_y
|
||||
. += P
|
||||
if(random_layer)
|
||||
. += new random_layer
|
||||
if(ispath(random_layer, /atom/movable/screen/parallax_layer/space/random/space_gas))
|
||||
var/atom/movable/screen/parallax_layer/space/random/space_gas/SG = locate(random_layer) in objects
|
||||
SG.add_atom_colour(random_gas_color, ADMIN_COLOUR_PRIORITY)
|
||||
|
||||
/atom/movable/screen/parallax_layer/space/layer_1
|
||||
icon_state = "layer1"
|
||||
speed = 0.6
|
||||
layer = 1
|
||||
parallax_intensity = PARALLAX_LOW
|
||||
|
||||
/atom/movable/screen/parallax_layer/space/layer_2
|
||||
icon_state = "layer2"
|
||||
speed = 1
|
||||
layer = 2
|
||||
parallax_intensity = PARALLAX_MED
|
||||
|
||||
/atom/movable/screen/parallax_layer/space/layer_3
|
||||
icon_state = "layer3"
|
||||
speed = 1.4
|
||||
layer = 3
|
||||
parallax_intensity = PARALLAX_HIGH
|
||||
|
||||
/atom/movable/screen/parallax_layer/space/random
|
||||
blend_mode = BLEND_OVERLAY
|
||||
speed = 3
|
||||
layer = 3
|
||||
parallax_intensity = PARALLAX_INSANE
|
||||
|
||||
/atom/movable/screen/parallax_layer/space/random/space_gas
|
||||
icon_state = "space_gas"
|
||||
|
||||
/atom/movable/screen/parallax_layer/space/random/asteroids
|
||||
icon_state = "asteroids"
|
||||
|
||||
/atom/movable/screen/parallax_layer/space/planet
|
||||
icon_state = "planet"
|
||||
blend_mode = BLEND_OVERLAY
|
||||
absolute = TRUE //Status of seperation
|
||||
speed = 3
|
||||
layer = 30
|
||||
dynamic_self_tile = FALSE
|
||||
|
||||
/atom/movable/screen/parallax_layer/space/planet/ShouldSee(client/C, atom/location)
|
||||
var/turf/T = get_turf(location)
|
||||
return ..() && T && is_station_level(T.z)
|
||||
@@ -14,6 +14,9 @@ SUBSYSTEM_DEF(atoms)
|
||||
|
||||
var/list/BadInitializeCalls = list()
|
||||
|
||||
/// Atoms that will be deleted once the subsystem is initialized
|
||||
var/list/queued_deletions = list()
|
||||
|
||||
initialized = INITIALIZATION_INSSATOMS
|
||||
|
||||
/datum/controller/subsystem/atoms/Initialize(timeofday)
|
||||
@@ -62,6 +65,12 @@ SUBSYSTEM_DEF(atoms)
|
||||
testing("Late initialized [late_loaders.len] atoms")
|
||||
late_loaders.Cut()
|
||||
|
||||
for (var/queued_deletion in queued_deletions)
|
||||
qdel(queued_deletion)
|
||||
|
||||
testing("[queued_deletions.len] atoms were queued for deletion.")
|
||||
queued_deletions.Cut()
|
||||
|
||||
/// Init this specific atom
|
||||
/datum/controller/subsystem/atoms/proc/InitAtom(atom/A, list/arguments)
|
||||
var/the_type = A.type
|
||||
@@ -151,6 +160,14 @@ SUBSYSTEM_DEF(atoms)
|
||||
if(fails & BAD_INIT_SLEPT)
|
||||
. += "- Slept during Initialize()\n"
|
||||
|
||||
/// Prepares an atom to be deleted once the atoms SS is initialized.
|
||||
/datum/controller/subsystem/atoms/proc/prepare_deletion(atom/target)
|
||||
if (initialized == INITIALIZATION_INNEW_REGULAR)
|
||||
// Atoms SS has already completed, just kill it now.
|
||||
qdel(target)
|
||||
else
|
||||
queued_deletions += WEAKREF(target)
|
||||
|
||||
/datum/controller/subsystem/atoms/Shutdown()
|
||||
var/initlog = InitLog()
|
||||
if(initlog)
|
||||
|
||||
@@ -5,21 +5,6 @@ SUBSYSTEM_DEF(parallax)
|
||||
priority = FIRE_PRIORITY_PARALLAX
|
||||
runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT
|
||||
var/list/currentrun
|
||||
var/planet_x_offset = 128
|
||||
var/planet_y_offset = 128
|
||||
var/random_layer
|
||||
var/random_parallax_color
|
||||
|
||||
|
||||
//These are cached per client so needs to be done asap so people joining at roundstart do not miss these.
|
||||
/datum/controller/subsystem/parallax/PreInit()
|
||||
. = ..()
|
||||
if(prob(70)) //70% chance to pick a special extra layer
|
||||
random_layer = pick(/atom/movable/screen/parallax_layer/random/space_gas, /atom/movable/screen/parallax_layer/random/asteroids)
|
||||
random_parallax_color = pick(COLOR_TEAL, COLOR_GREEN, COLOR_YELLOW, COLOR_CYAN, COLOR_ORANGE, COLOR_PURPLE)//Special color for random_layer1. Has to be done here so everyone sees the same color. [COLOR_SILVER]
|
||||
planet_y_offset = rand(100, 160)
|
||||
planet_x_offset = rand(100, 160)
|
||||
|
||||
|
||||
/datum/controller/subsystem/parallax/fire(resumed = FALSE)
|
||||
if (!resumed)
|
||||
@@ -28,28 +13,82 @@ SUBSYSTEM_DEF(parallax)
|
||||
//cache for sanic speed (lists are references anyways)
|
||||
var/list/currentrun = src.currentrun
|
||||
|
||||
while(length(currentrun))
|
||||
var/client/processing_client = currentrun[currentrun.len]
|
||||
currentrun.len--
|
||||
if (QDELETED(processing_client) || !processing_client.eye)
|
||||
if(times_fired % 5) // lazy tick
|
||||
while(length(currentrun))
|
||||
var/client/processing_client = currentrun[currentrun.len]
|
||||
currentrun.len--
|
||||
if (QDELETED(processing_client) || !processing_client.eye)
|
||||
if (MC_TICK_CHECK)
|
||||
return
|
||||
continue
|
||||
processing_client.parallax_holder?.Update()
|
||||
if (MC_TICK_CHECK)
|
||||
return
|
||||
continue
|
||||
|
||||
var/atom/movable/movable_eye = processing_client.eye
|
||||
if(!istype(movable_eye))
|
||||
continue
|
||||
|
||||
for (movable_eye; isloc(movable_eye.loc) && !isturf(movable_eye.loc); movable_eye = movable_eye.loc);
|
||||
|
||||
if(movable_eye == processing_client.movingmob)
|
||||
else // full tick
|
||||
while(length(currentrun))
|
||||
var/client/processing_client = currentrun[currentrun.len]
|
||||
currentrun.len--
|
||||
if (QDELETED(processing_client) || !processing_client.eye)
|
||||
if (MC_TICK_CHECK)
|
||||
return
|
||||
continue
|
||||
processing_client.parallax_holder?.Update(TRUE)
|
||||
if (MC_TICK_CHECK)
|
||||
return
|
||||
continue
|
||||
if(!isnull(processing_client.movingmob))
|
||||
LAZYREMOVE(processing_client.movingmob.client_mobs_in_contents, processing_client.mob)
|
||||
LAZYADD(movable_eye.client_mobs_in_contents, processing_client.mob)
|
||||
processing_client.movingmob = movable_eye
|
||||
if (MC_TICK_CHECK)
|
||||
return
|
||||
|
||||
currentrun = null
|
||||
|
||||
/**
|
||||
* Gets parallax type for zlevel.
|
||||
*/
|
||||
/datum/controller/subsystem/parallax/proc/get_parallax_type(z)
|
||||
return /datum/parallax/space
|
||||
|
||||
/**
|
||||
* Gets parallax for zlevel.
|
||||
*/
|
||||
/datum/controller/subsystem/parallax/proc/get_parallax_datum(z)
|
||||
var/datum_type = get_parallax_type(z)
|
||||
return new datum_type
|
||||
|
||||
/**
|
||||
* Gets parallax added vis contents for zlevel
|
||||
*/
|
||||
/datum/controller/subsystem/parallax/proc/get_parallax_vis_contents(z)
|
||||
return list()
|
||||
|
||||
/**
|
||||
* Gets parallax motion for a zlevel
|
||||
*
|
||||
* Returns null or list(speed, dir deg clockwise from north, windup, turnrate)
|
||||
* THE RETURNED LIST MUST BE A 4-TUPLE, OR PARALLAX WILL CRASH.
|
||||
* DO NOT SCREW WITH THIS UNLESS YOU KNOW WHAT YOU ARE DOING.
|
||||
*
|
||||
* This will override area motion
|
||||
*/
|
||||
/datum/controller/subsystem/parallax/proc/get_parallax_motion(z)
|
||||
return null
|
||||
|
||||
/**
|
||||
* updates all parallax for clients on a z
|
||||
*/
|
||||
/datum/controller/subsystem/parallax/proc/update_clients_on_z(z)
|
||||
for(var/client/C in GLOB.clients)
|
||||
if(C.mob.z == z)
|
||||
C.parallax_holder?.Update()
|
||||
|
||||
/**
|
||||
* resets all parallax for clients on a z
|
||||
*/
|
||||
/datum/controller/subsystem/parallax/proc/reset_clients_on_z(z)
|
||||
for(var/client/C in GLOB.clients)
|
||||
if(C.mob.z == z)
|
||||
C.parallax_holder?.Reset()
|
||||
|
||||
/**
|
||||
* updates motion of all clients on z
|
||||
*/
|
||||
/datum/controller/subsystem/parallax/proc/update_z_motion(z)
|
||||
for(var/client/C in GLOB.clients)
|
||||
if(C.mob.z == z)
|
||||
C.parallax_holder?.UpdateMotion()
|
||||
|
||||
@@ -513,7 +513,9 @@ SUBSYSTEM_DEF(shuttle)
|
||||
if(!midpoint)
|
||||
return FALSE
|
||||
var/area/shuttle/transit/A = new()
|
||||
A.parallax_movedir = travel_dir
|
||||
A.parallax_moving = TRUE
|
||||
A.parallax_move_angle = dir2angle(travel_dir)
|
||||
A.parallax_move_speed = M.parallax_speed
|
||||
A.contents = proposal.reserved_turfs
|
||||
var/obj/docking_port/stationary/transit/new_transit_dock = new(midpoint)
|
||||
new_transit_dock.reserved_area = proposal
|
||||
|
||||
@@ -603,8 +603,10 @@ SUBSYSTEM_DEF(ticker)
|
||||
news_message = "The burst of energy released near [station_name()] has been confirmed as merely a test of a new weapon. However, due to an unexpected mechanical error, their communications system has been knocked offline."
|
||||
if(SHUTTLE_HIJACK)
|
||||
news_message = "During routine evacuation procedures, the emergency shuttle of [station_name()] had its navigation protocols corrupted and went off course, but was recovered shortly after."
|
||||
if(GANG_VICTORY)
|
||||
news_message = "Company officials reaffirmed that sudden deployments of special forces are not in any way connected to rumors of [station_name()] being covered in graffiti."
|
||||
if(GANG_OPERATING)
|
||||
news_message = "The company would like to state that any rumors of criminal organizing on board stations such as [station_name()] are falsehoods, and not to be emulated."
|
||||
if(GANG_DESTROYED)
|
||||
news_message = "The crew of [station_name()] would like to thank the Spinward Stellar Coalition Police Department for quickly resolving a minor terror threat to the station."
|
||||
|
||||
if(SSblackbox.first_death)
|
||||
var/list/ded = SSblackbox.first_death
|
||||
|
||||
@@ -101,7 +101,7 @@
|
||||
if(!C)
|
||||
return
|
||||
watching += C
|
||||
M.overlay_fullscreen("cinematic",/atom/movable/screen/fullscreen/cinematic_backdrop)
|
||||
M.overlay_fullscreen("cinematic",/atom/movable/screen/fullscreen/tiled/cinematic_backdrop)
|
||||
C.screen += screen
|
||||
|
||||
//Sound helper
|
||||
|
||||
@@ -370,7 +370,7 @@
|
||||
user.emote("scream")
|
||||
user.gain_trauma(/datum/brain_trauma/severe/paralysis/spinesnapped) // oopsie indeed!
|
||||
shake_camera(user, 7, 7)
|
||||
user.overlay_fullscreen("flash", /atom/movable/screen/fullscreen/flash)
|
||||
user.overlay_fullscreen("flash", /atom/movable/screen/fullscreen/tiled/flash)
|
||||
user.clear_fullscreen("flash", 4.5)
|
||||
|
||||
if(94 to 98)
|
||||
@@ -381,7 +381,7 @@
|
||||
user.gain_trauma_type(BRAIN_TRAUMA_MILD)
|
||||
user.playsound_local(get_turf(user), 'sound/weapons/flashbang.ogg', 100, TRUE, 8, 0.9)
|
||||
shake_camera(user, 6, 6)
|
||||
user.overlay_fullscreen("flash", /atom/movable/screen/fullscreen/flash)
|
||||
user.overlay_fullscreen("flash", /atom/movable/screen/fullscreen/tiled/flash)
|
||||
user.clear_fullscreen("flash", 3.5)
|
||||
|
||||
if(84 to 93)
|
||||
@@ -394,7 +394,7 @@
|
||||
user.playsound_local(get_turf(user), 'sound/weapons/flashbang.ogg', 100, TRUE, 8, 0.9)
|
||||
user.DefaultCombatKnockdown(40)
|
||||
shake_camera(user, 5, 5)
|
||||
user.overlay_fullscreen("flash", /atom/movable/screen/fullscreen/flash)
|
||||
user.overlay_fullscreen("flash", /atom/movable/screen/fullscreen/tiled/flash)
|
||||
user.clear_fullscreen("flash", 2.5)
|
||||
|
||||
if(64 to 83)
|
||||
|
||||
@@ -200,7 +200,6 @@
|
||||
/datum/mind/proc/has_antag_datum(datum_type, check_subtypes = TRUE)
|
||||
if(!datum_type)
|
||||
return
|
||||
. = FALSE
|
||||
for(var/a in antag_datums)
|
||||
var/datum/antagonist/A = a
|
||||
if(check_subtypes && istype(A, datum_type))
|
||||
|
||||
@@ -295,6 +295,37 @@
|
||||
mood_change = -3
|
||||
timeout = 1 MINUTES
|
||||
|
||||
/datum/mood_event/high_five_alone
|
||||
description = "<span class='boldwarning'>I tried getting a high-five with no one around, how embarassing!</span>\n"
|
||||
mood_change = -2
|
||||
timeout = 1 MINUTES
|
||||
|
||||
/datum/mood_event/high_five_full_hand
|
||||
description = "<span class='boldwarning'>Oh God, I don't even know how to high-five correctly...</span>\n"
|
||||
mood_change = -1
|
||||
timeout = 45 SECONDS
|
||||
|
||||
/datum/mood_event/left_hanging
|
||||
description = "<span class='boldwarning'>But everyone loves high fives! Maybe people just... hate me?</span>\n"
|
||||
mood_change = -2
|
||||
timeout = 1.5 MINUTES
|
||||
|
||||
/datum/mood_event/too_slow
|
||||
description = "<span class='boldwarning'>NO! HOW COULD I BE.... TOO SLOW???</span>\n"
|
||||
mood_change = -2 // multiplied by how many people saw it happen, up to 8, so potentially massive. the ULTIMATE prank carries a lot of weight
|
||||
timeout = 2 MINUTES
|
||||
|
||||
/datum/mood_event/too_slow/add_effects(param)
|
||||
var/people_laughing_at_you = 1 // start with 1 in case they're on the same tile or something
|
||||
for(var/mob/living/carbon/iter_carbon in oview(owner, 7))
|
||||
if(iter_carbon.stat == CONSCIOUS)
|
||||
people_laughing_at_you++
|
||||
if(people_laughing_at_you > 7)
|
||||
break
|
||||
|
||||
mood_change *= people_laughing_at_you
|
||||
return ..()
|
||||
|
||||
/datum/mood_event/sacrifice_bad
|
||||
description = "<span class='warning'>Those darn savages!</span>\n"
|
||||
mood_change = -5
|
||||
|
||||
@@ -206,6 +206,21 @@
|
||||
description = "<span class='nicegreen'>Feels nice to get that out of the way!</span>\n"
|
||||
mood_change = 3
|
||||
|
||||
/datum/mood_event/high_five
|
||||
description = "<span class='nicegreen'>I love getting high fives!</span>\n"
|
||||
mood_change = 2
|
||||
timeout = 45 SECONDS
|
||||
|
||||
/datum/mood_event/high_ten
|
||||
description = "<span class='nicegreen'>AMAZING! A HIGH-TEN!</span>\n"
|
||||
mood_change = 3
|
||||
timeout = 45 SECONDS
|
||||
|
||||
/datum/mood_event/down_low
|
||||
description = "<span class='nicegreen'>HA! What a rube, they never stood a chance...</span>\n"
|
||||
mood_change = 4
|
||||
timeout = 1.5 MINUTES
|
||||
|
||||
/datum/mood_event/sacrifice_good
|
||||
description = "<span class='nicegreen'>The gods are pleased with this offering!</span>\n"
|
||||
mood_change = 5
|
||||
|
||||
@@ -771,7 +771,7 @@
|
||||
/datum/status_effect/necropolis_curse/proc/apply_curse(set_curse)
|
||||
curse_flags |= set_curse
|
||||
if(curse_flags & CURSE_BLINDING)
|
||||
owner.overlay_fullscreen("curse", /atom/movable/screen/fullscreen/curse, 1)
|
||||
owner.overlay_fullscreen("curse", /atom/movable/screen/fullscreen/scaled/curse, 1)
|
||||
|
||||
/datum/status_effect/necropolis_curse/proc/remove_curse(remove_curse)
|
||||
if(remove_curse & CURSE_BLINDING)
|
||||
@@ -929,11 +929,13 @@
|
||||
|
||||
/atom/movable/screen/alert/status_effect/strandling/Click(location, control, params)
|
||||
. = ..()
|
||||
to_chat(mob_viewer, "<span class='notice'>You attempt to remove the durathread strand from around your neck.</span>")
|
||||
if(do_after(mob_viewer, 35, null, mob_viewer))
|
||||
if(isliving(mob_viewer))
|
||||
var/mob/living/L = mob_viewer
|
||||
to_chat(mob_viewer, "<span class='notice'>You successfully remove the durathread strand.</span>")
|
||||
if(usr != owner)
|
||||
return
|
||||
to_chat(owner, "<span class='notice'>You attempt to remove the durathread strand from around your neck.</span>")
|
||||
if(do_after(owner, 35, null, owner))
|
||||
if(isliving(owner))
|
||||
var/mob/living/L = owner
|
||||
to_chat(owner, "<span class='notice'>You successfully remove the durathread strand.</span>")
|
||||
L.remove_status_effect(STATUS_EFFECT_CHOKINGSTRAND)
|
||||
|
||||
|
||||
|
||||
@@ -83,3 +83,89 @@
|
||||
/datum/status_effect/throat_soothed/on_remove()
|
||||
REMOVE_TRAIT(owner, TRAIT_SOOTHED_THROAT, "[STATUS_EFFECT_TRAIT]_[id]")
|
||||
return ..()
|
||||
|
||||
// this status effect is used to negotiate the high-fiving capabilities of all concerned parties
|
||||
/datum/status_effect/offering
|
||||
id = "offering"
|
||||
duration = -1
|
||||
tick_interval = -1
|
||||
status_type = STATUS_EFFECT_UNIQUE
|
||||
alert_type = null
|
||||
/// The people who were offered this item at the start
|
||||
var/list/possible_takers
|
||||
/// The actual item being offered
|
||||
var/obj/item/offered_item
|
||||
/// The type of alert given to people when offered, in case you need to override some behavior (like for high-fives)
|
||||
var/give_alert_type = /atom/movable/screen/alert/give
|
||||
|
||||
/datum/status_effect/offering/on_creation(mob/living/new_owner, obj/item/offer, give_alert_override)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
|
||||
offered_item = offer
|
||||
if(give_alert_override)
|
||||
give_alert_type = give_alert_override
|
||||
|
||||
for(var/mob/living/carbon/possible_taker in orange(1, owner))
|
||||
if(!owner.CanReach(possible_taker) || IS_DEAD_OR_INCAP(possible_taker) || !possible_taker.can_hold_items())
|
||||
continue
|
||||
register_candidate(possible_taker)
|
||||
|
||||
if(!possible_takers) // no one around
|
||||
qdel(src)
|
||||
return
|
||||
|
||||
RegisterSignal(owner, COMSIG_MOVABLE_MOVED, .proc/check_owner_in_range)
|
||||
RegisterSignal(offered_item, list(COMSIG_PARENT_QDELETING, COMSIG_ITEM_DROPPED), .proc/dropped_item)
|
||||
//RegisterSignal(owner, COMSIG_PARENT_EXAMINE_MORE, .proc/check_fake_out)
|
||||
|
||||
/datum/status_effect/offering/Destroy()
|
||||
for(var/i in possible_takers)
|
||||
var/mob/living/carbon/removed_taker = i
|
||||
remove_candidate(removed_taker)
|
||||
LAZYCLEARLIST(possible_takers)
|
||||
return ..()
|
||||
|
||||
/// Hook up the specified carbon mob to be offered the item in question, give them the alert and signals and all
|
||||
/datum/status_effect/offering/proc/register_candidate(mob/living/carbon/possible_candidate)
|
||||
var/atom/movable/screen/alert/give/G = possible_candidate.throw_alert("[owner]", give_alert_type)
|
||||
if(!G)
|
||||
return
|
||||
LAZYADD(possible_takers, possible_candidate)
|
||||
RegisterSignal(possible_candidate, COMSIG_MOVABLE_MOVED, .proc/check_taker_in_range)
|
||||
G.setup(possible_candidate, owner, offered_item)
|
||||
|
||||
/// Remove the alert and signals for the specified carbon mob. Automatically removes the status effect when we lost the last taker
|
||||
/datum/status_effect/offering/proc/remove_candidate(mob/living/carbon/removed_candidate)
|
||||
removed_candidate.clear_alert("[owner]")
|
||||
LAZYREMOVE(possible_takers, removed_candidate)
|
||||
UnregisterSignal(removed_candidate, COMSIG_MOVABLE_MOVED)
|
||||
if(!possible_takers && !QDELING(src))
|
||||
qdel(src)
|
||||
|
||||
/// One of our possible takers moved, see if they left us hanging
|
||||
/datum/status_effect/offering/proc/check_taker_in_range(mob/living/carbon/taker)
|
||||
SIGNAL_HANDLER
|
||||
if(owner.CanReach(taker) && !IS_DEAD_OR_INCAP(taker))
|
||||
return
|
||||
|
||||
remove_candidate(taker)
|
||||
|
||||
/// The offerer moved, see if anyone is out of range now
|
||||
/datum/status_effect/offering/proc/check_owner_in_range(mob/living/carbon/source)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
for(var/i in possible_takers)
|
||||
var/mob/living/carbon/checking_taker = i
|
||||
if(!istype(checking_taker) || !owner.CanReach(checking_taker) || IS_DEAD_OR_INCAP(checking_taker))
|
||||
remove_candidate(checking_taker)
|
||||
|
||||
/// We lost the item, give it up
|
||||
/datum/status_effect/offering/proc/dropped_item(obj/item/source)
|
||||
SIGNAL_HANDLER
|
||||
qdel(src)
|
||||
|
||||
/datum/status_effect/offering/secret_handshake
|
||||
id = "secret_handshake"
|
||||
give_alert_type = /atom/movable/screen/alert/give/secret_handshake
|
||||
|
||||
@@ -25,18 +25,19 @@
|
||||
/datum/status_effect/proc/on_creation(mob/living/new_owner, ...)
|
||||
if(new_owner)
|
||||
owner = new_owner
|
||||
if(owner)
|
||||
LAZYADD(owner.status_effects, src)
|
||||
if(!owner || !on_apply())
|
||||
if(QDELETED(owner) || !on_apply())
|
||||
qdel(src)
|
||||
return
|
||||
if(owner)
|
||||
LAZYADD(owner.status_effects, src)
|
||||
if(duration != -1)
|
||||
duration = world.time + duration
|
||||
next_tick = world.time + tick_interval
|
||||
if(alert_type)
|
||||
var/atom/movable/screen/alert/status_effect/A = owner.throw_alert(id, alert_type)
|
||||
A.attached_effect = src //so the alert can reference us, if it needs to
|
||||
linked_alert = A //so we can reference the alert, if we need to
|
||||
if(istype(A))
|
||||
A?.attached_effect = src //so the alert can reference us, if it needs to
|
||||
linked_alert = A //so we can reference the alert, if we need to
|
||||
START_PROCESSING(SSstatus_effects, src)
|
||||
return TRUE
|
||||
|
||||
|
||||
@@ -50,7 +50,12 @@
|
||||
|
||||
var/has_gravity = FALSE
|
||||
|
||||
var/parallax_movedir = 0
|
||||
/// Parallax moving?
|
||||
var/parallax_moving = FALSE
|
||||
/// Parallax move speed - 0 to disable
|
||||
var/parallax_move_speed = 0
|
||||
/// Parallax move dir - degrees clockwise from north
|
||||
var/parallax_move_angle = 0
|
||||
|
||||
var/list/ambientsounds = GENERIC
|
||||
flags_1 = CAN_BE_DIRTY_1
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
var/pass_flags = 0
|
||||
var/moving_diagonally = 0 //0: not doing a diagonal move. 1 and 2: doing the first/second step of the diagonal move
|
||||
var/atom/movable/moving_from_pull //attempt to resume grab after moving instead of before.
|
||||
var/list/client_mobs_in_contents // This contains all the client mobs within this container
|
||||
var/list/acted_explosions //for explosion dodging
|
||||
var/datum/forced_movement/force_moving = null //handled soley by forced_movement.dm
|
||||
|
||||
@@ -108,8 +107,6 @@
|
||||
for(var/movable_content in contents)
|
||||
qdel(movable_content)
|
||||
|
||||
LAZYCLEARLIST(client_mobs_in_contents)
|
||||
|
||||
moveToNullspace()
|
||||
|
||||
/atom/movable/proc/update_emissive_block()
|
||||
|
||||
@@ -176,12 +176,8 @@
|
||||
if (!inertia_moving)
|
||||
inertia_next_move = world.time + inertia_move_delay
|
||||
newtonian_move(Dir)
|
||||
if (length(client_mobs_in_contents))
|
||||
update_parallax_contents()
|
||||
|
||||
return TRUE
|
||||
|
||||
|
||||
// Make sure you know what you're doing if you call this, this is intended to only be called by byond directly.
|
||||
// You probably want CanPass()
|
||||
/atom/movable/Cross(atom/movable/AM)
|
||||
|
||||
@@ -242,6 +242,69 @@
|
||||
log_game("DYNAMIC: [key_name(M)] was selected by the [name] ruleset and has been made into a midround traitor.")
|
||||
return TRUE
|
||||
|
||||
//////////////////////////////////////////////
|
||||
// //
|
||||
// FAMILIES //
|
||||
// //
|
||||
//////////////////////////////////////////////
|
||||
|
||||
/datum/dynamic_ruleset/midround/families
|
||||
name = "Family Head Aspirants"
|
||||
persistent = TRUE
|
||||
antag_datum = /datum/antagonist/gang
|
||||
antag_flag = ROLE_FAMILY_HEAD_ASPIRANT
|
||||
antag_flag_override = ROLE_FAMILIES
|
||||
protected_roles = list("Prisoner", "Head of Personnel")
|
||||
restricted_roles = list("AI", "Cyborg", "Prisoner", "Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Chaplain", "Head of Personnel", "Quartermaster", "Chief Engineer", "Chief Medical Officer", "Research Director")
|
||||
required_candidates = 9
|
||||
weight = 3
|
||||
cost = 15
|
||||
requirements = list(101,101,101,50,30,20,10,10,10,10)
|
||||
flags = HIGH_IMPACT_RULESET
|
||||
blocking_rules = list(/datum/dynamic_ruleset/roundstart/families)
|
||||
/// A reference to the handler that is used to run pre_execute(), execute(), etc..
|
||||
var/datum/gang_handler/handler
|
||||
|
||||
/datum/dynamic_ruleset/midround/families/trim_candidates()
|
||||
..()
|
||||
candidates = living_players
|
||||
for(var/mob/living/player in candidates)
|
||||
if(issilicon(player))
|
||||
candidates -= player
|
||||
else if(is_centcom_level(player.z))
|
||||
candidates -= player
|
||||
else if(player.mind && (player.mind.special_role || player.mind.antag_datums?.len > 0))
|
||||
candidates -= player
|
||||
else if(HAS_TRAIT(player, TRAIT_MINDSHIELD))
|
||||
candidates -= player
|
||||
|
||||
|
||||
/datum/dynamic_ruleset/midround/families/ready(forced = FALSE)
|
||||
if (required_candidates > living_players.len)
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/datum/dynamic_ruleset/midround/families/pre_execute()
|
||||
..()
|
||||
handler = new /datum/gang_handler(candidates,restricted_roles)
|
||||
handler.gang_balance_cap = clamp((indice_pop - 3), 2, 5) // gang_balance_cap by indice_pop: (2,2,2,2,2,3,4,5,5,5)
|
||||
handler.midround_ruleset = TRUE
|
||||
handler.use_dynamic_timing = TRUE
|
||||
return handler.pre_setup_analogue()
|
||||
|
||||
/datum/dynamic_ruleset/midround/families/execute()
|
||||
return handler.post_setup_analogue(TRUE)
|
||||
|
||||
/datum/dynamic_ruleset/midround/families/clean_up()
|
||||
QDEL_NULL(handler)
|
||||
..()
|
||||
|
||||
/datum/dynamic_ruleset/midround/families/rule_process()
|
||||
return handler.process_analogue()
|
||||
|
||||
/datum/dynamic_ruleset/midround/families/round_result()
|
||||
return handler.set_round_result_analogue()
|
||||
|
||||
//////////////////////////////////////////////
|
||||
// //
|
||||
// Malfunctioning AI //
|
||||
|
||||
@@ -485,6 +485,47 @@
|
||||
SSticker.mode_result = "loss - servants failed their objective (summon ratvar)"
|
||||
SSticker.news_report = CULT_FAILURE
|
||||
|
||||
//////////////////////////////////////////////
|
||||
// //
|
||||
// FAMILIES //
|
||||
// //
|
||||
//////////////////////////////////////////////
|
||||
|
||||
/datum/dynamic_ruleset/roundstart/families
|
||||
name = "Families"
|
||||
persistent = TRUE
|
||||
antag_datum = /datum/antagonist/gang
|
||||
antag_flag = ROLE_FAMILIES
|
||||
protected_roles = list("Prisoner", "Head of Personnel")
|
||||
restricted_roles = list("AI", "Cyborg", "Prisoner", "Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Chaplain", "Head of Personnel", "Quartermaster", "Chief Engineer", "Chief Medical Officer", "Research Director")
|
||||
required_candidates = 9
|
||||
weight = 3
|
||||
cost = 15
|
||||
requirements = list(101,101,101,50,30,20,10,10,10,10)
|
||||
flags = HIGH_IMPACT_RULESET
|
||||
/// A reference to the handler that is used to run pre_execute(), execute(), etc..
|
||||
var/datum/gang_handler/handler
|
||||
|
||||
/datum/dynamic_ruleset/roundstart/families/pre_execute()
|
||||
..()
|
||||
handler = new /datum/gang_handler(candidates,restricted_roles)
|
||||
handler.gangs_to_generate = (antag_cap[indice_pop] / 2)
|
||||
handler.gang_balance_cap = clamp((indice_pop - 3), 2, 5) // gang_balance_cap by indice_pop: (2,2,2,2,2,3,4,5,5,5)
|
||||
return handler.pre_setup_analogue()
|
||||
|
||||
/datum/dynamic_ruleset/roundstart/families/execute()
|
||||
return handler.post_setup_analogue(TRUE)
|
||||
|
||||
/datum/dynamic_ruleset/roundstart/families/clean_up()
|
||||
QDEL_NULL(handler)
|
||||
..()
|
||||
|
||||
/datum/dynamic_ruleset/roundstart/families/rule_process()
|
||||
return handler.process_analogue()
|
||||
|
||||
/datum/dynamic_ruleset/roundstart/families/round_result()
|
||||
return handler.set_round_result_analogue()
|
||||
|
||||
// Admin only rulesets. The threat requirement is 101 so it is not possible to roll them.
|
||||
|
||||
//////////////////////////////////////////////
|
||||
|
||||
50
code/game/gamemodes/gang/gang.dm
Normal file
50
code/game/gamemodes/gang/gang.dm
Normal file
@@ -0,0 +1,50 @@
|
||||
/datum/game_mode/gang
|
||||
name = "Families"
|
||||
config_tag = "families"
|
||||
antag_flag = ROLE_TRAITOR
|
||||
false_report_weight = 5
|
||||
required_players = 0
|
||||
required_enemies = 1
|
||||
recommended_enemies = 4
|
||||
announce_span = "danger"
|
||||
announce_text = "Grove For Lyfe!"
|
||||
reroll_friendly = FALSE
|
||||
restricted_jobs = list("Cyborg", "AI", "Prisoner","Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel")//N O
|
||||
protected_jobs = list()
|
||||
|
||||
/// A reference to the handler that is used to run pre_setup(), post_setup(), etc..
|
||||
var/datum/gang_handler/handler
|
||||
|
||||
/datum/game_mode/gang/warriors
|
||||
name = "Warriors"
|
||||
config_tag = "warriors"
|
||||
announce_text = "Can you survive this onslaught?"
|
||||
|
||||
/datum/game_mode/gang/warriors/pre_setup()
|
||||
handler = new /datum/gang_handler(antag_candidates,restricted_jobs)
|
||||
var/list/datum/antagonist/gang/gangs_to_generate = subtypesof(/datum/antagonist/gang)
|
||||
handler.gangs_to_generate = gangs_to_generate.len
|
||||
handler.gang_balance_cap = 3
|
||||
return handler.pre_setup_analogue()
|
||||
|
||||
/datum/game_mode/gang/pre_setup()
|
||||
handler = new /datum/gang_handler(antag_candidates,restricted_jobs)
|
||||
return handler.pre_setup_analogue()
|
||||
|
||||
/datum/game_mode/gang/Destroy()
|
||||
QDEL_NULL(handler)
|
||||
return ..()
|
||||
|
||||
/datum/game_mode/gang/post_setup()
|
||||
handler.post_setup_analogue(FALSE)
|
||||
gamemode_ready = TRUE
|
||||
return ..()
|
||||
|
||||
/datum/game_mode/gang/process()
|
||||
handler.process_analogue()
|
||||
|
||||
/datum/game_mode/gang/set_round_result()
|
||||
return handler.set_round_result_analogue()
|
||||
|
||||
/datum/game_mode/gang/generate_report()
|
||||
return "Something something grove street home at least until I fucked everything up idk nobody reads these reports."
|
||||
@@ -1,247 +0,0 @@
|
||||
#define DOM_BLOCKED_SPAM_CAP 6
|
||||
//32 instead of 40 for safety reasons. How many turfs aren't walls around dominator for it to work
|
||||
//Update ppl somehow fuckup at 32, now we are down to 25. I hope to god they don't try harder to wall it.
|
||||
#define DOM_REQUIRED_TURFS 25
|
||||
#define DOM_HULK_HITS_REQUIRED 10
|
||||
|
||||
/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 = TRUE
|
||||
anchored = TRUE
|
||||
layer = HIGH_OBJ_LAYER
|
||||
max_integrity = 300
|
||||
integrity_failure = 0.33
|
||||
armor = list("melee" = 20, "bullet" = 50, "laser" = 50, "energy" = 50, "bomb" = 10, "bio" = 100, "rad" = 100, "fire" = 10, "acid" = 70)
|
||||
var/datum/team/gang/gang
|
||||
var/operating = FALSE //false=standby or broken, true=takeover
|
||||
var/warned = FALSE //if this device has set off the warning at <3 minutes yet
|
||||
var/spam_prevention = DOM_BLOCKED_SPAM_CAP //first message is immediate
|
||||
var/datum/effect_system/spark_spread/spark_system
|
||||
var/obj/effect/countdown/dominator/countdown
|
||||
|
||||
/obj/machinery/dominator/Initialize()
|
||||
. = ..()
|
||||
set_light(l_range = 2, l_power = 0.75)
|
||||
GLOB.poi_list |= src
|
||||
spark_system = new
|
||||
spark_system.set_up(5, TRUE, src)
|
||||
countdown = new(src)
|
||||
update_icon()
|
||||
|
||||
/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(75+severity/4, BURN, "energy", 0)
|
||||
..()
|
||||
|
||||
/obj/machinery/dominator/hulk_damage()
|
||||
return (max_integrity - integrity_failure) / DOM_HULK_HITS_REQUIRED
|
||||
|
||||
/obj/machinery/dominator/update_icon()
|
||||
cut_overlays()
|
||||
if(stat & BROKEN)
|
||||
icon_state = "dominator-broken"
|
||||
return
|
||||
icon_state = "dominator"
|
||||
if(operating)
|
||||
var/mutable_appearance/dominator_overlay = mutable_appearance('icons/obj/machines/dominator.dmi', "dominator-overlay")
|
||||
if(gang)
|
||||
dominator_overlay.color = gang.color
|
||||
add_overlay(dominator_overlay)
|
||||
if(obj_integrity/max_integrity < 0.66)
|
||||
add_overlay("damage")
|
||||
|
||||
/obj/machinery/dominator/examine(mob/user)
|
||||
. = ..()
|
||||
if(stat & BROKEN)
|
||||
return
|
||||
|
||||
if(gang && gang.domination_time != NOT_DOMINATING)
|
||||
if(gang.domination_time > world.time)
|
||||
. += "<span class='notice'>Hostile Takeover in progress. Estimated [gang.domination_time_remaining()] seconds remain.</span>"
|
||||
else
|
||||
. += "<span class='notice'>Hostile Takeover of [station_name()] successful. Have a great day.</span>"
|
||||
else
|
||||
. += "<span class='notice'>System on standby.</span>"
|
||||
. += "<span class='danger'>System Integrity: [round((obj_integrity/max_integrity)*100,1)]%</span>"
|
||||
|
||||
/obj/machinery/dominator/process()
|
||||
..()
|
||||
if(gang && gang.domination_time != NOT_DOMINATING)
|
||||
var/time_remaining = gang.domination_time_remaining()
|
||||
if(time_remaining > 0)
|
||||
if(!is_station_level(z))
|
||||
explosion(src, 5, 10, 20, 30) //you now get a nice explosion if this moves off station.
|
||||
qdel(src) //to make sure it doesn't continue to exist.
|
||||
if(excessive_walls_check())
|
||||
gang.domination_time += 20
|
||||
if(spam_prevention < DOM_BLOCKED_SPAM_CAP)
|
||||
spam_prevention++
|
||||
else
|
||||
playsound(loc, 'sound/machines/buzz-two.ogg', 50, 0) // Play sound buzz-two.ogg, not before cause its annoying.
|
||||
gang.message_gangtools("Warning: There are too many walls around your gang's dominator, its signal is being blocked!")
|
||||
say("Error: Takeover signal is currently blocked! There are too many walls within 3 standard units of this device.")
|
||||
spam_prevention = 0
|
||||
return
|
||||
. = TRUE
|
||||
playsound(loc, 'sound/items/timer.ogg', 10, 0)
|
||||
if(!warned && (time_remaining < 180))
|
||||
warned = TRUE
|
||||
var/area/domloc = get_base_area(loc)
|
||||
gang.message_gangtools("Less than 3 minutes remains in hostile takeover. Defend your dominator at [domloc.map_name]!")
|
||||
for(var/G in GLOB.gangs)
|
||||
var/datum/team/gang/tempgang = G
|
||||
if(tempgang != gang)
|
||||
tempgang.message_gangtools("WARNING: [gang.name] Gang takeover imminent. Their dominator at [domloc.map_name] must be destroyed!",1,1)
|
||||
else
|
||||
endgame()
|
||||
|
||||
if(!.)
|
||||
STOP_PROCESSING(SSmachines, src)
|
||||
|
||||
/obj/machinery/dominator/proc/endgame()
|
||||
set waitfor = FALSE
|
||||
Cinematic(CINEMATIC_MALF,world) //Here is the gang victory trigger on the dominator ending.
|
||||
gang.winner = TRUE
|
||||
SSticker.news_report = GANG_VICTORY
|
||||
SSticker.force_ending = TRUE
|
||||
|
||||
/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()
|
||||
update_icon()
|
||||
|
||||
|
||||
/obj/machinery/dominator/obj_break(damage_flag)
|
||||
if(!(stat & BROKEN) && !(flags_1 & NODECONSTRUCT_1))
|
||||
set_broken()
|
||||
|
||||
/obj/machinery/dominator/deconstruct(disassembled = TRUE)
|
||||
if(!(flags_1 & NODECONSTRUCT_1))
|
||||
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, attackchain_flags = NONE, damage_multiplier = 1)
|
||||
add_fingerprint(user)
|
||||
return ..()
|
||||
|
||||
/obj/machinery/dominator/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
|
||||
if(operating || (stat & BROKEN))
|
||||
examine(user)
|
||||
return
|
||||
|
||||
var/datum/team/gang/tempgang
|
||||
|
||||
var/datum/antagonist/gang/GA = user.mind.has_antag_datum(/datum/antagonist/gang)
|
||||
if(GA)
|
||||
tempgang = GA.gang
|
||||
if(!tempgang)
|
||||
examine(user)
|
||||
return
|
||||
|
||||
if(tempgang.domination_time != NOT_DOMINATING)
|
||||
to_chat(user, "<span class='warning'>Error: Hostile Takeover is already in progress.</span>")
|
||||
return
|
||||
|
||||
if(!tempgang.dom_attempts)
|
||||
to_chat(user, "<span class='warning'>Error: Unable to breach station network. Firewall has logged our signature and is blocking all further attempts.</span>")
|
||||
return
|
||||
|
||||
var/time = round(tempgang.determine_domination_time()/60,0.1)
|
||||
if(alert(user,"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.domination_time != NOT_DOMINATING) || !tempgang.dom_attempts || !in_range(src, user) || !isturf(loc))
|
||||
return 0
|
||||
|
||||
var/area/A = get_base_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)
|
||||
name = "[gang.name] Gang [name]"
|
||||
operating = TRUE
|
||||
update_icon()
|
||||
|
||||
countdown.start()
|
||||
countdown.color = gang.color
|
||||
|
||||
set_light(l_range = 3, l_power = 0.9)
|
||||
light_color = gang.color
|
||||
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/G in GLOB.gangs)
|
||||
var/datum/team/gang/vagos = G
|
||||
if(vagos != gang)
|
||||
vagos.message_gangtools("Enemy takeover attempt detected in [locname]: Estimated [time] minutes until our defeat.",1,1)
|
||||
|
||||
/obj/machinery/dominator/proc/excessive_walls_check() // why the fuck was this even a global proc...
|
||||
var/open = 0
|
||||
for(var/turf/T in view(3, src))
|
||||
if(!iswallturf(T)) //Check for /closed/wall, isclosedturf() moves it back to just checking for /closed/ which makes it very finicky.
|
||||
open++
|
||||
//to_chat(world, "THE DOMINATOR SEES [open] OPEN TURFS") uncomment to see what this shitty fucking wallcheck sees
|
||||
if(open < DOM_REQUIRED_TURFS)
|
||||
return TRUE
|
||||
else
|
||||
return FALSE
|
||||
/obj/machinery/dominator/proc/set_broken()
|
||||
if(gang)
|
||||
gang.domination_time = NOT_DOMINATING
|
||||
|
||||
var/takeover_in_progress = FALSE
|
||||
for(var/G in GLOB.gangs)
|
||||
var/datum/team/gang/ballas = G
|
||||
if(ballas.domination_time != NOT_DOMINATING)
|
||||
takeover_in_progress = TRUE
|
||||
break
|
||||
if(!takeover_in_progress)
|
||||
var/was_stranded = SSshuttle.emergency.mode == SHUTTLE_STRANDED
|
||||
if(!was_stranded)
|
||||
priority_announce("All hostile activity within station systems has ceased.","Network Alert")
|
||||
|
||||
if(NUM2SECLEVEL(GLOB.security_level) == "delta")
|
||||
set_security_level("red")
|
||||
|
||||
SSshuttle.clearHostileEnvironment(src)
|
||||
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)
|
||||
operating = FALSE
|
||||
stat |= BROKEN
|
||||
update_icon()
|
||||
STOP_PROCESSING(SSmachines, src)
|
||||
|
||||
#undef DOM_BLOCKED_SPAM_CAP
|
||||
#undef DOM_REQUIRED_TURFS
|
||||
#undef DOM_HULK_HITS_REQUIRED
|
||||
@@ -1,13 +0,0 @@
|
||||
/obj/effect/countdown/dominator
|
||||
name = "dominator countdown"
|
||||
text_size = 1
|
||||
color = "#e5e5e5" // 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.domination_time != NOT_DOMINATING)
|
||||
return D.gang.domination_time_remaining()
|
||||
else
|
||||
return "OFFLINE"
|
||||
@@ -1,479 +0,0 @@
|
||||
/datum/antagonist/gang
|
||||
name = "Gangster"
|
||||
roundend_category = "gangsters"
|
||||
can_coexist_with_others = FALSE
|
||||
job_rank = ROLE_GANG
|
||||
antagpanel_category = "Gang"
|
||||
threat = 2
|
||||
var/hud_type = "gangster"
|
||||
var/message_name = "Gangster"
|
||||
var/datum/team/gang/gang
|
||||
|
||||
/datum/antagonist/gang/can_be_owned(datum/mind/new_owner)
|
||||
. = ..()
|
||||
if(.)
|
||||
if(new_owner.unconvertable)
|
||||
return FALSE
|
||||
|
||||
/datum/antagonist/gang/apply_innate_effects(mob/living/mob_override)
|
||||
var/mob/living/M = mob_override || owner.current
|
||||
update_gang_icons_added(M)
|
||||
|
||||
/datum/antagonist/gang/remove_innate_effects(mob/living/mob_override)
|
||||
var/mob/living/M = mob_override || owner.current
|
||||
update_gang_icons_removed(M)
|
||||
|
||||
/datum/antagonist/gang/get_team()
|
||||
return gang
|
||||
|
||||
/datum/antagonist/gang/greet()
|
||||
gang.greet_gangster(owner)
|
||||
|
||||
/datum/antagonist/gang/farewell()
|
||||
if(ishuman(owner.current))
|
||||
owner.current.visible_message("<span class='deconversion_message'>[owner.current] looks like [owner.current.p_theyve()] just remembered [owner.current.p_their()] real allegiance!</span>", null, null, null, owner.current)
|
||||
to_chat(owner, "<span class='userdanger'>You are no longer a gangster! Your memories from the time you were in a gang are hazy... You don't seem to be able to recall the names of your previous allies, not even your bosses...</span>")
|
||||
|
||||
/datum/antagonist/gang/on_gain()
|
||||
if(!gang)
|
||||
create_team()
|
||||
..()
|
||||
var/mob/living/carbon/human/H = owner.current
|
||||
if(istype(H))
|
||||
if(owner.assigned_role == "Clown")
|
||||
to_chat(owner, "Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself.")
|
||||
H.dna.remove_mutation(CLOWNMUT)
|
||||
add_to_gang()
|
||||
|
||||
/datum/antagonist/gang/on_removal()
|
||||
remove_from_gang()
|
||||
..()
|
||||
|
||||
/datum/antagonist/gang/create_team(team)
|
||||
if(!gang) // add_antag_datum calls create_team, so we need to avoid generating two gangs in that case
|
||||
if(team)
|
||||
gang = team
|
||||
return
|
||||
var/datum/team/gang/gangteam = pick_n_take(GLOB.possible_gangs)
|
||||
if(gangteam)
|
||||
gang = new gangteam
|
||||
|
||||
/datum/antagonist/gang/proc/equip_gang() // Bosses get equipped with their tools
|
||||
return
|
||||
|
||||
/datum/antagonist/gang/proc/update_gang_icons_added(mob/living/M)
|
||||
var/datum/atom_hud/antag/gang/ganghud = GLOB.huds[gang.hud_entry_num]
|
||||
if(!ganghud)
|
||||
ganghud = new/datum/atom_hud/antag/gang()
|
||||
gang.hud_entry_num = GLOB.huds.len+1 // this is the index the gang hud will be added at
|
||||
GLOB.huds += ganghud
|
||||
ganghud.color = gang.color
|
||||
ganghud.join_hud(M)
|
||||
set_antag_hud(M,hud_type)
|
||||
|
||||
/datum/antagonist/gang/proc/update_gang_icons_removed(mob/living/M)
|
||||
var/datum/atom_hud/antag/gang/ganghud = GLOB.huds[gang.hud_entry_num]
|
||||
if(ganghud)
|
||||
ganghud.leave_hud(M)
|
||||
set_antag_hud(M, null)
|
||||
|
||||
/datum/antagonist/gang/proc/can_be_converted(mob/living/candidate)
|
||||
if(!candidate.mind)
|
||||
return FALSE
|
||||
if(!can_be_owned(candidate.mind))
|
||||
return FALSE
|
||||
var/mob/living/carbon/human/H = candidate
|
||||
if(!istype(H)) //Can't nonhumans
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/datum/antagonist/gang/proc/promote() // Bump up to boss
|
||||
var/datum/team/gang/old_gang = gang
|
||||
var/datum/mind/old_owner = owner
|
||||
owner.remove_antag_datum(/datum/antagonist/gang)
|
||||
var/datum/antagonist/gang/boss/lieutenant/new_boss = new
|
||||
new_boss.silent = TRUE
|
||||
old_owner.add_antag_datum(new_boss,old_gang)
|
||||
new_boss.silent = FALSE
|
||||
log_game("[key_name(old_owner)] has been promoted to Lieutenant in the [old_gang.name] Gang")
|
||||
to_chat(old_owner, "<FONT size=3 color=red><B>You have been promoted to Lieutenant!</B></FONT>")
|
||||
|
||||
|
||||
// Admin commands
|
||||
/datum/antagonist/gang/get_admin_commands()
|
||||
. = ..()
|
||||
.["Promote"] = CALLBACK(src,.proc/admin_promote)
|
||||
.["Set Influence"] = CALLBACK(src, .proc/admin_adjust_influence)
|
||||
if(gang.domination_time != NOT_DOMINATING)
|
||||
.["Set domination time left"] = CALLBACK(src, .proc/set_dom_time_left)
|
||||
|
||||
/datum/antagonist/gang/admin_add(datum/mind/new_owner,mob/admin)
|
||||
var/new_or_existing = input(admin, "Which gang do you want to be assigned to the user?", "Gangs") as null|anything in list("New","Existing")
|
||||
if(isnull(new_or_existing))
|
||||
return
|
||||
else if(new_or_existing == "New")
|
||||
var/newgang = input(admin, "Select a gang, or select random to pick a random one.", "New gang") as null|anything in GLOB.possible_gangs + "Random"
|
||||
if(isnull(newgang))
|
||||
return
|
||||
else if(newgang == "Random")
|
||||
var/datum/team/gang/G = pick_n_take(GLOB.possible_gangs)
|
||||
gang = new G
|
||||
else
|
||||
GLOB.possible_gangs -= newgang
|
||||
gang = new newgang
|
||||
else
|
||||
if(!GLOB.gangs.len) // no gangs exist
|
||||
to_chat(admin, "<span class='danger'>No gangs exist, please create a new one instead.</span>")
|
||||
return
|
||||
var/existinggang = input(admin, "Select a gang, or select random to pick a random one.", "Existing gang") as null|anything in GLOB.gangs + "Random"
|
||||
if(isnull(existinggang))
|
||||
return
|
||||
else if(existinggang == "Random")
|
||||
gang = pick(GLOB.gangs)
|
||||
else
|
||||
gang = existinggang
|
||||
..()
|
||||
return TRUE
|
||||
|
||||
/datum/antagonist/gang/proc/admin_promote(mob/admin)
|
||||
message_admins("[key_name_admin(admin)] has promoted [owner] to gang boss.")
|
||||
log_admin("[key_name(admin)] has promoted [owner] to boss.")
|
||||
promote()
|
||||
|
||||
/datum/antagonist/gang/proc/admin_adjust_influence()
|
||||
var/inf = input("Influence for [gang.name]","Gang influence", gang.influence) as null | num
|
||||
if(!isnull(inf))
|
||||
gang.influence = inf
|
||||
message_admins("[key_name_admin(usr)] changed [gang.name]'s influence to [inf].")
|
||||
log_admin("[key_name(usr)] changed [gang.name]'s influence to [inf].")
|
||||
|
||||
/datum/antagonist/gang/proc/add_to_gang()
|
||||
gang.add_member(owner)
|
||||
owner.current.log_message("<font color='red'>Has been converted to the [gang.name] gang!</font>", INDIVIDUAL_ATTACK_LOG)
|
||||
|
||||
/datum/antagonist/gang/proc/remove_from_gang()
|
||||
gang.remove_member(owner)
|
||||
owner.current.log_message("<font color='red'>Has been deconverted from the [gang.name] gang!</font>", INDIVIDUAL_ATTACK_LOG)
|
||||
|
||||
/datum/antagonist/gang/proc/set_dom_time_left(mob/admin)
|
||||
if(gang.domination_time == NOT_DOMINATING)
|
||||
return // an admin shouldn't need this
|
||||
var/seconds = input(admin, "Set the time left for the gang to win, in seconds", "Domination time left") as null|num
|
||||
if(seconds && seconds > 0)
|
||||
gang.domination_time = world.time + seconds*10
|
||||
gang.message_gangtools("Takeover shortened to [gang.domination_time_remaining()] seconds by your Syndicate benefactors.")
|
||||
|
||||
// Boss type. Those can use gang tools to buy items for their gang, in particular the Dominator, used to win the gamemode, along with more gang tools to promote fellow gangsters to boss status.
|
||||
/datum/antagonist/gang/boss
|
||||
name = "Gang boss"
|
||||
hud_type = "gang_boss"
|
||||
message_name = "Leader"
|
||||
threat = 10
|
||||
|
||||
/datum/antagonist/gang/boss/on_gain()
|
||||
..()
|
||||
if(gang)
|
||||
gang.leaders += owner
|
||||
|
||||
/datum/antagonist/gang/boss/on_removal()
|
||||
if(gang)
|
||||
gang.leaders -= owner
|
||||
..()
|
||||
|
||||
/datum/antagonist/gang/boss/antag_listing_name()
|
||||
return ..() + "(Boss)"
|
||||
|
||||
/datum/antagonist/gang/boss/equip_gang(gangtool = TRUE, pen = TRUE, spraycan = TRUE, hud = TRUE) // usually has to be called separately
|
||||
var/mob/living/carbon/human/H = owner.current
|
||||
if(!istype(H))
|
||||
return
|
||||
|
||||
var/list/slots = list (
|
||||
"backpack" = SLOT_IN_BACKPACK,
|
||||
"left pocket" = SLOT_L_STORE,
|
||||
"right pocket" = SLOT_R_STORE,
|
||||
"hands" = SLOT_HANDS
|
||||
)
|
||||
|
||||
if(gangtool)//Here is where all of the text occurs when a gang boss first spawns in.
|
||||
var/obj/item/device/gangtool/G = new()
|
||||
var/where = H.equip_in_one_of_slots(G, slots, critical = TRUE)
|
||||
if (!where)
|
||||
to_chat(H, "Your Syndicate benefactors were unfortunately unable to get you a Gangtool.")
|
||||
else
|
||||
G.register_device(H)
|
||||
to_chat(H, "The <b>Gangtool</b> 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(H, "As the gang boss, you can also promote your gang members to <b>lieutenant</b>. Unlike regular gangsters, Lieutenants cannot be deconverted and are able to use gangtools too.")
|
||||
|
||||
if(pen)
|
||||
var/obj/item/pen/gang/T = new()
|
||||
var/where2 = H.equip_in_one_of_slots(T, slots, critical = TRUE)
|
||||
if (!where2)
|
||||
to_chat(H, "Your Syndicate benefactors were unfortunately unable to get you a recruitment pen to start.")
|
||||
else
|
||||
to_chat(H, "The <b>recruitment pen</b> in your [where2] will help you get your gang started. Stab unsuspecting crew members with it to recruit them. All gangsters can use these, distribute them to see your gang grow.")
|
||||
|
||||
if(spraycan)
|
||||
var/obj/item/toy/crayon/spraycan/gang/SC = new(null,gang)
|
||||
var/where3 = H.equip_in_one_of_slots(SC, slots, critical = TRUE)
|
||||
if (!where3)
|
||||
to_chat(H, "Your Syndicate benefactors were unfortunately unable to get you a territory spraycan to start.")
|
||||
else
|
||||
to_chat(H, "The <b>territory spraycan</b> 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.")
|
||||
|
||||
if(hud)
|
||||
var/obj/item/clothing/glasses/hud/security/chameleon/C = new(null,gang)
|
||||
var/where4 = H.equip_in_one_of_slots(C, slots, critical = TRUE)
|
||||
if (!where4)
|
||||
to_chat(H, "Your Syndicate benefactors were unfortunately unable to get you a chameleon security HUD.")
|
||||
else
|
||||
to_chat(H, "The <b>chameleon security HUD</b> in your [where4] will help you keep track of who is mindshield-implanted, and unable to be recruited.")
|
||||
|
||||
// Admin commands for bosses
|
||||
/datum/antagonist/gang/boss/admin_add(datum/mind/new_owner,mob/admin)
|
||||
if(!new_owner.has_antag_datum(parent_type))
|
||||
..()
|
||||
to_chat(new_owner.current, "<span class='userdanger'>You are a member of the [gang.name] Gang leadership now!</span>")
|
||||
return
|
||||
promote()
|
||||
message_admins("[key_name_admin(admin)] has made [new_owner.current] a boss of the [gang.name] gang.")
|
||||
log_admin("[key_name(admin)] has made [new_owner.current] a boss of the [gang.name] gang.")
|
||||
to_chat(new_owner.current, "<span class='userdanger'>You are a member of the [gang.name] Gang leadership now!</span>")
|
||||
|
||||
/datum/antagonist/gang/boss/get_admin_commands()
|
||||
. = ..()
|
||||
. -= "Promote"
|
||||
.["Take gangtool"] = CALLBACK(src,.proc/admin_take_gangtool)
|
||||
.["Give gangtool"] = CALLBACK(src,.proc/admin_give_gangtool)
|
||||
.["Demote"] = CALLBACK(src,.proc/admin_demote)
|
||||
|
||||
/datum/antagonist/gang/boss/proc/demote()
|
||||
var/old_gang = gang
|
||||
var/datum/mind/old_owner = owner
|
||||
silent = TRUE
|
||||
owner.remove_antag_datum(/datum/antagonist/gang/boss)
|
||||
var/datum/antagonist/gang/new_gangster = new /datum/antagonist/gang()
|
||||
new_gangster.silent = TRUE
|
||||
old_owner.add_antag_datum(new_gangster,old_gang)
|
||||
new_gangster.silent = FALSE
|
||||
log_game("[key_name(old_owner)] has been demoted to Gangster in the [gang.name] Gang")
|
||||
to_chat(old_owner, "<span class='userdanger'>The gang has been disappointed of your leader traits! You are a regular gangster now!</span>")
|
||||
|
||||
/datum/antagonist/gang/boss/proc/admin_take_gangtool(mob/admin)
|
||||
var/list/L = owner.current.get_contents()
|
||||
var/obj/item/device/gangtool/gangtool = locate() in L
|
||||
if (!gangtool)
|
||||
to_chat(admin, "<span class='danger'>Deleting gangtool failed!</span>")
|
||||
return
|
||||
qdel(gangtool)
|
||||
|
||||
/datum/antagonist/gang/boss/proc/admin_give_gangtool(mob/admin)
|
||||
equip_gang(TRUE, FALSE, FALSE, FALSE)
|
||||
|
||||
/datum/antagonist/gang/boss/proc/admin_demote(datum/mind/target,mob/user)
|
||||
message_admins("[key_name_admin(user)] has demoted [owner.current] from gang boss.")
|
||||
log_admin("[key_name(user)] has demoted [owner.current] from gang boss.")
|
||||
admin_take_gangtool(user)
|
||||
demote()
|
||||
|
||||
/datum/antagonist/gang/boss/lieutenant
|
||||
name = "Gang Lieutenant"
|
||||
message_name = "Lieutenant"
|
||||
hud_type = "gang_lt"
|
||||
|
||||
#define MAXIMUM_RECALLS 3
|
||||
#define INFLUENCE_INTERVAL 1200 //This handles the interval between each count of influence.
|
||||
// Gang team datum. This handles the gang itself.
|
||||
/datum/team/gang
|
||||
name = "Gang"
|
||||
member_name = "gangster"
|
||||
var/hud_entry_num // because if you put something other than a number in GLOB.huds, god have mercy on your fucking soul friend
|
||||
var/list/leaders = list() // bosses
|
||||
var/max_leaders = MAX_LEADERS_GANG
|
||||
var/list/territories = list() // territories owned by the gang.
|
||||
var/list/lost_territories = list() // territories lost by the gang.
|
||||
var/list/new_territories = list() // territories captured by the gang.
|
||||
var/list/gangtools = list()
|
||||
var/domination_time = NOT_DOMINATING
|
||||
var/dom_attempts = INITIAL_DOM_ATTEMPTS
|
||||
var/color
|
||||
var/influence = 0 // influence of the gang, based on how many territories they own. Can be used to buy weapons and tools from a gang uplink.
|
||||
var/winner // Once the gang wins with a dominator, this becomes true. For roundend credits purposes.
|
||||
var/list/inner_outfits = list()
|
||||
var/list/outer_outfits = list()
|
||||
var/next_point_time
|
||||
var/recalls = MAXIMUM_RECALLS // Once this reaches 0, this gang cannot force recall the shuttle with their gangtool anymore
|
||||
|
||||
/datum/team/gang/New(starting_members)
|
||||
. = ..()
|
||||
GLOB.gangs += src
|
||||
if(starting_members)
|
||||
if(islist(starting_members))
|
||||
for(var/datum/mind/groveboss in starting_members)
|
||||
leaders += groveboss
|
||||
var/datum/antagonist/gang/boss/gb = new
|
||||
groveboss.add_antag_datum(gb, src)
|
||||
gb.equip_gang()
|
||||
|
||||
else
|
||||
var/datum/mind/CJ = starting_members
|
||||
if(istype(CJ))
|
||||
leaders += CJ
|
||||
var/datum/antagonist/gang/boss/bossdatum = new
|
||||
CJ.add_antag_datum(bossdatum, src)
|
||||
bossdatum.equip_gang()
|
||||
next_point_time = world.time + INFLUENCE_INTERVAL
|
||||
addtimer(CALLBACK(src, .proc/handle_territories), INFLUENCE_INTERVAL)
|
||||
|
||||
/datum/team/gang/Destroy()
|
||||
GLOB.gangs -= src
|
||||
..()
|
||||
|
||||
/datum/team/gang/roundend_report() //roundend report.
|
||||
var/list/report = list()
|
||||
report += "<span class='header'>[name]:</span>"
|
||||
if(winner)
|
||||
report += "<span class='greentext'>The [name] gang successfully activated the mind dominator!</span>"
|
||||
else
|
||||
report += "<span class='redtext'>The [name] gang has failed!</span>"
|
||||
|
||||
report += "The [name] gang bosses were:"
|
||||
report += printplayerlist(leaders)
|
||||
report += "The [name] [member_name]s were:"
|
||||
report += printplayerlist(members-leaders)
|
||||
|
||||
return "<div class='panel redborder'>[report.Join("<br>")]</div>"
|
||||
|
||||
/datum/team/gang/proc/greet_gangster(datum/mind/gangster) //The text a person receives when recruited.
|
||||
var/message = "<FONT size=3 color=red><B>You are now a member of the <font color='[color]'>[name]</font> Gang!</B></FONT>"
|
||||
message += "<font color='red'>Help your bosses take over the station by claiming territory with <b>spraycans</b>. Simply spray on any unclaimed area of the station.</font>"
|
||||
message += "<font color='red'>You can also use recruitment pens to recruit more to your cause, If your boss provides you one.</font>"
|
||||
message += "<font color='red'>Their ultimate objective is to take over the station with a Dominator machine.</font>"
|
||||
message += "<font color='red'>You can identify your mates by their <b>large, <font color='[color]'> \[G\]</font> icon</b>.</font>"
|
||||
to_chat(gangster, message)
|
||||
gangster.store_memory("You are a member of the [name] Gang!")
|
||||
|
||||
/datum/team/gang/proc/handle_territories()
|
||||
next_point_time = world.time + INFLUENCE_INTERVAL
|
||||
if(!leaders.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 = new_territories & lost_territories
|
||||
territories |= reclaimed_territories
|
||||
new_territories -= reclaimed_territories
|
||||
lost_territories -= reclaimed_territories
|
||||
|
||||
//Process lost territories
|
||||
for(var/area in lost_territories)
|
||||
if(lost_names != "")
|
||||
lost_names += ", "
|
||||
lost_names += "[lost_territories[area]]"
|
||||
territories -= area
|
||||
|
||||
//Calculate and report influence growth
|
||||
|
||||
//Process new territories
|
||||
for(var/area in new_territories)
|
||||
if(added_names != "")
|
||||
added_names += ", "
|
||||
added_names += "[new_territories[area]]"
|
||||
territories += area
|
||||
|
||||
//Report territory changes
|
||||
var/message = "<b>[src] Gang Status Report:</b>.<BR>*---------*<BR>"
|
||||
message += "<b>[new_territories.len] new territories:</b><br><i>[added_names]</i><br>"
|
||||
message += "<b>[lost_territories.len] territories lost:</b><br><i>[lost_names]</i><br>"
|
||||
//Clear the lists
|
||||
new_territories = list()
|
||||
lost_territories = list()
|
||||
var/total_territories = total_claimable_territories()
|
||||
var/control = round((territories.len/total_territories)*100, 1)
|
||||
var/uniformed = check_clothing()
|
||||
message += "Your gang now has <b>[control]% control</b> of the station.<BR>*---------*<BR>"
|
||||
if(domination_time != NOT_DOMINATING)
|
||||
var/new_time = max(world.time, domination_time - (uniformed * 4) - (territories.len * 2))
|
||||
if(new_time < domination_time)
|
||||
message += "Takeover shortened by [(domination_time - new_time)*0.1] seconds for defending [territories.len] territories.<BR>"
|
||||
domination_time = new_time
|
||||
message += "<b>[domination_time_remaining()] seconds remain</b> in hostile takeover.<BR>"
|
||||
else
|
||||
var/new_influence = check_territory_income()
|
||||
if(new_influence != influence)
|
||||
message += "Gang influence has increased by [new_influence - influence] for defending [territories.len] territories and [uniformed] uniformed gangsters.<BR>"
|
||||
influence = new_influence
|
||||
message += "Your gang now has <b>[influence] influence</b>.<BR>"
|
||||
message_gangtools(message)
|
||||
addtimer(CALLBACK(src, .proc/handle_territories), INFLUENCE_INTERVAL)
|
||||
|
||||
/datum/team/gang/proc/total_claimable_territories()
|
||||
var/list/valid_territories = list()
|
||||
for(var/z in SSmapping.levels_by_trait(ZTRAIT_STATION)) //First, collect all area types on the station zlevel
|
||||
for(var/ar in SSmapping.areas_in_z["[z]"])
|
||||
var/area/A = ar
|
||||
if(!(A.type in valid_territories) && (A.area_flags & VALID_TERRITORY))
|
||||
valid_territories |= A.type
|
||||
return valid_territories.len
|
||||
|
||||
/datum/team/gang/proc/check_territory_income()
|
||||
var/new_influence = min(999,influence + 15 + (check_clothing() * 2) + territories.len)
|
||||
return new_influence
|
||||
|
||||
/datum/team/gang/proc/check_clothing()
|
||||
//Count uniformed gangsters
|
||||
var/uniformed = 0
|
||||
for(var/datum/mind/gangmind in members)
|
||||
if(ishuman(gangmind.current))
|
||||
var/mob/living/carbon/human/gangster = gangmind.current
|
||||
//Gangster must be alive and should return 0 not continue if conditions are met.
|
||||
if(!istype(gangster) || gangster.stat == DEAD)
|
||||
return 0
|
||||
|
||||
var/obj/item/clothing/outfit
|
||||
var/obj/item/clothing/gang_outfit
|
||||
if(gangster.w_uniform)
|
||||
outfit = gangster.w_uniform
|
||||
if(outfit.type in inner_outfits)
|
||||
gang_outfit = outfit
|
||||
if(gangster.wear_suit)
|
||||
outfit = gangster.wear_suit
|
||||
if(outfit.type in outer_outfits)
|
||||
gang_outfit = outfit
|
||||
|
||||
if(gang_outfit)
|
||||
uniformed++
|
||||
return uniformed
|
||||
|
||||
/datum/team/gang/proc/adjust_influence(value)
|
||||
influence = max(0, influence + value)
|
||||
|
||||
/datum/team/gang/proc/message_gangtools(message)
|
||||
if(!gangtools.len || !message)
|
||||
return
|
||||
for(var/i in gangtools)
|
||||
var/obj/item/device/gangtool/tool = i
|
||||
var/mob/living/mob = get(tool.loc, /mob/living)
|
||||
if(mob && mob.mind && mob.stat == CONSCIOUS)
|
||||
var/datum/antagonist/gang/gangster = mob.mind.has_antag_datum(/datum/antagonist/gang)
|
||||
if(gangster.gang == src)
|
||||
to_chat(mob, "<span class='warning'>[icon2html(tool, mob)] [message]</span>")
|
||||
playsound(mob.loc, 'sound/machines/twobeep.ogg', 50, 1)
|
||||
return
|
||||
|
||||
/datum/team/gang/proc/domination()
|
||||
domination_time = world.time + determine_domination_time()*10
|
||||
set_security_level("delta")
|
||||
|
||||
/datum/team/gang/proc/determine_domination_time() // calculates the value in seconds (this is the initial domination time!)
|
||||
var/total_territories = total_claimable_territories()
|
||||
return max(180,480 - (round((territories.len/total_territories)*100, 1) * 9))
|
||||
|
||||
/datum/team/gang/proc/domination_time_remaining() // retrieves the value from world.time based deciseconds to seconds
|
||||
var/diff = domination_time - world.time
|
||||
return round(diff * 0.1)
|
||||
|
||||
|
||||
#undef MAXIMUM_RECALLS
|
||||
#undef INFLUENCE_INTERVAL
|
||||
@@ -1,139 +0,0 @@
|
||||
// Gang datums go here. If you want to create a new gang, you must be sure to edit:
|
||||
// name
|
||||
// color (must be a hex, "blue" isn't acceptable due to how spraycans are handled)
|
||||
// inner_outfits (must be a list() with typepaths of the clothes in it. One is fine, but there is support for multiple: one will be picked at random when bought)
|
||||
// outer_outfits (same as above)
|
||||
// You also need to make a gang graffiti, that will go in crayondecal.dmi inside our icons, with the same name of the gang it's assigned to. Nothing else,just the icon.
|
||||
// Those are all required. If one is missed, stuff could break.
|
||||
|
||||
/datum/team/gang/clandestine
|
||||
name = "Clandestine"
|
||||
color = "#FF0000"
|
||||
inner_outfits = list(/obj/item/clothing/under/syndicate/combat)
|
||||
outer_outfits = list(/obj/item/clothing/suit/jacket)
|
||||
|
||||
/datum/team/gang/prima
|
||||
name = "Prima"
|
||||
color = "#FFFF00"
|
||||
inner_outfits = list(/obj/item/clothing/under/color/yellow)
|
||||
outer_outfits = list(/obj/item/clothing/suit/hastur)
|
||||
|
||||
/datum/team/gang/zerog
|
||||
name = "Zero-G"
|
||||
color = "#C0C0C0"
|
||||
inner_outfits = list(/obj/item/clothing/under/suit/white)
|
||||
outer_outfits = list(/obj/item/clothing/suit/hooded/wintercoat)
|
||||
|
||||
/datum/team/gang/max
|
||||
name = "Max"
|
||||
color = "#800000"
|
||||
inner_outfits = list(/obj/item/clothing/under/color/maroon)
|
||||
outer_outfits = list(/obj/item/clothing/suit/poncho/red)
|
||||
|
||||
/datum/team/gang/blasto
|
||||
name = "Blasto"
|
||||
color = "#000080"
|
||||
inner_outfits = list(/obj/item/clothing/under/suit/navy)
|
||||
outer_outfits = list(/obj/item/clothing/suit/jacket/miljacket)
|
||||
|
||||
/datum/team/gang/waffle
|
||||
name = "Waffle"
|
||||
color = "#808000" //shared color with cyber, but they can keep brown cause waffles.
|
||||
inner_outfits = list(/obj/item/clothing/under/suit/green)
|
||||
outer_outfits = list(/obj/item/clothing/suit/poncho)
|
||||
|
||||
/datum/team/gang/north
|
||||
name = "North"
|
||||
color = "#00FF00"
|
||||
inner_outfits = list(/obj/item/clothing/under/color/green)
|
||||
outer_outfits = list(/obj/item/clothing/suit/poncho/green)
|
||||
|
||||
/datum/team/gang/omni
|
||||
name = "Omni"
|
||||
color = "#008080"
|
||||
inner_outfits = list(/obj/item/clothing/under/color/teal)
|
||||
outer_outfits = list(/obj/item/clothing/suit/chaplain/studentuni)
|
||||
|
||||
/datum/team/gang/newton
|
||||
name = "Newton"
|
||||
color = "#A52A2A"
|
||||
inner_outfits = list(/obj/item/clothing/under/color/brown)
|
||||
outer_outfits = list(/obj/item/clothing/suit/toggle/owlwings)
|
||||
|
||||
/datum/team/gang/cyber
|
||||
name = "Cyber"
|
||||
color = "#00f904" //Cyber and waffle shared colors, I made these guys green and made weed darker green.
|
||||
inner_outfits = list(/obj/item/clothing/under/color/lightbrown)
|
||||
outer_outfits = list(/obj/item/clothing/suit/chaplain/pharaoh)
|
||||
|
||||
/datum/team/gang/donk
|
||||
name = "Donk"
|
||||
color = "#0000FF"
|
||||
inner_outfits = list(/obj/item/clothing/under/color/darkblue)
|
||||
outer_outfits = list(/obj/item/clothing/suit/apron/overalls)
|
||||
|
||||
/datum/team/gang/gene
|
||||
name = "Gene"
|
||||
color = "#00FFFF"
|
||||
inner_outfits = list(/obj/item/clothing/under/color/blue)
|
||||
outer_outfits = list(/obj/item/clothing/suit/apron)
|
||||
|
||||
/datum/team/gang/gib
|
||||
name = "Gib"
|
||||
color = "#636060" //Applying black to grayscale... Zero-G is already grey too. oh well.
|
||||
inner_outfits = list(/obj/item/clothing/under/color/black)
|
||||
outer_outfits = list(/obj/item/clothing/suit/jacket/leather/overcoat)
|
||||
|
||||
/datum/team/gang/tunnel
|
||||
name = "Tunnel"
|
||||
color = "#FF00FF" //Gave the leather jacket to the tunnel gang over diablo.
|
||||
inner_outfits = list(/obj/item/clothing/under/costume/villain)
|
||||
outer_outfits = list(/obj/item/clothing/suit/jacket/leather)
|
||||
|
||||
/datum/team/gang/diablo
|
||||
name = "Diablo"
|
||||
color = "#FF0000" //literal early 90s skinhead regalia.
|
||||
inner_outfits = list(/obj/item/clothing/under/pants/classicjeans)
|
||||
outer_outfits = list(/obj/item/clothing/suit/suspenders)
|
||||
|
||||
/datum/team/gang/psyke
|
||||
name = "Psyke"
|
||||
color = "#808080"
|
||||
inner_outfits = list(/obj/item/clothing/under/color/grey)
|
||||
outer_outfits = list(/obj/item/clothing/suit/toggle/owlwings/griffinwings)
|
||||
|
||||
/datum/team/gang/osiron
|
||||
name = "Osiron"
|
||||
color = "#FFFFFF"
|
||||
inner_outfits = list(/obj/item/clothing/under/color/white)
|
||||
outer_outfits = list(/obj/item/clothing/suit/toggle/labcoat)
|
||||
|
||||
/datum/team/gang/sirius
|
||||
name = "Sirius"
|
||||
color = "#FFC0CB"
|
||||
inner_outfits = list(/obj/item/clothing/under/color/pink)
|
||||
outer_outfits = list(/obj/item/clothing/suit/jacket/puffer/vest)
|
||||
|
||||
/datum/team/gang/sleepingcarp
|
||||
name = "Sleeping Carp"
|
||||
color = "#800080"
|
||||
inner_outfits = list(/obj/item/clothing/under/color/lightpurple)
|
||||
outer_outfits = list(/obj/item/clothing/suit/hooded/carp_costume)
|
||||
|
||||
/datum/team/gang/h
|
||||
name = "H"
|
||||
color = "#993333"
|
||||
inner_outfits = list(/obj/item/clothing/under/costume/jabroni) //Why not?
|
||||
outer_outfits = list(/obj/item/clothing/suit/toggle/owlwings)
|
||||
|
||||
/datum/team/gang/rigatonifamily
|
||||
name = "Rigatoni family"
|
||||
color = "#cc9900" // p a s t a colored
|
||||
inner_outfits = list(/obj/item/clothing/under/rank/civilian/chef)
|
||||
outer_outfits = list(/obj/item/clothing/suit/apron/chef)
|
||||
|
||||
/datum/team/gang/weed
|
||||
name = "Weed"
|
||||
color = "#6cd648"
|
||||
inner_outfits = list(/obj/item/clothing/under/color/darkgreen)
|
||||
outer_outfits = list(/obj/item/clothing/suit/vapeshirt)
|
||||
@@ -1,38 +0,0 @@
|
||||
/obj/effect/decal/cleanable/crayon/Initialize(mapload, main, type, e_name, graf_rot, alt_icon = null)
|
||||
. = ..()
|
||||
if(type == "poseur tag")
|
||||
var/datum/team/gang/gang = pick(subtypesof(/datum/team/gang))
|
||||
var/gangname = initial(gang.name)
|
||||
icon = 'icons/effects/crayondecal.dmi'
|
||||
icon_state = "[gangname]"
|
||||
type = null
|
||||
|
||||
/obj/effect/decal/cleanable/crayon/gang
|
||||
icon = 'icons/effects/crayondecal.dmi'
|
||||
layer = ABOVE_NORMAL_TURF_LAYER //Harder to hide
|
||||
plane = ABOVE_WALL_PLANE
|
||||
do_icon_rotate = FALSE //These are designed to always face south, so no rotation please.
|
||||
var/datum/team/gang/gang
|
||||
|
||||
/obj/effect/decal/cleanable/crayon/gang/Initialize(mapload, datum/team/gang/G, e_name = "gang tag", rotation = 0, mob/user)
|
||||
if(!G)
|
||||
return INITIALIZE_HINT_QDEL
|
||||
gang = G
|
||||
var/newcolor = G.color
|
||||
var/area/territory = get_base_area(src)
|
||||
icon_state = G.name
|
||||
G.new_territories |= list(territory.type = territory.name)
|
||||
//If this isn't tagged by a specific gangster there's no bonus income.
|
||||
.=..(mapload, newcolor, icon_state, e_name, rotation)
|
||||
|
||||
/obj/effect/decal/cleanable/crayon/gang/Destroy()
|
||||
if(gang)
|
||||
var/area/territory = get_base_area(src)
|
||||
gang.territories -= territory.type
|
||||
gang.new_territories -= territory.type
|
||||
gang.lost_territories |= list(territory.type = territory.name)
|
||||
gang = null
|
||||
return ..()
|
||||
|
||||
/obj/effect/decal/cleanable/crayon/NeverShouldHaveComeHere(turf/T)
|
||||
return isspaceturf(T) || islava(T) || istype(T, /turf/open/water) || ischasm(T)
|
||||
@@ -1,34 +0,0 @@
|
||||
/datum/atom_hud/antag/gang
|
||||
var/color = null
|
||||
|
||||
/datum/atom_hud/antag/gang/add_to_hud(atom/A)
|
||||
if(!A)
|
||||
return
|
||||
var/image/holder = A.hud_list[ANTAG_HUD]
|
||||
if(holder)
|
||||
holder.color = color
|
||||
..()
|
||||
|
||||
/datum/atom_hud/antag/gang/remove_from_hud(atom/A)
|
||||
if(!A)
|
||||
return
|
||||
var/image/holder = A.hud_list[ANTAG_HUD]
|
||||
if(holder)
|
||||
holder.color = null
|
||||
..()
|
||||
|
||||
/datum/atom_hud/antag/gang/join_hud(mob/M)
|
||||
if(!istype(M))
|
||||
CRASH("join_hud(): [M] ([M.type]) is not a mob!")
|
||||
var/image/holder = M.hud_list[ANTAG_HUD]
|
||||
if(holder)
|
||||
holder.color = color
|
||||
..()
|
||||
|
||||
/datum/atom_hud/antag/gang/leave_hud(mob/M)
|
||||
if(!istype(M))
|
||||
CRASH("leave_hud(): [M] ([M.type]) is not a mob!")
|
||||
var/image/holder = M.hud_list[ANTAG_HUD]
|
||||
if(holder)
|
||||
holder.color = null
|
||||
..()
|
||||
@@ -1,411 +0,0 @@
|
||||
/datum/gang_item
|
||||
var/name
|
||||
var/item_path
|
||||
var/cost
|
||||
var/spawn_msg
|
||||
var/category
|
||||
var/list/gang_whitelist = list()
|
||||
var/list/gang_blacklist = list()
|
||||
var/id
|
||||
|
||||
/datum/gang_item/proc/purchase(mob/living/carbon/user, datum/team/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(!spawn_item(user, gang, gangtool))
|
||||
gang.adjust_influence(-real_cost)
|
||||
to_chat(user, "<span class='notice'>You bought \the [name].</span>")
|
||||
return TRUE
|
||||
|
||||
/datum/gang_item/proc/spawn_item(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool) // If this returns anything other than null, something fucked up and influence won't lower.
|
||||
if(item_path)
|
||||
var/obj/item/O = new item_path(user.loc)
|
||||
user.put_in_hands(O)
|
||||
else
|
||||
return TRUE
|
||||
if(spawn_msg)
|
||||
to_chat(user, "[spawn_msg]")
|
||||
|
||||
/datum/gang_item/proc/can_buy(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool)
|
||||
return gang && (gang.influence >= get_cost(user, gang, gangtool)) && can_see(user, gang, gangtool)
|
||||
|
||||
/datum/gang_item/proc/can_see(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool)
|
||||
return TRUE
|
||||
|
||||
/datum/gang_item/proc/get_cost(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool)
|
||||
return cost
|
||||
|
||||
/datum/gang_item/proc/get_cost_display(mob/living/carbon/user, datum/team/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/team/gang/gang, obj/item/device/gangtool/gangtool)
|
||||
return name
|
||||
|
||||
/datum/gang_item/proc/get_extra_info(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool)
|
||||
return
|
||||
|
||||
///////////////////
|
||||
//CLOTHING
|
||||
///////////////////
|
||||
|
||||
/datum/gang_item/clothing
|
||||
category = "Purchase Gang Clothes (Only the jumpsuit and suit give you added influence):"
|
||||
|
||||
/datum/gang_item/clothing/under
|
||||
name = "Gang Uniform"
|
||||
id = "under"
|
||||
cost = 1
|
||||
|
||||
/datum/gang_item/clothing/under/spawn_item(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool)
|
||||
if(gang.inner_outfits.len)
|
||||
var/outfit = pick(gang.inner_outfits)
|
||||
if(outfit)
|
||||
var/obj/item/O = new outfit(user.loc)
|
||||
user.put_in_hands(O)
|
||||
to_chat(user, "<span class='notice'> This is your gang's official uniform, wearing it will increase your influence")
|
||||
return
|
||||
return TRUE
|
||||
|
||||
/datum/gang_item/clothing/suit
|
||||
name = "Gang Armored Outerwear"
|
||||
id = "suit"
|
||||
cost = 1
|
||||
|
||||
/datum/gang_item/clothing/suit/spawn_item(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool)
|
||||
if(gang.outer_outfits.len)
|
||||
var/outfit = pick(gang.outer_outfits)
|
||||
if(outfit)
|
||||
var/obj/item/O = new outfit(user.loc)
|
||||
O.armor = O.armor.setRating(melee = 25, bullet = 35, laser = 15, energy = 10, bomb = 30, bio = 0, rad = 0, fire = 30, acid = 30)
|
||||
O.desc += " Tailored for the [gang.name] Gang to offer the wearer moderate protection against ballistics and physical trauma."
|
||||
user.put_in_hands(O)
|
||||
to_chat(user, "<span class='notice'> This is your gang's official outerwear, wearing it will increase your influence")
|
||||
return
|
||||
return TRUE
|
||||
|
||||
/datum/gang_item/clothing/hat
|
||||
name = "Pimp Hat"
|
||||
id = "hat"
|
||||
cost = 16
|
||||
item_path = /obj/item/clothing/head/collectable/petehat/gang
|
||||
|
||||
/obj/item/clothing/head/collectable/petehat/gang
|
||||
name = "pimpin' hat"
|
||||
desc = "The undisputed king of style."
|
||||
|
||||
/datum/gang_item/clothing/mask
|
||||
name = "Golden Death Mask"
|
||||
id = "mask"
|
||||
cost = 18
|
||||
item_path = /obj/item/clothing/mask/gskull
|
||||
|
||||
/obj/item/clothing/mask/gskull
|
||||
name = "golden death mask"
|
||||
icon_state = "gskull"
|
||||
desc = "Strike terror, and envy, into the hearts of your enemies."
|
||||
|
||||
/datum/gang_item/clothing/shoes
|
||||
name = "Bling Boots"
|
||||
id = "boots"
|
||||
cost = 20
|
||||
item_path = /obj/item/clothing/shoes/gang
|
||||
|
||||
/obj/item/clothing/shoes/gang
|
||||
name = "blinged-out boots"
|
||||
desc = "Stand aside peasants."
|
||||
icon_state = "bling"
|
||||
|
||||
/datum/gang_item/clothing/neck
|
||||
name = "Gold Necklace"
|
||||
id = "necklace"
|
||||
cost = 9
|
||||
item_path = /obj/item/clothing/neck/necklace/dope
|
||||
|
||||
/datum/gang_item/clothing/hands
|
||||
name = "Decorative Brass Knuckles"
|
||||
id = "hand"
|
||||
cost = 11
|
||||
item_path = /obj/item/clothing/gloves/gang
|
||||
|
||||
/obj/item/clothing/gloves/gang
|
||||
name = "braggadocio's brass knuckles"
|
||||
desc = "Purely decorative, don't find out the hard way."
|
||||
icon_state = "knuckles"
|
||||
w_class = 3
|
||||
|
||||
/datum/gang_item/clothing/shades //Addition: Why not have cool shades on a gang member anyways?
|
||||
name = "Cool Sunglasses"
|
||||
id = "glasses"
|
||||
cost = 5
|
||||
item_path = /obj/item/clothing/glasses/sunglasses
|
||||
|
||||
/datum/gang_item/clothing/belt
|
||||
name = "Badass Belt"
|
||||
id = "belt"
|
||||
cost = 13
|
||||
item_path = /obj/item/storage/belt/military/gang
|
||||
|
||||
/obj/item/storage/belt/military/gang
|
||||
name = "badass belt"
|
||||
icon_state = "gangbelt"
|
||||
item_state = "gang"
|
||||
desc = "The belt buckle simply reads 'BAMF'."
|
||||
|
||||
///////////////////
|
||||
//WEAPONS
|
||||
///////////////////
|
||||
|
||||
/datum/gang_item/weapon
|
||||
category = "Purchase Weapons:"
|
||||
|
||||
/datum/gang_item/weapon/ammo
|
||||
|
||||
/datum/gang_item/weapon/shuriken
|
||||
name = "Shuriken"
|
||||
id = "shuriken"
|
||||
cost = 2
|
||||
item_path = /obj/item/throwing_star
|
||||
|
||||
/datum/gang_item/weapon/switchblade
|
||||
name = "Switchblade"
|
||||
id = "switchblade"
|
||||
cost = 5
|
||||
item_path = /obj/item/switchblade
|
||||
|
||||
/datum/gang_item/weapon/surplus //For when a gang boss is extra broke or cheap.
|
||||
name = "Surplus Rifle"
|
||||
id = "surplus"
|
||||
cost = 6
|
||||
item_path = /obj/item/gun/ballistic/automatic/surplus
|
||||
|
||||
/datum/gang_item/weapon/ammo/surplus_ammo
|
||||
name = "Surplus Rifle Ammo"
|
||||
id = "surplus_ammo"
|
||||
cost = 3
|
||||
item_path = /obj/item/ammo_box/magazine/m10mm/rifle
|
||||
|
||||
/datum/gang_item/weapon/improvised
|
||||
name = "Sawn-Off Improvised Shotgun"
|
||||
id = "sawn"
|
||||
cost = 5
|
||||
item_path = /obj/item/gun/ballistic/revolver/doublebarrel/improvised/sawn
|
||||
|
||||
/datum/gang_item/weapon/ammo/improvised_ammo
|
||||
name = "Box of Buckshot"
|
||||
id = "buckshot"
|
||||
cost = 5
|
||||
item_path = /obj/item/storage/box/lethalshot
|
||||
|
||||
/datum/gang_item/weapon/pistol
|
||||
name = "10mm Pistol"
|
||||
id = "pistol"
|
||||
cost = 25
|
||||
item_path = /obj/item/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/sniper
|
||||
name = "Black Market .50cal Sniper Rifle"
|
||||
id = "sniper"
|
||||
cost = 35
|
||||
item_path = /obj/item/gun/ballistic/automatic/sniper_rifle
|
||||
|
||||
/datum/gang_item/weapon/ammo/sniper_ammo
|
||||
name = "Smuggled .50cal Sniper Rounds"
|
||||
id = "sniper_ammo"
|
||||
cost = 15
|
||||
item_path = /obj/item/ammo_box/magazine/sniper_rounds
|
||||
|
||||
/*/datum/gang_item/weapon/ammo/sleeper_ammo //no. absolutely no.
|
||||
name = "Illicit Soporific Cartridges"
|
||||
id = "sniper_ammo"
|
||||
cost = 15 //who the fuck thought a ONE-HIT K.O. for 15 gbp IN AN ENVIRONMENT WHERE WE'RE GETTING RID OF HARDSTUNS is a GOOD IDEA
|
||||
item_path = /obj/item/ammo_box/magazine/sniper_rounds/soporific*/
|
||||
|
||||
/datum/gang_item/weapon/machinegun
|
||||
name = "Mounted Machine Gun"
|
||||
id = "MG"
|
||||
cost = 45
|
||||
item_path = /obj/machinery/manned_turret
|
||||
spawn_msg = "<span class='notice'>The mounted machine gun features enhanced responsiveness. Hold down on the trigger while firing to control where you're shooting.</span>"
|
||||
|
||||
/datum/gang_item/weapon/machinegun/spawn_item(mob/living/carbon/user, obj/item/device/gangtool/gangtool)
|
||||
new item_path(user.loc)
|
||||
to_chat(user, spawn_msg)
|
||||
|
||||
/datum/gang_item/weapon/uzi
|
||||
name = "Uzi SMG"
|
||||
id = "uzi"
|
||||
cost = 50
|
||||
item_path = /obj/item/gun/ballistic/automatic/mini_uzi
|
||||
|
||||
/datum/gang_item/weapon/ammo/uzi_ammo
|
||||
name = "Uzi Ammo"
|
||||
id = "uzi_ammo"
|
||||
cost = 20
|
||||
item_path = /obj/item/ammo_box/magazine/uzim9mm
|
||||
|
||||
///////////////////
|
||||
//EQUIPMENT
|
||||
///////////////////
|
||||
|
||||
/datum/gang_item/equipment
|
||||
category = "Purchase Equipment:"
|
||||
|
||||
/datum/gang_item/equipment/spraycan
|
||||
name = "Territory Spraycan"
|
||||
id = "spraycan"
|
||||
cost = 1
|
||||
item_path = /obj/item/toy/crayon/spraycan/gang
|
||||
|
||||
/datum/gang_item/equipment/spraycan/spawn_item(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool)
|
||||
var/obj/item/O = new item_path(user.loc, gang)
|
||||
user.put_in_hands(O)
|
||||
|
||||
/datum/gang_item/equipment/sharpener
|
||||
name = "Sharpener"
|
||||
id = "whetstone"
|
||||
cost = 3
|
||||
item_path = /obj/item/sharpener
|
||||
|
||||
/datum/gang_item/equipment/emp
|
||||
name = "EMP Grenade"
|
||||
id = "EMP"
|
||||
cost = 7
|
||||
item_path = /obj/item/grenade/empgrenade
|
||||
|
||||
/datum/gang_item/equipment/c4
|
||||
name = "C4 Explosive"
|
||||
id = "c4"
|
||||
cost = 7
|
||||
item_path = /obj/item/grenade/plastic/c4
|
||||
|
||||
/datum/gang_item/equipment/frag
|
||||
name = "Fragmentation Grenade"
|
||||
id = "frag nade"
|
||||
cost = 5
|
||||
item_path = /obj/item/grenade/frag
|
||||
|
||||
/datum/gang_item/equipment/implant_breaker
|
||||
name = "Implant Breaker"
|
||||
id = "implant_breaker"
|
||||
cost = 10
|
||||
item_path = /obj/item/implanter/gang
|
||||
|
||||
/datum/gang_item/equipment/implant_breaker/spawn_item(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool)
|
||||
var/obj/item/O = new item_path(user.loc, gang)
|
||||
user.put_in_hands(O)
|
||||
to_chat(user, "<span class='notice'>The <b>implant breaker</b> 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.</span>")
|
||||
|
||||
/datum/gang_item/equipment/wetwork_boots
|
||||
name = "Wetwork boots"
|
||||
id = "wetwork"
|
||||
cost = 8
|
||||
item_path = /obj/item/clothing/shoes/combat/gang
|
||||
|
||||
/obj/item/clothing/shoes/combat/gang
|
||||
name = "Wetwork boots"
|
||||
desc = "A gang's best hitmen are prepared for anything."
|
||||
permeability_coefficient = 0.01
|
||||
clothing_flags = NOSLIP
|
||||
|
||||
/datum/gang_item/equipment/shield
|
||||
name = "Riot Shield"
|
||||
id = "riot_shield"
|
||||
cost = 25
|
||||
item_path = /obj/item/shield/riot
|
||||
|
||||
/datum/gang_item/equipment/gangsheild
|
||||
name = "Tower Shield"
|
||||
id = "metal"
|
||||
cost = 45 //High block of melee and even higher for bullets
|
||||
item_path = /obj/item/shield/riot/tower
|
||||
|
||||
/datum/gang_item/equipment/pen
|
||||
name = "Recruitment Pen"
|
||||
id = "pen"
|
||||
cost = 20
|
||||
item_path = /obj/item/pen/gang
|
||||
spawn_msg = "<span class='notice'>More <b>recruitment pens</b> will allow you to recruit gangsters faster. Only gang leaders can recruit with pens.</span>"
|
||||
|
||||
/datum/gang_item/equipment/pen/purchase(mob/living/carbon/user, datum/team/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/team/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/team/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 = 5
|
||||
|
||||
/datum/gang_item/equipment/gangtool/spawn_item(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool)
|
||||
var/item_type
|
||||
if(gang)
|
||||
item_type = /obj/item/device/gangtool/spare/lt
|
||||
if(gang.leaders.len < MAX_LEADERS_GANG)
|
||||
to_chat(user, "<span class='notice'><b>Gangtools</b> 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 [MAX_LEADERS_GANG-gang.leaders.len] more Lieutenants</span>")
|
||||
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/team/gang/gang, obj/item/device/gangtool/gangtool)
|
||||
if(gang && (gang.leaders.len < gang.max_leaders))
|
||||
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 = "<span class='notice'>The <b>dominator</b> will secure your gang's dominance over the station. Turn it on when you are ready to defend it.</span>"
|
||||
|
||||
/datum/gang_item/equipment/dominator/can_buy(mob/living/carbon/user, datum/team/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/team/gang/gang, obj/item/device/gangtool/gangtool)
|
||||
if(!gang || !gang.dom_attempts)
|
||||
return ..()
|
||||
return "<b>[..()]</b>"
|
||||
|
||||
/datum/gang_item/equipment/dominator/get_cost_display(mob/living/carbon/user, datum/team/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/team/gang/gang, obj/item/device/gangtool/gangtool)
|
||||
if(gang)
|
||||
return "This device requires a 5x5 area clear of walls to FUNCTION. (Estimated Takeover Time: [round(gang.determine_domination_time()/60,0.1)] minutes)"
|
||||
|
||||
/datum/gang_item/equipment/dominator/purchase(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool)
|
||||
var/area/userarea = get_base_area(user)
|
||||
if(!(userarea.type in gang.territories|gang.new_territories))
|
||||
to_chat(user,"<span class='warning'>The <b>dominator</b> can be spawned only on territory controlled by your gang!</span>")
|
||||
return FALSE
|
||||
for(var/obj/obj in get_turf(user))
|
||||
if(obj.density)
|
||||
to_chat(user, "<span class='warning'>There's not enough room here!</span>")
|
||||
return FALSE
|
||||
|
||||
return ..()
|
||||
|
||||
/datum/gang_item/equipment/dominator/spawn_item(mob/living/carbon/user, datum/team/gang/gang, obj/item/device/gangtool/gangtool)
|
||||
new item_path(user.loc)
|
||||
to_chat(user, spawn_msg)
|
||||
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* Gang Boss Pens
|
||||
*/
|
||||
/obj/item/pen/gang
|
||||
var/cooldown
|
||||
var/last_used
|
||||
|
||||
/obj/item/pen/gang/Initialize()
|
||||
. = ..()
|
||||
last_used = world.time
|
||||
|
||||
/obj/item/pen/gang/attack(mob/living/M, mob/user, stealth = TRUE) //ha
|
||||
if(!istype(M))
|
||||
return
|
||||
if(!ishuman(M) || !ishuman(user) || M.stat == DEAD)
|
||||
return ..()
|
||||
//var/datum/antagonist/gang/boss/L = user.mind.has_antag_datum(/datum/antagonist/gang/boss) //Pen works with bosses only.
|
||||
var/datum/antagonist/gang/L = user.mind.has_antag_datum(/datum/antagonist/gang) //Pen works with anyone in gang.
|
||||
if(!L)
|
||||
return ..()
|
||||
if(!..())
|
||||
return
|
||||
if(cooldown)
|
||||
to_chat(user, "<span class='warning'>[src] needs more time to recharge before it can be used.</span>")
|
||||
return
|
||||
if(!M.client || !M.mind)
|
||||
to_chat(user, "<span class='warning'>A braindead gangster is an useless gangster!</span>")
|
||||
return
|
||||
var/datum/team/gang/gang = L.gang
|
||||
if(!add_gangster(user, gang, M.mind))
|
||||
return
|
||||
cooldown = TRUE
|
||||
icon_state = "pen_blink"
|
||||
var/cooldown_time = 600/gang.leaders.len
|
||||
addtimer(CALLBACK(src, .proc/cooldown), cooldown_time)
|
||||
|
||||
/obj/item/pen/gang/proc/cooldown()
|
||||
cooldown = FALSE
|
||||
icon_state = "pen"
|
||||
var/mob/M = loc
|
||||
if(istype(M))
|
||||
to_chat(M, "<span class='notice'>[icon2html(src, M)] [src][(loc == M)?(""):(" in your [loc]")] vibrates softly. It is ready to be used again.</span>")
|
||||
|
||||
/obj/item/pen/gang/proc/add_gangster(mob/user, datum/team/gang/gang, datum/mind/gangster_mind, check = TRUE) // Basically a wrapper to add_antag_datum.
|
||||
var/datum/antagonist/dudegang = gangster_mind.has_antag_datum(/datum/antagonist/gang)
|
||||
if(dudegang)
|
||||
if(dudegang == gang)
|
||||
to_chat(user, "<span class='danger'>This mind is already controlled by your gang!</span>")
|
||||
return
|
||||
to_chat(user, "<span class='danger'>This mind is already controlled by someone else!</span>")
|
||||
return
|
||||
if(check && HAS_TRAIT(gangster_mind.current, TRAIT_MINDSHIELD)) //Check to see if the potential gangster is implanted
|
||||
to_chat(user, "<span class='danger'>This mind is too strong to control!</span>")
|
||||
return
|
||||
var/mob/living/carbon/human/H = gangster_mind.current // we are sure the dude's human cause it's checked in attack()
|
||||
H.silent = max(H.silent, 5)
|
||||
H.DefaultCombatKnockdown(100)
|
||||
gangster_mind.add_antag_datum(/datum/antagonist/gang, gang)
|
||||
return TRUE
|
||||
@@ -1,67 +0,0 @@
|
||||
//gang.dm
|
||||
//Gang War Game Mode
|
||||
GLOBAL_LIST_INIT(possible_gangs, subtypesof(/datum/team/gang))
|
||||
GLOBAL_LIST_EMPTY(gangs)
|
||||
/datum/game_mode/gang
|
||||
name = "gang war"
|
||||
config_tag = "gang"
|
||||
antag_flag = ROLE_GANG
|
||||
chaos = 9
|
||||
restricted_jobs = list("Prisoner", "AI", "Cyborg")
|
||||
protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster")
|
||||
required_players = 15
|
||||
required_enemies = 0
|
||||
recommended_enemies = 2
|
||||
enemy_minimum_age = 14
|
||||
|
||||
announce_span = "danger"
|
||||
announce_text = "A violent turf war has erupted on the station!\n\
|
||||
<span class='danger'>Gangsters</span>: Take over the station with a dominator.\n\
|
||||
<span class='notice'>Crew</span>: Prevent the gangs from expanding and initiating takeover."
|
||||
|
||||
var/list/datum/mind/gangboss_candidates = list()
|
||||
|
||||
/datum/game_mode/gang/generate_report()
|
||||
return "Cybersun Industries representatives claimed that they, in joint research with the Tiger Cooperative, have made a major breakthrough in brainwashing technology, and have \
|
||||
made the nanobots that apply the \"conversion\" very small and capable of fitting into usually innocent objects - namely, pens. While they refused to outsource this technology for \
|
||||
months to come due to its flaws, they reported some as missing but passed it off to carelessness. At Central Command, we don't like mysteries, and we have reason to believe that this \
|
||||
technology was stolen for anti-Nanotrasen use. Be on the lookout for territory claims and unusually violent crew behavior, applying mindshield implants as necessary."
|
||||
|
||||
/datum/game_mode/gang/pre_setup()
|
||||
if(CONFIG_GET(flag/protect_roles_from_antagonist))
|
||||
restricted_jobs += protected_jobs
|
||||
|
||||
if(CONFIG_GET(flag/protect_assistant_from_antagonist))
|
||||
restricted_jobs += "Assistant"
|
||||
|
||||
//Spawn more bosses depending on server population
|
||||
var/gangs_to_create = 2
|
||||
if(prob(num_players()) && num_players() > 1.5*required_players)
|
||||
gangs_to_create++
|
||||
if(prob(num_players()) && num_players() > 2*required_players)
|
||||
gangs_to_create++
|
||||
gangs_to_create = min(gangs_to_create, GLOB.possible_gangs.len)
|
||||
|
||||
for(var/i in 1 to gangs_to_create)
|
||||
if(!antag_candidates.len)
|
||||
break
|
||||
|
||||
//Now assign a boss for the gang
|
||||
var/datum/mind/boss = pick_n_take(antag_candidates)
|
||||
antag_candidates -= boss
|
||||
gangboss_candidates += boss
|
||||
boss.restricted_roles = restricted_jobs
|
||||
|
||||
if(gangboss_candidates.len < 1) //Need at least one gangs
|
||||
return
|
||||
|
||||
return TRUE
|
||||
|
||||
/datum/game_mode/gang/post_setup()
|
||||
set waitfor = FALSE
|
||||
..()
|
||||
for(var/i in gangboss_candidates)
|
||||
var/datum/mind/M = i
|
||||
var/datum/antagonist/gang/boss/B = new()
|
||||
M.add_antag_datum(B)
|
||||
B.equip_gang()
|
||||
@@ -1,259 +0,0 @@
|
||||
//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 = 'icons/obj/device.dmi'
|
||||
icon_state = "gangtool"
|
||||
item_state = "radio"
|
||||
lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
|
||||
throwforce = 0
|
||||
w_class = WEIGHT_CLASS_TINY
|
||||
throw_speed = 3
|
||||
throw_range = 7
|
||||
flags_1 = CONDUCT_1
|
||||
var/datum/team/gang/gang //Which gang uses this?
|
||||
var/recalling = 0
|
||||
var/outfits = 2
|
||||
var/free_pen = 0
|
||||
var/promotable = FALSE
|
||||
var/static/list/buyable_items = list()
|
||||
var/list/tags = list()
|
||||
|
||||
/obj/item/device/gangtool/Initialize()
|
||||
. = ..()
|
||||
update_icon()
|
||||
for(var/i in subtypesof(/datum/gang_item))
|
||||
var/datum/gang_item/G = i
|
||||
var/id = initial(G.id)
|
||||
var/cat = initial(G.category)
|
||||
if(id)
|
||||
if(!islist(buyable_items[cat]))
|
||||
buyable_items[cat] = list()
|
||||
buyable_items[cat][id] = new G
|
||||
/obj/item/device/gangtool/Destroy()
|
||||
if(gang)
|
||||
gang.gangtools -= src
|
||||
return ..()
|
||||
|
||||
/obj/item/device/gangtool/attack_self(mob/user)
|
||||
..()
|
||||
if (!can_use(user))
|
||||
return
|
||||
var/datum/antagonist/gang/boss/L = user.mind.has_antag_datum(/datum/antagonist/gang/boss)
|
||||
var/dat
|
||||
if(!gang)
|
||||
dat += "This device is not registered.<br><br>"
|
||||
if(L)
|
||||
if(promotable && L.gang.leaders.len < L.gang.max_leaders)
|
||||
dat += "Give this device to another member of your organization to use to promote them to Lieutenant.<br><br>"
|
||||
dat += "If this is meant as a spare device for yourself:<br>"
|
||||
dat += "<a href='?src=[REF(src)];register=1'>Register Device as Spare</a><br>"
|
||||
else if(promotable)
|
||||
var/datum/antagonist/gang/sweet = user.mind.has_antag_datum(/datum/antagonist/gang)
|
||||
if(sweet.gang.leaders.len < sweet.gang.max_leaders)
|
||||
dat += "You have been selected for a promotion!<br>"
|
||||
dat += "<a href='?src=[REF(src)];register=1'>Accept Promotion</a><br>"
|
||||
else
|
||||
dat += "No promotions available: All positions filled.<br>"
|
||||
else
|
||||
dat += "This device is not authorized to promote.<br>"
|
||||
else
|
||||
if(gang.domination_time != NOT_DOMINATING)
|
||||
dat += "<center><font color='red'>Takeover In Progress:<br><B>[DisplayTimeText(gang.domination_time_remaining() * 10)] remain</B></font></center>"
|
||||
|
||||
dat += "Registration: <B>[gang.name] Gang Boss</B><br>"
|
||||
dat += "Organization Size: <B>[gang.members.len]</B> | Station Control: <B>[gang.territories.len] territories under control.</B> | Influence: <B>[gang.influence]</B><br>"
|
||||
dat += "Time until Influence grows: <B>[time2text(gang.next_point_time - world.time, "mm:ss")]</B><br>"
|
||||
dat += "<a href='?src=[REF(src)];commute=1'>Send message to Gang</a><br>"
|
||||
dat += "<a href='?src=[REF(src)];recall=1'>Recall shuttle</a><br>"
|
||||
dat += "<hr>"
|
||||
for(var/cat in buyable_items)
|
||||
dat += "<b>[cat]</b><br>"
|
||||
for(var/id in buyable_items[cat])
|
||||
var/datum/gang_item/G = buyable_items[cat][id]
|
||||
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 = "<a href='?src=[REF(src)];purchase=1;id=[id];cat=[cat]'>[toAdd]</a>"
|
||||
dat += toAdd
|
||||
var/extra = G.get_extra_info(user, gang, src)
|
||||
if(extra)
|
||||
dat += "<br><i>[extra]</i>"
|
||||
dat += "<br>"
|
||||
dat += "<br>"
|
||||
|
||||
dat += "<a href='?src=[REF(src)];choice=refresh'>Refresh</a><br>"
|
||||
|
||||
var/datum/browser/popup = new(user, "gangtool", "Welcome to GangTool v4.0", 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"])
|
||||
if(islist(buyable_items[href_list["cat"]]))
|
||||
var/list/L = buyable_items[href_list["cat"]]
|
||||
var/datum/gang_item/G = L[href_list["id"]]
|
||||
if(G && G.can_buy(usr, gang, src))
|
||||
G.purchase(usr, gang, src, FALSE)
|
||||
|
||||
if(href_list["commute"])
|
||||
ping_gang(usr)
|
||||
if(href_list["recall"])
|
||||
recall(usr)
|
||||
attack_self(usr)
|
||||
|
||||
/obj/item/device/gangtool/update_icon()
|
||||
overlays.Cut()
|
||||
var/image/I = new(icon, "[icon_state]-overlay")
|
||||
if(gang)
|
||||
I.color = gang.color
|
||||
overlays.Add(I)
|
||||
|
||||
/obj/item/device/gangtool/proc/ping_gang(mob/user)
|
||||
if(!can_use(user))
|
||||
return
|
||||
var/message = stripped_input(user,"Discreetly send a gang-wide message.","Send Message")
|
||||
if(!message || !can_use(user))
|
||||
return
|
||||
if(!is_station_level(user.z))
|
||||
to_chat(user, "<span class='info'>[icon2html(src, user)]Error: Station out of range.</span>")
|
||||
return
|
||||
if(gang.members.len)
|
||||
var/datum/antagonist/gang/G = user.mind.has_antag_datum(/datum/antagonist/gang)
|
||||
if(!G)
|
||||
return
|
||||
var/ping = "<span class='danger'><B><i>[gang.name] [G.message_name] [user.real_name]</i>: [message]</B></span>"
|
||||
for(var/datum/mind/ganger in gang.members)
|
||||
if(ganger.current && is_station_level(ganger.current.z) && (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]")
|
||||
user.log_talk(message,LOG_SAY, tag="[gang.name] gangster")
|
||||
|
||||
/obj/item/device/gangtool/proc/register_device(mob/user)
|
||||
if(gang) //It's already been registered!
|
||||
return
|
||||
var/datum/antagonist/gang/G = user.mind.has_antag_datum(/datum/antagonist/gang)
|
||||
if(G)
|
||||
gang = G.gang
|
||||
gang.gangtools += src
|
||||
update_icon()
|
||||
if(!(user.mind in gang.leaders) && promotable)
|
||||
G.promote()
|
||||
free_pen = TRUE
|
||||
gang.message_gangtools("[user] has been promoted to Lieutenant.")
|
||||
to_chat(user, "The <b>Gangtool</b> 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 <b>recruitment pens</b> 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(user, "<span class='warning'>ACCESS DENIED: Unauthorized user.</span>")
|
||||
|
||||
/obj/item/device/gangtool/proc/recall(mob/user)
|
||||
if(!recallchecks(user))
|
||||
return
|
||||
if(recalling)
|
||||
to_chat(user, "<span class='warning'>Error: Recall already in progress.</span>")
|
||||
return
|
||||
gang.message_gangtools("[user] is attempting to recall the emergency shuttle.")
|
||||
recalling = TRUE
|
||||
to_chat(user, "<span class='info'>[icon2html(src, loc)]Generating shuttle recall order with codes retrieved from last call signal...</span>")
|
||||
addtimer(CALLBACK(src, .proc/recall2, user), rand(100,300))
|
||||
|
||||
/obj/item/device/gangtool/proc/recall2(mob/user)
|
||||
if(!recallchecks(user))
|
||||
return
|
||||
to_chat(user, "<span class='info'>[icon2html(src, loc)]Shuttle recall order generated. Accessing station long-range communication arrays...</span>")
|
||||
addtimer(CALLBACK(src, .proc/recall3, user), rand(100,300))
|
||||
|
||||
/obj/item/device/gangtool/proc/recall3(mob/user)
|
||||
if(!recallchecks(user))
|
||||
return
|
||||
var/list/living_crew = list()//shamelessly copied from mulligan code, there should be a helper for this
|
||||
for(var/mob/Player in GLOB.mob_list)
|
||||
if(Player.mind && Player.stat != DEAD && !isnewplayer(Player) && !isbrain(Player) && Player.client)
|
||||
living_crew += Player
|
||||
var/malc = CONFIG_GET(number/midround_antag_life_check)
|
||||
if(living_crew.len / GLOB.joined_player_list.len <= malc) //Shuttle cannot be recalled if too many people died
|
||||
to_chat(user, "<span class='warning'>[icon2html(src, user)]Error: Station communication systems compromised. Unable to establish connection.</span>")
|
||||
recalling = FALSE
|
||||
return
|
||||
to_chat(user, "<span class='info'>[icon2html(src, loc)]Comm arrays accessed. Broadcasting recall signal...</span>")
|
||||
addtimer(CALLBACK(src, .proc/recallfinal, user), rand(100,300))
|
||||
|
||||
/obj/item/device/gangtool/proc/recallfinal(mob/user)
|
||||
if(!recallchecks(user))
|
||||
return
|
||||
recalling = FALSE
|
||||
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)
|
||||
if(SSshuttle.cancelEvac(user))
|
||||
gang.recalls--
|
||||
return TRUE
|
||||
|
||||
to_chat(user, "<span class='info'>[icon2html(src, loc)]No response recieved. Emergency shuttle cannot be recalled at this time.</span>")
|
||||
return
|
||||
|
||||
/obj/item/device/gangtool/proc/recallchecks(mob/user)
|
||||
if(!can_use(user))
|
||||
return
|
||||
if(SSshuttle.emergencyNoRecall)
|
||||
return
|
||||
if(!gang.recalls)
|
||||
to_chat(user, "<span class='warning'>Error: Unable to access communication arrays. Firewall has logged our signature and is blocking all further attempts.</span>")
|
||||
return
|
||||
if(SSshuttle.emergency.mode != SHUTTLE_CALL) //Shuttle can only be recalled when it's moving to the station
|
||||
to_chat(user, "<span class='warning'>[icon2html(src, user)]Emergency shuttle cannot be recalled at this time.</span>")
|
||||
recalling = FALSE
|
||||
return
|
||||
if(!gang.dom_attempts)
|
||||
to_chat(user, "<span class='warning'>[icon2html(src, user)]Error: Unable to access communication arrays. Firewall has logged our signature and is blocking all further attempts.</span>")
|
||||
recalling = FALSE
|
||||
return
|
||||
if(!is_station_level(user.z)) //Shuttle can only be recalled while on station
|
||||
to_chat(user, "<span class='warning'>[icon2html(src, user)]Error: Device out of range of station communication arrays.</span>")
|
||||
recalling = FALSE
|
||||
return
|
||||
return TRUE
|
||||
|
||||
/obj/item/device/gangtool/proc/can_use(mob/living/carbon/human/user)
|
||||
if(!istype(user))
|
||||
return
|
||||
if(user.incapacitated())
|
||||
return
|
||||
if(!(src in user.contents))
|
||||
return
|
||||
if(!user.mind)
|
||||
return
|
||||
var/datum/antagonist/gang/G = user.mind.has_antag_datum(/datum/antagonist/gang)
|
||||
if(!G)
|
||||
to_chat(user, "<span class='notice'>Huh, what's this?</span>")
|
||||
return
|
||||
if(!isnull(gang) && G.gang != gang)
|
||||
to_chat(user, "<span class='danger'>You cannot use gang tools owned by enemy gangs!</span>")
|
||||
return
|
||||
return TRUE
|
||||
|
||||
|
||||
/obj/item/device/gangtool/spare
|
||||
outfits = TRUE
|
||||
|
||||
/obj/item/device/gangtool/spare/lt
|
||||
promotable = TRUE
|
||||
@@ -1,61 +0,0 @@
|
||||
/obj/item/implant/gang
|
||||
name = "gang implant"
|
||||
desc = "Makes you a gangster or such."
|
||||
activated = 0
|
||||
var/datum/team/gang/gang
|
||||
|
||||
/obj/item/implant/gang/Initialize(loc, setgang)
|
||||
.=..()
|
||||
gang = setgang
|
||||
|
||||
/obj/item/implant/gang/Destroy()
|
||||
gang = null
|
||||
return ..()
|
||||
|
||||
/obj/item/implant/gang/get_data()
|
||||
var/dat = {"<b>Implant Specifications:</b><BR>
|
||||
<b>Name:</b> Criminal brainwash implant<BR>
|
||||
<b>Life:</b> A few seconds after injection.<BR>
|
||||
<b>Important Notes:</b> Illegal<BR>
|
||||
<HR>
|
||||
<b>Implant Details:</b><BR>
|
||||
<b>Function:</b> Contains a small pod of nanobots that change the host's brain to be loyal to a certain organization.<BR>
|
||||
<b>Special Features:</b> This device will also emit a small EMP pulse, destroying any other implants within the host's brain.<BR>
|
||||
<b>Integrity:</b> Implant's EMP function will destroy itself in the process."}
|
||||
return dat
|
||||
|
||||
/obj/item/implant/gang/implant(mob/living/target, mob/user, silent = 0)
|
||||
if(!target || !target.mind || target.stat == DEAD)
|
||||
return 0
|
||||
var/datum/antagonist/gang/G = target.mind.has_antag_datum(/datum/antagonist/gang)
|
||||
if(G && G.gang == G)
|
||||
return 0 // it's pointless
|
||||
if(..())
|
||||
for(var/obj/item/implant/I in target.implants)
|
||||
if(I != src)
|
||||
qdel(I)
|
||||
|
||||
if(ishuman(target))
|
||||
var/success
|
||||
if(G)
|
||||
if(!istype(G, /datum/antagonist/gang/boss))
|
||||
success = TRUE //Was not a gang boss, convert as usual
|
||||
target.mind.remove_antag_datum(/datum/antagonist/gang)
|
||||
else
|
||||
success = TRUE
|
||||
if(!success)
|
||||
target.visible_message("<span class='warning'>[target] seems to resist the implant!</span>", "<span class='warning'>You feel the influence of your enemies try to invade your mind!</span>")
|
||||
return FALSE
|
||||
target.mind.add_antag_datum(/datum/antagonist/gang, gang)
|
||||
qdel(src)
|
||||
return TRUE
|
||||
|
||||
/obj/item/implanter/gang
|
||||
name = "implanter (gang)"
|
||||
|
||||
/obj/item/implanter/gang/Initialize(loc, gang)
|
||||
if(!gang)
|
||||
qdel(src)
|
||||
return
|
||||
imp = new /obj/item/implant/gang(src,gang)
|
||||
.=..()
|
||||
@@ -13,6 +13,7 @@ GLOBAL_LIST_EMPTY(objectives)
|
||||
var/completed = FALSE //currently only used for custom objectives.
|
||||
var/completable = TRUE //Whether this objective shows greentext when completed
|
||||
var/martyr_compatible = FALSE //If the objective is compatible with martyr objective, i.e. if you can still do it while dead.
|
||||
var/objective_name = "Objective" //name used in printing this objective (Objective #1)
|
||||
|
||||
/datum/objective/New(var/text)
|
||||
GLOB.objectives += src // CITADEL EDIT FOR CRYOPODS
|
||||
|
||||
@@ -407,7 +407,7 @@
|
||||
|
||||
/obj/machinery/camera/get_remote_view_fullscreens(mob/user)
|
||||
if(view_range == short_range) //unfocused
|
||||
user.overlay_fullscreen("remote_view", /atom/movable/screen/fullscreen/impaired, 2)
|
||||
user.overlay_fullscreen("remote_view", /atom/movable/screen/fullscreen/scaled/impaired, 2)
|
||||
|
||||
/obj/machinery/camera/update_remote_sight(mob/living/user)
|
||||
user.see_invisible = SEE_INVISIBLE_LIVING //can't see ghosts through cameras
|
||||
|
||||
@@ -275,7 +275,7 @@
|
||||
if(final)
|
||||
playsound(origin, 'sound/machines/terminal_prompt_confirm.ogg', 25, 0)
|
||||
remote_eye.setLoc(get_turf(final))
|
||||
C.overlay_fullscreen("flash", /atom/movable/screen/fullscreen/flash/static)
|
||||
C.overlay_fullscreen("flash", /atom/movable/screen/fullscreen/tiled/flash/static)
|
||||
C.clear_fullscreen("flash", 3) //Shorter flash than normal since it's an ~~advanced~~ console!
|
||||
else
|
||||
playsound(origin, 'sound/machines/terminal_prompt_deny.ogg', 25, 0)
|
||||
|
||||
21
code/game/mecha/combat/five_stars.dm
Normal file
21
code/game/mecha/combat/five_stars.dm
Normal file
@@ -0,0 +1,21 @@
|
||||
/obj/mecha/combat/five_stars
|
||||
desc = "A state of the art tank deployed by the Spinward Stellar Coalition National Guard."
|
||||
name = "\improper Tank"
|
||||
icon = 'icons/mecha/mecha_96x96.dmi'
|
||||
icon_state = "five_stars"
|
||||
armor = list("melee" = 100, "bullet" = 50, "laser" = 35, "energy" = 35, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100)
|
||||
step_in = 4
|
||||
dir_in = 1 //Facing North.
|
||||
max_integrity = 800
|
||||
pixel_x = -32
|
||||
pixel_y = -32
|
||||
|
||||
/obj/mecha/combat/five_stars/Initialize()
|
||||
. = ..()
|
||||
var/obj/item/mecha_parts/mecha_equipment/ME = new /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/spacecops(src)
|
||||
ME.attach(src)
|
||||
ME = new /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/lmg(src)
|
||||
ME.attach(src)
|
||||
ME = new /obj/item/mecha_parts/mecha_equipment/tesla_energy_relay(src)
|
||||
ME.attach(src)
|
||||
max_ammo()
|
||||
@@ -342,6 +342,9 @@
|
||||
harmful = TRUE
|
||||
ammo_type = "missiles_he"
|
||||
|
||||
/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/spacecops
|
||||
projectiles = 420
|
||||
|
||||
/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/breaching
|
||||
name = "\improper BRM-6 missile rack"
|
||||
desc = "A weapon for combat exosuits. Launches low-explosive breaching missiles designed to explode only when striking a sturdy target."
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
GLOBAL_LIST(gang_tags)
|
||||
|
||||
/obj/effect/decal/cleanable/crayon
|
||||
name = "rune"
|
||||
desc = "Graffiti. Damn kids."
|
||||
@@ -45,6 +47,8 @@
|
||||
data["pixel_x"] = pixel_x
|
||||
if(pixel_y != initial(pixel_y))
|
||||
data["pixel_y"] = pixel_y
|
||||
/obj/effect/decal/cleanable/crayon/NeverShouldHaveComeHere(turf/T)
|
||||
return isgroundlessturf(T)
|
||||
|
||||
/obj/effect/decal/cleanable/crayon/PersistenceLoad(list/data)
|
||||
. = ..()
|
||||
@@ -63,3 +67,18 @@
|
||||
pixel_x = data["pixel_x"]
|
||||
if(data["pixel_y"])
|
||||
pixel_y = data["pixel_y"]
|
||||
|
||||
/obj/effect/decal/cleanable/crayon/gang
|
||||
name = "Leet Like Jeff K gang tag"
|
||||
desc = "Looks like someone's claimed this area for Leet Like Jeff K."
|
||||
icon = 'icons/obj/gang/tags.dmi'
|
||||
layer = BELOW_MOB_LAYER
|
||||
var/datum/team/gang/my_gang
|
||||
|
||||
/obj/effect/decal/cleanable/crayon/gang/Initialize(mapload, main, type, e_name, graf_rot, alt_icon = null)
|
||||
. = ..()
|
||||
LAZYADD(GLOB.gang_tags, src)
|
||||
|
||||
/obj/effect/decal/cleanable/crayon/gang/Destroy()
|
||||
LAZYREMOVE(GLOB.gang_tags, src)
|
||||
..()
|
||||
|
||||
@@ -1237,6 +1237,32 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
|
||||
embed_chance_turf_mod = (!isnull(embedding["embed_chance_turf_mod"]) ? embedding["embed_chance_turf_mod"] : EMBED_CHANCE_TURF_MOD))
|
||||
return TRUE
|
||||
|
||||
|
||||
/**
|
||||
* * An interrupt for offering an item to other people, called mainly from [/mob/living/carbon/proc/give], in case you want to run your own offer behavior instead.
|
||||
*
|
||||
* * Return TRUE if you want to interrupt the offer.
|
||||
*
|
||||
* * Arguments:
|
||||
* * offerer - the person offering the item
|
||||
*/
|
||||
/obj/item/proc/on_offered(mob/living/carbon/offerer)
|
||||
if(SEND_SIGNAL(src, COMSIG_ITEM_OFFERING, offerer) & COMPONENT_OFFER_INTERRUPT)
|
||||
return TRUE
|
||||
|
||||
/**
|
||||
* * An interrupt for someone trying to accept an offered item, called mainly from [/mob/living/carbon/proc/take], in case you want to run your own take behavior instead.
|
||||
*
|
||||
* * Return TRUE if you want to interrupt the taking.
|
||||
*
|
||||
* * Arguments:
|
||||
* * offerer - the person offering the item
|
||||
* * taker - the person trying to accept the offer
|
||||
*/
|
||||
/obj/item/proc/on_offer_taken(mob/living/carbon/offerer, mob/living/carbon/taker)
|
||||
if(SEND_SIGNAL(src, COMSIG_ITEM_OFFER_TAKEN, offerer, taker) & COMPONENT_OFFER_INTERRUPT)
|
||||
return TRUE
|
||||
|
||||
/**
|
||||
* Updates all action buttons associated with this item
|
||||
*
|
||||
|
||||
@@ -298,7 +298,7 @@
|
||||
|
||||
/obj/item/toy/crayon/proc/draw_on(atom/target, mob/user, proximity, params)
|
||||
var/static/list/punctuation = list("!","?",".",",","/","+","-","=","%","#","&")
|
||||
|
||||
var/istagger = HAS_TRAIT(user, TRAIT_TAGGER)
|
||||
var/cost = 1
|
||||
if(paint_mode == PAINT_LARGE_HORIZONTAL)
|
||||
cost = 5
|
||||
@@ -355,14 +355,14 @@
|
||||
else if(drawing in graffiti|oriented)
|
||||
temp = "graffiti"
|
||||
|
||||
// If a gang member is using a gang spraycan, it'll behave differently
|
||||
var/gang_mode = FALSE
|
||||
if(gang && user.mind && user.mind.has_antag_datum(/datum/antagonist/gang)) //Heres a check.
|
||||
gang_mode = TRUE // No more runtimes if a non-gang member sprays a gang can, it just works like normal cans.
|
||||
// discontinue if the area isn't valid for tagging because gang "honour"
|
||||
var/gang_mode
|
||||
if(user.mind)
|
||||
gang_mode = user.mind.has_antag_datum(/datum/antagonist/gang)
|
||||
|
||||
if(gang_mode && (!can_claim_for_gang(user, target)))
|
||||
return
|
||||
|
||||
|
||||
var/graf_rot
|
||||
if(drawing in oriented)
|
||||
switch(user.dir)
|
||||
@@ -390,16 +390,12 @@
|
||||
audible_message("<span class='notice'>You hear spraying.</span>")
|
||||
playsound(user.loc, 'sound/effects/spray.ogg', 5, 1, 5)
|
||||
|
||||
var/takes_time = !instant //For order purposes, since I'm maximum bad.
|
||||
if(gang_mode)
|
||||
takes_time = TRUE
|
||||
|
||||
var/wait_time = 50
|
||||
if(paint_mode == PAINT_LARGE_HORIZONTAL)
|
||||
wait_time *= 3
|
||||
|
||||
if(takes_time) //This is what deteremines the time it takes to spray a tag in gang mode. 50 is Default.
|
||||
if(!do_after(user, gang_tag_delay, target = target)) //25 is a good number, but we have gang_tag_delay var now.
|
||||
if(gang_mode || !instant)
|
||||
if(!do_after(user, 50, target = target))
|
||||
return
|
||||
|
||||
if(length(text_buffer))
|
||||
@@ -410,16 +406,15 @@
|
||||
|
||||
|
||||
if(actually_paints)
|
||||
var/obj/effect/decal/cleanable/crayon/C = new(target, paint_color, drawing, temp, graf_rot)
|
||||
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)
|
||||
tag_for_gang(user, target, gang_mode)
|
||||
affected_turfs += target
|
||||
else
|
||||
switch(paint_mode)
|
||||
if(PAINT_NORMAL)
|
||||
var/obj/effect/decal/cleanable/crayon/C = new(target, paint_color, drawing, temp, graf_rot)
|
||||
C.add_hiddenprint(user)
|
||||
if(precision_mode)
|
||||
C.pixel_x = clamp(precision_x, -(world.icon_size/2), world.icon_size/2)
|
||||
@@ -432,14 +427,18 @@
|
||||
var/turf/left = locate(target.x-1,target.y,target.z)
|
||||
var/turf/right = locate(target.x+1,target.y,target.z)
|
||||
if(isValidSurface(left) && isValidSurface(right))
|
||||
var/obj/effect/decal/cleanable/crayon/C = new(left, paint_color, drawing, temp, graf_rot, PAINT_LARGE_HORIZONTAL_ICON)
|
||||
C.add_hiddenprint(user)
|
||||
C = new(left, paint_color, drawing, temp, graf_rot, PAINT_LARGE_HORIZONTAL_ICON)
|
||||
affected_turfs += left
|
||||
affected_turfs += right
|
||||
affected_turfs += target
|
||||
else
|
||||
to_chat(user, "<span class='warning'>There isn't enough space to paint!</span>")
|
||||
return
|
||||
C.add_hiddenprint(user)
|
||||
if(istagger)
|
||||
C.AddComponent(/datum/element/art, GOOD_ART)
|
||||
else
|
||||
C.AddComponent(/datum/element/art, BAD_ART)
|
||||
|
||||
if(!instant)
|
||||
to_chat(user, "<span class='notice'>You finish drawing \the [temp].</span>")
|
||||
@@ -462,52 +461,6 @@
|
||||
reagents.trans_to(t, ., volume_multiplier)
|
||||
check_empty(user)
|
||||
|
||||
|
||||
//////////////Gang mode stuff/////////////////
|
||||
/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_base_area(target)
|
||||
if(!A || (!is_station_level(A.z)) || !(A.area_flags & VALID_TERRITORY))
|
||||
to_chat(user, "<span class='warning'>[A] is unsuitable for tagging.</span>")
|
||||
return FALSE
|
||||
|
||||
var/spraying_over = FALSE
|
||||
for(var/G in target)
|
||||
var/obj/effect/decal/cleanable/crayon/gang/gangtag = G
|
||||
if(istype(gangtag))
|
||||
var/datum/antagonist/gang/GA = user.mind.has_antag_datum(/datum/antagonist/gang)
|
||||
if(gangtag.gang != GA.gang)
|
||||
spraying_over = TRUE
|
||||
break
|
||||
|
||||
var/occupying_gang = territory_claimed(A, user)
|
||||
if(occupying_gang && !spraying_over)
|
||||
to_chat(user, "<span class='danger'>[A] has already been tagged by the [occupying_gang] gang! You must get rid of or spray over the old tag first!</span>")
|
||||
return FALSE
|
||||
|
||||
// If you pass the gauntlet of checks, you're good to proceed
|
||||
return TRUE
|
||||
|
||||
/obj/item/toy/crayon/proc/territory_claimed(area/territory, mob/user)
|
||||
for(var/datum/team/gang/G in GLOB.gangs)
|
||||
if(territory.type in (G.territories|G.new_territories))
|
||||
. = 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/datum/antagonist/gang/G = user.mind.has_antag_datum(/datum/antagonist/gang)
|
||||
var/area/territory = get_base_area(target)
|
||||
|
||||
new /obj/effect/decal/cleanable/crayon/gang(target,G.gang,"graffiti",0,user) // Heres the gang tag.
|
||||
to_chat(user, "<span class='notice'>You tagged [territory] for your gang!</span>")
|
||||
/////////////////Gang end////////////////////
|
||||
|
||||
|
||||
/obj/item/toy/crayon/attack(mob/M, mob/user)
|
||||
if(edible && (M == user))
|
||||
to_chat(user, "You take a bite of the [src.name]. Delicious!")
|
||||
@@ -521,6 +474,49 @@
|
||||
else
|
||||
..()
|
||||
|
||||
//////////////Gang mode stuff/////////////////
|
||||
/obj/item/toy/crayon/proc/can_claim_for_gang(mob/user, atom/target)
|
||||
var/area/A = get_area(target)
|
||||
if(!A || (!is_station_level(A.z)))
|
||||
to_chat(user, "<span class='warning'>[A] is unsuitable for tagging.</span>")
|
||||
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, "<span class='warning'>You can't tag an APC.</span>")
|
||||
return FALSE
|
||||
|
||||
var/occupying_gang = territory_claimed(A, user)
|
||||
if(occupying_gang && !spraying_over)
|
||||
to_chat(user, "<span class='danger'>[A] has already been tagged by a gang! You must find and spray over the old tag first!</span>")
|
||||
return FALSE
|
||||
|
||||
// stolen from oldgang lmao
|
||||
return TRUE
|
||||
|
||||
/obj/item/toy/crayon/proc/tag_for_gang(mob/user, atom/target, datum/antagonist/gang/user_gang)
|
||||
for(var/obj/effect/decal/cleanable/crayon/old_marking in target)
|
||||
qdel(old_marking)
|
||||
|
||||
var/area/territory = get_area(target)
|
||||
|
||||
var/obj/effect/decal/cleanable/crayon/gang/tag = new /obj/effect/decal/cleanable/crayon/gang(target)
|
||||
tag.my_gang = user_gang.my_gang
|
||||
tag.icon_state = "[user_gang.gang_id]_tag"
|
||||
tag.name = "[tag.my_gang.name] gang tag"
|
||||
tag.desc = "Looks like someone's claimed this area for [tag.my_gang.name]."
|
||||
to_chat(user, "<span class='notice'>You tagged [territory] for [tag.my_gang.name]!</span>")
|
||||
|
||||
/obj/item/toy/crayon/proc/territory_claimed(area/territory, mob/user)
|
||||
for(var/obj/effect/decal/cleanable/crayon/gang/G in GLOB.gang_tags)
|
||||
if(get_area(G) == territory)
|
||||
return G
|
||||
|
||||
/////////////////Gang end////////////////////
|
||||
|
||||
/obj/item/toy/crayon/red
|
||||
icon_state = "crayonred"
|
||||
paint_color = "#DA0000"
|
||||
@@ -761,7 +757,7 @@
|
||||
|
||||
return
|
||||
|
||||
if(isobj(target))
|
||||
if(isobj(target) && !istype(target, /obj/effect/decal/cleanable/crayon/gang))
|
||||
if(actually_paints)
|
||||
if(istype(target, /obj/item/canvas)) //dont color our canvas neon green when im trying to paint please
|
||||
return
|
||||
@@ -866,26 +862,6 @@
|
||||
post_noise = FALSE
|
||||
reagent_contents = list(/datum/reagent/consumable/nothing = 1, /datum/reagent/toxin/mutetoxin = 1)
|
||||
|
||||
/obj/item/toy/crayon/spraycan/gang
|
||||
charges = 20 // Charges back to 20, which is the default value for them.
|
||||
gang = TRUE
|
||||
gang_tag_delay = 15 //Its 50% faster than a regular spraycan, for tagging. After-all they did spend points/meet the boss.
|
||||
|
||||
pre_noise = FALSE
|
||||
post_noise = TRUE // Its even more stealthy just a tad.
|
||||
|
||||
/obj/item/toy/crayon/spraycan/gang/Initialize(loc, datum/team/gang/G)
|
||||
..()
|
||||
if(G)
|
||||
gang = G
|
||||
paint_color = G.color
|
||||
update_icon()
|
||||
|
||||
/obj/item/toy/crayon/spraycan/gang/examine(mob/user)
|
||||
. = ..()
|
||||
if(user.mind && user.mind.has_antag_datum(/datum/antagonist/gang) || isobserver(user))
|
||||
. += "This spraycan has been specially modified with a stage 2 nozzle kit, making it faster."
|
||||
|
||||
/obj/item/toy/crayon/spraycan/infinite
|
||||
name = "infinite spraycan"
|
||||
charges = -1
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
if(target.mind.has_antag_datum(ANTAG_DATUM_VASSAL))
|
||||
SSticker.mode.remove_vassal(target.mind)
|
||||
|
||||
if(target.mind.has_antag_datum(/datum/antagonist/rev/head) || target.mind.unconvertable || target.mind.has_antag_datum(/datum/antagonist/gang/boss))
|
||||
if(target.mind.has_antag_datum(/datum/antagonist/rev/head) || (target.mind.unconvertable))
|
||||
if(!silent)
|
||||
target.visible_message("<span class='warning'>[target] seems to resist the implant!</span>", "<span class='warning'>You feel something interfering with your mental conditioning, but you resist it!</span>")
|
||||
var/obj/item/implanter/I = loc
|
||||
|
||||
@@ -657,3 +657,9 @@
|
||||
desc = "Worn by snails as armor and storage compartment."
|
||||
icon_state = "snailshell"
|
||||
item_state = "snailshell"
|
||||
|
||||
/obj/item/storage/backpack/henchmen
|
||||
name = "wings"
|
||||
desc = "Granted to the henchmen who deserve it. This probably doesn't include you."
|
||||
icon_state = "henchmen"
|
||||
item_state = "henchmen"
|
||||
|
||||
539
code/game/objects/items/summon.dm
Normal file
539
code/game/objects/items/summon.dm
Normal file
@@ -0,0 +1,539 @@
|
||||
/// doing nothing/orbiting idly
|
||||
#define STATE_IDLE 0
|
||||
/// performing reset animation
|
||||
#define STATE_RESET 1
|
||||
/// performing attack animation
|
||||
#define STATE_ATTACK 2
|
||||
/// performing animation between attacks
|
||||
#define STATE_RECOVER 3
|
||||
|
||||
/**
|
||||
* Simple summon weapon code in this file
|
||||
*
|
||||
* tl;dr latch onto target, repeatedly proc attacks, animate using transforms,
|
||||
* no real hitboxes/collisions, think of /datum/component/orbit-adjacent
|
||||
*/
|
||||
/obj/item/summon
|
||||
name = "a horrifying mistake"
|
||||
desc = "Why does this exist?"
|
||||
/// datum type
|
||||
var/host_type
|
||||
/// number of summons
|
||||
var/summon_count = 6
|
||||
/// how long it takes for a "stack" to fall off by itself
|
||||
var/stack_duration = 5 SECONDS
|
||||
/// our summon weapon host
|
||||
var/datum/summon_weapon_host/host
|
||||
/// range summons will chase to
|
||||
var/range = 7
|
||||
/// are we a ranged weapon?
|
||||
var/melee_only = TRUE
|
||||
|
||||
/obj/item/summon/Initialize()
|
||||
. = ..()
|
||||
if(host_type)
|
||||
host = new host_type(src, summon_count, range)
|
||||
|
||||
/obj/item/summon/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
|
||||
. = ..()
|
||||
if(!host)
|
||||
return
|
||||
if(!proximity_flag && melee_only)
|
||||
return
|
||||
Target(target)
|
||||
|
||||
/obj/item/summon/dropped(mob/user, silent)
|
||||
. = ..()
|
||||
addtimer(CALLBACK(src, .proc/check_activation), 0, TIMER_UNIQUE)
|
||||
|
||||
/obj/item/summon/equipped(mob/user, slot)
|
||||
. = ..()
|
||||
addtimer(CALLBACK(src, .proc/check_activation), 0, TIMER_UNIQUE)
|
||||
|
||||
/obj/item/summon/proc/check_activation()
|
||||
if(!host)
|
||||
return
|
||||
if(!isliving(loc))
|
||||
host.SetMaster(null)
|
||||
var/mob/living/L = loc
|
||||
if(!istype(L))
|
||||
return
|
||||
if(!L.is_holding(src))
|
||||
host.SetMaster(src)
|
||||
host.Suppress()
|
||||
host.SetMaster(L)
|
||||
host.Wake()
|
||||
|
||||
/obj/item/summon/proc/Target(atom/victim)
|
||||
if(!host?.CheckTarget(victim))
|
||||
return
|
||||
host.AutoTarget(victim, stack_duration)
|
||||
|
||||
/obj/item/summon/sword
|
||||
name = "spectral blade"
|
||||
desc = "An eldritch blade that summons phantasms to attack one's enemies."
|
||||
icon = 'icons/obj/items_and_weapons.dmi'
|
||||
icon_state = "spectral"
|
||||
item_state = "spectral"
|
||||
lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
|
||||
host_type = /datum/summon_weapon_host/sword
|
||||
force = 15
|
||||
sharpness = SHARP_EDGED
|
||||
|
||||
/**
|
||||
* Serves as the master datum for summon weapons
|
||||
*/
|
||||
/datum/summon_weapon_host
|
||||
/// master atom
|
||||
var/atom/master
|
||||
/// suppressed?
|
||||
var/active = TRUE
|
||||
/// actual projectiles
|
||||
var/list/datum/summon_weapon/controlled
|
||||
/// active projectiles - refreshing a projectile reorders the list, so if they all have the same stack durations, you can trust the list to have last-refreshed at [1]
|
||||
var/list/datum/summon_weapon/attacking
|
||||
/// idle projectiles
|
||||
var/list/datum/summon_weapon/idle
|
||||
/// projectile type
|
||||
var/weapon_type
|
||||
/// default stack time
|
||||
var/stack_time = 5 SECONDS
|
||||
/// range
|
||||
var/range = 7
|
||||
|
||||
/datum/summon_weapon_host/New(atom/master, count, range)
|
||||
src.master = master
|
||||
src.range = range
|
||||
controlled = list()
|
||||
attacking = list()
|
||||
idle = list()
|
||||
Create(count)
|
||||
|
||||
/datum/summon_weapon_host/Destroy()
|
||||
QDEL_LIST(controlled)
|
||||
master = null
|
||||
return ..()
|
||||
|
||||
/datum/summon_weapon_host/proc/SetMaster(atom/master, reset_on_failure = TRUE)
|
||||
var/changed = src.master != master
|
||||
src.master = master
|
||||
if(changed)
|
||||
for(var/datum/summon_weapon/weapon as anything in idle)
|
||||
weapon.Reset()
|
||||
if(!master && reset_on_failure)
|
||||
for(var/datum/summon_weapon/weapon as anything in attacking)
|
||||
weapon.Reset()
|
||||
|
||||
/datum/summon_weapon_host/proc/Create(count)
|
||||
if(!weapon_type)
|
||||
return
|
||||
for(var/i in 1 to min(count, clamp(20 - controlled.len - count, 0, 20)))
|
||||
var/datum/summon_weapon/weapon = new weapon_type
|
||||
Associate(weapon)
|
||||
|
||||
/datum/summon_weapon_host/proc/Associate(datum/summon_weapon/linking)
|
||||
if(linking.host && linking.host != src)
|
||||
linking.host.Disassociate(linking)
|
||||
linking.host = src
|
||||
controlled |= linking
|
||||
linking.Reset()
|
||||
|
||||
/datum/summon_weapon_host/proc/Disassociate(datum/summon_weapon/unlinking, reset = TRUE, autodel)
|
||||
if(unlinking.host == src)
|
||||
unlinking.host = null
|
||||
controlled -= unlinking
|
||||
if(reset)
|
||||
unlinking.Reset(del_no_host = autodel)
|
||||
idle -= unlinking
|
||||
attacking -= unlinking
|
||||
|
||||
/datum/summon_weapon_host/proc/AutoTarget(atom/victim, duration = stack_time)
|
||||
if(!active)
|
||||
return
|
||||
var/datum/summon_weapon/weapon = (idle.len && idle[1]) || (attacking.len && attacking[1])
|
||||
if(!weapon)
|
||||
return
|
||||
if(!CheckTarget(victim))
|
||||
return
|
||||
weapon.Target(victim)
|
||||
if(duration)
|
||||
weapon.ResetIn(duration)
|
||||
|
||||
/datum/summon_weapon_host/proc/OnTarget(datum/summon_weapon/weapon, atom/victim)
|
||||
attacking -= weapon
|
||||
idle -= weapon
|
||||
attacking |= weapon
|
||||
|
||||
/datum/summon_weapon_host/proc/OnReset(datum/summon_weapon/weapon, atom/victim)
|
||||
attacking -= weapon
|
||||
idle |= weapon
|
||||
|
||||
/datum/summon_weapon_host/proc/CheckTarget(atom/victim)
|
||||
if(isitem(victim))
|
||||
return FALSE
|
||||
if(QDELETED(victim))
|
||||
return FALSE
|
||||
if(victim == master)
|
||||
return FALSE
|
||||
if(isliving(victim))
|
||||
var/mob/living/L = victim
|
||||
if(L.stat == DEAD)
|
||||
return FALSE
|
||||
return TRUE
|
||||
if(isobj(victim))
|
||||
var/obj/O = victim
|
||||
return (O.obj_flags & CAN_BE_HIT)
|
||||
return FALSE
|
||||
|
||||
/datum/summon_weapon_host/proc/Suppress()
|
||||
active = FALSE
|
||||
for(var/datum/summon_weapon/weapon as anything in controlled)
|
||||
weapon.Reset()
|
||||
|
||||
/datum/summon_weapon_host/proc/Wake()
|
||||
active = TRUE
|
||||
for(var/datum/summon_weapon/weapon as anything in controlled)
|
||||
weapon.Reset()
|
||||
|
||||
/datum/summon_weapon_host/sword
|
||||
weapon_type = /datum/summon_weapon/sword
|
||||
|
||||
/**
|
||||
* A singular summoned object
|
||||
*
|
||||
* How summon weapons work:
|
||||
*
|
||||
* Reset() - makes it go back to its master.
|
||||
* Target() - locks onto a target for a duration
|
||||
*
|
||||
* The biggest challenge is synchronizing animations.
|
||||
* Variables keep track of when things tick, but,
|
||||
* animations are client-timed, and not server-timed
|
||||
*
|
||||
* Animations:
|
||||
* The weapon can only track its "intended" angle and dist
|
||||
* "Current" pixel x/y are always calculated relative to a target from the current orbiting atom the physical effect is on
|
||||
* There's 3 animations,
|
||||
* MoveTo(location, angle, dist, rotation)
|
||||
* Orbit(location)
|
||||
* Rotate(degrees)
|
||||
*
|
||||
* And an non-animation that just snaps it to a location,
|
||||
* HardReset(location)
|
||||
*/
|
||||
/datum/summon_weapon
|
||||
/// name
|
||||
var/name = "summoned weapon"
|
||||
/// host
|
||||
var/datum/summon_weapon_host/host
|
||||
/// icon file
|
||||
var/icon = 'icons/effects/summon.dmi'
|
||||
/// icon state
|
||||
var/icon_state
|
||||
/// mutable_appearance to use, will skip making from icon/icon state if so
|
||||
var/mutable_appearance/appearance
|
||||
/// the actual effect
|
||||
var/atom/movable/summon_weapon_effect/atom
|
||||
/// currently locked attack target
|
||||
var/atom/victim
|
||||
/// current angle from victim - clockwise from 0. null if not attacking.
|
||||
var/angle
|
||||
/// current distance from victim - pixels
|
||||
var/dist
|
||||
/// current rotation - angles clockwise from north
|
||||
var/rotation
|
||||
/// rand dist to rotate during reattack phase
|
||||
var/angle_vary = 45
|
||||
/// orbit distance from victim - pixels
|
||||
var/orbit_dist = 72
|
||||
/// orbit distance variation from victim
|
||||
var/orbit_dist_vary = 24
|
||||
/// attack delay in deciseconds - this is time spent between attacks
|
||||
var/attack_speed = 1.5
|
||||
/// attack length in deciseconds - this is the attack animation speed in total
|
||||
var/attack_length = 1.5
|
||||
/// attack damage
|
||||
var/attack_damage = 5
|
||||
/// reset animation duration
|
||||
var/reset_speed = 2
|
||||
/// attack damtype
|
||||
var/attack_type = BRUTE
|
||||
/// attack sound
|
||||
var/attack_sound = list(
|
||||
'sound/weapons/bladeslice.ogg',
|
||||
'sound/weapons/bladesliceb.ogg'
|
||||
)
|
||||
/// attack verb
|
||||
var/attack_verb = list(
|
||||
"rended",
|
||||
"pierced",
|
||||
"penetrated",
|
||||
"sliced"
|
||||
)
|
||||
/// current state
|
||||
var/state = STATE_IDLE
|
||||
/// animation locked until
|
||||
var/animation_lock
|
||||
/// animation lock timer
|
||||
var/animation_timerid
|
||||
/// reset timerid
|
||||
var/reset_timerid
|
||||
|
||||
/datum/summon_weapon/New(mutable_appearance/appearance_override)
|
||||
if(appearance_override)
|
||||
appearance = appearance_override
|
||||
Setup()
|
||||
attack_verb = typelist(NAMEOF(src, attack_verb), attack_verb)
|
||||
attack_sound = typelist(NAMEOF(src, attack_sound), attack_sound)
|
||||
|
||||
/datum/summon_weapon/Destroy()
|
||||
host.Disassociate(src, autodel = FALSE)
|
||||
QDEL_NULL(atom)
|
||||
QDEL_NULL(appearance)
|
||||
return ..()
|
||||
|
||||
/datum/summon_weapon/proc/Setup()
|
||||
atom = new
|
||||
if(!appearance)
|
||||
GenerateAppearance()
|
||||
atom.appearance = appearance
|
||||
atom.moveToNullspace()
|
||||
if(host)
|
||||
Reset()
|
||||
|
||||
/datum/summon_weapon/proc/GenerateAppearance()
|
||||
if(!appearance)
|
||||
appearance = new
|
||||
appearance.icon = icon
|
||||
appearance.icon_state = icon_state
|
||||
appearance.mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
||||
appearance.opacity = FALSE
|
||||
appearance.plane = GAME_PLANE
|
||||
appearance.layer = ABOVE_MOB_LAYER
|
||||
appearance.appearance_flags = KEEP_TOGETHER
|
||||
appearance.overlays = list(
|
||||
emissive_appearance(icon, icon_state)
|
||||
)
|
||||
|
||||
/datum/summon_weapon/proc/Reset(immediate = FALSE, del_no_host = TRUE)
|
||||
angle = null
|
||||
victim = null
|
||||
if(reset_timerid)
|
||||
deltimer(reset_timerid)
|
||||
reset_timerid = null
|
||||
host?.OnReset(src)
|
||||
atom.Release()
|
||||
state = STATE_RESET
|
||||
if(!host)
|
||||
if(del_no_host)
|
||||
qdel(src)
|
||||
return
|
||||
HardReset(null)
|
||||
atom.moveToNullspace()
|
||||
return
|
||||
if(immediate)
|
||||
if(animation_timerid)
|
||||
deltimer(animation_timerid)
|
||||
Act()
|
||||
else
|
||||
Wake()
|
||||
|
||||
/datum/summon_weapon/proc/ResetIn(ds)
|
||||
reset_timerid = addtimer(CALLBACK(src, .proc/Reset), ds, TIMER_STOPPABLE)
|
||||
|
||||
/datum/summon_weapon/proc/Target(atom/victim)
|
||||
if(!istype(victim) || !isturf(victim.loc) || (host && !host.CheckTarget(victim)))
|
||||
Reset()
|
||||
return
|
||||
src.victim = victim
|
||||
host.OnTarget(src, victim)
|
||||
state = STATE_ATTACK
|
||||
Wake()
|
||||
|
||||
/datum/summon_weapon/proc/Wake()
|
||||
if(!animation_timerid)
|
||||
Act()
|
||||
|
||||
/datum/summon_weapon/proc/AnimationLock(duration)
|
||||
if(animation_timerid)
|
||||
deltimer(animation_timerid)
|
||||
animation_timerid = addtimer(CALLBACK(src, .proc/Act), duration, TIMER_CLIENT_TIME | TIMER_STOPPABLE)
|
||||
|
||||
/datum/summon_weapon/proc/Act()
|
||||
animation_timerid = null
|
||||
switch(state)
|
||||
if(STATE_IDLE)
|
||||
return
|
||||
if(STATE_ATTACK)
|
||||
if(!isturf(victim.loc) || (host && !host.CheckTarget(victim)))
|
||||
Reset(TRUE)
|
||||
return
|
||||
state = STATE_RECOVER
|
||||
// register hit at the halfway mark
|
||||
// we can do better math to approximate when the attack will hit but i'm too tired to bother
|
||||
addtimer(CALLBACK(src, .proc/Hit, victim), attack_length / 2, TIMER_CLIENT_TIME)
|
||||
// we need to approximate our incoming angle - again, better math exists but why bother
|
||||
var/incoming_angle = angle
|
||||
if(isturf(atom.loc) && (atom.loc != victim.loc))
|
||||
incoming_angle = Get_Angle(atom.loc, victim.loc)
|
||||
// pierce through target
|
||||
// we do not want to turn while doing this so we pierce through them visually
|
||||
incoming_angle += 180
|
||||
var/outgoing_angle = SIMPLIFY_DEGREES(incoming_angle)
|
||||
AnimationLock(MoveTo(victim, null, outgoing_angle, orbit_dist + rand(-orbit_dist_vary, orbit_dist_vary), outgoing_angle, attack_length))
|
||||
if(STATE_RESET)
|
||||
state = STATE_IDLE
|
||||
if(!host || !host.active || !get_turf(host.master))
|
||||
atom.moveToNullspace()
|
||||
src.angle = null
|
||||
src.dist = null
|
||||
src.rotation = null
|
||||
return
|
||||
var/reset_angle = rand(0, 360)
|
||||
AnimationLock(MoveTo(host.master, null, reset_angle, 30, 90, reset_speed))
|
||||
addtimer(CALLBACK(src, .proc/Orbit, host.master, reset_angle, 30, 3 SECONDS), reset_speed, TIMER_CLIENT_TIME)
|
||||
if(STATE_RECOVER)
|
||||
state = STATE_ATTACK
|
||||
AnimationLock(Rotate(rand(-angle_vary, angle_vary), attack_speed, null))
|
||||
|
||||
/datum/summon_weapon/proc/Hit(atom/victim)
|
||||
if(!isobj(victim) && !isliving(victim))
|
||||
return FALSE
|
||||
if(isliving(victim))
|
||||
var/mob/living/L = victim
|
||||
L.apply_damage(attack_damage, attack_type)
|
||||
playsound(victim, pick(attack_sound), 75)
|
||||
else if(isobj(victim))
|
||||
var/obj/O = victim
|
||||
O.take_damage(attack_damage, attack_type)
|
||||
return TRUE
|
||||
|
||||
/**
|
||||
* relative to defaults to current location
|
||||
*/
|
||||
/datum/summon_weapon/proc/MoveTo(atom/destination, atom/relative_to, angle = 0, dist = 64, rotation = 180, time)
|
||||
. = time
|
||||
// construct final transform
|
||||
var/matrix/dest = ConstructMatrix(angle, dist, rotation)
|
||||
|
||||
// move to
|
||||
atom.Lock(destination)
|
||||
|
||||
// get relative first positions
|
||||
relative_to = get_turf(relative_to || atom.locked)
|
||||
destination = get_turf(destination)
|
||||
// if none, move to immediately and end
|
||||
if(!relative_to)
|
||||
atom.transform = dest
|
||||
src.angle = angle
|
||||
src.dist = dist
|
||||
src.rotation = rotation
|
||||
// end animations
|
||||
animate(atom, time = 0, flags = ANIMATION_END_NOW)
|
||||
return 0
|
||||
|
||||
// grab source
|
||||
var/rel_x = (destination.x - relative_to.x) * world.icon_size + src.dist * sin(src.angle)
|
||||
var/rel_y = (destination.y - relative_to.y) * world.icon_size + src.dist * cos(src.angle)
|
||||
|
||||
// construct source matrix
|
||||
var/matrix/source = new
|
||||
|
||||
source.Turn((relative_to == get_turf(atom.locked))? src.rotation : Get_Angle(relative_to, destination))
|
||||
source.Translate(rel_x, rel_y)
|
||||
|
||||
// set vars
|
||||
src.angle = angle
|
||||
src.dist = dist
|
||||
src.rotation = rotation
|
||||
|
||||
// animate
|
||||
atom.transform = source
|
||||
animate(atom, transform = dest, time, FALSE, LINEAR_EASING, ANIMATION_LINEAR_TRANSFORM | ANIMATION_END_NOW)
|
||||
|
||||
/**
|
||||
* rotation defaults to facing towards locked atom
|
||||
*/
|
||||
/datum/summon_weapon/proc/Rotate(degrees, time, rotation)
|
||||
. = time
|
||||
if(!dist)
|
||||
return 0
|
||||
var/matrix/M = ConstructMatrix(angle + degrees, dist, rotation || src.rotation)
|
||||
if(rotation)
|
||||
src.rotation = rotation
|
||||
angle += degrees
|
||||
animate(atom, transform = M, time, FALSE, LINEAR_EASING, ANIMATION_END_NOW | ANIMATION_LINEAR_TRANSFORM)
|
||||
|
||||
/datum/summon_weapon/proc/Orbit(atom/destination, initial_degrees = rand(0, 360), dist, speed)
|
||||
. = 0
|
||||
atom.Lock(destination)
|
||||
animate(atom, 0, FALSE, flags = ANIMATION_END_NOW)
|
||||
atom.transform = ConstructMatrix(initial_degrees, dist, 90)
|
||||
atom.SpinAnimation(speed, parallel = FALSE, segments = 10)
|
||||
// we can't predict dist/angle anymre because clienttime vs servertime.
|
||||
// well, we can, but, let's not be bothered with timeofday math eh.
|
||||
dist = 0
|
||||
angle = 0
|
||||
|
||||
/datum/summon_weapon/proc/ConstructMatrix(angle = 0, dist = 64, rotation = 0)
|
||||
var/matrix/M = new
|
||||
M.Turn(rotation)
|
||||
M.Translate(0, dist)
|
||||
M.Turn(angle)
|
||||
return M
|
||||
|
||||
/datum/summon_weapon/proc/HardReset(atom/snap_to)
|
||||
if(animation_timerid)
|
||||
deltimer(animation_timerid)
|
||||
atom.Release()
|
||||
atom.forceMove(snap_to)
|
||||
atom.transform = null
|
||||
|
||||
/datum/summon_weapon/sword
|
||||
name = "spectral blade"
|
||||
icon_state = "sword"
|
||||
attack_damage = 5
|
||||
attack_speed = 1.5
|
||||
attack_length = 1.5
|
||||
|
||||
/atom/movable/summon_weapon_effect
|
||||
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
||||
plane = GAME_PLANE
|
||||
layer = ABOVE_MOB_LAYER
|
||||
opacity = FALSE
|
||||
density = FALSE
|
||||
/// locked atom
|
||||
var/atom/locked
|
||||
|
||||
/atom/movable/summon_weapon_effect/Destroy()
|
||||
Release()
|
||||
return ..()
|
||||
|
||||
/atom/movable/summon_weapon_effect/proc/Lock(atom/target)
|
||||
if(locked == target)
|
||||
return
|
||||
if(locked)
|
||||
Release()
|
||||
if(!target)
|
||||
return
|
||||
locked = target
|
||||
forceMove(locked.loc)
|
||||
if(ismovable(locked))
|
||||
RegisterSignal(locked, COMSIG_MOVABLE_MOVED, .proc/Update)
|
||||
|
||||
/atom/movable/summon_weapon_effect/proc/Release()
|
||||
if(ismovable(locked))
|
||||
UnregisterSignal(locked, COMSIG_MOVABLE_MOVED)
|
||||
locked = null
|
||||
|
||||
/atom/movable/summon_weapon_effect/proc/Update()
|
||||
if(!locked)
|
||||
return
|
||||
if(loc != locked.loc)
|
||||
forceMove(locked.loc)
|
||||
|
||||
#undef STATE_IDLE
|
||||
#undef STATE_ATTACK
|
||||
#undef STATE_RECOVER
|
||||
#undef STATE_RESET
|
||||
@@ -1105,7 +1105,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
|
||||
icon_state = "madeyoulook"
|
||||
force = 0
|
||||
throwforce = 0
|
||||
item_flags = DROPDEL | ABSTRACT // | HAND_ITEM
|
||||
item_flags = DROPDEL | ABSTRACT | HAND_ITEM
|
||||
attack_verb = list("bopped")
|
||||
|
||||
/obj/item/circlegame/Initialize()
|
||||
@@ -1207,7 +1207,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
|
||||
item_state = "nothing"
|
||||
force = 0
|
||||
throwforce = 0
|
||||
item_flags = DROPDEL | ABSTRACT // | HAND_ITEM
|
||||
item_flags = DROPDEL | ABSTRACT | HAND_ITEM
|
||||
attack_verb = list("slapped")
|
||||
hitsound = 'sound/effects/snap.ogg'
|
||||
|
||||
@@ -1227,6 +1227,120 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/obj/item/slapper/on_offered(mob/living/carbon/offerer)
|
||||
. = TRUE
|
||||
|
||||
if(!(locate(/mob/living/carbon) in orange(1, offerer)))
|
||||
visible_message(span_danger("[offerer] raises [offerer.p_their()] arm, looking around for a high-five, but there's no one around!"), \
|
||||
span_warning("You post up, looking for a high-five, but finding no one within range!"), null, 2)
|
||||
return
|
||||
|
||||
offerer.visible_message(span_notice("[offerer] raises [offerer.p_their()] arm, looking for a high-five!"), \
|
||||
span_notice("You post up, looking for a high-five!"), null, 2)
|
||||
offerer.apply_status_effect(STATUS_EFFECT_OFFERING, src, /atom/movable/screen/alert/give/highfive)
|
||||
|
||||
/// Yeah broh! This is where we do the high-fiving (or high-tenning :o)
|
||||
/obj/item/slapper/on_offer_taken(mob/living/carbon/offerer, mob/living/carbon/taker)
|
||||
. = TRUE
|
||||
|
||||
var/open_hands_taker
|
||||
var/slappers_giver
|
||||
for(var/i in taker.held_items) // see how many hands the taker has open for high'ing
|
||||
if(isnull(i))
|
||||
open_hands_taker++
|
||||
|
||||
if(!open_hands_taker)
|
||||
to_chat(taker, span_warning("You can't high-five [offerer] with no open hands!"))
|
||||
SEND_SIGNAL(taker, COMSIG_ADD_MOOD_EVENT, "high_five", /datum/mood_event/high_five_full_hand) // not so successful now!
|
||||
return
|
||||
|
||||
for(var/i in offerer.held_items)
|
||||
var/obj/item/slapper/slap_check = i
|
||||
if(istype(slap_check))
|
||||
slappers_giver++
|
||||
|
||||
if(slappers_giver >= 2) // we only check this if it's already established the taker has 2+ hands free
|
||||
offerer.visible_message(span_notice("[taker] enthusiastically high-tens [offerer]!"), span_nicegreen("Wow! You're high-tenned [taker]!"), span_hear("You hear a sickening sound of flesh hitting flesh!"), ignored_mobs=taker)
|
||||
to_chat(taker, span_nicegreen("You give high-tenning [offerer] your all!"))
|
||||
playsound(offerer, 'sound/weapons/slap.ogg', 100, TRUE, 1)
|
||||
SEND_SIGNAL(offerer, COMSIG_ADD_MOOD_EVENT, "high_five", /datum/mood_event/high_ten)
|
||||
SEND_SIGNAL(taker, COMSIG_ADD_MOOD_EVENT, "high_five", /datum/mood_event/high_ten)
|
||||
else
|
||||
offerer.visible_message(span_notice("[taker] high-fives [offerer]!"), span_nicegreen("All right! You're high-fived by [taker]!"), span_hear("You hear a sickening sound of flesh hitting flesh!"), ignored_mobs=taker)
|
||||
to_chat(taker, span_nicegreen("You high-five [offerer]!"))
|
||||
playsound(offerer, 'sound/weapons/slap.ogg', 50, TRUE, -1)
|
||||
SEND_SIGNAL(offerer, COMSIG_ADD_MOOD_EVENT, "high_five", /datum/mood_event/high_five)
|
||||
SEND_SIGNAL(taker, COMSIG_ADD_MOOD_EVENT, "high_five", /datum/mood_event/high_five)
|
||||
qdel(src)
|
||||
|
||||
/// Gangster secret handshakes.
|
||||
/obj/item/slapper/secret_handshake
|
||||
name = "Secret Handshake"
|
||||
icon_state = "recruit"
|
||||
icon = 'icons/obj/gang/actions.dmi'
|
||||
/// References the active families gamemode handler (if one exists), for adding new family members to.
|
||||
var/datum/gang_handler/handler
|
||||
/// The typepath of the gang antagonist datum that the person who uses the package should have added to them -- remember that the distinction between e.g. Ballas and Grove Street is on the antag datum level, not the team datum level.
|
||||
var/gang_to_use
|
||||
/// The team datum that the person who uses this package should be added to.
|
||||
var/datum/team/gang/team_to_use
|
||||
|
||||
|
||||
/// Adds the user to the family that this package corresponds to, dispenses the free_clothes of that family, and adds them to the handler if it exists.
|
||||
/obj/item/slapper/secret_handshake/proc/add_to_gang(mob/living/user, original_name)
|
||||
var/datum/antagonist/gang/swappin_sides = new gang_to_use()
|
||||
swappin_sides.original_name = original_name
|
||||
swappin_sides.handler = handler
|
||||
user.mind.add_antag_datum(swappin_sides, team_to_use)
|
||||
var/policy = get_policy(ROLE_FAMILIES)
|
||||
if(policy)
|
||||
to_chat(user, policy)
|
||||
team_to_use.add_member(user.mind)
|
||||
swappin_sides.equip_gangster_in_inventory()
|
||||
if (!isnull(handler) && !handler.gangbangers.Find(user.mind)) // if we have a handler and they're not tracked by it
|
||||
handler.gangbangers += user.mind
|
||||
|
||||
/// Checks if the user is trying to use the package of the family they are in, and if not, adds them to the family, with some differing processing depending on whether the user is already a family member.
|
||||
/obj/item/slapper/secret_handshake/proc/attempt_join_gang(mob/living/user)
|
||||
if(!user?.mind)
|
||||
return
|
||||
var/datum/antagonist/gang/is_gangster = user.mind.has_antag_datum(/datum/antagonist/gang)
|
||||
var/real_name_backup = user.real_name
|
||||
if(is_gangster)
|
||||
if(is_gangster.my_gang == team_to_use)
|
||||
return
|
||||
real_name_backup = is_gangster.original_name
|
||||
is_gangster.my_gang.remove_member(user.mind)
|
||||
user.mind.remove_antag_datum(/datum/antagonist/gang)
|
||||
add_to_gang(user, real_name_backup)
|
||||
|
||||
/obj/item/slapper/secret_handshake/on_offer_taken(mob/living/carbon/offerer, mob/living/carbon/taker)
|
||||
. = TRUE
|
||||
if (!(null in taker.held_items))
|
||||
to_chat(taker, span_warning("You can't get taught the secret handshake if [offerer] has no free hands!"))
|
||||
return
|
||||
|
||||
if(HAS_TRAIT(taker, TRAIT_MINDSHIELD))
|
||||
to_chat(taker, "You attended a seminar on not signing up for a gang and are not interested.")
|
||||
return
|
||||
|
||||
var/datum/antagonist/gang/is_gangster = taker.mind.has_antag_datum(/datum/antagonist/gang)
|
||||
if(is_gangster?.starter_gangster)
|
||||
if(is_gangster.my_gang == team_to_use)
|
||||
to_chat(taker, "You started your family. You don't need to join it.")
|
||||
return
|
||||
to_chat(taker, "You started your family. You can't turn your back on it now.")
|
||||
return
|
||||
|
||||
offerer.visible_message(span_notice("[taker] is taught the secret handshake by [offerer]!"), span_nicegreen("All right! You've taught the secret handshake to [taker]!"), span_hear("You hear a bunch of weird shuffling and flesh slapping sounds!"), ignored_mobs=taker)
|
||||
to_chat(taker, span_nicegreen("You get taught the secret handshake by [offerer]!"))
|
||||
var/datum/antagonist/gang/owner_gang_datum = offerer.mind.has_antag_datum(/datum/antagonist/gang)
|
||||
handler = owner_gang_datum.handler
|
||||
gang_to_use = owner_gang_datum.type
|
||||
team_to_use = owner_gang_datum.my_gang
|
||||
attempt_join_gang(taker)
|
||||
qdel(src)
|
||||
|
||||
/obj/item/extendohand
|
||||
name = "extendo-hand"
|
||||
desc = "Futuristic tech has allowed these classic spring-boxing toys to essentially act as a fully functional hand-operated hand prosthetic."
|
||||
@@ -1261,7 +1375,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
|
||||
// attack_verb_simple = list("whack", "thwack", "wallop", "sock")
|
||||
// icon = 'icons/obj/items_and_weapons.dmi'
|
||||
// icon_state = "gohei"
|
||||
// inhand_icon_state = "gohei"
|
||||
// item_state = "gohei"
|
||||
// lefthand_file = 'icons/mob/inhands/weapons/staves_lefthand.dmi'
|
||||
// righthand_file = 'icons/mob/inhands/weapons/staves_righthand.dmi'
|
||||
|
||||
|
||||
@@ -531,7 +531,7 @@
|
||||
|
||||
/obj/structure/closet/get_remote_view_fullscreens(mob/user)
|
||||
if(user.stat == DEAD || !(user.sight & (SEEOBJS|SEEMOBS)))
|
||||
user.overlay_fullscreen("remote_view", /atom/movable/screen/fullscreen/impaired, 1)
|
||||
user.overlay_fullscreen("remote_view", /atom/movable/screen/fullscreen/scaled/impaired, 1)
|
||||
|
||||
/obj/structure/closet/emp_act(severity)
|
||||
. = ..()
|
||||
|
||||
@@ -139,7 +139,7 @@ GLOBAL_LIST_EMPTY(bodycontainers) //Let them act as spawnpoints for revenants an
|
||||
|
||||
/obj/structure/bodycontainer/get_remote_view_fullscreens(mob/user)
|
||||
if(user.stat == DEAD || !(user.sight & (SEEOBJS|SEEMOBS)))
|
||||
user.overlay_fullscreen("remote_view", /atom/movable/screen/fullscreen/impaired, 2)
|
||||
user.overlay_fullscreen("remote_view", /atom/movable/screen/fullscreen/scaled/impaired, 2)
|
||||
/*
|
||||
* Morgue
|
||||
*/
|
||||
|
||||
@@ -115,9 +115,9 @@ GLOBAL_PROTECT(LastAdminCalledProc)
|
||||
//adv proc call this, ya nerds
|
||||
/world/proc/WrapAdminProcCall(datum/target, procname, list/arguments)
|
||||
if(target == GLOBAL_PROC)
|
||||
return call("/proc/[procname]")(arglist(arguments))
|
||||
else if(target != world)
|
||||
return call(target, procname)(arglist(arguments))
|
||||
return text2path("/proc/[procname]")? call("/proc/[procname]")(arglist(arguments)) : null
|
||||
else if(target != world && istype(target, /datum)) // isdatum check incase someone manages to call WrapAdminProcCall(global) which would otherwise crash the process entirely
|
||||
return hascall(target, procname)? call(target, procname)(arglist(arguments)) : null
|
||||
else
|
||||
log_admin("[key_name(usr)] attempted to call world/proc/[procname] with arguments: [english_list(arguments)]")
|
||||
|
||||
|
||||
@@ -920,10 +920,10 @@
|
||||
else
|
||||
dat += "<td width='20%'><a href='?src=[REF(src)];[HrefToken()];jobban3=alien;jobban4=[REF(M)]'>Alien</a></td>"
|
||||
//Gang
|
||||
if(jobban_isbanned(M, ROLE_GANG) || isbanned_dept)
|
||||
dat += "<td width='20%'><a href='?src=[REF(src)];[HrefToken()];jobban3=gang;jobban4=[REF(M)]'><font color=red>Gang</font></a></td>"
|
||||
if(jobban_isbanned(M, ROLE_FAMILIES) || isbanned_dept)
|
||||
dat += "<td width='20%'><a href='?src=[REF(src)];[HrefToken()];jobban3=gang;jobban4=[REF(M)]'><font color=red>Families</font></a></td>"
|
||||
else
|
||||
dat += "<td width='20%'><a href='?src=[REF(src)];[HrefToken()];jobban3=gang;jobban4=[REF(M)]'>Gang</a></td>"
|
||||
dat += "<td width='20%'><a href='?src=[REF(src)];[HrefToken()];jobban3=gang;jobban4=[REF(M)]'>Families</a></td>"
|
||||
//Bloodsucker
|
||||
if(jobban_isbanned(M, ROLE_BLOODSUCKER) || isbanned_dept)
|
||||
dat += "<td width='20%'><a href='?src=[REF(src)];[HrefToken()];jobban3=bloodsucker;jobban4=[REF(M)]'><font color=red>Bloodsucker</font></a></td>"
|
||||
@@ -1008,7 +1008,7 @@
|
||||
if("ghostroles")
|
||||
joblist += list(ROLE_PAI, ROLE_POSIBRAIN, ROLE_DRONE , ROLE_DEATHSQUAD, ROLE_LAVALAND, ROLE_SENTIENCE)
|
||||
if("teamantags")
|
||||
joblist += list(ROLE_OPERATIVE, ROLE_REV, ROLE_CULTIST, ROLE_SERVANT_OF_RATVAR, ROLE_ABDUCTOR, ROLE_ALIEN, ROLE_GANG)
|
||||
joblist += list(ROLE_OPERATIVE, ROLE_REV, ROLE_CULTIST, ROLE_SERVANT_OF_RATVAR, ROLE_ABDUCTOR, ROLE_ALIEN, ROLE_FAMILIES)
|
||||
if("convertantags")
|
||||
joblist += list(ROLE_REV, ROLE_CULTIST, ROLE_SERVANT_OF_RATVAR, ROLE_ALIEN)
|
||||
if("otherroles")
|
||||
|
||||
@@ -3,11 +3,19 @@
|
||||
/proc/_abs(A)
|
||||
return abs(A)
|
||||
|
||||
/proc/_animate(atom/A, set_vars, time = 10, loop = 1, easing = LINEAR_EASING, flags = null)
|
||||
var/mutable_appearance/MA = new()
|
||||
for(var/v in set_vars)
|
||||
MA.vars[v] = set_vars[v]
|
||||
animate(A, appearance = MA, time, loop, easing, flags)
|
||||
/proc/_animate(atom/A, list/data, time = 10, loop = 1, easing = LINEAR_EASING, flags = null)
|
||||
if(!istype(A))
|
||||
return
|
||||
animate(A, appearance = data, time = time, loop = loop, easing = easing, flags = flags)
|
||||
|
||||
/proc/_animate_adv(atom/A, list/data, loop = 1, easing = LINEAR_EASING, flags = NONE)
|
||||
if(!A || !islist(data) || data.len < 1)
|
||||
return
|
||||
animate(A, appearance = (data[1] - "time"), time = data[1]["time"], loop = loop, easing = easing, flags = flags)
|
||||
if(data.len < 2)
|
||||
return
|
||||
for(var/i in 2 to data.len)
|
||||
animate(appearance = (data[i] - "time"), time = data[i]["time"])
|
||||
|
||||
/proc/_acrccos(A)
|
||||
return arccos(A)
|
||||
|
||||
@@ -34,10 +34,13 @@ GLOBAL_LIST_EMPTY(antagonists)
|
||||
var/antag_hud_name
|
||||
/// If above 0, this is the multiplier for the speed at which we hijack the shuttle. Do not directly read, use hijack_speed().
|
||||
var/hijack_speed = 0
|
||||
/// If set to true, the antag will not be added to the living antag list.
|
||||
var/soft_antag = FALSE
|
||||
/// The battlecry this antagonist shouts when suiciding with C4/X4.
|
||||
var/suicide_cry = ""
|
||||
/// The typepath for the outfit to show in the preview for the preferences menu.
|
||||
var/preview_outfit
|
||||
/// If set to true, the antag will not be added to the living antag list.
|
||||
var/soft_antag = FALSE
|
||||
|
||||
//Antag panel properties
|
||||
///This will hide adding this antag type in antag panel, use only for internal subtypes that shouldn't be added directly but still show if possessed by mind
|
||||
var/show_in_antagpanel = TRUE
|
||||
@@ -376,6 +379,43 @@ GLOBAL_LIST_EMPTY(antagonists)
|
||||
/datum/antagonist/proc/get_admin_commands()
|
||||
. = list()
|
||||
|
||||
/// Creates an icon from the preview outfit.
|
||||
/// Custom implementors of `get_preview_icon` should use this, as the
|
||||
/// result of `get_preview_icon` is expected to be the completed version.
|
||||
/datum/antagonist/proc/render_preview_outfit(datum/outfit/outfit, mob/living/carbon/human/dummy)
|
||||
dummy = dummy || new /mob/living/carbon/human/dummy/consistent
|
||||
dummy.equipOutfit(outfit, visualsOnly = TRUE)
|
||||
COMPILE_OVERLAYS(dummy)
|
||||
var/icon = getFlatIcon(dummy)
|
||||
|
||||
// We don't want to qdel the dummy right away, since its items haven't initialized yet.
|
||||
SSatoms.prepare_deletion(dummy)
|
||||
|
||||
return icon
|
||||
|
||||
/// Given an icon, will crop it to be consistent of those in the preferences menu.
|
||||
/// Not necessary, and in fact will look bad if it's anything other than a human.
|
||||
/datum/antagonist/proc/finish_preview_icon(icon/icon)
|
||||
// Zoom in on the top of the head and the chest
|
||||
// I have no idea how to do this dynamically.
|
||||
icon.Scale(115, 115)
|
||||
|
||||
// This is probably better as a Crop, but I cannot figure it out.
|
||||
icon.Shift(WEST, 8)
|
||||
icon.Shift(SOUTH, 30)
|
||||
|
||||
icon.Crop(1, 1, ANTAGONIST_PREVIEW_ICON_SIZE, ANTAGONIST_PREVIEW_ICON_SIZE)
|
||||
|
||||
return icon
|
||||
|
||||
/// Returns the icon to show on the preferences menu.
|
||||
/datum/antagonist/proc/get_preview_icon()
|
||||
if (isnull(preview_outfit))
|
||||
return null
|
||||
|
||||
return finish_preview_icon(render_preview_outfit(preview_outfit))
|
||||
|
||||
|
||||
/datum/antagonist/Topic(href,href_list)
|
||||
if(!check_rights(R_ADMIN))
|
||||
return
|
||||
|
||||
@@ -6,14 +6,3 @@
|
||||
continue
|
||||
if(!antag_type || !specific && istype(A,antag_type) || specific && A.type == antag_type)
|
||||
. += A.owner
|
||||
|
||||
//Get all teams [of type team_type]
|
||||
/proc/get_all_teams(team_type)
|
||||
. = list()
|
||||
for(var/V in GLOB.antagonists)
|
||||
var/datum/antagonist/A = V
|
||||
if(!A.owner)
|
||||
continue
|
||||
var/datum/team/T = A.get_team()
|
||||
if(!team_type || istype(T,team_type))
|
||||
. |= T
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
GLOBAL_LIST_EMPTY(antagonist_teams)
|
||||
|
||||
//A barebones antagonist team.
|
||||
/datum/team
|
||||
var/list/datum/mind/members = list()
|
||||
@@ -8,6 +10,7 @@
|
||||
|
||||
/datum/team/New(starting_members)
|
||||
. = ..()
|
||||
GLOB.antagonist_teams += src
|
||||
if(starting_members)
|
||||
if(islist(starting_members))
|
||||
for(var/datum/mind/M in starting_members)
|
||||
@@ -15,6 +18,10 @@
|
||||
else
|
||||
add_member(starting_members)
|
||||
|
||||
/datum/team/Destroy(force, ...)
|
||||
GLOB.antagonist_teams -= src
|
||||
. = ..()
|
||||
|
||||
/datum/team/proc/is_solo()
|
||||
return members.len == 1
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@
|
||||
|
||||
/datum/antagonist/abductor/admin_add(datum/mind/new_owner,mob/admin)
|
||||
var/list/current_teams = list()
|
||||
for(var/datum/team/abductor_team/T in get_all_teams(/datum/team/abductor_team))
|
||||
for(var/datum/team/abductor_team/T in GLOB.antagonist_teams)
|
||||
current_teams[T.name] = T
|
||||
var/choice = input(admin,"Add to which team ?") as null|anything in (current_teams + "new team")
|
||||
if (choice == "new team")
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
do_sparks(5, TRUE, AM)
|
||||
if(isliving(AM))
|
||||
var/mob/living/L = AM
|
||||
L.overlay_fullscreen("flash", /atom/movable/screen/fullscreen/flash/static)
|
||||
L.overlay_fullscreen("flash", /atom/movable/screen/fullscreen/tiled/flash/static)
|
||||
L.clear_fullscreen("flash", 5)
|
||||
var/obj/item/transfer_valve/TTV = locate() in L.GetAllContents()
|
||||
if(TTV)
|
||||
|
||||
@@ -141,7 +141,7 @@
|
||||
if(isliving(M.current) && M.current.stat != DEAD)
|
||||
var/turf/t_turf = isAI(M.current) ? get_step(get_step(src, NORTH),NORTH) : get_turf(src) // AI too fat, must make sure it always ends up a 2 tiles north instead of on the ark.
|
||||
do_teleport(M.current, t_turf, channel = TELEPORT_CHANNEL_CULT, forced = TRUE)
|
||||
M.current.overlay_fullscreen("flash", /atom/movable/screen/fullscreen/flash)
|
||||
M.current.overlay_fullscreen("flash", /atom/movable/screen/fullscreen/tiled/flash)
|
||||
M.current.clear_fullscreen("flash", 5)
|
||||
playsound(src, 'sound/magic/clockwork/invoke_general.ogg', 50, FALSE)
|
||||
recalls_remaining--
|
||||
@@ -311,7 +311,7 @@
|
||||
var/turf/T = get_turf(M)
|
||||
if(is_servant_of_ratvar(M) && (!T || T.z != z))
|
||||
M.forceMove(get_step(src, SOUTH))
|
||||
M.overlay_fullscreen("flash", /atom/movable/screen/fullscreen/flash)
|
||||
M.overlay_fullscreen("flash", /atom/movable/screen/fullscreen/tiled/flash)
|
||||
M.clear_fullscreen("flash", 5)
|
||||
progress_in_seconds += GATEWAY_SUMMON_RATE
|
||||
switch(progress_in_seconds)
|
||||
|
||||
@@ -104,7 +104,7 @@
|
||||
/mob/living/carbon/true_devil/assess_threat(judgement_criteria, lasercolor = "", datum/callback/weaponcheck=null)
|
||||
return 666
|
||||
|
||||
/mob/living/carbon/true_devil/flash_act(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0, type = /atom/movable/screen/fullscreen/flash, override_protection = 0)
|
||||
/mob/living/carbon/true_devil/flash_act(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0, type = /atom/movable/screen/fullscreen/tiled/flash, override_protection = 0)
|
||||
if(mind && has_bane(BANE_LIGHT))
|
||||
mind.disrupt_spells(-500)
|
||||
return ..() //flashes don't stop devils UNLESS it's their bane.
|
||||
|
||||
@@ -11,14 +11,21 @@
|
||||
var/role = "Security Officer"
|
||||
var/list/name_source
|
||||
threat = -5
|
||||
var/random_names = TRUE
|
||||
var/rip_and_tear = FALSE
|
||||
var/equip_ert = TRUE
|
||||
var/forge_objectives_for_ert = TRUE
|
||||
show_in_antagpanel = FALSE
|
||||
show_to_ghosts = TRUE
|
||||
antag_moodlet = /datum/mood_event/focused
|
||||
|
||||
/datum/antagonist/ert/on_gain()
|
||||
update_name()
|
||||
forge_objectives()
|
||||
equipERT()
|
||||
if(random_names)
|
||||
update_name()
|
||||
if(forge_objectives_for_ert)
|
||||
forge_objectives()
|
||||
if(equip_ert)
|
||||
equipERT()
|
||||
. = ..()
|
||||
|
||||
/datum/antagonist/ert/get_team()
|
||||
@@ -129,6 +136,7 @@
|
||||
return
|
||||
H.equipOutfit(outfit)
|
||||
|
||||
|
||||
/datum/antagonist/ert/greet()
|
||||
if(!ert_team)
|
||||
return
|
||||
@@ -160,3 +168,139 @@
|
||||
|
||||
missiondesc += "<BR><B>Your Mission</B> : [ert_team.mission.explanation_text]"
|
||||
to_chat(owner,missiondesc)
|
||||
|
||||
|
||||
/datum/antagonist/ert/families
|
||||
name = "Space Police Responder"
|
||||
antag_hud_type = ANTAG_HUD_SPACECOP
|
||||
antag_hud_name = "hud_spacecop"
|
||||
suicide_cry = "FOR THE SPACE POLICE!!"
|
||||
|
||||
/datum/antagonist/ert/families/apply_innate_effects(mob/living/mob_override)
|
||||
..()
|
||||
var/mob/living/M = mob_override || owner.current
|
||||
add_antag_hud(antag_hud_type, antag_hud_name, M)
|
||||
if(M.hud_used)
|
||||
var/datum/hud/H = M.hud_used
|
||||
var/atom/movable/screen/wanted/giving_wanted_lvl = new /atom/movable/screen/wanted()
|
||||
H.wanted_lvl = giving_wanted_lvl
|
||||
giving_wanted_lvl.hud = H
|
||||
H.infodisplay += giving_wanted_lvl
|
||||
H.mymob.client.screen += giving_wanted_lvl
|
||||
|
||||
|
||||
/datum/antagonist/ert/families/remove_innate_effects(mob/living/mob_override)
|
||||
var/mob/living/M = mob_override || owner.current
|
||||
remove_antag_hud(antag_hud_type, M)
|
||||
if(M.hud_used)
|
||||
var/datum/hud/H = M.hud_used
|
||||
H.infodisplay -= H.wanted_lvl
|
||||
QDEL_NULL(H.wanted_lvl)
|
||||
..()
|
||||
|
||||
/datum/antagonist/ert/families/greet()
|
||||
var/missiondesc = "<span class='warningplain'><B><font size=6 color=red>You are the [name].</font></B>"
|
||||
missiondesc += "<BR><B><font size=5 color=red>You are NOT a Nanotrasen Employee. You work for the local government.</font></B>"
|
||||
missiondesc += "<BR><B><font size=5 color=red>You are NOT a deathsquad. You are here to help innocents escape violence, criminal activity, and other dangerous things.</font></B>"
|
||||
missiondesc += "<BR>After an uptick in gang violence on [station_name()], you are responding to emergency calls from the station for immediate SSC Police assistance!\n"
|
||||
missiondesc += "<BR><B>Your Mission</B>:"
|
||||
missiondesc += "<BR> <B>1.</B> Serve the public trust."
|
||||
missiondesc += "<BR> <B>2.</B> Protect the innocent."
|
||||
missiondesc += "<BR> <B>3.</B> Uphold the law."
|
||||
missiondesc += "<BR> <B>4.</B> Find the Undercover Cops."
|
||||
missiondesc += "<BR> <B>5.</B> Detain Nanotrasen Security personnel if they harm any citizen."
|
||||
missiondesc += "<BR> You can <B>see gangsters</B> using your <B>special sunglasses</B>.</span>"
|
||||
to_chat(owner,missiondesc)
|
||||
var/policy = get_policy(ROLE_FAMILIES)
|
||||
if(policy)
|
||||
to_chat(owner, policy)
|
||||
var/mob/living/M = owner.current
|
||||
M.playsound_local(M, 'sound/effects/families_police.ogg', 100, FALSE, pressure_affected = FALSE)
|
||||
|
||||
/datum/antagonist/ert/families/undercover_cop
|
||||
name = "Undercover Cop"
|
||||
role = "Undercover Cop"
|
||||
outfit = /datum/outfit/families_police/beatcop
|
||||
var/free_clothes = list(/obj/item/clothing/glasses/hud/spacecop/hidden,
|
||||
/obj/item/clothing/under/rank/security/officer/beatcop,
|
||||
/obj/item/clothing/head/spacepolice)
|
||||
forge_objectives_for_ert = FALSE
|
||||
equip_ert = FALSE
|
||||
random_names = FALSE
|
||||
|
||||
/datum/antagonist/ert/families/undercover_cop/on_gain()
|
||||
if(istype(owner.current, /mob/living/carbon/human))
|
||||
for(var/C in free_clothes)
|
||||
var/obj/O = new C(owner.current)
|
||||
var/list/slots = list (
|
||||
"backpack" = ITEM_SLOT_BACKPACK,
|
||||
"left pocket" = SLOT_L_STORE,
|
||||
"right pocket" = SLOT_R_STORE
|
||||
)
|
||||
var/mob/living/carbon/human/H = owner.current
|
||||
var/equipped = H.equip_in_one_of_slots(O, slots)
|
||||
if(!equipped)
|
||||
to_chat(owner.current, "<span class='warningplain'>Unfortunately, you could not bring your [O] to this shift. You will need to find one.</span>")
|
||||
qdel(O)
|
||||
. = ..()
|
||||
|
||||
|
||||
/datum/antagonist/ert/families/undercover_cop/greet()
|
||||
var/missiondesc = "<span class='warningplain'><B><font size=3 color=red>You are the [name].</font></B>"
|
||||
missiondesc += "<BR><B><font size=3 color=red>You are NOT a Nanotrasen Employee. You work for the local government.</font></B>"
|
||||
missiondesc += "<BR>You are an undercover police officer on board [station_name()]. You've been sent here by the Spinward Stellar Coalition because of suspected abusive behavior by the security department, and to keep tabs on a potential criminal organization operation."
|
||||
missiondesc += "<BR><B>Your Mission</B>:"
|
||||
missiondesc += "<BR> <B>1.</B> Keep a close eye on any gangsters you spot. You can view gangsters using your sunglasses in your backpack."
|
||||
missiondesc += "<BR> <B>2.</B> Keep an eye on how Security handles any gangsters, and watch for excessive security brutality."
|
||||
missiondesc += "<BR> <B>3.</B> Remain undercover and do not get found out by Security or any gangs. Nanotrasen does not take kindly to being spied on."
|
||||
missiondesc += "<BR> <B>4.</B> When your backup arrives to extract you in 1 hour, inform them of everything you saw of note, and assist them in securing the situation.</span>"
|
||||
to_chat(owner,missiondesc)
|
||||
|
||||
/datum/antagonist/ert/families/beatcop
|
||||
name = "Beat Cop"
|
||||
role = "Police Officer"
|
||||
outfit = /datum/outfit/families_police/beatcop
|
||||
|
||||
/datum/antagonist/ert/families/beatcop/armored
|
||||
name = "Armored Beat Cop"
|
||||
role = "Police Officer"
|
||||
outfit = /datum/outfit/families_police/beatcop/armored
|
||||
|
||||
/datum/antagonist/ert/families/beatcop/swat
|
||||
name = "S.W.A.T. Member"
|
||||
role = "S.W.A.T. Officer"
|
||||
outfit = /datum/outfit/families_police/beatcop/swat
|
||||
|
||||
/datum/antagonist/ert/families/beatcop/fbi
|
||||
name = "FBI Agent"
|
||||
role = "FBI Agent"
|
||||
outfit = /datum/outfit/families_police/beatcop/fbi
|
||||
|
||||
/datum/antagonist/ert/families/beatcop/military
|
||||
name = "Space Military"
|
||||
role = "Sergeant"
|
||||
outfit = /datum/outfit/families_police/beatcop/military
|
||||
|
||||
/datum/antagonist/ert/families/beatcop/military/New()
|
||||
. = ..()
|
||||
name_source = GLOB.commando_names
|
||||
|
||||
/datum/antagonist/ert/marine
|
||||
name = "Marine Commander"
|
||||
outfit = /datum/outfit/ert/commander/alert
|
||||
role = "Commander"
|
||||
|
||||
/datum/antagonist/ert/marine/security
|
||||
name = "Marine Heavy"
|
||||
outfit = /datum/outfit/ert/security/alert
|
||||
role = "Trooper"
|
||||
|
||||
/datum/antagonist/ert/marine/engineer
|
||||
name = "Marine Engineer"
|
||||
outfit = /datum/outfit/ert/engineer/alert
|
||||
role = "Engineer"
|
||||
|
||||
/datum/antagonist/ert/marine/medic
|
||||
name = "Marine Medic"
|
||||
outfit = /datum/outfit/ert/medic/alert
|
||||
role = "Medical Officer"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user