This commit is contained in:
Ghommie
2019-12-23 04:32:09 +01:00
541 changed files with 13581 additions and 3235 deletions

View File

@@ -12,6 +12,7 @@
#define APPRENTICE_BLUESPACE "bluespace"
#define APPRENTICE_ROBELESS "robeless"
#define APPRENTICE_HEALING "healing"
#define APPRENTICE_MARTIAL "martial"
//ERT Types
@@ -40,3 +41,19 @@
#define NOT_DOMINATING -1
#define MAX_LEADERS_GANG 4
#define INITIAL_DOM_ATTEMPTS 3
//Bloodsucker defines
// Bloodsucker related antag datums
#define ANTAG_DATUM_BLOODSUCKER /datum/antagonist/bloodsucker
#define ANTAG_DATUM_VASSAL /datum/antagonist/vassal
//#define ANTAG_DATUM_HUNTER /datum/antagonist/vamphunter Disabled for now
// BLOODSUCKER
#define BLOODSUCKER_LEVEL_TO_EMBRACE 3
#define BLOODSUCKER_FRENZY_TIME 25 // How long the vamp stays in frenzy.
#define BLOODSUCKER_FRENZY_OUT_TIME 300 // How long the vamp goes back into frenzy.
#define BLOODSUCKER_STARVE_VOLUME 5 // Amount of blood, below which a Vamp is at risk of frenzy.
#define CAT_STRUCTURE "Structures"
#define MARTIALART_HUNTER "hunter-fu"

View File

@@ -9,12 +9,13 @@
#define META_GAS_FUSION_POWER 7
//ATMOS
//stuff you should probably leave well alone!
#define R_IDEAL_GAS_EQUATION 8.31 //kPa*L/(K*mol)
#define ONE_ATMOSPHERE 101.325 //kPa
#define TCMB 2.7 // -270.3degC
#define TCRYO 225 // -48.15degC
#define T0C 273.15 // 0degC
#define T20C 293.15 // 20degC
#define R_IDEAL_GAS_EQUATION 8.31446261815324 //kPa*L/(K*mol)
#define ONE_ATMOSPHERE 101.325 //kPa
#define TCMB 2.7 // -270.3degC
#define TCRYO 225 // -48.15degC
#define T0C 273.15 // 0degC
#define T20C 293.15 // 20degC
#define STEFANBOLTZMANN (5.670373*10e-8) // W/(m^2*K^4)
#define MOLES_CELLSTANDARD (ONE_ATMOSPHERE*CELL_VOLUME/(T20C*R_IDEAL_GAS_EQUATION)) //moles in a 2.5 m^3 cell at 101.325 Pa and 20 degC
#define M_CELL_WITH_RATIO (MOLES_CELLSTANDARD * 0.005) //compared against for superconductivity
@@ -149,9 +150,9 @@
//OPEN TURF ATMOS
#define OPENTURF_DEFAULT_ATMOS "o2=22;n2=82;TEMP=293.15" //the default air mix that open turfs spawn
#define TCOMMS_ATMOS "n2=100;TEMP=80" //-193,15°C telecommunications. also used for xenobiology slime killrooms
#define TCOMMS_ATMOS "n2=100;TEMP=80" //-193,15°C telecommunications. also used for xenobiology slime killrooms
#define AIRLESS_ATMOS "TEMP=2.7" //space
#define FROZEN_ATMOS "o2=22;n2=82;TEMP=180" //-93.15°C snow and ice turfs
#define FROZEN_ATMOS "o2=22;n2=82;TEMP=180" //-93.15°C snow and ice turfs
#define BURNMIX_ATMOS "o2=2500;plasma=5000;TEMP=370" //used in the holodeck burn test program
//ATMOSPHERICS DEPARTMENT GAS TANK TURFS

View File

@@ -58,6 +58,7 @@
#define ANTAG_HUD_SOULLESS 21
#define ANTAG_HUD_CLOCKWORK 22
#define ANTAG_HUD_BROTHER 23
#define ANTAG_HUD_BLOODSUCKER 24
// Notification action types
#define NOTIFY_JUMP "jump"

View File

@@ -57,9 +57,9 @@
#define BALLS_SACK_SIZE_DEF 8
#define BALLS_SACK_SIZE_MAX 40
#define CUM_RATE 0.035
#define CUM_RATE 2 // holy shit what a really shitty define name - relates to units per arbitrary measure of time?
#define CUM_RATE_MULT 1
#define CUM_EFFICIENCY 1//amount of nutrition required per life()
#define CUM_EFFICIENCY 1 //amount of nutrition required per life()
#define EGG_GIRTH_MIN 1//inches
#define EGG_GIRTH_DEF 6

View File

@@ -36,8 +36,10 @@
//////////////////////////////////////////////////////////////////
// /datum signals
#define COMSIG_COMPONENT_ADDED "component_added" //when a component is added to a datum: (/datum/component)
#define COMSIG_COMPONENT_REMOVING "component_removing" //before a component is removed from a datum because of RemoveComponent: (/datum/component)
#define COMSIG_COMPONENT_ADDED "component_added" //sent to the new datum parent when a component is added to them: (/datum/component)
#define COMSIG_COMPONENT_REMOVING "component_removing" //sent to the datum parent before a component is removed from them because of RemoveComponent: (/datum/component)
#define COMSIG_COMPONENT_UNREGISTER_PARENT "component_unregister_parent" //sent to the component itself when unregistered from a parent
#define COMSIG_COMPONENT_REGISTER_PARENT "component_register_parent" //sent to the component itself when registered to a parent
#define COMSIG_PARENT_PREQDELETED "parent_preqdeleted" //before a datum's Destroy() is called: (force), returning a nonzero value will cancel the qdel operation
#define COMSIG_PARENT_QDELETING "parent_qdeleting" //just before a datum's Destroy() is called: (force), at this point none of the other components chose to interrupt qdel and Destroy will be called
@@ -140,19 +142,22 @@
#define HEARING_RAW_MESSAGE 4
/* #define HEARING_RADIO_FREQ 5
#define HEARING_SPANS 6
#define HEARING_MESSAGE_MODE 7
#define HEARING_MESSAGE_MODE 7
#define HEARING_SOURCE 8*/
#define COMSIG_MOVABLE_DISPOSING "movable_disposing" //called when the movable is added to a disposal holder object for disposal movement: (obj/structure/disposalholder/holder, obj/machinery/disposal/source)
#define COMSIG_MOVABLE_TELEPORTED "movable_teleported" //from base of do_teleport(): (channel, turf/origin, turf/destination)
// /mind signals
#define COMSIG_MIND_TRANSFER "mind_transfer" //from base of mind/transfer_to(): (new_character, old_character)
#define COMSIG_PRE_MIND_TRANSFER "pre_mind_transfer" //from base of mind/transfer_to() before it's done: (new_character, old_character)
#define COMPONENT_STOP_MIND_TRANSFER 1 //stops the mind transfer from happening.
#define COMSIG_MIND_TRANSFER "mind_transfer" //from base of mind/transfer_to() when it's done: (new_character, old_character)
// /mob signals
#define COMSIG_MOB_EXAMINATE "mob_examinate" //from base of /mob/verb/examinate(): (atom/A)
#define COMPONENT_ALLOW_EXAMINE 1
#define COMSIG_MOB_DEATH "mob_death" //from base of mob/death(): (gibbed)
#define COMSIG_MOB_GHOSTIZE "mob_ghostize" //from base of mob/Ghostize(): (can_reenter_corpse)
#define COMPONENT_BLOCK_DEATH_BROADCAST 1 //stops the death from being broadcasted in deadchat.
#define COMSIG_MOB_GHOSTIZE "mob_ghostize" //from base of mob/Ghostize(): (can_reenter_corpse, special, penalize)
#define COMPONENT_BLOCK_GHOSTING 1
#define COMSIG_MOB_ALLOWED "mob_allowed" //from base of obj/allowed(mob/M): (/obj) returns bool, if TRUE the mob has id access to the obj
#define COMSIG_MOB_RECEIVE_MAGIC "mob_receive_magic" //from base of mob/anti_magic_check(): (mob/user, magic, holy, tinfoil, chargecost, self, protection_sources)
@@ -164,7 +169,9 @@
#define COMSIG_MOB_ITEM_AFTERATTACK "mob_item_afterattack" //from base of obj/item/afterattack(): (atom/target, mob/user, proximity_flag, click_parameters)
#define COMSIG_MOB_ATTACK_RANGED "mob_attack_ranged" //from base of mob/RangedAttack(): (atom/A, params)
#define COMSIG_MOB_THROW "mob_throw" //from base of /mob/throw_item(): (atom/target)
#define COMSIG_MOB_KEY_CHANGE "mob_key_change" //from base of /mob/transfer_ckey()
#define COMSIG_MOB_KEY_CHANGE "mob_key_change" //from base of /mob/transfer_ckey(): (new_character, old_character)
#define COMSIG_MOB_PRE_PLAYER_CHANGE "mob_pre_player_change" //sent to the target mob from base of /mob/transfer_ckey() and /mind/transfer_to(): (our_character, their_character)
// #define COMPONENT_STOP_MIND_TRANSFER 1
#define COMSIG_MOB_UPDATE_SIGHT "mob_update_sight" //from base of /mob/update_sight(): ()
#define COMSIG_MOB_SAY "mob_say" // from /mob/living/say(): (proc args list)
#define COMPONENT_UPPERCASE_SPEECH 1

View File

@@ -11,6 +11,7 @@
#define GROSS (1<<10)
#define TOXIC (1<<11)
#define PINEAPPLE (1<<12)
#define BREAKFAST (1<<13)
#define DRINK_NICE 1
#define DRINK_GOOD 2

View File

@@ -118,6 +118,8 @@ GLOBAL_LIST_INIT(turfs_without_ground, typecacheof(list(
#define ismouse(A) (istype(A, /mob/living/simple_animal/mouse))
#define iscow(A) (istype(A, /mob/living/simple_animal/cow))
#define isslime(A) (istype(A, /mob/living/simple_animal/slime))
#define isdrone(A) (istype(A, /mob/living/simple_animal/drone))

View File

@@ -59,11 +59,13 @@
#define LIGHT_RANGE_FIRE 3 //How many tiles standard fires glow.
#define LIGHTING_PLANE_ALPHA_VISIBLE 255
#define LIGHTING_PLANE_ALPHA_NV_TRAIT 250
#define LIGHTING_PLANE_ALPHA_NV_TRAIT 223
#define LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE 192
#define LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE 128 //For lighting alpha, small amounts lead to big changes. even at 128 its hard to figure out what is dark and what is light, at 64 you almost can't even tell.
#define LIGHTING_PLANE_ALPHA_INVISIBLE 0
#define NIGHT_VISION_DARKSIGHT_RANGE 3
//lighting area defines
#define DYNAMIC_LIGHTING_DISABLED 0 //dynamic lighting disabled (area stays at full brightness)
#define DYNAMIC_LIGHTING_ENABLED 1 //dynamic lighting enabled
@@ -80,4 +82,4 @@
#define FLASH_LIGHT_DURATION 2
#define FLASH_LIGHT_POWER 3
#define FLASH_LIGHT_RANGE 3.8
#define FLASH_LIGHT_RANGE 3.8

View File

@@ -57,6 +57,7 @@
#define MOVESPEED_ID_PRONE_DRAGGING "PRONE_DRAG"
#define MOVESPEED_ID_HUMAN_CARRYING "HUMAN_CARRY"
#define MOVESPEED_ID_SHRINK_RAY "SHRUNKEN_SPEED_MODIFIER"
#define MOVESPEED_ID_TASED_STATUS "TASED"

View File

@@ -35,6 +35,8 @@
#define ROLE_LAVALAND "lavaland"
#define ROLE_INTERNAL_AFFAIRS "internal affairs agent"
#define ROLE_GANG "gangster"
#define ROLE_BLOODSUCKER "bloodsucker"
//#define ROLE_MONSTERHUNTER "monster hunter" Disabled for now
//Missing assignment means it's not a gamemode specific role, IT'S NOT A BUG OR ERROR.
//The gamemode specific ones are just so the gamemodes can query whether a player is old enough
@@ -60,7 +62,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_GANG = /datum/game_mode/gang,
ROLE_BLOODSUCKER = /datum/game_mode/bloodsucker
//ROLE_MONSTERHUNTER Disabled for now
))
//Job defines for what happens when you fail to qualify for any job during job selection

View File

@@ -46,7 +46,7 @@
#define STATUS_EFFECT_SLEEPING /datum/status_effect/incapacitating/sleeping //the affected is asleep
#define STATUS_EFFECT_TASED /datum/status_effect/electrode //the affected has been tased, preventing fine muscle control
#define STATUS_EFFECT_TASED /datum/status_effect/no_combat_mode/electrode/ //the affected has been tased, preventing fine muscle control
#define STATUS_EFFECT_PACIFY /datum/status_effect/pacify //the affected is pacified, preventing direct hostile actions
@@ -68,6 +68,8 @@
#define STATUS_EFFECT_SAWBLEED /datum/status_effect/saw_bleed //if the bleed builds up enough, takes a ton of damage
#define STATUS_EFFECT_NECKSLICE /datum/status_effect/neck_slice //Creates the flavor messages for the neck-slice
#define STATUS_EFFECT_NECROPOLIS_CURSE /datum/status_effect/necropolis_curse
#define CURSE_BLINDING 1 //makes the edges of the target's screen obscured
#define CURSE_SPAWNING 2 //spawns creatures that attack the target only
@@ -83,6 +85,9 @@
#define STATUS_EFFECT_BREASTS_ENLARGEMENT /datum/status_effect/chem/breast_enlarger //Applied slowdown due to the ominous bulk.
#define STATUS_EFFECT_PENIS_ENLARGEMENT /datum/status_effect/chem/penis_enlarger //More applied slowdown, just like the above.
#define STATUS_EFFECT_NO_COMBAT_MODE /datum/status_effect/no_combat_mode //Wont allow combat mode and will disable it
#define STATUS_EFFECT_MESMERIZE /datum/status_effect/no_combat_mode/mesmerize //Just reskinned no_combat_mode
/////////////
// NEUTRAL //
/////////////

View File

@@ -66,6 +66,7 @@
#define TRAIT_HUSK "husk"
#define TRAIT_NOCLONE "noclone"
#define TRAIT_CLUMSY "clumsy"
#define TRAIT_CHUNKYFINGERS "chunkyfingers" //means that you can't use weapons with normal trigger guards.
#define TRAIT_DUMB "dumb"
#define TRAIT_MONKEYLIKE "monkeylike" //sets IsAdvancedToolUser to FALSE
#define TRAIT_PACIFISM "pacifism"
@@ -117,6 +118,7 @@
#define TRAIT_PARALYSIS_R_ARM "para-r-arm"
#define TRAIT_PARALYSIS_L_LEG "para-l-leg"
#define TRAIT_PARALYSIS_R_LEG "para-r-leg"
#define TRAIT_DISK_VERIFIER "disk-verifier"
#define TRAIT_UNINTELLIGIBLE_SPEECH "unintelligible-speech"
#define TRAIT_SOOTHED_THROAT "soothed-throat"
#define TRAIT_LAW_ENFORCEMENT_METABOLISM "law-enforcement-metabolism"
@@ -127,6 +129,12 @@
#define TRAIT_ABDUCTOR_TRAINING "abductor-training"
#define TRAIT_ABDUCTOR_SCIENTIST_TRAINING "abductor-scientist-training"
#define TRAIT_SURGEON "surgeon"
#define TRAIT_COLDBLOODED "coldblooded" // Your body is literal room temperature. Does not make you immune to the temp.
#define TRAIT_NONATURALHEAL "nonaturalheal" // Only Admins can heal you. NOTHING else does it unless it's given the god tag.
#define TRAIT_NORUNNING "norunning" // You walk!
#define TRAIT_NOMARROW "nomarrow" // You don't make blood, with chemicals or nanites.
#define TRAIT_NOPULSE "nopulse" // Your heart doesn't beat.
//non-mob traits
#define TRAIT_PARALYSIS "paralysis" //Used for limb-based paralysis, where replacing the limb will fix it
@@ -217,5 +225,8 @@
#define LOCKED_HELMET_TRAIT "locked-helmet"
#define NINJA_SUIT_TRAIT "ninja-suit"
#define ANTI_DROP_IMPLANT_TRAIT "anti-drop-implant"
#define SLEEPING_CARP_TRAIT "sleeping_carp"
#define ABDUCTOR_ANTAGONIST "abductor-antagonist"
#define MADE_UNCLONEABLE "made-uncloneable"
#define NUKEOP_TRAIT "nuke-op"
#define DEATHSQUAD_TRAIT "deathsquad"

View File

@@ -95,4 +95,22 @@ GLOBAL_VAR_INIT(cmp_field, "name")
return sorttext(A.sample_object.name, B.sample_object.name)
/proc/cmp_numbered_displays_name_dsc(datum/numbered_display/A, datum/numbered_display/B)
return sorttext(B.sample_object.name, A.sample_object.name)
return sorttext(B.sample_object.name, A.sample_object.name)
/proc/cmp_quirk_asc(datum/quirk/A, datum/quirk/B)
var/a_sign = num2sign(initial(A.value) * -1)
var/b_sign = num2sign(initial(B.value) * -1)
// Neutral traits go last.
if(a_sign == 0)
a_sign = 2
if(b_sign == 0)
b_sign = 2
var/a_name = initial(A.name)
var/b_name = initial(B.name)
if(a_sign != b_sign)
return a_sign - b_sign
else
return sorttext(b_name, a_name)

View File

@@ -320,6 +320,8 @@
parts += "[FOURSPACES][FOURSPACES][str]"
for(var/entry in mode.threat_tallies)
parts += "[FOURSPACES][FOURSPACES][entry] added [mode.threat_tallies[entry]]"
SSblackbox.record_feedback("tally","dynamic_threat",mode.threat_level,"Final threat level")
SSblackbox.record_feedback("tally","dynamic_threat",mode.threat,"Threat left")
return parts.Join("<br>")
/client/proc/roundend_report_file()

File diff suppressed because it is too large Load Diff

View File

@@ -1548,4 +1548,12 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new)
/proc/CallAsync(datum/source, proctype, list/arguments)
set waitfor = FALSE
return call(source, proctype)(arglist(arguments))
return call(source, proctype)(arglist(arguments))
/proc/num2sign(numeric)
if(numeric > 0)
return 1
else if(numeric < 0)
return -1
else
return 0

View File

@@ -107,6 +107,10 @@
#define ui_internal "EAST-1:28,CENTER+1:19"//CIT CHANGE - moves internal icon up a little bit to accommodate for the stamina meter
#define ui_mood "EAST-1:28,CENTER-3:10"
//living
#define ui_living_pull "EAST-1:28,CENTER-2:15"
#define ui_living_health "EAST-1:28,CENTER:15"
//borgs
#define ui_borg_health "EAST-1:28,CENTER-1:15" //borgs have the health display where humans have the pressure damage indicator.

View File

@@ -113,7 +113,7 @@
name = "Show Buttons"
else
name = "Hide Buttons"
UpdateIcon()
update_icon()
usr.update_action_buttons()
/obj/screen/movable/action_button/hide_toggle/AltClick(mob/user)
@@ -135,9 +135,9 @@
hide_icon = settings["toggle_icon"]
hide_state = settings["toggle_hide"]
show_state = settings["toggle_show"]
UpdateIcon()
update_icon()
/obj/screen/movable/action_button/hide_toggle/proc/UpdateIcon()
/obj/screen/movable/action_button/hide_toggle/update_icon()
cut_overlays()
add_overlay(mutable_appearance(hide_icon, hidden ? show_state : hide_state))

View File

@@ -213,6 +213,16 @@ or something covering your eyes."
desc = "Whoa man, you're tripping balls! Careful you don't get addicted... if you aren't already."
icon_state = "high"
/obj/screen/alert/mind_control
name = "Mind Control"
desc = "Your mind has been hijacked! Click to view the mind control command."
icon_state = "mind_control"
var/command
/obj/screen/alert/mind_control/Click()
var/mob/living/L = usr
to_chat(L, "<span class='mind_control'>[command]</span>")
/obj/screen/alert/hypnosis
name = "Hypnosis"
desc = "Something's hypnotizing you, but you're not really sure about what."

View File

@@ -349,6 +349,13 @@
devilsouldisplay = new /obj/screen/devil/soul_counter
infodisplay += devilsouldisplay
blood_display = new /obj/screen/bloodsucker/blood_counter // Blood Volume
infodisplay += blood_display
vamprank_display = new /obj/screen/bloodsucker/rank_counter // Vampire Rank
infodisplay += vamprank_display
sunlight_display = new /obj/screen/bloodsucker/sunlight_counter // Sunlight
infodisplay += sunlight_display
zone_select = new /obj/screen/zone_sel()
zone_select.icon = ui_style
zone_select.update_icon(mymob)

View File

@@ -0,0 +1,16 @@
/datum/hud/lavaland_elite
ui_style = 'icons/mob/screen_elite.dmi'
/datum/hud/lavaland_elite/New(mob/living/simple_animal/hostile/asteroid/elite)
..()
pull_icon = new /obj/screen/pull()
pull_icon.icon = ui_style
pull_icon.update_icon()
pull_icon.screen_loc = ui_living_pull
pull_icon.hud = src
static_inventory += pull_icon
healths = new /obj/screen/healths/lavaland_elite()
healths.hud = src
infodisplay += healths

View File

@@ -644,6 +644,12 @@
screen_loc = ui_construct_health
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
/obj/screen/healths/lavaland_elite
icon = 'icons/mob/screen_elite.dmi'
icon_state = "elite_health0"
screen_loc = ui_health
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
/obj/screen/healthdoll
name = "health doll"
screen_loc = ui_healthdoll

View File

@@ -110,7 +110,7 @@
totitemdamage *= 0.5
//CIT CHANGES END HERE
apply_damage(totitemdamage, I.damtype) //CIT CHANGE - replaces I.force with totitemdamage
if(I.damtype == BRUTE)
if(I.damtype == BRUTE && !HAS_TRAIT(src, TRAIT_NOMARROW))
if(prob(33))
I.add_mob_blood(src)
var/turf/location = get_turf(src)
@@ -163,4 +163,3 @@
/obj/item/proc/getweight()
return total_mass || w_class * 1.25

View File

@@ -379,3 +379,10 @@
/datum/config_entry/number/auto_transfer_delay
config_entry_value = 72000
min_val = 0
/datum/config_entry/number/marauder_delay_non_reebe
config_entry_value = 1800
min_val = 0
/datum/config_entry/flag/allow_clockwork_marauder_on_station
config_entry_value = TRUE

View File

@@ -12,6 +12,7 @@ SUBSYSTEM_DEF(persistence)
var/list/obj/structure/chisel_message/chisel_messages = list()
var/list/saved_messages = list()
var/list/saved_modes = list(1,2,3)
var/list/saved_dynamic_rules = list(list(),list(),list())
var/list/saved_threat_levels = list(1,1,1)
var/list/saved_maps
var/list/saved_trophies = list()
@@ -227,7 +228,10 @@ SUBSYSTEM_DEF(persistence)
CollectSecretSatchels()
CollectTrophies()
CollectRoundtype()
CollectThreatLevel()
if(istype(SSticker.mode, /datum/game_mode/dynamic))
var/datum/game_mode/dynamic/mode = SSticker.mode
CollectThreatLevel(mode)
CollectRulesets(mode)
RecordMaps()
SavePhotoPersistence() //THIS IS PERSISTENCE, NOT THE LOGGING PORTION.
if(CONFIG_GET(flag/use_antag_rep))
@@ -384,17 +388,27 @@ SUBSYSTEM_DEF(persistence)
fdel(json_file)
WRITE_FILE(json_file, json_encode(file_data))
/datum/controller/subsystem/persistence/proc/CollectThreatLevel()
if(istype(SSticker.mode, /datum/game_mode/dynamic))
var/datum/game_mode/dynamic/mode = SSticker.mode
saved_threat_levels[3] = saved_threat_levels[2]
saved_threat_levels[2] = saved_threat_levels [1]
saved_threat_levels[1] = mode.threat_level
var/json_file = file("data/RecentThreatLevels.json")
var/list/file_data = list()
file_data["data"] = saved_threat_levels
fdel(json_file)
WRITE_FILE(json_file, json_encode(file_data))
/datum/controller/subsystem/persistence/proc/CollectThreatLevel(var/datum/game_mode/dynamic/mode)
saved_threat_levels[3] = saved_threat_levels[2]
saved_threat_levels[2] = saved_threat_levels [1]
saved_threat_levels[1] = mode.threat_level
var/json_file = file("data/RecentThreatLevels.json")
var/list/file_data = list()
file_data["data"] = saved_threat_levels
fdel(json_file)
WRITE_FILE(json_file, json_encode(file_data))
/datum/controller/subsystem/persistence/proc/CollectRulesets(var/datum/game_mode/dynamic/mode)
saved_dynamic_rules[3] = saved_dynamic_rules[2]
saved_dynamic_rules[2] = saved_dynamic_rules[1]
saved_dynamic_rules[1] = list()
for(var/datum/dynamic_ruleset/ruleset in mode.executed_rules)
saved_dynamic_rules[1] += ruleset.config_tag
var/json_file = file("data/RecentRulesets.json")
var/list/file_data = list()
file_data["data"] = saved_dynamic_rules
fdel(json_file)
WRITE_FILE(json_file, json_encode(file_data))
/datum/controller/subsystem/persistence/proc/RecordMaps()
saved_maps = saved_maps?.len ? list("[SSmapping.config.map_name]") | saved_maps : list("[SSmapping.config.map_name]")

View File

@@ -12,27 +12,40 @@ PROCESSING_SUBSYSTEM_DEF(quirks)
var/list/quirk_names_by_path = list()
var/list/quirk_points = list() //Assoc. list of quirk names and their "point cost"; positive numbers are good traits, and negative ones are bad
var/list/quirk_objects = list() //A list of all quirk objects in the game, since some may process
var/list/quirk_blacklist = list() //A list a list of quirks that can not be used with each other. Format: list(quirk1,quirk2),list(quirk3,quirk4)
/datum/controller/subsystem/processing/quirks/Initialize(timeofday)
if(!quirks.len)
SetupQuirks()
quirk_blacklist = list(list("Blind","Nearsighted"),list("Jolly","Depression","Apathetic"),list("Ageusia","Deviant Tastes"),list("Ananas Affinity","Ananas Aversion"))
return ..()
/datum/controller/subsystem/processing/quirks/proc/SetupQuirks()
for(var/V in subtypesof(/datum/quirk))
// Sort by Positive, Negative, Neutral; and then by name
var/list/quirk_list = sortList(subtypesof(/datum/quirk), /proc/cmp_quirk_asc)
for(var/V in quirk_list)
var/datum/quirk/T = V
quirks[initial(T.name)] = T
quirk_points[initial(T.name)] = initial(T.value)
quirk_names_by_path[T] = initial(T.name)
/datum/controller/subsystem/processing/quirks/proc/AssignQuirks(mob/living/user, client/cli, spawn_effects, roundstart = FALSE, datum/job/job, silent = FALSE, mob/to_chat_target)
GenerateQuirks(cli)
var/list/quirks = cli.prefs.character_quirks.Copy()
var/badquirk = FALSE
var/list/my_quirks = cli.prefs.all_quirks.Copy()
var/list/cut
if(job && job.blacklisted_quirks)
cut = filter_quirks(quirks, job)
for(var/V in quirks)
user.add_quirk(V, spawn_effects)
if(job?.blacklisted_quirks)
cut = filter_quirks(my_quirks, job)
for(var/V in my_quirks)
var/datum/quirk/Q = quirks[V]
if(Q)
user.add_quirk(Q, spawn_effects)
else
stack_trace("Invalid quirk \"[V]\" in client [cli.ckey] preferences")
cli.prefs.all_quirks -= V
badquirk = TRUE
if(badquirk)
cli.prefs.save_character()
if(!silent && LAZYLEN(cut))
to_chat(to_chat_target || user, "<span class='boldwarning'>All of your non-neutral character quirks have been cut due to these quirks conflicting with your job assignment: [english_list(cut)].</span>")
@@ -50,28 +63,28 @@ PROCESSING_SUBSYSTEM_DEF(quirks)
for(var/i in quirk_names)
. += quirk_points_by_name(i)
/datum/controller/subsystem/processing/quirks/proc/filter_quirks(list/quirks, datum/job/job)
/datum/controller/subsystem/processing/quirks/proc/filter_quirks(list/our_quirks, datum/job/job)
var/list/cut = list()
var/list/banned_names = list()
for(var/i in job.blacklisted_quirks)
var/name = quirk_name_by_path(i)
if(name)
banned_names += name
var/list/blacklisted = quirks & banned_names
var/list/blacklisted = our_quirks & banned_names
if(length(blacklisted))
for(var/i in blacklisted)
quirks -= i
our_quirks -= i
cut += i
/* //Code to automatically reduce positive quirks until balance is even.
var/points_used = total_points(quirks)
var/points_used = total_points(our_quirks)
if(points_used > 0)
//they owe us points, let's collect.
for(var/i in quirks)
for(var/i in our_quirks)
var/points = quirk_points_by_name(i)
if(points > 0)
cut += i
quirks -= i
our_quirks -= i
points_used -= points
if(points_used <= 0)
break
@@ -79,14 +92,9 @@ PROCESSING_SUBSYSTEM_DEF(quirks)
//Nah, let's null all non-neutrals out.
if(cut.len)
for(var/i in quirks)
for(var/i in our_quirks)
if(quirk_points_by_name(i) != 0)
//cut += i -- Commented out: Only show the ones that triggered the quirk purge.
quirks -= i
our_quirks -= i
return cut
/datum/controller/subsystem/processing/quirks/proc/GenerateQuirks(client/user)
if(user.prefs.character_quirks.len)
return
user.prefs.character_quirks = user.prefs.all_quirks

View File

@@ -384,8 +384,8 @@ SUBSYSTEM_DEF(ticker)
captainless=0
if(player.mind.assigned_role != player.mind.special_role)
SSjob.EquipRank(N, player.mind.assigned_role, 0)
if(CONFIG_GET(flag/roundstart_traits) && ishuman(N.new_character))
SSquirks.AssignQuirks(N.new_character, N.client, TRUE, TRUE, SSjob.GetJob(player.mind.assigned_role), FALSE, N)
if(CONFIG_GET(flag/roundstart_traits) && ishuman(N.new_character))
SSquirks.AssignQuirks(N.new_character, N.client, TRUE, TRUE, SSjob.GetJob(player.mind.assigned_role), FALSE, N)
CHECK_TICK
if(captainless)
for(var/mob/dead/new_player/N in GLOB.player_list)

View File

@@ -93,7 +93,7 @@ SUBSYSTEM_DEF(traumas)
/obj/item/clothing/under/rank/head_of_security/grey, /obj/item/clothing/under/rank/head_of_security/alt,
/obj/item/clothing/under/rank/research_director/alt, /obj/item/clothing/under/rank/research_director/turtleneck,
/obj/item/clothing/under/captainparade, /obj/item/clothing/under/hosparademale, /obj/item/clothing/under/hosparadefem,
/obj/item/clothing/head/helmet/abductor, /obj/item/clothing/suit/armor/abductor/vest, /obj/item/abductor_baton,
/obj/item/clothing/head/helmet/abductor, /obj/item/clothing/suit/armor/abductor/vest, /obj/item/abductor/baton,
/obj/item/storage/belt/military/abductor, /obj/item/gun/energy/alien, /obj/item/abductor/silencer,
/obj/item/abductor/gizmo, /obj/item/clothing/under/rank/centcom_officer,
/obj/item/clothing/suit/space/hardsuit/ert, /obj/item/clothing/suit/space/hardsuit/ert/sec,
@@ -136,7 +136,7 @@ SUBSYSTEM_DEF(traumas)
"aliens" = typecacheof(list(/obj/item/clothing/mask/facehugger, /obj/item/organ/body_egg/alien_embryo,
/obj/structure/alien, /obj/item/toy/toy_xeno,
/obj/item/clothing/suit/armor/abductor, /obj/item/abductor, /obj/item/gun/energy/alien,
/obj/item/abductor_baton, /obj/item/radio/headset/abductor, /obj/item/scalpel/alien, /obj/item/hemostat/alien,
/obj/item/abductor/baton, /obj/item/radio/headset/abductor, /obj/item/scalpel/alien, /obj/item/hemostat/alien,
/obj/item/retractor/alien, /obj/item/circular_saw/alien, /obj/item/surgicaldrill/alien, /obj/item/cautery/alien,
/obj/item/clothing/head/helmet/abductor, /obj/structure/bed/abductor, /obj/structure/table_frame/abductor,
/obj/structure/table/abductor, /obj/structure/table/optable/abductor, /obj/structure/closet/abductor, /obj/item/organ/heart/gland,

View File

@@ -53,7 +53,7 @@
// If you want/expect to be moving the component around between parents, use this to register on the parent for signals
/datum/component/proc/RegisterWithParent()
return
SEND_SIGNAL(src, COMSIG_COMPONENT_REGISTER_PARENT) //CITADEL EDIT
/datum/component/proc/Initialize(...)
return
@@ -85,7 +85,7 @@
UnregisterFromParent()
/datum/component/proc/UnregisterFromParent()
return
SEND_SIGNAL(src, COMSIG_COMPONENT_UNREGISTER_PARENT) //CITADEL EDIT
/datum/proc/RegisterSignal(datum/target, sig_type_or_types, proctype, override = FALSE)
if(QDELETED(src) || QDELETED(target))

View File

@@ -19,12 +19,14 @@
src.damage_multiplier = damage_multiplier
/datum/component/bane/RegisterWithParent()
. = ..()
if(speciestype)
RegisterSignal(parent, COMSIG_ITEM_AFTERATTACK, .proc/speciesCheck)
else
RegisterSignal(parent, COMSIG_ITEM_AFTERATTACK, .proc/mobCheck)
/datum/component/bane/UnregisterFromParent()
. = ..()
UnregisterSignal(parent, COMSIG_ITEM_AFTERATTACK)
/datum/component/bane/proc/speciesCheck(obj/item/source, atom/target, mob/user, proximity_flag, click_parameters)

View File

@@ -21,9 +21,11 @@
RegisterSignal(parent, bounce, .proc/bounce_up)
/datum/component/bouncy/RegisterWithParent()
. = ..()
RegisterSignal(parent, bounce_signals, .proc/bounce_up)
/datum/component/bouncy/UnregisterFromParent()
. = ..()
UnregisterSignal(parent, bounce_signals)
/datum/component/bouncy/proc/bounce_up(datum/source)

View File

@@ -23,17 +23,51 @@
RegisterSignal(parent, COMSIG_ITEM_ATTACK, .proc/onItemAttack)
/datum/component/butchering/proc/onItemAttack(obj/item/source, mob/living/M, mob/living/user)
if(user.a_intent == INTENT_HARM && M.stat == DEAD && (M.butcher_results || M.guaranteed_butcher_results)) //can we butcher it?
if(user.a_intent != INTENT_HARM)
return
if(M.stat == DEAD && (M.butcher_results || M.guaranteed_butcher_results)) //can we butcher it?
if(butchering_enabled && (can_be_blunt || source.get_sharpness()))
INVOKE_ASYNC(src, .proc/startButcher, source, M, user)
return COMPONENT_ITEM_NO_ATTACK
if(ishuman(M) && source.force && source.get_sharpness())
var/mob/living/carbon/human/H = M
if((H.health <= H.crit_threshold || (user.pulling == H && user.grab_state >= GRAB_NECK) || H.IsSleeping()) && user.zone_selected == BODY_ZONE_HEAD) // Only sleeping, neck grabbed, or crit, can be sliced.
if(H.has_status_effect(/datum/status_effect/neck_slice))
user.show_message("<span class='warning'>[H]'s neck has already been already cut, you can't make the bleeding any worse!</span>", 1, \
"<span class='warning'>Their neck has already been already cut, you can't make the bleeding any worse!</span>")
return COMPONENT_ITEM_NO_ATTACK
INVOKE_ASYNC(src, .proc/startNeckSlice, source, H, user)
return COMPONENT_ITEM_NO_ATTACK
/datum/component/butchering/proc/startButcher(obj/item/source, mob/living/M, mob/living/user)
to_chat(user, "<span class='notice'>You begin to butcher [M]...</span>")
playsound(M.loc, butcher_sound, 50, TRUE, -1)
if(do_mob(user, M, speed) && M.Adjacent(source))
Butcher(user, M)
/datum/component/butchering/proc/startNeckSlice(obj/item/source, mob/living/carbon/human/H, mob/living/user)
user.visible_message("<span class='danger'>[user] is slitting [H]'s throat!</span>", \
"<span class='danger'>You start slicing [H]'s throat!</span>", \
"<span class='notice'>You hear a cutting noise!</span>", ignored_mobs = H)
H.show_message("<span class='userdanger'>Your throat is being slit by [user]!</span>", 1, \
"<span class = 'userdanger'>Something is cutting into your neck!</span>", NONE)
log_combat(user, H, "starts slicing the throat of")
playsound(H.loc, butcher_sound, 50, TRUE, -1)
if(do_mob(user, H, CLAMP(500 / source.force, 30, 100)) && H.Adjacent(source))
if(H.has_status_effect(/datum/status_effect/neck_slice))
user.show_message("<span class='warning'>[H]'s neck has already been already cut, you can't make the bleeding any worse!</span>", 1, \
"<span class='warning'>Their neck has already been already cut, you can't make the bleeding any worse!</span>")
return
H.visible_message("<span class='danger'>[user] slits [H]'s throat!</span>", \
"<span class='userdanger'>[user] slits your throat...</span>")
log_combat(user, H, "finishes slicing the throat of")
H.apply_damage(source.force, BRUTE, BODY_ZONE_HEAD)
H.bleed_rate = CLAMP(H.bleed_rate + 20, 0, 30)
H.apply_status_effect(/datum/status_effect/neck_slice)
/datum/component/butchering/proc/Butcher(mob/living/butcher, mob/living/meat)
var/turf/T = meat.drop_location()
var/final_effectiveness = effectiveness - meat.butcher_difficulty

View File

@@ -17,6 +17,7 @@
apply()
/datum/component/decal/RegisterWithParent()
. = ..()
if(first_dir)
RegisterSignal(parent, COMSIG_ATOM_DIR_CHANGE, .proc/rotate_react)
if(cleanable)
@@ -25,6 +26,7 @@
RegisterSignal(parent, COMSIG_PARENT_EXAMINE, .proc/examine)
/datum/component/decal/UnregisterFromParent()
. = ..()
UnregisterSignal(parent, list(COMSIG_ATOM_DIR_CHANGE, COMSIG_COMPONENT_CLEAN_ACT, COMSIG_PARENT_EXAMINE))
/datum/component/decal/Destroy()

View File

@@ -30,11 +30,13 @@
return ..()
/datum/component/fantasy/RegisterWithParent()
. = ..()
var/obj/item/master = parent
originalName = master.name
modify()
/datum/component/fantasy/UnregisterFromParent()
. = ..()
unmodify()
/datum/component/fantasy/InheritComponent(datum/component/fantasy/newComp, original, list/arguments)

View File

@@ -9,6 +9,7 @@
src.fire_stacks = fire_stacks
/datum/component/igniter/RegisterWithParent()
. = ..()
if(ismachinery(parent) || isstructure(parent) || isgun(parent)) // turrets, etc
RegisterSignal(parent, COMSIG_PROJECTILE_ON_HIT, .proc/projectile_hit)
else if(isitem(parent))
@@ -17,6 +18,7 @@
RegisterSignal(parent, COMSIG_HOSTILE_ATTACKINGTARGET, .proc/hostile_attackingtarget)
/datum/component/igniter/UnregisterFromParent()
. = ..()
UnregisterSignal(parent, list(COMSIG_ITEM_AFTERATTACK, COMSIG_HOSTILE_ATTACKINGTARGET, COMSIG_PROJECTILE_ON_HIT))
/datum/component/igniter/proc/item_afterattack(obj/item/source, atom/target, mob/user, proximity_flag, click_parameters)

View File

@@ -10,6 +10,7 @@
src.throw_anchored = throw_anchored
/datum/component/knockback/RegisterWithParent()
. = ..()
if(ismachinery(parent) || isstructure(parent) || isgun(parent)) // turrets, etc
RegisterSignal(parent, COMSIG_PROJECTILE_ON_HIT, .proc/projectile_hit)
else if(isitem(parent))
@@ -18,6 +19,7 @@
RegisterSignal(parent, COMSIG_HOSTILE_ATTACKINGTARGET, .proc/hostile_attackingtarget)
/datum/component/knockback/UnregisterFromParent()
. = ..()
UnregisterSignal(parent, list(COMSIG_ITEM_AFTERATTACK, COMSIG_HOSTILE_ATTACKINGTARGET, COMSIG_PROJECTILE_ON_HIT))
/datum/component/knockback/proc/item_afterattack(obj/item/source, atom/target, mob/user, proximity_flag, click_parameters)

View File

@@ -10,6 +10,7 @@
src.flat_heal = flat_heal
/datum/component/lifesteal/RegisterWithParent()
. = ..()
if(isgun(parent))
RegisterSignal(parent, COMSIG_PROJECTILE_ON_HIT, .proc/projectile_hit)
else if(isitem(parent))
@@ -18,6 +19,7 @@
RegisterSignal(parent, COMSIG_HOSTILE_ATTACKINGTARGET, .proc/hostile_attackingtarget)
/datum/component/lifesteal/UnregisterFromParent()
. = ..()
UnregisterSignal(parent, list(COMSIG_ITEM_AFTERATTACK, COMSIG_HOSTILE_ATTACKINGTARGET, COMSIG_PROJECTILE_ON_HIT))
/datum/component/lifesteal/proc/item_afterattack(obj/item/source, atom/target, mob/user, proximity_flag, click_parameters)

View File

@@ -150,15 +150,6 @@
if(9)
setSanity(sanity+0.4, maximum=SANITY_GREAT)
if(HAS_TRAIT(owner, TRAIT_DEPRESSION))
if(prob(0.05))
add_event(null, "depression", /datum/mood_event/depression)
clear_event(null, "jolly")
if(HAS_TRAIT(owner, TRAIT_JOLLY))
if(prob(0.05))
add_event(null, "jolly", /datum/mood_event/jolly)
clear_event(null, "depression")
HandleNutrition(owner)
/datum/component/mood/proc/setSanity(amount, minimum=SANITY_INSANE, maximum=SANITY_NEUTRAL)//I'm sure bunging this in here will have no negative repercussions.

View File

@@ -34,6 +34,7 @@
cloud_sync()
/datum/component/nanites/RegisterWithParent()
. = ..()
RegisterSignal(parent, COMSIG_HAS_NANITES, .proc/confirm_nanites)
RegisterSignal(parent, COMSIG_NANITE_UI_DATA, .proc/nanite_ui_data)
RegisterSignal(parent, COMSIG_NANITE_GET_PROGRAMS, .proc/get_programs)
@@ -57,6 +58,7 @@
RegisterSignal(parent, COMSIG_NANITE_SIGNAL, .proc/receive_signal)
/datum/component/nanites/UnregisterFromParent()
. = ..()
UnregisterSignal(parent, list(COMSIG_HAS_NANITES,
COMSIG_NANITE_UI_DATA,
COMSIG_NANITE_GET_PROGRAMS,

View File

@@ -20,12 +20,14 @@
begin_orbit(orbiter, radius, clockwise, rotation_speed, rotation_segments, pre_rotation)
/datum/component/orbiter/RegisterWithParent()
. = ..()
var/atom/target = parent
while(ismovableatom(target))
RegisterSignal(target, COMSIG_MOVABLE_MOVED, .proc/move_react)
target = target.loc
/datum/component/orbiter/UnregisterFromParent()
. = ..()
var/atom/target = parent
while(ismovableatom(target))
UnregisterSignal(target, COMSIG_MOVABLE_MOVED)

View File

@@ -13,10 +13,12 @@
src.override_projectile_range = override_projectile_range
/datum/component/shrapnel/RegisterWithParent()
. = ..()
if(ismachinery(parent) || isstructure(parent) || isgun(parent)) // turrets, etc
RegisterSignal(parent, COMSIG_PROJECTILE_ON_HIT, .proc/projectile_hit)
/datum/component/shrapnel/UnregisterFromParent()
. = ..()
UnregisterSignal(parent, list(COMSIG_PROJECTILE_ON_HIT))
/datum/component/shrapnel/proc/projectile_hit(atom/fired_from, atom/movable/firer, atom/target, Angle)

View File

@@ -0,0 +1,42 @@
/datum/component/shrink
var/olddens
var/oldopac
dupe_mode = COMPONENT_DUPE_HIGHLANDER
/datum/component/shrink/Initialize(shrink_time)
if(!isatom(parent))
return COMPONENT_INCOMPATIBLE
var/atom/parent_atom = parent
parent_atom.transform = parent_atom.transform.Scale(0.5,0.5)
olddens = parent_atom.density
oldopac = parent_atom.opacity
parent_atom.density = 0
parent_atom.opacity = 0
if(isliving(parent_atom))
var/mob/living/L = parent_atom
L.add_movespeed_modifier(MOVESPEED_ID_SHRINK_RAY, update=TRUE, priority=100, multiplicative_slowdown=4)
if(iscarbon(L))
var/mob/living/carbon/C = L
C.unequip_everything()
C.visible_message("<span class='warning'>[C]'s belongings fall off of [C.p_them()] as they shrink down!</span>",
"<span class='userdanger'>Your belongings fall away as everything grows bigger!</span>")
if(ishuman(C))
var/mob/living/carbon/human/H = C
H.physiology.damage_resistance -= 100//carbons take double damage while shrunk
parent_atom.visible_message("<span class='warning'>[parent_atom] shrinks down to a tiny size!</span>",
"<span class='userdanger'>Everything grows bigger!</span>")
QDEL_IN(src, shrink_time)
/datum/component/shrink/Destroy()
var/atom/parent_atom = parent
parent_atom.transform = parent_atom.transform.Scale(2,2)
parent_atom.density = olddens
parent_atom.opacity = oldopac
if(isliving(parent_atom))
var/mob/living/L = parent_atom
L.remove_movespeed_modifier(MOVESPEED_ID_SHRINK_RAY)
if(ishuman(L))
var/mob/living/carbon/human/H = L
H.physiology.damage_resistance += 100
..()

View File

@@ -0,0 +1,25 @@
/datum/component/sizzle
var/mutable_appearance/sizzling
var/sizzlealpha = 0
dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS
/datum/component/sizzle/Initialize()
if(!isatom(parent))
return COMPONENT_INCOMPATIBLE
setup_sizzle()
/datum/component/sizzle/InheritComponent(datum/component/C, i_am_original)
var/atom/food = parent
sizzlealpha += 5
sizzling.alpha = sizzlealpha
food.cut_overlay(sizzling)
food.add_overlay(sizzling)
/datum/component/sizzle/proc/setup_sizzle()
var/atom/food = parent
var/icon/grill_marks = icon(initial(food.icon), initial(food.icon_state)) //we only want to apply grill marks to the initial icon_state for each object
grill_marks.Blend("#fff", ICON_ADD) //fills the icon_state with white (except where it's transparent)
grill_marks.Blend(icon('icons/obj/kitchen.dmi', "grillmarks"), ICON_MULTIPLY) //adds grill marks and the remaining white areas become transparent
sizzling = new(grill_marks)
sizzling.alpha = sizzlealpha
food.add_overlay(sizzling)

View File

@@ -24,6 +24,7 @@
src.faction = faction
/datum/component/summoning/RegisterWithParent()
. = ..()
if(ismachinery(parent) || isstructure(parent) || isgun(parent)) // turrets, etc
RegisterSignal(parent, COMSIG_PROJECTILE_ON_HIT, .proc/projectile_hit)
else if(isitem(parent))
@@ -32,6 +33,7 @@
RegisterSignal(parent, COMSIG_HOSTILE_ATTACKINGTARGET, .proc/hostile_attackingtarget)
/datum/component/summoning/UnregisterFromParent()
. = ..()
UnregisterSignal(parent, list(COMSIG_ITEM_AFTERATTACK, COMSIG_HOSTILE_ATTACKINGTARGET, COMSIG_PROJECTILE_ON_HIT))
/datum/component/summoning/proc/item_afterattack(obj/item/source, atom/target, mob/user, proximity_flag, click_parameters)

View File

@@ -9,10 +9,12 @@
src.allowed_slot = allowed_slot
/datum/component/tactical/RegisterWithParent()
. = ..()
RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, .proc/modify)
RegisterSignal(parent, COMSIG_ITEM_DROPPED, .proc/unmodify)
/datum/component/tactical/UnregisterFromParent()
. = ..()
UnregisterSignal(parent, list(COMSIG_ITEM_EQUIPPED, COMSIG_ITEM_DROPPED))
unmodify()

View File

@@ -1,128 +1,245 @@
/**
* The virtual reality turned component.
* Originally created to overcome issues of mob polymorphing locking the player inside virtual reality
* and allow for a more "immersive" virtual reality in a virtual reality experience.
* It relies on comically complex order of logic, expect things to break if procs such as mind/transfer_to() are revamped.
* In short, a barebone not so hardcoded VR framework.
* If you plan to add more devices that make use of this component, remember to isolate their code outta here where possible.
*/
/datum/component/virtual_reality
can_transfer = TRUE
var/datum/mind/mastermind // where is my mind t. pixies
//the player's mind (not the parent's), should something happen to them or to their mob.
var/datum/mind/mastermind
//the current mob's mind, which we need to keep track for mind transfer.
var/datum/mind/current_mind
var/obj/machinery/vr_sleeper/vr_sleeper
//the action datum used by the mob to quit the vr session.
var/datum/action/quit_vr/quit_action
//This one's name should be self explainatory, currently used for emags.
var/you_die_in_the_game_you_die_for_real = FALSE
var/datum/component/virtual_reality/inception //The component works on a very fragile link betwixt mind, ckey and death.
//Used to allow people to play recursively playing vr while playing vr without many issues.
var/datum/component/virtual_reality/level_below
var/datum/component/virtual_reality/level_above
//Used to stop the component from executing certain functions that'd cause us some issues otherwise.
//FALSE if there is a connected player, otherwise TRUE.
var/session_paused = TRUE
//Used to stop unwarranted behaviour from happening in cases where the master mind transference is unsupported. Set on Initialize().
var/allow_mastermind_transfer = FALSE
/datum/component/virtual_reality/Initialize(mob/M, obj/machinery/vr_sleeper/gaming_pod, yolo = FALSE, new_char = TRUE)
if(!ismob(parent) || !istype(M))
/datum/component/virtual_reality/Initialize(yolo = FALSE, _allow_mastermind_transfer = FALSE)
var/mob/M = parent
if(!istype(M) || !M.mind)
return COMPONENT_INCOMPATIBLE
var/mob/vr_M = parent
mastermind = M.mind
RegisterSignal(M, list(COMSIG_MOB_DEATH, COMSIG_PARENT_QDELETING), .proc/game_over)
RegisterSignal(M, COMSIG_MOB_KEY_CHANGE, .proc/switch_player)
RegisterSignal(mastermind, COMSIG_MIND_TRANSFER, .proc/switch_player)
you_die_in_the_game_you_die_for_real = yolo
quit_action = new()
if(gaming_pod)
vr_sleeper = gaming_pod
RegisterSignal(vr_sleeper, COMSIG_ATOM_EMAG_ACT, .proc/you_only_live_once)
RegisterSignal(vr_sleeper, COMSIG_MACHINE_EJECT_OCCUPANT, .proc/revert_to_reality)
vr_M.ckey = M.ckey
var/datum/component/virtual_reality/clusterfk = M.GetComponent(/datum/component/virtual_reality)
if(clusterfk && !clusterfk.inception)
clusterfk.inception = src
SStgui.close_user_uis(M, src)
allow_mastermind_transfer = _allow_mastermind_transfer
quit_action = new
/datum/component/virtual_reality/Destroy()
QDEL_NULL(quit_action)
if(level_above)
level_above.level_below = null
level_above = null
if(level_below)
level_below.level_above = null
level_below = null
return ..()
/datum/component/virtual_reality/RegisterWithParent()
. = ..()
var/mob/M = parent
current_mind = M.mind
if(!quit_action)
quit_action = new
quit_action.Grant(M)
RegisterSignal(quit_action, COMSIG_ACTION_TRIGGER, .proc/revert_to_reality)
RegisterSignal(quit_action, COMSIG_ACTION_TRIGGER, .proc/action_trigger)
RegisterSignal(M, list(COMSIG_MOB_DEATH, COMSIG_PARENT_QDELETING), .proc/game_over)
RegisterSignal(M, COMSIG_MOB_GHOSTIZE, .proc/be_a_quitter)
RegisterSignal(M, COMSIG_MOB_KEY_CHANGE, .proc/pass_me_the_remote)
RegisterSignal(current_mind, COMSIG_MIND_TRANSFER, .proc/pass_me_the_remote)
mastermind.current.audiovisual_redirect = M
if(vr_sleeper)
vr_sleeper.vr_mob = M
RegisterSignal(M, COMSIG_MOB_KEY_CHANGE, .proc/on_player_transfer)
RegisterSignal(current_mind, COMSIG_MIND_TRANSFER, .proc/on_player_transfer)
RegisterSignal(current_mind, COMSIG_PRE_MIND_TRANSFER, .proc/pre_player_transfer)
if(mastermind?.current)
mastermind.current.audiovisual_redirect = M
/datum/component/virtual_reality/UnregisterFromParent()
quit_action.Remove(parent)
. = ..()
if(quit_action)
quit_action.Remove(parent)
UnregisterSignal(quit_action, COMSIG_ACTION_TRIGGER)
UnregisterSignal(parent, list(COMSIG_MOB_DEATH, COMSIG_PARENT_QDELETING, COMSIG_MOB_KEY_CHANGE, COMSIG_MOB_GHOSTIZE))
UnregisterSignal(current_mind, COMSIG_MIND_TRANSFER)
UnregisterSignal(quit_action, COMSIG_ACTION_TRIGGER)
UnregisterSignal(current_mind, list(COMSIG_MIND_TRANSFER, COMSIG_PRE_MIND_TRANSFER))
current_mind = null
mastermind.current.audiovisual_redirect = null
if(mastermind?.current)
mastermind.current.audiovisual_redirect = null
/datum/component/virtual_reality/proc/switch_player(datum/source, mob/new_mob, mob/old_mob)
if(vr_sleeper || !new_mob.mind)
// Machineries currently don't deal up with the occupant being polymorphed et similar... Or did something fuck up?
revert_to_reality()
return
old_mob.audiovisual_redirect = null
new_mob.audiovisual_redirect = parent
/datum/component/virtual_reality/proc/action_trigger(datum/signal_source, datum/action/source)
if(source != quit_action)
return COMPONENT_ACTION_BLOCK_TRIGGER
revert_to_reality(signal_source)
/**
* Called when attempting to connect a mob to a virtual reality mob.
* This will return FALSE if the mob is without player or dead. TRUE otherwise
*/
/datum/component/virtual_reality/proc/connect(mob/M)
var/mob/vr_M = parent
if(!M.mind || M.stat == DEAD || !vr_M.mind || vr_M.stat == DEAD)
return FALSE
var/datum/component/virtual_reality/VR = M.GetComponent(/datum/component/virtual_reality)
if(VR)
VR.level_below = src
level_above = VR
M.transfer_ckey(vr_M, FALSE)
mastermind = M.mind
mastermind.current.audiovisual_redirect = parent
RegisterSignal(mastermind, COMSIG_PRE_MIND_TRANSFER, .proc/switch_player)
RegisterSignal(M, list(COMSIG_MOB_DEATH, COMSIG_PARENT_QDELETING), .proc/game_over)
RegisterSignal(M, COMSIG_MOB_PRE_PLAYER_CHANGE, .proc/player_hijacked)
SStgui.close_user_uis(vr_M, src)
session_paused = FALSE
return TRUE
/**
* emag_act() hook. Makes the game deadlier, killing the mastermind mob too should the parent die.
*/
/datum/component/virtual_reality/proc/you_only_live_once()
if(you_die_in_the_game_you_die_for_real || vr_sleeper?.only_current_user_can_interact)
if(you_die_in_the_game_you_die_for_real)
return FALSE
you_die_in_the_game_you_die_for_real = TRUE
return TRUE
/datum/component/virtual_reality/proc/pass_me_the_remote(datum/source, mob/new_mob)
if(new_mob == mastermind.current)
revert_to_reality(source)
return TRUE
new_mob.TakeComponent(src)
return TRUE
/**
* Called when the mastermind mind is transferred to another mob.
* This is pretty much just going to simply quit the session until machineries support polymorphed occupants etcetera.
*/
/datum/component/virtual_reality/proc/switch_player(datum/source, mob/new_mob, mob/old_mob)
if(session_paused)
return
if(!allow_mastermind_transfer)
quit()
return COMPONENT_STOP_MIND_TRANSFER
UnregisterSignal(old_mob, list(COMSIG_MOB_DEATH, COMSIG_PARENT_QDELETING, COMSIG_MOB_PRE_PLAYER_CHANGE))
RegisterSignal(new_mob, list(COMSIG_MOB_DEATH, COMSIG_PARENT_QDELETING), .proc/game_over)
RegisterSignal(new_mob, COMSIG_MOB_PRE_PLAYER_CHANGE, .proc/player_hijacked)
old_mob.audiovisual_redirect = null
new_mob.audiovisual_redirect = parent
/**
* Called to stop the player mind from being transferred should the new mob happen to be one of our masterminds'.
* Since the target's mind.current is going to be null'd in the mind transfer process,
* This has to be done in a different signal proc than on_player_transfer(), by then the mastermind.current will be null.
*/
/datum/component/virtual_reality/proc/pre_player_transfer(datum/source, mob/new_mob, mob/old_mob)
if(!mastermind || session_paused)
return
if(new_mob == mastermind.current)
quit()
return COMPONENT_STOP_MIND_TRANSFER
if(!level_above)
return
var/datum/component/virtual_reality/VR = level_above
while(VR)
if(VR.mastermind.current == new_mob)
VR.quit() //this will revert the ckey back to new_mob.
return COMPONENT_STOP_MIND_TRANSFER
VR = VR.level_above
/**
* Called when someone or something else is somewhat about to replace the mastermind's mob key somehow.
* And potentially lock the player in a broken virtual reality plot. Not really something to be proud of.
*/
/datum/component/virtual_reality/proc/player_hijacked(datum/source, mob/our_character, mob/their_character)
if(session_paused)
return
if(!their_character)
quit(cleanup = TRUE)
return
var/will_it_be_handled_in_their_pre_player_transfer = FALSE
var/datum/component/virtual_reality/VR = src
while(VR)
if(VR.parent == their_character)
will_it_be_handled_in_their_pre_player_transfer = TRUE
break
VR = VR.level_below
if(!will_it_be_handled_in_their_pre_player_transfer) //it's not the player playing shenanigeans, abandon all ships.
quit(cleanup = TRUE)
/**
* Takes care of moving the component from a mob to another when their mind or ckey is transferred.
* The very reason this component even exists (else one would be stuck playing as a monky if monkyified)
*/
/datum/component/virtual_reality/proc/on_player_transfer(datum/source, mob/new_mob, mob/old_mob)
new_mob.TakeComponent(src)
/**
* Required for the component to be transferable from mob to mob.
*/
/datum/component/virtual_reality/PostTransfer()
if(!ismob(parent))
return COMPONENT_INCOMPATIBLE
/**
*The following procs simply acts as hooks for quit(), since components do not use callbacks anymore
*/
/datum/component/virtual_reality/proc/action_trigger(datum/signal_source, datum/action/source)
quit()
return COMPONENT_ACTION_BLOCK_TRIGGER
/datum/component/virtual_reality/proc/revert_to_reality(datum/source)
quit_it()
quit()
/datum/component/virtual_reality/proc/game_over(datum/source)
quit_it(TRUE, TRUE)
quit(you_die_in_the_game_you_die_for_real, TRUE)
return COMPONENT_BLOCK_DEATH_BROADCAST
/datum/component/virtual_reality/proc/be_a_quitter(datum/source, can_reenter_corpse)
quit_it()
return COMPONENT_BLOCK_GHOSTING
/datum/component/virtual_reality/proc/be_a_quitter(datum/source, can_reenter_corpse, special = FALSE, penalize = FALSE)
if(!special)
quit()
return COMPONENT_BLOCK_GHOSTING
/datum/component/virtual_reality/proc/virtual_reality_in_a_virtual_reality(mob/player, killme = FALSE, datum/component/virtual_reality/yo_dawg)
/datum/component/virtual_reality/proc/machine_destroyed(datum/source)
quit(cleanup = TRUE)
/**
* Takes care of deleting itself, moving the player back to the mastermind's current and queueing the parent for deletion.
* It supports nested virtual realities by recursively calling vr_in_a_vr(), which in turns calls quit(),
* up to the deepest level, where the ckey will be transferred back to our mastermind's mob instead.
* The above operation is skipped when session_paused is TRUE (ergo no player in control of the current mob).
* vars:
* * deathcheck is used to kill the master, you want this FALSE unless for stuff that doesn't involve emagging.
* * cleanup is used to queue the parent for the next vr_clean_master's run, where they'll be deleted should they be dead.
* * mob/override is used for the recursive virtual reality explained above and shouldn't be used outside of vr_in_a_vr().
*/
/datum/component/virtual_reality/proc/quit(deathcheck = FALSE, cleanup = FALSE, mob/override)
var/mob/M = parent
quit_it(FALSE, killme, player, yo_dawg)
yo_dawg.inception = null
if(killme)
M.death(FALSE)
/datum/component/virtual_reality/proc/quit_it(deathcheck = FALSE, cleanup = FALSE, mob/override)
var/mob/M = parent
var/mob/dreamer = override ? override : mastermind.current
if(!mastermind)
to_chat(M, "<span class='warning'>You feel a dreadful sensation, something terrible happened. You try to wake up, but you find yourself unable to...</span>")
else
var/key_transfer = FALSE
if(inception?.parent)
inception.virtual_reality_in_a_virtual_reality(dreamer, cleanup, src)
if(!session_paused)
session_paused = TRUE
var/mob/dreamer = override || mastermind.current
if(!dreamer) //This shouldn't happen.
stack_trace("virtual reality component quit() called without a mob to transfer the parent ckey to.")
to_chat(M, "<span class='warning'>You feel a dreadful sensation, something terrible happened. You try to wake up, but you find yourself unable to...</span>")
qdel(src)
return
if(level_below?.parent)
level_below.vr_in_a_vr(dreamer, deathcheck, (deathcheck && cleanup))
else
key_transfer = TRUE
if(key_transfer)
M.transfer_ckey(dreamer, FALSE)
dreamer.stop_sound_channel(CHANNEL_HEARTBEAT)
dreamer.audiovisual_redirect = null
if(deathcheck && you_die_in_the_game_you_die_for_real)
to_chat(mastermind, "<span class='warning'>You feel everything fading away...</span>")
dreamer.death(FALSE)
if(cleanup)
var/obj/effect/vr_clean_master/cleanbot = locate() in get_area(M)
if(cleanbot)
LAZYADD(cleanbot.corpse_party, M)
if(vr_sleeper)
vr_sleeper.vr_mob = null
vr_sleeper = null
qdel(src)
if(deathcheck)
to_chat(dreamer, "<span class='warning'>You feel everything fading away...</span>")
dreamer.death(FALSE)
mastermind.current.audiovisual_redirect = null
if(!cleanup)
if(level_above)
level_above.level_below = null
level_above = null
UnregisterSignal(mastermind.current, list(COMSIG_MOB_DEATH, COMSIG_PARENT_QDELETING, COMSIG_MOB_PRE_PLAYER_CHANGE))
UnregisterSignal(mastermind, COMSIG_PRE_MIND_TRANSFER)
mastermind = null
if(cleanup)
var/obj/effect/vr_clean_master/cleanbot = locate() in get_area(M)
if(cleanbot)
LAZYOR(cleanbot.corpse_party, M)
qdel(src)
/datum/component/virtual_reality/Destroy()
var/datum/action/quit_vr/delet_me = quit_action
. = ..()
qdel(delet_me)
/**
* Used for recursive virtual realities shenanigeans and should be called only through the above proc.
*/
/datum/component/virtual_reality/proc/vr_in_a_vr(mob/player, deathcheck = FALSE, lethal_cleanup = FALSE)
var/mob/M = parent
quit(deathcheck, lethal_cleanup, player)
M.audiovisual_redirect = null
if(lethal_cleanup)
M.death(FALSE)

View File

@@ -34,10 +34,12 @@
last_process = world.time
/datum/component/wet_floor/RegisterWithParent()
. = ..()
RegisterSignal(parent, COMSIG_TURF_IS_WET, .proc/is_wet)
RegisterSignal(parent, COMSIG_TURF_MAKE_DRY, .proc/dry)
/datum/component/wet_floor/UnregisterFromParent()
. = ..()
UnregisterSignal(parent, list(COMSIG_TURF_IS_WET, COMSIG_TURF_MAKE_DRY))
/datum/component/wet_floor/Destroy()

View File

@@ -255,7 +255,7 @@
M.fields["alg_d"] = "No allergies have been detected in this patient."
M.fields["cdi"] = "None"
M.fields["cdi_d"] = "No diseases have been diagnosed at the moment."
M.fields["notes"] = "No notes."
M.fields["notes"] = H.get_trait_string(medical)
medical += M
//Security Record

View File

@@ -1398,3 +1398,29 @@
var/mob/living/carbon/human/H = locate(href_list["copyoutfit"]) in GLOB.carbon_list
if(istype(H))
H.copy_outfit()
else if(href_list["modquirks"])
if(!check_rights(R_SPAWN))
return
var/mob/living/carbon/human/H = locate(href_list["modquirks"]) in GLOB.mob_list
if(!istype(H))
to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human")
return
var/list/options = list("Clear"="Clear")
for(var/x in subtypesof(/datum/quirk))
var/datum/quirk/T = x
var/qname = initial(T.name)
options[H.has_quirk(T) ? "[qname] (Remove)" : "[qname] (Add)"] = T
var/result = input(usr, "Choose quirk to add/remove","Quirk Mod") as null|anything in options
if(result)
if(result == "Clear")
for(var/datum/quirk/q in H.roundstart_quirks)
H.remove_quirk(q.type)
else
var/T = options[result]
if(H.has_quirk(T))
H.remove_quirk(T)
else
H.add_quirk(T,TRUE)

View File

@@ -27,6 +27,7 @@ GLOBAL_LIST_INIT(huds, list(
ANTAG_HUD_SOULLESS = new/datum/atom_hud/antag/hidden(),
ANTAG_HUD_CLOCKWORK = new/datum/atom_hud/antag(),
ANTAG_HUD_BROTHER = new/datum/atom_hud/antag/hidden(),
ANTAG_HUD_BLOODSUCKER = new/datum/atom_hud/antag/bloodsucker()
))
/datum/atom_hud

View File

@@ -4,7 +4,7 @@
mid_sounds = list('sound/machines/shower/shower_mid1.ogg'=1,'sound/machines/shower/shower_mid2.ogg'=1,'sound/machines/shower/shower_mid3.ogg'=1)
mid_length = 10
end_sound = 'sound/machines/shower/shower_end.ogg'
volume = 20
volume = 10
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -32,7 +32,7 @@
mid_sounds = list('sound/machines/fryer/deep_fryer_1.ogg' = 1, 'sound/machines/fryer/deep_fryer_2.ogg' = 1)
mid_length = 2
end_sound = 'sound/machines/fryer/deep_fryer_emerge.ogg'
volume = 15
volume = 5
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -43,3 +43,12 @@
mid_length = 10
end_sound = 'sound/machines/microwave/microwave-end.ogg'
volume = 90
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/datum/looping_sound/grill
mid_length = 2
mid_sounds = list('sound/machines/fryer/deep_fryer_1.ogg' = 1, 'sound/machines/fryer/deep_fryer_2.ogg' = 1)
volume = 10
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -8,9 +8,7 @@
var/deflection_chance = 0 //Chance to deflect projectiles
var/reroute_deflection = FALSE //Delete the bullet, or actually deflect it in some direction?
var/block_chance = 0 //Chance to block melee attacks using items while on throw mode.
var/restraining = 0 //used in cqc's disarm_act to check if the disarmed is being restrained and so whether they should be put in a chokehold or not
var/help_verb
var/no_guns = FALSE
var/pacifism_check = TRUE //are the martial arts combos/attacks unable to be used by pacifist.
var/allow_temp_override = TRUE //if this martial art can be overridden by temporary martial arts
@@ -28,14 +26,16 @@
/datum/martial_art/proc/add_to_streak(element,mob/living/carbon/human/D)
if(D != current_target)
current_target = D
streak = ""
restraining = 0
reset_streak(D)
streak = streak+element
if(length(streak) > max_streak_length)
streak = copytext(streak,2)
return
/datum/martial_art/proc/reset_streak(mob/living/carbon/human/new_target)
current_target = new_target
streak = ""
/datum/martial_art/proc/basic_hit(mob/living/carbon/human/A,mob/living/carbon/human/D)
var/damage = rand(A.dna.species.punchdamagelow, A.dna.species.punchdamagehigh)
@@ -81,7 +81,7 @@
D.forcesay(GLOB.hit_appends)
return 1
/datum/martial_art/proc/teach(mob/living/carbon/human/H,make_temporary=0)
/datum/martial_art/proc/teach(mob/living/carbon/human/H, make_temporary = FALSE)
if(!istype(H) || !H.mind)
return FALSE
if(H.mind.martial_art)

View File

@@ -9,24 +9,13 @@
id = MARTIALART_CQC
help_verb = /mob/living/carbon/human/proc/CQC_help
block_chance = 75
var/just_a_cook = FALSE
var/static/list/areas_under_siege = typecacheof(list(/area/crew_quarters/kitchen,
/area/crew_quarters/cafeteria,
/area/crew_quarters/bar))
var/old_grab_state = null
var/restraining = FALSE
/datum/martial_art/cqc/under_siege
name = "Close Quarters Cooking"
just_a_cook = TRUE
/datum/martial_art/cqc/proc/drop_restraining()
/datum/martial_art/cqc/reset_streak(mob/living/carbon/human/new_target)
. = ..()
restraining = FALSE
/datum/martial_art/cqc/can_use(mob/living/carbon/human/H)
var/area/A = get_area(H)
if(just_a_cook && !(is_type_in_typecache(A, areas_under_siege)))
return FALSE
return ..()
/datum/martial_art/cqc/proc/check_streak(mob/living/carbon/human/A, mob/living/carbon/human/D)
if(!can_use(A))
return FALSE
@@ -75,6 +64,7 @@
D.apply_damage(10, BRUTE)
log_combat(A, D, "kicked (CQC)")
if(D.IsKnockdown() && !D.stat)
log_combat(A, D, "knocked out (Head kick)(CQC)")
D.visible_message("<span class='warning'>[A] kicks [D]'s head, knocking [D.p_them()] out!</span>", \
"<span class='userdanger'>[A] kicks your head, knocking you out!</span>")
playsound(get_turf(A), 'sound/weapons/genhit1.ogg', 50, 1, -1)
@@ -85,7 +75,8 @@
/datum/martial_art/cqc/proc/Pressure(mob/living/carbon/human/A, mob/living/carbon/human/D)
if(!can_use(A))
return FALSE
D.visible_message("<span class='warning'>[A] forces their arm on [D]'s neck!</span>")
log_combat(A, D, "pressured (CQC)")
D.visible_message("<span class='warning'>[A] punches [D]'s neck!</span>")
D.adjustStaminaLoss(60)
playsound(get_turf(A), 'sound/weapons/cqchit1.ogg', 50, 1, -1)
return TRUE
@@ -96,18 +87,20 @@
if(!can_use(A))
return FALSE
if(!D.stat)
log_combat(A, D, "restrained (CQC)")
D.visible_message("<span class='warning'>[A] locks [D] into a restraining position!</span>", \
"<span class='userdanger'>[A] locks you into a restraining position!</span>")
D.adjustStaminaLoss(20)
D.Stun(100)
restraining = TRUE
addtimer(CALLBACK(src, .proc/drop_restraining), 50, TIMER_UNIQUE)
addtimer(VARSET_CALLBACK(src, restraining, FALSE), 50, TIMER_UNIQUE)
return TRUE
/datum/martial_art/cqc/proc/Consecutive(mob/living/carbon/human/A, mob/living/carbon/human/D)
if(!can_use(A))
return FALSE
if(!D.stat)
log_combat(A, D, "consecutive CQC'd (CQC)")
D.visible_message("<span class='warning'>[A] strikes [D]'s abdomen, neck and back consecutively</span>", \
"<span class='userdanger'>[A] strikes your abdomen, neck and back consecutively!</span>")
playsound(get_turf(D), 'sound/weapons/cqchit2.ogg', 50, 1, -1)
@@ -119,23 +112,20 @@
return TRUE
/datum/martial_art/cqc/grab_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
if(!can_use(A))
return FALSE
add_to_streak("G",D)
if(check_streak(A,D))
return TRUE
if(A == D) // no self grab.
return FALSE
if(A.grab_state >= GRAB_AGGRESSIVE)
if(A.a_intent == INTENT_GRAB && A!=D && can_use(A)) // A!=D prevents grabbing yourself
add_to_streak("G",D)
if(check_streak(A,D)) //if a combo is made no grab upgrade is done
return TRUE
old_grab_state = A.grab_state
D.grabbedby(A, 1)
else
A.start_pulling(D, 1)
if(A.pulling)
D.stop_pulling()
if(old_grab_state == GRAB_PASSIVE)
D.drop_all_held_items()
A.grab_state = GRAB_AGGRESSIVE //Instant agressive grab if on grab intent
log_combat(A, D, "grabbed", addition="aggressively")
A.grab_state = GRAB_AGGRESSIVE //Instant aggressive grab
return TRUE
D.visible_message("<span class='warning'>[A] violently grabs [D]!</span>", \
"<span class='userdanger'>[A] violently grabs you!</span>")
return TRUE
return FALSE
/datum/martial_art/cqc/harm_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
if(!can_use(A))
@@ -190,6 +180,7 @@
playsound(D, 'sound/weapons/punchmiss.ogg', 25, 1, -1)
log_combat(A, D, "disarmed (CQC)", "[I ? " grabbing \the [I]" : ""]")
if(restraining && A.pulling == D)
log_combat(A, D, "knocked out (Chokehold)(CQC)")
D.visible_message("<span class='danger'>[A] puts [D] into a chokehold!</span>", \
"<span class='userdanger'>[A] puts you into a chokehold!</span>")
D.SetSleeping(400)
@@ -208,9 +199,19 @@
to_chat(usr, "<b><i>You try to remember some of the basics of CQC.</i></b>")
to_chat(usr, "<span class='notice'>Slam</span>: Grab Harm. Slam opponent into the ground, knocking them down.")
to_chat(usr, "<span class='notice'>CQC Kick</span>: Disarm Harm Harm. Knocks opponent away. Knocks out stunned or knocked down opponents.")
to_chat(usr, "<span class='notice'>Restrain</span>: Grab Grab. Locks opponents into a restraining position, disarm to knock them out with a choke hold.")
to_chat(usr, "<span class='notice'>CQC Kick</span>: Harm Harm. Knocks opponent away. Knocks out stunned or knocked down opponents.")
to_chat(usr, "<span class='notice'>Restrain</span>: Grab Grab. Locks opponents into a restraining position, disarm to knock them out with a chokehold.")
to_chat(usr, "<span class='notice'>Pressure</span>: Disarm Grab. Decent stamina damage.")
to_chat(usr, "<span class='notice'>Consecutive CQC</span>: Disarm Disarm Harm. Mainly offensive move, huge damage and decent stamina damage.")
to_chat(usr, "<b><i>In addition, by having your throw mode on when being attacked, you enter an active defense mode where you have a chance to block and sometimes even counter attacks done to you.</i></b>")
///Subtype of CQC. Only used for the chef.
/datum/martial_art/cqc/under_siege
name = "Close Quarters Cooking"
///Prevents use if the cook is not in the kitchen.
/datum/martial_art/cqc/under_siege/can_use(mob/living/carbon/human/H) //this is used to make chef CQC only work in kitchen
if(!istype(get_area(H), /area/crew_quarters/kitchen))
return FALSE
return ..()

View File

@@ -9,35 +9,36 @@
id = MARTIALART_SLEEPINGCARP
deflection_chance = 100
reroute_deflection = TRUE
no_guns = TRUE
allow_temp_override = FALSE
help_verb = /mob/living/carbon/human/proc/sleeping_carp_help
var/old_grab_state = null
/datum/martial_art/the_sleeping_carp/proc/check_streak(mob/living/carbon/human/A, mob/living/carbon/human/D)
if(findtext(streak,WRIST_WRENCH_COMBO))
streak = ""
wristWrench(A,D)
return 1
return TRUE
if(findtext(streak,BACK_KICK_COMBO))
streak = ""
backKick(A,D)
return 1
return TRUE
if(findtext(streak,STOMACH_KNEE_COMBO))
streak = ""
kneeStomach(A,D)
return 1
return TRUE
if(findtext(streak,HEAD_KICK_COMBO))
streak = ""
headKick(A,D)
return 1
return TRUE
if(findtext(streak,ELBOW_DROP_COMBO))
streak = ""
elbowDrop(A,D)
return 1
return 0
return TRUE
return FALSE
/datum/martial_art/the_sleeping_carp/proc/wristWrench(mob/living/carbon/human/A, mob/living/carbon/human/D)
if(!D.stat && !D.IsStun() && !D.IsKnockdown())
log_combat(A, D, "wrist wrenched (Sleeping Carp)")
A.do_attack_animation(D, ATTACK_EFFECT_PUNCH)
D.visible_message("<span class='warning'>[A] grabs [D]'s wrist and wrenches it sideways!</span>", \
"<span class='userdanger'>[A] grabs your wrist and violently wrenches it to the side!</span>")
@@ -46,24 +47,29 @@
D.dropItemToGround(D.get_active_held_item())
D.apply_damage(5, BRUTE, pick(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM))
D.Knockdown(60)//CIT CHANGE - makes sleepingcarp use knockdown() for its stuns instead of stun()
return 1
log_combat(A, D, "wrist wrenched (Sleeping Carp)")
return TRUE
return basic_hit(A,D)
/datum/martial_art/the_sleeping_carp/proc/backKick(mob/living/carbon/human/A, mob/living/carbon/human/D)
if(A.dir == D.dir && !D.stat && !D.IsKnockdown())
A.do_attack_animation(D, ATTACK_EFFECT_PUNCH)
D.visible_message("<span class='warning'>[A] kicks [D] in the back!</span>", \
"<span class='userdanger'>[A] kicks you in the back, making you stumble and fall!</span>")
step_to(D,get_step(D,D.dir),1)
D.Knockdown(80)
playsound(get_turf(D), 'sound/weapons/punch1.ogg', 50, 1, -1)
return 1
log_combat(A, D, "back-kicked (Sleeping Carp)")
if(!D.stat && !D.IsKnockdown())
if(A.dir == D.dir)
log_combat(A, D, "back-kicked (Sleeping Carp)")
A.do_attack_animation(D, ATTACK_EFFECT_PUNCH)
D.visible_message("<span class='warning'>[A] kicks [D] in the back!</span>", \
"<span class='userdanger'>[A] kicks you in the back, making you stumble and fall!</span>")
step_to(D,get_step(D,D.dir),1)
D.Knockdown(80)
playsound(get_turf(D), 'sound/weapons/punch1.ogg', 50, 1, -1)
return TRUE
else
log_combat(A, D, "missed a back-kick (Sleeping Carp) on")
D.visible_message("<span class='warning'>[A] tries to kick [D] in the back, but misses!</span>", \
"<span class='userdanger'>[A] tries to kick you in the back, but misses!</span>")
return basic_hit(A,D)
/datum/martial_art/the_sleeping_carp/proc/kneeStomach(mob/living/carbon/human/A, mob/living/carbon/human/D)
if(!D.stat && !D.IsKnockdown())
log_combat(A, D, "stomach kneed (Sleeping Carp)")
A.do_attack_animation(D, ATTACK_EFFECT_KICK)
D.visible_message("<span class='warning'>[A] knees [D] in the stomach!</span>", \
"<span class='userdanger'>[A] winds you with a knee in the stomach!</span>")
@@ -71,12 +77,12 @@
D.losebreath += 3
D.Knockdown(40)//CIT CHANGE - makes sleepingcarp use knockdown() for its stuns instead of stun()
playsound(get_turf(D), 'sound/weapons/punch1.ogg', 50, 1, -1)
return 1
log_combat(A, D, "stomach kneed (Sleeping Carp)")
return TRUE
return basic_hit(A,D)
/datum/martial_art/the_sleeping_carp/proc/headKick(mob/living/carbon/human/A, mob/living/carbon/human/D)
if(!D.stat && !D.IsKnockdown())
log_combat(A, D, "head kicked (Sleeping Carp)")
A.do_attack_animation(D, ATTACK_EFFECT_KICK)
D.visible_message("<span class='warning'>[A] kicks [D] in the head!</span>", \
"<span class='userdanger'>[A] kicks you in the jaw!</span>")
@@ -84,12 +90,12 @@
D.drop_all_held_items()
playsound(get_turf(D), 'sound/weapons/punch1.ogg', 50, 1, -1)
D.Knockdown(80)//CIT CHANGE - makes sleepingcarp use knockdown() for its stuns instead of stun()
return 1
log_combat(A, D, "head kicked (Sleeping Carp)")
return TRUE
return basic_hit(A,D)
/datum/martial_art/the_sleeping_carp/proc/elbowDrop(mob/living/carbon/human/A, mob/living/carbon/human/D)
if(D.IsKnockdown() || D.resting || D.stat)
log_combat(A, D, "elbow dropped (Sleeping Carp)")
A.do_attack_animation(D, ATTACK_EFFECT_PUNCH)
D.visible_message("<span class='warning'>[A] elbow drops [D]!</span>", \
"<span class='userdanger'>[A] piledrives you with their elbow!</span>")
@@ -97,37 +103,29 @@
D.death() //FINISH HIM!
D.apply_damage(50, BRUTE, BODY_ZONE_CHEST)
playsound(get_turf(D), 'sound/weapons/punch1.ogg', 75, 1, -1)
return 1
log_combat(A, D, "elbow dropped (Sleeping Carp)")
return TRUE
return basic_hit(A,D)
/datum/martial_art/the_sleeping_carp/grab_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
add_to_streak("G",D)
if(check_streak(A,D))
return 1
if(A == D) //no self grab stun
return FALSE
if(A.grab_state >= GRAB_AGGRESSIVE)
if(A.a_intent == INTENT_GRAB && A!=D) // A!=D prevents grabbing yourself
add_to_streak("G",D)
if(check_streak(A,D)) //if a combo is made no grab upgrade is done
return TRUE
old_grab_state = A.grab_state
D.grabbedby(A, 1)
else
A.start_pulling(D, 1)
if(A.pulling)
if(old_grab_state == GRAB_PASSIVE)
D.drop_all_held_items()
D.stop_pulling()
if(A.a_intent == INTENT_GRAB)
log_combat(A, D, "grabbed", addition="aggressively")
D.visible_message("<span class='warning'>[A] violently grabs [D]!</span>", \
"<span class='userdanger'>[A] violently grabs you!</span>")
A.grab_state = GRAB_AGGRESSIVE //Instant aggressive grab
else
log_combat(A, D, "grabbed", addition="passively")
A.grab_state = GRAB_PASSIVE
return 1
A.grab_state = GRAB_AGGRESSIVE //Instant agressive grab if on grab intent
log_combat(A, D, "grabbed", addition="aggressively")
D.visible_message("<span class='warning'>[A] violently grabs [D]!</span>", \
"<span class='userdanger'>[A] violently grabs you!</span>")
return TRUE
return FALSE
/datum/martial_art/the_sleeping_carp/harm_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
add_to_streak("H",D)
if(check_streak(A,D))
return 1
return TRUE
A.do_attack_animation(D, ATTACK_EFFECT_PUNCH)
var/atk_verb = pick("punches", "kicks", "chops", "hits", "slams")
D.visible_message("<span class='danger'>[A] [atk_verb] [D]!</span>", \
@@ -138,15 +136,25 @@
D.visible_message("<span class='warning'>[D] stumbles and falls!</span>", "<span class='userdanger'>The blow sends you to the ground!</span>")
D.Knockdown(80)
log_combat(A, D, "[atk_verb] (Sleeping Carp)")
return 1
return TRUE
/datum/martial_art/the_sleeping_carp/disarm_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
add_to_streak("D",D)
if(check_streak(A,D))
return 1
return TRUE
return ..()
/datum/martial_art/the_sleeping_carp/teach(mob/living/carbon/human/H, make_temporary = FALSE)
. = ..()
if(!.)
return
ADD_TRAIT(H, TRAIT_NOGUNS, SLEEPING_CARP_TRAIT)
/datum/martial_art/the_sleeping_carp/on_remove(mob/living/carbon/human/H)
. = ..()
REMOVE_TRAIT(H, TRAIT_NOGUNS, SLEEPING_CARP_TRAIT)
/mob/living/carbon/human/proc/sleeping_carp_help()
set name = "Recall Teachings"
set desc = "Remember the martial techniques of the Sleeping Carp clan."
@@ -233,4 +241,4 @@
/obj/item/twohanded/bostaff/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
if(wielded)
return ..()
return 0
return FALSE

View File

@@ -88,6 +88,9 @@
/datum/mind/proc/transfer_to(mob/new_character, var/force_key_move = 0)
var/old_character = current
var/signals = SEND_SIGNAL(new_character, COMSIG_MOB_PRE_PLAYER_CHANGE, new_character, old_character) | SEND_SIGNAL(src, COMSIG_PRE_MIND_TRANSFER, new_character, old_character)
if(signals & COMPONENT_STOP_MIND_TRANSFER)
return
if(current) // remove ourself from our old body's mind variable
current.mind = null
SStgui.on_transfer(current, new_character)
@@ -125,7 +128,6 @@
transfer_martial_arts(new_character)
if(active || force_key_move)
new_character.key = key //now transfer the key to link the client to our new body
SEND_SIGNAL(src, COMSIG_MIND_TRANSFER, new_character, old_character)
//CIT CHANGE - makes arousal update when transfering bodies
if(isliving(new_character)) //New humans and such are by default enabled arousal. Let's always use the new mind's prefs.
@@ -134,6 +136,8 @@
L.canbearoused = L.client.prefs.arousable //Technically this should make taking over a character mean the body gain the new minds setting...
L.update_arousal_hud() //Removes the old icon
SEND_SIGNAL(src, COMSIG_MIND_TRANSFER, new_character, old_character)
/datum/mind/proc/store_memory(new_text)
if((length(memory) + length(new_text)) <= MAX_MESSAGE_LEN)
memory += "[new_text]<BR>"

View File

@@ -198,3 +198,48 @@
description = "<span class='nicegreen'>It feels quite cold out here.</span>\n"
mood_change = -2
timeout = 1 MINUTES
/datum/mood_event/vampcandle
description = "<span class='umbra'>Something is making your mind feel... loose...</span>\n"
mood_change = -10
timeout = 1 MINUTES
/datum/mood_event/drankblood_bad
description = "<span class='boldwarning'>I drank the blood of a lesser creature. Disgusting.</span>\n"
mood_change = -4
timeout = 900
/datum/mood_event/drankblood_dead
description = "<span class='boldwarning'>I drank dead blood. I am better than this.</span>\n"
mood_change = -7
timeout = 900
/datum/mood_event/drankblood_synth
description = "<span class='boldwarning'>I drank synthetic blood. What is wrong with me?</span>\n"
mood_change = -7
timeout = 900
/datum/mood_event/drankkilled
description = "<span class='boldwarning'>I drank from my victim until they died. I feel...less human.</span>\n"
mood_change = -12
timeout = 6000
/datum/mood_event/madevamp
description = "<span class='boldwarning'>A soul has been cursed to undeath by my own hand.</span>\n"
mood_change = -10
timeout = 10000
/datum/mood_event/vampatefood
description = "<span class='boldwarning'>Mortal nourishment no longer sustains me. I feel unwell.</span>\n"
mood_change = -6
timeout = 1000
/datum/mood_event/daylight_1
description = "<span class='boldwarning'>I slept poorly in a makeshift coffin during the day.</span>\n"
mood_change = -3
timeout = 1000
/datum/mood_event/daylight_2
description = "<span class='boldwarning'>I have been scorched by the unforgiving rays of the sun.</span>\n"
mood_change = -6
timeout = 1200

View File

@@ -134,6 +134,22 @@
mood_change = 3
timeout = 3000
/datum/mood_event/breakfast
description = "<span class='nicegreen'>Nothing like a hearty breakfast to start the shift.</span>\n"
mood_change = 2
timeout = 15 MINUTES
//Power gamer stuff below
/datum/mood_event/drankblood
description = "<span class='nicegreen'>I have fed greedly from that which nourishes me.</span>\n"
mood_change = 10
timeout = 900
/datum/mood_event/coffinsleep
description = "<span class='nicegreen'>I slept in a coffin during the day. I feel whole again.</span>\n"
mood_change = 8
timeout = 1200
//Cursed stuff below.
/datum/mood_event/orgasm
@@ -148,3 +164,8 @@
/datum/mood_event/fedprey
description = "<span class='nicegreen'>It feels quite cozy in here.</span>\n"
mood_change = 3
/datum/mood_event/hope_lavaland
description = "<span class='nicegreen'>What a peculiar emblem. It makes me feel hopeful for my future.</span>\n"
mood_change = 5

View File

@@ -13,6 +13,7 @@
return
ADD_TRAIT(owner, TRAIT_STUNIMMUNE, TRAIT_HULK)
ADD_TRAIT(owner, TRAIT_PUSHIMMUNE, TRAIT_HULK)
ADD_TRAIT(owner, TRAIT_CHUNKYFINGERS, TRAIT_HULK)
owner.update_body_parts()
SEND_SIGNAL(owner, COMSIG_ADD_MOOD_EVENT, "hulk", /datum/mood_event/hulk)
RegisterSignal(owner, COMSIG_MOB_SAY, .proc/handle_speech)
@@ -31,6 +32,7 @@
return
REMOVE_TRAIT(owner, TRAIT_STUNIMMUNE, TRAIT_HULK)
REMOVE_TRAIT(owner, TRAIT_PUSHIMMUNE, TRAIT_HULK)
ADD_TRAIT(owner, TRAIT_CHUNKYFINGERS, TRAIT_HULK)
owner.update_body_parts()
SEND_SIGNAL(owner, COMSIG_CLEAR_MOOD_EVENT, "hulk")
UnregisterSignal(owner, COMSIG_MOB_SAY)

View File

@@ -95,6 +95,7 @@
message = replacetext(message," ugly "," beautiful ")
message = replacetext(message," douchbag "," nice guy ")
message = replacetext(message," whore "," lady ")
message = replacetext(message," gamer "," intellectual ")
message = replacetext(message," nerd "," smarty pants ")
message = replacetext(message," moron "," fun person ")
message = replacetext(message," IT'S LOOSE "," EVERYTHING IS FINE ")

View File

@@ -229,4 +229,13 @@
id = "puzzle"
description = "Mystery to be solved."
suffix = "lavaland_surface_puzzle.dmm"
cost = 5
cost = 5
/datum/map_template/ruin/lavaland/elite_tumor
name = "Pulsating Tumor"
id = "tumor"
description = "A strange tumor which houses a powerful beast..."
suffix = "lavaland_surface_elite_tumor.dmm"
cost = 5
always_place = TRUE
allow_duplicates = TRUE

View File

@@ -80,34 +80,55 @@
desc = "You've fallen asleep. Wait a bit and you should wake up. Unless you don't, considering how helpless you are."
icon_state = "asleep"
//TASER
/datum/status_effect/electrode
id = "tased"
/datum/status_effect/no_combat_mode/
id = "no_combat_mode"
blocks_combatmode = TRUE
status_type = STATUS_EFFECT_REPLACE
alert_type = null
status_type = STATUS_EFFECT_REPLACE
/datum/status_effect/electrode/on_creation(mob/living/new_owner, set_duration)
/datum/status_effect/no_combat_mode/on_creation(mob/living/new_owner, set_duration)
if(isnum(set_duration))
duration = set_duration
. = ..()
if(iscarbon(owner))
var/mob/living/carbon/C = owner
if(C.combatmode)
C.toggle_combat_mode(TRUE)
/datum/status_effect/no_combat_mode/mesmerize
id = "Mesmerize"
alert_type = /obj/screen/alert/status_effect/mesmerized
/obj/screen/alert/status_effect/mesmerized
name = "Mesmerized"
desc = "You cant tear your sight from who is in front of you...Their gaze is simply too enthralling.."
icon = 'icons/mob/actions/bloodsucker.dmi'
icon_state = "power_mez"
/datum/status_effect/no_combat_mode/electrode
id = "tased"
/datum/status_effect/no_combat_mode/electrode/on_creation(mob/living/new_owner, set_duration)
if(isnum(set_duration)) //TODO, figure out how to grab from subtype
duration = set_duration
. = ..()
if(iscarbon(owner))
var/mob/living/carbon/C = owner
if(C.combatmode)
C.toggle_combat_mode(TRUE)
C.add_movespeed_modifier(MOVESPEED_ID_TASED_STATUS, TRUE, override = TRUE, multiplicative_slowdown = 8)
/datum/status_effect/electrode/on_remove()
/datum/status_effect/no_combat_mode/electrode/on_remove()
if(iscarbon(owner))
var/mob/living/carbon/C = owner
C.remove_movespeed_modifier(MOVESPEED_ID_TASED_STATUS)
. = ..()
/datum/status_effect/electrode/tick()
/datum/status_effect/no_combat_mode/electrode/tick()
if(owner)
owner.adjustStaminaLoss(5) //if you really want to try to stamcrit someone with a taser alone, you can, but it'll take time and good timing.
/datum/status_effect/electrode/nextmove_modifier() //why is this a proc. its no big deal since this doesnt get called often at all but literally w h y
/datum/status_effect/no_combat_mode/electrode/nextmove_modifier() //why is this a proc. its no big deal since this doesnt get called often at all but literally w h y
return 2
//OTHER DEBUFFS
@@ -388,6 +409,19 @@
else
new /obj/effect/temp_visual/bleed(get_turf(owner))
/datum/status_effect/neck_slice
id = "neck_slice"
status_type = STATUS_EFFECT_UNIQUE
alert_type = null
duration = -1
/datum/status_effect/neck_slice/tick()
var/mob/living/carbon/human/H = owner
if(H.stat == DEAD || H.bleed_rate <= 8)
H.remove_status_effect(/datum/status_effect/neck_slice)
if(prob(10))
H.emote(pick("gasp", "gag", "choke"))
/mob/living/proc/apply_necropolis_curse(set_curse, duration = 10 MINUTES)
var/datum/status_effect/necropolis_curse/C = has_status_effect(STATUS_EFFECT_NECROPOLIS_CURSE)
if(!set_curse)
@@ -702,4 +736,4 @@ datum/status_effect/pacify
if(LAZYLEN(targets) && I)
to_chat(owner, "<span class='warning'>Your arm spasms!</span>")
owner.log_message("threw [I] due to a Muscle Spasm", LOG_ATTACK)
owner.throw_item(pick(targets))
owner.throw_item(pick(targets))

View File

@@ -8,12 +8,14 @@
mob_trait = TRAIT_ALCOHOL_TOLERANCE
gain_text = "<span class='notice'>You feel like you could drink a whole keg!</span>"
lose_text = "<span class='danger'>You don't feel as resistant to alcohol anymore. Somehow.</span>"
medical_record_text = "Patient demonstrates a high tolerance for alcohol."
/datum/quirk/apathetic
name = "Apathetic"
desc = "You just don't care as much as other people. That's nice to have in a place like this, I guess."
value = 1
mood_quirk = TRUE
medical_record_text = "Patient was administered the Apathy Evaluation Scale but did not bother to complete it."
/datum/quirk/apathetic/add()
var/datum/component/mood/mood = quirk_holder.GetComponent(/datum/component/mood)
@@ -42,6 +44,7 @@
mob_trait = TRAIT_EMPATH
gain_text = "<span class='notice'>You feel in tune with those around you.</span>"
lose_text = "<span class='danger'>You feel isolated from others.</span>"
medical_record_text = "Patient is highly perceptive of and sensitive to social cues, or may possibly have ESP. Further testing needed."
/datum/quirk/freerunning
name = "Freerunning"
@@ -50,6 +53,7 @@
mob_trait = TRAIT_FREERUNNING
gain_text = "<span class='notice'>You feel lithe on your feet!</span>"
lose_text = "<span class='danger'>You feel clumsy again.</span>"
medical_record_text = "Patient scored highly on cardio tests."
/datum/quirk/friendly
name = "Friendly"
@@ -59,6 +63,7 @@
gain_text = "<span class='notice'>You want to hug someone.</span>"
lose_text = "<span class='danger'>You no longer feel compelled to hug others.</span>"
mood_quirk = TRUE
medical_record_text = "Patient demonstrates low-inhibitions for physical contact and well-developed arms. Requesting another doctor take over this case."
/datum/quirk/jolly
name = "Jolly"
@@ -66,6 +71,11 @@
value = 1
mob_trait = TRAIT_JOLLY
mood_quirk = TRUE
medical_record_text = "Patient demonstrates constant euthymia irregular for environment. It's a bit much, to be honest."
/datum/quirk/jolly/on_process()
if(prob(0.05))
SEND_SIGNAL(quirk_holder, COMSIG_ADD_MOOD_EVENT, "jolly", /datum/mood_event/jolly)
/datum/quirk/light_step
name = "Light Step"
@@ -74,6 +84,7 @@
mob_trait = TRAIT_LIGHT_STEP
gain_text = "<span class='notice'>You walk with a little more litheness.</span>"
lose_text = "<span class='danger'>You start tromping around like a barbarian.</span>"
medical_record_text = "Patient's dexterity belies a strong capacity for stealth."
/datum/quirk/quick_step
name = "Quick Step"
@@ -82,6 +93,7 @@
mob_trait = TRAIT_SPEEDY_STEP
gain_text = "<span class='notice'>You feel determined. No time to lose.</span>"
lose_text = "<span class='danger'>You feel less determined. What's the rush, man?</span>"
medical_record_text = "Patient scored highly on racewalking tests."
/datum/quirk/musician
name = "Musician"
@@ -90,6 +102,7 @@
mob_trait = TRAIT_MUSICIAN
gain_text = "<span class='notice'>You know everything about musical instruments.</span>"
lose_text = "<span class='danger'>You forget how musical instruments work.</span>"
medical_record_text = "Patient brain scans show a highly-developed auditory pathway."
/datum/quirk/musician/on_spawn()
var/mob/living/carbon/human/H = quirk_holder
@@ -108,6 +121,7 @@
mob_trait = TRAIT_PHOTOGRAPHER
gain_text = "<span class='notice'>You know everything about photography.</span>"
lose_text = "<span class='danger'>You forget how photo cameras work.</span>"
medical_record_text = "Patient mentions photography as a stress-relieving hobby."
/datum/quirk/photographer/on_spawn()
var/mob/living/carbon/human/H = quirk_holder
@@ -121,12 +135,14 @@
desc = "You know your body well, and can accurately assess the extent of your wounds."
value = 2
mob_trait = TRAIT_SELF_AWARE
medical_record_text = "Patient demonstrates an uncanny knack for self-diagnosis."
/datum/quirk/skittish
name = "Skittish"
desc = "You can conceal yourself in danger. Ctrl-shift-click a closed locker to jump into it, as long as you have access."
value = 2
mob_trait = TRAIT_SKITTISH
medical_record_text = "Patient demonstrates a high aversion to danger and has described hiding in containers out of fear."
/datum/quirk/spiritual
name = "Spiritual"
@@ -135,6 +151,7 @@
mob_trait = TRAIT_SPIRITUAL
gain_text = "<span class='notice'>You feel a little more faithful to the gods today.</span>"
lose_text = "<span class='danger'>You feel less faithful in the gods.</span>"
medical_record_text = "Patient reports a belief in a higher power."
/datum/quirk/tagger
name = "Tagger"
@@ -143,6 +160,7 @@
mob_trait = TRAIT_TAGGER
gain_text = "<span class='notice'>You know how to tag walls efficiently.</span>"
lose_text = "<span class='danger'>You forget how to tag walls properly.</span>"
medical_record_text = "Patient was recently seen for possible paint huffing incident."
/datum/quirk/tagger/on_spawn()
var/mob/living/carbon/human/H = quirk_holder
@@ -158,6 +176,7 @@
mob_trait = TRAIT_VORACIOUS
gain_text = "<span class='notice'>You feel HONGRY.</span>"
lose_text = "<span class='danger'>You no longer feel HONGRY.</span>"
medical_record_text = "Patient demonstrates a disturbing capacity for eating."
/datum/quirk/trandening
name = "High Luminosity Eyes"
@@ -179,6 +198,7 @@
mob_trait = TRAIT_HIGH_BLOOD
gain_text = "<span class='notice'>You feel full of blood!</span>"
lose_text = "<span class='notice'>You feel like your blood pressure went down.</span>"
medical_record_text = "Patient's blood tests report an abnormal concentration of red blood cells in their bloodstream."
/datum/quirk/bloodpressure/add()
var/mob/living/M = quirk_holder
@@ -188,3 +208,15 @@
/datum/quirk/bloodpressure/remove()
var/mob/living/M = quirk_holder
M.blood_ratio = 1
/datum/quirk/night_vision
name = "Night Vision"
desc = "You can see slightly more clearly in full darkness than most people."
value = 1
mob_trait = TRAIT_NIGHT_VISION
gain_text = "<span class='notice'>The shadows seem a little less dark.</span>"
lose_text = "<span class='danger'>Everything seems a little darker.</span>"
/datum/quirk/night_vision/on_spawn()
var/mob/living/carbon/human/H = quirk_holder
H.update_sight()

View File

@@ -22,14 +22,19 @@
value = -1
gain_text = "<span class='danger'>You start feeling depressed.</span>"
lose_text = "<span class='notice'>You no longer feel depressed.</span>" //if only it were that easy!
medical_record_text = "Patient has a severe mood disorder causing them to experience sudden moments of sadness."
medical_record_text = "Patient has a severe mood disorder, causing them to experience acute episodes of depression."
mood_quirk = TRUE
/datum/quirk/depression/on_process()
if(prob(0.05))
SEND_SIGNAL(quirk_holder, COMSIG_ADD_MOOD_EVENT, "depression", /datum/mood_event/depression)
/datum/quirk/family_heirloom
name = "Family Heirloom"
desc = "You are the current owner of an heirloom, passed down for generations. You have to keep it safe!"
value = -1
mood_quirk = TRUE
medical_record_text = "Patient demonstrates an unnatural attachment to a family heirloom."
var/obj/item/heirloom
var/where
@@ -143,6 +148,7 @@
name = "Nyctophobia"
desc = "As far as you can remember, you've always been afraid of the dark. While in the dark without a light source, you instinctually act careful, and constantly feel a sense of dread."
value = -1
medical_record_text = "Patient demonstrates a fear of the dark. (Seriously?)"
/datum/quirk/nyctophobia/on_process()
var/mob/living/carbon/human/H = quirk_holder
@@ -163,7 +169,8 @@
desc = "Bright lights irritate you. Your eyes start to water, your skin feels itchy against the photon radiation, and your hair gets dry and frizzy. Maybe it's a medical condition. If only Nanotrasen was more considerate of your needs..."
value = -1
gain_text = "<span class='danger'>The safty of light feels off...</span>"
lose_text = "<span class='notice'>Enlighing.</span>"
lose_text = "<span class='notice'>Enlightening.</span>"
medical_record_text = "Despite my warnings, the patient refuses turn on the lights, only to end up rolling down a full flight of stairs and into the cellar."
/datum/quirk/lightless/on_process()
var/turf/T = get_turf(quirk_holder)
@@ -332,16 +339,19 @@
medical_record_text = "Patient has an extreme or irrational fear and aversion to an undefined stimuli."
var/datum/brain_trauma/mild/phobia/phobia
/datum/quirk/phobia/add()
/datum/quirk/phobia/post_add()
var/mob/living/carbon/human/H = quirk_holder
phobia = new
H.gain_trauma(phobia, TRAUMA_RESILIENCE_SURGERY)
H.gain_trauma(phobia, TRAUMA_RESILIENCE_ABSOLUTE)
/datum/quirk/phobia/remove()
var/mob/living/carbon/human/H = quirk_holder
H?.cure_trauma_type(phobia, TRAUMA_RESILIENCE_ABSOLUTE)
/datum/quirk/mute
name = "Mute"
desc = "Due to some accident, medical condition, or simply by choice, you are completely unable to speak."
value = -2 //HALP MAINTS
mob_trait = TRAIT_MUTE
gain_text = "<span class='danger'>You find yourself unable to speak!</span>"
lose_text = "<span class='notice'>You feel a growing strength in your vocal chords.</span>"
medical_record_text = "Functionally mute, patient is unable to use their voice in any capacity."
@@ -350,14 +360,17 @@
/datum/quirk/mute/add()
var/mob/living/carbon/human/H = quirk_holder
mute = new
H.gain_trauma(mute, TRAUMA_RESILIENCE_SURGERY)
H.gain_trauma(mute, TRAUMA_RESILIENCE_ABSOLUTE)
/datum/quirk/mute/remove()
var/mob/living/carbon/human/H = quirk_holder
H?.cure_trauma_type(mute, TRAUMA_RESILIENCE_ABSOLUTE)
/datum/quirk/mute/on_process()
if(quirk_holder.mind && LAZYLEN(quirk_holder.mind.antag_datums))
to_chat(quirk_holder, "<span class='boldannounce'>Your antagonistic nature has caused your voice to be heard.</span>")
qdel(src)
/datum/quirk/unstable
name = "Unstable"
desc = "Due to past troubles, you are unable to recover your sanity if you lose it. Be very careful managing your mood!"
@@ -373,7 +386,7 @@
value = -4
gain_text = "<span class='danger'>You can't see anything.</span>"
lose_text = "<span class='notice'>You miraculously gain back your vision.</span>"
medical_record_text = "Subject has permanent blindness."
medical_record_text = "Patient has permanent blindness."
/datum/quirk/blindness/add()
quirk_holder.become_blind(ROUNDSTART_TRAIT)
@@ -384,3 +397,6 @@
if(!H.equip_to_slot_if_possible(glasses, SLOT_GLASSES, bypass_equip_delay_self = TRUE)) //if you can't put it on the user's eyes, put it in their hands, otherwise put it on their eyes eyes
H.put_in_hands(glasses)
H.regenerate_icons()
/datum/quirk/blindness/remove()
quirk_holder?.cure_blind(ROUNDSTART_TRAIT)

View File

@@ -16,6 +16,7 @@
value = 0
gain_text = "<span class='notice'>You feel an intense craving for pineapple.</span>"
lose_text = "<span class='notice'>Your feelings towards pineapples seem to return to a lukewarm state.</span>"
medical_record_text = "Patient demonstrates a pathological love of pineapple."
/datum/quirk/pineapple_liker/add()
var/mob/living/carbon/human/H = quirk_holder
@@ -34,6 +35,7 @@
value = 0
gain_text = "<span class='notice'>You find yourself pondering what kind of idiot actually enjoys pineapples...</span>"
lose_text = "<span class='notice'>Your feelings towards pineapples seem to return to a lukewarm state.</span>"
medical_record_text = "Patient is correct to think that pineapple is disgusting."
/datum/quirk/pineapple_hater/add()
var/mob/living/carbon/human/H = quirk_holder
@@ -52,6 +54,7 @@
value = 0
gain_text = "<span class='notice'>You start craving something that tastes strange.</span>"
lose_text = "<span class='notice'>You feel like eating normal food again.</span>"
medical_record_text = "Patient demonstrates irregular nutrition preferences."
/datum/quirk/deviant_tastes/add()
var/mob/living/carbon/human/H = quirk_holder
@@ -92,7 +95,7 @@
value = 0
gain_text = "<span class='notice'>You feel more prudish.</span>"
lose_text = "<span class='notice'>You don't feel as prudish as before.</span>"
medical_record_text = "Patient exhibits a special gene that makes them immune to Crocin and Hexacrocin."
medical_record_text = "Patient exhibits a special gene that makes them immune to aphrodisiacs."
/datum/quirk/libido
name = "Nymphomania"
@@ -134,6 +137,7 @@
value = 0
mob_trait = TRAIT_PHARMA
lose_text = "<span class='notice'>Your liver feels different.</span>"
medical_record_text = "Non-invasive tests report that the patient's metabolism is indeed incompatible with a certain \"stimulants\"."
var/active = FALSE
var/power = 0
var/cachedmoveCalc = 1

View File

@@ -22,27 +22,17 @@
target_trait = ZTRAIT_STATION
immunity_type = "rad"
var/radiation_intensity = 100
/datum/weather/rad_storm/telegraph()
..()
status_alarm(TRUE)
/datum/weather/rad_storm/weather_act(mob/living/L)
var/resist = L.getarmor(null, "rad")
if(prob(40))
if(ishuman(L))
var/mob/living/carbon/human/H = L
if(H.dna && !HAS_TRAIT(H, TRAIT_RADIMMUNE))
if(prob(max(0,100-resist)))
H.randmuti()
if(prob(50))
if(prob(90))
H.randmutb()
else
H.randmutg()
H.domutcheck()
L.rad_act(20)
var/ratio = 1 - (min(resist, 100) / 100)
L.rad_act(radiation_intensity * ratio)
/datum/weather/rad_storm/end()
if(..())

View File

@@ -327,7 +327,7 @@ GLOBAL_LIST_EMPTY(teleportlocs)
for(var/obj/machinery/light/L in src)
L.update()
/area/proc/updateicon()
/area/proc/update_icon()
var/weather_icon
for(var/V in SSweather.processing)
var/datum/weather/W = V
@@ -337,7 +337,7 @@ GLOBAL_LIST_EMPTY(teleportlocs)
if(!weather_icon)
icon_state = null
/area/space/updateicon()
/area/space/update_icon()
icon_state = null
/*
@@ -370,7 +370,7 @@ GLOBAL_LIST_EMPTY(teleportlocs)
/area/proc/power_change()
for(var/obj/machinery/M in src) // for each machine in the area
M.power_change() // reverify power status (to update icons etc.)
updateicon()
update_icon()
/area/proc/usage(chan)
var/used = 0

View File

@@ -0,0 +1,292 @@
/datum/game_mode
var/list/datum/mind/bloodsuckers = list() // List of minds belonging to this game mode.
var/list/datum/mind/vassals = list() // List of minds that have been turned into Vassals.
//var/list/datum/mind/vamphunters = list() // List of minds hunting vampires. Disabled at the moment
var/obj/effect/sunlight/bloodsucker_sunlight // Sunlight Timer. Created on first Bloodsucker assign. Destroyed on last removed Bloodsucker.
// LISTS //
var/list/vassal_allowed_antags = list(/datum/antagonist/brother, /datum/antagonist/traitor, /datum/antagonist/traitor/internal_affairs, /datum/antagonist/survivalist, \
/datum/antagonist/rev, /datum/antagonist/nukeop, /datum/antagonist/pirate, /datum/antagonist/cult, /datum/antagonist/abductee)
// The antags you're allowed to be if turning Vassal.
/datum/game_mode/bloodsucker
name = "bloodsucker"
config_tag = "bloodsucker"
traitor_name = "Bloodsucker"
antag_flag = ROLE_BLOODSUCKER
false_report_weight = 1
restricted_jobs = list("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 = 20
required_enemies = 2
recommended_enemies = 4
reroll_friendly = FALSE
enemy_minimum_age = 7
round_ends_with_antag_death = FALSE
announce_span = "danger"
announce_text = "Filthy, bloodsucking vampires are crawling around disguised as crewmembers!\n\
<span class='danger'>Bloodsuckers</span>: The crew are cattle, while you are both shepherd and slaughterhouse.\n\
<span class='notice'>Crew</span>: Put an end to the undead infestation before the station is overcome!"
/datum/game_mode/bloodsucker/generate_report()
return "Reports indicate that some of your crew may have toppled statues in the past week, angering the gods and becoming cursed with undeath and a desire for blood. Watch out for crewmembers that seem to shun the light or are found pale and delirious."
// Seems to be run by game ONCE, and finds all potential players to be antag.
/datum/game_mode/bloodsucker/pre_setup()
// Set Restricted Jobs
if(CONFIG_GET(flag/protect_roles_from_antagonist))
restricted_jobs += protected_jobs
if(CONFIG_GET(flag/protect_assistant_from_antagonist))
restricted_jobs += "Assistant"
// Set number of Vamps
recommended_enemies = CLAMP(round(num_players()/10), 1, 6);
// Select Antags
for(var/i = 0, i < recommended_enemies, i++)
if (!antag_candidates.len)
break
var/datum/mind/bloodsucker = pick(antag_candidates)
// Can we even BE a bloodsucker?
//if (can_make_bloodsucker(bloodsucker, display_warning=FALSE))
bloodsuckers += bloodsucker
bloodsucker.restricted_roles = restricted_jobs
log_game("[bloodsucker.key] (ckey) has been selected as a Bloodsucker.")
antag_candidates.Remove(bloodsucker) // Apparently you can also write antag_candidates -= bloodsucker
// Assign Hunters (as many as monsters, plus one)
//assign_monster_hunters(bloodsuckers.len, TRUE, bloodsuckers) // Disabled for now
// Do we have enough vamps to continue?
return bloodsuckers.len >= required_enemies
// Gamemode is all done being set up. We have all our Vamps. We now pick objectives and let them know what's happening.
/datum/game_mode/bloodsucker/post_setup()
// Sunlight (Creating Bloodsuckers manually will check to create this, too)
check_start_sunlight()
// Vamps
for(var/datum/mind/bloodsucker in bloodsuckers)
// spawn() --> Run block of code but game continues on past it.
// sleep() --> Run block of code and freeze code there (including whoever called us) until it's resolved.
//Clean Bloodsucker Species (racist?)
//clean_invalid_species(bloodsucker)
// TO-DO !!!
// Add Bloodsucker Antag Datum (or remove from list on Fail)
if (!make_bloodsucker(bloodsucker))
bloodsuckers -= bloodsucker
// NOTE: Hunters are done in ..() parent proc
return ..()
// Checking for ACTUALLY Dead Vamps
/datum/game_mode/bloodsucker/are_special_antags_dead()
// Bloodsucker not Final Dead
for(var/datum/mind/bloodsucker in bloodsuckers)
if(!bloodsucker.AmFinalDeath())
return FALSE
return TRUE
// Init Sunlight (called from datum_bloodsucker.on_gain(), in case game mode isn't even Bloodsucker
/datum/game_mode/proc/check_start_sunlight()
// Already Sunlight (and not about to cancel)
if (istype(bloodsucker_sunlight) && !bloodsucker_sunlight.cancel_me)
return
bloodsucker_sunlight = new ()
// End Sun (last bloodsucker removed)
/datum/game_mode/proc/check_cancel_sunlight()
// No Sunlight
if (!istype(bloodsucker_sunlight))
return
if (bloodsuckers.len <= 0)
bloodsucker_sunlight.cancel_me = TRUE
qdel(bloodsucker_sunlight)
bloodsucker_sunlight = null
/datum/game_mode/proc/is_daylight()
return istype(bloodsucker_sunlight) && bloodsucker_sunlight.amDay
//////////////////////////////////////////////////////////////////////////////
/datum/game_mode/proc/can_make_bloodsucker(datum/mind/bloodsucker, datum/mind/creator, display_warning=TRUE) // Creator is just here so we can display fail messages to whoever is turning us.
// No Mind
if(!bloodsucker || !bloodsucker.key) // KEY is client login?
//if(creator) // REMOVED. You wouldn't see their name if there is no mind, so why say anything?
// to_chat(creator, "<span class='danger'>[bloodsucker] isn't self-aware enough to be raised as a Bloodsucker!</span>")
return FALSE
// Current body is invalid
if(!ishuman(bloodsucker.current))// && !ismonkey(bloodsucker.current))
if(display_warning && creator)
to_chat(creator, "<span class='danger'>[bloodsucker] isn't evolved enough to be raised as a Bloodsucker!</span>")
return FALSE
// Species Must have a HEART (Sorry Plasmabois)
var/mob/living/carbon/human/H = bloodsucker.current
if(NOBLOOD in H.dna.species.species_traits)
if(display_warning && creator)
to_chat(creator, "<span class='danger'>[bloodsucker]'s DNA isn't compatible!</span>")
return FALSE
// Already a Non-Human Antag
if(bloodsucker.has_antag_datum(/datum/antagonist/abductor) || bloodsucker.has_antag_datum(/datum/antagonist/devil) || bloodsucker.has_antag_datum(/datum/antagonist/changeling))
return FALSE
// Already a vamp
if(bloodsucker.has_antag_datum(ANTAG_DATUM_BLOODSUCKER))
if(display_warning && creator)
to_chat(creator, "<span class='danger'>[bloodsucker] is already a Bloodsucker!</span>")
return FALSE
// Not High Enough
if(creator)
var/datum/antagonist/bloodsucker/creator_bloodsucker = creator.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)
if(!istype(creator_bloodsucker) || creator_bloodsucker.vamplevel < BLOODSUCKER_LEVEL_TO_EMBRACE)
to_chat(creator, "<span class='danger'>Your blood is too thin to turn this corpse!</span>")
return FALSE
return TRUE
/datum/game_mode/proc/make_bloodsucker(datum/mind/bloodsucker, datum/mind/creator = null) // NOTE: This is a game_mode/proc, NOT a game_mode/bloodsucker/proc! We need to access this function despite the game mode.
if (!can_make_bloodsucker(bloodsucker))
return FALSE
// Create Datum: Fledgling
var/datum/antagonist/bloodsucker/A
// [FLEDGLING]
if (creator)
A = new (bloodsucker)
A.creator = creator
bloodsucker.add_antag_datum(A)
// Log
message_admins("[bloodsucker] has become a Bloodsucker, and was created by [creator].")
log_admin("[bloodsucker] has become a Bloodsucker, and was created by [creator].")
// [MASTER]
else
A = bloodsucker.add_antag_datum(ANTAG_DATUM_BLOODSUCKER)
return TRUE
/datum/game_mode/proc/remove_bloodsucker(datum/mind/bloodsucker)
bloodsucker.remove_antag_datum(ANTAG_DATUM_BLOODSUCKER)
/datum/game_mode/proc/clean_invalid_species(datum/mind/bloodsucker)
// Only checking for Humans here
if (!ishuman(bloodsucker.current) || !bloodsucker.current.client)
return
var/am_valid = TRUE
var/mob/living/carbon/human/H = bloodsucker.current
// Check if PLASMAMAN?
if(NOBLOOD in H.dna.species.species_traits)
am_valid = FALSE
// PROBLEM:
//
// Setting species leaves clothes on. If you were a plasmaman, we need to reassign your entire outfit. Otherwise
// everyone will wonder why you're a human with Plasma clothes (jk they'll know you're antag)
// Convert to HUMAN (along with ID and PDA)
if (!am_valid)
H.set_species(/datum/species/human)
H.real_name = H.client.prefs.custom_names["human"]
var/obj/item/card/id/ID = H.wear_id?.GetID()
if(ID)
ID.registered_name = H.real_name
ID.update_label()
/datum/game_mode/proc/can_make_vassal(mob/living/target, datum/mind/creator, display_warning=TRUE)//, check_antag_or_loyal=FALSE)
// Not Correct Type: Abort
if (!iscarbon(target) || !creator)
return FALSE
if (target.stat > UNCONSCIOUS)
return FALSE
// Check Overdose: Am I even addicted to blood? Do I even have any in me?
//if (!target.reagents.addiction_list || !target.reagents.reagent_list)
//message_admins("DEBUG2: can_make_vassal() Abort: No reagents")
// return 0
// Check Overdose: Did my current volume go over the Overdose threshold?
//var/am_addicted = 0
//for (var/datum/reagent/blood/vampblood/blood in target.reagents.addiction_list) // overdosed is tracked in reagent_list, not addiction_list.
//message_admins("DEBUG3: can_make_vassal() Found Blood! [blood] [blood.overdose]")
//if (blood.overdosed)
// am_addicted = 1 // Blood is present in addiction? That's all we need.
// break
//if (!am_addicted)
//message_admins("DEBUG4: can_make_vassal() Abort: No Blood")
// return 0
// No Mind!
if (!target.mind || !target.mind.key)
if (display_warning)
to_chat(creator, "<span class='danger'>[target] isn't self-aware enough to be made into a Vassal.</span>")
return FALSE
// Already MY Vassal
var/datum/antagonist/vassal/V = target.mind.has_antag_datum(ANTAG_DATUM_VASSAL)
if (istype(V) && V.master)
if (V.master.owner == creator)
if (display_warning)
to_chat(creator, "<span class='danger'>[target] is already your loyal Vassal!</span>")
else
if (display_warning)
to_chat(creator, "<span class='danger'>[target] is the loyal Vassal of another Bloodsucker!</span>")
return FALSE
// Already Antag or Loyal (Vamp Hunters count as antags)
if (target.mind.enslaved_to || AmInvalidAntag(target.mind)) //!VassalCheckAntagValid(target.mind, check_antag_or_loyal)) // HAS_TRAIT(target, TRAIT_MINDSHIELD, "implant") ||
if (display_warning)
to_chat(creator, "<span class='danger'>[target] resists the power of your blood to dominate their mind!</span>")
return FALSE
return TRUE
/datum/game_mode/proc/AmValidAntag(datum/mind/M)
// No List?
if(!islist(M.antag_datums) || M.antag_datums.len == 0)
return FALSE
// Am I NOT an invalid Antag? NOTE: We already excluded non-antags above. Don't worry about the "No List?" check in AmInvalidIntag()
return !AmInvalidAntag(M)
/datum/game_mode/proc/AmInvalidAntag(datum/mind/M)
// No List?
if(!islist(M.antag_datums) || M.antag_datums.len == 0)
return FALSE
// Does even ONE antag appear in this mind that isn't in the list? Then FAIL!
for(var/datum/antagonist/antag_datum in M.antag_datums)
if (!(antag_datum.type in vassal_allowed_antags)) // vassal_allowed_antags is a list stored in the game mode, above.
//message_admins("DEBUG VASSAL: Found Invalid: [antag_datum] // [antag_datum.type]")
return TRUE
//message_admins("DEBUG VASSAL: Valid Antags! (total of [M.antag_datums.len])")
// WHEN YOU DELETE THE ABOVE: Remove the 3 second timer on converting the vassal too.
return FALSE
/datum/game_mode/proc/make_vassal(mob/living/target, datum/mind/creator)
if (!can_make_vassal(target,creator))
return FALSE
// Make Vassal
var/datum/antagonist/vassal/V = new (target.mind)
var/datum/antagonist/bloodsucker/B = creator.has_antag_datum(ANTAG_DATUM_BLOODSUCKER)
V.master = B
target.mind.add_antag_datum(V, V.master.get_team())
// Update Bloodsucker Title (we're a daddy now)
B.SelectTitle(am_fledgling = FALSE) // Only works if you have no title yet.
// Log
message_admins("[target] has become a Vassal, and is enslaved to [creator].")
log_admin("[target] has become a Vassal, and is enslaved to [creator].")
return TRUE
/datum/game_mode/proc/remove_vassal(datum/mind/vassal)
vassal.remove_antag_datum(ANTAG_DATUM_VASSAL)

View File

@@ -0,0 +1,50 @@
/*
// Called from game mode pre_setup()
/datum/game_mode/proc/assign_monster_hunters(monster_count = 4, guaranteed_hunters = FALSE, list/datum/mind/exclude_from_hunter)
// Not all game modes GUARANTEE a hunter
if (rand(0,2) == 0) // 50% of the time, we get fewer or NO Hunters
if (!guaranteed_hunters)
return
else
monster_count /= 2
var/list/no_hunter_jobs = list("AI","Cyborg")
// Set Restricted Jobs
if(CONFIG_GET(flag/protect_roles_from_antagonist))
no_hunter_jobs += list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster")
if(CONFIG_GET(flag/protect_assistant_from_antagonist))
no_hunter_jobs += "Assistant"
// Find Valid Hunters
var/list/datum/mind/hunter_candidates = get_players_for_role(ROLE_MONSTERHUNTER)
// Assign Hunters (as many as vamps, plus one)
for(var/i = 1, i < monster_count, i++) // Start at 1 so we skip Hunters if there's only one sucker.
if (!hunter_candidates.len)
break
// Assign Hunter
var/datum/mind/hunter = pick(hunter_candidates)
hunter_candidates.Remove(hunter) // Remove Either Way
// Already Antag? Skip
if (islist(exclude_from_hunter) && (locate(hunter) in exclude_from_hunter)) //if (islist(hunter.antag_datums) && hunter.antag_datums.len)
i --
continue
// NOTE:
vamphunters += hunter
hunter.restricted_roles = no_hunter_jobs
log_game("[hunter.key] (ckey) has been selected as a Hunter.")
// Called from game mode post_setup()
/datum/game_mode/proc/finalize_monster_hunters(monster_count = 4)
var/amEvil = TRUE // First hunter is always an evil boi
for(var/datum/mind/hunter in vamphunters)
var/datum/antagonist/vamphunter/A = new (hunter)
A.bad_dude = amEvil
hunter.add_antag_datum(A)
amEvil = FALSE // Every other hunter is just a boring greytider
*/

View File

@@ -265,6 +265,7 @@
armor = list("melee" = 40, "bullet" = 40, "laser" = 50, "energy" = 35, "bomb" = 20, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100)
max_temperature = 35000
operation_req_access = list(ACCESS_SYNDICATE)
internals_req_access = list(ACCESS_SYNDICATE)
wreckage = /obj/structure/mecha_wreckage/honker/dark
max_equip = 3
spawn_tracked = FALSE

View File

@@ -300,6 +300,7 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
/datum/game_mode/dynamic/proc/log_threat(var/log_str,var/verbose = FALSE)
threat_log_verbose += ("[worldtime2text()]: "+log_str)
SSblackbox.record_feedback("tally","dynamic_threat_log",1,log_str)
if(!verbose)
threat_log += log_str
@@ -329,6 +330,10 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
peaceful_percentage = round(LORENTZ_CUMULATIVE_DISTRIBUTION(relative_threat, GLOB.dynamic_curve_centre, GLOB.dynamic_curve_width), 0.01)*100
threat = threat_level
SSblackbox.record_feedback("tally","dynamic_threat",threat_level,"Initial threat level")
SSblackbox.record_feedback("tally","dynamic_threat",GLOB.dynamic_curve_centre,"Curve centre")
SSblackbox.record_feedback("tally","dynamic_threat",GLOB.dynamic_curve_width,"Curve width")
SSblackbox.record_feedback("tally","dynamic_threat",peaceful_percentage,"Percent of same-vote rounds that are more peaceful")
/datum/game_mode/dynamic/can_start()
message_admins("Dynamic mode parameters for the round:")
@@ -340,6 +345,7 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
if(GLOB.dynamic_forced_threat_level >= 0)
threat_level = round(GLOB.dynamic_forced_threat_level, 0.1)
threat = threat_level
SSblackbox.record_feedback("tally","dynamic_threat",threat_level,"Threat level (forced by admins)")
else
generate_threat()
@@ -384,7 +390,8 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
if (roundstart_rules.len <= 0)
log_game("DYNAMIC: [roundstart_rules.len] rules.")
return TRUE
SSblackbox.record_feedback("tally","dynamic",roundstart_rules.len,"Roundstart rules considered")
SSblackbox.record_feedback("tally","dynamic",roundstart_rules.len,"Players readied up")
if(GLOB.dynamic_forced_roundstart_ruleset.len > 0)
rigged_roundstart()
else
@@ -537,6 +544,7 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
if(rule.execute())
if(rule.persistent)
current_rules += rule
SSblackbox.record_feedback("associative","dynamic_rulesets",1,rule.get_blackbox_info())
return TRUE
rule.clean_up() // Refund threat, delete teams and so on.
executed_rules -= rule
@@ -615,6 +623,7 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
else if(new_rule.flags & ONLY_RULESET)
only_ruleset_executed = TRUE
log_game("DYNAMIC: Making a call to a specific ruleset...[new_rule.name]!")
SSblackbox.record_feedback("associative","dynamic_rulesets",1,new_rule.get_blackbox_info())
executed_rules += new_rule
if (new_rule.persistent)
current_rules += new_rule
@@ -639,6 +648,7 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
message_admins("[key_name(M)] joined the station, and was selected by the [rule.name] ruleset.")
log_game("DYNAMIC: [key_name(M)] joined the station, and was selected by the [rule.name] ruleset.")
executed_rules += rule
SSblackbox.record_feedback("associative","dynamic_rulesets",1,rule.get_blackbox_info())
rule.candidates.Cut()
if (rule.persistent)
current_rules += rule
@@ -655,6 +665,8 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
for (var/datum/dynamic_ruleset/rule in current_rules)
if(rule.rule_process() == RULESET_STOP_PROCESSING) // If rule_process() returns 1 (RULESET_STOP_PROCESSING), stop processing.
current_rules -= rule
SSblackbox.record_feedback("tally","dynamic",1,"Rulesets finished")
SSblackbox.record_feedback("associative","dynamic_rulesets_finished",1,rule.get_blackbox_info())
if (midround_injection_cooldown < world.time)
if (GLOB.dynamic_forced_extended)
@@ -673,6 +685,7 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
update_playercounts()
if (get_injection_chance())
SSblackbox.record_feedback("tally","dynamic",1,"Attempted midround injections")
var/cur_threat_frac = threat/threat_level
var/list/drafted_rules = list()
var/antag_num = current_players[CURRENT_LIVING_ANTAGS].len
@@ -688,12 +701,16 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
drafted_rules[rule] = round(rule.get_weight() + (rule.cost * cur_threat_frac))
else
drafted_rules[rule] = rule.get_weight()
else if(threat < rule.cost)
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough threat to spend")
if (drafted_rules.len > 0)
SSblackbox.record_feedback("tally","dynamic",1,"Successful midround injections")
picking_midround_latejoin_rule(drafted_rules)
else
midround_injection_cooldown = (midround_injection_cooldown + world.time)/2
if(event_injection_cooldown < world.time)
SSblackbox.record_feedback("tally","dynamic",1,"Attempted event injections")
var/event_injection_cooldown_middle = 0.5*(GLOB.dynamic_event_delay_max + GLOB.dynamic_event_delay_min)
event_injection_cooldown = (round(CLAMP(EXP_DISTRIBUTION(event_injection_cooldown_middle), GLOB.dynamic_event_delay_min, GLOB.dynamic_event_delay_max)) + world.time)
message_admins("DYNAMIC: Doing event injection.")
@@ -704,7 +721,10 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
if(rule.acceptable(current_players[CURRENT_LIVING_PLAYERS].len, threat_level) && threat >= rule.cost)
if(rule.ready())
drafted_rules[rule] = rule.get_weight()
else if(threat < rule.cost)
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough threat to spend")
if(drafted_rules.len > 0)
SSblackbox.record_feedback("tally","dynamic",1,"Successful event injections")
picking_midround_latejoin_rule(drafted_rules)
/// Updates current_players.
@@ -795,6 +815,7 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
forced_latejoin_rule = null
else if (latejoin_injection_cooldown < world.time && prob(get_injection_chance()))
SSblackbox.record_feedback("tally","dynamic",1,"Attempted latejoin injections")
var/list/drafted_rules = list()
for (var/datum/dynamic_ruleset/latejoin/rule in latejoin_rules)
if (rule.acceptable(current_players[CURRENT_LIVING_PLAYERS].len, threat_level) && threat >= rule.cost)
@@ -812,12 +833,14 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
drafted_rules[rule] = rule.get_weight()
if (drafted_rules.len > 0 && picking_midround_latejoin_rule(drafted_rules))
SSblackbox.record_feedback("tally","dynamic",1,"Successful latejoin injections")
var/latejoin_injection_cooldown_middle = 0.5*(GLOB.dynamic_latejoin_delay_max + GLOB.dynamic_latejoin_delay_min)
latejoin_injection_cooldown = round(CLAMP(EXP_DISTRIBUTION(latejoin_injection_cooldown_middle), GLOB.dynamic_latejoin_delay_min, GLOB.dynamic_latejoin_delay_max)) + world.time
/// Refund threat, but no more than threat_level.
/datum/game_mode/dynamic/proc/refund_threat(regain)
threat = min(threat_level,threat+regain)
SSblackbox.record_feedback("tally","dynamic_threat",regain,"Refunded threat")
log_threat("[regain] refunded. Threat is now [threat].", verbose = TRUE)
/// Generate threat and increase the threat_level if it goes beyond, capped at 100
@@ -825,11 +848,13 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
threat = min(100, threat+gain)
if(threat > threat_level)
threat_level = threat
SSblackbox.record_feedback("tally","dynamic_threat",gain,"Created threat")
log_threat("[gain] created. Threat is now [threat] and threat level is now [threat_level].", verbose = TRUE)
/// Expend threat, can't fall under 0.
/datum/game_mode/dynamic/proc/spend_threat(cost)
threat = max(threat-cost,0)
SSblackbox.record_feedback("tally","dynamic_threat",cost,"Threat spent")
log_threat("[cost] spent. Threat is now [threat].", verbose = TRUE)
/// Turns the value generated by lorentz distribution to threat value between 0 and 100.

View File

@@ -80,6 +80,9 @@
/// Delay for when execute will get called from the time of post_setup (roundstart) or process (midround/latejoin).
/// Make sure your ruleset works with execute being called during the game when using this, and that the clean_up proc reverts it properly in case of faliure.
var/delay = 0
/// Whether or not recent-round weight values are taken into account for this ruleset.
/// Weight reduction uses the same values as secret's recent-round mode weight reduction.
var/always_max_weight = FALSE
/datum/dynamic_ruleset/New()
..()
@@ -91,8 +94,15 @@
var/costs = CONFIG_GET(keyed_list/dynamic_cost)
var/requirementses = CONFIG_GET(keyed_list/dynamic_requirements) // can't damn well use requirements
var/high_population_requirements = CONFIG_GET(keyed_list/dynamic_high_population_requirement)
var/list/repeated_mode_adjust = CONFIG_GET(number_list/repeated_mode_adjust)
if(config_tag in weights)
weight = weights[config_tag]
var/weight_mult = 1
if(!always_max_weight && SSpersistence.saved_dynamic_rules.len == 3 && repeated_mode_adjust.len == 3)
var/saved_dynamic_rules = SSpersistence.saved_dynamic_rules
for(var/i in 1 to 3)
if(config_tag in saved_dynamic_rules[i])
weight_mult -= (repeated_mode_adjust[i]/100)
weight = weights[config_tag] * weight_mult
if(config_tag in costs)
cost = costs[config_tag]
if(config_tag in requirementses)
@@ -115,12 +125,15 @@
/// If your rule has extra checks, such as counting security officers, do that in ready() instead
/datum/dynamic_ruleset/proc/acceptable(population = 0, threat_level = 0)
if(minimum_players > population)
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to low pop")
return FALSE
if(maximum_players > 0 && population > maximum_players)
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to high pop")
return FALSE
if (population >= GLOB.dynamic_high_pop_limit)
indice_pop = 10
if(threat_level < high_population_requirement)
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough threat level")
log_game("DYNAMIC: [name] did not reach threat level threshold: [threat_level]/[high_population_requirement]")
return FALSE
else
@@ -132,6 +145,7 @@
log_game("DYNAMIC: requirements and antag_cap lists have different lengths in ruleset [name]. Likely config issue, report this.")
indice_pop = min(requirements.len,round(population/pop_per_requirement)+1)
if(threat_level < requirements[indice_pop])
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough threat level")
log_game("DYNAMIC: [name] did not reach threat level threshold: [threat_level]/[requirements[indice_pop]]")
return FALSE
else
@@ -178,6 +192,7 @@
/// IMPORTANT: If ready() returns TRUE, that means pre_execute() or execute() should never fail!
/datum/dynamic_ruleset/proc/ready(forced = 0)
if (required_candidates > candidates.len)
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough candidates")
return FALSE
return TRUE
@@ -212,6 +227,24 @@
/datum/dynamic_ruleset/proc/check_finished()
return FALSE
/// Returns a list to be displayed on statbus.
/datum/dynamic_ruleset/proc/get_blackbox_info()
var/list/ruleset_data = list()
ruleset_data["name"] = name
ruleset_data["rule_type"] = ruletype
ruleset_data["cost"] = total_cost
ruleset_data["weight"] = weight
ruleset_data["scaled_times"] = scaled_times
ruleset_data["antagonist_type"] = antag_datum
ruleset_data["population_tier"] = indice_pop
ruleset_data["assigned"] = list()
for (var/datum/mind/M in assigned)
var/assigned_data = list()
assigned_data["key"] = M.key
assigned_data["name"] = M.name
ruleset_data["assigned"] += list(assigned_data)
return ruleset_data
//////////////////////////////////////////////
// //
// ROUNDSTART RULESETS //

View File

@@ -3,6 +3,17 @@
var/typepath // typepath of the event
var/triggering
/datum/dynamic_ruleset/event/get_blackbox_info()
var/list/ruleset_data = list()
ruleset_data["name"] = name
ruleset_data["rule_type"] = ruletype
ruleset_data["cost"] = total_cost
ruleset_data["weight"] = weight
ruleset_data["scaled_times"] = scaled_times
ruleset_data["event_type"] = typepath
ruleset_data["population_tier"] = indice_pop
return ruleset_data
/datum/dynamic_ruleset/event/execute()
var/datum/round_event/E = new typepath()
E.current_players = get_active_player_count(alive_check = 1, afk_check = 1, human_check = 1)
@@ -26,6 +37,7 @@
var/threat = round(mode.threat_level/10)
if (job_check < required_enemies[threat])
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough enemy roles")
return FALSE
return TRUE
@@ -125,6 +137,7 @@
requirements = list(5,5,5,5,5,5,5,5,5,5)
high_population_requirement = 5
repeatable = TRUE
always_max_weight = TRUE
//////////////////////////////////////////////
// //
@@ -146,12 +159,15 @@
/datum/dynamic_ruleset/event/meteor_wave/ready()
if(mode.threat_level > 40 && mode.threat >= 25 && prob(20))
name = "Meteor Wave: Threatening"
cost = 25
typepath = /datum/round_event/meteor_wave/threatening
else if(mode.threat_level > 50 && mode.threat >= 40 && prob(30))
name = "Meteor Wave: Catastrophic"
cost = 40
typepath = /datum/round_event/meteor_wave/catastrophic
else
name = "Meteor Wave: Normal"
cost = 15
typepath = /datum/round_event/meteor_wave
return ..()
@@ -280,6 +296,7 @@
requirements = list(5,5,5,5,5,5,5,5,5,5)
high_population_requirement = 5
repeatable = TRUE
always_max_weight = TRUE
/datum/dynamic_ruleset/event/space_dust
name = "Minor Space Dust"
@@ -293,6 +310,7 @@
requirements = list(5,5,5,5,5,5,5,5,5,5)
high_population_requirement = 5
repeatable = TRUE
always_max_weight = TRUE
/datum/dynamic_ruleset/event/major_dust
name = "Major Space Dust"
@@ -332,6 +350,7 @@
requirements = list(101,101,101,5,5,5,5,5,5,5)
high_population_requirement = 5
repeatable = TRUE
always_max_weight = TRUE
/datum/dynamic_ruleset/event/radiation_storm
name = "Radiation Storm"

View File

@@ -36,9 +36,9 @@
continue // Dead players cannot count as opponents
if (M.mind && M.mind.assigned_role && (M.mind.assigned_role in enemy_roles) && (!(M in candidates) || (M.mind.assigned_role in restricted_roles)))
job_check++ // Checking for "enemies" (such as sec officers). To be counters, they must either not be candidates to that rule, or have a job that restricts them from it
var/threat = round(mode.threat_level/10)
if (job_check < required_enemies[threat])
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough enemy roles")
return FALSE
return ..()
@@ -69,6 +69,7 @@
high_population_requirement = 15
repeatable = TRUE
flags = TRAITOR_RULESET
always_max_weight = TRUE
//////////////////////////////////////////////
// //

View File

@@ -87,6 +87,7 @@
var/threat = round(mode.threat_level/10)
if (job_check < required_enemies[threat])
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough enemy roles")
return FALSE
return TRUE
@@ -179,6 +180,7 @@
repeatable = TRUE
high_population_requirement = 15
flags = TRAITOR_RULESET
always_max_weight = TRUE
/datum/dynamic_ruleset/midround/autotraitor/acceptable(population = 0, threat = 0)
var/player_count = mode.current_players[CURRENT_LIVING_PLAYERS].len
@@ -291,6 +293,7 @@
/datum/dynamic_ruleset/midround/from_ghosts/wizard/ready(forced = FALSE)
if (required_candidates > (dead_players.len + list_observers.len))
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough ghosts")
return FALSE
if(GLOB.wizardstart.len == 0)
log_admin("Cannot accept Wizard ruleset. Couldn't find any wizard spawn points.")
@@ -353,6 +356,7 @@
/datum/dynamic_ruleset/midround/from_ghosts/nuclear/ready(forced = FALSE)
if (required_candidates > (dead_players.len + list_observers.len))
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough ghosts")
return FALSE
return ..()
@@ -389,6 +393,7 @@
/datum/dynamic_ruleset/midround/from_ghosts/blob/ready(forced = FALSE)
if (required_candidates > (dead_players.len + list_observers.len))
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough ghosts")
return FALSE
return ..()
@@ -420,6 +425,7 @@
/datum/dynamic_ruleset/midround/from_ghosts/xenomorph/ready(forced = FALSE)
if (required_candidates > (dead_players.len + list_observers.len))
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough ghosts")
return FALSE
return ..()
@@ -519,6 +525,7 @@
/datum/dynamic_ruleset/midround/from_ghosts/sentient_disease/ready(forced = FALSE)
if (required_candidates > (dead_players.len + list_observers.len))
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough ghosts")
return FALSE
return ..()
@@ -557,6 +564,7 @@
if(deadMobs < REVENANT_SPAWN_THRESHOLD)
return FALSE
if(required_candidates > (dead_players.len + list_observers.len))
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough ghosts")
return FALSE
for(var/mob/living/L in GLOB.dead_mob_list) //look for any dead bodies
var/turf/T = get_turf(L)
@@ -604,6 +612,7 @@
/datum/dynamic_ruleset/midround/from_ghosts/slaughter_demon/ready(forced = FALSE)
if(required_candidates > (dead_players.len + list_observers.len))
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough ghosts")
return FALSE
for(var/obj/effect/landmark/carpspawn/L in GLOB.landmarks_list)
if(isturf(L.loc))
@@ -655,6 +664,7 @@
/datum/dynamic_ruleset/midround/from_ghosts/abductors/ready(forced = FALSE)
if(required_candidates > (dead_players.len + list_observers.len))
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough ghosts")
return FALSE
team = new /datum/team/abductor_team
if(team.team_number > ABDUCTOR_MAX_TEAMS)
@@ -694,6 +704,7 @@
/datum/dynamic_ruleset/midround/from_ghosts/ninja/ready(forced = FALSE)
if(required_candidates > (dead_players.len + list_observers.len))
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough ghosts")
return FALSE
if(!spawn_loc)
var/list/spawn_locs = list()
@@ -734,3 +745,31 @@
#undef ABDUCTOR_MAX_TEAMS
#undef REVENANT_SPAWN_THRESHOLD
//////////////////////////////////////////////
// //
// BLOODSUCKERS //
// //
//////////////////////////////////////////////
/datum/dynamic_ruleset/latejoin/bloodsucker
name = "Bloodsucker Infiltrator"
config_tag = "latejoin_bloodsucker"
antag_datum = ANTAG_DATUM_BLOODSUCKER
antag_flag = ROLE_TRAITOR
restricted_roles = list("AI", "Cyborg")
protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster")
required_candidates = 1
weight = 3
cost = 10
requirements = list(90,80,70,60,55,50,45,40,35,30)
high_population_requirement = 30
repeatable = TRUE
/datum/dynamic_ruleset/latejoin/bloodsucker/execute()
var/mob/M = pick(candidates)
assigned += M.mind
M.mind.special_role = antag_flag
if(mode.make_bloodsucker(M.mind))
mode.bloodsuckers += M
return TRUE

View File

@@ -21,6 +21,7 @@
requirements = list(50,50,50,50,50,50,50,50,50,50)
high_population_requirement = 40
antag_cap = list(1,1,1,1,2,2,2,2,3,3)
always_max_weight = TRUE
var/autotraitor_cooldown = 450 // 15 minutes (ticks once per 2 sec)
/datum/dynamic_ruleset/roundstart/traitor/pre_execute()
@@ -778,3 +779,42 @@
var/ramp_up_final = CLAMP(round(meteorminutes/rampupdelta), 1, 10)
spawn_meteors(ramp_up_final, wavetype)
//////////////////////////////////////////////
// //
// BLOODSUCKERS //
// //
//////////////////////////////////////////////
/datum/dynamic_ruleset/roundstart/bloodsucker
name = "Bloodsuckers"
config_tag = "bloodsucker"
persistent = TRUE
antag_flag = ROLE_BLOODSUCKER
antag_datum = ANTAG_DATUM_BLOODSUCKER
minimum_required_age = 0
protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster")
restricted_roles = list("Cyborg", "AI")
required_candidates = 1
weight = 2
cost = 15
scaling_cost = 10
requirements = list(90,80,70,60,50,50,50,50,50,50)
high_population_requirement = 50
antag_cap = list(1,1,1,1,1,2,2,2,2,2)
/datum/dynamic_ruleset/roundstart/bloodsucker/pre_execute()
var/num_bloodsuckers = antag_cap[indice_pop] * (scaled_times + 1)
for (var/i = 1 to num_bloodsuckers)
var/mob/M = pick_n_take(candidates)
assigned += M.mind
M.mind.special_role = ROLE_BLOODSUCKER
M.mind.restricted_roles = restricted_roles
return TRUE
/datum/dynamic_ruleset/roundstart/bloodsucker/execute()
mode.check_start_sunlight()
for(var/datum/mind/M in assigned)
if(mode.make_bloodsucker(M))
mode.bloodsuckers += M
return TRUE

View File

@@ -81,6 +81,7 @@
///Everyone should now be on the station and have their normal gear. This is the place to give the special roles extra things
/datum/game_mode/proc/post_setup(report) //Gamemodes can override the intercept report. Passing TRUE as the argument will force a report.
//finalize_monster_hunters() Disabled for now
if(!report)
report = !CONFIG_GET(flag/no_intercept_report)
addtimer(CALLBACK(GLOBAL_PROC, .proc/display_roundstart_logout_report), ROUNDSTART_LOGOUT_REPORT_TIME)
@@ -305,48 +306,88 @@
// The odds become:
// Player A: 150 / 250 = 0.6 = 60%
// Player B: 100 / 250 = 0.4 = 40%
/datum/game_mode/proc/antag_pick(list/datum/candidates)
//Use return list if you want a list, with the arg being the number you want returned.
//WARNING: THIS PROC DOES NOT TAKE INTO ACCOUNT WHAT SSPersistence ALREADY HAS FOR "ADJUST ANTAG REP". If this is used more than once
//and the person rolls more than once, they will not get even more deduction!
//More efficient if you use return list instead of calling this multiple times
//fail_default_pick makes it use pick() instead of antag rep if it can't find anyone
//allow_zero_if_insufficient allows it to pick people with zero rep if there isn't enough antags
/datum/game_mode/proc/antag_pick(list/datum/mind/candidates, return_list = FALSE, fail_default_pick = TRUE, allow_zero_if_insufficient = TRUE)
if(!CONFIG_GET(flag/use_antag_rep)) // || candidates.len <= 1)
return pick(candidates)
// Tickets start at 100
var/DEFAULT_ANTAG_TICKETS = CONFIG_GET(number/default_antag_tickets)
//whoever named the config entries is a bad person :(
// You may use up to 100 extra tickets (double your odds)
var/MAX_TICKETS_PER_ROLL = CONFIG_GET(number/max_tickets_per_roll)
var/total_tickets = 0
MAX_TICKETS_PER_ROLL += DEFAULT_ANTAG_TICKETS
var/p_ckey
var/p_rep
for(var/datum/mind/mind in candidates)
p_ckey = ckey(mind.key)
total_tickets += min(SSpersistence.antag_rep[p_ckey] + DEFAULT_ANTAG_TICKETS, MAX_TICKETS_PER_ROLL)
var/antag_select = rand(1,total_tickets)
var/current = 1
for(var/datum/mind/mind in candidates)
p_ckey = ckey(mind.key)
p_rep = SSpersistence.antag_rep[p_ckey]
var/previous = current
var/spend = min(p_rep + DEFAULT_ANTAG_TICKETS, MAX_TICKETS_PER_ROLL)
current += spend
if(antag_select >= previous && antag_select <= (current-1))
SSpersistence.antag_rep_change[p_ckey] = -(spend - DEFAULT_ANTAG_TICKETS)
// WARNING("AR_DEBUG: Player [mind.key] won spending [spend] tickets from starting value [SSpersistence.antag_rep[p_ckey]]")
return mind
WARNING("Something has gone terribly wrong. /datum/game_mode/proc/antag_pick failed to select a candidate. Falling back to pick()")
return pick(candidates)
//Tickets you get for free
var/free_tickets = CONFIG_GET(number/default_antag_tickets)
//Max extra tickets you can use
var/additional_tickets = CONFIG_GET(number/max_tickets_per_roll)
var/list/ckey_to_mind = list() //this is admittedly shitcode but I'm webediting
var/list/prev_tickets = SSpersistence.antag_rep //cache for hyper-speed in theory. how many tickets someone has stored
var/list/curr_tickets = list() //how many tickets someone has for *this* antag roll, so with the free tickets
var/list/datum/mind/insufficient = list() //who got cucked out of an antag roll due to not having *any* tickets
for(var/datum/mind/M in candidates)
var/mind_ckey = ckey(M.key)
var/can_spend = min(prev_tickets[mind_ckey], additional_tickets) //they can only spend up to config/max_tickets_per_roll
var/amount = can_spend + free_tickets //but they get config/default_antag_tickets for free
if(amount <= 0) //if they don't have any
insufficient += M //too bad!
continue
curr_tickets[mind_ckey] = amount
ckey_to_mind[mind_ckey] = M //make sure we can look them up after picking
if(!return_list) //return a single guy
var/ckey
if(length(curr_tickets))
ckey = pickweight(curr_tickets)
SSpersistence.antag_rep_change[ckey] = -(curr_tickets[ckey] - free_tickets) //deduct what they spent
var/mind = ckey_to_mind[ckey] || (allow_zero_if_insufficient? pick(insufficient) : null) //we want their mind
if(!mind) //no mind
var/warning = "WARNING: No antagonists were successfully picked by /datum/gamemode/proc/antag_pick()![fail_default_pick? " Defaulting to pick()!":""]"
message_admins(warning)
log_game(warning)
if(fail_default_pick)
mind = pick(candidates)
return mind
else //the far more efficient and proper use of this, to get a list
var/list/rolled = list()
var/list/spend_tickets = list()
for(var/i in 1 to return_list)
if(!length(curr_tickets)) //ah heck, we're out of candidates..
break
var/ckey = pickweight(curr_tickets) //pick
rolled += ckey //add
spend_tickets[ckey] = curr_tickets[ckey] - free_tickets
curr_tickets -= ckey //don't roll them again
var/missing = return_list - length(rolled)
var/list/add
if((missing > 0) && allow_zero_if_insufficient) //need more..
for(var/i in 1 to missing)
if(!length(insufficient))
break //still not enough
var/datum/mind/M = pick_n_take(insufficient)
add += M
if(!length(rolled) && !length(add)) //if no one could normally roll AND no one can zero roll
var/warning = "WARNING: No antagonists were successfully picked by /datum/gamemode/proc/antag_pick()![fail_default_pick? " Defaulting to pick()!":""]"
message_admins(warning)
log_game(warning)
var/list/failed = list()
if(fail_default_pick)
var/list/C = candidates.Copy()
for(var/i in 1 to return_list)
if(!length(C))
break
failed += pick_n_take(C)
return failed //Wew, no one qualified!
for(var/i in 1 to length(rolled))
var/ckey = rolled[i]
SSpersistence.antag_rep_change[ckey] = -(spend_tickets[ckey]) //deduct what all of the folks who rolled spent
rolled[i] = ckey_to_mind[ckey] //whoever called us wants minds, not ckeys
if(add)
rolled += add
return rolled
/datum/game_mode/proc/get_players_for_role(role)
var/list/players = list()

View File

@@ -0,0 +1,246 @@
#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 = 100
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(100, BURN, "energy", 0)
..()
/obj/machinery/dominator/hulk_damage()
return (max_integrity - integrity_failure) / DOM_HULK_HITS_REQUIRED
/obj/machinery/dominator/tesla_act()
qdel(src)
/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_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
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
if(!.)
STOP_PROCESSING(SSmachines, src)
/obj/machinery/dominator/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0)
switch(damage_type)
if(BRUTE)
if(damage_amount)
playsound(src, 'sound/effects/bang.ogg', 50, 1)
else
playsound(loc, 'sound/weapons/tap.ogg', 50, 1)
if(BURN)
playsound(src.loc, 'sound/items/welder.ogg', 100, 1)
/obj/machinery/dominator/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1)
. = ..()
if(.)
if(obj_integrity/max_integrity > 0.66)
if(prob(damage_amount*2))
spark_system.start()
else if(!(stat & BROKEN))
spark_system.start()
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)
add_fingerprint(user)
..()
/obj/machinery/dominator/attack_hand(mob/user)
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_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(get_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

View File

@@ -0,0 +1,13 @@
/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"

View File

@@ -0,0 +1,477 @@
/datum/antagonist/gang
name = "Gangster"
roundend_category = "gangsters"
can_coexist_with_others = FALSE
job_rank = ROLE_GANG
antagpanel_category = "Gang"
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!</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"
/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)
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)
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)
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)
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.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

View File

@@ -0,0 +1,139 @@
// 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_jacket/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_jacket/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_jacket/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/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/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/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)

View File

@@ -0,0 +1,38 @@
/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 = GAME_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_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_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)

View File

@@ -0,0 +1,34 @@
/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
..()

View File

@@ -0,0 +1,417 @@
/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
name = "Illicit Soporific Cartridges"
id = "sniper_ammo"
cost = 15
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/syndieminibomb/concussion/frag
/datum/gang_item/equipment/stimpack
name = "Black Market Stimulants"
id = "stimpack"
cost = 12
item_path = /obj/item/reagent_containers/syringe/stimulants
/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_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)

View File

@@ -0,0 +1,59 @@
/*
* 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.Knockdown(100)
gangster_mind.add_antag_datum(/datum/antagonist/gang, gang)
return TRUE

View File

@@ -0,0 +1,65 @@
//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
restricted_jobs = list("Security Officer", "Warden", "Detective", "AI", "Cyborg","Captain", "Head of Personnel", "Head of Security")
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()

View File

@@ -0,0 +1,259 @@
//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

View File

@@ -0,0 +1,61 @@
/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)
.=..()

View File

@@ -14,8 +14,8 @@
false_report_weight = 10
restricted_jobs = list("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 = 30
required_enemies = 2
required_players = 20
required_enemies = 1
recommended_enemies = 3
enemy_minimum_age = 14

View File

@@ -25,10 +25,10 @@
// update the invisibility and icon
/obj/machinery/bluespace_beacon/hide(intact)
invisibility = intact ? INVISIBILITY_MAXIMUM : 0
updateicon()
update_icon()
// update the icon_state
/obj/machinery/bluespace_beacon/proc/updateicon()
/obj/machinery/bluespace_beacon/update_icon()
var/state="floor_beacon"
if(invisibility)
@@ -45,4 +45,4 @@
else if (Beacon.loc != loc)
Beacon.forceMove(loc)
updateicon()
update_icon()

View File

@@ -181,8 +181,9 @@ Class Procs:
if(isliving(A))
var/mob/living/L = A
L.update_canmove()
SEND_SIGNAL(src, COMSIG_MACHINE_EJECT_OCCUPANT, occupant)
occupant = null
if(occupant)
SEND_SIGNAL(src, COMSIG_MACHINE_EJECT_OCCUPANT, occupant)
occupant = null
/obj/machinery/proc/can_be_occupant(atom/movable/am)
return occupant_typecache ? is_type_in_typecache(am, occupant_typecache) : isliving(am)

View File

@@ -12,7 +12,6 @@
var/filling = FALSE
var/obj/item/reagent_containers/blood/bag = null
var/obj/item/reagent_containers/blood/outbag = null
var/bloodstored = 0
var/maxbloodstored = 1000
var/menustat = "menu"
var/efficiency = 0
@@ -20,7 +19,7 @@
/obj/machinery/bloodbankgen/Initialize()
. = ..()
create_reagents(1000)
create_reagents(maxbloodstored, AMOUNT_VISIBLE)
update_icon()
/obj/machinery/bloodbankgen/Destroy()
@@ -28,12 +27,13 @@
QDEL_NULL(outbag)
return ..()
/obj/machinery/bloodbankgen/contents_explosion(severity, target)
..()
/obj/machinery/bloodbankgen/examine(mob/user)
. = ..()
if(bag)
bag.ex_act(severity, target)
. += "<span class='notice'>It has \a [bag.name] hooked to its <b>input</b> slot. The counter reads: \"Current Capacity: [bag.reagents.total_volume] of [bag.reagents.maximum_volume]\"</span>"
if(outbag)
outbag.ex_act(severity, target)
. += "<span class='notice'>It has \a [bag.name] hooked to its <b>output</b> slot. The counter reads: \"Current Capacity: [outbag.reagents.total_volume] of [outbag.reagents.maximum_volume]\"</span>"
/obj/machinery/bloodbankgen/handle_atom_del(atom/A)
..()
@@ -115,80 +115,48 @@
/obj/machinery/bloodbankgen/process()
if(!is_operational())
return PROCESS_KILL
bloodstored = reagents.total_volume
return
var/transfer_amount = 20
if(draining)
if(reagents.total_volume >= reagents.maximum_volume)
draining = FALSE
if(reagents.total_volume >= reagents.maximum_volume || !bag || !bag.reagents.total_volume)
beep_stop_pumping()
return
if(bag)
if(bag.reagents.total_volume)
var/datum/reagent/blood/B = bag.reagents.has_reagent("blood")
if(B)
var/amount = reagents.maximum_volume - reagents.total_volume //monitor the machine's internal storage
amount = min(amount, transfer_amount)
if(!amount)
draining = FALSE
updateUsrDialog()
visible_message("[src] beeps loudly.")
playsound(loc, 'sound/machines/twobeep.ogg', 50, 1)
return
if(bag.blood_type == "SY") //no infinite loops using synthetics.
reagents.add_reagent("syntheticblood", amount)
else
reagents.add_reagent("syntheticblood", (amount+(5*efficiency)))
if(bag.reagents.total_volume >= amount)
bag.reagents.remove_reagent("blood", amount)
else
visible_message("[src] beeps loudly.")
playsound(loc, 'sound/machines/twobeep.ogg', 50, 1)
draining = FALSE
bag.update_icon()
update_icon()
updateUsrDialog()
else
draining = FALSE
updateUsrDialog()
var/blood_amount = bag.reagents.get_reagent_amount("blood")
//monitor the machine and blood bag's reagents storage.
var/amount = min(blood_amount, min(transfer_amount, reagents.maximum_volume - reagents.total_volume))
if(!amount)
beep_stop_pumping()
return
var/bonus = bag.blood_type == "SY" ? 0 : 5 * efficiency //no infinite loops using synthetics.
reagents.add_reagent("syntheticblood", amount + bonus)
bag.reagents.remove_reagent("blood", amount)
update_icon()
if(filling)
if(!reagents || !reagents.total_volume)
filling = FALSE //there ain't anything in the machine yo.
if(!reagents.total_volume || !outbag || outbag.reagents.total_volume >= outbag.reagents.maximum_volume)
beep_stop_pumping("[src] pings.", TRUE)
return
if(outbag && outbag.reagents.total_volume < outbag.reagents.maximum_volume)
var/amount = outbag.reagents.maximum_volume - outbag.reagents.total_volume //monitor the output bag's internal storage
amount = min(amount, transfer_amount)
if(!amount)
filling = FALSE
visible_message("[src] pings.")
playsound(loc, 'sound/machines/beep.ogg', 50, 1)
updateUsrDialog()
return
//monitor the output bag's reagents storage.
var/amount = min(transfer_amount, outbag.reagents.maximum_volume - outbag.reagents.total_volume)
reagents.trans_to(outbag, amount)
update_icon()
reagents.trans_to(outbag, amount)
outbag.update_icon()
update_icon()
updateUsrDialog()
else
visible_message("[src] pings.")
playsound(loc, 'sound/machines/beep.ogg', 50, 1)
filling = FALSE
updateUsrDialog()
return
/obj/machinery/bloodbankgen/proc/beep_stop_pumping(msg = "[src] beeps loudly.", out_instead_of_in = FALSE)
if(out_instead_of_in)
filling = FALSE
else
draining = FALSE
updateUsrDialog()
audible_message(msg)
playsound(loc, 'sound/machines/twobeep.ogg', 50, 1)
/obj/machinery/bloodbankgen/attackby(obj/item/O, mob/user, params)
if(user.a_intent == INTENT_HARM)
return ..()
if(default_deconstruction_screwdriver(user, "bloodbank-off", "bloodbank-off", O))
if(default_deconstruction_screwdriver(user, "bloodbank-off", "bloodbank-off", O) || default_unfasten_wrench(user, O, 20) == SUCCESSFUL_UNFASTEN)
if(bag)
var/obj/item/reagent_containers/blood/B = bag
B.forceMove(drop_location())
@@ -204,31 +172,37 @@
return
if(istype(O, /obj/item/reagent_containers/blood))
. = 1 //no afterattack
if(!panel_open)
if(bag && outbag)
to_chat(user, "<span class='warning'>This machine already has bags attached.</span>")
if(!bag && !outbag)
var/choice = alert(user, "Choose where to place [O]", "", "Input", "Cancel", "Output")
switch(choice)
if("Cancel")
return FALSE
if("Input")
attachinput(O, user)
if("Output")
attachoutput(O, user)
else if(!bag)
attachinput(O, user)
else if(!outbag)
attachoutput(O, user)
else
to_chat(user, "<span class='warning'>Close the maintenance panel first.</span>")
return
. = TRUE //no afterattack
var/msg
if(panel_open)
. += "Close the maintenance panel"
if(!anchored)
. += "[msg ? " and a" : "A"]nchor its bolts"
if(length(msg))
to_chat(user, "<span class='warning'>[msg] first.</span>")
return
if(bag && outbag)
to_chat(user, "<span class='warning'>This machine already has bags attached.</span>")
if(!bag && !outbag)
var/choice = alert(user, "Choose where to place [O]", "", "Input", "Cancel", "Output")
switch(choice)
if("Cancel")
return FALSE
if("Input")
attachinput(O, user)
if("Output")
attachoutput(O, user)
else if(!bag)
attachinput(O, user)
else if(!outbag)
attachoutput(O, user)
else
to_chat(user, "<span class='warning'>You cannot put this in [src]!</span>")
/obj/machinery/bloodbankgen/is_operational()
return ..() && anchored
/obj/machinery/bloodbankgen/ui_interact(mob/user)
. = ..()
@@ -268,7 +242,7 @@
if(!bag && !outbag)
dat += "<div class='statusDisplay'>No containers inside, please insert container.</div>"
var/datum/browser/popup = new(user, "bloodbankgen", name, 350, 520)
var/datum/browser/popup = new(user, "bloodbankgen", name, 350, 420)
popup.set_content(dat)
popup.open()
@@ -306,6 +280,7 @@
if(usr && Adjacent(usr) && !issiliconoradminghost(usr))
usr.put_in_hands(bag)
bag = null
draining = null
update_icon()
/obj/machinery/bloodbankgen/proc/detachoutput()
@@ -314,6 +289,7 @@
if(usr && Adjacent(usr) && !issiliconoradminghost(usr))
usr.put_in_hands(outbag)
outbag = null
filling = null
update_icon()
/obj/machinery/bloodbankgen/proc/attachinput(obj/item/O, mob/user)
@@ -339,23 +315,22 @@
to_chat(user, "<span class='notice'>There is already something in this slot!</span>")
/obj/machinery/bloodbankgen/Topic(href, href_list)
if(..() || panel_open)
. = ..()
if(. | !is_operational())
return
usr.set_machine(src)
if(href_list["activateinput"])
activateinput()
updateUsrDialog()
else if(href_list["detachinput"])
detachinput()
updateUsrDialog()
else if(href_list["activateoutput"])
activateoutput()
updateUsrDialog()
else if(href_list["detachoutput"])
detachoutput()
updateUsrDialog()
updateUsrDialog()

View File

@@ -13,7 +13,7 @@
var/chargelevel = -1
var/charge_rate = 500
/obj/machinery/cell_charger/proc/updateicon()
/obj/machinery/cell_charger/update_icon()
cut_overlays()
if(charging)
add_overlay(image(charging.icon, charging.icon_state))
@@ -53,7 +53,7 @@
charging = W
user.visible_message("[user] inserts a cell into [src].", "<span class='notice'>You insert a cell into [src].</span>")
chargelevel = -1
updateicon()
update_icon()
else
if(!charging && default_deconstruction_screwdriver(user, icon_state, icon_state, W))
return
@@ -76,7 +76,7 @@
charging.update_icon()
charging = null
chargelevel = -1
updateicon()
update_icon()
/obj/machinery/cell_charger/attack_hand(mob/user)
. = ..()
@@ -127,4 +127,4 @@
use_power(charge_rate)
charging.give(charge_rate) //this is 2558, efficient batteries exist
updateicon()
update_icon()

View File

@@ -70,7 +70,7 @@
if(check_access(I))
authenticated = 1
auth_id = "[I.registered_name] ([I.assignment])"
if((20 in I.access))
if((ACCESS_CAPTAIN in I.access))
authenticated = 2
playsound(src, 'sound/machines/terminal_on.ogg', 50, 0)
if(obj_flags & EMAGGED)
@@ -279,7 +279,7 @@
// OMG CENTCOM LETTERHEAD
if("MessageCentCom")
if(authenticated==2)
if(authenticated)
if(!checkCCcooldown())
to_chat(usr, "<span class='warning'>Arrays recycling. Please stand by.</span>")
return

View File

@@ -58,8 +58,6 @@
/obj/machinery/firealarm/update_icon()
cut_overlays()
SSvis_overlays.remove_vis_overlay(src, managed_vis_overlays)
var/area/A = src.loc
A = A.loc
if(panel_open)
icon_state = "fire_b[buildstage]"
@@ -69,23 +67,32 @@
icon_state = "firex"
return
icon_state = "fire0"
if(stat & NOPOWER)
icon_state = "fire0"
return
if(is_station_level(z))
add_overlay("overlay_[GLOB.security_level]")
SSvis_overlays.add_vis_overlay(src, icon, "overlay_[GLOB.security_level]", ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir)
else
add_overlay("overlay_[SEC_LEVEL_GREEN]")
SSvis_overlays.add_vis_overlay(src, icon, "overlay_[SEC_LEVEL_GREEN]", ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir)
add_overlay("fire_overlay")
if(detecting)
add_overlay("overlay_[A.fire ? "fire" : "clear"]")
SSvis_overlays.add_vis_overlay(src, icon, "overlay_[A.fire ? "fire" : "clear"]", ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir)
if(is_station_level(z))
add_overlay("fire_[GLOB.security_level]")
SSvis_overlays.add_vis_overlay(src, icon, "fire_[GLOB.security_level]", ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir)
else
add_overlay("overlay_fire")
SSvis_overlays.add_vis_overlay(src, icon, "overlay_fire", ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir)
add_overlay("fire_[SEC_LEVEL_GREEN]")
SSvis_overlays.add_vis_overlay(src, icon, "fire_[SEC_LEVEL_GREEN]", ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir)
var/area/A = src.loc
A = A.loc
if(!detecting || !A.fire)
add_overlay("fire_off")
SSvis_overlays.add_vis_overlay(src, icon, "fire_off", ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir)
else if(obj_flags & EMAGGED)
add_overlay("fire_emagged")
SSvis_overlays.add_vis_overlay(src, icon, "fire_emagged", ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir)
else
add_overlay("fire_on")
SSvis_overlays.add_vis_overlay(src, icon, "fire_on", ABOVE_LIGHTING_LAYER, ABOVE_LIGHTING_PLANE, dir)
/obj/machinery/firealarm/emp_act(severity)
. = ..()
@@ -101,6 +108,7 @@
if(obj_flags & EMAGGED)
return
obj_flags |= EMAGGED
update_icon()
if(user)
user.visible_message("<span class='warning'>Sparks fly out of [src]!</span>",
"<span class='notice'>You emag [src], disabling its thermal sensors.</span>")
@@ -112,51 +120,39 @@
alarm()
..()
/obj/machinery/firealarm/proc/alarm()
if(!is_operational() && (last_alarm+FIREALARM_COOLDOWN < world.time))
/obj/machinery/firealarm/proc/alarm(mob/user)
if(!is_operational() || (last_alarm+FIREALARM_COOLDOWN > world.time))
return
last_alarm = world.time
var/area/A = get_area(src)
A.firealert(src)
playsound(src.loc, 'goon/sound/machinery/FireAlarm.ogg', 75)
playsound(loc, 'goon/sound/machinery/FireAlarm.ogg', 75)
if(user)
log_game("[user] triggered a fire alarm at [COORD(src)]")
/obj/machinery/firealarm/proc/reset()
/obj/machinery/firealarm/proc/reset(mob/user)
if(!is_operational())
return
var/area/A = get_area(src)
A.firereset(src)
if(user)
log_game("[user] reset a fire alarm at [COORD(src)]")
/obj/machinery/firealarm/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "firealarm", name, 300, 150, master_ui, state)
ui.open()
/obj/machinery/firealarm/ui_data(mob/user)
var/list/data = list()
data["emagged"] = obj_flags & EMAGGED ? 1 : 0
if(is_station_level(z))
data["seclevel"] = get_security_level()
else
data["seclevel"] = "green"
/obj/machinery/firealarm/attack_hand(mob/user)
if(buildstage != 2)
return ..()
add_fingerprint(user)
var/area/A = get_area(src)
data["alarm"] = A.fire
if(A.fire)
reset(user)
else
alarm(user)
return data
/obj/machinery/firealarm/attack_ai(mob/user)
return attack_hand(user)
/obj/machinery/firealarm/ui_act(action, params)
if(..() || buildstage != 2)
return
switch(action)
if("reset")
reset()
. = TRUE
if("alarm")
alarm()
. = TRUE
/obj/machinery/firealarm/attack_robot(mob/user)
return attack_hand(user)
/obj/machinery/firealarm/attackby(obj/item/W, mob/user, params)
add_fingerprint(user)
@@ -200,6 +196,12 @@
to_chat(user, "<span class='notice'>You cut the wires from \the [src].</span>")
update_icon()
return
else if(W.force) //hit and turn it on
..()
var/area/A = get_area(src)
if(!A.fire)
alarm()
return
if(1)
if(istype(W, /obj/item/stack/cable_coil))
var/obj/item/stack/cable_coil/coil = W
@@ -322,8 +324,3 @@
if (!party_overlay)
party_overlay = iconstate2appearance('icons/turf/areas.dmi', "party")
A.add_overlay(party_overlay)
/obj/machinery/firealarm/partyalarm/ui_data(mob/user)
. = ..()
var/area/A = get_area(src)
.["alarm"] = A && A.party

View File

@@ -21,9 +21,9 @@
name = "light switch ([area.name])"
on = area.lightswitch
updateicon()
update_icon()
/obj/machinery/light_switch/proc/updateicon()
/obj/machinery/light_switch/update_icon()
if(stat & NOPOWER)
icon_state = "light-p"
else
@@ -41,11 +41,11 @@
on = !on
area.lightswitch = on
area.updateicon()
area.update_icon()
for(var/obj/machinery/light_switch/L in area)
L.on = on
L.updateicon()
L.update_icon()
area.power_change()
@@ -57,7 +57,7 @@
else
stat |= NOPOWER
updateicon()
update_icon()
/obj/machinery/light_switch/emp_act(severity)
. = ..()

View File

@@ -46,10 +46,10 @@
// update the invisibility and icon
/obj/machinery/magnetic_module/hide(intact)
invisibility = intact ? INVISIBILITY_MAXIMUM : 0
updateicon()
update_icon()
// update the icon_state
/obj/machinery/magnetic_module/proc/updateicon()
/obj/machinery/magnetic_module/update_icon()
var/state="floor_magnet"
var/onstate=""
if(!on)
@@ -161,7 +161,7 @@
else
use_power = NO_POWER_USE
updateicon()
update_icon()
/obj/machinery/magnetic_module/proc/magnetic_process() // proc that actually does the magneting

View File

@@ -72,10 +72,10 @@
// hide the object if turf is intact
/obj/machinery/navbeacon/hide(intact)
invisibility = intact ? INVISIBILITY_MAXIMUM : 0
updateicon()
update_icon()
// update the icon_state
/obj/machinery/navbeacon/proc/updateicon()
/obj/machinery/navbeacon/update_icon()
var/state="navbeacon[open]"
if(invisibility)
@@ -94,7 +94,7 @@
user.visible_message("[user] [open ? "opens" : "closes"] the beacon's cover.", "<span class='notice'>You [open ? "open" : "close"] the beacon's cover.</span>")
updateicon()
update_icon()
else if (istype(I, /obj/item/card/id)||istype(I, /obj/item/pda))
if(open)

View File

@@ -326,6 +326,9 @@
/obj/machinery/suit_storage_unit/attackby(obj/item/I, mob/user, params)
if(state_open && is_operational())
if(istype(I, /obj/item/clothing/head/mob_holder))
to_chat(user, "<span class='warning'>You can't quite fit that in while you hold it!</span>")
return
if(istype(I, /obj/item/clothing/suit))
if(suit)
to_chat(user, "<span class='warning'>The unit already contains a suit!.</span>")
@@ -437,4 +440,4 @@
if(I)
I.forceMove(loc)
. = TRUE
update_icon()
update_icon()

View File

@@ -219,6 +219,10 @@
update_icon()
return
if(istype(W, /obj/item/clothing/head/mob_holder))
to_chat(user, "<span class='warning'>It's too unwieldly to put in this way.</span>")
return 1
else if(user.a_intent != INTENT_HARM)
if (!state_open)

View File

@@ -27,6 +27,7 @@
max_temperature = 35000
leg_overload_coeff = 100
operation_req_access = list(ACCESS_SYNDICATE)
internals_req_access = list(ACCESS_SYNDICATE)
wreckage = /obj/structure/mecha_wreckage/gygax/dark
max_equip = 4
spawn_tracked = FALSE

Some files were not shown because too many files have changed in this diff Show More