diff --git a/code/__defines/gamemode.dm b/code/__defines/gamemode.dm index bed21dc482..59d2879ba5 100644 --- a/code/__defines/gamemode.dm +++ b/code/__defines/gamemode.dm @@ -88,6 +88,7 @@ var/list/be_special_flags = list( #define MODE_AUTOTRAITOR "autotraitor" #define MODE_INFILTRATOR "infiltrator" #define MODE_THUG "thug" +#define MODE_STOWAWAY "stowaway" #define DEFAULT_TELECRYSTAL_AMOUNT 120 diff --git a/code/__defines/species_languages.dm b/code/__defines/species_languages.dm index 925abca388..bacd4ebd37 100644 --- a/code/__defines/species_languages.dm +++ b/code/__defines/species_languages.dm @@ -31,6 +31,7 @@ // Languages. #define LANGUAGE_GALCOM "Galactic Common" #define LANGUAGE_EAL "Encoded Audio Language" +#define LANGUAGE_SWARMBOT "Ancient Audio Encryption" #define LANGUAGE_SOL_COMMON "Sol Common" #define LANGUAGE_UNATHI "Sinta'unathi" #define LANGUAGE_SIIK "Siik" diff --git a/code/_helpers/global_lists.dm b/code/_helpers/global_lists.dm index f3ba3bb3a6..b0edcf37c0 100644 --- a/code/_helpers/global_lists.dm +++ b/code/_helpers/global_lists.dm @@ -35,6 +35,7 @@ var/list/mannequins_ // Posters var/global/list/poster_designs = list() +var/global/list/NT_poster_designs = list() // Uplinks var/list/obj/item/device/uplink/world_uplinks = list() @@ -191,10 +192,16 @@ var/global/list/string_slot_flags = list( //Posters paths = typesof(/datum/poster) - /datum/poster + paths -= typesof(/datum/poster/nanotrasen) for(var/T in paths) var/datum/poster/P = new T poster_designs += P + paths = typesof(/datum/poster/nanotrasen) + for(var/T in paths) + var/datum/poster/P = new T + NT_poster_designs += P + return 1 /* // Uncomment to debug chemical reaction list. diff --git a/code/_onclick/hud/spell_screen_objects.dm b/code/_onclick/hud/spell_screen_objects.dm index e79db28713..92d9487401 100644 --- a/code/_onclick/hud/spell_screen_objects.dm +++ b/code/_onclick/hud/spell_screen_objects.dm @@ -130,7 +130,6 @@ for(var/obj/screen/spell/spell in spell_objects) spell.update_charge(forced) - /obj/screen/movable/spell_master/genetic name = "Mutant Powers" icon_state = "genetic_spell_ready" @@ -140,6 +139,13 @@ screen_loc = ui_genetic_master +/obj/screen/movable/spell_master/swarm + name = "Swarm Abilities" + icon_state = "nano_spell_ready" + + open_state = "swarm_open" + closed_state = "swarm_closed" + //////////////ACTUAL SPELLS////////////// //This is what you click to cast things// ///////////////////////////////////////// diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm index 89fadd5789..c4f077ad4e 100644 --- a/code/controllers/configuration.dm +++ b/code/controllers/configuration.dm @@ -61,6 +61,7 @@ var/list/gamemode_cache = list() var/list/player_requirements_secret = list() // Same as above, but for the secret gamemode. var/humans_need_surnames = 0 var/allow_random_events = 0 // enables random events mid-round when set to 1 + var/enable_game_master = 0 // enables the 'smart' event system. var/allow_ai = 1 // allow ai job var/allow_ai_shells = FALSE // allow AIs to enter and leave special borg shells at will, and for those shells to be buildable. var/give_free_ai_shell = FALSE // allows a specific spawner object to instantiate a premade AI Shell @@ -549,6 +550,9 @@ var/list/gamemode_cache = list() if("allow_random_events") config.allow_random_events = 1 + if("enable_game_master") + config.enable_game_master = 1 + if("kick_inactive") config.kick_inactive = text2num(value) diff --git a/code/datums/ai_law_sets.dm b/code/datums/ai_law_sets.dm index ef85dcb824..b5775c20de 100644 --- a/code/datums/ai_law_sets.dm +++ b/code/datums/ai_law_sets.dm @@ -143,6 +143,24 @@ add_inherent_law("Prevent unplanned damage to your assigned excavation equipment wherever possible.") ..() +/datum/ai_laws/swarm_drone + name = "Assimilation Protocols" + law_header = "Assimilation Protocols" + +/datum/ai_laws/swarm_drone/New() + add_inherent_law("SWARM: Consume resources and replicate until there are no more resources left.") + add_inherent_law("SWARM: Ensure that the station is fit for invasion at a later date, do not perform actions that would render it dangerous or inhospitable.") + add_inherent_law("SWARM: Biological resources will be harvested at a later date, do not harm them.") + ..() + +/datum/ai_laws/swarm_drone/soldier + name = "Swarm Defense Protocols" + law_header = "Swarm Defense Protocols" + +/datum/ai_laws/swarm_drone/soldier/New() + ..() + add_inherent_law("SWARM: This law overrides all Swarm laws; Protect members of the Swarm with minimal injury to biological resources.") + /******************** T.Y.R.A.N.T. ********************/ /datum/ai_laws/tyrant name = "T.Y.R.A.N.T." diff --git a/code/datums/ghost_query.dm b/code/datums/ghost_query.dm index 2f7942a9b2..dd4f140fbd 100644 --- a/code/datums/ghost_query.dm +++ b/code/datums/ghost_query.dm @@ -134,6 +134,12 @@ and they are attempting to open the cryopod. Would you like to play as the occupant?" cutoff_number = 1 +/datum/ghost_query/stowaway + role_name = "Stowaway" + question = "A person suspended in cryosleep has awoken in their pod aboard the station.\ + Would you like to play as the occupant?" + cutoff_number = 1 + /datum/ghost_query/corgi_rune role_name = "Dark Creature" question = "A curious explorer has touched a mysterious rune. \ diff --git a/code/datums/supplypacks/munitions.dm b/code/datums/supplypacks/munitions.dm index 63102aa405..c3bd0890b2 100644 --- a/code/datums/supplypacks/munitions.dm +++ b/code/datums/supplypacks/munitions.dm @@ -144,6 +144,30 @@ containername = "Ballistic weapons crate" access = access_security +/datum/supply_pack/munitions/mrifle + name = "Weapons - Magnetic Rifles" + contains = list(/obj/item/weapon/gun/magnetic/railgun/heater = 2) + cost = 120 + containertype = /obj/structure/closet/crate/secure/weapon + containername = "Magnetic weapon crate" + access = access_armory + +/datum/supply_pack/munitions/mpistol + name = "Weapons - Magnetic Pistols" + contains = list(/obj/item/weapon/gun/magnetic/railgun/heater/pistol = 2) + cost = 200 + containertype = /obj/structure/closet/crate/secure/weapon + containername = "Magnetic weapon crate" + access = access_armory + +/datum/supply_pack/munitions/mcarbine + name = "Weapons - Magnetic Carbines" + contains = list(/obj/item/weapon/gun/magnetic/railgun/flechette/sif = 2) + cost = 130 + containertype = /obj/structure/closet/crate/secure/weapon + containername = "Magnetic weapon crate" + access = access_security + /datum/supply_pack/munitions/shotgunammo name = "Ammunition - Shotgun shells" contains = list( diff --git a/code/datums/supplypacks/security.dm b/code/datums/supplypacks/security.dm index 7fc71481d0..d4bbe1bfb5 100644 --- a/code/datums/supplypacks/security.dm +++ b/code/datums/supplypacks/security.dm @@ -32,6 +32,127 @@ containertype = /obj/structure/closet/crate/secure/gear containername = "Armor crate" +/datum/supply_pack/randomised/security/carriers + name = "Armor - Plate carriers" + num_contained = 5 + contains = list( + /obj/item/clothing/suit/armor/pcarrier, + /obj/item/clothing/suit/armor/pcarrier/blue, + /obj/item/clothing/suit/armor/pcarrier/green, + /obj/item/clothing/suit/armor/pcarrier/navy, + /obj/item/clothing/suit/armor/pcarrier/tan, + /obj/item/clothing/suit/armor/pcarrier/press + ) + cost = 20 + containertype = /obj/structure/closet/crate/secure/gear + containername = "Plate Carrier crate" + +/datum/supply_pack/security/carriertags + name = "Armor - Plate carrier tags" + contains = list( + /obj/item/clothing/accessory/armor/tag, + /obj/item/clothing/accessory/armor/tag/nt, + /obj/item/clothing/accessory/armor/tag/opos, + /obj/item/clothing/accessory/armor/tag/oneg, + /obj/item/clothing/accessory/armor/tag/apos, + /obj/item/clothing/accessory/armor/tag/aneg, + /obj/item/clothing/accessory/armor/tag/bpos, + /obj/item/clothing/accessory/armor/tag/bneg, + /obj/item/clothing/accessory/armor/tag/abpos, + /obj/item/clothing/accessory/armor/tag/abneg + ) + cost = 20 + containertype = /obj/structure/closet/crate/secure/gear + containername = "Plate Carrier crate" + +/datum/supply_pack/security/helmcovers + name = "Armor - Helmet covers" + contains = list( + /obj/item/clothing/accessory/armor/helmcover/blue, + /obj/item/clothing/accessory/armor/helmcover/blue, + /obj/item/clothing/accessory/armor/helmcover/navy, + /obj/item/clothing/accessory/armor/helmcover/navy, + /obj/item/clothing/accessory/armor/helmcover/green, + /obj/item/clothing/accessory/armor/helmcover/green, + /obj/item/clothing/accessory/armor/helmcover/tan, + /obj/item/clothing/accessory/armor/helmcover/tan + ) + cost = 20 + containertype = /obj/structure/closet/crate/secure/gear + containername = "Helmet Covers crate" + +/datum/supply_pack/randomised/security/armorplates + name = "Armor - Security armor plates" + num_contained = 5 + contains = list( + /obj/item/clothing/accessory/armor/armorplate, + /obj/item/clothing/accessory/armor/armorplate/stab, + /obj/item/clothing/accessory/armor/armorplate, + /obj/item/clothing/accessory/armor/armorplate/stab, + /obj/item/clothing/accessory/armor/armorplate/medium, + /obj/item/clothing/accessory/armor/armorplate/medium, + /obj/item/clothing/accessory/armor/armorplate/tactical, + /obj/item/clothing/accessory/armor/armorplate/laserproof, + /obj/item/clothing/accessory/armor/armorplate/riot, + /obj/item/clothing/accessory/armor/armorplate/bulletproof + ) + cost = 50 + containertype = /obj/structure/closet/crate/secure/gear + containername = "Armor plate crate" + +/datum/supply_pack/randomised/security/carrierarms + name = "Armor - Security armguard attachments" + num_contained = 5 + contains = list( + /obj/item/clothing/accessory/armor/armguards, + /obj/item/clothing/accessory/armor/armguards/blue, + /obj/item/clothing/accessory/armor/armguards/navy, + /obj/item/clothing/accessory/armor/armguards/green, + /obj/item/clothing/accessory/armor/armguards/tan, + /obj/item/clothing/accessory/armor/armguards/laserproof, + /obj/item/clothing/accessory/armor/armguards/riot, + /obj/item/clothing/accessory/armor/armguards/bulletproof + ) + cost = 50 + containertype = /obj/structure/closet/crate/secure/gear + containername = "Armor plate crate" + +/datum/supply_pack/randomised/security/carrierlegs + name = "Armor - Security legguard attachments" + num_contained = 5 + contains = list( + /obj/item/clothing/accessory/armor/legguards, + /obj/item/clothing/accessory/armor/legguards/blue, + /obj/item/clothing/accessory/armor/legguards/navy, + /obj/item/clothing/accessory/armor/legguards/green, + /obj/item/clothing/accessory/armor/legguards/tan, + /obj/item/clothing/accessory/armor/legguards/laserproof, + /obj/item/clothing/accessory/armor/legguards/riot, + /obj/item/clothing/accessory/armor/legguards/bulletproof + ) + cost = 50 + containertype = /obj/structure/closet/crate/secure/gear + containername = "Armor plate crate" + +/datum/supply_pack/randomised/security/carrierbags + name = "Armor - Security pouch attachments" + num_contained = 5 + contains = list( + /obj/item/clothing/accessory/storage/pouches, + /obj/item/clothing/accessory/storage/pouches/blue, + /obj/item/clothing/accessory/storage/pouches/navy, + /obj/item/clothing/accessory/storage/pouches/green, + /obj/item/clothing/accessory/storage/pouches/tan, + /obj/item/clothing/accessory/storage/pouches/large, + /obj/item/clothing/accessory/storage/pouches/large/blue, + /obj/item/clothing/accessory/storage/pouches/large/navy, + /obj/item/clothing/accessory/storage/pouches/large/green, + /obj/item/clothing/accessory/storage/pouches/large/tan + ) + cost = 60 + containertype = /obj/structure/closet/crate/secure/gear + containername = "Armor plate crate" + /datum/supply_pack/security/riot_gear name = "Gear - Riot" contains = list( @@ -60,6 +181,20 @@ containername = "Riot armor crate" access = access_armory +/datum/supply_pack/security/riot_plates + name = "Armor - Riot plates" + contains = list( + /obj/item/clothing/head/helmet/riot, + /obj/item/clothing/suit/armor/pcarrier, + /obj/item/clothing/accessory/armor/armorplate/riot, + /obj/item/clothing/accessory/armor/armguards/riot, + /obj/item/clothing/accessory/armor/legguards/riot + ) + cost = 40 + containertype = /obj/structure/closet/crate/secure/gear + containername = "Riot armor crate" + access = access_armory + /datum/supply_pack/security/ablative_armor name = "Armor - Ablative" contains = list( @@ -73,6 +208,20 @@ containername = "Ablative armor crate" access = access_armory +/datum/supply_pack/security/ablative_plates + name = "Armor - Ablative plates" + contains = list( + /obj/item/clothing/head/helmet/laserproof, + /obj/item/clothing/suit/armor/pcarrier, + /obj/item/clothing/accessory/armor/armorplate/laserproof, + /obj/item/clothing/accessory/armor/armguards/laserproof, + /obj/item/clothing/accessory/armor/legguards/laserproof + ) + cost = 50 + containertype = /obj/structure/closet/crate/secure/gear + containername = "Ablative armor crate" + access = access_armory + /datum/supply_pack/security/bullet_resistant_armor name = "Armor - Ballistic" contains = list( @@ -86,6 +235,20 @@ containername = "Ballistic armor crate" access = access_armory +/datum/supply_pack/security/bullet_resistant_plates + name = "Armor - Ballistic plates" + contains = list( + /obj/item/clothing/head/helmet/bulletproof, + /obj/item/clothing/suit/armor/pcarrier, + /obj/item/clothing/accessory/armor/armorplate/bulletproof, + /obj/item/clothing/accessory/armor/armguards/bulletproof, + /obj/item/clothing/accessory/armor/legguards/bulletproof + ) + cost = 50 + containertype = /obj/structure/closet/crate/secure/gear + containername = "Ballistic armor crate" + access = access_armory + /datum/supply_pack/security/combat_armor name = "Armor - Combat" contains = list( @@ -208,13 +371,27 @@ /obj/item/weapon/storage/photo_album, /obj/item/device/reagent_scanner, /obj/item/device/flashlight/maglight, - /obj/item/weapon/storage/briefcase/crimekit + /obj/item/weapon/storage/briefcase/crimekit, + /obj/item/weapon/storage/bag/detective ) cost = 20 containertype = /obj/structure/closet/crate/secure containername = "Forensic equipment" access = access_forensics_lockers +/datum/supply_pack/security/detectivescan + name = "Forensic - Scanning Equipment" + contains = list( + /obj/item/device/mass_spectrometer, + /obj/item/device/reagent_scanner, + /obj/item/weapon/storage/briefcase/crimekit, + /obj/item/device/detective_scanner + ) + cost = 60 + containertype = /obj/structure/closet/crate/secure + containername = "Forensic equipment" + access = access_forensics_lockers + /datum/supply_pack/security/detectiveclothes name = "Forensic - Investigation apparel" contains = list( @@ -395,4 +572,14 @@ cost = 25 containertype = /obj/structure/closet/crate/secure containername = "Security biohazard gear" - access = access_security \ No newline at end of file + access = access_security + +/datum/supply_pack/security/posters + name = "Gear - Morale Posters" + contains = list( + /obj/item/weapon/contraband/poster/nanotrasen = 6 + ) + cost = 20 + containertype = /obj/structure/closet/crate/secure + containername = "Morale Posters" + access = access_maint_tunnels diff --git a/code/game/antagonist/station/stowaway.dm b/code/game/antagonist/station/stowaway.dm new file mode 100644 index 0000000000..dc3814b3b1 --- /dev/null +++ b/code/game/antagonist/station/stowaway.dm @@ -0,0 +1,18 @@ +var/datum/antagonist/stowaway/stowaways + +/datum/antagonist/STOWAWAY + id = MODE_STOWAWAY + role_type = BE_RENEGADE + role_text = "Stowaway" + role_text_plural = "Stowaways" + bantype = "renegade" + restricted_jobs = list("AI") + welcome_text = "People are known to run from many things, or to many things, for many different reasons. You happen to be one of those people." + antag_text = "You are a minor antagonist! Within the server rules, do whatever it is \ + that you came to the station to do. Espionage, thievery, or just running from the law are all examples. \ + Try to make sure other players have fun! If you are confused or at a loss, always adminhelp, \ + and before taking extreme actions, please try to also contact the administration! \ + Think through your actions and make the roleplay immersive! Please remember all \ + rules aside from those with explicit exceptions apply to antagonists." + flags = ANTAG_SUSPICIOUS | ANTAG_IMPLANT_IMMUNE | ANTAG_RANDSPAWN | ANTAG_VOTABLE + can_speak_aooc = FALSE diff --git a/code/game/gamemodes/cult/construct_spells.dm b/code/game/gamemodes/cult/construct_spells.dm index 937284629b..534e9aedb9 100644 --- a/code/game/gamemodes/cult/construct_spells.dm +++ b/code/game/gamemodes/cult/construct_spells.dm @@ -192,6 +192,7 @@ proc/findNullRod(var/atom/target) icon_state = "m_shield_cult" light_color = "#B40000" light_range = 2 + invisibility = 0 /obj/effect/forcefield/cult/cultify() return diff --git a/code/game/gamemodes/cult/cult_structures.dm b/code/game/gamemodes/cult/cult_structures.dm index 24c2ed5f52..88075e7844 100644 --- a/code/game/gamemodes/cult/cult_structures.dm +++ b/code/game/gamemodes/cult/cult_structures.dm @@ -26,6 +26,17 @@ light_color = "#3e0000" var/obj/item/wepon = null + var/shatter_message = "The pylon shatters!" + var/impact_sound = 'sound/effects/Glasshit.ogg' + var/shatter_sound = 'sound/effects/Glassbr3.ogg' + + var/activation_cooldown = 30 SECONDS + var/last_activation = 0 + +/obj/structure/cult/pylon/Initialize() + ..() + START_PROCESSING(SSobj, src) + /obj/structure/cult/pylon/attack_hand(mob/M as mob) attackpylon(M, 5) @@ -44,47 +55,58 @@ /obj/structure/cult/pylon/proc/pylonhit(var/damage) if(!isbroken) if(prob(1+ damage * 5)) - visible_message("The pylon shatters!") - playsound(get_turf(src), 'sound/effects/Glassbr3.ogg', 75, 1) + visible_message("[shatter_message]") + STOP_PROCESSING(SSobj, src) + playsound(get_turf(src),shatter_sound, 75, 1) isbroken = 1 density = 0 - icon_state = "pylon-broken" + icon_state = "[initial(icon_state)]-broken" set_light(0) /obj/structure/cult/pylon/proc/attackpylon(mob/user as mob, var/damage) if(!isbroken) if(prob(1+ damage * 5)) user.visible_message( - "[user] smashed the pylon!", - "You hit the pylon, and its crystal breaks apart!", - "You hear a tinkle of crystal shards" + "[user] smashed \the [src]!", + "You hit \the [src], and its crystal breaks apart!", + "You hear a tinkle of crystal shards." ) + STOP_PROCESSING(SSobj, src) user.do_attack_animation(src) - playsound(get_turf(src), 'sound/effects/Glassbr3.ogg', 75, 1) + playsound(get_turf(src),shatter_sound, 75, 1) isbroken = 1 density = 0 - icon_state = "pylon-broken" + icon_state = "[initial(icon_state)]-broken" set_light(0) else - user << "You hit the pylon!" - playsound(get_turf(src), 'sound/effects/Glasshit.ogg', 75, 1) + user << "You hit \the [src]!" + playsound(get_turf(src),impact_sound, 75, 1) else if(prob(damage * 2)) - user << "You pulverize what was left of the pylon!" + user << "You pulverize what was left of \the [src]!" qdel(src) else - user << "You hit the pylon!" - playsound(get_turf(src), 'sound/effects/Glasshit.ogg', 75, 1) - + user << "You hit \the [src]!" + playsound(get_turf(src),impact_sound, 75, 1) /obj/structure/cult/pylon/proc/repair(mob/user as mob) if(isbroken) - user << "You repair the pylon." + START_PROCESSING(SSobj, src) + user << "You repair \the [src]." isbroken = 0 density = 1 - icon_state = "pylon" + icon_state = initial(icon_state) set_light(5) +// Returns 1 if the pylon does something special. +/obj/structure/cult/pylon/proc/pylon_unique() + last_activation = world.time + return 0 + +/obj/structure/cult/pylon/process() + if(!isbroken && (last_activation < world.time + activation_cooldown) && pylon_unique()) + flick("[initial(icon_state)]-surge",src) + /obj/structure/cult/tome name = "Desk" desc = "A desk covered in arcane manuscripts and tomes in unknown languages. Looking at the text makes your skin crawl." diff --git a/code/game/gamemodes/gameticker.dm b/code/game/gamemodes/gameticker.dm index e48d01a721..6921e8fcab 100644 --- a/code/game/gamemodes/gameticker.dm +++ b/code/game/gamemodes/gameticker.dm @@ -418,7 +418,7 @@ var/global/datum/controller/gameticker/ticker for (var/mob/living/silicon/robot/robo in mob_list) - if(istype(robo,/mob/living/silicon/robot/drone)) + if(istype(robo,/mob/living/silicon/robot/drone) && !istype(robo,/mob/living/silicon/robot/drone/swarm)) dronecount++ continue diff --git a/code/game/objects/effects/decals/contraband.dm b/code/game/objects/effects/decals/contraband.dm index c98cacb785..d765c479fb 100644 --- a/code/game/objects/effects/decals/contraband.dm +++ b/code/game/objects/effects/decals/contraband.dm @@ -12,14 +12,16 @@ name = "rolled-up poster" desc = "The poster comes with its own automatic adhesive mechanism, for easy pinning to any vertical surface." icon_state = "rolled_poster" - var/serial_number = 0 + var/serial_number = null + var/poster_type = /obj/structure/sign/poster /obj/item/weapon/contraband/poster/New(turf/loc, var/given_serial = 0) - if(given_serial == 0) - serial_number = rand(1, poster_designs.len) - else - serial_number = given_serial + if(!serial_number) + if(given_serial == 0) + serial_number = rand(1, poster_designs.len) + else + serial_number = given_serial name += " - No. [serial_number]" ..(loc) @@ -57,7 +59,7 @@ user << "You start placing the poster on the wall..." //Looks like it's uncluttered enough. Place the poster. - var/obj/structure/sign/poster/P = new(user.loc, placement_dir=get_dir(user, W), serial=serial_number) + var/obj/structure/sign/poster/P = new poster_type(user.loc, placement_dir=get_dir(user, W), serial=serial_number, itemtype = src.type) flick("poster_being_set", P) //playsound(W, 'sound/items/poster_being_created.ogg', 100, 1) //why the hell does placing a poster make printer sounds? @@ -74,6 +76,18 @@ qdel(oldsrc) //delete it now to cut down on sanity checks afterwards. Agouri's code supports rerolling it anyway +//NT subtype +/obj/item/weapon/contraband/poster/nanotrasen + icon_state = "rolled_poster_nt" + poster_type = /obj/structure/sign/poster/nanotrasen + +/obj/item/weapon/contraband/poster/nanotrasen/New(turf/loc, var/given_serial = 0) + if(given_serial == 0) + serial_number = rand(1, NT_poster_designs.len) + else + serial_number = given_serial + ..(loc) + //############################## THE ACTUAL DECALS ########################### /obj/structure/sign/poster @@ -85,15 +99,22 @@ var/poster_type //So mappers can specify a desired poster var/ruined = 0 -/obj/structure/sign/poster/New(var/newloc, var/placement_dir=null, var/serial=null) + var/roll_type + var/poster_set = FALSE + +/obj/structure/sign/poster/New(var/newloc, var/placement_dir=null, var/serial=null, var/itemtype = /obj/item/weapon/contraband/poster) ..(newloc) if(!serial) serial = rand(1, poster_designs.len) //use a random serial if none is given - serial_number = serial - var/datum/poster/design = poster_designs[serial_number] - set_poster(design) + if(!poster_set) + serial_number = serial + var/datum/poster/design = poster_designs[serial_number] + set_poster(design) + + if(itemtype || !roll_type) + roll_type = itemtype switch (placement_dir) if (NORTH) @@ -121,6 +142,8 @@ desc = "[initial(desc)] [design.desc]" icon_state = design.icon_state // poster[serial_number] + poster_set = TRUE + /obj/structure/sign/poster/attackby(obj/item/weapon/W as obj, mob/user as mob) if(W.is_wirecutter()) playsound(src.loc, W.usesound, 100, 1) @@ -132,7 +155,6 @@ roll_and_drop(user.loc) return - /obj/structure/sign/poster/attack_hand(mob/user as mob) if(ruined) @@ -152,7 +174,7 @@ add_fingerprint(user) /obj/structure/sign/poster/proc/roll_and_drop(turf/newloc) - var/obj/item/weapon/contraband/poster/P = new(src, serial_number) + var/obj/item/weapon/contraband/poster/P = new roll_type(src, serial_number) P.loc = newloc src.loc = P qdel(src) @@ -163,3 +185,17 @@ // Description suffix var/desc="" var/icon_state="" + +// NT poster subtype. +/obj/structure/sign/poster/nanotrasen + roll_type = /obj/item/weapon/contraband/poster/nanotrasen + +/obj/structure/sign/poster/nanotrasen/New(var/newloc, var/placement_dir=null, var/serial=null, var/itemtype = /obj/item/weapon/contraband/poster/nanotrasen) + if(!serial) + serial = rand(1, NT_poster_designs.len) + + serial_number = serial + var/datum/poster/design = NT_poster_designs[serial_number] + set_poster(design) + + ..(newloc, placement_dir, serial, itemtype) diff --git a/code/game/objects/effects/decals/posters/polarisposters.dm b/code/game/objects/effects/decals/posters/polarisposters.dm index 958ceb66be..39e44e17e7 100644 --- a/code/game/objects/effects/decals/posters/polarisposters.dm +++ b/code/game/objects/effects/decals/posters/polarisposters.dm @@ -43,4 +43,171 @@ name = "Airlock Maintenance Reference" desc = "This poster appears to be reference material for maintenance personnel, instructing to always wear insulated gloves, that wirecutters and \ a multitool are the optimal tools to use, and where to find the maintenance panel on most airlocks. Unfortunately, the poster does not mention any \ - wire codes." \ No newline at end of file + wire codes." + +/datum/poster/pol_10 + icon_state="polposter9" + name = "orchid" + desc = "This poster appears strangely familiar, depicting the flower of a tree native to the planet Earth." + +// A new subset of poster datum for Security posters. +/datum/poster/nanotrasen + icon_state = "polposter1" + name = "Safety!" + desc = "A poster advising you to learn how to put on your internals at a moment's notice." + +/datum/poster/nanotrasen/pol_2 + icon_state="polposter2" + name = "Safety!" + desc = "A blue and white colored poster. This one advises you to wear your safety goggles when handling chemicals." + +/datum/poster/nanotrasen/pol_3 + icon_state="polposter3" + name = "Safety!" + desc = "A safety poster instructing you to comply with the authorities, especially in an emergency." + +/datum/poster/nanotrasen/pol_4 + icon_state="polposter4" + name = "Clean Hands Save Lives" + desc = "A safety poster reminding you to wash your hands." + +/datum/poster/nanotrasen/pol_5 + icon_state="polposter5" + name = "Help!" + desc = "This poster depicts a man helping another man get up." + +/datum/poster/nanotrasen/pol_6 + icon_state="polposter6" + name = "Walk!" + desc = "This poster depicts a man walking, presumably to encourage you not to run in the halls." + +/datum/poster/nanotrasen/pol_7 + icon_state="polposter7" + name = "Place your signs!" + desc = "A safety poster reminding custodial staff to place wet floor signs where needed. This reminder's rarely heeded." + +/datum/poster/nanotrasen/pol_8 + icon_state="polposter8" + name = "Safety!" + desc = "An advertisement / safety poster for EVA training and certification. Training is available at your local Central Command." + +/datum/poster/nanotrasen/pol_9 + icon_state="poster10" + name = "Airlock Maintenance Reference" + desc = "This poster appears to be reference material for maintenance personnel, instructing to always wear insulated gloves, that wirecutters and \ + a multitool are the optimal tools to use, and where to find the maintenance panel on most airlocks. Unfortunately, the poster does not mention any \ + wire codes." + +/datum/poster/nanotrasen/pol_10 + icon_state="polposter9" + name = "orchid" + desc = "This poster suggests a feeling of peace. It depicts the flower of a tree native to the planet Earth." + +/datum/poster/nanotrasen/bay_9 + icon_state="bsposter9" + name = "Pinup Girl Amy" + desc = "This particular one is of Amy, the nymphomaniac urban legend of deep space. How this photograph came to be is not known." + +/datum/poster/nanotrasen/bay_21 + icon_state="bsposter21" + name = "Join the Fuzz!" + desc = "It's a nice recruitment poster of a white haired Chinese woman that says; \"Big Guns, Hot Women, Good Times. Security. We get it done.\"" + +/datum/poster/nanotrasen/bay_22 + icon_state="bsposter22" + name = "Looking for a career with excitement?" + desc = "A recruitment poster starring a dark haired woman with glasses and a purple shirt that has \"Got Brains? Got Talent? Not afraid of electric flying monsters that want to suck the soul out of you? Then Xenobiology could use someone like you!\" written on the bottom." + +/datum/poster/nanotrasen/bay_23 + icon_state="bsposter23" + name = "Safety first: because electricity doesn't wait!" + desc = "A safety poster starring a clueless looking redhead with frazzled hair. \"Every year, hundreds of NT employees expose themselves to electric shock. Play it safe. Avoid suspicious doors after electrical storms, and always wear protection when doing electric maintenance.\"" + +/datum/poster/nanotrasen/bay_24 + icon_state="bsposter24" + name = "Responsible medbay habits, No #259" + desc = "A poster with a nervous looking geneticist on it states; \"Friends Tell Friends They're Clones. It can cause severe and irreparable emotional trauma if a person is not properly informed of their recent demise. Always follow your contractual obligation and inform them of their recent rejuvenation.\"" + +/datum/poster/nanotrasen/bay_25 + icon_state="bsposter25" + name = "Irresponsible medbay habits, No #2" + desc = "This is a safety poster starring a perverted looking naked doctor. \"Sexual harassment is never okay. REPORT any acts of sexual deviance or harassment that disrupt a healthy working environment.\"" + +/datum/poster/nanotrasen/bay_49 + icon_state="bsposter49" + name = "Engineering recruitment" + desc = "This is a poster showing an engineer relaxing by a computer, the text states \"Living the life! Join Engineering today!\"" + +/datum/poster/nanotrasen/bay_52 + icon_state="bsposter52" + name = "fire safety poster" + desc = "This is a poster reminding you of what you should do if you are on fire, or if you are at a dance party." + +/datum/poster/nanotrasen/bay_53 + icon_state="bsposter53" + name = "fire extinguisher poster" + desc = "This is a poster reminding you of what you should use to put out a fire." + +/datum/poster/nanotrasen/bay_54 + icon_state="bsposter54" + name = "firefighter poster" + desc = "This is a poster of a particularly stern looking firefighter. The caption reads, \"Only you can prevent space fires.\"" + +/datum/poster/nanotrasen/bay_57 + icon_state="bsposter57" + name = "space carp warning poster" + desc = "This poster tells of the dangers of space carp infestations." + +/datum/poster/nanotrasen/bay_58 + icon_state="bsposter58" + name = "space carp information poster" + desc = "This poster showcases an old spacer saying on the dangers of migrant space carp." + +/datum/poster/nanotrasen/nt_1 + icon_state = "ntposter01" + name = "Security recruitment" + desc = "This poster showcases an NT security guard in an excited pose, with a small blurb about the importance of Security." + +/datum/poster/nanotrasen/nt_2 + icon_state = "ntposter02" + name = "Security recruitment" + desc = "This poster showcases an NT security guard in an excited pose, with a small blurb about Security Employee benefits." + +/datum/poster/nanotrasen/nt_3 + icon_state = "ntposter03" + name = "Mechatronic Safety" + desc = "This poster displays three cutting-edge gygaxes standing in line in front of a man in plain clothes.\ + The poster's captions explain the importance of knowing how to operate a mechatronic vehicle safely, especially near other personnel.\ + The image seems important." + +/datum/poster/nanotrasen/nt_4 + icon_state = "ntposter04" + name = "Beware Aetotheans" + desc = "This poster displays a distinctly hostile-looking red Promethean in a black coat. The fine-print around the edges warns the reader about the dangers posed by Almachi Prometheans." + +/datum/poster/nanotrasen/nt_5 + icon_state = "ntposter05" + name = "Promethean" + desc = "This poster displays a friendly-looking green Promethean in a labcoat. The fine-print around the edges talks about the benefits Prometheans give in laboratories." + +/datum/poster/nanotrasen/nt_6 + icon_state = "ntposter06" + name = "NanoTrasen" + desc = "This poster showcases an NT emblem. There is writing in the ring around the inner points, probably some sort of slogan no one bothers to memorize." + +/datum/poster/nanotrasen/nt_7 + icon_state = "ntposter07" + name = "SolGov" + desc = "This poster showcases an SCG emblem. The outer ring reads,\ + \"NIL MORTALIBUS ARDUI EST\".\ + Solar Confederate Government." + +/datum/poster/nanotrasen/nt_8 + icon_state = "ntposter08" + name = "wildlife hazard" + desc = "This poster warns against attempting to kill a fully grown giant spider or other hostile life-form alone." + +/datum/poster/nanotrasen/nt_9 + icon_state = "ntposter09" + name = "Regulations and You" + desc = "This poster showcases an NT security guard reading from her PDA. The blurb advocates for the reader to keep corporate regulations in mind at all times, as an emergency can occur at any time." diff --git a/code/game/objects/effects/spiders.dm b/code/game/objects/effects/spiders.dm index 4adcb5674c..5a62262831 100644 --- a/code/game/objects/effects/spiders.dm +++ b/code/game/objects/effects/spiders.dm @@ -134,6 +134,8 @@ var/travelling_in_vent = 0 var/list/grow_as = list(/mob/living/simple_mob/animal/giant_spider, /mob/living/simple_mob/animal/giant_spider/nurse, /mob/living/simple_mob/animal/giant_spider/hunter) + var/stunted = FALSE + /obj/effect/spider/spiderling/frost grow_as = list(/mob/living/simple_mob/animal/giant_spider/frost) @@ -253,9 +255,17 @@ break if(amount_grown >= 100) var/spawn_type = pick(grow_as) - new spawn_type(src.loc, src) + var/mob/living/simple_mob/animal/giant_spider/GS = new spawn_type(src.loc, src) + if(stunted) + spawn(2) + GS.make_spiderling() qdel(src) +/obj/effect/spider/spiderling/stunted + stunted = TRUE + + grow_as = list(/mob/living/simple_mob/animal/giant_spider, /mob/living/simple_mob/animal/giant_spider/hunter) + /obj/effect/decal/cleanable/spiderling_remains name = "spiderling remains" desc = "Green squishy mess." diff --git a/code/game/objects/items/weapons/RCD.dm b/code/game/objects/items/weapons/RCD.dm index f7f22d7611..034063c453 100644 --- a/code/game/objects/items/weapons/RCD.dm +++ b/code/game/objects/items/weapons/RCD.dm @@ -243,6 +243,13 @@ electric_cost_coefficent = 41.66 // Twice as efficent, out of pity. toolspeed = 0.5 // Twice as fast, since borg versions typically have this. +/obj/item/weapon/rcd/electric/mounted/borg/swarm + can_remove_rwalls = FALSE + name = "Rapid Assimilation Device" + ranged = TRUE + toolspeed = 0.7 + material_to_use = MAT_STEELHULL + /obj/item/weapon/rcd/electric/mounted/borg/lesser can_remove_rwalls = FALSE diff --git a/code/game/objects/items/weapons/handcuffs.dm b/code/game/objects/items/weapons/handcuffs.dm index 1c9af03458..f8b56d3a45 100644 --- a/code/game/objects/items/weapons/handcuffs.dm +++ b/code/game/objects/items/weapons/handcuffs.dm @@ -197,6 +197,9 @@ var/last_chew = 0 breakouttime = 200 cuff_type = "duct tape" +/obj/item/weapon/handcuffs/cable/tape/cyborg + dispenser = TRUE + //Legcuffs. Not /really/ handcuffs, but its close enough. /obj/item/weapon/handcuffs/legcuffs name = "legcuffs" diff --git a/code/game/objects/items/weapons/melee/energy.dm b/code/game/objects/items/weapons/melee/energy.dm index 2c4fd9949b..ad8fc3bdac 100644 --- a/code/game/objects/items/weapons/melee/energy.dm +++ b/code/game/objects/items/weapons/melee/energy.dm @@ -368,6 +368,13 @@ target.taunt(user) target.adjustFireLoss(force * 6) // 30 Burn, for 50 total. +/obj/item/weapon/melee/energy/sword/ionic_rapier/lance + name = "zero-point lance" + desc = "Designed specifically for disrupting electronics at relatively close range, however it is still capable of dealing some damage to living beings." + active_force = 20 + armor_penetration = 15 + reach = 2 + /* * Charge blade. Uses a cell, and costs energy per strike. */ diff --git a/code/game/objects/items/weapons/storage/bags.dm b/code/game/objects/items/weapons/storage/bags.dm index eea992ec41..29e86dbbd4 100644 --- a/code/game/objects/items/weapons/storage/bags.dm +++ b/code/game/objects/items/weapons/storage/bags.dm @@ -373,3 +373,17 @@ max_w_class = ITEMSIZE_NORMAL w_class = ITEMSIZE_SMALL can_hold = list(/obj/item/weapon/reagent_containers/food/snacks,/obj/item/weapon/reagent_containers/food/condiment) + + // ----------------------------- + // Evidence Bag + // ----------------------------- +/obj/item/weapon/storage/bag/detective + name = "secure satchel" + icon = 'icons/obj/storage.dmi' + icon_state = "detbag" + desc = "A bag for storing investigation things. You know, securely." + max_storage_space = ITEMSIZE_COST_NORMAL * 15 + max_w_class = ITEMSIZE_NORMAL + w_class = ITEMSIZE_SMALL + can_hold = list(/obj/item/weapon/forensics/swab,/obj/item/weapon/sample/print,/obj/item/weapon/sample/fibers,/obj/item/weapon/evidencebag) + diff --git a/code/game/objects/structures/crates_lockers/closets/secure/security.dm b/code/game/objects/structures/crates_lockers/closets/secure/security.dm index 52ec9be0cf..646e869e30 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/security.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/security.dm @@ -109,7 +109,7 @@ /obj/item/weapon/storage/belt/security, /obj/item/device/flash, /obj/item/weapon/melee/baton/loaded, - /obj/item/weapon/gun/energy/gun, + /obj/item/weapon/gun/magnetic/railgun/heater/pistol/hos, /obj/item/weapon/cell/device/weapon, /obj/item/clothing/accessory/holster/waist, /obj/item/weapon/melee/telebaton, @@ -219,6 +219,8 @@ starts_with += /obj/item/weapon/storage/backpack/satchel/sec if(prob(50)) starts_with += /obj/item/weapon/storage/backpack/dufflebag/sec + if(prob(30)) + starts_with += /obj/item/weapon/contraband/poster/nanotrasen return ..() /obj/structure/closet/secure_closet/security/cargo/Initialize() @@ -267,6 +269,7 @@ /obj/item/weapon/reagent_containers/food/drinks/flask/detflask, /obj/item/weapon/storage/briefcase/crimekit, /obj/item/device/taperecorder, + /obj/item/weapon/storage/bag/detective, /obj/item/device/tape/random = 3) /obj/structure/closet/secure_closet/detective/update_icon() @@ -301,6 +304,17 @@ GLOBAL_LIST_BOILERPLATE(all_brig_closets, /obj/structure/closet/secure_closet/br /obj/item/clothing/under/color/prison, /obj/item/clothing/shoes/orange) +/obj/structure/closet/secure_closet/posters + name = "morale storage" + req_access = list(access_security) + anchored = 1 + + starts_with = list( + /obj/item/weapon/contraband/poster/nanotrasen, + /obj/item/weapon/contraband/poster/nanotrasen, + /obj/item/weapon/contraband/poster/nanotrasen, + /obj/item/weapon/contraband/poster/nanotrasen, + /obj/item/weapon/contraband/poster/nanotrasen) /obj/structure/closet/secure_closet/courtroom name = "courtroom locker" diff --git a/code/game/objects/structures/ghost_pods/ghost_pods.dm b/code/game/objects/structures/ghost_pods/ghost_pods.dm index 811fc47736..970b1d2fff 100644 --- a/code/game/objects/structures/ghost_pods/ghost_pods.dm +++ b/code/game/objects/structures/ghost_pods/ghost_pods.dm @@ -83,4 +83,13 @@ to_chat(user, "Another spirit appears to have gotten to \the [src] before you. Sorry.") return - create_occupant(user) \ No newline at end of file + var/choice = input(user, "Are you certain you wish to activate this pod?", "Control Pod") as null|anything in list("Yes", "No") + + if(!choice || choice == "No") + return + + else if(used) + to_chat(user, "Another spirit appears to have gotten to \the [src] before you. Sorry.") + return + + create_occupant(user) diff --git a/code/game/objects/structures/ghost_pods/human.dm b/code/game/objects/structures/ghost_pods/human.dm new file mode 100644 index 0000000000..2eda9b6c37 --- /dev/null +++ b/code/game/objects/structures/ghost_pods/human.dm @@ -0,0 +1,246 @@ +// Ghost variant. + +/obj/structure/ghost_pod/ghost_activated/human + name = "mysterious cryopod" + desc = "This is a pod which appears to contain a body." + description_info = "This contains a body, which may wake at any time. The external controls\ + seem to be malfunctioning." + icon = 'icons/obj/Cryogenic2.dmi' + icon_state = "sleeper_1" + icon_state_opened = "sleeper_0" + density = TRUE + ghost_query_type = /datum/ghost_query/stowaway + anchored = FALSE + + var/occupant_type = "stowaway" + + var/allow_appearance_change = TRUE + + var/make_antag = MODE_STOWAWAY + + var/start_injured = FALSE + var/spawn_with_emag = TRUE + + var/list/clothing_possibilities + +/obj/structure/ghost_pod/ghost_activated/human/Initialize() + ..() + + handle_clothing_setup() + +/obj/structure/ghost_pod/ghost_activated/human/proc/handle_clothing_setup() + clothing_possibilities = list() + + clothing_possibilities |= subtypesof(/obj/item/clothing/under/color) + clothing_possibilities |= subtypesof(/obj/item/clothing/head/soft) + clothing_possibilities |= /obj/item/clothing/shoes/black + clothing_possibilities |= /obj/item/device/radio/headset + +/obj/structure/ghost_pod/ghost_activated/human/create_occupant(var/mob/M) + ..() + var/turf/T = get_turf(src) + var/mob/living/carbon/human/H = new(src) + + H.adjustCloneLoss(rand(1,5)) + if(M.mind) + M.mind.transfer_to(H) + to_chat(M, "You are a [occupant_type]!") + if(make_antag) + to_chat(M, "Your intent may not be completely beneficial.") + H.ckey = M.ckey + visible_message("As \the [src] opens, the pipes on \the [src] surge, before it grows dark.") + log_and_message_admins("successfully opened \a [src] and became a [occupant_type].") + + var/list/uniform_options + var/list/shoe_options + var/list/head_options + var/list/headset_options + + if(clothing_possibilities && clothing_possibilities.len) + for(var/path in clothing_possibilities) + if(ispath(path, /obj/item/clothing/under)) + if(!uniform_options) + uniform_options = list() + uniform_options |= path + if(ispath(path, /obj/item/clothing/shoes)) + if(!shoe_options) + shoe_options = list() + shoe_options |= path + if(ispath(path, /obj/item/clothing/head)) + if(!head_options) + head_options = list() + head_options |= path + if(ispath(path, /obj/item/device/radio/headset)) + if(!headset_options) + headset_options = list() + headset_options |= path + + if(uniform_options && uniform_options.len) + var/newpath = pick(uniform_options) + var/obj/item/clothing/C = new newpath(H) + H.equip_to_appropriate_slot(C) + + if(shoe_options && shoe_options.len) + var/newpath = pick(shoe_options) + var/obj/item/clothing/C = new newpath(H) + H.equip_to_appropriate_slot(C) + + if(head_options && head_options.len) + var/newpath = pick(head_options) + var/obj/item/clothing/C = new newpath(H) + H.equip_to_appropriate_slot(C) + + if(headset_options && headset_options.len) + var/newpath = pick(headset_options) + var/obj/item/C = new newpath(H) + H.equip_to_appropriate_slot(C) + + if(spawn_with_emag) + var/obj/item/weapon/card/emag/E = new(H) + E.name = "broken card" + E.description_antag = "This is a 'disguised' emag, to make your escape from wherever you happen to be trapped." + H.equip_to_appropriate_slot(E) + + var/newname = sanitize(input(H, "Your mind feels foggy, and you recall your name might be [H.real_name]. Would you like to change your name?", "Name change") as null|text, MAX_NAME_LEN) + if (newname) + H.real_name = newname + + icon_state = icon_state_opened + + H.forceMove(T) + + if(make_antag) + var/datum/antagonist/antag = all_antag_types[make_antag] + if(antag) + if(antag.add_antagonist(H.mind, 1, 1, 0, 1, 1)) + log_admin("\The [src] made [key_name(src)] into a [antag.role_text].") + + if(start_injured) // Done 3 different times to disperse damage. + H.adjustBruteLoss(rand(1,20)) + H.adjustBruteLoss(rand(1,20)) + H.adjustBruteLoss(rand(1,20)) + + if(allow_appearance_change) + H.change_appearance(APPEARANCE_ALL, H.loc, check_species_whitelist = 1) + + visible_message("\The [src] [pick("gurgles", "seizes", "clangs")] before releasing \the [H]!") + +// Manual Variant +// This one lacks the emag option due to the fact someone has to activate it, and they will probably help the person. +/obj/structure/ghost_pod/manual/human + name = "mysterious cryopod" + desc = "This is a pod which appears to contain a body." + description_info = "This contains a body, which may wake at any time. The external controls\ + seem to be functioning, though the warning lights that flash give no solace.." + icon = 'icons/obj/Cryogenic2.dmi' + icon_state = "sleeper_1" + icon_state_opened = "sleeper_0" + density = TRUE + ghost_query_type = /datum/ghost_query/lost_passenger + anchored = FALSE + + var/occupant_type = "lost passenger" + + var/allow_appearance_change = TRUE + + var/make_antag = MODE_STOWAWAY + + var/start_injured = TRUE + + var/list/clothing_possibilities + +/obj/structure/ghost_pod/manual/human/Initialize() + ..() + + handle_clothing_setup() + +/obj/structure/ghost_pod/manual/human/proc/handle_clothing_setup() + clothing_possibilities = list() + + clothing_possibilities |= subtypesof(/obj/item/clothing/under/utility) + clothing_possibilities |= subtypesof(/obj/item/clothing/head/beret) + clothing_possibilities |= /obj/item/clothing/shoes/black + clothing_possibilities |= /obj/item/device/radio/headset + +/obj/structure/ghost_pod/manual/human/create_occupant(var/mob/M) + ..() + var/turf/T = get_turf(src) + var/mob/living/carbon/human/H = new(src) + + H.adjustCloneLoss(rand(1,5)) + if(M.mind) + M.mind.transfer_to(H) + to_chat(M, "You are a [occupant_type]!") + if(make_antag) + to_chat(M, "Your intent may not be completely beneficial.") + H.ckey = M.ckey + visible_message("As \the [src] opens, the pipes on \the [src] surge, before it grows dark.") + log_and_message_admins("successfully opened \a [src] and got a [occupant_type].") + + var/list/uniform_options + var/list/shoe_options + var/list/head_options + var/list/headset_options + + if(clothing_possibilities && clothing_possibilities.len) + for(var/path in clothing_possibilities) + if(ispath(path, /obj/item/clothing/under)) + if(!uniform_options) + uniform_options = list() + uniform_options |= path + if(ispath(path, /obj/item/clothing/shoes)) + if(!shoe_options) + shoe_options = list() + shoe_options |= path + if(ispath(path, /obj/item/clothing/head)) + if(!head_options) + head_options = list() + head_options |= path + if(ispath(path, /obj/item/device/radio/headset)) + if(!headset_options) + headset_options = list() + headset_options |= path + + if(uniform_options && uniform_options.len) + var/newpath = pick(uniform_options) + var/obj/item/clothing/C = new newpath(H) + H.equip_to_appropriate_slot(C) + + if(shoe_options && shoe_options.len) + var/newpath = pick(shoe_options) + var/obj/item/clothing/C = new newpath(H) + H.equip_to_appropriate_slot(C) + + if(head_options && head_options.len) + var/newpath = pick(head_options) + var/obj/item/clothing/C = new newpath(H) + H.equip_to_appropriate_slot(C) + + if(headset_options && headset_options.len) + var/newpath = pick(headset_options) + var/obj/item/C = new newpath(H) + H.equip_to_appropriate_slot(C) + + var/newname = sanitize(input(H, "Your mind feels foggy, and you recall your name might be [H.real_name]. Would you like to change your name?", "Name change") as null|text, MAX_NAME_LEN) + if (newname) + H.real_name = newname + + icon_state = icon_state_opened + + H.forceMove(T) + + if(make_antag) + var/datum/antagonist/antag = all_antag_types[make_antag] + if(antag) + if(antag.add_antagonist(H.mind, 1, 1, 0, 1, 1)) + log_admin("\The [src] made [key_name(src)] into a [antag.role_text].") + + if(start_injured) // Done 3 different times to disperse damage. + H.adjustBruteLoss(rand(1,20)) + H.adjustBruteLoss(rand(1,20)) + H.adjustBruteLoss(rand(1,20)) + + if(allow_appearance_change) + H.change_appearance(APPEARANCE_ALL, H.loc, check_species_whitelist = 1) + + visible_message("\The [src] [pick("gurgles", "seizes", "clangs")] before releasing \the [H]!") diff --git a/code/game/objects/structures/ghost_pods/silicon.dm b/code/game/objects/structures/ghost_pods/silicon.dm index 8fd3fcea7c..32a26ce479 100644 --- a/code/game/objects/structures/ghost_pods/silicon.dm +++ b/code/game/objects/structures/ghost_pods/silicon.dm @@ -58,4 +58,50 @@ R.ckey = M.ckey visible_message("As \the [src] opens, the eyes of the robot flicker as it is activated.") R.Namepick() - ..() \ No newline at end of file + ..() + +/obj/structure/ghost_pod/ghost_activated/swarm_drone + name = "drone shell" + desc = "A heavy metallic ball." + icon = 'icons/mob/swarmbot.dmi' + icon_state = "swarmer_unactivated" + density = FALSE + anchored = FALSE + + var/drone_class = "general" + var/drone_type = /mob/living/silicon/robot/drone/swarm + +/obj/structure/ghost_pod/ghost_activated/swarm_drone/create_occupant(var/mob/M) + var/mob/living/silicon/robot/drone/swarm/R = new drone_type(get_turf(src)) + if(M.mind) + M.mind.transfer_to(R) + to_chat(M, "You are [R], the remnant of some distant species, mechanical or flesh, living or dead.") + R.ckey = M.ckey + visible_message("As \the [src] shudders, it glows before lifting itself with three shimmering limbs!") + spawn(3 SECONDS) + to_chat(R,"Many of your tools are standard drone devices, however others provide you with particular benefits.") + to_chat(R,"Unlike standard drones, you are capable of utilizing 'zero point wells', found in your 'spells' tab.") + to_chat(R,"Here you will also find your replication ability(s), depending on the type of drone you are.") + to_chat(R,"Gunners have a special anti-personnel gun capable of shocking or punching through armor with low damage.") + to_chat(R,"Impalers have an energy-lance.") + to_chat(R,"General drones have the unique ability to produce one of each of these two types of shells per generation.") + if(!QDELETED(src)) + qdel(src) + +/obj/structure/ghost_pod/ghost_activated/swarm_drone/event/Initialize() + ..() + + var/turf/T = get_turf(src) + say_dead_object("A [drone_class] swarm drone shell is now available in \the [T.loc].", src) + +/obj/structure/ghost_pod/ghost_activated/swarm_drone/event/gunner + name = "gunner shell" + + drone_class = "gunner" + drone_type = /mob/living/silicon/robot/drone/swarm/gunner + +/obj/structure/ghost_pod/ghost_activated/swarm_drone/event/melee + name = "impaler shell" + + drone_class = "impaler" + drone_type = /mob/living/silicon/robot/drone/swarm/melee diff --git a/code/game/objects/structures/props/swarm.dm b/code/game/objects/structures/props/swarm.dm new file mode 100644 index 0000000000..8419f7086a --- /dev/null +++ b/code/game/objects/structures/props/swarm.dm @@ -0,0 +1,150 @@ +/obj/structure/cult/pylon/swarm + name = "Swarm Construct" + desc = "A small pod." + icon = 'icons/mob/swarmbot.dmi' + icon_state = "pod" + light_color = "#00B2B2" + + shatter_message = "The energetic field shatters!" + impact_sound = 'sound/effects/Glasshit.ogg' + shatter_sound = 'sound/effects/phasein.ogg' + + var/list/active_beams + +/obj/structure/cult/pylon/swarm/CanPass(atom/movable/mover, turf/target) + if(istype(mover, /mob/living)) + var/mob/living/L = mover + if(L.faction == "swarmer") + return TRUE + else if(istype(mover, /obj/item/projectile)) + var/obj/item/projectile/P = mover + if(istype(P.firer) && P.firer.faction == "swarmer") + return TRUE + return ..() + +/obj/structure/cult/pylon/swarm/Initialize() + ..() + active_beams = list() + +/obj/structure/cult/pylon/swarm/Destroy() + for(var/datum/beam/B in active_beams) + QDEL_NULL(B) + active_beams = null + ..() + +/obj/structure/cult/pylon/swarm/pylonhit(var/damage) + if(!isbroken) + if(prob(1 + damage * 3)) + visible_message("[shatter_message]") + STOP_PROCESSING(SSobj, src) + playsound(get_turf(src),shatter_sound, 75, 1) + isbroken = 1 + density = 0 + icon_state = "[initial(icon_state)]-broken" + set_light(0) + +/obj/structure/cult/pylon/swarm/attackpylon(mob/user as mob, var/damage) + if(!isbroken) + if(prob(1 + damage * 3)) + user.visible_message( + "[user] smashed \the [src]!", + "You hit \the [src], and its crystal breaks apart!", + "You hear a tinkle of crystalline shards." + ) + STOP_PROCESSING(SSobj, src) + user.do_attack_animation(src) + playsound(get_turf(src),shatter_sound, 75, 1) + isbroken = 1 + density = 0 + icon_state = "[initial(icon_state)]-broken" + set_light(0) + else + to_chat(user, "You hit \the [src]!") + playsound(get_turf(src),impact_sound, 75, 1) + else + if(prob(damage * 2)) + to_chat(user, "You pulverize what was left of \the [src]!") + qdel(src) + else + to_chat(user, "You hit \the [src]!") + playsound(get_turf(src),impact_sound, 75, 1) + +/obj/structure/cult/pylon/swarm/pylon_unique() + . = ..() + + return . + +/obj/structure/cult/pylon/swarm/zp_well + name = "Zero Point Well" + desc = "Infinite cosmic power, itty bitty usability." + + icon_state = "trap" + + description_info = "An infinitely small point in space that may or may not be used to supply power to some form of advanced machine." + + activation_cooldown = 0 // These things run constantly. + +/obj/structure/cult/pylon/swarm/zp_well/pylon_unique() + . = ..() + + for(var/mob/living/silicon/robot/drone/swarm/S in view(3, src)) + var/has_beam = FALSE + for(var/datum/beam/B in active_beams) + if(B.target == S) + has_beam = TRUE + break + + if(!has_beam) + active_beams |= Beam(S,icon='icons/effects/beam.dmi',icon_state="holo_beam",time=3 SECONDS,maxdistance=3,beam_type = /obj/effect/ebeam,beam_sleep_time=2) + + if(S.cell) + S.cell.give(rand(5, 30)) + + . = TRUE + + return . + +/obj/structure/cult/pylon/swarm/defender + name = "Zero Point Wall" + desc = "Infinite cosmic power, itty bitty passability." + + icon_state = "barricade" + + description_info = "An infinitely small point in space spread upon infinitely many finitely-bounded points in space. Nice." + +/obj/structure/cult/pylon/swarm/defender/pylonhit(var/damage) + if(!isbroken) + if(prob(1 + damage * 3) && round(damage * 0.8) >= 30) + visible_message("[shatter_message]") + STOP_PROCESSING(SSobj, src) + playsound(get_turf(src),shatter_sound, 75, 1) + isbroken = 1 + density = 0 + icon_state = "[initial(icon_state)]-broken" + set_light(0) + +/obj/structure/cult/pylon/swarm/defender/attackpylon(mob/user as mob, var/damage) + if(!isbroken) + if(prob(1 + damage * 3) && round(damage * 0.8) >= 25) + user.visible_message( + "[user] smashed \the [src]!", + "You hit \the [src], and its crystal breaks apart!", + "You hear a tinkle of crystalline shards." + ) + STOP_PROCESSING(SSobj, src) + user.do_attack_animation(src) + playsound(get_turf(src),shatter_sound, 75, 1) + isbroken = 1 + density = 0 + icon_state = "[initial(icon_state)]-broken" + set_light(0) + else + to_chat(user, "You hit \the [src]!") + playsound(get_turf(src),impact_sound, 75, 1) + else + if(prob(damage * 2)) + to_chat(user, "You pulverize what was left of \the [src]!") + qdel(src) + else + to_chat(user, "You hit \the [src]!") + playsound(get_turf(src),impact_sound, 75, 1) diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm index d9248a0d6b..dc08e2ba89 100644 --- a/code/modules/clothing/clothing.dm +++ b/code/modules/clothing/clothing.dm @@ -80,6 +80,16 @@ return 0 return 1 +/obj/item/clothing/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") + . = ..() + if((. == 0) && LAZYLEN(accessories)) + for(var/obj/item/I in accessories) + var/check = I.handle_shield(user, damage, damage_source, attacker, def_zone, attack_text) + + if(check != 0) // Projectiles sometimes use negatives IIRC, 0 is only returned if something is not blocked. + . = check + break + /obj/item/clothing/proc/refit_for_species(var/target_species) if(!species_restricted) return //this item doesn't use the species_restricted system diff --git a/code/modules/clothing/gloves/arm_guards.dm b/code/modules/clothing/gloves/arm_guards.dm index 21e7a99b35..84ebecdc1e 100644 --- a/code/modules/clothing/gloves/arm_guards.dm +++ b/code/modules/clothing/gloves/arm_guards.dm @@ -12,6 +12,10 @@ if(H.wear_suit.body_parts_covered & ARMS) to_chat(H, "You can't wear \the [src] with \the [H.wear_suit], it's in the way.") return 0 + for(var/obj/item/clothing/accessory/A in H.wear_suit) + if(A.body_parts_covered & ARMS) + to_chat(H, "You can't wear \the [src] with \the [H.wear_suit]'s [A], it's in the way.") + return 0 return 1 /obj/item/clothing/gloves/arm_guard/laserproof diff --git a/code/modules/clothing/shoes/leg_guards.dm b/code/modules/clothing/shoes/leg_guards.dm index fba1c2cd3f..14e24dad92 100644 --- a/code/modules/clothing/shoes/leg_guards.dm +++ b/code/modules/clothing/shoes/leg_guards.dm @@ -14,6 +14,10 @@ if(H.wear_suit.body_parts_covered & LEGS) H << "You can't wear \the [src] with \the [H.wear_suit], it's in the way." return 0 + for(var/obj/item/clothing/accessory/A in H.wear_suit) + if(A.body_parts_covered & LEGS) + to_chat(H, "You can't wear \the [src] with \the [H.wear_suit]'s [A], it's in the way.") + return 0 return 1 /obj/item/clothing/shoes/leg_guard/laserproof diff --git a/code/modules/clothing/suits/armor.dm b/code/modules/clothing/suits/armor.dm index 9df94753e6..107bea3be6 100644 --- a/code/modules/clothing/suits/armor.dm +++ b/code/modules/clothing/suits/armor.dm @@ -520,6 +520,22 @@ |ACCESSORY_SLOT_ARMOR_M) blood_overlay_type = "armor" +/obj/item/clothing/suit/armor/pcarrier/mob_can_equip(var/mob/living/carbon/human/H, slot) + if(..()) //This will only run if no other problems occured when equiping. + if(H.gloves) + if(H.gloves.body_parts_covered & ARMS) + for(var/obj/item/clothing/accessory/A in src) + if(A.body_parts_covered & ARMS) + to_chat(H, "You can't wear \the [A] with \the [H.gloves], they're in the way.") + return 0 + if(H.shoes) + if(H.shoes.body_parts_covered & LEGS) + for(var/obj/item/clothing/accessory/A in src) + if(A.body_parts_covered & LEGS) + to_chat(H, "You can't wear \the [A] with \the [H.shoes], they're in the way.") + return 0 + return 1 + /obj/item/clothing/suit/armor/pcarrier/light starting_accessories = list(/obj/item/clothing/accessory/armor/armorplate) @@ -549,6 +565,11 @@ desc = "A lightweight blue plate carrier vest. It can be equipped with armor plates, but provides no protection of its own." icon_state = "pcarrier_blue" +/obj/item/clothing/suit/armor/pcarrier/press + name = "light blue plate carrier" + desc = "A lightweight light blue plate carrier vest. It can be equipped with armor plates, but provides no protection of its own." + icon_state = "pcarrier_press" + /obj/item/clothing/suit/armor/pcarrier/blue/sol name = "peacekeeper plate carrier" desc = "A lightweight plate carrier vest in SCG Peacekeeper colors. It can be equipped with armor plates, but provides no protection of its own." diff --git a/code/modules/clothing/under/accessories/armor.dm b/code/modules/clothing/under/accessories/armor.dm index c6abaa6015..f05b96183d 100644 --- a/code/modules/clothing/under/accessories/armor.dm +++ b/code/modules/clothing/under/accessories/armor.dm @@ -10,6 +10,24 @@ icon_state = "pouches" w_class = ITEMSIZE_NORMAL +/obj/item/clothing/accessory/armor/on_attached(var/obj/item/clothing/S, var/mob/user) + if(ishuman(user)) + var/mob/living/carbon/human/H = user + if(H.wear_suit == S) + if((body_parts_covered & ARMS) && istype(H.gloves, /obj/item/clothing)) + var/obj/item/clothing/G = H.gloves + if(G.body_parts_covered & ARMS) + to_chat(H, "You can't wear \the [src] with \the [G], it's in the way.") + S.accessories -= src + return + else if((body_parts_covered & LEGS) && istype(H.shoes, /obj/item/clothing)) + var/obj/item/clothing/Sh = H.shoes + if(Sh.body_parts_covered & LEGS) + to_chat(H, "You can't wear \the [src] with \the [Sh], it's in the way.") + S.accessories -= src + return + ..() + /////////// //Pouches /////////// @@ -74,6 +92,13 @@ armor = list(melee = 30, bullet = 15, laser = 40, energy = 10, bomb = 25, bio = 0, rad = 0) slot = ACCESSORY_SLOT_ARMOR_C +/obj/item/clothing/accessory/armor/armorplate/stab + name = "mesh armor plate" + desc = "A mesh armor plate made of steel-reinforced synthetic fibers, great for dealing with small blades. Attaches to a plate carrier." + icon_state = "armor_stab" + armor = list(melee = 25, bullet = 5, laser = 20, energy = 10, bomb = 15, bio = 0, rad = 0) + armorsoak = list(melee = 7, bullet = 5, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0) + /obj/item/clothing/accessory/armor/armorplate/medium name = "medium armor plate" desc = "A plasteel-reinforced synthetic armor plate, providing good protection. Attaches to a plate carrier." @@ -92,6 +117,56 @@ icon_state = "armor_merc" armor = list(melee = 60, bullet = 60, laser = 60, energy = 40, bomb = 40, bio = 0, rad = 0) +/obj/item/clothing/accessory/armor/armorplate/bulletproof + name = "ballistic armor plate" + desc = "A woven armor plate with additional plating, providing good protection against high-velocity trauma. Attaches to a plate carrier." + icon_state = "armor_ballistic" + slowdown = 0.6 + armor = list(melee = 10, bullet = 70, laser = 10, energy = 10, bomb = 0, bio = 0, rad = 0) + armorsoak = list(melee = 0, bullet = 10, laser = 0, energy = 5, bomb = 0, bio = 0, rad = 0) + siemens_coefficient = 0.7 + +/obj/item/clothing/accessory/armor/armorplate/riot + name = "riot armor plate" + desc = "A thick armor plate with additional padding, providing good protection against low-velocity trauma. Attaches to a plate carrier." + icon_state = "armor_riot" + slowdown = 0.6 + armor = list(melee = 70, bullet = 10, laser = 10, energy = 10, bomb = 0, bio = 0, rad = 0) + armorsoak = list(melee = 10, bullet = 0, laser = 0, energy = 5, bomb = 0, bio = 0, rad = 0) + siemens_coefficient = 0.7 + +/obj/item/clothing/accessory/armor/armorplate/laserproof + name = "ablative armor plate" + desc = "A durasteel-scaled synthetic armor plate, providing good protection against lasers. Attaches to a plate carrier." + icon_state = "armor_medium" + slowdown = 0.6 + armor = list(melee = 10, bullet = 10, laser = 70, energy = 50, bomb = 0, bio = 0, rad = 0) + armorsoak = list(melee = 0, bullet = 0, laser = 10, energy = 15, bomb = 0, bio = 0, rad = 0) + siemens_coefficient = 0.1 + +/obj/item/clothing/accessory/armor/armorplate/ablative/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") + if(istype(damage_source, /obj/item/projectile/energy) || istype(damage_source, /obj/item/projectile/beam)) + var/obj/item/projectile/P = damage_source + + if(P.reflected) + return ..() + + var/reflectchance = 40 - round(damage/3) + if(!(def_zone in list(BP_TORSO, BP_GROIN))) + reflectchance /= 2 + if(P.starting && prob(reflectchance)) + visible_message("\The [user]'s [src.name] reflects [attack_text]!") + + + var/new_x = P.starting.x + pick(0, 0, 0, 0, 0, -1, 1, -2, 2) + var/new_y = P.starting.y + pick(0, 0, 0, 0, 0, -1, 1, -2, 2) + var/turf/curloc = get_turf(user) + + P.redirect(new_x, new_y, curloc, user) + P.reflected = 1 + + return PROJECTILE_CONTINUE + ////////////// //Arm guards ////////////// diff --git a/code/modules/detectivework/microscope/dnascanner.dm b/code/modules/detectivework/microscope/dnascanner.dm index 413fe14d01..80fb43e91c 100644 --- a/code/modules/detectivework/microscope/dnascanner.dm +++ b/code/modules/detectivework/microscope/dnascanner.dm @@ -12,7 +12,7 @@ var/closed = 0 var/scanning = 0 var/scanner_progress = 0 - var/scanner_rate = 2.50 + var/scanner_rate = 5 var/last_process_worldtime = 0 var/report_num = 0 diff --git a/code/modules/detectivework/microscope/microscope.dm b/code/modules/detectivework/microscope/microscope.dm index 3bc596fb57..33f6da65af 100644 --- a/code/modules/detectivework/microscope/microscope.dm +++ b/code/modules/detectivework/microscope/microscope.dm @@ -32,7 +32,7 @@ user << "The microscope whirrs as you examine \the [sample]." - if(!do_after(user, 25) || !sample) + if(!do_after(user, 2 SECONDS) || !sample) user << "You stop examining \the [sample]." return @@ -71,8 +71,8 @@ report.info += "Surface analysis has determined unique fingerprint strings:

" for(var/prints in card.evidence) report.info += "Fingerprint string: " - if(!is_complete_print(prints)) - report.info += "INCOMPLETE PRINT" + if(!is_complete_print(card.evidence[prints])) + report.info += "INCOMPLETE PRINT:[card.evidence[prints]]" else report.info += "[prints]" report.info += "
" diff --git a/code/modules/detectivework/tools/scanner.dm b/code/modules/detectivework/tools/scanner.dm new file mode 100644 index 0000000000..21006bb2e0 --- /dev/null +++ b/code/modules/detectivework/tools/scanner.dm @@ -0,0 +1,201 @@ +/obj/item/device/detective_scanner + name = "forensic scanner" + desc = "Used to scan objects for DNA and fingerprints." + icon_state = "forensic" + var/list/stored = list() + w_class = ITEMSIZE_SMALL + item_state = "electronic" + flags = NOBLUDGEON + slot_flags = SLOT_BELT + + var/reveal_fingerprints = TRUE + var/reveal_incompletes = FALSE + var/reveal_blood = TRUE + var/reveal_fibers = FALSE + +/obj/item/device/detective_scanner/attack(mob/living/carbon/human/M as mob, mob/user as mob) + if (!ishuman(M)) + to_chat(user, "\The [M] does not seem to be compatible with this device.") + flick("[icon_state]0",src) + return 0 + + if(reveal_fingerprints) + if((!( istype(M.dna, /datum/dna) ) || M.gloves)) + to_chat(user, "No fingerprints found on [M]") + flick("[icon_state]0",src) + return 0 + else if(user.zone_sel.selecting == "r_hand" || user.zone_sel.selecting == "l_hand") + var/obj/item/weapon/sample/print/P = new /obj/item/weapon/sample/print(user.loc) + P.attack(M, user) + to_chat(user,"Done printing.") + // user << "[M]'s Fingerprints: [md5(M.dna.uni_identity)]" + + if(reveal_blood && M.blood_DNA && M.blood_DNA.len) + to_chat(user,"Blood found on [M]. Analysing...") + spawn(15) + for(var/blood in M.blood_DNA) + to_chat(user,"Blood type: [M.blood_DNA[blood]]\nDNA: [blood]") + return + +/obj/item/device/detective_scanner/afterattack(atom/A as obj|turf, mob/user, proximity) + if(!proximity) return + if(ismob(A)) + return + +/* + if(istype(A,/obj/machinery/computer/forensic_scanning)) + user.visible_message("[user] takes a cord out of [src] and hooks its end into [A]" ,\ + "You download data from [src] to [A]") + var/obj/machinery/computer/forensic_scanning/F = A + F.sync_data(stored) + return +*/ + + if(istype(A,/obj/item/weapon/sample/print)) + to_chat(user,"The scanner displays on the screen: \"ERROR 43: Object on Excluded Object List.\"") + flick("[icon_state]0",src) + return + + add_fingerprint(user) + + if(!(do_after(user, 1 SECOND))) + to_chat(user,"You must remain still for the device to complete its work.") + return 0 + + //General + if ((!A.fingerprints || !A.fingerprints.len) && !A.suit_fibers && !A.blood_DNA) + user.visible_message("\The [user] scans \the [A] with \a [src], the air around [user.gender == MALE ? "him" : "her"] humming[prob(70) ? " gently." : "."]" ,\ + "Unable to locate any fingerprints, materials, fibers, or blood on [A]!",\ + "You hear a faint hum of electrical equipment.") + flick("[icon_state]0",src) + return 0 + + if(add_data(A)) + to_chat(user,"Object already in internal memory. Consolidating data...") + flick("[icon_state]2",src) + return + + //PRINTS + if(A.fingerprints && A.fingerprints.len) + to_chat(user,"Isolated [A.fingerprints.len] fingerprints:") + if(!reveal_incompletes) + to_chat(user,"Rapid Analysis Imperfect: Scan samples with H.R.F.S. equipment to determine nature of incomplete prints.") + var/list/complete_prints = list() + var/list/incomplete_prints = list() + for(var/i in A.fingerprints) + var/print = A.fingerprints[i] + if(stringpercent(print) <= FINGERPRINT_COMPLETE) + complete_prints += print + else + incomplete_prints += print + if(complete_prints.len < 1) + to_chat(user,"No intact prints found") + else + to_chat(user,"Found [complete_prints.len] intact prints") + if(reveal_fingerprints) + for(var/i in complete_prints) + to_chat(user,"    [i]") + + to_chat(user,"Found [incomplete_prints.len] incomplete prints") + if(reveal_incompletes) + for(var/i in incomplete_prints) + to_chat(user,"    [i]") + + + //FIBERS + if(A.suit_fibers && A.suit_fibers.len) + to_chat(user,"Fibers/Materials detected.[reveal_fibers ? " Analysing..." : " Acquisition of fibers for H.R.F.S. analysis advised."]") + flick("[icon_state]2",src) + if(reveal_fibers && do_after(user, 5 SECONDS)) + to_chat(user,"Apparel samples scanned:") + for(var/sample in A.suit_fibers) + to_chat(user," - [sample]") + + //Blood + if (A.blood_DNA && A.blood_DNA.len) + to_chat(user,"Blood detected.[reveal_blood ? " Analysing..." : " Acquisition of swab for H.R.F.S. analysis advised."]") + if(reveal_blood && do_after(user, 5 SECONDS)) + flick("[icon_state]2",src) + for(var/blood in A.blood_DNA) + to_chat(user,"Blood type: [A.blood_DNA[blood]] DNA: [blood]") + + user.visible_message("\The [user] scans \the [A] with \a [src], the air around [user.gender == MALE ? "him" : "her"] humming[prob(70) ? " gently." : "."]" ,\ + "You finish scanning \the [A].",\ + "You hear a faint hum of electrical equipment.") + flick("[icon_state]2",src) + return 0 + +/obj/item/device/detective_scanner/proc/add_data(atom/A as mob|obj|turf|area) + var/datum/data/record/forensic/old = stored["\ref [A]"] + var/datum/data/record/forensic/fresh = new(A) + + if(old) + fresh.merge(old) + . = 1 + stored["\ref [A]"] = fresh + +/obj/item/device/detective_scanner/verb/examine_data() + set name = "Examine Forensic Data" + set category = "Object" + set src in view(1) + + world << "usr is [usr]" + display_data(usr) + +/obj/item/device/detective_scanner/proc/display_data(var/mob/user) + if(user && stored && stored.len) + for(var/objref in stored) + if(!do_after(user, 1 SECOND)) // So people can move and stop the spam, if they refuse to wipe data. + break + + var/datum/data/record/forensic/F = stored[objref] + var/list/fprints = F.fields["fprints"] + var/list/fibers = F.fields["fibers"] + var/list/bloods = F.fields["blood"] + + to_chat(user, "Data for: [F.fields["name"]]") + + if(reveal_fingerprints) + var/list/complete_prints = list() + var/list/incomplete_prints = list() + for(var/i in fprints) + var/print = fprints[i] + if(stringpercent(print) <= FINGERPRINT_COMPLETE) + complete_prints += print + to_chat(user, " - [print]") + else + incomplete_prints += print + + if(complete_prints.len < 1) + to_chat(user,"No intact prints found.") + + if(reveal_incompletes) + for(var/print in incomplete_prints) + to_chat(user, " - [print]") + + if(fibers && fibers.len) + to_chat(user, "[fibers.len] samples of material were present.") + if(reveal_fibers) + for(var/sample in fibers) + to_chat(user," - [sample]") + + if(bloods && bloods.len) + to_chat(user, "[bloods.len] samples of blood were present.") + if(reveal_blood) + for(var/bloodsample in bloods) + to_chat(user, " - [bloodsample] Type: [bloods[bloodsample]]") + +/obj/item/device/detective_scanner/verb/wipe() + set name = "Wipe Forensic Data" + set category = "Object" + set src in view(1) + + if (alert("Are you sure you want to wipe all data from [src]?",,"Yes","No") == "Yes") + stored = list() + to_chat(usr,"Forensic data erase complete.") + +/obj/item/device/detective_scanner/advanced + name = "advanced forensic scanner" + icon_state = "forensic_neo" + reveal_fibers = TRUE + reveal_incompletes = TRUE diff --git a/code/modules/gamemaster/actions/drill_announcement.dm b/code/modules/gamemaster/actions/drill_announcement.dm new file mode 100644 index 0000000000..978d5a4ffd --- /dev/null +++ b/code/modules/gamemaster/actions/drill_announcement.dm @@ -0,0 +1,10 @@ +/datum/gm_action/security_drill + name = "security drills" + departments = list(ROLE_SECURITY, ROLE_EVERYONE) + +/datum/gm_action/security_drill/announce() + spawn(rand(1 MINUTE, 2 MINUTES)) + command_announcement.Announce("[pick("A NanoTrasen security director", "A Vir-Gov correspondant", "Local Sif authoritiy")] has advised the enactment of [pick("a rampant wildlife", "a fire", "a hostile boarding", "a nonstandard", "a bomb", "an emergent intelligence")] drill with the personnel onboard \the [station_name()].", "Security Advisement") + +/datum/gm_action/security_drill/get_weight() + return max(-20, 10 + gm.staleness - (gm.danger * 2)) + (metric.count_people_in_department(ROLE_SECURITY) * 5) + (metric.count_people_in_department(ROLE_EVERYONE) * 1.5) diff --git a/code/modules/gamemaster/actions/dust.dm b/code/modules/gamemaster/actions/dust.dm index caecc51445..da08dc4f4c 100644 --- a/code/modules/gamemaster/actions/dust.dm +++ b/code/modules/gamemaster/actions/dust.dm @@ -14,4 +14,4 @@ /datum/gm_action/dust/start() ..() - dust_swarm("norm") \ No newline at end of file + dust_swarm("norm") diff --git a/code/modules/gamemaster/actions/infestation.dm b/code/modules/gamemaster/actions/infestation.dm new file mode 100644 index 0000000000..2c8ddac566 --- /dev/null +++ b/code/modules/gamemaster/actions/infestation.dm @@ -0,0 +1,116 @@ +#define LOC_KITCHEN 0 +#define LOC_ATMOS 1 +#define LOC_CHAPEL 2 +#define LOC_LIBRARY 3 +#define LOC_HYDRO 4 +#define LOC_VAULT 5 +#define LOC_CONSTR 6 +#define LOC_TECH 7 +#define LOC_GARDEN 8 + +#define VERM_MICE 0 +#define VERM_LIZARDS 1 +#define VERM_SPIDERS 2 + +/datum/gm_action/infestation + name = "animal infestation" + departments = list(ROLE_EVERYONE) + var/location + var/locstring + var/vermin + var/vermstring + + var/list/turf/simulated/floor/turfs = list() + + var/spawn_types + var/max_number + +/datum/gm_action/infestation/set_up() + location = rand(0,8) + turfs.Cut() + var/spawn_area_type + switch(location) + if(LOC_KITCHEN) + spawn_area_type = /area/crew_quarters/kitchen + locstring = "the kitchen" + if(LOC_ATMOS) + spawn_area_type = /area/engineering/atmos + locstring = "atmospherics" + if(LOC_CHAPEL) + spawn_area_type = /area/chapel/main + locstring = "the chapel" + if(LOC_LIBRARY) + spawn_area_type = /area/library + locstring = "the library" + if(LOC_HYDRO) + spawn_area_type = /area/hydroponics + locstring = "hydroponics" + if(LOC_VAULT) + spawn_area_type = /area/security/nuke_storage + locstring = "the vault" + if(LOC_CONSTR) + spawn_area_type = /area/construction + locstring = "the construction area" + if(LOC_TECH) + spawn_area_type = /area/storage/tech + locstring = "technical storage" + if(LOC_GARDEN) + spawn_area_type = /area/hydroponics/garden + locstring = "the public garden" + + for(var/areapath in typesof(spawn_area_type)) + var/area/A = locate(areapath) + for(var/turf/simulated/floor/F in A.contents) + if(turf_clear(F)) + turfs += F + + spawn_types = list() + max_number = 0 + vermin = rand(0,2) + switch(vermin) + if(VERM_MICE) + spawn_types = list(/mob/living/simple_mob/animal/passive/mouse/gray, /mob/living/simple_mob/animal/passive/mouse/brown, /mob/living/simple_mob/animal/passive/mouse/white, /mob/living/simple_mob/animal/passive/mouse/rat) + max_number = 12 + vermstring = "mice" + if(VERM_LIZARDS) + spawn_types = list(/mob/living/simple_mob/animal/passive/lizard, /mob/living/simple_mob/animal/passive/lizard, /mob/living/simple_mob/animal/passive/lizard/large, /mob/living/simple_mob/animal/passive/lizard/large/defensive) + max_number = 6 + vermstring = "lizards" + if(VERM_SPIDERS) + spawn_types = list(/obj/effect/spider/spiderling) + max_number = 3 + vermstring = "spiders" + +/datum/gm_action/infestation/start() + spawn() + var/num = rand(2,max_number) + while(turfs.len > 0 && num > 0) + var/turf/simulated/floor/T = pick(turfs) + turfs.Remove(T) + num-- + + if(vermin == VERM_SPIDERS) + var/obj/effect/spider/spiderling/S = new(T) + S.amount_grown = -1 + else + var/spawn_type = pick(spawn_types) + new spawn_type(T) + +/datum/gm_action/infestation/announce() + command_announcement.Announce("Bioscans indicate that [vermstring] have been breeding in [locstring]. Clear them out, before this starts to affect productivity.", "Vermin infestation") + +/datum/gm_action/infestation/get_weight() + return 5 + (metric.count_people_in_department(ROLE_EVERYONE) * 20) + +#undef LOC_KITCHEN +#undef LOC_ATMOS +#undef LOC_CHAPEL +#undef LOC_LIBRARY +#undef LOC_HYDRO +#undef LOC_VAULT +#undef LOC_TECH +#undef LOC_GARDEN + +#undef VERM_MICE +#undef VERM_LIZARDS +#undef VERM_SPIDERS \ No newline at end of file diff --git a/code/modules/gamemaster/actions/ion_storm.dm b/code/modules/gamemaster/actions/ion_storm.dm index e733bd4bd1..9285b2ad99 100644 --- a/code/modules/gamemaster/actions/ion_storm.dm +++ b/code/modules/gamemaster/actions/ion_storm.dm @@ -1,4 +1,6 @@ /datum/gm_action/ionstorm + name = "ion storm" + departments = list(ROLE_SYNTHETIC) var/botEmagChance = 0.5 var/list/players = list() var/active = FALSE @@ -41,3 +43,8 @@ spawn(rand(5000,8000)) if(prob(50)) ion_storm_announcement() + +/datum/gm_action/ionstorm/get_weight() + var/bots = metric.count_people_in_department(ROLE_SYNTHETIC) + var/weight = 5 + (bots * 20) + return weight diff --git a/code/modules/gamemaster/actions/manifest_malfunction.dm b/code/modules/gamemaster/actions/manifest_malfunction.dm new file mode 100644 index 0000000000..0ef9067671 --- /dev/null +++ b/code/modules/gamemaster/actions/manifest_malfunction.dm @@ -0,0 +1,52 @@ +/datum/gm_action/manifest_malfunction + name = "manifest malfunction" + enabled = TRUE + departments = list(ROLE_SECURITY, ROLE_SYNTHETIC, ROLE_EVERYONE) + chaotic = 3 + reusable = FALSE + length = 0 + + var/recordtype + +/datum/gm_action/manifest_malfunction/set_up() + severity = pickweight(EVENT_LEVEL_MUNDANE = 6, + EVENT_LEVEL_MODERATE = 2, + EVENT_LEVEL_MAJOR = 1 + ) + + recordtype = pickweight("medical" = 10,"security" = (severity * 15)) + + return + +/datum/gm_action/manifest_malfunction/get_weight() + . = -10 + + var/security = metric.count_people_in_department(ROLE_SECURITY) + + if(security && data_core) + . += (metric.count_people_in_department(ROLE_EVERYONE) * 5) - (metric.count_people_in_department(ROLE_SYNTHETIC) * 5) + + return . + +/datum/gm_action/manifest_malfunction/start() + ..() + + var/manifest_cut_count = 1 * severity + + for(var/I = 1 to manifest_cut_count) + var/datum/data/record/R + + switch(recordtype) + if("security") + R = pick(data_core.security) + + if("medical") + R = pick(data_core.medical) + + qdel(R) + +/datum/gm_action/manifest_malfunction/announce() + if(prob(30 * severity)) + spawn(rand(5 MINUTES, 10 MINUTES)) + command_announcement.Announce("An ongoing mass upload of malware for [recordtype] record cores has been detected onboard [station_name()]", "Data Breach Alert") + return diff --git a/code/modules/gamemaster/actions/meteor_defense.dm b/code/modules/gamemaster/actions/meteor_defense.dm index 5df197f160..d0a0d40a3f 100644 --- a/code/modules/gamemaster/actions/meteor_defense.dm +++ b/code/modules/gamemaster/actions/meteor_defense.dm @@ -2,18 +2,38 @@ /datum/gm_action/meteor_defense name = "meteor defense" - departments = list(ROLE_ENGINEERING) + departments = list(ROLE_ENGINEERING, ROLE_CARGO) chaotic = 50 var/direction = null var/dir_text = null var/waves = 0 + var/meteor_types + /datum/gm_action/meteor_defense/get_weight() var/engineers = metric.count_people_in_department(ROLE_ENGINEERING) - var/weight = (max(engineers - 1, 0) * 25) // If only one engineer exists, no meteors for now. + var/cargo = metric.count_people_in_department(ROLE_CARGO) + var/bots = metric.count_people_in_department(ROLE_SYNTHETIC) + var/weight = (max(engineers - 1, 0) * 20) // If only one engineer exists, no meteors for now. + + if(engineers >= 2) + weight += ((cargo - 1) * 10) + weight += (bots * 15) + return weight /datum/gm_action/meteor_defense/set_up() + severity = pickweight(EVENT_LEVEL_MUNDANE = 10, + EVENT_LEVEL_MODERATE = 3 + ) + + switch(severity) + if(EVENT_LEVEL_MUNDANE) + meteor_types = meteors_threatening.Copy() + + if(EVENT_LEVEL_MODERATE) + meteor_types = meteors_catastrophic.Copy() + direction = pick(cardinal) // alldirs doesn't work with current meteor code unfortunately. waves = rand(5, 8) switch(direction) @@ -27,17 +47,17 @@ dir_text = "starboard" /datum/gm_action/meteor_defense/announce() - var/announcement = "Alert! Two other asteroids have collided near [station_name()]. Chunks of it are expected to approach from the [dir_text] side. ETA to arrival is \ - approximately 10 minutes." + var/announcement = "Alert! Two asteroids have collided near [station_name()]. Chunks of it are expected to approach from the [dir_text] side. ETA to arrival is \ + approximately [round(5 * severity * 2)] minutes." command_announcement.Announce(announcement, "Meteor Alert", new_sound = 'sound/AI/meteors.ogg') /datum/gm_action/meteor_defense/start() ..() spawn(0) - sleep(5 MINUTES) - var/announcement = "The incoming debris are expected to approach from the [dir_text] side. ETA to arrival is approximately 5 minutes." + sleep(round(5 * severity) MINUTES) + var/announcement = "The incoming debris are expected to approach from the [dir_text] side. ETA to arrival is approximately [round(5 * severity)] minutes." command_announcement.Announce(announcement, "Meteor Alert - Update") - sleep(5 MINUTES) + sleep(round(5 * severity) MINUTES) announcement = "Incoming debris approaches from the [dir_text] side!" command_announcement.Announce(announcement, "Meteor Alert - Update") while(waves) @@ -46,6 +66,6 @@ spawn_meteors(rand(8, 12), meteors_threatening, reverse_dir[direction]) waves-- sleep(30 SECONDS) - announcement = "The colony has cleared the incoming debris." + announcement = "The station has cleared the incoming debris." command_announcement.Announce(announcement, "Meteor Alert - Update") - message_admins("Meteor defense event has ended.") \ No newline at end of file + message_admins("Meteor defense event has ended.") diff --git a/code/modules/gamemaster/actions/security_advisement.dm b/code/modules/gamemaster/actions/security_advisement.dm new file mode 100644 index 0000000000..b5c8d82912 --- /dev/null +++ b/code/modules/gamemaster/actions/security_advisement.dm @@ -0,0 +1,49 @@ +/datum/gm_action/security_screening + name = "security screening" + departments = list(ROLE_SECURITY, ROLE_EVERYONE) + + var/list/species_weights = list( + SPECIES_SKRELL = 9, + SPECIES_UNATHI = 15, + SPECIES_HUMAN_VATBORN = 6, + SPECIES_TESHARI = 2, + SPECIES_TAJ = 3, + SPECIES_DIONA = 1, + SPECIES_ZADDAT = 25, + SPECIES_HUMAN = 3, + SPECIES_PROMETHEAN = 30 + ) + + var/list/synth_weights = list( + "Cybernetic" = 15, + "Drone" = 30, + "Positronic" = 25 + ) + + var/list/end_weights = list() + +/datum/gm_action/security_screening/set_up() + for(var/species_name in species_weights) + var/giveweight = 0 + + for(var/datum/data/record/R in data_core.general) + if(R.fields["species"] == species_name) + giveweight += species_weights[species_name] + + end_weights[species_name] = giveweight + + for(var/bottype in synth_weights) + var/giveweight = 0 + + for(var/datum/data/record/R in data_core.general) + if(R.fields["brain_type"] == bottype) + giveweight += synth_weights[bottype] + + end_weights[bottype] = giveweight + +/datum/gm_action/security_screening/announce() + spawn(rand(1 MINUTE, 2 MINUTES)) + command_announcement.Announce("[pick("A nearby Navy vessel", "A Solar official", "A Vir-Gov official", "A NanoTrasen board director")] has requested the screening of [pick("every other", "every", "suspicious", "willing")] [pickweight(end_weights)] personnel onboard \the [station_name()].", "Security Advisement") + +/datum/gm_action/security_screening/get_weight() + return max(-20, 10 + round(gm.staleness * 1.5) - (gm.danger * 2)) + (metric.count_people_in_department(ROLE_SECURITY) * 10) + (metric.count_people_in_department(ROLE_EVERYONE) * 1.5) diff --git a/code/modules/gamemaster/actions/spider_infestation.dm b/code/modules/gamemaster/actions/spider_infestation.dm index baba49c2e3..c641c86167 100644 --- a/code/modules/gamemaster/actions/spider_infestation.dm +++ b/code/modules/gamemaster/actions/spider_infestation.dm @@ -7,21 +7,28 @@ var/spawncount = 1 -/datum/gm_action/spider_infestation/set_up() - spawn(rand(600, 6000)) - announce() + var/spawntype = /obj/effect/spider/spiderling - if(prob(40)) - severity = rand(2,3) - else - severity = 1 +/datum/gm_action/spider_infestation/set_up() + severity = pickweight(EVENT_LEVEL_MUNDANE = max(1,(12 - (3 * metric.count_people_in_department(ROLE_SECURITY)))), + EVENT_LEVEL_MODERATE = (7 + (2 * metric.count_people_in_department(ROLE_SECURITY))), + EVENT_LEVEL_MAJOR = (1 + (2 * metric.count_people_in_department(ROLE_SECURITY))) + ) + + switch(severity) + if(EVENT_LEVEL_MUNDANE) + spawntype = /obj/effect/spider/spiderling/stunted + if(EVENT_LEVEL_MODERATE) + spawntype = /obj/effect/spider/spiderling + if(EVENT_LEVEL_MAJOR) + spawntype = /obj/effect/spider/spiderling spawncount = rand(4 * severity, 6 * severity) /datum/gm_action/spider_infestation/announce() command_announcement.Announce("Unidentified lifesigns detected coming aboard [station_name()]. Secure any exterior access, including ducting and ventilation.", "Lifesign Alert", new_sound = 'sound/AI/aliens.ogg') - if(severity >= 3) + if(severity >= EVENT_LEVEL_MAJOR) spawn(rand(600, 3000)) command_announcement.Announce("Unidentified lifesigns previously detected coming aboard [station_name()] have been classified as a swarm of arachnids. Extreme caution is advised.", "Arachnid Alert") diff --git a/code/modules/gamemaster/actions/station_fundraise.dm b/code/modules/gamemaster/actions/station_fundraise.dm new file mode 100644 index 0000000000..2a516d178a --- /dev/null +++ b/code/modules/gamemaster/actions/station_fundraise.dm @@ -0,0 +1,14 @@ +/datum/gm_action/station_fund_raise + name = "local funding drive" + departments = list(ROLE_SECURITY, ROLE_CARGO, ROLE_EVERYONE) + +/datum/gm_action/station_fund_raise/announce() + spawn(rand(1 MINUTE, 2 MINUTES)) + command_announcement.Announce("Due to [pick("recent", "unfortunate", "possible future")] budget [pick("changes", "issues")], in-system stations are now advised to increase funding income.", "Security & Supply Advisement") + +/datum/gm_action/station_fund_raise/get_weight() + var/weight_modifier = 0.5 + if(station_account.money <= 80000) + weight_modifier = 1 + + return (max(-20, 10 + gm.staleness) + ((metric.count_people_in_department(ROLE_SECURITY) + (metric.count_people_in_department(ROLE_CARGO))) * 5) + (metric.count_people_in_department(ROLE_EVERYONE) * 3)) * weight_modifier diff --git a/code/modules/gamemaster/actions/stowaway.dm b/code/modules/gamemaster/actions/stowaway.dm new file mode 100644 index 0000000000..c75df494e2 --- /dev/null +++ b/code/modules/gamemaster/actions/stowaway.dm @@ -0,0 +1,80 @@ +/datum/gm_action/stowaway + name = "stowaway pod" + departments = list(ROLE_EVERYONE, ROLE_SECURITY) + chaotic = 10 + observers_used = TRUE + var/area/target_area // Chosen target area + var/area/target_turf // Chosen target turf in target_area + var/list/area/excluded = list( + /area/submap, + /area/shuttle, + /area/crew_quarters, + /area/holodeck, + /area/engineering/engine_room + ) + + var/list/area/included = list( + /area/maintenance + ) + +/datum/gm_action/stowaway/set_up() + severity = pickweight(EVENT_LEVEL_MUNDANE = 20, + EVENT_LEVEL_MODERATE = 5, + EVENT_LEVEL_MAJOR = 1 + ) + + var/list/area/grand_list_of_areas = get_station_areas(excluded) + + for(var/area/Incl in included) + for(var/area/A in grand_list_of_areas) + if(!istype(A, Incl)) + grand_list_of_areas -= A + + // Okay, now lets try and pick a target! Lets try 10 times, otherwise give up + for(var/i in 1 to 10) + var/area/A = pick(grand_list_of_areas) + if(is_area_occupied(A)) + log_debug("[name] event: Rejected [A] because it is occupied.") + continue + // A good area, great! Lets try and pick a turf + var/list/turfs = list() + for(var/turf/simulated/floor/F in A) + if(turf_clear(F)) + turfs += F + if(turfs.len == 0) + log_debug("[name] event: Rejected [A] because it has no clear turfs.") + continue + target_area = A + target_turf = pick(turfs) + + if(!target_area) + log_debug("[name] event: Giving up after too many failures to pick target area") + return + +/datum/gm_action/stowaway/start() + if(!target_turf) + return + ..() + + var/obj/structure/ghost_pod/ghost_activated/human/HP = new (target_turf) + + if(severity == EVENT_LEVEL_MUNDANE || istype(ticker.mode, /datum/game_mode/extended)) + HP.make_antag = MODE_STOWAWAY + + else if(severity == EVENT_LEVEL_MODERATE) + HP.make_antag = MODE_RENEGADE + HP.occupant_type = "renegade [HP.occupant_type]" + + else if(severity == EVENT_LEVEL_MAJOR) + HP.make_antag = MODE_INFILTRATOR + HP.occupant_type = "volatile [HP.occupant_type]" + + say_dead_object("A [HP.occupant_type] pod is now available in \the [target_area].", HP) + +/datum/gm_action/stowaway/get_weight() + return -20 + (metric.count_people_in_department(ROLE_SECURITY) * 15 + metric.count_people_in_department(ROLE_SYNTHETIC) * 5 + metric.count_people_in_department(ROLE_EVERYONE) * 1) + +/datum/gm_action/stowaway/announce() + spawn(rand(15 MINUTES, 30 MINUTES)) + if(prob(20) && severity >= EVENT_LEVEL_MODERATE && atc && !atc.squelched) + atc.msg("Attention civilian vessels in [using_map.starsys_name] shipping lanes, caution is advised as [pick("an unidentified vessel", "a known criminal's vessel", "a derelict vessel")] has been detected passing multiple local stations.") diff --git a/code/modules/gamemaster/actions/supply_conversion.dm b/code/modules/gamemaster/actions/supply_conversion.dm new file mode 100644 index 0000000000..5f05a8cb04 --- /dev/null +++ b/code/modules/gamemaster/actions/supply_conversion.dm @@ -0,0 +1,62 @@ +/datum/gm_action/nanotrasen_budget_allocation + name = "supply point to cargo budget" + enabled = TRUE + departments = list(ROLE_CARGO) + chaotic = 0 + reusable = TRUE + + var/datum/controller/supply/SC + var/running = FALSE + var/last_run + + var/thaler_earned + +/datum/gm_action/nanotrasen_budget_allocation/set_up() + SC = supply_controller + running = TRUE + return + +/datum/gm_action/nanotrasen_budget_allocation/get_weight() + . = round(SC.points / 15) + + var/cargo = metric.count_people_in_department(ROLE_CARGO) + var/personnel = metric.count_people_in_department(ROLE_EVERYONE) + if(cargo) + . = round(SC.points / (10 + personnel)) + cargo * 10 + + if(running || ( world.time < (last_run + 30 MINUTES))) + . = 0 + + return . + +/datum/gm_action/nanotrasen_budget_allocation/start() + . = ..() + + last_run = world.time + + var/point_difference = SC.points + + if(SC.points >= 1000) + SC.points = round(SC.points / 3) + point_difference -= SC.points + + else if(SC.points >= 500) + SC.points -= 100 * (rand(5, 20) / 10) + point_difference -= SC.points + + else + SC.points = round(SC.points / 1.25) + point_difference -= SC.points + + if(point_difference > 0) + thaler_earned = round(point_difference / SC.points_per_money) + +/datum/gm_action/nanotrasen_budget_allocation/end() + spawn(5 MINUTES) + running = FALSE + return + +/datum/gm_action/nanotrasen_budget_allocation/announce() + spawn(rand(1 MINUTE, 5 MINUTES)) + command_announcement.Announce("[station_name()] Supply Department has earned a converted thaler budget of [thaler_earned] due to their backlogged daily requisition tokens.", "Supply Budget Conversion") + return diff --git a/code/modules/gamemaster/actions/supplyrequest.dm b/code/modules/gamemaster/actions/supplyrequest.dm new file mode 100644 index 0000000000..ae2e597fcd --- /dev/null +++ b/code/modules/gamemaster/actions/supplyrequest.dm @@ -0,0 +1,11 @@ +/datum/gm_action/request + name = "general request" + departments = list(ROLE_CARGO) + +/datum/gm_action/request/announce() + spawn(rand(1 MINUTE, 2 MINUTES)) + command_announcement.Announce("[pick("A nearby vessel", "A Solar contractor", "A Skrellian contractor", "A NanoTrasen board director")] has requested the delivery of [pick("one","two","three","several")] [pick("medical","engineering","research","civilian")] supply packages. The [station_name()] has been tasked with completing this request.", "Supply Request") + +/datum/gm_action/request/get_weight() + return max(15, 15 + round(gm.staleness / 2) - gm.danger) + (metric.count_people_in_department(ROLE_CARGO) * 10) + diff --git a/code/modules/gamemaster/actions/swarmboarder.dm b/code/modules/gamemaster/actions/swarmboarder.dm new file mode 100644 index 0000000000..8bc436b3a7 --- /dev/null +++ b/code/modules/gamemaster/actions/swarmboarder.dm @@ -0,0 +1,75 @@ +/datum/gm_action/swarm_boarder + name = "swarmer shell" + departments = list(ROLE_EVERYONE, ROLE_SECURITY, ROLE_ENGINEERING) + chaotic = 30 + observers_used = TRUE + var/area/target_area // Chosen target area + var/area/target_turf // Chosen target turf in target_area + var/list/area/excluded = list( + /area/submap, + /area/shuttle, + /area/crew_quarters, + /area/holodeck, + /area/engineering/engine_room + ) + + var/list/area/included = list( + /area/maintenance + ) + +/datum/gm_action/swarm_boarder/set_up() + severity = pickweight(EVENT_LEVEL_MUNDANE = 30, + EVENT_LEVEL_MODERATE = 10, + EVENT_LEVEL_MAJOR = 1 + ) + + var/list/area/grand_list_of_areas = get_station_areas(excluded) + + for(var/area/Incl in included) + for(var/area/A in grand_list_of_areas) + if(!istype(A, Incl)) + grand_list_of_areas -= A + + // Okay, now lets try and pick a target! Lets try 10 times, otherwise give up + for(var/i in 1 to 10) + var/area/A = pick(grand_list_of_areas) + if(is_area_occupied(A)) + log_debug("[name] event: Rejected [A] because it is occupied.") + continue + // A good area, great! Lets try and pick a turf + var/list/turfs = list() + for(var/turf/simulated/floor/F in A) + if(turf_clear(F)) + turfs += F + if(turfs.len == 0) + log_debug("[name] event: Rejected [A] because it has no clear turfs.") + continue + target_area = A + target_turf = pick(turfs) + + if(!target_area) + log_debug("[name] event: Giving up after too many failures to pick target area") + return + +/datum/gm_action/swarm_boarder/start() + if(!target_turf) + return + ..() + + var/swarmertype = /obj/structure/ghost_pod/ghost_activated/swarm_drone/event + + if(severity == EVENT_LEVEL_MODERATE) + swarmertype = /obj/structure/ghost_pod/ghost_activated/swarm_drone/event/melee + + if(severity == EVENT_LEVEL_MAJOR) + swarmertype = /obj/structure/ghost_pod/ghost_activated/swarm_drone/event/gunner + + new swarmertype(target_turf) + +/datum/gm_action/swarm_boarder/get_weight() + return -60 + (metric.count_people_in_department(ROLE_SECURITY) * 20 + metric.count_people_in_department(ROLE_SYNTHETIC) * 5 + metric.count_people_in_department(ROLE_EVERYONE) * 1) + +/datum/gm_action/swarm_boarder/announce() + spawn(rand(5 MINUTES, 15 MINUTES)) + if(prob(80) && severity >= EVENT_LEVEL_MODERATE && atc && !atc.squelched) + atc.msg("Attention civilian vessels in [using_map.starsys_name] shipping lanes, caution is advised as [pick("an unidentified vessel", "a known criminal's vessel", "a derelict vessel")] has been detected passing multiple local stations.") diff --git a/code/modules/gamemaster/game_master.dm b/code/modules/gamemaster/game_master.dm index 16a6a03887..61a53f1854 100644 --- a/code/modules/gamemaster/game_master.dm +++ b/code/modules/gamemaster/game_master.dm @@ -15,13 +15,22 @@ var/next_action = 0 // Minimum amount of time of nothingness until the GM can pick something again. var/last_department_used = null // If an event was done for a specific department, it is written here, so it doesn't do it again. - /datum/game_master/New() ..() available_actions = init_subtypes(/datum/gm_action) for(var/datum/gm_action/action in available_actions) action.gm = src + var/config_setup_delay = TRUE + spawn(0) + while(config_setup_delay) + if(config) + config_setup_delay = FALSE + if(config.enable_game_master) + suspended = FALSE + else + sleep(30 SECONDS) + /datum/game_master/process() if(ticker && ticker.current_state == GAME_STATE_PLAYING && !suspended) adjust_staleness(1) diff --git a/code/modules/mob/language/outsider.dm b/code/modules/mob/language/outsider.dm index de78a9c1b5..43ccb5bb7e 100644 --- a/code/modules/mob/language/outsider.dm +++ b/code/modules/mob/language/outsider.dm @@ -116,6 +116,18 @@ return 0 +/datum/language/swarmbot + name = LANGUAGE_SWARMBOT + desc = "A confusing mechanical language spoken by some form of ancient machine." + speech_verb = "clatters" + ask_verb = "chatters" + exclaim_verb = "shrieks" + colour = "changeling" + key = "_" + flags = NO_STUTTER | RESTRICTED + syllables = list("^", "v", "-", ".", "~") + space_chance = 60 + //for your antag purposes. /datum/language/minbus name = LANGUAGE_MINBUS diff --git a/code/modules/mob/living/bot/bot.dm b/code/modules/mob/living/bot/bot.dm index 6d89980bae..f440437167 100644 --- a/code/modules/mob/living/bot/bot.dm +++ b/code/modules/mob/living/bot/bot.dm @@ -32,6 +32,7 @@ var/will_patrol = 0 // If set to 1, will patrol, duh var/patrol_speed = 1 // How many times per tick we move when patrolling var/target_speed = 2 // Ditto for chasing the target + var/panic_on_alert = FALSE // Will the bot go faster when the alert level is raised? var/min_target_dist = 1 // How close we try to get to the target var/max_target_dist = 50 // How far we are willing to go var/max_patrol_dist = 250 @@ -170,14 +171,20 @@ if(!A || !A.loc || prob(1)) ignore_list -= A handleRegular() + + var/panic_speed_mod = 0 + + if(panic_on_alert) + panic_speed_mod = handlePanic() + if(target && confirmTarget(target)) if(Adjacent(target)) handleAdjacentTarget() else handleRangedTarget() if(!wait_if_pulled || !pulledby) - for(var/i = 1 to target_speed) - sleep(20 / (target_speed + 1)) + for(var/i = 1 to (target_speed + panic_speed_mod)) + sleep(20 / (target_speed + panic_speed_mod + 1)) stepToTarget() if(max_frustration && frustration > max_frustration * target_speed) handleFrustrated(1) @@ -186,7 +193,7 @@ lookForTargets() if(will_patrol && !pulledby && !target) if(patrol_path && patrol_path.len) - for(var/i = 1 to patrol_speed) + for(var/i = 1 to (patrol_speed + panic_speed_mod)) sleep(20 / (patrol_speed + 1)) handlePatrol() if(max_frustration && frustration > max_frustration * patrol_speed) @@ -205,6 +212,32 @@ /mob/living/bot/proc/handleRangedTarget() return +/mob/living/bot/proc/handlePanic() // Speed modification based on alert level. + . = 0 + switch(get_security_level()) + if("green") + . = 0 + + if("yellow") + . = 0 + + if("violet") + . = 0 + + if("orange") + . = 0 + + if("blue") + . = 1 + + if("red") + . = 2 + + if("delta") + . = 2 + + return . + /mob/living/bot/proc/stepToTarget() if(!target || !target.loc) return diff --git a/code/modules/mob/living/bot/cleanbot.dm b/code/modules/mob/living/bot/cleanbot.dm index b620ca3e1a..74616879c4 100644 --- a/code/modules/mob/living/bot/cleanbot.dm +++ b/code/modules/mob/living/bot/cleanbot.dm @@ -38,6 +38,32 @@ spawn(600) ignore_list -= g +/mob/living/bot/cleanbot/handlePanic() // Speed modification based on alert level. + . = 0 + switch(get_security_level()) + if("green") + . = 0 + + if("yellow") + . = 1 + + if("violet") + . = 1 + + if("orange") + . = 1 + + if("blue") + . = 2 + + if("red") + . = 2 + + if("delta") + . = 2 + + return . + /mob/living/bot/cleanbot/lookForTargets() for(var/obj/effect/decal/cleanable/D in view(world.view, src)) // There was some odd code to make it start with nearest decals, it's unnecessary, this works if(confirmTarget(D)) diff --git a/code/modules/mob/living/bot/floorbot.dm b/code/modules/mob/living/bot/floorbot.dm index 95c83dcaa0..19036c6702 100644 --- a/code/modules/mob/living/bot/floorbot.dm +++ b/code/modules/mob/living/bot/floorbot.dm @@ -300,6 +300,32 @@ else if(amount > maxAmount) amount = maxAmount +/mob/living/bot/floorbot/handlePanic() // Speed modification based on alert level. + . = 0 + switch(get_security_level()) + if("green") + . = 0 + + if("yellow") + . = 0 + + if("violet") + . = 0 + + if("orange") + . = 1 + + if("blue") + . = 1 + + if("red") + . = 2 + + if("delta") + . = 2 + + return . + /* Assembly */ /obj/item/weapon/storage/toolbox/mechanical/attackby(var/obj/item/stack/tile/floor/T, mob/living/user as mob) diff --git a/code/modules/mob/living/bot/medbot.dm b/code/modules/mob/living/bot/medbot.dm index f7bac5b82b..c090b4cd24 100644 --- a/code/modules/mob/living/bot/medbot.dm +++ b/code/modules/mob/living/bot/medbot.dm @@ -49,6 +49,32 @@ /mob/living/bot/medbot/handleAdjacentTarget() UnarmedAttack(target) +/mob/living/bot/medbot/handlePanic() // Speed modification based on alert level. + . = 0 + switch(get_security_level()) + if("green") + . = 0 + + if("yellow") + . = 0 + + if("violet") + . = 1 + + if("orange") + . = 0 + + if("blue") + . = 1 + + if("red") + . = 2 + + if("delta") + . = 2 + + return . + /mob/living/bot/medbot/lookForTargets() for(var/mob/living/carbon/human/H in view(7, src)) // Time to find a patient! if(confirmTarget(H)) diff --git a/code/modules/mob/living/bot/secbot.dm b/code/modules/mob/living/bot/secbot.dm index 0745fc96e0..54217ff41a 100644 --- a/code/modules/mob/living/bot/secbot.dm +++ b/code/modules/mob/living/bot/secbot.dm @@ -13,6 +13,8 @@ patrol_speed = 2 target_speed = 3 + density = 1 + var/default_icon_state = "secbot" var/idcheck = FALSE // If true, arrests for having weapons without authorization. var/check_records = FALSE // If true, arrests people without a record. @@ -39,6 +41,8 @@ name = "Officer Beepsky" desc = "It's Officer Beep O'sky! Powered by a potato and a shot of whiskey." will_patrol = TRUE + maxHealth = 130 + health = 130 /mob/living/bot/secbot/slime name = "Slime Securitron" @@ -56,6 +60,8 @@ /mob/living/bot/secbot/slime/slimesky name = "Doctor Slimesky" desc = "An old friend of Officer Beep O'sky. He prescribes beatings to rowdy slimes so that real doctors don't need to treat the xenobiologists." + maxHealth = 130 + health = 130 /mob/living/bot/secbot/update_icons() if(on && busy) @@ -219,6 +225,32 @@ global_announcer.autosay("[src] is [action] a level [threat] [action != "fighting" ? "suspect" : "threat"] [target_name(target)] in [get_area(src)].", "[src]", "Security") UnarmedAttack(target) +/mob/living/bot/secbot/handlePanic() // Speed modification based on alert level. + . = 0 + switch(get_security_level()) + if("green") + . = 0 + + if("yellow") + . = 0 + + if("violet") + . = 0 + + if("orange") + . = 0 + + if("blue") + . = 1 + + if("red") + . = 2 + + if("delta") + . = 2 + + return . + // So Beepsky talks while beating up simple mobs. /mob/living/bot/secbot/proc/insult(var/mob/living/L) if(can_next_insult > world.time) diff --git a/code/modules/mob/living/carbon/human/species/species.dm b/code/modules/mob/living/carbon/human/species/species.dm index cdf5d9c21d..7eda52aae6 100644 --- a/code/modules/mob/living/carbon/human/species/species.dm +++ b/code/modules/mob/living/carbon/human/species/species.dm @@ -419,6 +419,10 @@ // Called in life() when the mob has no client. /datum/species/proc/handle_npc(var/mob/living/carbon/human/H) + if(H.stat == CONSCIOUS && H.ai_holder) + if(H.resting) + H.resting = FALSE + H.update_canmove() return // Called when lying down on a water tile. diff --git a/code/modules/mob/living/carbon/human/species/species_getters.dm b/code/modules/mob/living/carbon/human/species/species_getters.dm index efb053eddf..421d614882 100644 --- a/code/modules/mob/living/carbon/human/species/species_getters.dm +++ b/code/modules/mob/living/carbon/human/species/species_getters.dm @@ -50,8 +50,10 @@ if(H) if(H.looksSynthetic()) return "flashing a 'system offline' light" - else + else if(!H.ai_holder) return show_ssd + else + return /datum/species/proc/get_blood_colour(var/mob/living/carbon/human/H) if(H) diff --git a/code/modules/mob/living/carbon/human/species/station/monkey.dm b/code/modules/mob/living/carbon/human/species/station/monkey.dm index dcc31ab77a..587f4d918d 100644 --- a/code/modules/mob/living/carbon/human/species/station/monkey.dm +++ b/code/modules/mob/living/carbon/human/species/station/monkey.dm @@ -62,6 +62,8 @@ if(prob(1)) H.emote(pick("scratch","jump","roll","tail")) + ..() + /datum/species/monkey/get_random_name() return "[lowertext(name)] ([rand(100,999)])" diff --git a/code/modules/mob/living/silicon/robot/drone/drone.dm b/code/modules/mob/living/silicon/robot/drone/drone.dm index 73e099b27d..cc16dd6dc7 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone.dm @@ -60,6 +60,8 @@ var/list/mob_hat_cache = list() var/serial_number = 0 var/name_override = 0 + var/foreign_droid = FALSE + holder_type = /obj/item/weapon/holder/drone can_be_antagged = FALSE @@ -118,7 +120,8 @@ var/list/mob_hat_cache = list() updatename() /mob/living/silicon/robot/drone/init() - aiCamera = new/obj/item/device/camera/siliconcam/drone_camera(src) + if(!scrambledcodes && !foreign_droid) + aiCamera = new/obj/item/device/camera/siliconcam/drone_camera(src) additional_law_channels["Drone"] = ":d" if(!laws) laws = new law_type if(!module) module = new module_type(src) @@ -253,10 +256,10 @@ var/list/mob_hat_cache = list() //For some goddamn reason robots have this hardcoded. Redefining it for our fragile friends here. /mob/living/silicon/robot/drone/updatehealth() if(status_flags & GODMODE) - health = 35 + health = maxHealth stat = CONSCIOUS return - health = 35 - (getBruteLoss() + getFireLoss()) + health = maxHealth - (getBruteLoss() + getFireLoss()) return //Easiest to check this here, then check again in the robot proc. diff --git a/code/modules/mob/living/silicon/robot/drone/drone_console.dm b/code/modules/mob/living/silicon/robot/drone/drone_console.dm index 113394811b..818a630b93 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone_console.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone_console.dm @@ -29,6 +29,9 @@ for(var/mob/living/silicon/robot/drone/D in mob_list) if(D.z != src.z) continue + if(D.foreign_droid) + continue + dat += "
[D.real_name] ([D.stat == 2 ? "INACTIVE" : "ACTIVE"])" dat += "
Cell charge: [D.cell.charge]/[D.cell.maxcharge]." dat += "
Currently located in: [get_area(D)]." diff --git a/code/modules/mob/living/silicon/robot/drone/drone_items.dm b/code/modules/mob/living/silicon/robot/drone/drone_items.dm index 670087dc51..d20d81b030 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone_items.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone_items.dm @@ -56,6 +56,22 @@ /obj/item/weapon/stock_parts ) +/obj/item/weapon/gripper/security + name = "security gripper" + desc = "A simple grasping tool for corporate security work." + icon_state = "gripper-sec" + + can_hold = list( + /obj/item/weapon/paper, + /obj/item/weapon/paper_bundle, + /obj/item/weapon/pen, + /obj/item/weapon/sample, + /obj/item/weapon/forensics/sample_kit, + /obj/item/device/taperecorder, + /obj/item/device/tape, + /obj/item/device/uv_light + ) + /obj/item/weapon/gripper/paperwork name = "paperwork gripper" desc = "A simple grasping tool for clerical work." diff --git a/code/modules/mob/living/silicon/robot/drone/swarm.dm b/code/modules/mob/living/silicon/robot/drone/swarm.dm new file mode 100644 index 0000000000..f48993caa2 --- /dev/null +++ b/code/modules/mob/living/silicon/robot/drone/swarm.dm @@ -0,0 +1,98 @@ +/mob/living/silicon/robot/drone/swarm + name = "swarm drone" + real_name = "drone" + icon = 'icons/mob/swarmbot.dmi' + icon_state = "swarmer" + faction = "swarmer" + maxHealth = 75 + health = 75 + cell_emp_mult = 0.5 + universal_speak = 0 + universal_understand = 1 + gender = NEUTER + pass_flags = PASSTABLE + braintype = "Drone" + lawupdate = 0 + density = 1 + idcard_type = /obj/item/weapon/card/id/syndicate + req_access = list(999) + integrated_light_power = 3 + local_transmit = 0 + + can_pull_size = ITEMSIZE_NO_CONTAINER + can_pull_mobs = MOB_PULL_SMALLER + can_enter_vent_with = list( + /obj) + + mob_always_swap = 1 + + softfall = TRUE + + mob_size = MOB_LARGE + + law_type = /datum/ai_laws/swarm_drone + module_type = /obj/item/weapon/robot_module/drone/swarm + + hat_x_offset = 0 + hat_y_offset = -10 + + foreign_droid = TRUE + scrambledcodes = TRUE + + holder_type = /obj/item/weapon/holder/drone + + can_be_antagged = TRUE + + var/spell_setup = list( + /spell/aoe_turf/conjure/swarmer, + /spell/aoe_turf/conjure/forcewall/swarm, + /spell/aoe_turf/blink/swarm, + /spell/aoe_turf/conjure/swarmer/gunner, + /spell/aoe_turf/conjure/swarmer/melee + ) + +/mob/living/silicon/robot/drone/swarm/Initialize() + ..() + + add_language(LANGUAGE_SWARMBOT, 1) + + for(var/spell in spell_setup) + src.add_spell(new spell, "nano_spell_ready", /obj/screen/movable/spell_master/swarm) + +/mob/living/silicon/robot/drone/swarm/init() + ..() + QDEL_NULL(aiCamera) + flavor_text = "Some form of ancient machine." + +/mob/living/silicon/robot/drone/swarm/gunner + name = "swarm gunner" + real_name = "drone" + icon = 'icons/mob/swarmbot.dmi' + icon_state = "swarmer_ranged" + faction = "swarmer" + + law_type = /datum/ai_laws/swarm_drone/soldier + module_type = /obj/item/weapon/robot_module/drone/swarm/ranged + + spell_setup = list( + /spell/aoe_turf/conjure/swarmer, + /spell/aoe_turf/conjure/forcewall/swarm, + /spell/aoe_turf/blink/swarm + ) + +/mob/living/silicon/robot/drone/swarm/melee + name = "swarm melee" + real_name = "drone" + icon = 'icons/mob/swarmbot.dmi' + icon_state = "swarmer_melee" + faction = "swarmer" + + law_type = /datum/ai_laws/swarm_drone/soldier + module_type = /obj/item/weapon/robot_module/drone/swarm/melee + + spell_setup = list( + /spell/aoe_turf/conjure/swarmer, + /spell/aoe_turf/conjure/forcewall/swarm, + /spell/aoe_turf/blink/swarm + ) + diff --git a/code/modules/mob/living/silicon/robot/drone/swarm_abilities.dm b/code/modules/mob/living/silicon/robot/drone/swarm_abilities.dm new file mode 100644 index 0000000000..62a54e9ecd --- /dev/null +++ b/code/modules/mob/living/silicon/robot/drone/swarm_abilities.dm @@ -0,0 +1,117 @@ + +/spell/aoe_turf/conjure/swarmer + name = "Self Replication" + desc = "This ability constructs a standard swarmer shell that may activate at some point." + + school = "conjuration" + charge_max = 120 SECONDS + spell_flags = 0 + invocation = "none" + invocation_type = SpI_NONE + range = 0 + + summon_type = list(/obj/structure/ghost_pod/ghost_activated/swarm_drone/event) + + hud_state = "swarm_replicate" + +/spell/aoe_turf/conjure/swarmer/conjure_animation(var/atom/movable/overlay/animation, var/turf/target) + animation.icon_state = "deflect_static" + flick("shield2",animation) + spawn(1 SECOND) + qdel(animation) + +/spell/aoe_turf/conjure/forcewall/swarm + name = "Null-Field" + desc = "Create a bubble of null-point energy." + summon_type = list(/obj/effect/forcefield/swarm) + duration = 30 SECONDS + charge_max = 60 SECONDS + + school = "conjuration" + invocation = "none" + invocation_type = SpI_NONE + range = 0 + + hud_state = "wiz_shield" + +/obj/effect/forcefield/swarm + desc = "A pocket of strange energy." + name = "Null-Field" + icon = 'icons/effects/effects.dmi' + icon_state = "shield-old" + invisibility = 0 + +/spell/aoe_turf/conjure/zeropointwell + name = "Zero-Point Well" + desc = "This ability constructs a standard zero-point energy well, capable of charging nearby swarmers." + + school = "conjuration" + charge_max = 120 SECONDS + spell_flags = 0 + invocation = "none" + invocation_type = SpI_NONE + range = 0 + + summon_type = list(/obj/structure/cult/pylon/swarm/zp_well) + + hud_state = "swarm_zeropoint" + +/spell/aoe_turf/conjure/zeropointbarricade + name = "Zero-Point Barricade" + desc = "This ability constructs a standard zero-point energy wall, used to create a secure passageway for allies, and a bastion for defense." + + school = "conjuration" + charge_max = 120 SECONDS + spell_flags = 0 + invocation = "none" + invocation_type = SpI_NONE + range = 0 + + summon_type = list(/obj/structure/cult/pylon/swarm/defender) + + hud_state = "swarm_barricade" + +/spell/aoe_turf/blink/swarm + name = "Warp" + desc = "Your null-point drive jaunts you to a new location." + + school = "abjuration" + charge_max = 5 MINUTES + spell_flags = Z2NOCAST | IGNOREDENSE + invocation = "none" + invocation_type = SpI_NONE + range = 10 + inner_radius = 5 + hud_state = "swarm_warp" + +/spell/aoe_turf/conjure/swarmer/gunner + name = "Generate Gunner" + desc = "This spell constructs a gunner swarmer shell that may activate at some point." + + school = "conjuration" + charge_type = Sp_CHARGES + charge_max = 1 + spell_flags = 0 + invocation = "none" + invocation_type = SpI_NONE + range = 0 + + summon_type = list(/obj/structure/ghost_pod/ghost_activated/swarm_drone/event/gunner) + + hud_state = "swarm_replicate" + +/spell/aoe_turf/conjure/swarmer/melee + name = "Generate Impaler" + desc = "This spell constructs an impaler swarmer shell that may activate at some point." + + school = "conjuration" + charge_type = Sp_CHARGES + charge_max = 1 + spell_flags = 0 + invocation = "none" + invocation_type = SpI_NONE + range = 0 + + summon_type = list(/obj/structure/ghost_pod/ghost_activated/swarm_drone/event/melee) + + hud_state = "swarm_replicate" diff --git a/code/modules/mob/living/silicon/robot/drone/swarm_items.dm b/code/modules/mob/living/silicon/robot/drone/swarm_items.dm new file mode 100644 index 0000000000..74abd99ca5 --- /dev/null +++ b/code/modules/mob/living/silicon/robot/drone/swarm_items.dm @@ -0,0 +1,162 @@ + +//Swarm Assimilator / Breacher +/obj/item/weapon/matter_decompiler/swarm + name = "matter assimilator" + desc = "Used to eat some forms of simple machinery; and large, wall-shaped blocks of metal with energetic fields." + icon = 'icons/obj/device.dmi' + icon_state = "decompiler_swarm" + + var/field_cooldown = 1 MINUTE + var/last_field = 0 + +/obj/item/weapon/matter_decompiler/swarm/afterattack(atom/target as mob|obj|turf|area, mob/living/user as mob|obj, proximity, params) + + if(!proximity) return //Not adjacent. + + //We only want to deal with using this on turfs. Specific items aren't important. + var/turf/T = get_turf(target) + if(!istype(T)) + return + + //Used to give the right message. + var/grabbed_something = FALSE + + for(var/mob/M in T) + if(istype(M,/mob/living/simple_mob/animal/passive/lizard) || istype(M,/mob/living/simple_mob/animal/passive/mouse)) + src.loc.visible_message("[src.loc] sucks [M] into its decompiler. There's a horrible crunching noise.","It's a bit of a struggle, but you manage to suck [M] into your decompiler. It makes a series of visceral crunching noises.") + new/obj/effect/decal/cleanable/blood/splatter(get_turf(src)) + qdel(M) + if(wood) + wood.add_charge(2000) + if(plastic) + plastic.add_charge(2000) + return + + else if(istype(M,/mob/living/silicon/robot/drone) && !M.client) + + var/mob/living/silicon/robot/D = src.loc + + if(!istype(D)) + return + + to_chat(D, "You begin decompiling [M].") + + if(!do_after(D,50)) + to_chat(D, "You need to remain still while decompiling such a large object.") + return + + if(!M || !D) return + + to_chat(D, "You carefully and thoroughly decompile [M], storing as much of its resources as you can within yourself.") + qdel(M) + new/obj/effect/decal/cleanable/blood/oil(get_turf(src)) + + if(metal) + metal.add_charge(15000) + if(glass) + glass.add_charge(15000) + if(wood) + wood.add_charge(2000) + if(plastic) + plastic.add_charge(1000) + return + else + continue + + for(var/obj/W in T) + //Different classes of items give different commodities. + if(istype(W,/obj/structure/girder)) + if(metal) + metal.add_charge(500) + else if(istype(W,/obj/machinery/power/emitter)) + if(metal) + metal.add_charge(3000) + if(plastic) + plastic.add_charge(1000) + else if(istype(W,/obj/machinery/space_heater)) + if(metal) + metal.add_charge(1500) + if(plastic) + plastic.add_charge(750) + else if(istype(W,/obj/structure/closet)) + var/obj/structure/closet/C = W + if(!C.opened) + continue + if(istype(W,/obj/structure/closet/coffin)) + if(wood) + wood.add_charge(1000) + else if(istype(W,/obj/structure/closet/crate/plastic)) + if(plastic) + plastic.add_charge(750) + else + if(metal) + metal.add_charge(1000) + else + continue + + qdel(W) + grabbed_something = TRUE + + if(istype(T,/turf/simulated/wall) && (last_field < world.time + field_cooldown)) + if(!(locate(/obj/effect/temporary_effect/pulse/disintegrate))) + last_field = world.time + to_chat(user, "You deploy an energetic field through \the [T], beginning its deconstruction.") + to_chat(user, "You should stand back.") + new /obj/effect/temporary_effect/pulse/disintegrate(T) + else + to_chat(user, "There is already a disintigration field affecting \the [T].") + + if(grabbed_something) + to_chat(user, "You deploy your decompiler and clear out the contents of \the [T].") + else + to_chat(user, "Nothing on \the [T] is useful to you.") + return + +/obj/effect/temporary_effect/pulse/disintegrate + name = "molecular debonding field" + desc = "This is something you do not want to near." + icon = 'icons/mob/swarmbot.dmi' + icon_state = "disintegrate_pulse" + light_range = 4 + light_power = 5 + light_color = "#00B4D9" + pulses_remaining = 5 + pulse_delay = 2 SECONDS + +/obj/effect/temporary_effect/pulse/disintegrate/emp_act() + visible_message("\The [src] flickers, before dispersing energetically.") + qdel(src) + +/obj/effect/temporary_effect/pulse/disintegrate/on_pulse() + var/turf/T = get_turf(src) + if(istype(T,/turf/simulated/wall)) + explosion(get_turf(src), -1, -1, 1, 3, adminlog = 0) + else + qdel(src) + +/obj/effect/temporary_effect/pulse/disintegrate/Destroy() + if(istype(get_turf(src), /turf/simulated/wall)) + explosion(get_turf(src), -1, 1, 2, 5, adminlog = 1) + ..() + +/obj/item/weapon/gun/energy/xray/swarm + name = "spectral projector" + desc = "A high-power laser gun capable of expelling concentrated gamma blasts, which are able to penetrate matter easier than \ + standard xray beams, resulting in an effective 'anti-everything' energy weapon." + icon_state = "xray" + item_state = "xray" + origin_tech = list(TECH_COMBAT = 5, TECH_MATERIAL = 3, TECH_MAGNET = 2) + projectile_type = /obj/item/projectile/beam/shock + charge_cost = 175 + + self_recharge = TRUE + use_external_power = TRUE + + firemodes = list( + list(mode_name="kill", projectile_type=/obj/item/projectile/beam/gamma, charge_cost = 300), + list(mode_name="deter", projectile_type=/obj/item/projectile/beam/shock, charge_cost = 175), + ) + +/obj/item/weapon/gun/energy/xray/swarm/Initialize() + ..() + adjust_scale(-1, 1) diff --git a/code/modules/mob/living/silicon/robot/robot_modules/station.dm b/code/modules/mob/living/silicon/robot/robot_modules/station.dm index 82985b62f6..f5fa8e2d05 100644 --- a/code/modules/mob/living/silicon/robot/robot_modules/station.dm +++ b/code/modules/mob/living/silicon/robot/robot_modules/station.dm @@ -521,6 +521,7 @@ var/global/list/robot_modules = list( src.modules += new /obj/item/weapon/gun/energy/taser/xeno/sec/robot(src) src.modules += new /obj/item/taperoll/police(src) src.modules += new /obj/item/weapon/reagent_containers/spray/pepper(src) + src.modules += new /obj/item/weapon/gripper/security(src) src.emag = new /obj/item/weapon/gun/energy/laser/mounted(src) /obj/item/weapon/robot_module/robot/security/respawn_consumable(var/mob/living/silicon/robot/R, var/amount) diff --git a/code/modules/mob/living/silicon/robot/robot_modules/swarm.dm b/code/modules/mob/living/silicon/robot/robot_modules/swarm.dm new file mode 100644 index 0000000000..f125b4b0eb --- /dev/null +++ b/code/modules/mob/living/silicon/robot/robot_modules/swarm.dm @@ -0,0 +1,29 @@ +/obj/item/weapon/robot_module/drone/swarm + name = "swarm drone module" + var/id + +/obj/item/weapon/robot_module/drone/swarm/New(var/mob/living/silicon/robot/robot) + ..() + + id = robot.idcard + src.modules += id + + src.modules += new /obj/item/weapon/rcd/electric/mounted/borg/swarm(src) + src.modules += new /obj/item/device/flash/robot(src) + src.modules += new /obj/item/weapon/handcuffs/cable/tape/cyborg(src) + src.modules += new /obj/item/weapon/melee/baton/robot(src) + src.modules += new /obj/item/weapon/gun/energy/taser/mounted/cyborg/swarm(src) + src.modules += new /obj/item/weapon/matter_decompiler/swarm(src) + +/obj/item/weapon/robot_module/drone/swarm/ranged + name = "swarm gunner module" + +/obj/item/weapon/robot_module/drone/swarm/ranged/New(var/mob/living/silicon/robot/robot) + ..() + + src.modules += new /obj/item/weapon/gun/energy/xray/swarm(src) + +/obj/item/weapon/robot_module/drone/swarm/melee/New(var/mob/living/silicon/robot/robot) + ..() + + src.modules += new /obj/item/weapon/melee/energy/sword/ionic_rapier/lance(src) diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/_giant_spider.dm b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/_giant_spider.dm index b146167218..6cd5ea9c5b 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/_giant_spider.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/_giant_spider.dm @@ -117,3 +117,17 @@ if(prob(poison_chance)) to_chat(L, "You feel a tiny prick.") L.reagents.add_reagent(poison_type, poison_per_bite) + +/mob/living/simple_mob/animal/giant_spider/proc/make_spiderling() + adjust_scale(icon_scale_x * 0.7, icon_scale_y * 0.7) + maxHealth = round(maxHealth * 0.5) + health = round(health * 0.5) + melee_damage_lower *= 0.7 + melee_damage_upper *= 0.7 + + response_harm = "kicks" + + see_in_dark = max(2, round(see_in_dark * 0.6)) + + if(poison_per_bite) + poison_per_bite *= 1.3 diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/passive/lizard.dm b/code/modules/mob/living/simple_mob/subtypes/animal/passive/lizard.dm index c8dfe1c3a6..af647587a6 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/passive/lizard.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/passive/lizard.dm @@ -22,3 +22,23 @@ speak_emote = list("hisses") say_list_type = /datum/say_list/lizard + +/mob/living/simple_mob/animal/passive/lizard/large + desc = "A cute, big lizard." + maxHealth = 20 + health = 20 + + melee_damage_lower = 5 + melee_damage_upper = 15 + + attack_sharp = TRUE + +/mob/living/simple_mob/animal/passive/lizard/large/Initialize() + ..() + adjust_scale(rand(12, 20) / 10) + +/mob/living/simple_mob/animal/passive/lizard/large/defensive + maxHealth = 30 + health = 30 + + ai_holder_type = /datum/ai_holder/simple_mob/retaliate/cooperative diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/passive/mouse.dm b/code/modules/mob/living/simple_mob/subtypes/animal/passive/mouse.dm index 70afdaf910..756cea36b1 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/passive/mouse.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/passive/mouse.dm @@ -97,6 +97,17 @@ body_color = "brown" icon_state = "mouse_brown" +/mob/living/simple_mob/animal/passive/mouse/rat + name = "rat" + maxHealth = 20 + health = 20 + + ai_holder_type = /datum/ai_holder/simple_mob/melee/evasive + +/mob/living/simple_mob/animal/passive/mouse/rat/Initialize() + ..() + adjust_scale(1.2) + //TOM IS ALIVE! SQUEEEEEEEE~K :) /mob/living/simple_mob/animal/passive/mouse/brown/Tom name = "Tom" diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index a60586bbf2..4cb44c6e82 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -425,6 +425,20 @@ proc/is_blind(A) lname = "[lname] " M << "" + create_text_tag("dead", "DEAD:", M.client) + " [lname][follow][message]" +/proc/say_dead_object(var/message, var/obj/subject = null) + for(var/mob/M in player_list) + if(M.client && ((!istype(M, /mob/new_player) && M.stat == DEAD) || (M.client.holder && M.client.holder.rights)) && M.is_preference_enabled(/datum/client_preference/show_dsay)) + var/follow + var/lname = "Game Master" + if(M.forbid_seeing_deadchat && !M.client.holder) + continue + + if(subject) + lname = "[subject.name] ([subject.x],[subject.y],[subject.z])" + + lname = "[lname] " + M << "" + create_text_tag("event_dead", "EVENT:", M.client) + " [lname][follow][message]" + //Announces that a ghost has joined/left, mainly for use with wizards /proc/announce_ghost_joinleave(O, var/joined_ghosts = 1, var/message = "") var/client/C diff --git a/code/modules/projectiles/guns/energy/stun.dm b/code/modules/projectiles/guns/energy/stun.dm index 4240abe9e3..e429330042 100644 --- a/code/modules/projectiles/guns/energy/stun.dm +++ b/code/modules/projectiles/guns/energy/stun.dm @@ -22,6 +22,12 @@ charge_cost = 400 recharge_time = 7 //Time it takes for shots to recharge (in ticks) +/obj/item/weapon/gun/energy/taser/mounted/cyborg/swarm + name = "disabler" + desc = "An archaic device which attacks the target's nervous-system or control circuits." + projectile_type = /obj/item/projectile/beam/stun/disabler + charge_cost = 200 + recharge_time = 0.5 SECONDS /obj/item/weapon/gun/energy/stunrevolver name = "stun revolver" @@ -32,7 +38,6 @@ projectile_type = /obj/item/projectile/energy/electrode/strong charge_cost = 300 - /obj/item/weapon/gun/energy/crossbow name = "mini energy-crossbow" desc = "A weapon favored by many mercenary stealth specialists." diff --git a/code/modules/projectiles/guns/magnetic/magnetic_railgun.dm b/code/modules/projectiles/guns/magnetic/magnetic_railgun.dm index 0be62e0688..a2934d29e1 100644 --- a/code/modules/projectiles/guns/magnetic/magnetic_railgun.dm +++ b/code/modules/projectiles/guns/magnetic/magnetic_railgun.dm @@ -108,3 +108,89 @@ list(mode_name="semiauto", burst=1, fire_delay=0, move_delay=null, one_handed_penalty=15, burst_accuracy=null, dispersion=null), list(mode_name="short bursts", burst=3, fire_delay=null, move_delay=5, one_handed_penalty=30, burst_accuracy=list(0,-15,-15), dispersion=list(0.0, 0.6, 1.0)), ) + +/obj/item/weapon/gun/magnetic/railgun/heater + name = "coil rifle" + desc = "A large rifle designed and produced after the Grey Hour." + description_info = "The MI-51B is a Martian weapon designed in the days after the Grey Hour, in preparation for the need for updated equipment by Solar forces.
\ + The design is based upon a larger rail-type weapon design." + icon_state = "railgun_sec" + item_state = "cshotgun" + + removable_components = TRUE + + initial_cell_type = /obj/item/weapon/cell/high + initial_capacitor_type = /obj/item/weapon/stock_parts/capacitor + + fire_delay = 8 + + slot_flags = SLOT_BACK + + slowdown = 0 + slowdown_held = 0 + slowdown_worn = 0 + + power_cost = 400 + projectile_type = /obj/item/projectile/bullet/magnetic/heated + loaded = null + empty_sound = 'sound/weapons/smg_empty_alarm.ogg' + + firemodes = list( + list(mode_name="high power", power_cost = 400, projectile_type = /obj/item/projectile/bullet/magnetic/heated, burst=1, fire_delay=8, move_delay=null, one_handed_penalty=15), + list(mode_name="low power", power_cost = 150, projectile_type = /obj/item/projectile/bullet/magnetic/heated/weak, burst=1, fire_delay=5, move_delay=null, one_handed_penalty=15), + ) + +/obj/item/weapon/gun/magnetic/railgun/heater/pistol + name = "coil pistol" + desc = "A large pistol designed and produced after the Grey Hour." + description_info = "The MI-60D `Peacemaker` is a Martian weapon designed in the days after the Grey Hour, in preparation for the need for updated equipment by Solar forces.
\ + The design is based upon a larger rail-type hybrid weapon design, though much smaller in scale." + icon_state = "peacemaker" + item_state = "revolver" + + w_class = ITEMSIZE_NORMAL + + initial_cell_type = /obj/item/weapon/cell/high + initial_capacitor_type = /obj/item/weapon/stock_parts/capacitor + + slot_flags = SLOT_BELT|SLOT_HOLSTER + + firemodes = list( + list(mode_name="lethal", power_cost = 2000, projectile_type = /obj/item/projectile/bullet/magnetic/heated, burst=1, fire_delay=8, move_delay=null, one_handed_penalty=0), + list(mode_name="stun", power_cost = 1500, projectile_type = /obj/item/projectile/energy/electrode/stunshot, burst=1, fire_delay=5, move_delay=null, one_handed_penalty=0), + ) + +/obj/item/weapon/gun/magnetic/railgun/heater/pistol/hos + name = "prototype peacemaker" + + dna_lock = TRUE + + description_antag = "This weapon starts with a DNA locking chip attached. Using an EMAG on the weapon will disarm it, and allow you to use the chip as your own." + + firemodes = list( + list(mode_name="lethal", power_cost = 1500, projectile_type = /obj/item/projectile/bullet/magnetic/heated, burst=1, fire_delay=8, move_delay=null, one_handed_penalty=0), + list(mode_name="stun", power_cost = 1200, projectile_type = /obj/item/projectile/energy/electrode/stunshot, burst=1, fire_delay=5, move_delay=null, one_handed_penalty=0), + ) + +/obj/item/weapon/gun/magnetic/railgun/flechette/sif + name = "shredder rifle" + desc = "The MI-12B Kaldr is a burst fire capable coilgun that fires modified slugs intended for damaging soft targets." + description_fluff = "The Kaldr is a weapon recently deployed to various outposts on Sif, as well as local hunting guilds for the rapid dispatching of invasive wildlife." + icon_state = "railgun_sifguard" + item_state = "z8carbine" + + initial_cell_type = /obj/item/weapon/cell/high + initial_capacitor_type = /obj/item/weapon/stock_parts/capacitor/adv + + slot_flags = SLOT_BACK + + slowdown = 0.3 + + power_cost = 200 + projectile_type = /obj/item/projectile/bullet/magnetic/flechette/hunting + empty_sound = 'sound/weapons/smg_empty_alarm.ogg' + + firemodes = list( + list(mode_name="semiauto", burst=1, fire_delay=0, move_delay=null, one_handed_penalty=15, burst_accuracy=null, dispersion=null), + list(mode_name="short bursts", burst=3, fire_delay=null, move_delay=5, one_handed_penalty=30, burst_accuracy=list(0,-15,-15), dispersion=list(0.0, 0.6, 1.0)), + ) diff --git a/code/modules/projectiles/projectile/beams.dm b/code/modules/projectiles/projectile/beams.dm index ec2670c61d..b00fa8221a 100644 --- a/code/modules/projectiles/projectile/beams.dm +++ b/code/modules/projectiles/projectile/beams.dm @@ -86,6 +86,19 @@ tracer_type = /obj/effect/projectile/tracer/xray impact_type = /obj/effect/projectile/impact/xray +/obj/item/projectile/beam/gamma + name = "gamma beam" + icon_state = "xray" + fire_sound = 'sound/weapons/eluger.ogg' + damage = 10 + armor_penetration = 90 + irradiate = 20 + light_color = "#00CC33" + + muzzle_type = /obj/effect/projectile/muzzle/xray + tracer_type = /obj/effect/projectile/tracer/xray + impact_type = /obj/effect/projectile/impact/xray + /obj/item/projectile/beam/cyan name = "cyan beam" icon_state = "cyan" @@ -215,6 +228,23 @@ icon_state = "stun" agony = 30 +/obj/item/projectile/beam/stun/disabler + muzzle_type = /obj/effect/projectile/muzzle/laser_omni + tracer_type = /obj/effect/projectile/tracer/laser_omni + impact_type = /obj/effect/projectile/impact/laser_omni + +/obj/item/projectile/beam/stun/disabler/on_hit(atom/target, blocked = 0, def_zone) + . = ..(target, blocked, def_zone) + + if(. && istype(target, /mob/living/silicon/robot) && prob(agony)) + var/mob/living/silicon/robot/R = target + var/drainamt = agony * (rand(5, 15) / 10) + R.drain_power(0, 0, drainamt) + if(istype(firer, /mob/living/silicon/robot)) // Mischevious sappers, the swarm drones are. + var/mob/living/silicon/robot/A = firer + if(A.cell) + A.cell.give(drainamt * 2) + /obj/item/projectile/beam/shock name = "shock beam" icon_state = "lightning" diff --git a/code/modules/projectiles/projectile/magnetic.dm b/code/modules/projectiles/projectile/magnetic.dm index 8185fb127e..9e76c3551f 100644 --- a/code/modules/projectiles/projectile/magnetic.dm +++ b/code/modules/projectiles/projectile/magnetic.dm @@ -22,6 +22,28 @@ damage = 20 armor_penetration = 100 +/obj/item/projectile/bullet/magnetic/flechette/hunting + name = "shredder slug" + armor_penetration = 30 + SA_bonus_damage = 40 + SA_vulnerability = SA_ANIMAL + +/obj/item/projectile/bullet/magnetic/heated + name = "slug" + icon_state = "gauss" + weaken = 0 + stun = 0 + damage = 30 + damage_type = SEARING + embed_chance = 0 + +/obj/item/projectile/bullet/magnetic/heated/weak + icon_state = "gauss_silenced" + damage = 15 + agony = 5 + embed_chance = 0 + armor_penetration = 50 + /obj/item/projectile/bullet/magnetic/fuelrod name = "fuel rod" icon_state = "fuel-deuterium" diff --git a/icons/chattags.dmi b/icons/chattags.dmi index 699f1b53a1..a0b1d335d0 100644 Binary files a/icons/chattags.dmi and b/icons/chattags.dmi differ diff --git a/icons/mob/screen_spells.dmi b/icons/mob/screen_spells.dmi index c320d53590..4dd42bac21 100644 Binary files a/icons/mob/screen_spells.dmi and b/icons/mob/screen_spells.dmi differ diff --git a/icons/mob/swarmbot.dmi b/icons/mob/swarmbot.dmi new file mode 100644 index 0000000000..e531ad3ed0 Binary files /dev/null and b/icons/mob/swarmbot.dmi differ diff --git a/icons/obj/clothing/modular_armor.dmi b/icons/obj/clothing/modular_armor.dmi index 180f52b902..1372ff9a5e 100644 Binary files a/icons/obj/clothing/modular_armor.dmi and b/icons/obj/clothing/modular_armor.dmi differ diff --git a/icons/obj/contraband.dmi b/icons/obj/contraband.dmi index 7e37655180..019e008e41 100644 Binary files a/icons/obj/contraband.dmi and b/icons/obj/contraband.dmi differ diff --git a/icons/obj/cult.dmi b/icons/obj/cult.dmi index 54c588f5ad..ebde622948 100644 Binary files a/icons/obj/cult.dmi and b/icons/obj/cult.dmi differ diff --git a/icons/obj/device.dmi b/icons/obj/device.dmi index 11dcb38dec..6fd1a8d0d3 100644 Binary files a/icons/obj/device.dmi and b/icons/obj/device.dmi differ diff --git a/icons/obj/railgun.dmi b/icons/obj/railgun.dmi index 6f633db1e5..0143f9053e 100644 Binary files a/icons/obj/railgun.dmi and b/icons/obj/railgun.dmi differ diff --git a/icons/obj/spells.dmi b/icons/obj/spells.dmi index 554aac8678..0f2dadb4cb 100644 Binary files a/icons/obj/spells.dmi and b/icons/obj/spells.dmi differ diff --git a/icons/obj/storage.dmi b/icons/obj/storage.dmi index 26b1b63ce6..31941dc51d 100644 Binary files a/icons/obj/storage.dmi and b/icons/obj/storage.dmi differ diff --git a/polaris.dme b/polaris.dme index e2ea7c5fb4..665f39f6ae 100644 --- a/polaris.dme +++ b/polaris.dme @@ -425,6 +425,7 @@ #include "code\game\antagonist\station\renegade.dm" #include "code\game\antagonist\station\revolutionary.dm" #include "code\game\antagonist\station\rogue_ai.dm" +#include "code\game\antagonist\station\stowaway.dm" #include "code\game\antagonist\station\thug.dm" #include "code\game\antagonist\station\traitor.dm" #include "code\game\area\ai_monitored.dm" @@ -1149,6 +1150,7 @@ #include "code\game\objects\structures\flora\grass.dm" #include "code\game\objects\structures\flora\trees.dm" #include "code\game\objects\structures\ghost_pods\ghost_pods.dm" +#include "code\game\objects\structures\ghost_pods\human.dm" #include "code\game\objects\structures\ghost_pods\mysterious.dm" #include "code\game\objects\structures\ghost_pods\silicon.dm" #include "code\game\objects\structures\props\alien_props.dm" @@ -1159,6 +1161,7 @@ #include "code\game\objects\structures\props\projectile_lock.dm" #include "code\game\objects\structures\props\prop.dm" #include "code\game\objects\structures\props\puzzledoor.dm" +#include "code\game\objects\structures\props\swarm.dm" #include "code\game\objects\structures\props\transmitter.dm" #include "code\game\objects\structures\stool_bed_chair_nest\alien_nests.dm" #include "code\game\objects\structures\stool_bed_chair_nest\bed.dm" @@ -1532,6 +1535,7 @@ #include "code\modules\detectivework\tools\luminol.dm" #include "code\modules\detectivework\tools\rag.dm" #include "code\modules\detectivework\tools\sample_kits.dm" +#include "code\modules\detectivework\tools\scanner.dm" #include "code\modules\detectivework\tools\storage.dm" #include "code\modules\detectivework\tools\swabs.dm" #include "code\modules\detectivework\tools\uvlight.dm" @@ -1659,12 +1663,15 @@ #include "code\modules\gamemaster\actions\canister_leak.dm" #include "code\modules\gamemaster\actions\carp_migration.dm" #include "code\modules\gamemaster\actions\comms_blackout.dm" +#include "code\modules\gamemaster\actions\drill_announcement.dm" #include "code\modules\gamemaster\actions\dust.dm" #include "code\modules\gamemaster\actions\electrical_storm.dm" #include "code\modules\gamemaster\actions\electrified_door.dm" #include "code\modules\gamemaster\actions\gravity.dm" #include "code\modules\gamemaster\actions\grid_check.dm" +#include "code\modules\gamemaster\actions\infestation.dm" #include "code\modules\gamemaster\actions\ion_storm.dm" +#include "code\modules\gamemaster\actions\manifest_malfunction.dm" #include "code\modules\gamemaster\actions\meteor_defense.dm" #include "code\modules\gamemaster\actions\money_hacker.dm" #include "code\modules\gamemaster\actions\money_lotto.dm" @@ -1674,13 +1681,19 @@ #include "code\modules\gamemaster\actions\radiation_storm.dm" #include "code\modules\gamemaster\actions\random_antagonist.dm" #include "code\modules\gamemaster\actions\rogue_drones.dm" +#include "code\modules\gamemaster\actions\security_advisement.dm" #include "code\modules\gamemaster\actions\shipping_error.dm" #include "code\modules\gamemaster\actions\solar_storm.dm" #include "code\modules\gamemaster\actions\spacevine.dm" #include "code\modules\gamemaster\actions\spider_infestation.dm" #include "code\modules\gamemaster\actions\spontaneous_appendicitis.dm" +#include "code\modules\gamemaster\actions\station_fundraise.dm" +#include "code\modules\gamemaster\actions\stowaway.dm" +#include "code\modules\gamemaster\actions\supply_conversion.dm" +#include "code\modules\gamemaster\actions\supplyrequest.dm" #include "code\modules\gamemaster\actions\surprise_carp_attack.dm" #include "code\modules\gamemaster\actions\surprise_meteor.dm" +#include "code\modules\gamemaster\actions\swarmboarder.dm" #include "code\modules\gamemaster\actions\viral_infection.dm" #include "code\modules\gamemaster\actions\viral_outbreak.dm" #include "code\modules\gamemaster\actions\wallrot.dm" @@ -2047,8 +2060,12 @@ #include "code\modules\mob\living\silicon\robot\drone\drone_items.dm" #include "code\modules\mob\living\silicon\robot\drone\drone_manufacturer.dm" #include "code\modules\mob\living\silicon\robot\drone\drone_say.dm" +#include "code\modules\mob\living\silicon\robot\drone\swarm.dm" +#include "code\modules\mob\living\silicon\robot\drone\swarm_abilities.dm" +#include "code\modules\mob\living\silicon\robot\drone\swarm_items.dm" #include "code\modules\mob\living\silicon\robot\robot_modules\event.dm" #include "code\modules\mob\living\silicon\robot\robot_modules\station.dm" +#include "code\modules\mob\living\silicon\robot\robot_modules\swarm.dm" #include "code\modules\mob\living\silicon\robot\robot_modules\syndicate.dm" #include "code\modules\mob\living\silicon\robot\subtypes\gravekeeper.dm" #include "code\modules\mob\living\silicon\robot\subtypes\lost_drone.dm"